vitest-plugin-vis 2.1.1 → 2.3.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 (67) hide show
  1. package/esm/client/ctx.d.ts +2 -1
  2. package/esm/client/ctx.d.ts.map +1 -1
  3. package/esm/client/ctx.js +2 -0
  4. package/esm/client/expect/to_match_image_snapshot.d.ts +1 -1
  5. package/esm/client/expect/to_match_image_snapshot.d.ts.map +1 -1
  6. package/esm/client/expect/to_match_image_snapshot.types.d.ts +1 -8
  7. package/esm/client/expect/to_match_image_snapshot.types.d.ts.map +1 -1
  8. package/esm/client/image_snapshot_matcher.d.ts +2 -3
  9. package/esm/client/image_snapshot_matcher.d.ts.map +1 -1
  10. package/esm/client/image_snapshot_matcher.js +6 -3
  11. package/esm/client/image_snapshot_matcher.logic.d.ts +1 -1
  12. package/esm/client/image_snapshot_matcher.logic.d.ts.map +1 -1
  13. package/esm/client/page/has_image_snapshot.d.ts.map +1 -1
  14. package/esm/client/page/has_image_snapshot.js +1 -1
  15. package/esm/client/should_take_snapshot.d.ts +2 -2
  16. package/esm/client/should_take_snapshot.d.ts.map +1 -1
  17. package/esm/client/should_take_snapshot.js +3 -3
  18. package/esm/client/snapshot_options.d.ts +3 -6
  19. package/esm/client/snapshot_options.d.ts.map +1 -1
  20. package/esm/client/snapshot_options.js +19 -1
  21. package/esm/client/vitest_suite_proxy.d.ts +2 -1
  22. package/esm/client/vitest_suite_proxy.d.ts.map +1 -1
  23. package/esm/client.d.ts +1 -1
  24. package/esm/client.d.ts.map +1 -1
  25. package/esm/client.js +1 -1
  26. package/esm/config/types.d.ts +1 -1
  27. package/esm/config/types.d.ts.map +1 -1
  28. package/esm/index.d.ts +2 -1
  29. package/esm/index.d.ts.map +1 -1
  30. package/esm/index.js +1 -1
  31. package/esm/presets/enable.d.ts +3 -0
  32. package/esm/presets/enable.d.ts.map +1 -0
  33. package/esm/presets/enable.js +4 -0
  34. package/esm/server/vis_context.d.ts +4 -4
  35. package/esm/server/vis_context.d.ts.map +1 -1
  36. package/esm/server/vis_context.logic.d.ts +4 -4
  37. package/esm/server/vis_context.logic.d.ts.map +1 -1
  38. package/esm/server/vis_context.logic.js +4 -0
  39. package/esm/setup/create_vis.d.ts +18 -5
  40. package/esm/setup/create_vis.d.ts.map +1 -1
  41. package/esm/setup/create_vis.js +10 -30
  42. package/esm/setup/vis.d.ts +1 -14
  43. package/esm/setup/vis.d.ts.map +1 -1
  44. package/esm/shared/types.d.ts +21 -0
  45. package/esm/shared/types.d.ts.map +1 -1
  46. package/package.json +5 -1
  47. package/readme.md +37 -15
  48. package/src/client/ctx.ts +2 -0
  49. package/src/client/expect/to_match_image_snapshot.ts +1 -1
  50. package/src/client/expect/to_match_image_snapshot.types.ts +1 -16
  51. package/src/client/image_snapshot_matcher.logic.ts +1 -1
  52. package/src/client/image_snapshot_matcher.ts +12 -5
  53. package/src/client/page/has_image_snapshot.ts +6 -1
  54. package/src/client/should_take_snapshot.ts +4 -4
  55. package/src/client/snapshot_options.ts +27 -9
  56. package/src/client/vitest_suite_proxy.ts +3 -1
  57. package/src/client.ts +1 -1
  58. package/src/config/types.ts +1 -1
  59. package/src/index.ts +2 -1
  60. package/src/presets/enable.ts +5 -0
  61. package/src/server/vis_context.logic.ts +5 -1
  62. package/src/setup/create_vis.ts +73 -39
  63. package/src/shared/types.ts +22 -2
  64. package/esm/client/snapshot_options.internal.d.ts +0 -3
  65. package/esm/client/snapshot_options.internal.d.ts.map +0 -1
  66. package/esm/client/snapshot_options.internal.js +0 -20
  67. package/src/client/snapshot_options.internal.ts +0 -22
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,YAAY,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,SAAS,CAAA;AAErD,MAAM,WAAW,2BAA2B;IAC3C;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACtC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC/B,EAAE,EAAE,MAAM,CAAA;QAGV,KAAK,EAAE,MAAM,CAAA;KACb,KAAK,MAAM,CAAA;CACZ;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAA;AAE/C,MAAM,MAAM,uBAAuB,GAAG;IACrC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;IACtD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,qBAAqB,CAAC,CAAC,GAAG,MAAM,IAAI;IAC/C,gBAAgB,EAAE,CAAC,CAAA;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,SAAS,CAAA;CAC9C,CAAA;AACD,MAAM,MAAM,sBAAsB,CAAC,CAAC,GAAG,OAAO,IAAI;IACjD,gBAAgB,CAAC,EAAE,CAAC,GAAG,SAAS,CAAA;IAChC;;OAEG;IACH,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;CAC1D,CAAA;AAED,MAAM,MAAM,2BAA2B,CAAC,CAAC,SAAS,gBAAgB,GAAG,OAAO,IAAI,CAAC,CAAC,SAAS,MAAM,GAC9F,qBAAqB,CAAC,CAAC,CAAC,GACxB,sBAAsB,CAAC,CAAC,CAAC,CAAC,GAC5B,uBAAuB,CAAA;AAExB,MAAM,MAAM,mBAAmB,GAAG;IACjC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,UAAU,MAAM,YAAY,CAAA;AACxC,OAAO,KAAK,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,SAAS,CAAA;AACrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAClD,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAA;AAE1C,MAAM,WAAW,2BAA2B;IAC3C;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACtC;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE;QAC/B,EAAE,EAAE,MAAM,CAAA;QACV,KAAK,EAAE,MAAM,CAAA;QACb,cAAc,EAAE,OAAO,CAAA;KACvB,KAAK,MAAM,CAAA;CACZ;AAED,MAAM,MAAM,gBAAgB,GAAG,OAAO,GAAG,MAAM,CAAA;AAE/C,MAAM,MAAM,uBAAuB,GAAG;IACrC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;IACtD;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,qBAAqB,CAAC,CAAC,GAAG,MAAM,IAAI;IAC/C,gBAAgB,EAAE,CAAC,CAAA;IACnB;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,SAAS,CAAA;CAC9C,CAAA;AACD,MAAM,MAAM,sBAAsB,CAAC,CAAC,GAAG,OAAO,IAAI;IACjD,gBAAgB,CAAC,EAAE,CAAC,GAAG,SAAS,CAAA;IAChC;;OAEG;IACH,WAAW,CAAC,EAAE,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,GAAG,SAAS,CAAA;CAC1D,CAAA;AAED,MAAM,MAAM,2BAA2B,CAAC,CAAC,SAAS,gBAAgB,GAAG,OAAO,IAAI,CAAC,CAAC,SAAS,MAAM,GAC9F,qBAAqB,CAAC,CAAC,CAAC,GACxB,sBAAsB,CAAC,CAAC,CAAC,CAAC,GAC5B,uBAAuB,CAAA;AAExB,MAAM,MAAM,mBAAmB,GAAG;IACjC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACtC,CAAA;AACD,MAAM,MAAM,2BAA2B,CAAC,CAAC,SAAS,gBAAgB,GAAG,OAAO,IAAI,2BAA2B,GAC1G,sBAAsB,GACtB,2BAA2B,CAAC,CAAC,CAAC,GAAG;IAChC;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAClC,CAAA;AAEF,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,gBAAgB,IAAI,2BAA2B,CAAC,CAAC,CAAC,GACpF,mBAAmB,GAAG;IACrB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CACtB,CAAA;AAEF,MAAM,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,GAAG;IAC7D,IAAI,EAAE;QAAE,CAAC,IAAI,CAAC,CAAC,EAAE,YAAY,CAAC,OAAO,GAAG,MAAM,CAAC,GAAG;YAAE,cAAc,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;SAAE,CAAA;KAAE,CAAA;CAC5F,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest-plugin-vis",
3
- "version": "2.1.1",
3
+ "version": "2.3.0",
4
4
  "description": "Vitest visual testing plugin",
