Skip to main content
Module

std/testing/snapshot_test.ts

Deno standard library
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.import { stripColor } from "../fmt/colors.ts";import { dirname, fromFileUrl, join, toFileUrl } from "../path/mod.ts";import { assert, assertInstanceOf, AssertionError, assertRejects, fail,} from "./asserts.ts";import { assertSnapshot, createAssertSnapshot, serialize } from "./snapshot.ts";
const SNAPSHOT_MODULE_URL = toFileUrl(join( dirname(fromFileUrl(import.meta.url)), "snapshot.ts",));
function formatTestOutput(string: string) { // Strip colors and obfuscate any timings return stripColor(string).replace(/([0-9])+m?s/g, "--ms").replace( /(?<=running ([0-9])+ test(s)? from )(.*)(?=test.ts)/g, "<tempDir>/", );}
function formatTestError(string: string) { // Strip colors and remove "Check file:///workspaces/deno_std/testing/.tmp/test.ts" // as this is always output to stderr return stripColor(string).replace(/^Check file:\/\/(.+)\n/g, "");}
function testFnWithTempDir( fn: (t: Deno.TestContext, tempDir: string) => Promise<void>,) { return async (t: Deno.TestContext) => { const tempDir = await Deno.makeTempDir(); try { await fn(t, tempDir); } finally { await Deno.remove(tempDir, { recursive: true }); } };}
class TestClass { a = 1; b = 2; init() { this.b = 3; } get getA() { return this.a; } func() {}}
const map = new Map();map.set("Hello", "World!");map.set(() => "Hello", "World!");map.set(1, 2);
Deno.test("Snapshot Test", async (t) => { await assertSnapshot(t, { a: 1, b: 2 }); await assertSnapshot(t, new TestClass()); await assertSnapshot(t, map); await assertSnapshot(t, new Set([1, 2, 3])); await assertSnapshot(t, { fn() {} }); await assertSnapshot(t, function fn() {}); await assertSnapshot(t, [1, 2, 3]); await assertSnapshot(t, "hello world");});
Deno.test("Snapshot Test - step", async (t) => { await assertSnapshot(t, { a: 1, b: 2 }); await t.step("Nested", async (t) => { await assertSnapshot(t, new TestClass()); await assertSnapshot(t, map); await t.step("Nested Nested", async (t) => { await assertSnapshot(t, new Set([1, 2, 3])); await assertSnapshot(t, { fn() {} }); await assertSnapshot(t, function fn() {}); }); await assertSnapshot(t, [1, 2, 3]); }); await assertSnapshot(t, "hello world");});
Deno.test("Snapshot Test - Adverse String \\ ` ${}", async (t) => { await assertSnapshot(t, "\\ ` ${}");});
Deno.test("Snapshot Test - Multi-Line Strings", async (t) => { await t.step("string", async (t) => { await assertSnapshot( t, `<html> <head> <title>Snapshot Test - Multi-Line Strings</title> </head> <body> <h1> Snapshot Test - Multi-Line Strings </h2> <p> This is a snapshot of a multi-line string. </p> </body></html>`, ); });
await t.step("string in array", async (t) => { await assertSnapshot(t, [ `<h1> Header</h1>`, `<p> Content</p>`, ]); });
await t.step("string in object", async (t) => { await assertSnapshot(t, { str: ` Line #1 Line #2 Line #3`, }); });});
Deno.test( "Snapshot Test - Failed Assertion", testFnWithTempDir(async (t, tempDir) => { let count = 0; async function testFailedAssertion<T>( snapshot: T, actual: T, ): Promise<AssertionError> { const snapshotFilePath = join(tempDir, `snapshot_file_${++count}.snap`); await Deno.writeTextFile( snapshotFilePath, `export const snapshot = {};
snapshot[\`name 1\`] = \`${serialize(snapshot)}\`;`, );
try { await assertSnapshot(t, actual, { path: snapshotFilePath, mode: "assert", name: "name", }); fail("Snapshot assertion passed when it was expected to fail"); } catch (error) { assertInstanceOf(error, AssertionError); return error as AssertionError; } }
await t.step("Object", async (t) => { const error = await testFailedAssertion([1, 2, 3], [1, 2]); await assertSnapshot(t, stripColor(error.message)); });
await t.step("String", async (t) => { const error = await testFailedAssertion("Hello World!", "Hello!"); await assertSnapshot(t, stripColor(error.message)); }); }),);
Deno.test("Snapshot Test - Options", async (t) => { const VALUE = [1, 2, 3];
await t.step("dir", async (t) => { await t.step("relative", async (t) => { await assertSnapshot(t, VALUE, { dir: "__snapshots__/options_tests/", }); });
await t.step("absolute", async (t) => { await assertSnapshot(t, VALUE, { dir: join(Deno.cwd(), "testing/__snapshots__/options_tests/"), }); }); });
await t.step("path", async (t) => { await t.step("relative", async (t) => { await assertSnapshot(t, VALUE, { path: "__snapshots__/options_tests/custom_path.snap", }); });
await t.step("absolute", async (t) => { await assertSnapshot(t, VALUE, { path: join( Deno.cwd(), "testing/__snapshots__/options_tests/custom_path.snap", ), }); }); });
await t.step("name", async (t) => { await assertSnapshot(t, VALUE, { name: "custom name", });
await assertSnapshot(t, VALUE, { name: "custom name", }); });
await t.step("serializer", async (t) => { await assertSnapshot<Array<number>>(t, VALUE, { serializer: (actual) => { return `Array Length: ${actual.length}\n\n${serialize(actual)}`; }, }); });
await t.step("msg", async (t) => { await t.step("missing snapshot", async (t) => { try { await assertSnapshot<Array<number>>(t, VALUE, { msg: "[CUSTOM ERROR MESSAGE - MISSING SNAPSHOT]", mode: "assert", name: "MISSING SNAPSHOT", }); fail("Snapshot should not exist"); } catch (error) { assertInstanceOf(error, AssertionError); await assertSnapshot(t, error.message); } });
await t.step("missing snapshot file", async (t) => { try { await assertSnapshot<Array<number>>(t, VALUE, { msg: "[CUSTOM ERROR MESSAGE - MISSING SNAPSHOT]", mode: "assert", path: "MISSING_SNAPSHOT_FILE.snap", }); fail("Snapshot file should not exist"); } catch (error) { assertInstanceOf(error, AssertionError); await assertSnapshot(t, error.message); } }); });
await t.step( "mode", testFnWithTempDir(async (t, tempDir) => { const snapshotFilePath = join(tempDir, "snapshot.snap"); const snapshotName = "snapshot";
async function runTest(test: string) { const tempTestFileName = "test.ts"; const tempTestFilePath = join(tempDir, tempTestFileName); await Deno.writeTextFile(tempTestFilePath, test);
const process = await Deno.run({ cmd: [ Deno.execPath(), "test", "--allow-all", tempTestFilePath, "--", "-u", ], stdout: "piped", stderr: "piped", }); const output = await process.output(); const error = await process.stderrOutput(); process.close();
return { output: new TextDecoder().decode(output), error: new TextDecoder().decode(error), }; }
const result = await runTest(` import { assertSnapshot } from "${SNAPSHOT_MODULE_URL}";
Deno.test("${snapshotName}", async (t) => { await assertSnapshot(t, [1, 2, 3], { path: "${snapshotFilePath.replace(/\\/g, "\\\\")}", mode: "update", }); }); `);
const { snapshot } = await import(toFileUrl(snapshotFilePath).toString());
await assertSnapshot(t, snapshot[`${snapshotName} 1`]); await assertSnapshot(t, formatTestOutput(result.output)); assert(!formatTestError(result.error), "unexpected output to stderr"); }), );});
Deno.test( "Snapshot Test - Update", testFnWithTempDir(async (t, tempDir) => { const tempTestFileName = "test.ts"; const tempTestFilePath = join(tempDir, tempTestFileName); const tempSnapshotFilePath = join( tempDir, "__snapshots__", `${tempTestFileName}.snap`, );
async function runTestWithUpdateFlag(test: string) { await Deno.writeTextFile(tempTestFilePath, test);
const { stdout, stderr } = await Deno.spawn(Deno.execPath(), { args: ["test", "--allow-all", tempTestFilePath, "--", "-u"], });
return { output: new TextDecoder().decode(stdout), error: new TextDecoder().decode(stderr), snapshots: await Deno.readTextFile(tempSnapshotFilePath), }; }
function assertNoError(error: string) { if (formatTestError(error)) { throw new AssertionError(`Unexpected Error:\n\n${error}\n`); } }
/** * New snapshot */ const result1 = await runTestWithUpdateFlag( ` import { assertSnapshot } from "${SNAPSHOT_MODULE_URL}";
Deno.test("Snapshot Test - Update", async (t) => { await assertSnapshot(t, [ 1, 2, ]); });`, );
assertNoError(result1.error); await assertSnapshot(t, formatTestOutput(result1.output), { name: "Snapshot Test - Update - New snapshot", }); await assertSnapshot(t, result1.snapshots, { name: "Snapshot Test - Update - New snapshot", });
/** * Existing snapshot - no changes */ const result2 = await runTestWithUpdateFlag( ` import { assertSnapshot } from "${SNAPSHOT_MODULE_URL}";
Deno.test("Snapshot Test - Update", async (t) => { await assertSnapshot(t, [ 1, 2, ]); });`, );
assertNoError(result2.error); await assertSnapshot(t, formatTestOutput(result2.output), { name: "Snapshot Test - Update - Existing snapshot - no changes", }); await assertSnapshot(t, result2.snapshots, { name: "Snapshot Test - Update - Existing snapshot - no changes", });
/** * Existing snapshot - updates */ const result3 = await runTestWithUpdateFlag(` import { assertSnapshot } from "${SNAPSHOT_MODULE_URL}";
Deno.test("Snapshot Test - Update", async (t) => { await assertSnapshot(t, [ 1, 2, 3, 5, ]); }); `);
assertNoError(result3.error); await assertSnapshot(t, formatTestOutput(result3.output), { name: "Snapshot Test - Update - Existing snapshot - updates", }); await assertSnapshot(t, result3.snapshots, { name: "Snapshot Test - Update - Existing snapshot - updates", });
/** * Existing snapshots - reverse order 1 */ const result4 = await runTestWithUpdateFlag(` import { assertSnapshot } from "${SNAPSHOT_MODULE_URL}";
Deno.test("Snapshot Test - First", async (t) => { await assertSnapshot(t, "FIRST"); });
Deno.test("Snapshot Test - Second", async (t) => { await assertSnapshot(t, "SECOND"); }); `);
assertNoError(result4.error); await assertSnapshot(t, formatTestOutput(result4.output), { name: "Snapshot Test - Update - Existing snapshots - reverse order 1", }); await assertSnapshot(t, result4.snapshots, { name: "Snapshot Test - Update - Existing snapshots - reverse order 1", });
/** * Existing snapshots - reverse order 2 */ const result5 = await runTestWithUpdateFlag(` import { assertSnapshot } from "${SNAPSHOT_MODULE_URL}";
Deno.test("Snapshot Test - Second", async (t) => { await assertSnapshot(t, "SECOND"); });
Deno.test("Snapshot Test - First", async (t) => { await assertSnapshot(t, "FIRST"); }); `);
assertNoError(result5.error); await assertSnapshot(t, formatTestOutput(result5.output), { name: "Snapshot Test - Update - Existing snapshots - reverse order 2", }); await assertSnapshot(t, result5.snapshots, { name: "Snapshot Test - Update - Existing snapshots - reverse order 2", }); }),);
// Regression test for https://github.com/denoland/deno_std/issues/2140// Long strings should not be truncated with ellipsisDeno.test("Snapshot Test - Regression #2140", async (t) => { await assertSnapshot(t, { title: "Testing a page", content: ` <h1>Testing a page</h1> <p>This is a test</p> <ul> <li>1</li> <li>2</li> <li>3</li> <li>4</li> </ul> `, });});
// Regression test for https://github.com/denoland/deno_std/issues/2144// Empty arrays should be compactedDeno.test("Snapshot Test - Regression #2144", async (t) => { const config = { fmt: { files: { exclude: [], include: [], }, options: {}, }, }; await assertSnapshot(t, config);});
Deno.test("Snapshot Test - Empty #2245", async (t) => { await assertSnapshot(t, "", { serializer: (x) => x });});
Deno.test("SnapshotTest - createAssertSnapshot", async (t) => { const assertMonochromeSnapshot = createAssertSnapshot<string>({ serializer: stripColor, });
await t.step("No Options", async (t) => { await assertMonochromeSnapshot( t, "\x1b[32mThis green text has had it's colours stripped\x1b[39m", ); });
await t.step("Options Object", async (t) => { await assertMonochromeSnapshot( t, "\x1b[32mThis green text has had it's colours stripped\x1b[39m", { name: "SnapshotTest - createAssertSnapshot - Options Object - Custom Name", }, ); });
await t.step("Message", async (t) => { const assertMissingSnapshot = createAssertSnapshot<string>({ mode: "assert", name: "[MISSING SNAPSHOT]", });
const err = await assertRejects(async () => { await assertMissingSnapshot( t, null, "This snapshot has failed as expected", ); }, AssertionError);
await assertSnapshot(t, err.message); });
await t.step("Composite", async (t) => { const assertMonochromeSnapshotComposite = createAssertSnapshot<string>({ name: "SnapshotTest - createAssertSnapshot - Composite - Custom Name", }, assertMonochromeSnapshot);
await assertMonochromeSnapshotComposite( t, "\x1b[32mThis green text has had it's colours stripped\x1b[39m", ); });});