Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(snapshot): reject multiple toMatchInlineSnapshot updates at the same location #6332

Merged
merged 4 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions packages/snapshot/src/port/state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -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"`);
}
});
Original file line number Diff line number Diff line change
@@ -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.
Copy link
Member

@sheremet-va sheremet-va Aug 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we do something about this? this message implies that it's not fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is not ideal but I couldn't come up with a quick solution from what's available currently.
I think this is rather an edge case compared to OP's case, so we could postpone a proper fix later until someone comes across this. Btw, this behavior is same for current Vitest so that's unchanged. Jest also ends up with infinite re-run for this case.

Mentioning an idea for a proper fix, it would probably require keeping track of all toMatchInlineSnapshot call locations, not only failed ones. Currently parseErrorStacktrace is not used when toMatchInlineSnapshot succeeds, so I need to move up some code from SnapshotState._addSnapshot and also adjust states probably.

test("fail 2", () => {
for (const str of ["foo", "bar"]) {
expect(str).toMatchInlineSnapshot(`"bar"`);
}
});
7 changes: 7 additions & 0 deletions test/cli/test/__snapshots__/fails.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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"`;
Expand Down
5 changes: 4 additions & 1 deletion test/cli/test/fails.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down