From 17646ac7294e02200082bc40225d3158d5e3451b Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 18 Aug 2026 15:59:56 +0900 Subject: [PATCH] fix(snapshot): reject when `toMatchFileSnapshot` uses same snapshot file as `toMatchSnapshot` (#10978) Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com> Co-authored-by: OpenCode Co-authored-by: Vladimir --- docs/guide/snapshot.md | 4 +++ packages/snapshot/src/client.ts | 8 ++++++ test/e2e/snapshots/file.test.ts | 46 ++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/docs/guide/snapshot.md b/docs/guide/snapshot.md index df11944d2..69acc1076 100644 --- a/docs/guide/snapshot.md +++ b/docs/guide/snapshot.md @@ -108,6 +108,10 @@ it('render basic', async () => { It will compare with the content of `./test/basic.output.html`. And can be written back with the `--update` flag. +::: warning +Do not use a snapshot path managed by Vitest, such as `__snapshots__/basic.test.ts.snap`, with `toMatchFileSnapshot`. Choose a separate file path pattern for file snapshots. +::: + ## Visual Snapshots For visual regression testing of UI components and pages, Vitest provides built-in support through [browser mode](/guide/browser/) with the [`toMatchScreenshot()`](/api/browser/assertions#tomatchscreenshot) assertion: diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index 9a398cfbd..85aa7f3fd 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -135,6 +135,14 @@ export class SnapshotClient { } const snapshotState = this.getSnapshotState(filepath) + if (rawSnapshot?.file === snapshotState.snapshotPath) { + // note that this hard rejection is best-effort in a sense that, + // if `toMatchFileSnapshot` is called with a different test file's snapshot path, + // this check will not catch it. + throw new Error( + `File snapshot cannot use the same path as the test snapshot file: ${rawSnapshot.file}`, + ) + } const testName = [name, ...(message ? [message] : [])].join(' > ') // Probe first so we can mark as checked even on early return diff --git a/test/e2e/snapshots/file.test.ts b/test/e2e/snapshots/file.test.ts index 0aa5b246b..fa4d37f8e 100644 --- a/test/e2e/snapshots/file.test.ts +++ b/test/e2e/snapshots/file.test.ts @@ -1,6 +1,6 @@ import { join } from 'node:path' import { expect, test } from 'vitest' -import { editFile, runVitest } from '../../test-utils' +import { editFile, runInlineTests, runVitest } from '../../test-utils' test('white space sensitive', async () => { const root = join(import.meta.dirname, 'fixtures/file') @@ -26,3 +26,47 @@ test('white space sensitive', async () => { `) expect(vitest.exitCode).toBe(1) }) + +test('file snapshot cannot use the test snapshot path', async () => { + const result = await runInlineTests({ + 'basic.test.ts': ` +import { expect, test } from 'vitest' + +test('file snapshot', async () => { + await expect('content').toMatchFileSnapshot('__snapshots__/basic.test.ts.snap') +}) +`, + }) + + expect(result.errorTree()).toMatchInlineSnapshot(` + { + "basic.test.ts": { + "file snapshot": [ + "File snapshot cannot use the same path as the test snapshot file: /__snapshots__/basic.test.ts.snap", + ], + }, + } + `) +}) + +test('file snapshot can use another test filename', async () => { + const result = await runInlineTests({ + 'other.test.ts': ` +import { expect, test } from 'vitest' + +test('file snapshot', async () => { + await expect('content').toMatchFileSnapshot('__snapshots__/basic.test.ts.snap') +}) +`, + }, { update: true }) + + expect(result.stderr).toBe('') + expect(result.testTree()).toMatchInlineSnapshot(` + { + "other.test.ts": { + "file snapshot": "passed", + }, + } + `) + expect(result.fs.readFile('__snapshots__/basic.test.ts.snap')).toBe('content') +}) -- 2.51.2