From a71cb8163caaf0cdc9ce96509e3483e94b35cdac Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Sun, 6 Dec 2020 20:54:08 +0100 Subject: [PATCH] Normalize speech snapshot matcher signatures (#16) --- .changeset/small-starfishes-yell.md | 6 +++ examples/jest/index.test.ts | 26 +++++-------- src/__tests__/extendExpect.js | 57 +++++++++++++++++++++++++---- src/index.js | 14 +++---- src/matcherTypes.d.ts | 4 +- 5 files changed, 75 insertions(+), 32 deletions(-) create mode 100644 .changeset/small-starfishes-yell.md diff --git a/.changeset/small-starfishes-yell.md b/.changeset/small-starfishes-yell.md new file mode 100644 index 0000000..a85f12d --- /dev/null +++ b/.changeset/small-starfishes-yell.md @@ -0,0 +1,6 @@ +--- +"example-jest": patch +"screen-reader-testing-library": patch +--- + +Implement same signature as `toMatchSpeechSnapshot` in `toMatchSpeechInlineSnapshot` diff --git a/examples/jest/index.test.ts b/examples/jest/index.test.ts index e0eaf1d..8f21c19 100644 --- a/examples/jest/index.test.ts +++ b/examples/jest/index.test.ts @@ -43,26 +43,20 @@ describe("chromium", () => { await page.bringToFront(); await awaitNvdaRecording(); - await expect( - speechRecorder.record(async () => { - await page.keyboard.press("s"); - }) - ).resolves.toMatchSpeechInlineSnapshot(` + await expect(async () => { + await page.keyboard.press("s"); + }).toMatchSpeechInlineSnapshot(` "banner landmark" "Search, combo box, expanded, has auto complete, editable, Search…, blank" `); - await expect( - speechRecorder.record(async () => { - await page.keyboard.type("Rating"); - }) - ).resolves.toMatchSpeechInlineSnapshot(``); - - await expect( - speechRecorder.record(async () => { - await page.keyboard.press("ArrowDown"); - }) - ).resolves.toMatchSpeechInlineSnapshot(` + await expect(async () => { + await page.keyboard.type("Rating"); + }).toMatchSpeechInlineSnapshot(``); + + await expect(async () => { + await page.keyboard.press("ArrowDown"); + }).toMatchSpeechInlineSnapshot(` "list" "Link to the result, 1 of 5" `); diff --git a/src/__tests__/extendExpect.js b/src/__tests__/extendExpect.js index 3f616d3..d4f397e 100644 --- a/src/__tests__/extendExpect.js +++ b/src/__tests__/extendExpect.js @@ -1,19 +1,62 @@ +const { promises: fs } = require("fs"); +const os = require("os"); +const path = require("path"); const { extendExpect } = require("../index"); -extendExpect(expect, "unused"); +/** + * @type {string} + */ +let logFilePath; +/** + * @param {string[][]} speech + */ +function speakMock(speech) { + // Check existing fixtures for how to mock speech output. + const mockedSpeach = speech + .map((line) => { + return `Speaking [${line + .map((group) => { + return `'${group}'`; + }) + .join(", ")}]\n`; + }) + .join(""); + return fs.writeFile(logFilePath, mockedSpeach, { flag: "a" }); +} -test("custom inline snapshot with no lines", () => { - expect([]).toMatchSpeechInlineSnapshot(``); +beforeAll(async () => { + logFilePath = path.join( + os.tmpdir(), + "srtl-testing", + `extendExpect-${new Date().valueOf()}.log` + ); + await fs.mkdir(path.dirname(logFilePath), { recursive: true }); + await fs.writeFile(logFilePath, "", { flag: "w" }); + extendExpect(expect, logFilePath); }); -test("custom inline snapshot with one line", () => { +afterAll(async () => { + await fs.unlink(logFilePath); +}); + +test("custom inline snapshot with no lines", async () => { + await expect(async () => { + await speakMock([]); + }).toMatchSpeechInlineSnapshot(``); +}); + +test("custom inline snapshot with one line", async () => { const actualSpeech = [["banner landmark"]]; - expect(actualSpeech).toMatchSpeechInlineSnapshot(`"banner landmark"`); + await expect(async () => { + await speakMock(actualSpeech); + }).toMatchSpeechInlineSnapshot(`"banner landmark"`); }); -test("custom inline snapshot with two lines", () => { +test("custom inline snapshot with two lines", async () => { const actualSpeech = [["banner landmark"], ["Search", "combobox"]]; - expect(actualSpeech).toMatchSpeechInlineSnapshot(` + await expect(async () => { + await speakMock(actualSpeech); + }).toMatchSpeechInlineSnapshot(` "banner landmark" "Search, combobox" `); diff --git a/src/index.js b/src/index.js index dd69d59..e5124a2 100644 --- a/src/index.js +++ b/src/index.js @@ -102,21 +102,19 @@ function createMatchers(logFilePath) { const speechRecorder = createSpeechRecorder(logFilePath); /** - * @param {Speech} recordedSpeech + * @param {() => Promise} fn * @param {string} [expectedSpeechSnapshot] - * @returns {ReturnType} + * @returns {Promise>} * @this {import('jest-snapshot/build/types').Context} */ - function toMatchSpeechInlineSnapshot(recordedSpeech, expectedSpeechSnapshot) { + async function toMatchSpeechInlineSnapshot(fn, expectedSpeechSnapshot) { + // Otherwise jest uses the async stack trace which makes it impossible to know the actual callsite of `toMatchSpeechInlineSnapshot`. + this.error = new Error(); // Abort test on first mismatch. // Subsequent actions will be based on an incorrect state otherwise and almost always fail as well. this.dontThrow = () => {}; - if (typeof recordedSpeech === "function") { - throw new Error( - "Recording lines is not implemented by the matcher. Use `expect(recordLines(async () => {})).resolves.toMatchInlineSnapshot()` instead" - ); - } + const recordedSpeech = await speechRecorder.record(fn); const actualSpeechSnapshot = { [speechSnapshotBrand]: true, speech: recordedSpeech, diff --git a/src/matcherTypes.d.ts b/src/matcherTypes.d.ts index 586528c..d336109 100644 --- a/src/matcherTypes.d.ts +++ b/src/matcherTypes.d.ts @@ -7,7 +7,9 @@ declare global { interface Matchers { toAnnounceNVDA(expectedLines: Speech): Promise; toMatchSpeechSnapshot(snapshotName?: string): Promise; - toMatchSpeechInlineSnapshot(expectedLinesSnapshot?: string): void; + toMatchSpeechInlineSnapshot( + expectedLinesSnapshot?: string + ): Promise; } } }