5
5
  "keywords": [
6
6
  "image-snapshot",
@@ -40,6 +40,10 @@
40
40
  "types": "./esm/presets/auto.d.ts",
41
41
  "default": "./esm/presets/auto.js"
42
42
  },
43
+ "./presets/enable": {
44
+ "types": "./esm/presets/enable.d.ts",
45
+ "default": "./esm/presets/enable.js"
46
+ },
43
47
  "./presets/manual": {
44
48
  "types": "./esm/presets/manual.d.ts",
45
49
  "default": "./esm/presets/manual.js"
package/readme.md CHANGED
@@ -83,7 +83,8 @@ export default defineConfig({
83
83
  }) => `__vis__/${ci ? platform : 'local'}`,
84
84
  platform: '...', // {process.platform} or `local` (deprecated use `snapshotRootDir` instead)
85
85
  customizeSnapshotSubpath: (subpath) => trimCommonFolder(subpath),
86
- customizeSnapshotId: (id, index) => `${id}-${index}`,
86
+ // will change to "isAutoSnapshot ? `${id}-auto` : `${id}-${index}`" in the next major release.
87
+ customizeSnapshotId: ({ id, index, isAutoSnapshot }) => `${id}-${index}`,
87
88
  // set a default subject (e.g. 'subject') to capture image snapshot
88
89
  subjectDataTestId: undefined,
89
90
  comparisonMethod: 'pixel',
@@ -94,7 +95,7 @@ export default defineConfig({
94
95
  failureThreshold: 0,
95
96
  })
96
97
  ],
