From db634ea39271da590d64079b46a97184ccc63bfb Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 12:38:58 +0900 Subject: [PATCH 01/27] wip: rework snapshot state --- packages/snapshot/src/client.ts | 42 ++++++++++++++++++++- packages/snapshot/src/manager.ts | 1 + packages/snapshot/src/port/state.ts | 1 + packages/vitest/src/runtime/runners/test.ts | 34 ++++++++++------- 4 files changed, 64 insertions(+), 14 deletions(-) diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index cfa9508eb2d3..00a5e57fe8d9 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -50,8 +50,10 @@ export interface SnapshotClientOptions { } export class SnapshotClient { + // TODO: remove state filepath?: string name?: string + // TODO: do we need file map here? snapshotState: SnapshotState | undefined snapshotStateMap: Map = new Map() @@ -65,6 +67,7 @@ export class SnapshotClient { this.filepath = filepath this.name = name + // TODO: remove and make it explicit if (this.snapshotState?.testFilePath !== filepath) { await this.finishCurrentRun() @@ -78,8 +81,44 @@ export class SnapshotClient { } } + async setup( + filepath: string, + options: SnapshotStateOptions, + ): Promise { + if (this.snapshotStateMap.has(filepath)) { + throw new Error('already setup') + } + this.snapshotStateMap.set( + filepath, + await SnapshotState.create(filepath, options), + ) + } + + async finish(filepath: string): Promise { + const state = this.snapshotStateMap.get(filepath) + if (!state) { + throw new Error('missing setup') + } + const result = await state.pack() + this.snapshotStateMap.delete(filepath) + return result + } + + // TODO: by test id + skip(filepath: string, name: string): void { + const state = this.snapshotStateMap.get(filepath) + if (!state) { + throw new Error('missing setup') + } + state.markSnapshotsAsCheckedForTest(name) + } + getSnapshotState(filepath: string): SnapshotState { - return this.snapshotStateMap.get(filepath)! + const state = this.snapshotStateMap.get(filepath) + if (!state) { + throw new Error('snapshot state not initialized') + } + return state } clearTest(): void { @@ -91,6 +130,7 @@ export class SnapshotClient { this.snapshotState?.markSnapshotsAsCheckedForTest(name) } + // TODO: add test id assert(options: AssertOptions): void { const { filepath = this.filepath, diff --git a/packages/snapshot/src/manager.ts b/packages/snapshot/src/manager.ts index 5a4caa0029af..87ffe7a199da 100644 --- a/packages/snapshot/src/manager.ts +++ b/packages/snapshot/src/manager.ts @@ -23,6 +23,7 @@ export class SnapshotManager { addSnapshotResult(this.summary, result) } + // TODO: can remove in favor of SnapshotEnvironment.resolvePath? resolvePath(testPath: string): string { const resolver = this.options.resolveSnapshotPath || (() => { diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index d12f66746068..232a3c980040 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -50,6 +50,7 @@ export default class SnapshotState { private _counters: Map private _dirty: boolean private _updateSnapshot: SnapshotUpdateState + // TODO: key by test id. how to match with `initialData`? private _snapshotData: SnapshotData private _initialData: SnapshotData private _inlineSnapshots: Array diff --git a/packages/vitest/src/runtime/runners/test.ts b/packages/vitest/src/runtime/runners/test.ts index 642fd526c8f2..9286f4b6c37e 100644 --- a/packages/vitest/src/runtime/runners/test.ts +++ b/packages/vitest/src/runtime/runners/test.ts @@ -45,7 +45,7 @@ export class VitestTestRunner implements VitestRunner { } onBeforeRunFiles() { - this.snapshotClient.clear() + // this.snapshotClient.clear() } onAfterRunFiles() { @@ -62,21 +62,24 @@ export class VitestTestRunner implements VitestRunner { for (const test of getTests(suite)) { if (test.mode === 'skip') { const name = getNames(test).slice(1).join(' > ') - this.snapshotClient.skipTestSnapshots(name) + // this.snapshotClient.skipTestSnapshots(name) + this.snapshotClient.skip(suite.file.filepath, name) } } - const result = await this.snapshotClient.finishCurrentRun() - if (result) { - await rpc().snapshotSaved(result) - } + const result = await this.snapshotClient.finish(suite.file.filepath) + await rpc().snapshotSaved(result) + // const result = await this.snapshotClient.finishCurrentRun() + // if (result) { + // await rpc().snapshotSaved(result) + // } } this.workerState.current = suite.suite || suite.file } onAfterRunTask(test: Task) { - this.snapshotClient.clearTest() + // this.snapshotClient.clearTest() if (this.config.logHeapUsage && typeof process !== 'undefined') { test.result!.heap = process.memoryUsage().heapUsed @@ -110,13 +113,17 @@ export class VitestTestRunner implements VitestRunner { // initialize snapshot state before running file suite if (suite.mode !== 'skip' && 'filepath' in suite) { - // default "name" is irrelevant for Vitest since each snapshot assertion - // (e.g. `toMatchSnapshot`) specifies "filepath" / "name" pair explicitly - await this.snapshotClient.startCurrentRun( - (suite as File).filepath, - '__default_name_', + await this.snapshotClient.setup( + suite.file.filepath, this.workerState.config.snapshotOptions, ) + // // default "name" is irrelevant for Vitest since each snapshot assertion + // // (e.g. `toMatchSnapshot`) specifies "filepath" / "name" pair explicitly + // await this.snapshotClient.startCurrentRun( + // (suite as File).filepath, + // '__default_name_', + // this.workerState.config.snapshotOptions, + // ) } this.workerState.current = suite @@ -132,7 +139,8 @@ export class VitestTestRunner implements VitestRunner { expectedAssertionsNumberErrorGen: null, testPath: test.file.filepath, currentTestName: getTestName(test), - snapshotState: this.snapshotClient.snapshotState, + snapshotState: this.snapshotClient.getSnapshotState(test.file.filepath), + // snapshotState: this.snapshotClient.snapshotState, }, (globalThis as any)[GLOBAL_EXPECT], ) From 7cf11f5fbf27b264e9f7d97e3dfdf30c0014a482 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 12:46:05 +0900 Subject: [PATCH 02/27] wip: remove more --- packages/snapshot/src/client.ts | 85 +++++-------------- .../vitest/src/integrations/snapshot/chai.ts | 15 ++-- packages/vitest/src/runtime/runners/test.ts | 24 +----- 3 files changed, 29 insertions(+), 95 deletions(-) diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index 00a5e57fe8d9..1daaaa407061 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -34,8 +34,8 @@ export interface Context { interface AssertOptions { received: unknown - filepath?: string - name?: string + filepath: string + name: string message?: string isInline?: boolean properties?: object @@ -50,37 +50,10 @@ export interface SnapshotClientOptions { } export class SnapshotClient { - // TODO: remove state - filepath?: string - name?: string - // TODO: do we need file map here? - snapshotState: SnapshotState | undefined snapshotStateMap: Map = new Map() constructor(private options: SnapshotClientOptions = {}) {} - async startCurrentRun( - filepath: string, - name: string, - options: SnapshotStateOptions, - ): Promise { - this.filepath = filepath - this.name = name - - // TODO: remove and make it explicit - if (this.snapshotState?.testFilePath !== filepath) { - await this.finishCurrentRun() - - if (!this.getSnapshotState(filepath)) { - this.snapshotStateMap.set( - filepath, - await SnapshotState.create(filepath, options), - ) - } - this.snapshotState = this.getSnapshotState(filepath) - } - } - async setup( filepath: string, options: SnapshotStateOptions, @@ -95,24 +68,24 @@ export class SnapshotClient { } async finish(filepath: string): Promise { - const state = this.snapshotStateMap.get(filepath) - if (!state) { - throw new Error('missing setup') - } + const state = this.getSnapshotState(filepath) const result = await state.pack() this.snapshotStateMap.delete(filepath) return result } - // TODO: by test id - skip(filepath: string, name: string): void { - const state = this.snapshotStateMap.get(filepath) - if (!state) { - throw new Error('missing setup') - } + skipTest(filepath: string, name: string): void { + const state = this.getSnapshotState(filepath) state.markSnapshotsAsCheckedForTest(name) } + // TODO: can we use name instead of test.id? + clearTest(filepath: string, name: string): void { + const state = this.getSnapshotState(filepath) + state + name + } + getSnapshotState(filepath: string): SnapshotState { const state = this.snapshotStateMap.get(filepath) if (!state) { @@ -121,20 +94,10 @@ export class SnapshotClient { return state } - clearTest(): void { - this.filepath = undefined - this.name = undefined - } - - skipTestSnapshots(name: string): void { - this.snapshotState?.markSnapshotsAsCheckedForTest(name) - } - - // TODO: add test id assert(options: AssertOptions): void { const { - filepath = this.filepath, - name = this.name, + filepath, + name, message, isInline = false, properties, @@ -149,6 +112,8 @@ export class SnapshotClient { throw new Error('Snapshot cannot be used outside of test') } + const snapshotState = this.getSnapshotState(filepath) + if (typeof properties === 'object') { if (typeof received !== 'object' || !received) { throw new Error( @@ -162,7 +127,7 @@ export class SnapshotClient { if (!pass) { throw createMismatchError( 'Snapshot properties mismatched', - this.snapshotState?.expand, + snapshotState.expand, received, properties, ) @@ -179,8 +144,6 @@ export class SnapshotClient { const testName = [name, ...(message ? [message] : [])].join(' > ') - const snapshotState = this.getSnapshotState(filepath) - const { actual, expected, key, pass } = snapshotState.match({ testName, received, @@ -193,7 +156,7 @@ export class SnapshotClient { if (!pass) { throw createMismatchError( `Snapshot \`${key || 'unknown'}\` mismatched`, - this.snapshotState?.expand, + snapshotState.expand, actual?.trim(), expected?.trim(), ) @@ -205,7 +168,7 @@ export class SnapshotClient { throw new Error('Raw snapshot is required') } - const { filepath = this.filepath, rawSnapshot } = options + const { filepath, rawSnapshot } = options if (rawSnapshot.content == null) { if (!filepath) { @@ -229,16 +192,6 @@ export class SnapshotClient { return this.assert(options) } - async finishCurrentRun(): Promise { - if (!this.snapshotState) { - return null - } - const result = await this.snapshotState.pack() - - this.snapshotState = undefined - return result - } - clear(): void { this.snapshotStateMap.clear() } diff --git a/packages/vitest/src/integrations/snapshot/chai.ts b/packages/vitest/src/integrations/snapshot/chai.ts index 3837d2b88d20..b7e89077bb69 100644 --- a/packages/vitest/src/integrations/snapshot/chai.ts +++ b/packages/vitest/src/integrations/snapshot/chai.ts @@ -44,10 +44,7 @@ function getError(expected: () => void | Error, promise: string | undefined) { throw new Error('snapshot function didn\'t throw') } -function getTestNames(test?: Test) { - if (!test) { - return {} - } +function getTestNames(test: Test) { return { filepath: test.file.filepath, name: getNames(test).slice(1).join(' > '), @@ -69,7 +66,7 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { throw new Error(`${key} cannot be used with "not"`) } const expected = utils.flag(this, 'object') - const test = utils.flag(this, 'vitest-test') + const test: Test = utils.flag(this, 'vitest-test') if (typeof properties === 'string' && typeof message === 'undefined') { message = properties properties = undefined @@ -96,7 +93,7 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { throw new Error('toMatchFileSnapshot cannot be used with "not"') } const expected = utils.flag(this, 'object') - const test = utils.flag(this, 'vitest-test') as Test + const test: Test = utils.flag(this, 'vitest-test') const errorMessage = utils.flag(this, 'message') const promise = getSnapshotClient().assertRaw({ @@ -127,7 +124,7 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { if (isNot) { throw new Error('toMatchInlineSnapshot cannot be used with "not"') } - const test = utils.flag(this, 'vitest-test') + const test: Test = utils.flag(this, 'vitest-test') const isInsideEach = test && (test.each || test.suite?.each) if (isInsideEach) { throw new Error( @@ -169,7 +166,7 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { ) } const expected = utils.flag(this, 'object') - const test = utils.flag(this, 'vitest-test') + const test: Test = utils.flag(this, 'vitest-test') const promise = utils.flag(this, 'promise') as string | undefined const errorMessage = utils.flag(this, 'message') getSnapshotClient().assert({ @@ -194,7 +191,7 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { 'toThrowErrorMatchingInlineSnapshot cannot be used with "not"', ) } - const test = utils.flag(this, 'vitest-test') + const test: Test = utils.flag(this, 'vitest-test') const isInsideEach = test && (test.each || test.suite?.each) if (isInsideEach) { throw new Error( diff --git a/packages/vitest/src/runtime/runners/test.ts b/packages/vitest/src/runtime/runners/test.ts index 9286f4b6c37e..d9a96d6cea53 100644 --- a/packages/vitest/src/runtime/runners/test.ts +++ b/packages/vitest/src/runtime/runners/test.ts @@ -44,11 +44,8 @@ export class VitestTestRunner implements VitestRunner { this.workerState.current = file } - onBeforeRunFiles() { - // this.snapshotClient.clear() - } - onAfterRunFiles() { + this.snapshotClient.clear() this.workerState.current = undefined } @@ -62,25 +59,19 @@ export class VitestTestRunner implements VitestRunner { for (const test of getTests(suite)) { if (test.mode === 'skip') { const name = getNames(test).slice(1).join(' > ') - // this.snapshotClient.skipTestSnapshots(name) - this.snapshotClient.skip(suite.file.filepath, name) + // TODO: skip by test.id + this.snapshotClient.skipTest(suite.file.filepath, name) } } const result = await this.snapshotClient.finish(suite.file.filepath) await rpc().snapshotSaved(result) - // const result = await this.snapshotClient.finishCurrentRun() - // if (result) { - // await rpc().snapshotSaved(result) - // } } this.workerState.current = suite.suite || suite.file } onAfterRunTask(test: Task) { - // this.snapshotClient.clearTest() - if (this.config.logHeapUsage && typeof process !== 'undefined') { test.result!.heap = process.memoryUsage().heapUsed } @@ -117,19 +108,13 @@ export class VitestTestRunner implements VitestRunner { suite.file.filepath, this.workerState.config.snapshotOptions, ) - // // default "name" is irrelevant for Vitest since each snapshot assertion - // // (e.g. `toMatchSnapshot`) specifies "filepath" / "name" pair explicitly - // await this.snapshotClient.startCurrentRun( - // (suite as File).filepath, - // '__default_name_', - // this.workerState.config.snapshotOptions, - // ) } this.workerState.current = suite } onBeforeTryTask(test: Task) { + this.snapshotClient.clearTest(test.file.filepath, test.name) setState( { assertionCalls: 0, @@ -140,7 +125,6 @@ export class VitestTestRunner implements VitestRunner { testPath: test.file.filepath, currentTestName: getTestName(test), snapshotState: this.snapshotClient.getSnapshotState(test.file.filepath), - // snapshotState: this.snapshotClient.snapshotState, }, (globalThis as any)[GLOBAL_EXPECT], ) From 047badf842ce20e23d9e598e75641ce1b53ad768 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 14:14:22 +0900 Subject: [PATCH 03/27] wip: test --- test/core/test/repro.test.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 test/core/test/repro.test.ts diff --git a/test/core/test/repro.test.ts b/test/core/test/repro.test.ts new file mode 100644 index 000000000000..e5d9f09f9c57 --- /dev/null +++ b/test/core/test/repro.test.ts @@ -0,0 +1,20 @@ +import { expect, test } from 'vitest' + +test('repro', () => { + expect +}) + +// test('inline repeats', { repeats: 1 }, () => { +// expect('foo').toMatchInlineSnapshot(`"foo"`); +// }); + +// test('inline retry', { retry: 1 }, (ctx) => { +// expect('foo').toMatchInlineSnapshot(`"foo"`) +// if (ctx.task.result?.retryCount === 0) { +// throw new Error('boom') +// } +// }) + +// test('foo', { retry: 1 }, () => { +// expect('foo').toMatchSnapshot() +// }); From 0aea548fe31ae22626c0dac36c89c0553f9b9cc5 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 14:27:40 +0900 Subject: [PATCH 04/27] wip: clear inline snapshots --- packages/snapshot/src/client.ts | 5 ++--- packages/snapshot/src/port/inlineSnapshot.ts | 1 + packages/snapshot/src/port/state.ts | 14 +++++++++++--- test/core/test/repro.test.ts | 20 ++++++++++---------- 4 files changed, 24 insertions(+), 16 deletions(-) diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index 1daaaa407061..755c93da53cf 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -79,11 +79,10 @@ export class SnapshotClient { state.markSnapshotsAsCheckedForTest(name) } - // TODO: can we use name instead of test.id? + // TODO: use test.id to handle multiple tests with same name clearTest(filepath: string, name: string): void { const state = this.getSnapshotState(filepath) - state - name + state.clearTest(name) } getSnapshotState(filepath: string): SnapshotState { diff --git a/packages/snapshot/src/port/inlineSnapshot.ts b/packages/snapshot/src/port/inlineSnapshot.ts index 2f252017fa88..c8474448b4ee 100644 --- a/packages/snapshot/src/port/inlineSnapshot.ts +++ b/packages/snapshot/src/port/inlineSnapshot.ts @@ -10,6 +10,7 @@ import { export interface InlineSnapshot { snapshot: string file: string + testName: string line: number column: number } diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 232a3c980040..0f80174bedfb 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -54,7 +54,7 @@ export default class SnapshotState { private _snapshotData: SnapshotData private _initialData: SnapshotData private _inlineSnapshots: Array - private _inlineSnapshotStacks: Array + private _inlineSnapshotStacks: Array private _rawSnapshots: Array private _uncheckedKeys: Set private _snapshotFormat: PrettyFormatOptions @@ -119,6 +119,11 @@ export default class SnapshotState { }) } + clearTest(testName: string): void { + this._inlineSnapshots = this._inlineSnapshots.filter(s => s.testName !== testName) + this._inlineSnapshotStacks = this._inlineSnapshotStacks.filter(s => s.testName !== testName) + } + protected _inferInlineSnapshotStack(stacks: ParsedStack[]): ParsedStack | null { // if called inside resolves/rejects, stacktrace is different const promiseIndex = stacks.findIndex(i => @@ -139,12 +144,13 @@ export default class SnapshotState { private _addSnapshot( key: string, receivedSerialized: string, - options: { rawSnapshot?: RawSnapshotInfo; stack?: ParsedStack }, + options: { rawSnapshot?: RawSnapshotInfo; stack?: ParsedStack; testName: string }, ): void { this._dirty = true if (options.stack) { this._inlineSnapshots.push({ snapshot: receivedSerialized, + testName: options.testName, ...options.stack, }) } @@ -321,7 +327,7 @@ export default class SnapshotState { this._inlineSnapshots = this._inlineSnapshots.filter(s => !(s.file === stack!.file && s.line === stack!.line && s.column === stack!.column)) throw new Error('toMatchInlineSnapshot cannot be called multiple times at the same location.') } - this._inlineSnapshotStacks.push(stack) + this._inlineSnapshotStacks.push({ ...stack, testName }) } // These are the conditions on when to write snapshots: @@ -347,6 +353,7 @@ export default class SnapshotState { this._addSnapshot(key, receivedSerialized, { stack, + testName, rawSnapshot, }) } @@ -357,6 +364,7 @@ export default class SnapshotState { else { this._addSnapshot(key, receivedSerialized, { stack, + testName, rawSnapshot, }) this.added++ diff --git a/test/core/test/repro.test.ts b/test/core/test/repro.test.ts index e5d9f09f9c57..6cec0536c6f9 100644 --- a/test/core/test/repro.test.ts +++ b/test/core/test/repro.test.ts @@ -1,19 +1,19 @@ import { expect, test } from 'vitest' test('repro', () => { - expect + expect(0).toBe(0) }) -// test('inline repeats', { repeats: 1 }, () => { -// expect('foo').toMatchInlineSnapshot(`"foo"`); -// }); +test('inline repeats', { repeats: 1 }, () => { + expect('foo').toMatchInlineSnapshot(`"foo"`) +}) -// test('inline retry', { retry: 1 }, (ctx) => { -// expect('foo').toMatchInlineSnapshot(`"foo"`) -// if (ctx.task.result?.retryCount === 0) { -// throw new Error('boom') -// } -// }) +test('inline retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchInlineSnapshot(`"foo"`) + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) // test('foo', { retry: 1 }, () => { // expect('foo').toMatchSnapshot() From 4c835edca783f9157765c7b5cd1c7c1a6087cf6e Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 15:04:36 +0900 Subject: [PATCH 05/27] wip: clear file --- packages/snapshot/src/port/state.ts | 11 ++++++ .../test/__snapshots__/repro.test.ts.snap | 13 +++++++ test/core/test/repro.test.ts | 35 +++++++++++++++++-- 3 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 test/core/test/__snapshots__/repro.test.ts.snap diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 0f80174bedfb..85ba10fedefa 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -122,6 +122,17 @@ export default class SnapshotState { clearTest(testName: string): void { this._inlineSnapshots = this._inlineSnapshots.filter(s => s.testName !== testName) this._inlineSnapshotStacks = this._inlineSnapshotStacks.filter(s => s.testName !== testName) + if (this._counters.has(testName)) { + let counter = this._counters.get(testName)! + for (const key in this._snapshotData) { + if (keyToTestName(key) === testName) { + counter-- + this._snapshotData[key] = this._initialData[key] + this._uncheckedKeys.add(key) + } + } + this._counters.set(testName, counter) + } } protected _inferInlineSnapshotStack(stacks: ParsedStack[]): ParsedStack | null { diff --git a/test/core/test/__snapshots__/repro.test.ts.snap b/test/core/test/__snapshots__/repro.test.ts.snap new file mode 100644 index 000000000000..a368365c6eda --- /dev/null +++ b/test/core/test/__snapshots__/repro.test.ts.snap @@ -0,0 +1,13 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`file repeats 1`] = `"foo"`; + +exports[`file repeats many 1`] = `"foo"`; + +exports[`file repeats many 2`] = `"bar"`; + +exports[`file retry 1`] = `"foo"`; + +exports[`file retry many 1`] = `"foo"`; + +exports[`file retry many 2`] = `"bar"`; diff --git a/test/core/test/repro.test.ts b/test/core/test/repro.test.ts index 6cec0536c6f9..fbb5e03ec9c0 100644 --- a/test/core/test/repro.test.ts +++ b/test/core/test/repro.test.ts @@ -15,6 +15,35 @@ test('inline retry', { retry: 1 }, (ctx) => { } }) -// test('foo', { retry: 1 }, () => { -// expect('foo').toMatchSnapshot() -// }); +test('file repeats', { repeats: 1 }, () => { + expect('foo').toMatchSnapshot() +}) + +test('file retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file repeats many', { repeats: 1 }, () => { + expect('foo').toMatchSnapshot() + expect('bar').toMatchSnapshot() +}) + +test('file retry many', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + expect('bar').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +// TODO +test.skip('file retry partial', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } + expect('bar').toMatchSnapshot() +}) From 99abe4c158a0c1246a6bdad297b95ff4a37b99d6 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 15:47:26 +0900 Subject: [PATCH 06/27] fix: reset to _initialData --- packages/snapshot/src/port/state.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 85ba10fedefa..795dfb2cd51e 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -75,8 +75,8 @@ export default class SnapshotState { ) { const { data, dirty } = getSnapshotData(snapshotContent, options) this._fileExists = snapshotContent != null // TODO: update on watch? - this._initialData = data - this._snapshotData = data + this._initialData = { ...data } + this._snapshotData = { ...data } this._dirty = dirty this._inlineSnapshots = [] this._inlineSnapshotStacks = [] @@ -120,6 +120,9 @@ export default class SnapshotState { } clearTest(testName: string): void { + // TODO: key by test.id + // TODO: reset this.added, matched, etc.. + this._inlineSnapshots = this._inlineSnapshots.filter(s => s.testName !== testName) this._inlineSnapshotStacks = this._inlineSnapshotStacks.filter(s => s.testName !== testName) if (this._counters.has(testName)) { @@ -176,8 +179,9 @@ export default class SnapshotState { } } + // TODO: not used. remove it clear(): void { - this._snapshotData = this._initialData + this._snapshotData = { ...this._initialData } // this._inlineSnapshots = [] this._counters = new Map() this.added = 0 From c17461564b4e16aad9c6e9d01418761ba95629f3 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 16:00:35 +0900 Subject: [PATCH 07/27] fix: fix retry partial --- packages/snapshot/src/port/state.ts | 13 ++++++------- test/core/test/__snapshots__/repro.test.ts.snap | 4 ++++ test/core/test/repro.test.ts | 3 +-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 795dfb2cd51e..35e2ee6de606 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -120,21 +120,20 @@ export default class SnapshotState { } clearTest(testName: string): void { - // TODO: key by test.id + // TODO: key by test.id (handle multiple tests with same title) // TODO: reset this.added, matched, etc.. this._inlineSnapshots = this._inlineSnapshots.filter(s => s.testName !== testName) this._inlineSnapshotStacks = this._inlineSnapshotStacks.filter(s => s.testName !== testName) if (this._counters.has(testName)) { - let counter = this._counters.get(testName)! - for (const key in this._snapshotData) { - if (keyToTestName(key) === testName) { - counter-- + const counter = this._counters.get(testName)! + for (let i = 1; i <= counter; i++) { + const key = testNameToKey(testName, counter) + if (key in this._snapshotData || key in this._initialData) { this._snapshotData[key] = this._initialData[key] - this._uncheckedKeys.add(key) } } - this._counters.set(testName, counter) + this._counters.delete(testName) } } diff --git a/test/core/test/__snapshots__/repro.test.ts.snap b/test/core/test/__snapshots__/repro.test.ts.snap index a368365c6eda..55fb93d2deec 100644 --- a/test/core/test/__snapshots__/repro.test.ts.snap +++ b/test/core/test/__snapshots__/repro.test.ts.snap @@ -11,3 +11,7 @@ exports[`file retry 1`] = `"foo"`; exports[`file retry many 1`] = `"foo"`; exports[`file retry many 2`] = `"bar"`; + +exports[`file retry partial 1`] = `"foo"`; + +exports[`file retry partial 2`] = `"bar"`; diff --git a/test/core/test/repro.test.ts b/test/core/test/repro.test.ts index fbb5e03ec9c0..ec0c8149fefe 100644 --- a/test/core/test/repro.test.ts +++ b/test/core/test/repro.test.ts @@ -39,8 +39,7 @@ test('file retry many', { retry: 1 }, (ctx) => { } }) -// TODO -test.skip('file retry partial', { retry: 1 }, (ctx) => { +test('file retry partial', { retry: 1 }, (ctx) => { expect('foo').toMatchSnapshot() if (ctx.task.result?.retryCount === 0) { throw new Error('boom') From 3dcf25d33b0f87138a490fce70303323753031a4 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 17:51:17 +0900 Subject: [PATCH 08/27] chore: cleanup more --- packages/snapshot/src/port/state.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 35e2ee6de606..8c3c4323474a 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -178,18 +178,6 @@ export default class SnapshotState { } } - // TODO: not used. remove it - clear(): void { - this._snapshotData = { ...this._initialData } - // this._inlineSnapshots = [] - this._counters = new Map() - this.added = 0 - this.matched = 0 - this.unmatched = 0 - this.updated = 0 - this._dirty = false - } - async save(): Promise { const hasExternalSnapshots = Object.keys(this._snapshotData).length const hasInlineSnapshots = this._inlineSnapshots.length From 8817ac558a34fd9d893884aa7ae80952152c2e80 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 19:01:09 +0900 Subject: [PATCH 09/27] test: more --- ...repro.test.ts.snap => repro1.test.ts.snap} | 0 .../test/{repro.test.ts => repro1.test.ts} | 0 test/core/test/repro2.test.ts | 21 +++++++++++++++++++ 3 files changed, 21 insertions(+) rename test/core/test/__snapshots__/{repro.test.ts.snap => repro1.test.ts.snap} (100%) rename test/core/test/{repro.test.ts => repro1.test.ts} (100%) create mode 100644 test/core/test/repro2.test.ts diff --git a/test/core/test/__snapshots__/repro.test.ts.snap b/test/core/test/__snapshots__/repro1.test.ts.snap similarity index 100% rename from test/core/test/__snapshots__/repro.test.ts.snap rename to test/core/test/__snapshots__/repro1.test.ts.snap diff --git a/test/core/test/repro.test.ts b/test/core/test/repro1.test.ts similarity index 100% rename from test/core/test/repro.test.ts rename to test/core/test/repro1.test.ts diff --git a/test/core/test/repro2.test.ts b/test/core/test/repro2.test.ts new file mode 100644 index 000000000000..7956a82fea03 --- /dev/null +++ b/test/core/test/repro2.test.ts @@ -0,0 +1,21 @@ +import { expect, test } from 'vitest' + +test('repro', () => { + expect(0).toBe(0) +}) + +test('inline same title', () => { + expect('foo').toMatchInlineSnapshot(`"foo"`) +}) + +test('inline same title', () => { + expect('bar').toMatchInlineSnapshot(`"bar"`) +}) + +test('file same title', () => { + expect('foo').toMatchSnapshot() +}) + +test('file same title', () => { + expect('bar').toMatchSnapshot() +}) From c5e45272e660d7e6bee5ec6d464992fb0a250dd3 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 19:27:09 +0900 Subject: [PATCH 10/27] wip: _testIdToKeys state --- packages/snapshot/src/client.ts | 12 +++-- packages/snapshot/src/port/inlineSnapshot.ts | 2 +- packages/snapshot/src/port/state.ts | 53 +++++++++++-------- packages/snapshot/src/types/index.ts | 1 + .../vitest/src/integrations/snapshot/chai.ts | 1 + packages/vitest/src/runtime/runners/test.ts | 8 ++- 6 files changed, 43 insertions(+), 34 deletions(-) diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index 755c93da53cf..f4b9bb83c2e2 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -36,6 +36,7 @@ interface AssertOptions { received: unknown filepath: string name: string + testId: string message?: string isInline?: boolean properties?: object @@ -74,15 +75,14 @@ export class SnapshotClient { return result } - skipTest(filepath: string, name: string): void { + skipTest(filepath: string, testId: string): void { const state = this.getSnapshotState(filepath) - state.markSnapshotsAsCheckedForTest(name) + state.markSnapshotsAsCheckedForTest(testId) } - // TODO: use test.id to handle multiple tests with same name - clearTest(filepath: string, name: string): void { + clearTest(filepath: string, testId: string): void { const state = this.getSnapshotState(filepath) - state.clearTest(name) + state.clearTest(testId) } getSnapshotState(filepath: string): SnapshotState { @@ -97,6 +97,7 @@ export class SnapshotClient { const { filepath, name, + testId, message, isInline = false, properties, @@ -144,6 +145,7 @@ export class SnapshotClient { const testName = [name, ...(message ? [message] : [])].join(' > ') const { actual, expected, key, pass } = snapshotState.match({ + testId, testName, received, isInline, diff --git a/packages/snapshot/src/port/inlineSnapshot.ts b/packages/snapshot/src/port/inlineSnapshot.ts index c8474448b4ee..09f059661655 100644 --- a/packages/snapshot/src/port/inlineSnapshot.ts +++ b/packages/snapshot/src/port/inlineSnapshot.ts @@ -9,8 +9,8 @@ import { export interface InlineSnapshot { snapshot: string + testId: string file: string - testName: string line: number column: number } diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 8c3c4323474a..864acfc9868e 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -50,11 +50,11 @@ export default class SnapshotState { private _counters: Map private _dirty: boolean private _updateSnapshot: SnapshotUpdateState - // TODO: key by test id. how to match with `initialData`? private _snapshotData: SnapshotData private _initialData: SnapshotData private _inlineSnapshots: Array - private _inlineSnapshotStacks: Array + private _inlineSnapshotStacks: Array + private _testIdToKeys = new Map() private _rawSnapshots: Array private _uncheckedKeys: Set private _snapshotFormat: PrettyFormatOptions @@ -111,30 +111,34 @@ export default class SnapshotState { return this._environment } - markSnapshotsAsCheckedForTest(testName: string): void { - this._uncheckedKeys.forEach((uncheckedKey) => { - if (keyToTestName(uncheckedKey) === testName) { - this._uncheckedKeys.delete(uncheckedKey) - } - }) + markSnapshotsAsCheckedForTest(testId: string): void { + for (const key of this._testIdToKeys.get(testId) ?? []) { + this._uncheckedKeys.delete(key) + } } - clearTest(testName: string): void { - // TODO: key by test.id (handle multiple tests with same title) - // TODO: reset this.added, matched, etc.. + clearTest(testId: string): void { + // TODO: reset stats: added, matched, etc.. + + this._inlineSnapshots = this._inlineSnapshots.filter(s => s.testId !== testId) + this._inlineSnapshotStacks = this._inlineSnapshotStacks.filter(s => s.testId !== testId) - this._inlineSnapshots = this._inlineSnapshots.filter(s => s.testName !== testName) - this._inlineSnapshotStacks = this._inlineSnapshotStacks.filter(s => s.testName !== testName) - if (this._counters.has(testName)) { - const counter = this._counters.get(testName)! - for (let i = 1; i <= counter; i++) { - const key = testNameToKey(testName, counter) + for (const key of this._testIdToKeys.get(testId) ?? []) { + const name = keyToTestName(key) + const counter = this._counters.get(name) + if (typeof counter !== 'undefined') { if (key in this._snapshotData || key in this._initialData) { this._snapshotData[key] = this._initialData[key] } + if (counter > 0) { + this._counters.set(name, counter - 1) + } + else { + this._counters.delete(name) + } } - this._counters.delete(testName) } + this._testIdToKeys.delete(testId) } protected _inferInlineSnapshotStack(stacks: ParsedStack[]): ParsedStack | null { @@ -157,13 +161,13 @@ export default class SnapshotState { private _addSnapshot( key: string, receivedSerialized: string, - options: { rawSnapshot?: RawSnapshotInfo; stack?: ParsedStack; testName: string }, + options: { rawSnapshot?: RawSnapshotInfo; stack?: ParsedStack; testId: string }, ): void { this._dirty = true if (options.stack) { this._inlineSnapshots.push({ snapshot: receivedSerialized, - testName: options.testName, + testId: options.testId, ...options.stack, }) } @@ -237,6 +241,7 @@ export default class SnapshotState { } match({ + testId, testName, received, key, @@ -251,6 +256,8 @@ export default class SnapshotState { if (!key) { key = testNameToKey(testName, count) } + this._testIdToKeys.set(testId, (this._testIdToKeys.get(key) ?? [])) + this._testIdToKeys.get(testId)?.push(key) // Do not mark the snapshot as "checked" if the snapshot is inline and // there's an external snapshot. This way the external snapshot can be @@ -329,7 +336,7 @@ export default class SnapshotState { this._inlineSnapshots = this._inlineSnapshots.filter(s => !(s.file === stack!.file && s.line === stack!.line && s.column === stack!.column)) throw new Error('toMatchInlineSnapshot cannot be called multiple times at the same location.') } - this._inlineSnapshotStacks.push({ ...stack, testName }) + this._inlineSnapshotStacks.push({ ...stack, testId }) } // These are the conditions on when to write snapshots: @@ -355,7 +362,7 @@ export default class SnapshotState { this._addSnapshot(key, receivedSerialized, { stack, - testName, + testId, rawSnapshot, }) } @@ -366,7 +373,7 @@ export default class SnapshotState { else { this._addSnapshot(key, receivedSerialized, { stack, - testName, + testId, rawSnapshot, }) this.added++ diff --git a/packages/snapshot/src/types/index.ts b/packages/snapshot/src/types/index.ts index d1bf8af4fac8..a106f9a24741 100644 --- a/packages/snapshot/src/types/index.ts +++ b/packages/snapshot/src/types/index.ts @@ -24,6 +24,7 @@ export interface SnapshotStateOptions { } export interface SnapshotMatchOptions { + testId: string testName: string received: unknown key?: string diff --git a/packages/vitest/src/integrations/snapshot/chai.ts b/packages/vitest/src/integrations/snapshot/chai.ts index b7e89077bb69..9cd96fc2ba1b 100644 --- a/packages/vitest/src/integrations/snapshot/chai.ts +++ b/packages/vitest/src/integrations/snapshot/chai.ts @@ -48,6 +48,7 @@ function getTestNames(test: Test) { return { filepath: test.file.filepath, name: getNames(test).slice(1).join(' > '), + testId: test.id, } } diff --git a/packages/vitest/src/runtime/runners/test.ts b/packages/vitest/src/runtime/runners/test.ts index d9a96d6cea53..e55df4b6be75 100644 --- a/packages/vitest/src/runtime/runners/test.ts +++ b/packages/vitest/src/runtime/runners/test.ts @@ -14,7 +14,7 @@ import type { import type { SerializedConfig } from '../config' import type { VitestExecutor } from '../execute' import { getState, GLOBAL_EXPECT, setState } from '@vitest/expect' -import { getNames, getTestName, getTests } from '@vitest/runner/utils' +import { getTestName, getTests } from '@vitest/runner/utils' import { createExpect } from '../../integrations/chai/index' import { getSnapshotClient } from '../../integrations/snapshot/chai' import { vi } from '../../integrations/vi' @@ -58,9 +58,7 @@ export class VitestTestRunner implements VitestRunner { // mark snapshots in skipped tests as not obsolete for (const test of getTests(suite)) { if (test.mode === 'skip') { - const name = getNames(test).slice(1).join(' > ') - // TODO: skip by test.id - this.snapshotClient.skipTest(suite.file.filepath, name) + this.snapshotClient.skipTest(suite.file.filepath, test.id) } } @@ -114,7 +112,7 @@ export class VitestTestRunner implements VitestRunner { } onBeforeTryTask(test: Task) { - this.snapshotClient.clearTest(test.file.filepath, test.name) + this.snapshotClient.clearTest(test.file.filepath, test.id) setState( { assertionCalls: 0, From 558ba71733fb368a2d8baea7e2dff87d29601534 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 19:31:17 +0900 Subject: [PATCH 11/27] test: more --- test/core/test/__snapshots__/repro2.test.ts.snap | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 test/core/test/__snapshots__/repro2.test.ts.snap diff --git a/test/core/test/__snapshots__/repro2.test.ts.snap b/test/core/test/__snapshots__/repro2.test.ts.snap new file mode 100644 index 000000000000..c1acf6864dfd --- /dev/null +++ b/test/core/test/__snapshots__/repro2.test.ts.snap @@ -0,0 +1,5 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`file same title 1`] = `"foo"`; + +exports[`file same title 2`] = `"bar"`; From 5b1182b16c2ceb43d54bd6fdcc3deda606c5f60b Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 19:37:17 +0900 Subject: [PATCH 12/27] wip: _testIdToKeys --- packages/snapshot/src/port/state.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 864acfc9868e..af82661249b9 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -112,6 +112,7 @@ export default class SnapshotState { } markSnapshotsAsCheckedForTest(testId: string): void { + // TODO: broken... for skipped test, we don't even have _testIdToKeys for (const key of this._testIdToKeys.get(testId) ?? []) { this._uncheckedKeys.delete(key) } @@ -256,7 +257,7 @@ export default class SnapshotState { if (!key) { key = testNameToKey(testName, count) } - this._testIdToKeys.set(testId, (this._testIdToKeys.get(key) ?? [])) + this._testIdToKeys.set(testId, (this._testIdToKeys.get(testId) ?? [])) this._testIdToKeys.get(testId)?.push(key) // Do not mark the snapshot as "checked" if the snapshot is inline and From 940dbb3f8ebc2c1ecd34dd0590ea1238e5657fd0 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Wed, 30 Oct 2024 19:39:27 +0900 Subject: [PATCH 13/27] fix: fix markSnapshotsAsCheckedForTest --- packages/snapshot/src/client.ts | 4 ++-- packages/snapshot/src/port/state.ts | 11 ++++++----- packages/vitest/src/runtime/runners/test.ts | 5 +++-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index f4b9bb83c2e2..b15bee07baea 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -75,9 +75,9 @@ export class SnapshotClient { return result } - skipTest(filepath: string, testId: string): void { + skipTest(filepath: string, testName: string): void { const state = this.getSnapshotState(filepath) - state.markSnapshotsAsCheckedForTest(testId) + state.markSnapshotsAsCheckedForTest(testName) } clearTest(filepath: string, testId: string): void { diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index af82661249b9..10dcb89469d7 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -111,11 +111,12 @@ export default class SnapshotState { return this._environment } - markSnapshotsAsCheckedForTest(testId: string): void { - // TODO: broken... for skipped test, we don't even have _testIdToKeys - for (const key of this._testIdToKeys.get(testId) ?? []) { - this._uncheckedKeys.delete(key) - } + markSnapshotsAsCheckedForTest(testName: string): void { + this._uncheckedKeys.forEach((uncheckedKey) => { + if (keyToTestName(uncheckedKey) === testName) { + this._uncheckedKeys.delete(uncheckedKey) + } + }) } clearTest(testId: string): void { diff --git a/packages/vitest/src/runtime/runners/test.ts b/packages/vitest/src/runtime/runners/test.ts index e55df4b6be75..fd2a931133af 100644 --- a/packages/vitest/src/runtime/runners/test.ts +++ b/packages/vitest/src/runtime/runners/test.ts @@ -14,7 +14,7 @@ import type { import type { SerializedConfig } from '../config' import type { VitestExecutor } from '../execute' import { getState, GLOBAL_EXPECT, setState } from '@vitest/expect' -import { getTestName, getTests } from '@vitest/runner/utils' +import { getNames, getTestName, getTests } from '@vitest/runner/utils' import { createExpect } from '../../integrations/chai/index' import { getSnapshotClient } from '../../integrations/snapshot/chai' import { vi } from '../../integrations/vi' @@ -58,7 +58,8 @@ export class VitestTestRunner implements VitestRunner { // mark snapshots in skipped tests as not obsolete for (const test of getTests(suite)) { if (test.mode === 'skip') { - this.snapshotClient.skipTest(suite.file.filepath, test.id) + const name = getNames(test).slice(1).join(' > ') + this.snapshotClient.skipTest(suite.file.filepath, name) } } From 80accd65524f6acb13fc158d567c10606b13e3df Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 11:27:34 +0900 Subject: [PATCH 14/27] refactor: minor --- packages/snapshot/src/client.ts | 1 + packages/snapshot/src/port/state.ts | 9 +++++---- packages/snapshot/src/port/utils.ts | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index b15bee07baea..32279959a0d8 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -50,6 +50,7 @@ export interface SnapshotClientOptions { isEqual?: (received: unknown, expected: unknown) => boolean } +// TODO: probably we don't need much refactoring here yet. separate refactoring export class SnapshotClient { snapshotStateMap: Map = new Map() diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 10dcb89469d7..4c975155dfb9 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -23,6 +23,7 @@ import { saveRawSnapshots } from './rawSnapshot' import { addExtraLineBreaks, + DefaultMap, getSnapshotData, keyToTestName, normalizeNewlines, @@ -54,7 +55,7 @@ export default class SnapshotState { private _initialData: SnapshotData private _inlineSnapshots: Array private _inlineSnapshotStacks: Array - private _testIdToKeys = new Map() + private _testIdToKeys = new DefaultMap(() => []) private _rawSnapshots: Array private _uncheckedKeys: Set private _snapshotFormat: PrettyFormatOptions @@ -125,7 +126,7 @@ export default class SnapshotState { this._inlineSnapshots = this._inlineSnapshots.filter(s => s.testId !== testId) this._inlineSnapshotStacks = this._inlineSnapshotStacks.filter(s => s.testId !== testId) - for (const key of this._testIdToKeys.get(testId) ?? []) { + for (const key of this._testIdToKeys.get(testId)) { const name = keyToTestName(key) const counter = this._counters.get(name) if (typeof counter !== 'undefined') { @@ -252,14 +253,14 @@ export default class SnapshotState { error, rawSnapshot, }: SnapshotMatchOptions): SnapshotReturnOptions { + // this also increments counter for inline snapshots. maybe we don't need to? this._counters.set(testName, (this._counters.get(testName) || 0) + 1) const count = Number(this._counters.get(testName)) if (!key) { key = testNameToKey(testName, count) } - this._testIdToKeys.set(testId, (this._testIdToKeys.get(testId) ?? [])) - this._testIdToKeys.get(testId)?.push(key) + this._testIdToKeys.get(testId).push(key) // Do not mark the snapshot as "checked" if the snapshot is inline and // there's an external snapshot. This way the external snapshot can be diff --git a/packages/snapshot/src/port/utils.ts b/packages/snapshot/src/port/utils.ts index d3435a493354..111d15cb15f1 100644 --- a/packages/snapshot/src/port/utils.ts +++ b/packages/snapshot/src/port/utils.ts @@ -265,3 +265,19 @@ export function deepMergeSnapshot(target: any, source: any): any { } return target } + +export class DefaultMap extends Map { + constructor( + private defaultFn: (key: K) => V, + entries?: Iterable, + ) { + super(entries) + } + + override get(key: K): V { + if (!this.has(key)) { + this.set(key, this.defaultFn(key)) + } + return super.get(key)! + } +} From 3fc11d5d2fcbd030cd5cc401e43d6ca69c4d0d00 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 11:43:51 +0900 Subject: [PATCH 15/27] fix: clear added/updated stats --- packages/snapshot/src/port/state.ts | 44 +++++++++++++++-------------- packages/snapshot/src/port/utils.ts | 14 +++++++++ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 4c975155dfb9..2dfe42cfebc0 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -23,6 +23,7 @@ import { saveRawSnapshots } from './rawSnapshot' import { addExtraLineBreaks, + CounterMap, DefaultMap, getSnapshotData, keyToTestName, @@ -61,12 +62,11 @@ export default class SnapshotState { private _snapshotFormat: PrettyFormatOptions private _environment: SnapshotEnvironment private _fileExists: boolean - - added: number + private added = new CounterMap() + private matched = new CounterMap() + private unmatched = new CounterMap() + private updated = new CounterMap() expand: boolean - matched: number - unmatched: number - updated: number private constructor( public testFilePath: string, @@ -85,11 +85,7 @@ export default class SnapshotState { this._uncheckedKeys = new Set(Object.keys(this._snapshotData)) this._counters = new Map() this.expand = options.expand || false - this.added = 0 - this.matched = 0 - this.unmatched = 0 this._updateSnapshot = options.updateSnapshot - this.updated = 0 this._snapshotFormat = { printBasicPrototype: false, escapeString: false, @@ -121,11 +117,11 @@ export default class SnapshotState { } clearTest(testId: string): void { - // TODO: reset stats: added, matched, etc.. - + // clear inline this._inlineSnapshots = this._inlineSnapshots.filter(s => s.testId !== testId) this._inlineSnapshotStacks = this._inlineSnapshotStacks.filter(s => s.testId !== testId) + // clear file for (const key of this._testIdToKeys.get(testId)) { const name = keyToTestName(key) const counter = this._counters.get(name) @@ -142,6 +138,12 @@ export default class SnapshotState { } } this._testIdToKeys.delete(testId) + + // clear stats + this.added.delete(testId) + this.updated.delete(testId) + this.matched.delete(testId) + this.unmatched.delete(testId) } protected _inferInlineSnapshotStack(stacks: ParsedStack[]): ParsedStack | null { @@ -357,10 +359,10 @@ export default class SnapshotState { if (this._updateSnapshot === 'all') { if (!pass) { if (hasSnapshot) { - this.updated++ + this.updated.increment(testId) } else { - this.added++ + this.added.increment(testId) } this._addSnapshot(key, receivedSerialized, { @@ -370,7 +372,7 @@ export default class SnapshotState { }) } else { - this.matched++ + this.matched.increment(testId) } } else { @@ -379,7 +381,7 @@ export default class SnapshotState { testId, rawSnapshot, }) - this.added++ + this.added.increment(testId) } return { @@ -392,7 +394,7 @@ export default class SnapshotState { } else { if (!pass) { - this.unmatched++ + this.unmatched.increment(testId) return { actual: removeExtraLineBreaks(receivedSerialized), count, @@ -405,7 +407,7 @@ export default class SnapshotState { } } else { - this.matched++ + this.matched.increment(testId) return { actual: '', count, @@ -436,10 +438,10 @@ export default class SnapshotState { const status = await this.save() snapshot.fileDeleted = status.deleted - snapshot.added = this.added - snapshot.matched = this.matched - snapshot.unmatched = this.unmatched - snapshot.updated = this.updated + snapshot.added = this.added.total() + snapshot.matched = this.matched.total() + snapshot.unmatched = this.unmatched.total() + snapshot.updated = this.updated.total() snapshot.unchecked = !status.deleted ? uncheckedCount : 0 snapshot.uncheckedKeys = Array.from(uncheckedKeys) diff --git a/packages/snapshot/src/port/utils.ts b/packages/snapshot/src/port/utils.ts index 111d15cb15f1..1773945f10de 100644 --- a/packages/snapshot/src/port/utils.ts +++ b/packages/snapshot/src/port/utils.ts @@ -281,3 +281,17 @@ export class DefaultMap extends Map { return super.get(key)! } } + +export class CounterMap extends Map { + increment(key: K): void { + this.set(key, (this.get(key) ?? 0) + 1) + } + + total(): number { + let total = 0 + for (const x of this.values()) { + total += x + } + return total + } +} From de9b88ef2db0844f8b4104c33fb8678a4a7e9d41 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 12:06:02 +0900 Subject: [PATCH 16/27] chore: comment --- packages/snapshot/src/client.ts | 1 - packages/snapshot/src/port/state.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index 32279959a0d8..b15bee07baea 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -50,7 +50,6 @@ export interface SnapshotClientOptions { isEqual?: (received: unknown, expected: unknown) => boolean } -// TODO: probably we don't need much refactoring here yet. separate refactoring export class SnapshotClient { snapshotStateMap: Map = new Map() diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index 2dfe42cfebc0..c7a0f837932c 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -255,7 +255,7 @@ export default class SnapshotState { error, rawSnapshot, }: SnapshotMatchOptions): SnapshotReturnOptions { - // this also increments counter for inline snapshots. maybe we don't need to? + // this also increments counter for inline snapshots. maybe we shouldn't? this._counters.set(testName, (this._counters.get(testName) || 0) + 1) const count = Number(this._counters.get(testName)) From 4a69ec17b6375dcb282d018291d26d59642f0fbc Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 16:53:39 +0900 Subject: [PATCH 17/27] test: move some snapshot test --- test/snapshots/package.json | 6 ++-- ...rent.test.js => inline-concurrent.test.js} | 0 ...shots-inline-js.test.js => inline.test.js} | 0 ....test.ts.snap => test-update.test.ts.snap} | 36 +++++++++---------- .../test-update/inline-concurrent.test.js} | 0 .../fixtures/test-update/inline.test.js} | 0 test/snapshots/test/snapshots.test.ts | 14 -------- test/snapshots/test/test-update.test.ts | 10 ++++++ 8 files changed, 32 insertions(+), 34 deletions(-) rename test/snapshots/test-update/{inline-test-template-concurrent.test.js => inline-concurrent.test.js} (100%) rename test/snapshots/test-update/{snapshots-inline-js.test.js => inline.test.js} (100%) rename test/snapshots/test/__snapshots__/{snapshots.test.ts.snap => test-update.test.ts.snap} (95%) rename test/snapshots/{tools/inline-test-template-concurrent.js => test/fixtures/test-update/inline-concurrent.test.js} (100%) rename test/snapshots/{tools/inline-test-template.js => test/fixtures/test-update/inline.test.js} (100%) create mode 100644 test/snapshots/test/test-update.test.ts diff --git a/test/snapshots/package.json b/test/snapshots/package.json index 2e062a10cd7f..6b19ad617fcf 100644 --- a/test/snapshots/package.json +++ b/test/snapshots/package.json @@ -3,10 +3,12 @@ "type": "module", "private": true, "scripts": { - "test": "pnpm run test:generate && pnpm run test:update && pnpm run test:snaps", - "test:generate": "node tools/generate-inline-test.mjs", + "test": "pnpm run test:generate && pnpm run test:update && pnpm test:update-new && pnpm test:update-none && pnpm run test:snaps", + "test:generate": "rm -rf ./test-update && cp -r ./test/fixtures/test-update ./test-update", "test:snaps": "vitest run --dir test", "test:update": "vitest run -u --dir test-update", + "test:update-none": "CI=true vitest run --dir test-update", + "test:update-new": "CI=false vitest run --dir test-update", "coverage": "vitest run --coverage" }, "dependencies": { diff --git a/test/snapshots/test-update/inline-test-template-concurrent.test.js b/test/snapshots/test-update/inline-concurrent.test.js similarity index 100% rename from test/snapshots/test-update/inline-test-template-concurrent.test.js rename to test/snapshots/test-update/inline-concurrent.test.js diff --git a/test/snapshots/test-update/snapshots-inline-js.test.js b/test/snapshots/test-update/inline.test.js similarity index 100% rename from test/snapshots/test-update/snapshots-inline-js.test.js rename to test/snapshots/test-update/inline.test.js diff --git a/test/snapshots/test/__snapshots__/snapshots.test.ts.snap b/test/snapshots/test/__snapshots__/test-update.test.ts.snap similarity index 95% rename from test/snapshots/test/__snapshots__/snapshots.test.ts.snap rename to test/snapshots/test/__snapshots__/test-update.test.ts.snap index d42ea091ab42..e03790dd1aed 100644 --- a/test/snapshots/test/__snapshots__/snapshots.test.ts.snap +++ b/test/snapshots/test/__snapshots__/test-update.test.ts.snap @@ -1,23 +1,6 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html -exports[`concurrent snapshot update 1`] = ` -"import { it } from 'vitest' - -it.concurrent('1st', ({ expect }) => { - expect('hi1').toMatchInlineSnapshot(\`"hi1"\`) -}) - -it.concurrent('2nd', ({ expect }) => { - expect('hi2').toMatchInlineSnapshot(\`"hi2"\`) -}) - -it.concurrent('3rd', ({ expect }) => { - expect('hi3').toMatchInlineSnapshot(\`"hi3"\`) -}) -" -`; - -exports[`js snapshots generated correctly 1`] = ` +exports[`inline.test.js 1`] = ` "import { describe, expect, test } from 'vitest' // when snapshots are generated Vitest reruns \`toMatchInlineSnapshot\` checks @@ -89,3 +72,20 @@ describe('snapshots with properties', () => { }) " `; + +exports[`inline-concurrent.test.js 1`] = ` +"import { it } from 'vitest' + +it.concurrent('1st', ({ expect }) => { + expect('hi1').toMatchInlineSnapshot(\`"hi1"\`) +}) + +it.concurrent('2nd', ({ expect }) => { + expect('hi2').toMatchInlineSnapshot(\`"hi2"\`) +}) + +it.concurrent('3rd', ({ expect }) => { + expect('hi3').toMatchInlineSnapshot(\`"hi3"\`) +}) +" +`; diff --git a/test/snapshots/tools/inline-test-template-concurrent.js b/test/snapshots/test/fixtures/test-update/inline-concurrent.test.js similarity index 100% rename from test/snapshots/tools/inline-test-template-concurrent.js rename to test/snapshots/test/fixtures/test-update/inline-concurrent.test.js diff --git a/test/snapshots/tools/inline-test-template.js b/test/snapshots/test/fixtures/test-update/inline.test.js similarity index 100% rename from test/snapshots/tools/inline-test-template.js rename to test/snapshots/test/fixtures/test-update/inline.test.js diff --git a/test/snapshots/test/snapshots.test.ts b/test/snapshots/test/snapshots.test.ts index 13556156b4c2..1f07adf0d2ec 100644 --- a/test/snapshots/test/snapshots.test.ts +++ b/test/snapshots/test/snapshots.test.ts @@ -1,5 +1,3 @@ -import fs from 'node:fs/promises' -import pathe from 'pathe' import { expect, test } from 'vitest' import { editFile, runVitest } from '../../test-utils' @@ -14,18 +12,6 @@ test('non default snapshot format', () => { `) }) -test('js snapshots generated correctly', async () => { - const path = pathe.resolve(__dirname, '../test-update/snapshots-inline-js.test.js') - const content = await fs.readFile(path, 'utf8') - expect(content).toMatchSnapshot() -}) - -test('concurrent snapshot update', async () => { - const path = pathe.resolve(__dirname, '../test-update/inline-test-template-concurrent.test.js') - const content = await fs.readFile(path, 'utf8') - expect(content).toMatchSnapshot() -}) - test('--update works for workspace project', async () => { // setup wrong snapshot value editFile( diff --git a/test/snapshots/test/test-update.test.ts b/test/snapshots/test/test-update.test.ts new file mode 100644 index 000000000000..487a3846285c --- /dev/null +++ b/test/snapshots/test/test-update.test.ts @@ -0,0 +1,10 @@ +import fs from 'node:fs/promises' +import { join } from 'node:path' +import { expect, test } from 'vitest' + +const dir = join(import.meta.dirname, '../test-update') +const files = await fs.readdir(dir) +test.for(files)('%s', async (file) => { + const content = await fs.readFile(join(dir, file), 'utf8') + expect(content).toMatchSnapshot() +}) From fd8d60cc1fd600a41fed93d743a331aa428e201c Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 17:14:43 +0900 Subject: [PATCH 18/27] test: test same title --- .../same-title-file.test.js.snap | 11 +++++ .../test-update/same-title-file.test.js | 20 +++++++++ .../test-update/same-title-inline.test.js | 16 +++++++ .../__snapshots__/test-update.test.ts.snap | 44 +++++++++++++++++++ .../same-title-file.test.js.snap | 5 +++ .../test-update/same-title-file.test.js | 20 +++++++++ .../test-update/same-title-inline.test.js | 16 +++++++ test/snapshots/test/test-update.test.ts | 3 +- 8 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 test/snapshots/test-update/__snapshots__/same-title-file.test.js.snap create mode 100644 test/snapshots/test-update/same-title-file.test.js create mode 100644 test/snapshots/test-update/same-title-inline.test.js create mode 100644 test/snapshots/test/fixtures/test-update/__snapshots__/same-title-file.test.js.snap create mode 100644 test/snapshots/test/fixtures/test-update/same-title-file.test.js create mode 100644 test/snapshots/test/fixtures/test-update/same-title-inline.test.js diff --git a/test/snapshots/test-update/__snapshots__/same-title-file.test.js.snap b/test/snapshots/test-update/__snapshots__/same-title-file.test.js.snap new file mode 100644 index 000000000000..acf7b18f1f93 --- /dev/null +++ b/test/snapshots/test-update/__snapshots__/same-title-file.test.js.snap @@ -0,0 +1,11 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`same title exist 1`] = `"a"`; + +exports[`same title exist 2`] = `"b"`; + +exports[`same title new 1`] = `"a"`; + +exports[`same title new 2`] = `"b"`; + +exports[`same title new 3`] = `"c"`; diff --git a/test/snapshots/test-update/same-title-file.test.js b/test/snapshots/test-update/same-title-file.test.js new file mode 100644 index 000000000000..cf4955532f88 --- /dev/null +++ b/test/snapshots/test-update/same-title-file.test.js @@ -0,0 +1,20 @@ +import { expect, test } from 'vitest' + +test('same title exist', () => { + // correct entry exists in .snap + expect('a').toMatchSnapshot() +}) + +test('same title exist', () => { + // wrong entry exists in .snap + expect('b').toMatchSnapshot() +}) + +test('same title new', () => { + expect('a').toMatchSnapshot() +}) + +test('same title new', () => { + expect('b').toMatchSnapshot() + expect('c').toMatchSnapshot() +}) diff --git a/test/snapshots/test-update/same-title-inline.test.js b/test/snapshots/test-update/same-title-inline.test.js new file mode 100644 index 000000000000..84fab3fe3448 --- /dev/null +++ b/test/snapshots/test-update/same-title-inline.test.js @@ -0,0 +1,16 @@ +import { expect, test } from 'vitest' + +test('same title', () => { + expect('new').toMatchInlineSnapshot(`"new"`) + expect('new').toMatchInlineSnapshot(`"new"`) +}) + +test('same title', () => { + expect('a').toMatchInlineSnapshot(`"a"`) + expect('a').toMatchInlineSnapshot(`"a"`) +}) + +test('same title', () => { + expect('b').toMatchInlineSnapshot(`"b"`) + expect('b').toMatchInlineSnapshot(`"b"`) +}) diff --git a/test/snapshots/test/__snapshots__/test-update.test.ts.snap b/test/snapshots/test/__snapshots__/test-update.test.ts.snap index e03790dd1aed..93c0ca74ba46 100644 --- a/test/snapshots/test/__snapshots__/test-update.test.ts.snap +++ b/test/snapshots/test/__snapshots__/test-update.test.ts.snap @@ -89,3 +89,47 @@ it.concurrent('3rd', ({ expect }) => { }) " `; + +exports[`same-title-file.test.js 1`] = ` +"import { expect, test } from 'vitest' + +test('same title exist', () => { + // correct entry exists in .snap + expect('a').toMatchSnapshot() +}) + +test('same title exist', () => { + // wrong entry exists in .snap + expect('b').toMatchSnapshot() +}) + +test('same title new', () => { + expect('a').toMatchSnapshot() +}) + +test('same title new', () => { + expect('b').toMatchSnapshot() + expect('c').toMatchSnapshot() +}) +" +`; + +exports[`same-title-inline.test.js 1`] = ` +"import { expect, test } from 'vitest' + +test('same title', () => { + expect('new').toMatchInlineSnapshot(\`"new"\`) + expect('new').toMatchInlineSnapshot(\`"new"\`) +}) + +test('same title', () => { + expect('a').toMatchInlineSnapshot(\`"a"\`) + expect('a').toMatchInlineSnapshot(\`"a"\`) +}) + +test('same title', () => { + expect('b').toMatchInlineSnapshot(\`"b"\`) + expect('b').toMatchInlineSnapshot(\`"b"\`) +}) +" +`; diff --git a/test/snapshots/test/fixtures/test-update/__snapshots__/same-title-file.test.js.snap b/test/snapshots/test/fixtures/test-update/__snapshots__/same-title-file.test.js.snap new file mode 100644 index 000000000000..90d340867340 --- /dev/null +++ b/test/snapshots/test/fixtures/test-update/__snapshots__/same-title-file.test.js.snap @@ -0,0 +1,5 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`same title exist 1`] = `"a"`; + +exports[`same title exist 2`] = `"wrong"`; diff --git a/test/snapshots/test/fixtures/test-update/same-title-file.test.js b/test/snapshots/test/fixtures/test-update/same-title-file.test.js new file mode 100644 index 000000000000..cf4955532f88 --- /dev/null +++ b/test/snapshots/test/fixtures/test-update/same-title-file.test.js @@ -0,0 +1,20 @@ +import { expect, test } from 'vitest' + +test('same title exist', () => { + // correct entry exists in .snap + expect('a').toMatchSnapshot() +}) + +test('same title exist', () => { + // wrong entry exists in .snap + expect('b').toMatchSnapshot() +}) + +test('same title new', () => { + expect('a').toMatchSnapshot() +}) + +test('same title new', () => { + expect('b').toMatchSnapshot() + expect('c').toMatchSnapshot() +}) diff --git a/test/snapshots/test/fixtures/test-update/same-title-inline.test.js b/test/snapshots/test/fixtures/test-update/same-title-inline.test.js new file mode 100644 index 000000000000..27726affdd57 --- /dev/null +++ b/test/snapshots/test/fixtures/test-update/same-title-inline.test.js @@ -0,0 +1,16 @@ +import { expect, test } from 'vitest' + +test('same title', () => { + expect('new').toMatchInlineSnapshot() + expect('new').toMatchInlineSnapshot() +}) + +test('same title', () => { + expect('a').toMatchInlineSnapshot(`"a"`) + expect('a').toMatchInlineSnapshot(`"a"`) +}) + +test('same title', () => { + expect('b').toMatchInlineSnapshot(`"wrong"`) + expect('b').toMatchInlineSnapshot(`"wrong"`) +}) diff --git a/test/snapshots/test/test-update.test.ts b/test/snapshots/test/test-update.test.ts index 487a3846285c..abeed679776e 100644 --- a/test/snapshots/test/test-update.test.ts +++ b/test/snapshots/test/test-update.test.ts @@ -3,7 +3,8 @@ import { join } from 'node:path' import { expect, test } from 'vitest' const dir = join(import.meta.dirname, '../test-update') -const files = await fs.readdir(dir) +const dirents = await fs.readdir(dir, { withFileTypes: true }) +const files = dirents.filter(f => f.isFile()).map(f => f.name) test.for(files)('%s', async (file) => { const content = await fs.readFile(join(dir, file), 'utf8') expect(content).toMatchSnapshot() From 1cdaa678e2a77e558d91f584daefc93ba43e0035 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 17:20:47 +0900 Subject: [PATCH 19/27] test: test retry --- .../__snapshots__/retry.test.ts.snap | 17 +++++++ test/snapshots/test-update/retry.test.ts | 44 +++++++++++++++++ .../__snapshots__/test-update.test.ts.snap | 48 +++++++++++++++++++ .../test/fixtures/test-update/retry.test.ts | 44 +++++++++++++++++ 4 files changed, 153 insertions(+) create mode 100644 test/snapshots/test-update/__snapshots__/retry.test.ts.snap create mode 100644 test/snapshots/test-update/retry.test.ts create mode 100644 test/snapshots/test/fixtures/test-update/retry.test.ts diff --git a/test/snapshots/test-update/__snapshots__/retry.test.ts.snap b/test/snapshots/test-update/__snapshots__/retry.test.ts.snap new file mode 100644 index 000000000000..55fb93d2deec --- /dev/null +++ b/test/snapshots/test-update/__snapshots__/retry.test.ts.snap @@ -0,0 +1,17 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`file repeats 1`] = `"foo"`; + +exports[`file repeats many 1`] = `"foo"`; + +exports[`file repeats many 2`] = `"bar"`; + +exports[`file retry 1`] = `"foo"`; + +exports[`file retry many 1`] = `"foo"`; + +exports[`file retry many 2`] = `"bar"`; + +exports[`file retry partial 1`] = `"foo"`; + +exports[`file retry partial 2`] = `"bar"`; diff --git a/test/snapshots/test-update/retry.test.ts b/test/snapshots/test-update/retry.test.ts new file mode 100644 index 000000000000..f8c9e0f8146c --- /dev/null +++ b/test/snapshots/test-update/retry.test.ts @@ -0,0 +1,44 @@ +import { expect, test } from 'vitest' + +test('inline repeats', { repeats: 1 }, () => { + expect('foo').toMatchInlineSnapshot(`"foo"`) +}) + +test('inline retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchInlineSnapshot(`"foo"`) + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file repeats', { repeats: 1 }, () => { + expect('foo').toMatchSnapshot() +}) + +test('file retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file repeats many', { repeats: 1 }, () => { + expect('foo').toMatchSnapshot() + expect('bar').toMatchSnapshot() +}) + +test('file retry many', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + expect('bar').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file retry partial', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } + expect('bar').toMatchSnapshot() +}) diff --git a/test/snapshots/test/__snapshots__/test-update.test.ts.snap b/test/snapshots/test/__snapshots__/test-update.test.ts.snap index 93c0ca74ba46..f7fb628944a4 100644 --- a/test/snapshots/test/__snapshots__/test-update.test.ts.snap +++ b/test/snapshots/test/__snapshots__/test-update.test.ts.snap @@ -90,6 +90,54 @@ it.concurrent('3rd', ({ expect }) => { " `; +exports[`retry.test.ts 1`] = ` +"import { expect, test } from 'vitest' + +test('inline repeats', { repeats: 1 }, () => { + expect('foo').toMatchInlineSnapshot(\`"foo"\`) +}) + +test('inline retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchInlineSnapshot(\`"foo"\`) + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file repeats', { repeats: 1 }, () => { + expect('foo').toMatchSnapshot() +}) + +test('file retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file repeats many', { repeats: 1 }, () => { + expect('foo').toMatchSnapshot() + expect('bar').toMatchSnapshot() +}) + +test('file retry many', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + expect('bar').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file retry partial', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } + expect('bar').toMatchSnapshot() +}) +" +`; + exports[`same-title-file.test.js 1`] = ` "import { expect, test } from 'vitest' diff --git a/test/snapshots/test/fixtures/test-update/retry.test.ts b/test/snapshots/test/fixtures/test-update/retry.test.ts new file mode 100644 index 000000000000..cb99fc99e2ee --- /dev/null +++ b/test/snapshots/test/fixtures/test-update/retry.test.ts @@ -0,0 +1,44 @@ +import { expect, test } from 'vitest' + +test('inline repeats', { repeats: 1 }, () => { + expect('foo').toMatchInlineSnapshot() +}) + +test('inline retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchInlineSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file repeats', { repeats: 1 }, () => { + expect('foo').toMatchSnapshot() +}) + +test('file retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file repeats many', { repeats: 1 }, () => { + expect('foo').toMatchSnapshot() + expect('bar').toMatchSnapshot() +}) + +test('file retry many', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + expect('bar').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file retry partial', { retry: 1 }, (ctx) => { + expect('foo').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } + expect('bar').toMatchSnapshot() +}) From e0768a10816f2ce24e8f713ea5c1bc07cefd5e57 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 17:26:29 +0900 Subject: [PATCH 20/27] test: cleanup --- .../test/__snapshots__/repro2.test.ts.snap | 5 -- test/core/test/repro1.test.ts | 48 --------------- test/core/test/repro2.test.ts | 21 ------- .../__snapshots__/retry-file.test.ts.snap} | 0 .../__snapshots__/retry.test.ts.snap | 17 ------ .../retry-file.test.ts} | 11 ---- .../test-update/retry-inline.test.ts | 12 ++++ .../__snapshots__/test-update.test.ts.snap | 58 +------------------ .../fixtures/test-update/retry-file.test.ts} | 11 ---- .../fixtures/test-update/retry-inline.test.ts | 12 ++++ test/snapshots/test/test-update.test.ts | 2 +- 11 files changed, 26 insertions(+), 171 deletions(-) delete mode 100644 test/core/test/__snapshots__/repro2.test.ts.snap delete mode 100644 test/core/test/repro1.test.ts delete mode 100644 test/core/test/repro2.test.ts rename test/{core/test/__snapshots__/repro1.test.ts.snap => snapshots/test-update/__snapshots__/retry-file.test.ts.snap} (100%) delete mode 100644 test/snapshots/test-update/__snapshots__/retry.test.ts.snap rename test/snapshots/{test/fixtures/test-update/retry.test.ts => test-update/retry-file.test.ts} (75%) create mode 100644 test/snapshots/test-update/retry-inline.test.ts rename test/snapshots/{test-update/retry.test.ts => test/fixtures/test-update/retry-file.test.ts} (74%) create mode 100644 test/snapshots/test/fixtures/test-update/retry-inline.test.ts diff --git a/test/core/test/__snapshots__/repro2.test.ts.snap b/test/core/test/__snapshots__/repro2.test.ts.snap deleted file mode 100644 index c1acf6864dfd..000000000000 --- a/test/core/test/__snapshots__/repro2.test.ts.snap +++ /dev/null @@ -1,5 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`file same title 1`] = `"foo"`; - -exports[`file same title 2`] = `"bar"`; diff --git a/test/core/test/repro1.test.ts b/test/core/test/repro1.test.ts deleted file mode 100644 index ec0c8149fefe..000000000000 --- a/test/core/test/repro1.test.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { expect, test } from 'vitest' - -test('repro', () => { - expect(0).toBe(0) -}) - -test('inline repeats', { repeats: 1 }, () => { - expect('foo').toMatchInlineSnapshot(`"foo"`) -}) - -test('inline retry', { retry: 1 }, (ctx) => { - expect('foo').toMatchInlineSnapshot(`"foo"`) - if (ctx.task.result?.retryCount === 0) { - throw new Error('boom') - } -}) - -test('file repeats', { repeats: 1 }, () => { - expect('foo').toMatchSnapshot() -}) - -test('file retry', { retry: 1 }, (ctx) => { - expect('foo').toMatchSnapshot() - if (ctx.task.result?.retryCount === 0) { - throw new Error('boom') - } -}) - -test('file repeats many', { repeats: 1 }, () => { - expect('foo').toMatchSnapshot() - expect('bar').toMatchSnapshot() -}) - -test('file retry many', { retry: 1 }, (ctx) => { - expect('foo').toMatchSnapshot() - expect('bar').toMatchSnapshot() - if (ctx.task.result?.retryCount === 0) { - throw new Error('boom') - } -}) - -test('file retry partial', { retry: 1 }, (ctx) => { - expect('foo').toMatchSnapshot() - if (ctx.task.result?.retryCount === 0) { - throw new Error('boom') - } - expect('bar').toMatchSnapshot() -}) diff --git a/test/core/test/repro2.test.ts b/test/core/test/repro2.test.ts deleted file mode 100644 index 7956a82fea03..000000000000 --- a/test/core/test/repro2.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { expect, test } from 'vitest' - -test('repro', () => { - expect(0).toBe(0) -}) - -test('inline same title', () => { - expect('foo').toMatchInlineSnapshot(`"foo"`) -}) - -test('inline same title', () => { - expect('bar').toMatchInlineSnapshot(`"bar"`) -}) - -test('file same title', () => { - expect('foo').toMatchSnapshot() -}) - -test('file same title', () => { - expect('bar').toMatchSnapshot() -}) diff --git a/test/core/test/__snapshots__/repro1.test.ts.snap b/test/snapshots/test-update/__snapshots__/retry-file.test.ts.snap similarity index 100% rename from test/core/test/__snapshots__/repro1.test.ts.snap rename to test/snapshots/test-update/__snapshots__/retry-file.test.ts.snap diff --git a/test/snapshots/test-update/__snapshots__/retry.test.ts.snap b/test/snapshots/test-update/__snapshots__/retry.test.ts.snap deleted file mode 100644 index 55fb93d2deec..000000000000 --- a/test/snapshots/test-update/__snapshots__/retry.test.ts.snap +++ /dev/null @@ -1,17 +0,0 @@ -// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html - -exports[`file repeats 1`] = `"foo"`; - -exports[`file repeats many 1`] = `"foo"`; - -exports[`file repeats many 2`] = `"bar"`; - -exports[`file retry 1`] = `"foo"`; - -exports[`file retry many 1`] = `"foo"`; - -exports[`file retry many 2`] = `"bar"`; - -exports[`file retry partial 1`] = `"foo"`; - -exports[`file retry partial 2`] = `"bar"`; diff --git a/test/snapshots/test/fixtures/test-update/retry.test.ts b/test/snapshots/test-update/retry-file.test.ts similarity index 75% rename from test/snapshots/test/fixtures/test-update/retry.test.ts rename to test/snapshots/test-update/retry-file.test.ts index cb99fc99e2ee..a8a31a8f58d8 100644 --- a/test/snapshots/test/fixtures/test-update/retry.test.ts +++ b/test/snapshots/test-update/retry-file.test.ts @@ -1,16 +1,5 @@ import { expect, test } from 'vitest' -test('inline repeats', { repeats: 1 }, () => { - expect('foo').toMatchInlineSnapshot() -}) - -test('inline retry', { retry: 1 }, (ctx) => { - expect('foo').toMatchInlineSnapshot() - if (ctx.task.result?.retryCount === 0) { - throw new Error('boom') - } -}) - test('file repeats', { repeats: 1 }, () => { expect('foo').toMatchSnapshot() }) diff --git a/test/snapshots/test-update/retry-inline.test.ts b/test/snapshots/test-update/retry-inline.test.ts new file mode 100644 index 000000000000..0d93ccf02176 --- /dev/null +++ b/test/snapshots/test-update/retry-inline.test.ts @@ -0,0 +1,12 @@ +import { expect, test } from 'vitest' + +test('inline repeats', { repeats: 1 }, () => { + expect('foo').toMatchInlineSnapshot(`"foo"`) +}) + +test('inline retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchInlineSnapshot(`"foo"`) + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) diff --git a/test/snapshots/test/__snapshots__/test-update.test.ts.snap b/test/snapshots/test/__snapshots__/test-update.test.ts.snap index f7fb628944a4..3321ee7ef68e 100644 --- a/test/snapshots/test/__snapshots__/test-update.test.ts.snap +++ b/test/snapshots/test/__snapshots__/test-update.test.ts.snap @@ -90,7 +90,7 @@ it.concurrent('3rd', ({ expect }) => { " `; -exports[`retry.test.ts 1`] = ` +exports[`retry-inline.test.ts 1`] = ` "import { expect, test } from 'vitest' test('inline repeats', { repeats: 1 }, () => { @@ -103,62 +103,6 @@ test('inline retry', { retry: 1 }, (ctx) => { throw new Error('boom') } }) - -test('file repeats', { repeats: 1 }, () => { - expect('foo').toMatchSnapshot() -}) - -test('file retry', { retry: 1 }, (ctx) => { - expect('foo').toMatchSnapshot() - if (ctx.task.result?.retryCount === 0) { - throw new Error('boom') - } -}) - -test('file repeats many', { repeats: 1 }, () => { - expect('foo').toMatchSnapshot() - expect('bar').toMatchSnapshot() -}) - -test('file retry many', { retry: 1 }, (ctx) => { - expect('foo').toMatchSnapshot() - expect('bar').toMatchSnapshot() - if (ctx.task.result?.retryCount === 0) { - throw new Error('boom') - } -}) - -test('file retry partial', { retry: 1 }, (ctx) => { - expect('foo').toMatchSnapshot() - if (ctx.task.result?.retryCount === 0) { - throw new Error('boom') - } - expect('bar').toMatchSnapshot() -}) -" -`; - -exports[`same-title-file.test.js 1`] = ` -"import { expect, test } from 'vitest' - -test('same title exist', () => { - // correct entry exists in .snap - expect('a').toMatchSnapshot() -}) - -test('same title exist', () => { - // wrong entry exists in .snap - expect('b').toMatchSnapshot() -}) - -test('same title new', () => { - expect('a').toMatchSnapshot() -}) - -test('same title new', () => { - expect('b').toMatchSnapshot() - expect('c').toMatchSnapshot() -}) " `; diff --git a/test/snapshots/test-update/retry.test.ts b/test/snapshots/test/fixtures/test-update/retry-file.test.ts similarity index 74% rename from test/snapshots/test-update/retry.test.ts rename to test/snapshots/test/fixtures/test-update/retry-file.test.ts index f8c9e0f8146c..a8a31a8f58d8 100644 --- a/test/snapshots/test-update/retry.test.ts +++ b/test/snapshots/test/fixtures/test-update/retry-file.test.ts @@ -1,16 +1,5 @@ import { expect, test } from 'vitest' -test('inline repeats', { repeats: 1 }, () => { - expect('foo').toMatchInlineSnapshot(`"foo"`) -}) - -test('inline retry', { retry: 1 }, (ctx) => { - expect('foo').toMatchInlineSnapshot(`"foo"`) - if (ctx.task.result?.retryCount === 0) { - throw new Error('boom') - } -}) - test('file repeats', { repeats: 1 }, () => { expect('foo').toMatchSnapshot() }) diff --git a/test/snapshots/test/fixtures/test-update/retry-inline.test.ts b/test/snapshots/test/fixtures/test-update/retry-inline.test.ts new file mode 100644 index 000000000000..9af58c26d837 --- /dev/null +++ b/test/snapshots/test/fixtures/test-update/retry-inline.test.ts @@ -0,0 +1,12 @@ +import { expect, test } from 'vitest' + +test('inline repeats', { repeats: 1 }, () => { + expect('foo').toMatchInlineSnapshot() +}) + +test('inline retry', { retry: 1 }, (ctx) => { + expect('foo').toMatchInlineSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) diff --git a/test/snapshots/test/test-update.test.ts b/test/snapshots/test/test-update.test.ts index abeed679776e..acb0fb69ecf6 100644 --- a/test/snapshots/test/test-update.test.ts +++ b/test/snapshots/test/test-update.test.ts @@ -4,7 +4,7 @@ import { expect, test } from 'vitest' const dir = join(import.meta.dirname, '../test-update') const dirents = await fs.readdir(dir, { withFileTypes: true }) -const files = dirents.filter(f => f.isFile()).map(f => f.name) +const files = dirents.filter(f => f.isFile() && f.name.includes('inline')).map(f => f.name) test.for(files)('%s', async (file) => { const content = await fs.readFile(join(dir, file), 'utf8') expect(content).toMatchSnapshot() From 5bd6a34f21c3d36414bb76eb7c8af80da110d816 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 18:01:41 +0900 Subject: [PATCH 21/27] test: test summary --- test/snapshots/package.json | 1 + .../summary/__snapshots__/basic.test.ts.snap | 19 +++++++ .../test/fixtures/summary/basic.test.ts | 52 +++++++++++++++++++ .../test/fixtures/summary/vitest.config.ts | 1 + .../__snapshots__/retry-file.test.ts.snap | 17 ++++++ test/snapshots/test/summary.test.ts | 42 +++++++++++++++ 6 files changed, 132 insertions(+) create mode 100644 test/snapshots/test/fixtures/summary/__snapshots__/basic.test.ts.snap create mode 100644 test/snapshots/test/fixtures/summary/basic.test.ts create mode 100644 test/snapshots/test/fixtures/summary/vitest.config.ts create mode 100644 test/snapshots/test/fixtures/test-update/__snapshots__/retry-file.test.ts.snap create mode 100644 test/snapshots/test/summary.test.ts diff --git a/test/snapshots/package.json b/test/snapshots/package.json index 6b19ad617fcf..a5b783fedc9a 100644 --- a/test/snapshots/package.json +++ b/test/snapshots/package.json @@ -9,6 +9,7 @@ "test:update": "vitest run -u --dir test-update", "test:update-none": "CI=true vitest run --dir test-update", "test:update-new": "CI=false vitest run --dir test-update", + "test:fixtures": "vitest", "coverage": "vitest run --coverage" }, "dependencies": { diff --git a/test/snapshots/test/fixtures/summary/__snapshots__/basic.test.ts.snap b/test/snapshots/test/fixtures/summary/__snapshots__/basic.test.ts.snap new file mode 100644 index 000000000000..708517958d0f --- /dev/null +++ b/test/snapshots/test/fixtures/summary/__snapshots__/basic.test.ts.snap @@ -0,0 +1,19 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`file normal 1`] = `"@SNAP4"`; + +exports[`file repeats 1`] = `"@SNAP5"`; + +exports[`file repeats many 1`] = `"@SNAP7"`; + +exports[`file repeats many 2`] = `"@SNAP8"`; + +exports[`file retry 1`] = `"@SNAP6"`; + +exports[`file retry many 1`] = `"@SNAP9"`; + +exports[`file retry many 2`] = `"@SNAP10"`; + +exports[`file retry partial 1`] = `"@SNAP11"`; + +exports[`file retry partial 2`] = `"@SNAP12"`; diff --git a/test/snapshots/test/fixtures/summary/basic.test.ts b/test/snapshots/test/fixtures/summary/basic.test.ts new file mode 100644 index 000000000000..98ae9ea0a8ab --- /dev/null +++ b/test/snapshots/test/fixtures/summary/basic.test.ts @@ -0,0 +1,52 @@ +import { expect, test } from 'vitest' + +test('inline normal', () => { + expect('@SNAP1').toMatchInlineSnapshot(`"@SNAP1"`) +}) + +test('inline repeats', { repeats: 1 }, () => { + expect('@SNAP2').toMatchInlineSnapshot(`"@SNAP2"`) +}) + +test('inline retry', { retry: 1 }, (ctx) => { + expect('@SNAP3').toMatchInlineSnapshot(`"@SNAP3"`) + if (ctx.task.result?.retryCount === 0) { + throw new Error('boom') + } +}) + +test('file normal', () => { + expect('@SNAP4').toMatchSnapshot() +}) + +test('file repeats', { repeats: 1 }, () => { + expect('@SNAP5').toMatchSnapshot() +}) + +test('file retry', { retry: 1 }, (ctx) => { + expect('@SNAP6').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('@retry') + } +}) + +test('file repeats many', { repeats: 1 }, () => { + expect('@SNAP7').toMatchSnapshot() + expect('@SNAP8').toMatchSnapshot() +}) + +test('file retry many', { retry: 1 }, (ctx) => { + expect('@SNAP9').toMatchSnapshot() + expect('@SNAP10').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('@retry') + } +}) + +test('file retry partial', { retry: 1 }, (ctx) => { + expect('@SNAP11').toMatchSnapshot() + if (ctx.task.result?.retryCount === 0) { + throw new Error('@retry') + } + expect('@SNAP12').toMatchSnapshot() +}) diff --git a/test/snapshots/test/fixtures/summary/vitest.config.ts b/test/snapshots/test/fixtures/summary/vitest.config.ts new file mode 100644 index 000000000000..b1c6ea436a54 --- /dev/null +++ b/test/snapshots/test/fixtures/summary/vitest.config.ts @@ -0,0 +1 @@ +export default {} diff --git a/test/snapshots/test/fixtures/test-update/__snapshots__/retry-file.test.ts.snap b/test/snapshots/test/fixtures/test-update/__snapshots__/retry-file.test.ts.snap new file mode 100644 index 000000000000..55fb93d2deec --- /dev/null +++ b/test/snapshots/test/fixtures/test-update/__snapshots__/retry-file.test.ts.snap @@ -0,0 +1,17 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`file repeats 1`] = `"foo"`; + +exports[`file repeats many 1`] = `"foo"`; + +exports[`file repeats many 2`] = `"bar"`; + +exports[`file retry 1`] = `"foo"`; + +exports[`file retry many 1`] = `"foo"`; + +exports[`file retry many 2`] = `"bar"`; + +exports[`file retry partial 1`] = `"foo"`; + +exports[`file retry partial 2`] = `"bar"`; diff --git a/test/snapshots/test/summary.test.ts b/test/snapshots/test/summary.test.ts new file mode 100644 index 000000000000..5a438afba53c --- /dev/null +++ b/test/snapshots/test/summary.test.ts @@ -0,0 +1,42 @@ +import fs from 'node:fs' +import { join } from 'node:path' +import { expect, test } from 'vitest' +import { runVitest } from '../../test-utils' + +function fsUpdate(file: string, updateFn: (data: string) => string) { + fs.writeFileSync(file, updateFn(fs.readFileSync(file, 'utf-8'))) +} + +test('summary', async () => { + // cleanup snapshot + const dir = join(import.meta.dirname, 'fixtures/summary') + const testFile = join(dir, 'basic.test.ts') + const snapshotFile = join(dir, '__snapshots__/basic.test.ts.snap') + fsUpdate(testFile, s => s.replace(/`"@SNAP\d"`/g, '')) + fs.rmSync(snapshotFile, { recursive: true, force: true }) + + // write everything + let vitest = await runVitest({ + root: 'test/fixtures/summary', + update: true, + }) + expect(vitest.stdout).toContain('Snapshots 12 written') + + // write partially + fsUpdate(testFile, s => s.replace('`"@SNAP2"`', '')) + fsUpdate(snapshotFile, s => s.replace('exports[`file repeats 1`] = `"@SNAP5"`;', '')) + vitest = await runVitest({ + root: 'test/fixtures/summary', + update: true, + }) + expect(vitest.stdout).toContain('Snapshots 2 written') + + // update partially + fsUpdate(testFile, s => s.replace('`"@SNAP2"`', '`"@WRONG"`')) + fsUpdate(snapshotFile, s => s.replace('`"@SNAP5"`', '`"@WRONG"`')) + vitest = await runVitest({ + root: 'test/fixtures/summary', + update: true, + }) + expect(vitest.stdout).toContain('Snapshots 2 updated') +}) From 09a4bcdd35393f5209ea8a79aa09ef5e3cc3bd41 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 18:03:53 +0900 Subject: [PATCH 22/27] chore: cleanup --- packages/snapshot/src/manager.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/snapshot/src/manager.ts b/packages/snapshot/src/manager.ts index 87ffe7a199da..5a4caa0029af 100644 --- a/packages/snapshot/src/manager.ts +++ b/packages/snapshot/src/manager.ts @@ -23,7 +23,6 @@ export class SnapshotManager { addSnapshotResult(this.summary, result) } - // TODO: can remove in favor of SnapshotEnvironment.resolvePath? resolvePath(testPath: string): string { const resolver = this.options.resolveSnapshotPath || (() => { From 671344fe76e7ad9b29b7c68f5cc0339a37a52619 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 31 Oct 2024 18:28:26 +0900 Subject: [PATCH 23/27] refactor: minor --- packages/snapshot/src/port/state.ts | 18 ++++++------------ packages/snapshot/src/port/utils.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/packages/snapshot/src/port/state.ts b/packages/snapshot/src/port/state.ts index c7a0f837932c..89e777e4155f 100644 --- a/packages/snapshot/src/port/state.ts +++ b/packages/snapshot/src/port/state.ts @@ -49,7 +49,7 @@ interface SaveStatus { } export default class SnapshotState { - private _counters: Map + private _counters = new CounterMap() private _dirty: boolean private _updateSnapshot: SnapshotUpdateState private _snapshotData: SnapshotData @@ -83,7 +83,6 @@ export default class SnapshotState { this._inlineSnapshotStacks = [] this._rawSnapshots = [] this._uncheckedKeys = new Set(Object.keys(this._snapshotData)) - this._counters = new Map() this.expand = options.expand || false this._updateSnapshot = options.updateSnapshot this._snapshotFormat = { @@ -124,17 +123,12 @@ export default class SnapshotState { // clear file for (const key of this._testIdToKeys.get(testId)) { const name = keyToTestName(key) - const counter = this._counters.get(name) - if (typeof counter !== 'undefined') { + const count = this._counters.get(name) + if (count > 0) { if (key in this._snapshotData || key in this._initialData) { this._snapshotData[key] = this._initialData[key] } - if (counter > 0) { - this._counters.set(name, counter - 1) - } - else { - this._counters.delete(name) - } + this._counters.set(name, count - 1) } } this._testIdToKeys.delete(testId) @@ -256,8 +250,8 @@ export default class SnapshotState { rawSnapshot, }: SnapshotMatchOptions): SnapshotReturnOptions { // this also increments counter for inline snapshots. maybe we shouldn't? - this._counters.set(testName, (this._counters.get(testName) || 0) + 1) - const count = Number(this._counters.get(testName)) + this._counters.increment(testName) + const count = this._counters.get(testName) if (!key) { key = testNameToKey(testName, count) diff --git a/packages/snapshot/src/port/utils.ts b/packages/snapshot/src/port/utils.ts index 1773945f10de..64902bdef1da 100644 --- a/packages/snapshot/src/port/utils.ts +++ b/packages/snapshot/src/port/utils.ts @@ -282,9 +282,13 @@ export class DefaultMap extends Map { } } -export class CounterMap extends Map { +export class CounterMap extends DefaultMap { + constructor() { + super(() => 0) + } + increment(key: K): void { - this.set(key, (this.get(key) ?? 0) + 1) + this.set(key, this.get(key) + 1) } total(): number { From 63d981752d75440810b80b7b29a5f7aac7a3ee2e Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 4 Nov 2024 16:32:27 +0900 Subject: [PATCH 24/27] test: use import.meta.glob --- test/snapshots/test/test-update.test.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/test/snapshots/test/test-update.test.ts b/test/snapshots/test/test-update.test.ts index acb0fb69ecf6..7e76b777cc37 100644 --- a/test/snapshots/test/test-update.test.ts +++ b/test/snapshots/test/test-update.test.ts @@ -1,11 +1,8 @@ -import fs from 'node:fs/promises' -import { join } from 'node:path' import { expect, test } from 'vitest' -const dir = join(import.meta.dirname, '../test-update') -const dirents = await fs.readdir(dir, { withFileTypes: true }) -const files = dirents.filter(f => f.isFile() && f.name.includes('inline')).map(f => f.name) -test.for(files)('%s', async (file) => { - const content = await fs.readFile(join(dir, file), 'utf8') - expect(content).toMatchSnapshot() -}) +const entries = import.meta.glob('../test-update/*inline*', { eager: true, query: 'raw' }) +for (const [file, mod] of Object.entries(entries)) { + test(file.split('/').at(-1)!, () => { + expect((mod as any).default).toMatchSnapshot() + }) +} From 99c64e64122b4ad49bd2588330df4da0be59e032 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 4 Nov 2024 16:53:45 +0900 Subject: [PATCH 25/27] fix: ensure test context --- .../vitest/src/integrations/snapshot/chai.ts | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/vitest/src/integrations/snapshot/chai.ts b/packages/vitest/src/integrations/snapshot/chai.ts index 9cd96fc2ba1b..4c2f715bc456 100644 --- a/packages/vitest/src/integrations/snapshot/chai.ts +++ b/packages/vitest/src/integrations/snapshot/chai.ts @@ -53,6 +53,14 @@ function getTestNames(test: Test) { } export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { + function getTest(assertionName: string, obj: object) { + const test = utils.flag(obj, 'vitest-test') + if (!test) { + throw new Error(`'${assertionName}' cannot be used without test context`) + } + return test as Test + } + for (const key of ['matchSnapshot', 'toMatchSnapshot']) { utils.addMethod( chai.Assertion.prototype, @@ -67,7 +75,7 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { throw new Error(`${key} cannot be used with "not"`) } const expected = utils.flag(this, 'object') - const test: Test = utils.flag(this, 'vitest-test') + const test = getTest(key, this) if (typeof properties === 'string' && typeof message === 'undefined') { message = properties properties = undefined @@ -94,7 +102,7 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { throw new Error('toMatchFileSnapshot cannot be used with "not"') } const expected = utils.flag(this, 'object') - const test: Test = utils.flag(this, 'vitest-test') + const test = getTest('toMatchFileSnapshot', this) const errorMessage = utils.flag(this, 'message') const promise = getSnapshotClient().assertRaw({ @@ -125,8 +133,8 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { if (isNot) { throw new Error('toMatchInlineSnapshot cannot be used with "not"') } - const test: Test = utils.flag(this, 'vitest-test') - const isInsideEach = test && (test.each || test.suite?.each) + const test = getTest('toMatchInlineSnapshot', this) + const isInsideEach = test.each || test.suite?.each if (isInsideEach) { throw new Error( 'InlineSnapshot cannot be used inside of test.each or describe.each', @@ -167,7 +175,7 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { ) } const expected = utils.flag(this, 'object') - const test: Test = utils.flag(this, 'vitest-test') + const test = getTest('toThrowErrorMatchingSnapshot', this) const promise = utils.flag(this, 'promise') as string | undefined const errorMessage = utils.flag(this, 'message') getSnapshotClient().assert({ @@ -192,8 +200,8 @@ export const SnapshotPlugin: ChaiPlugin = (chai, utils) => { 'toThrowErrorMatchingInlineSnapshot cannot be used with "not"', ) } - const test: Test = utils.flag(this, 'vitest-test') - const isInsideEach = test && (test.each || test.suite?.each) + const test = getTest('toThrowErrorMatchingInlineSnapshot', this) + const isInsideEach = test.each || test.suite?.each if (isInsideEach) { throw new Error( 'InlineSnapshot cannot be used inside of test.each or describe.each', From ebbadda5695a1fa64c900daafdc82551a36e87e0 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 4 Nov 2024 17:13:20 +0900 Subject: [PATCH 26/27] fix: tweak api --- packages/snapshot/src/client.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index b15bee07baea..98a4d8d53dc9 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -60,7 +60,7 @@ export class SnapshotClient { options: SnapshotStateOptions, ): Promise { if (this.snapshotStateMap.has(filepath)) { - throw new Error('already setup') + return } this.snapshotStateMap.set( filepath, @@ -88,7 +88,9 @@ export class SnapshotClient { getSnapshotState(filepath: string): SnapshotState { const state = this.snapshotStateMap.get(filepath) if (!state) { - throw new Error('snapshot state not initialized') + throw new Error( + `The snapshot state for '${filepath}' is not found. Did you call 'SnapshotClient.setup()'?`, + ) } return state } From 04e1ddf516b26d4272a082a817f15f1031b46d6e Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Mon, 4 Nov 2024 17:46:53 +0900 Subject: [PATCH 27/27] chore: make `testId` optional --- packages/snapshot/src/client.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index 98a4d8d53dc9..39858802feca 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -36,7 +36,11 @@ interface AssertOptions { received: unknown filepath: string name: string - testId: string + /** + * Not required but needed for `SnapshotClient.clearTest` to implement test-retry behavior. + * @default name + */ + testId?: string message?: string isInline?: boolean properties?: object @@ -99,7 +103,7 @@ export class SnapshotClient { const { filepath, name, - testId, + testId = name, message, isInline = false, properties,