From 5cc592be9760f6141104ce13aca40a86342bbcca Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 5 Apr 2022 13:04:33 +0300 Subject: [PATCH] feat: add resolveSnapshotPath option (#1101) --- docs/config/index.md | 15 ++++++++++++++ .../src/integrations/snapshot/client.ts | 11 ++-------- .../src/integrations/snapshot/manager.ts | 20 ++++++++++++++++--- packages/vitest/src/node/config.ts | 4 ++++ packages/vitest/src/node/core.ts | 7 ++++++- packages/vitest/src/node/pool.ts | 3 +++ packages/vitest/src/runtime/run.ts | 2 +- packages/vitest/src/types/config.ts | 7 ++++++- packages/vitest/src/types/snapshot.ts | 1 + packages/vitest/src/types/worker.ts | 1 + test/core/test/moved-snapshot.test.ts | 5 +++++ test/core/test/moved-snapshot.test.ts.snap | 3 +++ test/core/vitest.config.ts | 7 ++++++- 13 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 test/core/test/moved-snapshot.test.ts create mode 100644 test/core/test/moved-snapshot.test.ts.snap diff --git a/docs/config/index.md b/docs/config/index.md index 2f32284aceff..92abdabe164f 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -431,3 +431,18 @@ Overrides Vite mode. Run tests only against changed files. If no value is provided, it will run tests against uncomitted changes (includes staged and unstaged). To run tests against changes made in last commit, you can use `--changed HEAD~1`. You can also pass commit hash or branch name. + +### resolveSnapshotPath + +- **Type**: `(testPath: string, snapExtension: string) => string` +- **Default**: stores snapshot files in `__snapshots__` directory + +Overrides default snapshot path. For example, to store snapshots next to test files: + +```ts +export default { + test: { + resolveSnapshotPath: (testPath, snapExtension) => testPath + snapExtension, + }, +} +``` diff --git a/packages/vitest/src/integrations/snapshot/client.ts b/packages/vitest/src/integrations/snapshot/client.ts index 3c6ce256df5d..e6689c4548f2 100644 --- a/packages/vitest/src/integrations/snapshot/client.ts +++ b/packages/vitest/src/integrations/snapshot/client.ts @@ -1,4 +1,3 @@ -import path from 'pathe' import { expect } from 'chai' import type { SnapshotResult, Test } from '../../types' import { rpc } from '../../runtime/rpc' @@ -13,18 +12,12 @@ export interface Context { fullTitle?: string } -const resolveSnapshotPath = (testPath: string) => - path.join( - path.join(path.dirname(testPath), '__snapshots__'), - `${path.basename(testPath)}.snap`, - ) - export class SnapshotClient { test: Test | undefined testFile = '' snapshotState: SnapshotState | undefined - setTest(test: Test) { + async setTest(test: Test) { this.test = test if (this.testFile !== this.test.file!.filepath) { @@ -33,7 +26,7 @@ export class SnapshotClient { this.testFile = this.test!.file!.filepath this.snapshotState = new SnapshotState( - resolveSnapshotPath(this.testFile), + await rpc().resolveSnapshotPath(this.testFile), getWorkerState().config.snapshotOptions, ) } diff --git a/packages/vitest/src/integrations/snapshot/manager.ts b/packages/vitest/src/integrations/snapshot/manager.ts index fade357f94c0..78927b35def7 100644 --- a/packages/vitest/src/integrations/snapshot/manager.ts +++ b/packages/vitest/src/integrations/snapshot/manager.ts @@ -1,19 +1,33 @@ -import type { ResolvedConfig, SnapshotResult, SnapshotStateOptions, SnapshotSummary } from '../../types' +import { basename, dirname, join } from 'pathe' +import type { SnapshotResult, SnapshotStateOptions, SnapshotSummary } from '../../types' export class SnapshotManager { summary: SnapshotSummary = undefined! + extension = '.snap' - constructor(public config: ResolvedConfig) { + constructor(public options: SnapshotStateOptions) { this.clear() } clear() { - this.summary = emptySummary(this.config.snapshotOptions) + this.summary = emptySummary(this.options) } add(result: SnapshotResult) { addSnapshotResult(this.summary, result) } + + resolvePath(testPath: string) { + const resolver = this.options.resolveSnapshotPath || (() => { + return join( + join( + dirname(testPath), '__snapshots__'), + `${basename(testPath)}${this.extension}`, + ) + }) + + return resolver(testPath, this.extension) + } } export function emptySummary(options: SnapshotStateOptions): SnapshotSummary { diff --git a/packages/vitest/src/node/config.ts b/packages/vitest/src/node/config.ts index 35b6e5f88c2e..194d2657300d 100644 --- a/packages/vitest/src/node/config.ts +++ b/packages/vitest/src/node/config.ts @@ -110,8 +110,12 @@ export function resolveConfig( : UPDATE_SNAPSHOT ? 'all' : 'new', + resolveSnapshotPath: options.resolveSnapshotPath, } + if (options.resolveSnapshotPath) + delete (resolved as UserConfig).resolveSnapshotPath + if (process.env.VITEST_MAX_THREADS) resolved.maxThreads = parseInt(process.env.VITEST_MAX_THREADS) diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index d961097ba665..ba852b4facb8 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -62,7 +62,7 @@ export class Vitest { this.server = server this.config = resolved this.state = new StateManager() - this.snapshot = new SnapshotManager(resolved) + this.snapshot = new SnapshotManager({ ...resolved.snapshotOptions }) this.reporters = resolved.reporters .map((i) => { if (typeof i === 'string') { @@ -91,6 +91,11 @@ export class Vitest { const hasCustomReporter = toArray(this.config.reporters) .some(reporter => typeof reporter !== 'string') + // cannot be serialized for sending to workers + // reimplemented on rpc + if (this.config.snapshotOptions.resolveSnapshotPath) + this.config.snapshotOptions.resolveSnapshotPath = undefined + if (!hasCustomReporter && !this.configOverride) return this.config diff --git a/packages/vitest/src/node/pool.ts b/packages/vitest/src/node/pool.ts index d7c5debb84d8..dd5f4a9420d7 100644 --- a/packages/vitest/src/node/pool.ts +++ b/packages/vitest/src/node/pool.ts @@ -118,6 +118,9 @@ function createChannel(ctx: Vitest) { snapshotSaved(snapshot) { ctx.snapshot.add(snapshot) }, + resolveSnapshotPath(testPath: string) { + return ctx.snapshot.resolvePath(testPath) + }, async getSourceMap(id, force) { if (force) { const mod = ctx.server.moduleGraph.getModuleById(id) diff --git a/packages/vitest/src/runtime/run.ts b/packages/vitest/src/runtime/run.ts index 1b1bc0e7c7c9..cccdeb2a66c4 100644 --- a/packages/vitest/src/runtime/run.ts +++ b/packages/vitest/src/runtime/run.ts @@ -78,7 +78,7 @@ export async function runTest(test: Test) { clearModuleMocks() - getSnapshotClient().setTest(test) + await getSnapshotClient().setTest(test) const workerState = getWorkerState() diff --git a/packages/vitest/src/types/config.ts b/packages/vitest/src/types/config.ts index 37e8bd8bc0cc..83fddf78fd61 100644 --- a/packages/vitest/src/types/config.ts +++ b/packages/vitest/src/types/config.ts @@ -290,6 +290,11 @@ export interface InlineConfig { * Format options for snapshot testing. */ snapshotFormat?: PrettyFormatOptions + + /** + * Resolve custom snapshot path + */ + resolveSnapshotPath?: (path: string, extension: string) => string } export interface UserConfig extends InlineConfig { @@ -338,7 +343,7 @@ export interface UserConfig extends InlineConfig { changed?: boolean | string } -export interface ResolvedConfig extends Omit, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters'> { +export interface ResolvedConfig extends Omit, 'config' | 'filters' | 'coverage' | 'testNamePattern' | 'related' | 'api' | 'reporters' | 'resolveSnapshotPath'> { base?: string config?: string diff --git a/packages/vitest/src/types/snapshot.ts b/packages/vitest/src/types/snapshot.ts index abedf718f970..a85ed9fde82d 100644 --- a/packages/vitest/src/types/snapshot.ts +++ b/packages/vitest/src/types/snapshot.ts @@ -8,6 +8,7 @@ export interface SnapshotStateOptions { updateSnapshot: SnapshotUpdateState expand?: boolean snapshotFormat?: PrettyFormatOptions + resolveSnapshotPath?: (path: string, extension: string) => string } export interface SnapshotMatchOptions { diff --git a/packages/vitest/src/types/worker.ts b/packages/vitest/src/types/worker.ts index d4c2fcd50454..bb2717620750 100644 --- a/packages/vitest/src/types/worker.ts +++ b/packages/vitest/src/types/worker.ts @@ -29,6 +29,7 @@ export interface WorkerRPC { onTaskUpdate: (pack: TaskResultPack[]) => void snapshotSaved: (snapshot: SnapshotResult) => void + resolveSnapshotPath: (testPath: string) => string } export interface WorkerGlobalState { diff --git a/test/core/test/moved-snapshot.test.ts b/test/core/test/moved-snapshot.test.ts new file mode 100644 index 000000000000..fba722827d52 --- /dev/null +++ b/test/core/test/moved-snapshot.test.ts @@ -0,0 +1,5 @@ +import { expect, test } from 'vitest' + +test('snapshot is stored close to file', () => { + expect('moved snapshot').toMatchSnapshot() +}) diff --git a/test/core/test/moved-snapshot.test.ts.snap b/test/core/test/moved-snapshot.test.ts.snap new file mode 100644 index 000000000000..7ead04e42027 --- /dev/null +++ b/test/core/test/moved-snapshot.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1 + +exports[`snapshot is stored close to file 1`] = `"moved snapshot"`; diff --git a/test/core/vitest.config.ts b/test/core/vitest.config.ts index f444eb55c357..5849761fc3fe 100644 --- a/test/core/vitest.config.ts +++ b/test/core/vitest.config.ts @@ -1,4 +1,4 @@ -import { resolve } from 'pathe' +import { basename, dirname, join, resolve } from 'pathe' import { defineConfig } from 'vite' export default defineConfig({ @@ -45,5 +45,10 @@ export default defineConfig({ coverage: { reporter: ['text', 'html'], }, + resolveSnapshotPath: (path, extension) => { + if (path.includes('moved-snapshot')) + return path + extension + return join(dirname(path), '__snapshots__', `${basename(path)}${extension}`) + }, }, })