97
- test:{
98
+ test: {
98
99
  browser: {/* ... */}
99
100
  }
100
101
  })
@@ -113,8 +114,8 @@ export default defineConfig({
113
114
  plugins: [
114
115
  vis({ preset: 'none' })
115
116
  ],
116
- test:{
117
- browser:{/* ... */},
117
+ test: {
118
+ browser: {/* ... */},
118
119
  setupFiles: ['vitest.setup.ts']
119
120
  }
120
121
  })
@@ -123,13 +124,17 @@ export default defineConfig({
123
124
  import { vis } from 'vitest-plugin-vis/setup'
124
125
 
125
126
  // if you set `preset: none` in `vitest.config.ts`,
126
- // you can enable `auto` or `manual` setup here
127
+ // you can use a preset manually here.
128
+
129
+ // Take snapshot at the end of each rendering test.
127
130
  vis.presets.auto()
131
+ // Enable snapshot testing for manual snapshot only.
128
132
  vis.presets.manual()
133
+ // Enable snapshot testing, allow auto snapshot with `setAutoSnapshotOptions()`
134
+ vis.presets.enable()
129
135
 
130
-
131
- // or capture image snapshot for all rendering tests
132
- // for multiple themes (light and dark in this example)
136
+ // Take image snapshot for each rendering test,
137
+ // one snapshot per theme (light and dark in this example).
133
138
  //
134
139
  // Note that this changes the theme in the `afterEach` hook.
135
140
  // If you want to capture manual snapshots in different themes,
@@ -160,10 +165,10 @@ To address this, you can add the following to your `tsconfig.json`:
160
165
 
161
166
  ## Usage
162
167
 
163
- ### Auto Snapshot
168
+ ### Auto Preset
164
169
 
165
170
  By default, the plugin will use the `auto` preset,
166
- which will take a snapshot at the end of each test.
171
+ which will take a snapshot at the end of each rendering test.
167
172
 
168
173
  You can control how the auto snapshot is taken using the `setAutoSnapshotOptions` function:
169
174
 
@@ -213,7 +218,7 @@ setAutoSnapshotOptions(true /* or false */)
213
218
  setAutoSnapshotOptions({
214
219
  enable: true,
215
220
  comparisonMethod: 'pixel',
216
- customizeSnapshotId: (id, index) => `${id}-custom-${index}`,
221
+ customizeSnapshotId: ({ id, index, isAutoSnapshot }) => `${id}-custom-${index}`,
217
222
  // pixelmatch or ssim.js options, depending on `comparisonMethod`.
218
223
  diffOptions: { threshold: 0.01 },
219
224
  failureThreshold: 0.01,
@@ -238,7 +243,7 @@ vis.presets.theme({
238
243
  })
239
244
  ```
240
245
 
241
- ### Manual Snapshot
246
+ ### Manual Preset
242
247
 
243
248
  You can also set the preset to `manual` and compare snapshots manually:
244
249
 
@@ -251,7 +256,7 @@ export default defineConfig({
251
256
  vis({ preset: 'manual' })
252
257
  ],
253
258
  test:{
254
- browser:{/* ... */}
259
+ browser: {/* ... */}
255
260
  }
256
261
  })
257
262
 
@@ -281,7 +286,7 @@ it('manual snapshot with options', async ({ expect }) => {
281
286
  page.render(<div data-testid="subject">hello world</div>)
282
287
  const subject = page.getByTestId('subject')
283
288
  await expect(subject).toMatchImageSnapshot({
284
- customizeSnapshotId: (id, index) => `${id}-custom-${index}`,
289
+ customizeSnapshotId: ({ id, index, isAutoSnapshot }) => `${id}-custom-${index}`,
285
290
  failureThreshold: 0.01,
286
291
  failureThresholdType: 'percent',
287
292
  diffOptions: {
@@ -292,6 +297,23 @@ it('manual snapshot with options', async ({ expect }) => {
292
297
  })
293
298
  ```
294
299
 
300
+ ### Enable Preset
301
+
302
+ The `enable` preset allows you to take manual snapshot,
303
+ as well as auto snapshot at the end on a rendering test using the `setAutoSnapshotOptions` function:
304
+
305
+ ```ts
306
+ import { setAutoSnapshotOptions } from 'vitest-plugin-vis'
307
+ import { beforeEach, it } from 'vitest'
308
+
309
+
310
+ it('take snapshot', async () => {
311
+ setAutoSnapshotOptions(/* options */) // e.g. `true`
312
+
313
+ // ...your test...
314
+ })
315
+ ```
316
+
295
317
  ### Has Snapshot
296
318
 
297
319
  While less common, you can also check if a snapshot exists:
@@ -311,7 +333,7 @@ it('Has Snapshot', async ({ expect }) => {
311
333
  })
312
334
  ```
313
335
 
314
- This is useful when you are performing some negative test.
336
+ This is useful when you are performing negative test.
315
337
 
316
338
  ## Git Ignore
317
339
 
package/src/client/ctx.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  import { getCurrentSuite, getCurrentTest } from './vitest_suite_proxy.ts'
2
2
 
3
3
  export const ctx = {
4
+ autoEnabled: undefined as boolean | undefined,
4
5
  getCurrentTest,
5
6
  getCurrentSuite,
6
7
  __test__reset() {
7
8
  ctx.getCurrentTest = getCurrentTest
8
9
  ctx.getCurrentSuite = getCurrentSuite
10
+ ctx.autoEnabled = undefined
9
11
  },
10
12
  }
@@ -1,9 +1,9 @@
1
1
  import { commands } from '@vitest/browser/context'
2
2
  import type { AsyncExpectationResult } from '@vitest/expect'
3
+ import type { ToMatchImageSnapshotOptions } from '../../shared/types.ts'
3
4
  import { ctx } from '../ctx.ts'
4
5
  import { imageSnapshotMatcher } from '../image_snapshot_matcher.ts'
5
6
  import { success } from './expectation_result.ts'
6
- import type { ToMatchImageSnapshotOptions } from './to_match_image_snapshot.types.ts'
7
7
 
8
8
  export function toMatchImageSnapshot(
9
9
  /**
@@ -1,20 +1,5 @@
1
- import type {
2
- ComparisonMethod,
3
- ImageSnapshotCompareOptions,
4
- ImageSnapshotIdOptions,
5
- ImageSnapshotTimeoutOptions,
6
- } from '../../shared/types.ts'
1
+ import type { ComparisonMethod, ToMatchImageSnapshotOptions } from '../../shared/types.ts'
7
2
 
8
3
  export interface ImageSnapshotMatcher {
9
4
  toMatchImageSnapshot<M extends ComparisonMethod>(options?: ToMatchImageSnapshotOptions<M> | undefined): Promise<void>
10
5
  }
11
-
12
- export type ToMatchImageSnapshotOptions<M extends ComparisonMethod = 'pixel'> = ImageSnapshotTimeoutOptions &
13
- ImageSnapshotIdOptions &
14
- ImageSnapshotCompareOptions<M> & {
15
- /**
16
- * Expect the matcher to fail.
17
- * If it passes, it will throw an error with details.
18
- */
19
- expectToFail?: boolean | undefined
20
- }
@@ -1,4 +1,4 @@
1
- import type { ToMatchImageSnapshotOptions } from './expect/to_match_image_snapshot.types.ts'
1
+ import type { ToMatchImageSnapshotOptions } from '../shared/types.ts'
2
2
 
3
3
  export function prettifyOptions(options: ToMatchImageSnapshotOptions<any> | undefined) {
4
4
  if (!options) return 'none'
@@ -1,13 +1,12 @@
1
1
  import dedent from 'dedent'
2
2
  import { resolve } from 'pathe'
3
- import type { Task } from 'vitest'
4
3
  import type { ImageSnapshotNextIndexCommand } from '../commands.ts'
5
4
  import type { PrepareImageSnapshotComparisonCommand } from '../server/commands/prepare_image_snapshot_comparison.ts'
6
5
  import type { WriteImageSnapshotCommand } from '../server/commands/write_image_snapshot.ts'
7
6
  import { isBase64String } from '../shared/base64.ts'
8
7
  import { compareImage } from '../shared/compare_image.ts'
8
+ import type { CurrentTest, ToMatchImageSnapshotOptions } from '../shared/types.ts'
9
9
  import { alignImagesToSameSize } from './align_images.ts'
10
- import type { ToMatchImageSnapshotOptions } from './expect/to_match_image_snapshot.types.ts'
11
10
  import { toDataURL, toImageData } from './image_data.ts'
12
11
  import { prettifyOptions } from './image_snapshot_matcher.logic.ts'
13
12
  import { convertElementToCssSelector } from './selector.ts'
@@ -17,12 +16,19 @@ import { server } from './vitest_browser_context_proxy.ts'
17
16
  export function imageSnapshotMatcher(
18
17
  commands: PrepareImageSnapshotComparisonCommand & WriteImageSnapshotCommand & ImageSnapshotNextIndexCommand,
19
18
  ) {
20
- return async function matchImageSnapshot(test: Task, subject: any, options?: ToMatchImageSnapshotOptions<any>) {
19
+ return async function matchImageSnapshot(
20
+ test: CurrentTest & {},
21
+ subject: any,
22
+ options?: ToMatchImageSnapshotOptions<any>,
23
+ ) {
24
+ const isAutoSnapshot = !!test.meta.vis?.isAutoSnapshot
21
25
  const taskId = toTaskId(test)
22
26
  const info = await commands.prepareImageSnapshotComparison(
23
27
  taskId,
24
28
  parseImageSnapshotSubject(subject),
25
- options?.customizeSnapshotId ? await parseImageSnapshotOptions(commands, taskId, options) : options,
29
+ options?.customizeSnapshotId
30
+ ? await parseImageSnapshotOptions(commands, taskId, isAutoSnapshot, options)
31
+ : options,
26
32
  )
27
33
 
28
34
  if (!info) return
@@ -103,10 +109,11 @@ async function writeSnapshot(commands: WriteImageSnapshotCommand, path: string,
103
109
  async function parseImageSnapshotOptions(
104
110
  commands: ImageSnapshotNextIndexCommand,
105
111
  taskId: string,
112
+ isAutoSnapshot: boolean,
106
113
  options: ToMatchImageSnapshotOptions<any>,
107
114
  ) {
108
115
  const index = await commands.imageSnapshotNextIndex(taskId)
109
116
  const { customizeSnapshotId, ...rest } = options
110
- const snapshotFileId = customizeSnapshotId!({ id: taskId, index })
117
+ const snapshotFileId = customizeSnapshotId!({ id: taskId, index, isAutoSnapshot })
111
118
  return { ...rest, snapshotFileId }
112
119
  }
@@ -26,7 +26,12 @@ export function hasImageSnapshot(this: BrowserPage, options?: ImageSnapshotIdOpt
26
26
  if (options?.customizeSnapshotId) {
27
27
  return commands
28
28
  .imageSnapshotNextIndex(taskId)
29
- .then((index) => commands.hasImageSnapshot(taskId, options.customizeSnapshotId!({ id: taskId, index })))
29
+ .then((index) =>
30
+ commands.hasImageSnapshot(
31
+ taskId,
32
+ options.customizeSnapshotId!({ id: taskId, index, isAutoSnapshot: !!test.meta.vis?.isAutoSnapshot }),
33
+ ),
34
+ )
30
35
  }
31
36
 
32
37
  return commands.hasImageSnapshot(taskId)
@@ -1,6 +1,6 @@
1
+ import type { SnapshotMeta } from '../shared/types.ts'
1
2
  import { ctx } from './ctx.ts'
2
- import { getAutoSnapshotOptions } from './snapshot_options.internal.ts'
3
- import type { SnapshotMeta } from './snapshot_options.ts'
3
+ import { extractAutoSnapshotOptions } from './snapshot_options.ts'
4
4
 
5
5
  /**
6
6
  * Determine should snapshot be taken.
@@ -10,6 +10,6 @@ import type { SnapshotMeta } from './snapshot_options.ts'
10
10
  * disabled by `!snapshot` tag: false
11
11
  */
12
12
  export function shouldTakeSnapshot(meta?: SnapshotMeta<any> | undefined) {
13
- const m = meta ?? getAutoSnapshotOptions(ctx.getCurrentTest())
14
- return document.body.childElementCount > 0 && m?.enable !== false
13
+ const m = meta ?? extractAutoSnapshotOptions(ctx.getCurrentTest())
14
+ return document.body.childElementCount > 0 && m?.enable
15
15
  }
@@ -1,14 +1,6 @@
1
- import { getCurrentTest } from 'vitest/suite'
2
1
  import { NAME } from '../shared/constants.ts'
3
- import type { AutoSnapshotOptions, ComparisonMethod } from '../shared/types.ts'
2
+ import type { ComparisonMethod, SnapshotMeta } from '../shared/types.ts'
4
3
  import { ctx } from './ctx.ts'
5
- import type { ToMatchImageSnapshotOptions } from './expect/to_match_image_snapshot.types.ts'
6
-
7
- export type SnapshotMeta<M extends ComparisonMethod> = ToMatchImageSnapshotOptions<M> &
8
- AutoSnapshotOptions & {
9
- enable?: boolean | undefined
10
- [key: string]: unknown
11
- }
12
4
 
13
5
  type Suite = { meta: Record<string, any>; suite?: Suite | undefined }
14
6
 
@@ -20,6 +12,10 @@ export type MetaTask =
20
12
  }
21
13
  | undefined
22
14
 
15
+ export function enableAuto() {
16
+ ctx.autoEnabled = true
17
+ }
18
+
23
19
  /**
24
20
  * Set the snapshot options for auto snapshot.
25
21
  *
@@ -62,3 +58,25 @@ function parseArgs<M extends ComparisonMethod>(
62
58
  function parseMeta<M extends ComparisonMethod>(meta: boolean | SnapshotMeta<M>): SnapshotMeta<M> {
63
59
  return typeof meta === 'boolean' ? ({ enable: meta } as any) : { enable: true, ...meta }
64
60
  }
61
+
62
+ export function extractAutoSnapshotOptions<M extends SnapshotMeta<any> = SnapshotMeta<any>>(
63
+ task: MetaTask,
64
+ ): M | undefined {
65
+ if (!task) return
66
+
67
+ const list: any[] = []
68
+ let current = task
69
+ while (current?.suite) {
70
+ list.unshift(current.suite.meta)
71
+ current = current.suite
72
+ }
73
+ list.unshift(task.file?.meta)
74
+ list.push(task.meta)
75
+ return list.reduce(
76
+ (acc, cur) => {
77
+ const meta = cur?.[NAME]
78
+ return meta ? Object.assign({}, acc, meta) : acc
79
+ },
80
+ { enable: ctx.autoEnabled },
81
+ )
82
+ }
@@ -1,3 +1,5 @@
1
+ import type { CurrentTest } from '../shared/types.ts'
2
+
1
3
  let vitestSuite: Awaited<typeof import('vitest/suite')>
2
4
 
3
5
  if ((globalThis as any).__vitest_browser__) {
@@ -6,5 +8,5 @@ if ((globalThis as any).__vitest_browser__) {
6
8
  })
7
9
  }
8
10
 
9
- export const getCurrentTest = () => vitestSuite?.getCurrentTest()
11
+ export const getCurrentTest = () => vitestSuite?.getCurrentTest() as CurrentTest
10
12
  export const getCurrentSuite = () => vitestSuite?.getCurrentSuite()
package/src/client.ts CHANGED
@@ -2,6 +2,6 @@ export * from './client/expect/expectation_result.ts'
2
2
  export type * from './client/expect/to_match_image_snapshot.types.ts'
3
3
  export * from './client/image_snapshot_matcher.ts'
4
4
  export * from './client/should_take_snapshot.ts'
5
- export * from './client/snapshot_options.ts'
5
+ export { setAutoSnapshotOptions, type MetaTask } from './client/snapshot_options.ts'
6
6
  export * from './client/task_id.ts'
7
7
  export type * from './shared/types.ts'
@@ -15,7 +15,7 @@ export type VisOptions<M extends ComparisonMethod = 'pixel'> = ImageSnapshotTime
15
15
  /**
16
16
  * Loads the `test.setupFiles` of the specified preset.
17
17
  */
18
- preset?: 'manual' | 'auto' | 'none' | undefined
18
+ preset?: 'enable' | 'manual' | 'auto' | 'none' | undefined
19
19
  /**
20
20
  * The name of the subdirectory that the baseline snapshots get saved in.
21
21
  *
package/src/index.ts CHANGED
@@ -2,4 +2,5 @@
2
2
  import './client/expect/augment.ts'
3
3
  import './client/page/augment.ts'
4
4
 
5
- export * from './client/snapshot_options.ts'
5
+ export { setAutoSnapshotOptions, type MetaTask } from './client/snapshot_options.ts'
6
+ export type * from './shared/types.ts'
@@ -0,0 +1,5 @@
1
+ import { vis } from '../setup/vis.ts'
2
+ import '../client/expect/extend.ts'
3
+ import '../client/page/extend.ts'
4
+
5
+ vis.presets.enable()
@@ -1,7 +1,9 @@
1
1
  import { join, relative } from 'pathe'
2
2
  import { pick } from 'type-plus'
3
+ import { getCurrentTest } from 'vitest/suite'
3
4
  import type { VisOptions } from '../config/types.ts'
4
5
  import { BASELINE_DIR, DIFF_DIR, RESULT_DIR } from '../shared/constants.ts'
6
+ import type { CurrentTest } from '../shared/types.ts'
5
7
  import { file } from './file.ts'
6
8
  import { getSnapshotSubpath, resolveSnapshotRootDir } from './snapshot_path.ts'
7
9
  import { ctx } from './vis_context.ctx.ts'
@@ -70,11 +72,13 @@ export function createVisContext() {
70
72
  },
71
73
  getSnapshotFilename(info: { taskId: string; task: { count: number } }, snapshotFileId: string | undefined) {
72
74
  if (snapshotFileId) return `${snapshotFileId}.png`
73
-
75
+ const test = getCurrentTest() as CurrentTest
76
+ const isAutoSnapshot = !!test?.meta.vis?.isAutoSnapshot
74
77
  const customizeSnapshotId = visOptions.customizeSnapshotId ?? (({ id, index }) => `${id}-${index}`)
75
78
  return `${customizeSnapshotId({
76
79
  id: info.taskId,
77
80
  index: info.task.count,
81
+ isAutoSnapshot,
78
82
  })}.png`
79
83
  },
80
84
  getSuiteInfo(testPath: string, taskId: string) {
@@ -1,49 +1,81 @@
1
1
  import dedent from 'dedent'
2
2
  import { afterEach, beforeAll } from 'vitest'
3
- import { type SnapshotMeta, toTaskId } from '../client.ts'
3
+ import { type ComparisonMethod, type SnapshotMeta, toTaskId } from '../client.ts'
4
4
  import { ctx } from '../client/ctx.ts'
5
5
  import { shouldTakeSnapshot } from '../client/should_take_snapshot.ts'
6
- import { getAutoSnapshotOptions } from '../client/snapshot_options.internal.js'
6
+ import { enableAuto, extractAutoSnapshotOptions } from '../client/snapshot_options.ts'
7
7
  import type { SetupVisSuiteCommand } from '../server/commands/setup_vis_suite.ts'
8
8
 
9
- export function createVis<M extends SnapshotMeta<any>>(commands: SetupVisSuiteCommand) {
9
+ /**
10
+ * Visual test configuration on the client side.
11
+ */
12
+ export type VisClientConfigurator<SM extends SnapshotMeta<ComparisonMethod>> = {
13
+ presets: {
14
+ /**
15
+ * Enable visual testing.
16
+ *
17
+ * auto snapshot is turned off by default.
18
+ * You can specify the test to take a snapshot during `afterEach()` hook with `setAutoSnapshotOptions()`.
19
+ */
20
+ enable(): void
21
+ /**
22
+ * Enable visual testing.
23
+ *
24
+ * `setAutoSnapshotOptions` will have no effect in this preset.
25
+ */
26
+ manual(): void
27
+ /**
28
+ * Enable automatic visual testing.
29
+ *
30
+ * This will take a snapshot after each test.
31
+ */
32
+ auto(): void
33
+ /**
34
+ * Enable automatic visual testing with multiple themes.
35
+ *
36
+ * This will take a snapshot after each test for each theme.
37
+ *
38
+ * @param themes A record of theme names and their setup functions.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * vis().presets.theme({
43
+ * light() { document.body.classList.add('light') },
44
+ * dark() { document.body.classList.add('dark') },
45
+ * })
46
+ * ```
47
+ */
48
+ theme<M>(themes: Record<string, (options: M & SM) => Promise<boolean> | Promise<void> | boolean | void>): void
49
+ }
50
+ beforeAll: {
51
+ setup(): Promise<void>
52
+ }
53
+ afterEach: {
54
+ matchImageSnapshot(): Promise<void>
55
+ matchPerTheme<M>(
56
+ themes: Record<string, (options: M & SM) => Promise<boolean> | Promise<void> | boolean | void>,
57
+ ): () => Promise<void>
58
+ }
59
+ }
60
+
61
+ export function createVis<SM extends SnapshotMeta<ComparisonMethod>>(commands: SetupVisSuiteCommand) {
10
62
  let subjectDataTestId: string | undefined
11
- /**
12
- * Visual test configuration on the client side.
13
- */
14
- const vis = {
63
+
64
+ const vis: VisClientConfigurator<SM> = {
15
65
  presets: {
16
- /**
17
- * Enable manual visual testing.
18
- */
66
+ enable() {
67
+ beforeAll(vis.beforeAll.setup)
68
+ afterEach(vis.afterEach.matchImageSnapshot)
69
+ },
19
70
  manual() {
20
71
  beforeAll(vis.beforeAll.setup)
21
72
  },
22
- /**
23
- * Enable automatic visual testing.
24
- *
25
- * This will take a snapshot after each test.
26
- */
27
73
  auto() {
28
74
  beforeAll(vis.beforeAll.setup)
29
75
  afterEach(vis.afterEach.matchImageSnapshot)
76
+ enableAuto()
30
77
  },
31
- /**
32
- * Enable automatic visual testing with multiple themes.
33
- *
34
- * This will take a snapshot after each test for each theme.
35
- *
36
- * @param themes A record of theme names and their setup functions.
37
- *
38
- * @example
39
- * ```ts
40
- * vis().presets.theme({
41
- * light() { document.body.classList.add('light') },
42
- * dark() { document.body.classList.add('dark') },
43
- * })
44
- * ```
45
- */
46
- theme(themes: Record<string, (options: M) => Promise<boolean> | Promise<void> | boolean | void>) {
78
+ theme(themes) {
47
79
  beforeAll(vis.beforeAll.setup)
48
80
  afterEach(vis.afterEach.matchPerTheme(themes))
49
81
  },
@@ -59,38 +91,40 @@ export function createVis<M extends SnapshotMeta<any>>(commands: SetupVisSuiteCo
59
91
 
60
92
  if ((test?.result?.errors?.length ?? 0) > 0) return
61
93
 
62
- const meta = getAutoSnapshotOptions(test)
94
+ const meta = extractAutoSnapshotOptions(test)
63
95
  if (!shouldTakeSnapshot(meta)) return
64
96
 
97
+ test.meta.vis = { ...test.meta.vis, isAutoSnapshot: true }
98
+
65
99
  await test!.context.expect(getSubject(meta?.subjectDataTestId ?? subjectDataTestId)).toMatchImageSnapshot(meta)
66
100
  },
67
- matchPerTheme(themes: Record<string, (options: M) => Promise<boolean> | Promise<void> | boolean | void>) {
101
+ matchPerTheme(themes) {
68
102
  return async function matchImageSnapshot() {
69
103
  const test = ctx.getCurrentTest()
70
104
  if ((test?.result?.errors?.length ?? 0) > 0) return
71
105
 
72
- const meta = getAutoSnapshotOptions<M>(test)
106
+ const meta = extractAutoSnapshotOptions(test)
73
107
  if (!shouldTakeSnapshot(meta)) return
74
- const errors: any[] = []
108
+ const errors: Array<[string, Error]> = []
75
109
  for (const themeId in themes) {
76
110
  try {
77
111
  await new Promise((a) => setTimeout(a, 10))
78
- const r = await themes[themeId]!(meta!)
112
+ const r = await themes[themeId]!(meta! as any)
79
113
  if (r === false) continue
80
114
  await test!.context
81
115
  .expect(getSubject(meta?.subjectDataTestId ?? subjectDataTestId))
82
116
  .toMatchImageSnapshot({
83
117
  ...meta,
84
118
  customizeSnapshotId: meta?.customizeSnapshotId
85
- ? ({ id, index }) => `${meta.customizeSnapshotId!({ id, index })}-${themeId}`
119
+ ? ({ id, index }) => `${meta.customizeSnapshotId!({ id, index, isAutoSnapshot: true })}-${themeId}`
86
120
  : ({ id }) => `${id}-${themeId}`,
87
121
  })
88
122
  } catch (error) {
89
- errors.push([themeId, error])
123
+ errors.push([themeId, error as Error])
90
124
  }
91
125
  }
92
126
  if (errors.length > 0) {
93
- if (errors.length === 1) throw errors[0][1]
127
+ if (errors.length === 1) throw errors[0]![1]
94
128
  const taskId = toTaskId(test!)
95
129
  throw new AggregateError(
96
130
  errors,
@@ -1,5 +1,7 @@
1
1
  import type pixelMatch from 'pixelmatch'
2
2
  import type { Options as SsimOptions } from 'ssim.js'
3
+ import type { getCurrentTest } from 'vitest/suite'
4
+ import type { NAME } from './constants.ts'
3
5
 
4
6
  export interface ImageSnapshotTimeoutOptions {
5
7
  /**
@@ -19,9 +21,8 @@ export interface ImageSnapshotIdOptions {
19
21
  */
20
22
  customizeSnapshotId?: (context: {
21
23
  id: string
22
- // width: number
23
- // height: number
24
24
  index: number
25
+ isAutoSnapshot: boolean
25
26
  }) => string
26
27
  }
27
28
 
@@ -71,3 +72,22 @@ export type AutoSnapshotOptions = {
71
72
  */
72
73
  subjectDataTestId?: string | undefined
73
74
  }
75
+ export type ToMatchImageSnapshotOptions<M extends ComparisonMethod = 'pixel'> = ImageSnapshotTimeoutOptions &
76
+ ImageSnapshotIdOptions &
77
+ ImageSnapshotCompareOptions<M> & {
78
+ /**
79
+ * Expect the matcher to fail.
80
+ * If it passes, it will throw an error with details.
81
+ */
82
+ expectToFail?: boolean | undefined
83
+ }
84
+
85
+ export type SnapshotMeta<M extends ComparisonMethod> = ToMatchImageSnapshotOptions<M> &
86
+ AutoSnapshotOptions & {
87
+ enable?: boolean | undefined
88
+ [key: string]: unknown
89
+ }
90
+
91
+ export type CurrentTest = ReturnType<typeof getCurrentTest> & {
92
+ meta: { [NAME]?: SnapshotMeta<'pixel' | 'ssim'> & { isAutoSnapshot?: boolean | undefined } }
93
+ }
@@ -1,3 +0,0 @@
1
- import type { MetaTask, SnapshotMeta } from './snapshot_options.ts';
2
- export declare function getAutoSnapshotOptions<M extends SnapshotMeta<any> = SnapshotMeta<any>>(task: MetaTask): M | undefined;
3
- //# sourceMappingURL=snapshot_options.internal.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"snapshot_options.internal.d.ts","sourceRoot":"","sources":["../../src/client/snapshot_options.internal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEnE,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,YAAY,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,QAAQ,GAAG,CAAC,GAAG,SAAS,CAkBrH"}
@@ -1,20 +0,0 @@
1
- import { NAME } from "../shared/constants.js";
2
- export function getAutoSnapshotOptions(task) {
3
- if (!task)
4
- return;
5
- const list = [];
6
- let current = task;
7
- while (current?.suite) {
8
- list.unshift(current.suite.meta);
9
- current = current.suite;
10
- }
11
- list.unshift(task.file?.meta);
12
- list.push(task.meta);
13
- return list.reduce((acc, cur) => {
14
- if (cur?.[NAME]) {
15
- const { [NAME]: meta } = cur;
16
- return Object.assign({}, acc, meta);
17
- }
18
- return acc;
19
- }, {});
20
- }