vitest-plugin-vis 4.2.2 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/dist/auto_snapshots/_extract_auto_snapshot_options.mjs +1 -1
  2. package/dist/auto_snapshots/_extract_auto_snapshot_options.mjs.map +1 -1
  3. package/dist/client/external/vitest/vitest_browser_context_proxy.mjs.map +1 -1
  4. package/dist/client/external/vitest/vitest_suite_proxy.d.mts +1 -3
  5. package/dist/client/external/vitest/vitest_suite_proxy.mjs +5 -5
  6. package/dist/client/external/vitest/vitest_suite_proxy.mjs.map +1 -1
  7. package/dist/client/page/_ctx.d.mts +1 -1
  8. package/dist/client/page/_ctx.mjs.map +1 -1
  9. package/dist/client/snapshot/compare_image_snapshot.logic.mjs +2 -1
  10. package/dist/client/snapshot/compare_image_snapshot.logic.mjs.map +1 -1
  11. package/dist/client/snapshot/compare_image_snapshot.mjs +1 -1
  12. package/dist/client/snapshot/compare_image_snapshot.mjs.map +1 -1
  13. package/dist/client/suite/auto_snapshot_matcher.d.mts +2 -2
  14. package/dist/client/suite/auto_snapshot_matcher.d.mts.map +1 -1
  15. package/dist/client/suite/auto_snapshot_matcher.mjs +4 -2
  16. package/dist/client/suite/auto_snapshot_matcher.mjs.map +1 -1
  17. package/dist/config/types.d.mts +15 -3
  18. package/dist/config/vis.mjs +1 -1
  19. package/dist/config/vis.mjs.map +1 -1
  20. package/dist/{client-api.d.mts → exports/client-api.d.mts} +12 -12
  21. package/dist/exports/client-api.mjs +11 -0
  22. package/dist/exports/config.d.mts +5 -0
  23. package/dist/exports/config.mjs +4 -0
  24. package/dist/exports/presets/auto.mjs +9 -0
  25. package/dist/exports/presets/auto.mjs.map +1 -0
  26. package/dist/exports/presets/manual.mjs +9 -0
  27. package/dist/exports/presets/manual.mjs.map +1 -0
  28. package/dist/exports/server-utils.d.mts +5 -0
  29. package/dist/exports/server-utils.mjs +6 -0
  30. package/dist/exports/setup-api.d.mts +4 -0
  31. package/dist/exports/setup-api.mjs +5 -0
  32. package/dist/exports/setup.d.mts +3 -0
  33. package/dist/exports/setup.mjs +4 -0
  34. package/dist/exports/testing/node.d.mts +4 -0
  35. package/dist/exports/testing/node.mjs +5 -0
  36. package/dist/exports/testing.d.mts +2 -0
  37. package/dist/exports/testing.mjs +3 -0
  38. package/dist/server/commands/prepare_image_snapshot_comparison.mjs.map +1 -1
  39. package/dist/server/commands/prepare_page_image_snapshot_comparison.mjs.map +1 -1
  40. package/dist/server/snapshot_path.d.mts +0 -1
  41. package/dist/server/snapshot_path.d.mts.map +1 -1
  42. package/dist/server/snapshot_path_limits.d.mts +28 -0
  43. package/dist/server/snapshot_path_limits.d.mts.map +1 -0
  44. package/dist/server/snapshot_path_limits.mjs +92 -0
  45. package/dist/server/snapshot_path_limits.mjs.map +1 -0
  46. package/dist/server/suite.mjs +8 -2
  47. package/dist/server/suite.mjs.map +1 -1
  48. package/dist/server/testing/stubBrowserCommandContext.mjs +9 -0
  49. package/dist/server/testing/stubBrowserCommandContext.mjs.map +1 -0
  50. package/dist/server/testing/stubSuite.d.mts +17 -0
  51. package/dist/server/testing/stubSuite.d.mts.map +1 -0
  52. package/dist/server/testing/stubSuite.mjs +23 -0
  53. package/dist/server/testing/stubSuite.mjs.map +1 -0
  54. package/dist/server/testing/stubUserConfig.mjs +8 -0
  55. package/dist/server/testing/stubUserConfig.mjs.map +1 -0
  56. package/dist/server/vis_options.d.mts +120 -0
  57. package/dist/server/vis_options.d.mts.map +1 -0
  58. package/dist/server/vis_options.mjs +7 -1
  59. package/dist/server/vis_options.mjs.map +1 -1
  60. package/dist/server/vis_server_context.logic.mjs +37 -10
  61. package/dist/server/vis_server_context.logic.mjs.map +1 -1
  62. package/dist/server/vis_server_context.types.d.mts +32 -2
  63. package/dist/server/vis_server_context.types.d.mts.map +1 -0
  64. package/dist/setup/create_vis.d.mts +1 -66
  65. package/dist/setup/create_vis.d.mts.map +1 -1
  66. package/dist/setup/create_vis.mjs +9 -45
  67. package/dist/setup/create_vis.mjs.map +1 -1
  68. package/dist/shared/types.d.mts +21 -1
  69. package/dist/shared/types.d.mts.map +1 -1
  70. package/package.json +22 -49
  71. package/readme.md +10 -4
  72. package/src/auto_snapshots/_extract_auto_snapshot_options.ts +1 -1
  73. package/src/client/expect/to_match_image_snapshot.mdx +24 -1
  74. package/src/client/external/vitest/vitest_browser_context_proxy.ts +2 -2
  75. package/src/client/external/vitest/vitest_suite_proxy.ts +6 -6
  76. package/src/client/page/_ctx.ts +2 -2
  77. package/src/client/page/to_match_image_snapshot.mdx +23 -1
  78. package/src/client/snapshot/compare_image_snapshot.logic.ts +1 -0
  79. package/src/client/snapshot/compare_image_snapshot.ts +2 -2
  80. package/src/client/suite/auto_snapshot_matcher.ts +9 -1
  81. package/src/config/types.ts +15 -3
  82. package/src/config/vis.ts +1 -1
  83. package/src/exports/client-api.ts +13 -0
  84. package/src/exports/config.ts +3 -0
  85. package/src/exports/presets/auto.ts +3 -0
  86. package/src/exports/presets/manual.ts +3 -0
  87. package/src/exports/server-utils.ts +7 -0
  88. package/src/exports/setup-api.ts +5 -0
  89. package/src/exports/setup.ts +3 -0
  90. package/src/exports/testing/node.ts +4 -0
  91. package/src/exports/testing.ts +1 -0
  92. package/src/server/commands/prepare_image_snapshot_comparison.ts +2 -2
  93. package/src/server/commands/prepare_page_image_snapshot_comparison.ts +2 -1
  94. package/src/server/snapshot_path_limits.ts +135 -0
  95. package/src/server/suite.ts +19 -4
  96. package/src/server/vis_options.ts +10 -1
  97. package/src/server/vis_server_context.logic.ts +47 -17
  98. package/src/setup/create_vis.mdx +2 -6
  99. package/src/setup/create_vis.ts +13 -123
  100. package/src/shared/types.ts +21 -1
  101. package/dist/client-api.mjs +0 -11
  102. package/dist/config.d.mts +0 -5
  103. package/dist/config.mjs +0 -4
  104. package/dist/presets/auto.mjs +0 -16
  105. package/dist/presets/auto.mjs.map +0 -1
  106. package/dist/presets/enable.d.mts +0 -1
  107. package/dist/presets/enable.mjs +0 -11
  108. package/dist/presets/enable.mjs.map +0 -1
  109. package/dist/presets/manual.mjs +0 -11
  110. package/dist/presets/manual.mjs.map +0 -1
  111. package/dist/server-utils.d.mts +0 -4
  112. package/dist/server-utils.mjs +0 -5
  113. package/dist/setup-api.d.mts +0 -4
  114. package/dist/setup-api.mjs +0 -5
  115. package/dist/setup.d.mts +0 -13
  116. package/dist/setup.d.mts.map +0 -1
  117. package/dist/setup.mjs +0 -13
  118. package/dist/setup.mjs.map +0 -1
  119. package/dist/testing.d.mts +0 -2
  120. package/dist/testing.mjs +0 -3
  121. package/src/client-api.ts +0 -13
  122. package/src/config.ts +0 -3
  123. package/src/presets/auto.ts +0 -9
  124. package/src/presets/enable.ts +0 -5
  125. package/src/presets/manual.ts +0 -5
  126. package/src/server-utils.ts +0 -3
  127. package/src/setup-api.ts +0 -5
  128. package/src/setup.ts +0 -10
  129. package/src/testing.ts +0 -1
  130. /package/dist/{presets → exports/presets}/auto.d.mts +0 -0
  131. /package/dist/{presets → exports/presets}/manual.d.mts +0 -0
