diff --git a/packages/snapshot/src/client.ts b/packages/snapshot/src/client.ts index 20bbc1fb8fab..575c304799de 100644 --- a/packages/snapshot/src/client.ts +++ b/packages/snapshot/src/client.ts @@ -1,7 +1,7 @@ import type { RawSnapshotInfo } from './port/rawSnapshot' import type { SnapshotResult, SnapshotStateOptions } from './types' import SnapshotState from './port/state' -import { deepMergeSnapshot, DefaultMap } from './port/utils' +import { deepMergeSnapshot, DefaultMap, PromiseMap } from './port/utils' function createMismatchError( message: string, @@ -83,7 +83,7 @@ export class SnapshotClient { private fileToTestIds = new DefaultMap>(() => new Set()) private testIdToSnapshotPath = new Map() - private snapshotPathToState = new Map() + private snapshotPathToState = new PromiseMap() // resolve snapshot file for each test and reuse state for same snapshot file // TODO: concurrent safe @@ -95,13 +95,10 @@ export class SnapshotClient { this.fileToTestIds.get(filepath).add(testId) const snapshotPath = await options.snapshotEnvironment.resolvePath(filepath) this.testIdToSnapshotPath.set(testId, snapshotPath) - // share same snapshot state for same snapshot path - let state = this.snapshotPathToState.get(snapshotPath) - if (!state) { + const state = await this.snapshotPathToState.getOrCreate(snapshotPath, async () => { const content = await options.snapshotEnvironment.readSnapshotFile(snapshotPath) - state = new SnapshotState(filepath, snapshotPath, content, options) - this.snapshotPathToState.set(snapshotPath, state) - } + return new SnapshotState(filepath, snapshotPath, content, options) + }) state.clearTest(testId) return state } diff --git a/packages/snapshot/src/port/utils.ts b/packages/snapshot/src/port/utils.ts index 1773945f10de..d609a80b977a 100644 --- a/packages/snapshot/src/port/utils.ts +++ b/packages/snapshot/src/port/utils.ts @@ -295,3 +295,19 @@ export class CounterMap extends Map { return total } } + +export class PromiseMap extends Map { + private inner = new Map>() + + getOrCreate(key: K, createFn: () => Promise): Promise { + let promise = this.inner.get(key) + if (!promise) { + promise = createFn().then((value) => { + this.set(key, value) + return value + }) + this.inner.set(key, promise) + } + return promise + } +} diff --git a/test/core/test/nested-test.test.ts b/test/core/test/nested-test.test.ts index b03cabffe8c7..8663b4e97f8e 100644 --- a/test/core/test/nested-test.test.ts +++ b/test/core/test/nested-test.test.ts @@ -15,14 +15,16 @@ test('nested test should throw error', () => { }) describe('parallel tests', () => { - test.concurrent('parallel test 1 with nested test', () => { + test.concurrent('parallel test 1 with nested test', ({ expect }) => { expect(() => { test('test inside test', () => {}) }).toThrowErrorMatchingInlineSnapshot(`[Error: Calling the test function inside another test function is not allowed. Please put it inside "describe" or "suite" so it can be properly collected.]`) }) test.concurrent('parallel test 2 without nested test', () => {}) test.concurrent('parallel test 3 without nested test', () => {}) - test.concurrent('parallel test 4 with nested test', () => { + // TODO: the error is not guaranteed since getCurrentTest can be already cleared + // https://stackblitz.com/edit/vitest-dev-vitest-44ezrs?file=test%2Fbasic.test.ts + test.skip.concurrent('parallel test 4 with nested test', () => { expect(() => { test('test inside test', () => {}) }).toThrowErrorMatchingInlineSnapshot(`[Error: Calling the test function inside another test function is not allowed. Please put it inside "describe" or "suite" so it can be properly collected.]`)