From 746d898651fc30aaa0d2a47af9ceeaaaa0432311 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Thu, 14 Nov 2024 01:04:34 +0900 Subject: [PATCH] feat(snapshot): provide `config` to `resolveSnapshotPath` (#6800) --- docs/config/index.md | 2 +- packages/browser/src/node/rpc.ts | 6 ++++-- packages/snapshot/src/manager.ts | 4 ++-- packages/snapshot/src/types/index.ts | 2 +- packages/vitest/src/node/pools/rpc.ts | 5 ++++- packages/vitest/src/node/types/config.ts | 11 ++++++++++- packages/vitest/src/public/node.ts | 2 ++ .../__snapshots__/project1/basic.test.ts.snap | 3 +++ .../__snapshots__/project2/basic.test.ts.snap | 3 +++ .../snapshot-path-context/basic.test.ts | 5 +++++ .../snapshot-path-context/vitest.config.ts | 15 +++++++++++++++ .../snapshot-path-context/vitest.workspace.ts | 18 ++++++++++++++++++ test/config/test/snapshot.test.ts | 19 +++++++++++++++++++ 13 files changed, 87 insertions(+), 8 deletions(-) create mode 100644 test/config/fixtures/snapshot-path-context/__snapshots__/project1/basic.test.ts.snap create mode 100644 test/config/fixtures/snapshot-path-context/__snapshots__/project2/basic.test.ts.snap create mode 100644 test/config/fixtures/snapshot-path-context/basic.test.ts create mode 100644 test/config/fixtures/snapshot-path-context/vitest.config.ts create mode 100644 test/config/fixtures/snapshot-path-context/vitest.workspace.ts create mode 100644 test/config/test/snapshot.test.ts diff --git a/docs/config/index.md b/docs/config/index.md index 7a441d0687f0..0bab567ee59f 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -1899,7 +1899,7 @@ A list of paths to snapshot serializer modules for snapshot testing, useful if y ### resolveSnapshotPath -- **Type**: `(testPath: string, snapExtension: string) => string` +- **Type**: `(testPath: string, snapExtension: string, context: { config: SerializedConfig }) => string` - **Default**: stores snapshot files in `__snapshots__` directory Overrides default snapshot path. For example, to store snapshots next to test files: diff --git a/packages/browser/src/node/rpc.ts b/packages/browser/src/node/rpc.ts index 6d37821be88e..0147438cddb9 100644 --- a/packages/browser/src/node/rpc.ts +++ b/packages/browser/src/node/rpc.ts @@ -1,5 +1,5 @@ import type { ErrorWithDiff } from 'vitest' -import type { BrowserCommandContext } from 'vitest/node' +import type { BrowserCommandContext, ResolveSnapshotPathHandlerContext } from 'vitest/node' import type { WebSocket } from 'ws' import type { BrowserServer } from './server' import type { WebSocketBrowserEvents, WebSocketBrowserHandlers } from './types' @@ -90,7 +90,9 @@ export function setupBrowserRpc(server: BrowserServer) { return ctx.report('onUserConsoleLog', log) }, resolveSnapshotPath(testPath) { - return ctx.snapshot.resolvePath(testPath) + return ctx.snapshot.resolvePath(testPath, { + config: project.getSerializableConfig(), + }) }, resolveSnapshotRawPath(testPath, rawPath) { return ctx.snapshot.resolveRawPath(testPath, rawPath) diff --git a/packages/snapshot/src/manager.ts b/packages/snapshot/src/manager.ts index 5a4caa0029af..11ed69847aab 100644 --- a/packages/snapshot/src/manager.ts +++ b/packages/snapshot/src/manager.ts @@ -23,7 +23,7 @@ export class SnapshotManager { addSnapshotResult(this.summary, result) } - resolvePath(testPath: string): string { + resolvePath(testPath: string, context?: T): string { const resolver = this.options.resolveSnapshotPath || (() => { return join( @@ -32,7 +32,7 @@ export class SnapshotManager { ) }) - const path = resolver(testPath, this.extension) + const path = resolver(testPath, this.extension, context) return path } diff --git a/packages/snapshot/src/types/index.ts b/packages/snapshot/src/types/index.ts index d1bf8af4fac8..b5378e5a8cb6 100644 --- a/packages/snapshot/src/types/index.ts +++ b/packages/snapshot/src/types/index.ts @@ -20,7 +20,7 @@ export interface SnapshotStateOptions { snapshotEnvironment: SnapshotEnvironment expand?: boolean snapshotFormat?: PrettyFormatOptions - resolveSnapshotPath?: (path: string, extension: string) => string + resolveSnapshotPath?: (path: string, extension: string, context?: any) => string } export interface SnapshotMatchOptions { diff --git a/packages/vitest/src/node/pools/rpc.ts b/packages/vitest/src/node/pools/rpc.ts index 9018c2b9a6bd..28b65a4bcd97 100644 --- a/packages/vitest/src/node/pools/rpc.ts +++ b/packages/vitest/src/node/pools/rpc.ts @@ -1,5 +1,6 @@ import type { RawSourceMap } from 'vite-node' import type { RuntimeRPC } from '../../types/rpc' +import type { ResolveSnapshotPathHandlerContext } from '../types/config' import type { WorkspaceProject } from '../workspace' import { mkdir, writeFile } from 'node:fs/promises' import { join } from 'pathe' @@ -20,7 +21,9 @@ export function createMethodsRPC(project: WorkspaceProject, options: MethodsOpti ctx.snapshot.add(snapshot) }, resolveSnapshotPath(testPath: string) { - return ctx.snapshot.resolvePath(testPath) + return ctx.snapshot.resolvePath(testPath, { + config: project.getSerializableConfig(), + }) }, async getSourceMap(id, force) { if (force) { diff --git a/packages/vitest/src/node/types/config.ts b/packages/vitest/src/node/types/config.ts index 1eae8e3c1bb8..fe600405ddc1 100644 --- a/packages/vitest/src/node/types/config.ts +++ b/packages/vitest/src/node/types/config.ts @@ -6,6 +6,7 @@ import type { SerializedDiffOptions } from '@vitest/utils/diff' import type { AliasOptions, ConfigEnv, DepOptimizationConfig, ServerOptions, UserConfig as ViteUserConfig } from 'vite' import type { ViteNodeServerOptions } from 'vite-node' import type { ChaiConfig } from '../../integrations/chai/config' +import type { SerializedConfig } from '../../runtime/config' import type { EnvironmentOptions } from '../../types/environment' import type { Arrayable, ErrorWithDiff, ParsedStack, ProvidedContext } from '../../types/general' import type { HappyDOMOptions } from '../../types/happy-dom-options' @@ -225,6 +226,14 @@ type ReporterWithOptions = : [Name, Partial] : [Name, Record] +export interface ResolveSnapshotPathHandlerContext { config: SerializedConfig } + +export type ResolveSnapshotPathHandler = ( + testPath: string, + snapExtension: string, + context: ResolveSnapshotPathHandlerContext +) => string + export interface InlineConfig { /** * Name of the project. Will be used to display in the reporter. @@ -574,7 +583,7 @@ export interface InlineConfig { /** * Resolve custom snapshot path */ - resolveSnapshotPath?: (path: string, extension: string) => string + resolveSnapshotPath?: ResolveSnapshotPathHandler /** * Path to a custom snapshot environment module that has a default export of `SnapshotEnvironment` object. diff --git a/packages/vitest/src/public/node.ts b/packages/vitest/src/public/node.ts index e46a3911134e..c8ba9ef7679f 100644 --- a/packages/vitest/src/public/node.ts +++ b/packages/vitest/src/public/node.ts @@ -79,6 +79,8 @@ export type { ProjectConfig, ResolvedConfig, ResolvedProjectConfig, + ResolveSnapshotPathHandler, + ResolveSnapshotPathHandlerContext, RuntimeConfig, SequenceHooks, SequenceSetupFiles, diff --git a/test/config/fixtures/snapshot-path-context/__snapshots__/project1/basic.test.ts.snap b/test/config/fixtures/snapshot-path-context/__snapshots__/project1/basic.test.ts.snap new file mode 100644 index 000000000000..ec0254df2305 --- /dev/null +++ b/test/config/fixtures/snapshot-path-context/__snapshots__/project1/basic.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`basic 1`] = `"hello"`; diff --git a/test/config/fixtures/snapshot-path-context/__snapshots__/project2/basic.test.ts.snap b/test/config/fixtures/snapshot-path-context/__snapshots__/project2/basic.test.ts.snap new file mode 100644 index 000000000000..ec0254df2305 --- /dev/null +++ b/test/config/fixtures/snapshot-path-context/__snapshots__/project2/basic.test.ts.snap @@ -0,0 +1,3 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`basic 1`] = `"hello"`; diff --git a/test/config/fixtures/snapshot-path-context/basic.test.ts b/test/config/fixtures/snapshot-path-context/basic.test.ts new file mode 100644 index 000000000000..4cb90a2abcc7 --- /dev/null +++ b/test/config/fixtures/snapshot-path-context/basic.test.ts @@ -0,0 +1,5 @@ +import { test, expect } from 'vitest' + +test('basic', () => { + expect('hello').toMatchSnapshot() +}) diff --git a/test/config/fixtures/snapshot-path-context/vitest.config.ts b/test/config/fixtures/snapshot-path-context/vitest.config.ts new file mode 100644 index 000000000000..a03d2b41cc56 --- /dev/null +++ b/test/config/fixtures/snapshot-path-context/vitest.config.ts @@ -0,0 +1,15 @@ +import { join, dirname, basename } from 'node:path'; +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + resolveSnapshotPath(path, extension, context) { + return join( + dirname(path), + '__snapshots__', + context.config.name ?? 'na', + basename(path) + extension + ); + }, + }, +}); diff --git a/test/config/fixtures/snapshot-path-context/vitest.workspace.ts b/test/config/fixtures/snapshot-path-context/vitest.workspace.ts new file mode 100644 index 000000000000..30764821b1e0 --- /dev/null +++ b/test/config/fixtures/snapshot-path-context/vitest.workspace.ts @@ -0,0 +1,18 @@ +import { defineWorkspace } from 'vitest/config' + +export default defineWorkspace([ + { + extends: './vitest.config.ts', + test: { + name: 'project1', + root: import.meta.dirname, + } + }, + { + extends: './vitest.config.ts', + test: { + name: 'project2', + root: import.meta.dirname, + } + } +]) diff --git a/test/config/test/snapshot.test.ts b/test/config/test/snapshot.test.ts new file mode 100644 index 000000000000..9e890cc40062 --- /dev/null +++ b/test/config/test/snapshot.test.ts @@ -0,0 +1,19 @@ +import { expect, test } from 'vitest' +import { runVitest } from '../../test-utils' + +test('resolveSnapshotPath context', async () => { + const { stderr, ctx } = await runVitest({ + root: './fixtures/snapshot-path-context', + }) + expect(stderr).toBe('') + expect( + Object.fromEntries( + ctx!.state.getFiles().map(f => [`${f.projectName}|${f.name}`, f.result?.state]), + ), + ).toMatchInlineSnapshot(` + { + "project1|basic.test.ts": "pass", + "project2|basic.test.ts": "pass", + } + `) +})