@@ -0,0 +1,135 @@
1
+ import { createHash } from 'node:crypto'
2
+ import { basename, dirname, extname, join, resolve } from 'pathe'
3
+
4
+ /** Classic Win32 `MAX_PATH` includes NUL; tools often treat ~259 as usable. */
5
+ export const WIN_MAX_PATH = 260
6
+
7
+ /**
8
+ * Budget under {@link WIN_MAX_PATH} to leave margin for tooling and long `projectRoot` prefixes.
9
+ */
10
+ export const WIN_SNAPSHOT_PATH_BUDGET = 250
11
+
12
+ /** NTFS maximum length of a single path component (directory or file name). */
13
+ export const NTFS_MAX_FILENAME = 255
14
+
15
+ /** Max length of the readable prefix taken from the original basename (tersify-style). */
16
+ const SNAPSHOT_SUBPATH_PREFIX_MAX_LEN = 24
17
+
18
+ /**
19
+ * Minimal probe file under the snapshot folder — detects when the directory prefix alone is too long.
20
+ */
21
+ const SNAPSHOT_SUBPATH_PROBE_MIN_BASENAME = '0.png'
22
+
23
+ export function absoluteSnapshotFilePath(projectRoot: string, baselineDir: string, snapshotFilename: string): string {
24
+ return resolve(projectRoot, join(baselineDir, snapshotFilename))
25
+ }
26
+
27
+ /**
28
+ * @returns true when the legacy absolute path should be shortened for Windows-safe tooling.
29
+ */
30
+ export function legacySnapshotPathExceedsLimit(absolutePath: string): boolean {
31
+ if (absolutePath.length >= WIN_SNAPSHOT_PATH_BUDGET) return true
32
+ if (basename(absolutePath).length > NTFS_MAX_FILENAME) return true
33
+ return false
34
+ }
35
+
36
+ /**
37
+ * First readable token of a snapshot basename stem (before the first `.`), similar in spirit to
38
+ * [tersify](https://github.com/unional/tersify) terse labels — keeps a short human hint, not the whole name.
39
+ */
40
+ function snapshotBasenameTersifyPrefix(stem: string): string {
41
+ if (!stem) return 'snap'
42
+ const dot = stem.indexOf('.')
43
+ const first = dot === -1 ? stem : stem.slice(0, dot)
44
+ const cleaned = first.replace(/[^\w-]/g, '')
45
+ return (cleaned || 'snap').slice(0, SNAPSHOT_SUBPATH_PREFIX_MAX_LEN)
46
+ }
47
+
48
+ /**
49
+ * Keeps all directory segments of {@link rawSnapshotSubpath}; only the final filename is replaced with
50
+ * `{prefix}-{hash12}{ext}` where `prefix` comes from the first segment of the original stem (e.g. `Button.stories.ts` → `Button`).
51
+ */
52
+ export function formatCondensedSnapshotSubpath(rawSnapshotSubpath: string): string {
53
+ const norm = rawSnapshotSubpath.replace(/\\/g, '/')
54
+ const dir = dirname(norm)
55
+ const file = basename(norm)
56
+ const ext = extname(file)
57
+ const stem = ext ? file.slice(0, -ext.length) : file
58
+ const prefix = snapshotBasenameTersifyPrefix(stem)
59
+ const h = createHash('sha256').update(norm).digest('hex').slice(0, 12)
60
+ const shortFile = `${prefix}-${h}${ext}`
61
+ if (dir === '.' || dir === '') return shortFile
62
+ return join(dir, shortFile)
63
+ }
64
+
65
+ /**
66
+ * `snapshotBaselineRootAbs` is the absolute `__baselines__` root; with `rawSnapshotSubpath` this yields the
67
+ * test-module snapshot directory. Only checks whether that layout can host a tiny baseline file; long
68
+ * `taskId-key.png` paths are handled with {@link resolveLegacySnapshotFileRelativePath} using real names.
69
+ */
70
+ function rawSnapshotSubpathExceedsSafePathBudget(snapshotBaselineRootAbs: string, rawSnapshotSubpath: string): boolean {
71
+ const dirAbs = resolve(snapshotBaselineRootAbs, rawSnapshotSubpath)
72
+
73
+ if (legacySnapshotPathExceedsLimit(resolve(dirAbs, SNAPSHOT_SUBPATH_PROBE_MIN_BASENAME))) return true
74
+
75
+ for (const segment of rawSnapshotSubpath.split(/[/\\]/)) {
76
+ if (segment.length > NTFS_MAX_FILENAME) return true
77
+ }
78
+
79
+ return false
80
+ }
81
+
82
+ /**
83
+ * Returns {@link rawSnapshotSubpath} or a tersified final segment when `shortenLongSnapshotPaths` is on and
84
+ * `snapshotBaselineRootAbs` + subpath + a minimal `0.png` would exceed {@link legacySnapshotPathExceedsLimit},
85
+ * or a subpath segment exceeds NTFS length. Long snapshot **filenames** (`taskId-key.png`) are shortened
86
+ * separately via {@link resolveLegacySnapshotFileRelativePath}. Baseline, result, and diff trees share the
87
+ * same resolved subpath via {@link getTaskSubpath}.
88
+ */
89
+ export function resolveSnapshotSubpathWithinLimits(params: {
90
+ snapshotBaselineRootAbs: string
91
+ rawSnapshotSubpath: string
92
+ shortenLongSnapshotPaths: boolean
93
+ }): string {
94
+ const { snapshotBaselineRootAbs, rawSnapshotSubpath, shortenLongSnapshotPaths } = params
95
+ if (!shortenLongSnapshotPaths) return rawSnapshotSubpath
96
+
97
+ if (!rawSnapshotSubpathExceedsSafePathBudget(snapshotBaselineRootAbs, rawSnapshotSubpath)) {
98
+ return rawSnapshotSubpath
99
+ }
100
+
101
+ return formatCondensedSnapshotSubpath(rawSnapshotSubpath)
102
+ }
103
+
104
+ /**
105
+ * When `shortenLongSnapshotPaths` is on and the legacy `{@link getLegacySnapshotFilename}` path exceeds
106
+ * {@link legacySnapshotPathExceedsLimit}, rewrites only the final path segment (after the last `/`) like
107
+ * {@link formatCondensedSnapshotSubpath}, so nested `taskId` directories stay intact and `…-*.png` globs keep working.
108
+ */
109
+ export function resolveLegacySnapshotFileRelativePath(params: {
110
+ projectRoot: string
111
+ baselineDir: string
112
+ legacySnapshotFilename: string
113
+ shortenLongSnapshotPaths: boolean
114
+ }): string {
115
+ const { projectRoot, baselineDir, legacySnapshotFilename, shortenLongSnapshotPaths } = params
116
+ if (!shortenLongSnapshotPaths) return legacySnapshotFilename
117
+
118
+ const norm = legacySnapshotFilename.replace(/\\/g, '/')
119
+ const abs = absoluteSnapshotFilePath(projectRoot, baselineDir, norm)
120
+ if (!legacySnapshotPathExceedsLimit(abs)) return legacySnapshotFilename
121
+
122
+ return formatCondensedSnapshotSubpath(norm)
123
+ }
124
+
125
+ export function getLegacySnapshotFilename(params: {
126
+ taskId: string
127
+ explicitSnapshotKey: string | undefined
128
+ defaultSnapshotKey: string | undefined
129
+ taskCount: number
130
+ }): string {
131
+ const key =
132
+ params.explicitSnapshotKey ??
133
+ (typeof params.defaultSnapshotKey === 'string' ? params.defaultSnapshotKey : String(params.taskCount))
134
+ return `${params.taskId}-${key}.png`
135
+ }
@@ -1,8 +1,9 @@
1
- import { join, relative } from 'pathe'
1
+ import { join, relative, resolve } from 'pathe'
2
2
  import type { VisOptions } from '../config/types.ts'
