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.
- package/esm/client/ctx.d.ts +2 -1
- package/esm/client/ctx.d.ts.map +1 -1
- package/esm/client/ctx.js +2 -0
- package/esm/client/expect/to_match_image_snapshot.d.ts +1 -1
- package/esm/client/expect/to_match_image_snapshot.d.ts.map +1 -1
- package/esm/client/expect/to_match_image_snapshot.types.d.ts +1 -8
- package/esm/client/expect/to_match_image_snapshot.types.d.ts.map +1 -1
- package/esm/client/image_snapshot_matcher.d.ts +2 -3
- package/esm/client/image_snapshot_matcher.d.ts.map +1 -1
- package/esm/client/image_snapshot_matcher.js +6 -3
- package/esm/client/image_snapshot_matcher.logic.d.ts +1 -1
- package/esm/client/image_snapshot_matcher.logic.d.ts.map +1 -1
- package/esm/client/page/has_image_snapshot.d.ts.map +1 -1
- package/esm/client/page/has_image_snapshot.js +1 -1
- package/esm/client/should_take_snapshot.d.ts +2 -2
- package/esm/client/should_take_snapshot.d.ts.map +1 -1
- package/esm/client/should_take_snapshot.js +3 -3
- package/esm/client/snapshot_options.d.ts +3 -6
- package/esm/client/snapshot_options.d.ts.map +1 -1
- package/esm/client/snapshot_options.js +19 -1
- package/esm/client/vitest_suite_proxy.d.ts +2 -1
- package/esm/client/vitest_suite_proxy.d.ts.map +1 -1
- package/esm/client.d.ts +1 -1
- package/esm/client.d.ts.map +1 -1
- package/esm/client.js +1 -1
- package/esm/config/types.d.ts +1 -1
- package/esm/config/types.d.ts.map +1 -1
- package/esm/index.d.ts +2 -1
- package/esm/index.d.ts.map +1 -1
- package/esm/index.js +1 -1
- package/esm/presets/enable.d.ts +3 -0
- package/esm/presets/enable.d.ts.map +1 -0
- package/esm/presets/enable.js +4 -0
- package/esm/server/vis_context.d.ts +4 -4
- package/esm/server/vis_context.d.ts.map +1 -1
- package/esm/server/vis_context.logic.d.ts +4 -4
- package/esm/server/vis_context.logic.d.ts.map +1 -1
- package/esm/server/vis_context.logic.js +4 -0
- package/esm/setup/create_vis.d.ts +18 -5
- package/esm/setup/create_vis.d.ts.map +1 -1
- package/esm/setup/create_vis.js +10 -30
- package/esm/setup/vis.d.ts +1 -14
- package/esm/setup/vis.d.ts.map +1 -1
- package/esm/shared/types.d.ts +21 -0
- package/esm/shared/types.d.ts.map +1 -1
- package/package.json +5 -1
- package/readme.md +37 -15
- package/src/client/ctx.ts +2 -0
- package/src/client/expect/to_match_image_snapshot.ts +1 -1
- package/src/client/expect/to_match_image_snapshot.types.ts +1 -16
- package/src/client/image_snapshot_matcher.logic.ts +1 -1
- package/src/client/image_snapshot_matcher.ts +12 -5
- package/src/client/page/has_image_snapshot.ts +6 -1
- package/src/client/should_take_snapshot.ts +4 -4
- package/src/client/snapshot_options.ts +27 -9
- package/src/client/vitest_suite_proxy.ts +3 -1
- package/src/client.ts +1 -1
- package/src/config/types.ts +1 -1
- package/src/index.ts +2 -1
- package/src/presets/enable.ts +5 -0
- package/src/server/vis_context.logic.ts +5 -1
- package/src/setup/create_vis.ts +73 -39
- package/src/shared/types.ts +22 -2
- package/esm/client/snapshot_options.internal.d.ts +0 -3
- package/esm/client/snapshot_options.internal.d.ts.map +0 -1
- package/esm/client/snapshot_options.internal.js +0 -20
- 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;
|
|
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.
|
|
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
|
-
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
|
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
|
|
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 '
|
|
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(
|
|
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
|
|
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) =>
|
|
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 {
|
|
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 ??
|
|
14
|
-
return document.body.childElementCount > 0 && m?.enable
|
|
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 {
|
|
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
|
|
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'
|
package/src/config/types.ts
CHANGED
|
@@ -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
|
@@ -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) {
|
package/src/setup/create_vis.ts
CHANGED
|
@@ -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 {
|
|
6
|
+
import { enableAuto, extractAutoSnapshotOptions } from '../client/snapshot_options.ts'
|
|
7
7
|
import type { SetupVisSuiteCommand } from '../server/commands/setup_vis_suite.ts'
|
|
8
8
|
|
|
9
|
-
|
|
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
|
-
|
|
13
|
-
*/
|
|
14
|
-
const vis = {
|
|
63
|
+
|
|
64
|
+
const vis: VisClientConfigurator<SM> = {
|
|
15
65
|
presets: {
|
|
16
|
-
|
|
17
|
-
|
|
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 =
|
|
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
|
|
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 =
|
|
106
|
+
const meta = extractAutoSnapshotOptions(test)
|
|
73
107
|
if (!shouldTakeSnapshot(meta)) return
|
|
74
|
-
const errors:
|
|
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,
|
package/src/shared/types.ts
CHANGED
|
@@ -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 +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
|
-
}
|