From 1606f34f27ffb386fd922467666f53ed2f62bed3 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 13 Aug 2024 19:59:58 +0900 Subject: [PATCH] fix(snapshot): reject multiple `toMatchInlineSnapshot` updates at the same location (#6332) --- packages/snapshot/src/port/state.ts | 7 ++++++ ...ne-snapshop-inside-loop-update-all.test.ts | 23 +++++++++++++++++++ ...e-snapshop-inside-loop-update-none.test.ts | 9 ++++++++ .../cli/test/__snapshots__/fails.test.ts.snap | 7 ++++++ test/cli/test/fails.test.ts | 5 +++- 5 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 test/cli/fixtures/fails/inline-snapshop-inside-loop-update-all.test.ts create mode 100644 test/cli/fixtures/fails/inline-snapshop-inside-loop-update-none.test.ts diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index eab28e81e005..a08fae338a7d 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -158,6 +158,13 @@ export default class SnapshotState { // location for js files, but `column-1` points to the same in both js/ts // https://github.com/vitejs/vite/issues/8657 stack.column-- + // reject multiple inline snapshots at the same location + const duplicateIndex = this._inlineSnapshots.findIndex(s => s.file === stack.file && s.line === stack.line && s.column === stack.column) + if (duplicateIndex >= 0) { + // remove the first one to avoid updating an inline snapshot + this._inlineSnapshots.splice(duplicateIndex, 1) + throw new Error('toMatchInlineSnapshot cannot be called multiple times at the same location.') + } this._inlineSnapshots.push({ snapshot: receivedSerialized, ...stack, diff --git a/test/cli/fixtures/fails/inline-snapshop-inside-loop-update-all.test.ts b/test/cli/fixtures/fails/inline-snapshop-inside-loop-update-all.test.ts new file mode 100644 index 000000000000..ffad4925d808 --- /dev/null +++ b/test/cli/fixtures/fails/inline-snapshop-inside-loop-update-all.test.ts @@ -0,0 +1,23 @@ +import {test, expect} from "vitest"; + +test("ok", () => { + expect("ok").toMatchInlineSnapshot(`"ok"`); +}); + +test("fail 1", () => { + for (const str of ["foo", "bar"]) { + expect(str).toMatchInlineSnapshot(); + } +}); + +test("fail 3", () => { + for (const str of ["ok", "ok"]) { + expect(str).toMatchInlineSnapshot(); + } +}); + +test("somehow ok", () => { + for (const str of ["ok", "ok"]) { + expect(str).toMatchInlineSnapshot(`"ok"`); + } +}); diff --git a/test/cli/fixtures/fails/inline-snapshop-inside-loop-update-none.test.ts b/test/cli/fixtures/fails/inline-snapshop-inside-loop-update-none.test.ts new file mode 100644 index 000000000000..ae2ccbff44d9 --- /dev/null +++ b/test/cli/fixtures/fails/inline-snapshop-inside-loop-update-none.test.ts @@ -0,0 +1,9 @@ +import {test, expect} from "vitest"; + +// this test causes infinite re-run when --watch and --update +// since snapshot update switches between "foo" and "bar" forever. +test("fail 2", () => { + for (const str of ["foo", "bar"]) { + expect(str).toMatchInlineSnapshot(`"bar"`); + } +}); diff --git a/test/cli/test/__snapshots__/fails.test.ts.snap b/test/cli/test/__snapshots__/fails.test.ts.snap index 5ee6a3c42c5a..6572cb5552da 100644 --- a/test/cli/test/__snapshots__/fails.test.ts.snap +++ b/test/cli/test/__snapshots__/fails.test.ts.snap @@ -31,6 +31,13 @@ Error: InlineSnapshot cannot be used inside of test.each or describe.each Error: InlineSnapshot cannot be used inside of test.each or describe.each" `; +exports[`should fail inline-snapshop-inside-loop-update-all.test.ts > inline-snapshop-inside-loop-update-all.test.ts 1`] = ` +"Error: toMatchInlineSnapshot cannot be called multiple times at the same location. +Error: toMatchInlineSnapshot cannot be called multiple times at the same location." +`; + +exports[`should fail inline-snapshop-inside-loop-update-none.test.ts > inline-snapshop-inside-loop-update-none.test.ts 1`] = `"Error: Snapshot \`fail 2 1\` mismatched"`; + exports[`should fail mock-import-proxy-module.test.ts > mock-import-proxy-module.test.ts 1`] = `"Error: There are some problems in resolving the mocks API."`; exports[`should fail nested-suite.test.ts > nested-suite.test.ts 1`] = `"AssertionError: expected true to be false // Object.is equality"`; diff --git a/test/cli/test/fails.test.ts b/test/cli/test/fails.test.ts index 04ef933d2e75..0bb19ad73288 100644 --- a/test/cli/test/fails.test.ts +++ b/test/cli/test/fails.test.ts @@ -8,7 +8,10 @@ const root = resolve(__dirname, '../fixtures/fails') const files = await fg('**/*.test.ts', { cwd: root, dot: true }) it.each(files)('should fail %s', async (file) => { - const { stderr } = await runVitest({ root }, [file]) + const { stderr } = await runVitest({ + root, + update: file === 'inline-snapshop-inside-loop-update-all.test.ts' ? true : undefined, + }, [file]) expect(stderr).toBeTruthy() const msg = String(stderr)