3
3
  import { BASELINE_DIR, DIFF_DIR, RESULT_DIR } from '../shared/constants.ts'
4
4
  import { getProjectName, getProjectRoot } from './project.ts'
5
5
  import { getSnapshotSubpath, resolveSnapshotRootDir } from './snapshot_path.ts'
6
+ import { resolveSnapshotSubpathWithinLimits } from './snapshot_path_limits.ts'
6
7
  import { getVisOption } from './vis_options.ts'
7
8
  import { deps } from './vis_server_context.deps.ts'
8
9
  import type { ExtendedBrowserCommandContext, VisSuite, VisSuites } from './vis_server_context.types.ts'
@@ -75,7 +76,11 @@ async function createSuite(
75
76
  return state
76
77
  }
77
78
 
78
- export function createModule(state: VisSuite, testPath: string, options: Pick<VisOptions, 'snapshotSubpath'>) {
79
+ export function createModule(
80
+ state: VisSuite,
81
+ testPath: string,
82
+ options: Pick<VisOptions, 'snapshotSubpath' | 'shortenLongSnapshotPaths'>,
83
+ ) {
79
84
  const taskSubpath = getTaskSubpath(state, testPath, options)
80
85
  return {
81
86
  taskSubpath,
@@ -86,8 +91,18 @@ export function createModule(state: VisSuite, testPath: string, options: Pick<Vi
86
91
  }
87
92
  }
88
93
 
89
- export function getTaskSubpath(state: VisSuite, testPath: string, options: Pick<VisOptions, 'snapshotSubpath'>) {
90
- return getSnapshotSubpath(relative(state.projectRoot, testPath), options)
94
+ export function getTaskSubpath(
95
+ state: VisSuite,
96
+ testPath: string,
97
+ options: Pick<VisOptions, 'snapshotSubpath' | 'shortenLongSnapshotPaths'>,
98
+ ) {
99
+ const raw = getSnapshotSubpath(relative(state.projectRoot, testPath), options)
100
+ const snapshotBaselineRootAbs = resolve(state.projectRoot, state.snapshotBaselineDir)
101
+ return resolveSnapshotSubpathWithinLimits({
102
+ snapshotBaselineRootAbs,
103
+ rawSnapshotSubpath: raw,
104
+ shortenLongSnapshotPaths: options.shortenLongSnapshotPaths === true,
105
+ })
91
106
  }
92
107
 
93
108
  export function getSuite(context: ExtendedBrowserCommandContext) {
@@ -8,6 +8,13 @@ const DEFAULT_PROJECT_NAME = '__default'
8
8
 
9
9
  const visOptions: Record<string, VisOptions<any> | undefined> = {}
10
10
 
11
+ /** Clears stored options (for tests that share this module). */
12
+ export function clearVisOptionsRegistryForTesting() {
13
+ for (const key of Object.keys(visOptions)) {
14
+ delete visOptions[key]
15
+ }
16
+ }
17
+
11
18
  export function setVisOption(
12
19
  userConfig: { test?: { name?: TestUserConfig['name'] | undefined } },
13
20
  options: VisOptions<any> | undefined,
@@ -22,5 +29,7 @@ export function setVisOption(
22
29
 
23
30
  export function getVisOption(context: ExtendedBrowserCommandContext) {
24
31
  const id = getProjectName(context) || DEFAULT_PROJECT_NAME
25
- return visOptions[id] ?? {}
32
+ const base = visOptions[DEFAULT_PROJECT_NAME] ?? {}
33
+ const override = visOptions[id] ?? {}
34
+ return { ...base, ...override }
26
35
  }
@@ -1,8 +1,13 @@
1
1
  import { basename, join, resolve } from 'pathe'
2
2
  import { pick } from 'type-plus'
3
- import type { ImageSnapshotKeyOptions } from '../client-api.ts'
3
+ import type { ImageSnapshotKeyOptions } from '../exports/client-api.ts'
4
4
  import type { ImageSnapshotResult } from '../shared/commands.types.ts'
5
5
  import { file } from './externals/file.ts'
6
+ import {
7
+ absoluteSnapshotFilePath,
8
+ getLegacySnapshotFilename,
9
+ resolveLegacySnapshotFileRelativePath,
10
+ } from './snapshot_path_limits.ts'
6
11
  import { getSuite, getTaskSubpath } from './suite.ts'
7
12
  import { getVisOption } from './vis_options.ts'
8
13
  import type { ExtendedBrowserCommandContext } from './vis_server_context.types.ts'
@@ -11,16 +16,23 @@ export function createVisServerContext() {
11
16
  const context = {
12
17
  async getSnapshotResults(browserContext: ExtendedBrowserCommandContext, taskId: string) {
13
18
  const suiteInfo = await context.getSuiteInfo(browserContext, taskId)
14
- const baselines = await file.glob(join(suiteInfo.projectRoot, suiteInfo.baselineDir, `${suiteInfo.taskId}-*.png`))
15
- const results = await file.glob(join(suiteInfo.projectRoot, suiteInfo.resultDir, `${suiteInfo.taskId}-*.png`))
16
- const diffs = await file.glob(join(suiteInfo.projectRoot, suiteInfo.diffDir, `${suiteInfo.taskId}-*.png`))
19
+ const base = resolve(suiteInfo.projectRoot, suiteInfo.baselineDir)
20
+
21
+ const legacyPattern = join(base, `${suiteInfo.taskId}-*.png`)
22
+ const baselines = await file.glob(legacyPattern)
23
+
24
+ const resultPattern = join(resolve(suiteInfo.projectRoot, suiteInfo.resultDir), `${suiteInfo.taskId}-*.png`)
25
+ const results = await file.glob(resultPattern)
26
+
27
+ const diffPattern = join(resolve(suiteInfo.projectRoot, suiteInfo.diffDir), `${suiteInfo.taskId}-*.png`)
28
+ const diffs = await file.glob(diffPattern)
17
29
 
18
30
  const r: ImageSnapshotResult[] = []
19
31
  await Promise.all(
20
32
  baselines.map(async (baselinePath) => {
21
33
  const filename = basename(baselinePath)
22
- const resultPath = results.find((r) => r.endsWith(filename))
23
- const diffPath = diffs.find((d) => d.endsWith(filename))
34
+ const resultPath = results.find((p) => p.endsWith(filename))
35
+ const diffPath = diffs.find((p) => p.endsWith(filename))
24
36
 
25
37
  const baselineBuffer = await file.tryReadFile(baselinePath)
26
38
  const resultBuffer = await file.tryReadFile(resultPath)
@@ -45,7 +57,7 @@ export function createVisServerContext() {
45
57
 
46
58
  const { baselineDir, resultDir, diffDir, task } = suiteInfo
47
59
 
48
- task.count = task.count + 1
60
+ task.count += 1
49
61
  const baselinePath = join(baselineDir, snapshotFilename)
50
62
  const resultPath = join(resultDir, snapshotFilename)
51
63
  const diffPath = join(diffDir, snapshotFilename)
@@ -74,22 +86,40 @@ export function createVisServerContext() {
74
86
  snapshotKey: string | undefined,
75
87
  ) {
76
88
  const info = await context.getSuiteInfo(browserContext, taskId)
77
-
78
- return file.existFile(
79
- resolve(info.projectRoot, info.baselineDir, context.getSnapshotFilename(browserContext, info, snapshotKey)),
80
- )
89
+ const visOptions = getVisOption(browserContext)
90
+ const legacyFilename = getLegacySnapshotFilename({
91
+ taskId: info.taskId,
92
+ explicitSnapshotKey: snapshotKey,
93
+ defaultSnapshotKey: typeof visOptions.snapshotKey === 'string' ? visOptions.snapshotKey : undefined,
94
+ taskCount: info.task.count,
95
+ })
96
+ const filename = resolveLegacySnapshotFileRelativePath({
97
+ projectRoot: info.projectRoot,
98
+ baselineDir: info.baselineDir,
99
+ legacySnapshotFilename: legacyFilename,
100
+ shortenLongSnapshotPaths: visOptions.shortenLongSnapshotPaths === true,
101
+ })
102
+ const legacyAbs = absoluteSnapshotFilePath(info.projectRoot, info.baselineDir, filename)
103
+ return file.existFile(legacyAbs)
81
104
  },
82
105
  getSnapshotFilename(
83
106
  browserContext: ExtendedBrowserCommandContext,
84
- info: { taskId: string; task: { count: number } },
107
+ info: { taskId: string; task: { count: number }; suiteId: string; baselineDir: string; projectRoot: string },
85
108
  snapshotKey: string | undefined,
86
109
  ) {
87
- if (snapshotKey) return `${info.taskId}-${snapshotKey}.png`
88
110
  const visOptions = getVisOption(browserContext)
89
- if (typeof visOptions.snapshotKey === 'string') {
90
- return `${info.taskId}-${visOptions.snapshotKey}.png`
91
- }
92
- return `${info.taskId}-${info.task.count}.png`
111
+ const legacyFilename = getLegacySnapshotFilename({
112
+ taskId: info.taskId,
113
+ explicitSnapshotKey: snapshotKey,
114
+ defaultSnapshotKey: typeof visOptions.snapshotKey === 'string' ? visOptions.snapshotKey : undefined,
115
+ taskCount: info.task.count,
116
+ })
117
+ return resolveLegacySnapshotFileRelativePath({
118
+ projectRoot: info.projectRoot,
119
+ baselineDir: info.baselineDir,
120
+ legacySnapshotFilename: legacyFilename,
121
+ shortenLongSnapshotPaths: visOptions.shortenLongSnapshotPaths === true,
122
+ })
93
123
  },
94
124
  async getSuiteInfo(browserContext: ExtendedBrowserCommandContext, taskId: string) {
95
125
  const projectState = await getSuite(browserContext)
@@ -1,11 +1,7 @@
1
1
  import { Meta, Markdown ,Canvas} from "@storybook/addon-docs/blocks";
2
2
 
3
- <Meta title="setup/createVis" tags={['deprecated']} />
3
+ <Meta title="setup/createVis" />
4
4
 
5
5
  # `createVis`
6
6
 
7
- 🪦 Deprecated
8
-
9
- `createVis` in `vitest-plugin-vis/setup` is deprecated.
10
-
11
- It is moved to `vitest-plugin-vis/setup-api`.
7
+ Import `createVis` from `vitest-plugin-vis/setup-api` (not from `vitest-plugin-vis/setup`).
@@ -1,9 +1,8 @@
1
1
  import { afterEach, beforeAll, expect } from 'vitest'
2
2
  import { setAutoSnapshotOptions } from '../auto_snapshots/auto_snapshot_options.ts'
3
- import type { SnapshotMeta } from '../auto_snapshots/snapshot_meta.ts'
4
3
  import { autoSnapshotMatcher } from '../client/suite/auto_snapshot_matcher.ts'
5
4
  import type { SetupVisSuiteCommand } from '../shared/commands.types.ts'
6
- import type { ComparisonMethod, SetupVisOptions } from '../shared/types.ts'
5
+ import type { SetupVisOptions } from '../shared/types.ts'
7
6
 
8
7
  /**
9
8
  * Visual test configuration on the client side.
@@ -38,80 +37,6 @@ export type VisClientConfigurator<GM extends Record<string, any> | unknown = unk
38
37
  * ```
39
38
  */
40
39
  setup(options?: SetupVisOptions<GM>): void
41
- /**
42
- * @deprecated Use `vis.setup()` instead.
43
- */
44
- presets: {
45
- /**
46
- * @deprecated Use `vis.setup()` instead.
47
- *
48
- * Enable visual testing.
49
- *
50
- * auto snapshot is turned off by default.
51
- * You can specify the test to take a snapshot during `afterEach()` hook with `setAutoSnapshotOptions()`.
52
- */
53
- enable(): void
54
- /**
55
- * @deprecated Use `vis.setup()` instead.
56
- *
57
- * Enable visual testing.
58
- *
59
- * `setAutoSnapshotOptions` will have no effect in this preset.
60
- */
61
- manual(): void
62
- /**
63
- * @deprecated Use `vis.setup()` instead.
64
- *
65
- * Enable automatic visual testing.
66
- *
67
- * This will take a snapshot after each test.
68
- */
69
- auto(): void
70
- /**
71
- * @deprecated Use `vis.setup()` instead.
72
- *
73
- * Enable automatic visual testing with multiple themes.
74
- *
75
- * This will take a snapshot after each test for each theme.
76
- *
77
- * @param themes A record of theme names and their setup functions.
78
- *
79
- * @example
80
- * ```ts
81
- * vis().presets.theme({
82
- * light() { document.body.classList.add('light') },
83
- * dark() { document.body.classList.add('dark') },
84
- * })
85
- * ```
86
- */
87
- theme<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(
88
- themes: Record<
89
- string,
90
- boolean | ((options: SnapshotMeta<C> & M & GM) => Promise<boolean> | Promise<void> | boolean | void)
91
- >,
92
- ): void
93
- }
94
- beforeAll: {
95
- /**
96
- * @deprecated No known use case.
97
- */
98
- setup(): Promise<void>
99
- }
100
- afterEach: {
101
- /**
102
- * @deprecated No known use case.
103
- */
104
- matchImageSnapshot(): Promise<void>
105
- /**
106
- * @deprecated No known use case.
107
- */
108
- matchPerTheme<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(
109
- themes: Record<
110
- string,
111
- boolean | ((options: SnapshotMeta<C> & M & GM) => Promise<boolean> | Promise<void> | boolean | void)
112
- >,
113
- ): () => Promise<void>
114
- }
115
40
  }
116
41
 
117
42
  export function createVis<GM extends Record<string, any> | unknown = unknown>(commands: SetupVisSuiteCommand) {
@@ -119,59 +44,24 @@ export function createVis<GM extends Record<string, any> | unknown = unknown>(co
119
44
 
120
45
  const vis: VisClientConfigurator<GM> = {
121
46
  setup(options) {
122
- if (!options || options.auto === false) {
123
- beforeAll(matcher.setup)
124
- } else {
125
- beforeAll(async () => {
126
- await matcher.setup()
127
- setAutoSnapshotOptions(true)
128
- })
129
- }
47
+ const suiteDefaults =
48
+ options?.createMissingBaseline !== undefined
49
+ ? { createMissingBaseline: options.createMissingBaseline }
50
+ : undefined
51
+
52
+ beforeAll(async () => {
53
+ await matcher.setup()
54
+ setAutoSnapshotOptions(!!options?.auto)
55
+ })
130
56
 
131
57
  if (typeof options?.auto === 'function') {
132
- afterEach(matcher.createMatcher({ auto: options.auto }))
58
+ afterEach(matcher.createMatcher({ auto: options.auto }, suiteDefaults))
133
59
  } else if (typeof options?.auto === 'object') {
134
- afterEach(matcher.createMatcher(options.auto))
60
+ afterEach(matcher.createMatcher(options.auto, suiteDefaults))
135
61
  } else {
136
- afterEach(matcher.createMatcher({ async auto() {} }))
62
+ afterEach(matcher.createMatcher({ async auto() {} }, suiteDefaults))
137
63
  }
138
64
  },
139
- presets: {
140
- enable() {
141
- beforeAll(matcher.setup)
142
- afterEach(matcher.createMatcher({ async auto() {} }))
143
- },
144
- manual() {
145
- beforeAll(matcher.setup)
146
- },
147
- auto() {
148
- beforeAll(async () => {
149
- await matcher.setup()
150
- setAutoSnapshotOptions(true)
151
- })
152
- afterEach(matcher.createMatcher({ async auto() {} }))
153
- },
154
- theme(themes) {
155
- beforeAll(async () => {
156
- await matcher.setup()
157
- setAutoSnapshotOptions(true)
158
- })
159
- afterEach(matcher.createMatcher(themes))
160
- },
161
- },
162
- beforeAll: {
163
- async setup() {
164
- await matcher.setup()
165
- },
166
- },
167
- afterEach: {
168
- async matchImageSnapshot() {
169
- return matcher.createMatcher({ async auto() {} })()
170
- },
171
- matchPerTheme(themes) {
172
- return matcher.createMatcher(themes)
173
- },
174
- },
175
65
  }
176
66
  return vis
177
67
  }
@@ -94,6 +94,15 @@ export type ToMatchImageSnapshotOptions<M extends ComparisonMethod = 'pixel'> =
94
94
  * If it passes, it will throw an error with details.
95
95
  */
96
96
  expectToFail?: boolean | undefined
97
+ /**
98
+ * Create a baseline image when none exists on disk.
99
+ *
100
+ * If `true`, the captured image is written to the baseline path and the assertion succeeds,
101
+ * without Vitest `-u` / `updateSnapshot: 'all'`.
102
+ *
103
+ * @default false
104
+ */
105
+ createMissingBaseline?: boolean | undefined
97
106
  }
98
107
 
99
108
  export interface PageImageSnapshotOptions {
@@ -145,7 +154,12 @@ export type ImageSnapshotComparisonInfo = {
145
154
 
146
155
  export type MatchImageSnapshotOptions = ImageSnapshotTimeoutOptions &
147
156
  ImageSnapshotCompareOptions<any> &
148
- ImageSnapshotKeyOptions
157
+ ImageSnapshotKeyOptions & {
158
+ /**
159
+ * When `true`, write a missing baseline on first run (see {@link ToMatchImageSnapshotOptions} `createMissingBaseline`).
160
+ */
161
+ createMissingBaseline?: boolean | undefined
162
+ }
149
163
 
150
164
  export type SetupVisOptions<GM extends Record<string, any> | unknown = unknown> = {
151
165
  auto?:
@@ -160,4 +174,10 @@ export type SetupVisOptions<GM extends Record<string, any> | unknown = unknown>
160
174
  options: SnapshotMeta<C> & M & GM,
161
175
  ) => Promise<boolean> | Promise<void> | boolean | void)
162
176
  >
177
+ /**
178
+ * Default {@link ToMatchImageSnapshotOptions#createMissingBaseline} for auto snapshots.
179
+ *
180
+ * @default false
181
+ */
182
+ createMissingBaseline?: boolean | undefined
163
183
  }
@@ -1,11 +0,0 @@
1
- import { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR } from "./shared/constants.mjs";
2
- import { setAutoSnapshotOptions } from "./auto_snapshots/auto_snapshot_options.mjs";
3
- import { hasImageSnapshotAction } from "./client/actions/has_image_snapshot_action.mjs";
4
- import { loadImageSnapshotResultsAction } from "./client/actions/load_image_snapshot_results_action.mjs";
5
- import { matchImageSnapshotAction } from "./client/actions/match_image_snapshot_action.mjs";
6
- import { matchPageImageSnapshotAction } from "./client/actions/match_page_image_snapshot_action.mjs";
7
- import { success } from "./client/expect/expectation_result.mjs";
8
- import { toTaskId } from "./client/task/task_id.mjs";
9
- import { autoSnapshotMatcher } from "./client/suite/auto_snapshot_matcher.mjs";
10
-
11
- export { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR, autoSnapshotMatcher, hasImageSnapshotAction, loadImageSnapshotResultsAction, matchImageSnapshotAction, matchPageImageSnapshotAction, setAutoSnapshotOptions, success, toTaskId };
package/dist/config.d.mts DELETED
@@ -1,5 +0,0 @@
1
- import { ComparisonMethod, PixelComparisonOptions, PixelDiffOptions, SsimComparisonOptions, SsimDiffOptions } from "./shared/types.mjs";
2
- import { VisOptions } from "./config/types.mjs";
3
- import { vis } from "./config/vis.mjs";
4
- import { trimCommonFolder } from "./shared/trim_common_folder.mjs";
5
- export { ComparisonMethod, PixelComparisonOptions, PixelDiffOptions, SsimComparisonOptions, SsimDiffOptions, VisOptions, trimCommonFolder, vis };
package/dist/config.mjs DELETED
@@ -1,4 +0,0 @@
1
- import { trimCommonFolder } from "./shared/trim_common_folder.mjs";
2
- import { vis } from "./config/vis.mjs";
3
-
4
- export { trimCommonFolder, vis };
@@ -1,16 +0,0 @@
1
- import { setAutoSnapshotOptions } from "../auto_snapshots/auto_snapshot_options.mjs";
2
- import "../index.mjs";
3
- import { vis } from "../setup/vis.mjs";
4
- import "../setup.mjs";
5
- import { afterEach, beforeAll } from "vitest";
6
-
7
- //#region src/presets/auto.ts
8
- beforeAll(async () => {
9
- await vis.beforeAll.setup();
10
- setAutoSnapshotOptions(true);
11
- });
12
- afterEach(() => vis.afterEach.matchImageSnapshot());
13
-
14
- //#endregion
15
- export { };
16
- //# sourceMappingURL=auto.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"auto.mjs","names":[],"sources":["../../src/presets/auto.ts"],"sourcesContent":["import { afterEach, beforeAll } from 'vitest'\nimport { setAutoSnapshotOptions } from '../index.ts'\nimport { vis } from '../setup.ts'\n\nbeforeAll(async () => {\n\tawait vis.beforeAll.setup()\n\tsetAutoSnapshotOptions(true)\n})\nafterEach(() => vis.afterEach.matchImageSnapshot())\n"],"mappings":";;;;;;;AAIA,UAAU,YAAY;AACrB,OAAM,IAAI,UAAU,OAAO;AAC3B,wBAAuB,KAAK;EAC3B;AACF,gBAAgB,IAAI,UAAU,oBAAoB,CAAC"}
@@ -1 +0,0 @@
1
- export { };
@@ -1,11 +0,0 @@
1
- import { vis } from "../setup/vis.mjs";
2
- import "../setup.mjs";
3
- import { afterEach, beforeAll } from "vitest";
4
-
5
- //#region src/presets/enable.ts
6
- beforeAll(() => vis.beforeAll.setup());
7
- afterEach(() => vis.afterEach.matchImageSnapshot());
8
-
9
- //#endregion
10
- export { };
11
- //# sourceMappingURL=enable.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"enable.mjs","names":[],"sources":["../../src/presets/enable.ts"],"sourcesContent":["import { afterEach, beforeAll } from 'vitest'\nimport { vis } from '../setup.ts'\n\nbeforeAll(() => vis.beforeAll.setup())\nafterEach(() => vis.afterEach.matchImageSnapshot())\n"],"mappings":";;;;;AAGA,gBAAgB,IAAI,UAAU,OAAO,CAAC;AACtC,gBAAgB,IAAI,UAAU,oBAAoB,CAAC"}
@@ -1,11 +0,0 @@
1
- import { vis } from "../setup/vis.mjs";
2
- import "../setup.mjs";
3
- import { afterEach, beforeAll } from "vitest";
4
-
5
- //#region src/presets/manual.ts
6
- beforeAll(() => vis.beforeAll.setup());
7
- afterEach(() => vis.afterEach.matchImageSnapshot());
8
-
9
- //#endregion
10
- export { };
11
- //# sourceMappingURL=manual.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"manual.mjs","names":[],"sources":["../../src/presets/manual.ts"],"sourcesContent":["import { afterEach, beforeAll } from 'vitest'\nimport { vis } from '../setup.ts'\n\nbeforeAll(() => vis.beforeAll.setup())\nafterEach(() => vis.afterEach.matchImageSnapshot())\n"],"mappings":";;;;;AAGA,gBAAgB,IAAI,UAAU,OAAO,CAAC;AACtC,gBAAgB,IAAI,UAAU,oBAAoB,CAAC"}
@@ -1,4 +0,0 @@
1
- import { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR } from "./shared/constants.mjs";
2
- import { trimCommonFolder } from "./shared/trim_common_folder.mjs";
3
- import { getSnapshotRootDir } from "./server/snapshot_path.mjs";
4
- export { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR, getSnapshotRootDir, trimCommonFolder };
@@ -1,5 +0,0 @@
1
- import { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR } from "./shared/constants.mjs";
2
- import { trimCommonFolder } from "./shared/trim_common_folder.mjs";
3
- import { getSnapshotRootDir } from "./server/snapshot_path.mjs";
4
-
5
- export { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR, getSnapshotRootDir, trimCommonFolder };
@@ -1,4 +0,0 @@
1
- import "./client/expect/extend.mjs";
2
- import "./client/page/extend.mjs";
3
- import { VisClientConfigurator, createVis } from "./setup/create_vis.mjs";
4
- export { VisClientConfigurator, createVis };
@@ -1,5 +0,0 @@
1
- import "./client/expect/extend.mjs";
2
- import "./client/page/extend.mjs";
3
- import { createVis } from "./setup/create_vis.mjs";
4
-
5
- export { createVis };
package/dist/setup.d.mts DELETED
@@ -1,13 +0,0 @@
1
- import { createVis as createVis$1 } from "./setup/create_vis.mjs";
2
- import "./setup-api.mjs";
3
- import { vis } from "./setup/vis.mjs";
4
-
5
- //#region src/setup.d.ts
6
-
7
- /**
8
- * @deprecated Use `createVis` from `vitest-plugin-vis/setup-api` instead.
9
- */
10
- declare const createVis: typeof createVis$1;
11
- //#endregion
12
- export { createVis, vis };
13
- //# sourceMappingURL=setup.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"setup.d.mts","names":[],"sources":["../src/setup.ts"],"sourcesContent":[],"mappings":";;;;;;;;AASA;cAAa,kBAAS"}