vitest-plugin-vis 1.6.5 → 2.0.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/image_snapshot_matcher.js +1 -1
- package/esm/client/page/has_image_snapshot.js +1 -1
- package/esm/client/snapshot_options.d.ts +1 -0
- package/esm/client/snapshot_options.d.ts.map +1 -1
- package/esm/client/snapshot_options.internal.d.ts +1 -1
- package/esm/client/snapshot_options.internal.d.ts.map +1 -1
- package/esm/config/types.d.ts +14 -5
- package/esm/config/types.d.ts.map +1 -1
- package/esm/server/commands/setup_vis_suite.js +1 -0
- package/esm/server/snapshot_path.d.ts +2 -1
- package/esm/server/snapshot_path.d.ts.map +1 -1
- package/esm/server/snapshot_path.js +18 -2
- package/esm/server/vis_context.d.ts +1 -1
- package/esm/server/vis_context.d.ts.map +1 -1
- package/esm/server/vis_context.logic.d.ts +1 -14
- package/esm/server/vis_context.logic.d.ts.map +1 -1
- package/esm/server/vis_context.logic.js +8 -7
- package/esm/server/vis_context.types.d.ts +22 -1
- package/esm/server/vis_context.types.d.ts.map +1 -1
- package/esm/setup/create_vis.d.ts +4 -3
- package/esm/setup/create_vis.d.ts.map +1 -1
- package/esm/setup/create_vis.js +6 -3
- package/esm/setup/vis.d.ts +2 -2
- package/esm/shared/constants.d.ts +1 -0
- package/esm/shared/constants.d.ts.map +1 -1
- package/esm/shared/constants.js +1 -0
- package/esm/shared/types.d.ts +4 -1
- package/esm/shared/types.d.ts.map +1 -1
- package/esm/testing/stubBrowserCommandContext.d.ts +5 -0
- package/esm/testing/stubBrowserCommandContext.d.ts.map +1 -1
- package/esm/testing/stubBrowserCommandContext.js +13 -0
- package/package.json +8 -8
- package/readme.md +50 -19
- package/src/client/image_snapshot_matcher.ts +1 -1
- package/src/client/page/has_image_snapshot.ts +1 -1
- package/src/client/snapshot_options.internal.ts +1 -1
- package/src/client/snapshot_options.ts +1 -0
- package/src/config/types.ts +17 -5
- package/src/server/commands/setup_vis_suite.ts +2 -2
- package/src/server/snapshot_path.ts +17 -2
- package/src/server/vis_context.logic.ts +9 -22
- package/src/server/vis_context.types.ts +23 -1
- package/src/setup/create_vis.ts +10 -8
- package/src/shared/constants.ts +1 -0
- package/src/shared/types.ts +6 -1
- package/src/testing/stubBrowserCommandContext.ts +15 -0
|
@@ -72,6 +72,6 @@ async function writeSnapshot(commands, path, image) {
|
|
|
72
72
|
async function parseImageSnapshotOptions(commands, taskId, options) {
|
|
73
73
|
const index = await commands.imageSnapshotNextIndex(taskId);
|
|
74
74
|
const { customizeSnapshotId, ...rest } = options;
|
|
75
|
-
const snapshotFileId = customizeSnapshotId(taskId, index);
|
|
75
|
+
const snapshotFileId = customizeSnapshotId({ id: taskId, index });
|
|
76
76
|
return { ...rest, snapshotFileId };
|
|
77
77
|
}
|
|
@@ -17,7 +17,7 @@ export function hasImageSnapshot(options) {
|
|
|
17
17
|
if (options?.customizeSnapshotId) {
|
|
18
18
|
return commands
|
|
19
19
|
.imageSnapshotNextIndex(taskId)
|
|
20
|
-
.then((index) => commands.hasImageSnapshot(taskId, options.customizeSnapshotId(taskId, index)));
|
|
20
|
+
.then((index) => commands.hasImageSnapshot(taskId, options.customizeSnapshotId({ id: taskId, index })));
|
|
21
21
|
}
|
|
22
22
|
return commands.hasImageSnapshot(taskId);
|
|
23
23
|
}
|
|
@@ -2,6 +2,7 @@ import type { AutoSnapshotOptions, ComparisonMethod } from '../shared/types.ts';
|
|
|
2
2
|
import type { ToMatchImageSnapshotOptions } from './expect/to_match_image_snapshot.types.ts';
|
|
3
3
|
export type SnapshotMeta<M extends ComparisonMethod> = ToMatchImageSnapshotOptions<M> & AutoSnapshotOptions & {
|
|
4
4
|
enable?: boolean | undefined;
|
|
5
|
+
[key: string]: unknown;
|
|
5
6
|
};
|
|
6
7
|
type Suite = {
|
|
7
8
|
meta: Record<string, any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot_options.d.ts","sourceRoot":"","sources":["../../src/client/snapshot_options.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE/E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAA;AAE5F,MAAM,MAAM,YAAY,CAAC,CAAC,SAAS,gBAAgB,IAAI,2BAA2B,CAAC,CAAC,CAAC,GACpF,mBAAmB,GAAG;IACrB,MAAM,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"snapshot_options.d.ts","sourceRoot":"","sources":["../../src/client/snapshot_options.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAE/E,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAA;AAE5F,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,KAAK,KAAK,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;CAAE,CAAA;AAErE,MAAM,MAAM,QAAQ,GACjB;IACA,IAAI,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAAE,GAAG,SAAS,CAAA;IAChD,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAA;IACzB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;CACxB,GACD,SAAS,CAAA;AAEZ;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,gBAAgB,EAChE,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,GAC7B,IAAI,CAAA;AACP,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,gBAAgB,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,GAAG,OAAO,GAAG,IAAI,CAAA"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { MetaTask, SnapshotMeta } from './snapshot_options.ts';
|
|
2
|
-
export declare function getAutoSnapshotOptions(task: MetaTask):
|
|
2
|
+
export declare function getAutoSnapshotOptions<M extends SnapshotMeta<any> = SnapshotMeta<any>>(task: MetaTask): M | undefined;
|
|
3
3
|
//# sourceMappingURL=snapshot_options.internal.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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"}
|
package/esm/config/types.d.ts
CHANGED
|
@@ -11,22 +11,31 @@ export type VisOptions<M extends ComparisonMethod = 'pixel'> = ImageSnapshotTime
|
|
|
11
11
|
* <snapshotRootDir>/<platform>/<snapshotSubpath>/<snapshotId>.png
|
|
12
12
|
*
|
|
13
13
|
* Default: undefined
|
|
14
|
+
*
|
|
15
|
+
* @deprecated Use `snapshotRootDir` instead.
|
|
14
16
|
*/
|
|
15
17
|
platform?: string | undefined;
|
|
16
18
|
/**
|
|
17
19
|
* The snapshot folder relative to the root of the project.
|
|
18
20
|
*
|
|
19
|
-
* Default: `__vis__`
|
|
21
|
+
* Default: `{projectRoot}/__vis__/{platform}`
|
|
20
22
|
*/
|
|
21
|
-
snapshotRootDir?: string |
|
|
23
|
+
snapshotRootDir?: string | ((context: {
|
|
24
|
+
ci: boolean;
|
|
25
|
+
browserName: string;
|
|
26
|
+
providerName: string;
|
|
27
|
+
platform: string;
|
|
28
|
+
screenshotFailures: boolean | undefined;
|
|
29
|
+
screenshotDirectory: string | undefined;
|
|
30
|
+
}) => string) | undefined;
|
|
22
31
|
/**
|
|
23
32
|
* Customize the snapshot subpath.
|
|
24
33
|
*
|
|
25
34
|
* The snapshot subpath is used along with `snapshotRootDir` to determine the folder of the snapshots:
|
|
26
35
|
*
|
|
27
|
-
* - baseline: `<snapshotRootDir
|
|
28
|
-
* - result: `<snapshotRootDir>/
|
|
29
|
-
* - diff: `<snapshotRootDir>/
|
|
36
|
+
* - baseline: `<snapshotRootDir>/baselines/<snapshotSubpath>/<snapshotId>.png`
|
|
37
|
+
* - result: `<snapshotRootDir>/results/<snapshotSubpath>/<snapshotId>.png`
|
|
38
|
+
* - diff: `<snapshotRootDir>/diffs/<snapshotSubpath>/<snapshotId>.png`
|
|
30
39
|
*
|
|
31
40
|
* Typically, you place your test files either in a dedicated `tests` folder or in the `src` folder along with your source code.
|
|
32
41
|
* By default, the snapshot subpath is the test file path with that folder removed to reduces nesting of the snapshot folders.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,mBAAmB,EACnB,gBAAgB,EAChB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,MAAM,oBAAoB,CAAA;AAE3B,YAAY,EAAE,gBAAgB,EAAE,CAAA;AAEhC,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,gBAAgB,GAAG,OAAO,IAAI,2BAA2B,GACzF,sBAAsB,GACtB,2BAA2B,CAAC,CAAC,CAAC,GAC9B,mBAAmB,GAAG;IACrB;;OAEG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAC/C
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,mBAAmB,EACnB,gBAAgB,EAChB,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,MAAM,oBAAoB,CAAA;AAE3B,YAAY,EAAE,gBAAgB,EAAE,CAAA;AAEhC,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,gBAAgB,GAAG,OAAO,IAAI,2BAA2B,GACzF,sBAAsB,GACtB,2BAA2B,CAAC,CAAC,CAAC,GAC9B,mBAAmB,GAAG;IACrB;;OAEG;IACH,MAAM,CAAC,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IAC/C;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B;;;;OAIG;IACH,eAAe,CAAC,EACb,MAAM,GACN,CAAC,CAAC,OAAO,EAAE;QACX,EAAE,EAAE,OAAO,CAAA;QACX,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAA;QACpB,QAAQ,EAAE,MAAM,CAAA;QAChB,kBAAkB,EAAE,OAAO,GAAG,SAAS,CAAA;QACvC,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAA;KACtC,KAAK,MAAM,CAAC,GACb,SAAS,CAAA;IACZ;;;;;;;;;;;;;;;;;;;OAmBG;IACH,wBAAwB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAA;CACtD,CAAA"}
|
|
@@ -2,5 +2,6 @@ import { visContext } from "../vis_context.js";
|
|
|
2
2
|
import { assertTestPathDefined } from "./_assertions.js";
|
|
3
3
|
export const setupVisSuite = async (context) => {
|
|
4
4
|
assertTestPathDefined(context, 'setupVisSuite');
|
|
5
|
+
// using props not currently listed in the types
|
|
5
6
|
return visContext.setupSuite(context);
|
|
6
7
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { VisOptions } from '../config/types.ts';
|
|
2
|
-
|
|
2
|
+
import type { PartialBrowserCommandContext } from './vis_context.types.ts';
|
|
3
|
+
export declare function resolveSnapshotRootDir(suite: PartialBrowserCommandContext, options: VisOptions): string;
|
|
3
4
|
export declare function getSnapshotSubpath(suiteName: string, options: Pick<VisOptions, 'customizeSnapshotSubpath'>): string;
|
|
4
5
|
//# sourceMappingURL=snapshot_path.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"snapshot_path.d.ts","sourceRoot":"","sources":["../../src/server/snapshot_path.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"snapshot_path.d.ts","sourceRoot":"","sources":["../../src/server/snapshot_path.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAGpD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAA;AAE1E,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,4BAA4B,EAAE,OAAO,EAAE,UAAU,UAY9F;AAKD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,0BAA0B,CAAC,UAG1G"}
|
|
@@ -1,7 +1,23 @@
|
|
|
1
|
+
import ci from 'is-ci';
|
|
1
2
|
import { SNAPSHOT_ROOT_DIR } from "../shared/constants.js";
|
|
2
3
|
import { trimCommonFolder } from "../shared/trim_common_folder.js";
|
|
3
|
-
export function resolveSnapshotRootDir(options) {
|
|
4
|
-
|
|
4
|
+
export function resolveSnapshotRootDir(suite, options) {
|
|
5
|
+
if (!options.snapshotRootDir)
|
|
6
|
+
return getSnapshotRootDir(SNAPSHOT_ROOT_DIR, options.platform);
|
|
7
|
+
const snapshotRootDir = options.snapshotRootDir;
|
|
8
|
+
if (typeof snapshotRootDir === 'string')
|
|
9
|
+
return getSnapshotRootDir(snapshotRootDir, options.platform);
|
|
10
|
+
return snapshotRootDir({
|
|
11
|
+
ci,
|
|
12
|
+
browserName: suite.provider.browserName,
|
|
13
|
+
providerName: suite.provider.name,
|
|
14
|
+
platform: process.platform,
|
|
15
|
+
screenshotFailures: suite.provider.options.screenshotFailures,
|
|
16
|
+
screenshotDirectory: suite.provider.options.screenshotDirectory,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function getSnapshotRootDir(snapshotRootDir, platform = process.platform) {
|
|
20
|
+
return `${snapshotRootDir}/${ci ? platform : 'local'}`;
|
|
5
21
|
}
|
|
6
22
|
export function getSnapshotSubpath(suiteName, options) {
|
|
7
23
|
const customizeSnapshotSubpath = options.customizeSnapshotSubpath ?? trimCommonFolder;
|
|
@@ -3,7 +3,7 @@ export declare const visContext: {
|
|
|
3
3
|
__test__getOptions(): import("../config.ts").VisOptions<any>;
|
|
4
4
|
__test__reset(): void;
|
|
5
5
|
__test__getState(): import("./vis_context.types.ts").VisState;
|
|
6
|
-
setupSuite(context: import("./vis_context.
|
|
6
|
+
setupSuite(context: import("./vis_context.types.ts").PartialBrowserCommandContext): Promise<import("type-plus").Pick._<import("./vis_context.types.ts").VisState, "subjectDataTestId">>;
|
|
7
7
|
getSnapshotInfo(testPath: string, name: string, options?: {
|
|
8
8
|
snapshotFileId?: string | undefined;
|
|
9
9
|
}): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vis_context.d.ts","sourceRoot":"","sources":["../../src/server/vis_context.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU;;;;;;;
|
|
1
|
+
{"version":3,"file":"vis_context.d.ts","sourceRoot":"","sources":["../../src/server/vis_context.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU;;;;;;;sBAC69C,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CADz8C,CAAA"}
|
|
@@ -1,18 +1,5 @@
|
|
|
1
1
|
import type { VisOptions } from '../config/types.ts';
|
|
2
|
-
import type { VisState } from './vis_context.types.ts';
|
|
3
|
-
export type PartialBrowserCommandContext = {
|
|
4
|
-
project: {
|
|
5
|
-
config: {
|
|
6
|
-
root: string;
|
|
7
|
-
snapshotOptions: {
|
|
8
|
-
updateSnapshot: 'all' | 'new' | 'none';
|
|
9
|
-
};
|
|
10
|
-
testTimeout: number;
|
|
11
|
-
hookTimeout: number;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
testPath: string;
|
|
15
|
-
};
|
|
2
|
+
import type { PartialBrowserCommandContext, VisState } from './vis_context.types.ts';
|
|
16
3
|
export declare function createVisContext(): {
|
|
17
4
|
setOptions<M extends "pixel" | "ssim">(options?: VisOptions<M>): void;
|
|
18
5
|
__test__getOptions(): VisOptions<any>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vis_context.logic.d.ts","sourceRoot":"","sources":["../../src/server/vis_context.logic.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAKpD,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"vis_context.logic.d.ts","sourceRoot":"","sources":["../../src/server/vis_context.logic.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAKpD,OAAO,KAAK,EAAE,4BAA4B,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAEpF,wBAAgB,gBAAgB;eAMnB,CAAC,SAAS,OAAO,GAAG,MAAM,YAAW,UAAU,CAAC,CAAC,CAAC;;;;IAY7D;;;;OAIG;wBACuB,4BAA4B;8BAa5B,MAAM,QAAQ,MAAM,YAAY;QAAE,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;;;;;;;;;;;;;;;;;;;2BAkB1E,MAAM,UAAU,MAAM;+BAGlB,MAAM,UAAU,MAAM,kBAAkB,MAAM,GAAG,SAAS;8BAI3D;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE;YAAE,KAAK,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,kBAAkB,MAAM,GAAG,SAAS;2BASlF,MAAM,UAAU,MAAM;;;;;;;;;;EAe9C;AAyBD,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,0BAA0B,CAAC;;;;;;;;EAWnH;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,0BAA0B,CAAC,UAElH"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { join, relative } from 'pathe';
|
|
2
2
|
import { pick } from 'type-plus';
|
|
3
|
-
import { DIFF_DIR, RESULT_DIR } from "../shared/constants.js";
|
|
3
|
+
import { BASELINE_DIR, DIFF_DIR, RESULT_DIR } from "../shared/constants.js";
|
|
4
4
|
import { file } from "./file.js";
|
|
5
5
|
import { getSnapshotSubpath, resolveSnapshotRootDir } from "./snapshot_path.js";
|
|
6
6
|
import { ctx } from "./vis_context.ctx.js";
|
|
@@ -64,8 +64,11 @@ export function createVisContext() {
|
|
|
64
64
|
getSnapshotFilename(info, snapshotFileId) {
|
|
65
65
|
if (snapshotFileId)
|
|
66
66
|
return `${snapshotFileId}.png`;
|
|
67
|
-
const customizeSnapshotId = visOptions.customizeSnapshotId ?? ((id, index) => `${id}-${index}`);
|
|
68
|
-
return `${customizeSnapshotId(
|
|
67
|
+
const customizeSnapshotId = visOptions.customizeSnapshotId ?? (({ id, index }) => `${id}-${index}`);
|
|
68
|
+
return `${customizeSnapshotId({
|
|
69
|
+
id: info.taskId,
|
|
70
|
+
index: info.task.count,
|
|
71
|
+
})}.png`;
|
|
69
72
|
},
|
|
70
73
|
getSuiteInfo(testPath, taskId) {
|
|
71
74
|
const suiteId = getSuiteId(state, testPath, visOptions);
|
|
@@ -84,16 +87,14 @@ export function createVisContext() {
|
|
|
84
87
|
return context;
|
|
85
88
|
}
|
|
86
89
|
async function setupState(suite, visOptions) {
|
|
87
|
-
const snapshotRootDir = resolveSnapshotRootDir(visOptions);
|
|
90
|
+
const snapshotRootDir = resolveSnapshotRootDir(suite, visOptions);
|
|
88
91
|
const projectPath = suite.project.config.root;
|
|
89
|
-
const platform = visOptions.platform ?? ctx.getSnapshotPlatform();
|
|
90
92
|
const state = {
|
|
91
93
|
projectPath,
|
|
92
|
-
platform,
|
|
93
94
|
testTimeout: suite.project.config.testTimeout,
|
|
94
95
|
hookTimeout: suite.project.config.hookTimeout,
|
|
95
96
|
snapshotRootDir,
|
|
96
|
-
snapshotBaselineDir: join(snapshotRootDir,
|
|
97
|
+
snapshotBaselineDir: join(snapshotRootDir, BASELINE_DIR),
|
|
97
98
|
snapshotResultDir: join(snapshotRootDir, RESULT_DIR),
|
|
98
99
|
snapshotDiffDir: join(snapshotRootDir, DIFF_DIR),
|
|
99
100
|
snapshotRootPath: join(projectPath, snapshotRootDir),
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export type VisState = {
|
|
2
2
|
projectPath: string;
|
|
3
|
-
platform: string;
|
|
4
3
|
testTimeout: number;
|
|
5
4
|
hookTimeout: number;
|
|
6
5
|
snapshotRootDir: string;
|
|
@@ -18,4 +17,26 @@ export type VisState = {
|
|
|
18
17
|
}>;
|
|
19
18
|
}>;
|
|
20
19
|
};
|
|
20
|
+
export type PartialBrowserCommandContext = {
|
|
21
|
+
project: {
|
|
22
|
+
config: {
|
|
23
|
+
root: string;
|
|
24
|
+
snapshotOptions: {
|
|
25
|
+
updateSnapshot: 'all' | 'new' | 'none';
|
|
26
|
+
};
|
|
27
|
+
testTimeout: number;
|
|
28
|
+
hookTimeout: number;
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
provider: {
|
|
32
|
+
name: string;
|
|
33
|
+
browserName: string;
|
|
34
|
+
options: {
|
|
35
|
+
headless: boolean | undefined;
|
|
36
|
+
screenshotFailures: boolean | undefined;
|
|
37
|
+
screenshotDirectory: string | undefined;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
testPath: string;
|
|
41
|
+
};
|
|
21
42
|
//# sourceMappingURL=vis_context.types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vis_context.types.d.ts","sourceRoot":"","sources":["../../src/server/vis_context.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG;IACtB,WAAW,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"vis_context.types.d.ts","sourceRoot":"","sources":["../../src/server/vis_context.types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,QAAQ,GAAG;IACtB,WAAW,EAAE,MAAM,CAAA;IAEnB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,eAAe,EAAE,MAAM,CAAA;IACvB,mBAAmB,EAAE,MAAM,CAAA;IAC3B,iBAAiB,EAAE,MAAM,CAAA;IACzB,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;IACxB,iBAAiB,EAAE,MAAM,GAAG,SAAS,CAAA;IACrC,MAAM,EAAE,MAAM,CACb,MAAM,EACN;QACC,WAAW,EAAE,MAAM,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,EAAE,MAAM,CACZ,MAAM,EACN;YACC,KAAK,EAAE,MAAM,CAAA;SACb,CACD,CAAA;KACD,CACD,CAAA;CACD,CAAA;AACD,MAAM,MAAM,4BAA4B,GAAG;IAC1C,OAAO,EAAE;QACR,MAAM,EAAE;YACP,IAAI,EAAE,MAAM,CAAA;YACZ,eAAe,EAAE;gBAChB,cAAc,EAAE,KAAK,GAAG,KAAK,GAAG,MAAM,CAAA;aACtC,CAAA;YACD,WAAW,EAAE,MAAM,CAAA;YACnB,WAAW,EAAE,MAAM,CAAA;SACnB,CAAA;KACD,CAAA;IACD,QAAQ,EAAE;QACT,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,MAAM,CAAA;QACnB,OAAO,EAAE;YACR,QAAQ,EAAE,OAAO,GAAG,SAAS,CAAA;YAC7B,kBAAkB,EAAE,OAAO,GAAG,SAAS,CAAA;YACvC,mBAAmB,EAAE,MAAM,GAAG,SAAS,CAAA;SACvC,CAAA;KACD,CAAA;IACD,QAAQ,EAAE,MAAM,CAAA;CAChB,CAAA"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { type SnapshotMeta } from '../client.ts';
|
|
1
2
|
import type { SetupVisSuiteCommand } from '../server/commands/setup_vis_suite.ts';
|
|
2
|
-
export declare function createVis(commands: SetupVisSuiteCommand): {
|
|
3
|
+
export declare function createVis<M extends SnapshotMeta<any>>(commands: SetupVisSuiteCommand): {
|
|
3
4
|
presets: {
|
|
4
5
|
/**
|
|
5
6
|
* Enable manual visual testing.
|
|
@@ -26,14 +27,14 @@ export declare function createVis(commands: SetupVisSuiteCommand): {
|
|
|
26
27
|
* })
|
|
27
28
|
* ```
|
|
28
29
|
*/
|
|
29
|
-
theme(themes: Record<string, () =>
|
|
30
|
+
theme(themes: Record<string, (options: M) => Promise<boolean> | Promise<void> | boolean | void>): void;
|
|
30
31
|
};
|
|
31
32
|
beforeAll: {
|
|
32
33
|
setup(): Promise<void>;
|
|
33
34
|
};
|
|
34
35
|
afterEach: {
|
|
35
36
|
matchImageSnapshot(): Promise<void>;
|
|
36
|
-
matchPerTheme(themes: Record<string, () => Promise<void> | void>): () => Promise<void>;
|
|
37
|
+
matchPerTheme(themes: Record<string, (options: M) => Promise<boolean> | Promise<void> | boolean | void>): () => Promise<void>;
|
|
37
38
|
};
|
|
38
39
|
};
|
|
39
40
|
//# sourceMappingURL=create_vis.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create_vis.d.ts","sourceRoot":"","sources":["../../src/setup/create_vis.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"create_vis.d.ts","sourceRoot":"","sources":["../../src/setup/create_vis.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,YAAY,EAAY,MAAM,cAAc,CAAA;AAI1D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAA;AAEjF,wBAAgB,SAAS,CAAC,CAAC,SAAS,YAAY,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,oBAAoB;;QAOlF;;WAEG;;QAIH;;;;WAIG;;QAKH;;;;;;;;;;;;;;WAcG;sBACW,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;;;;;;;8BAqBzE,MAAM,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,GAAG,IAAI,CAAC;;EA4CzG"}
|
package/esm/setup/create_vis.js
CHANGED
|
@@ -72,14 +72,17 @@ export function createVis(commands) {
|
|
|
72
72
|
const errors = [];
|
|
73
73
|
for (const themeId in themes) {
|
|
74
74
|
try {
|
|
75
|
-
await new Promise((a) => setTimeout(
|
|
75
|
+
await new Promise((a) => setTimeout(a, 10));
|
|
76
|
+
const r = await themes[themeId](meta);
|
|
77
|
+
if (r === false)
|
|
78
|
+
continue;
|
|
76
79
|
await test.context
|
|
77
80
|
.expect(getSubject(meta?.subjectDataTestId ?? subjectDataTestId))
|
|
78
81
|
.toMatchImageSnapshot({
|
|
79
82
|
...meta,
|
|
80
83
|
customizeSnapshotId: meta?.customizeSnapshotId
|
|
81
|
-
? (id, index) => `${meta.customizeSnapshotId(id, index)}-${themeId}`
|
|
82
|
-
: (id) => `${id}-${themeId}`,
|
|
84
|
+
? ({ id, index }) => `${meta.customizeSnapshotId({ id, index })}-${themeId}`
|
|
85
|
+
: ({ id }) => `${id}-${themeId}`,
|
|
83
86
|
});
|
|
84
87
|
}
|
|
85
88
|
catch (error) {
|
package/esm/setup/vis.d.ts
CHANGED
|
@@ -5,14 +5,14 @@ export declare const vis: {
|
|
|
5
5
|
presets: {
|
|
6
6
|
manual(): void;
|
|
7
7
|
auto(): void;
|
|
8
|
-
theme(themes: Record<string, () =>
|
|
8
|
+
theme(themes: Record<string, (options: import("../client.ts").SnapshotMeta<any>) => Promise<boolean> | Promise<void> | boolean | void>): void;
|
|
9
9
|
};
|
|
10
10
|
beforeAll: {
|
|
11
11
|
setup(): Promise<void>;
|
|
12
12
|
};
|
|
13
13
|
afterEach: {
|
|
14
14
|
matchImageSnapshot(): Promise<void>;
|
|
15
|
-
matchPerTheme(themes: Record<string, () => Promise<void> | void>): () => Promise<void>;
|
|
15
|
+
matchPerTheme(themes: Record<string, (options: import("../client.ts").SnapshotMeta<any>) => Promise<boolean> | Promise<void> | boolean | void>): () => Promise<void>;
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
//# sourceMappingURL=vis.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const NAME = "vis";
|
|
2
2
|
export declare const SNAPSHOT_ROOT_DIR = "__vis__";
|
|
3
|
+
export declare const BASELINE_DIR = "__baselines__";
|
|
3
4
|
export declare const RESULT_DIR = "__results__";
|
|
4
5
|
export declare const DIFF_DIR = "__diffs__";
|
|
5
6
|
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,QAAQ,CAAA;AACzB,eAAO,MAAM,iBAAiB,YAAY,CAAA;AAC1C,eAAO,MAAM,UAAU,gBAAgB,CAAA;AACvC,eAAO,MAAM,QAAQ,cAAc,CAAA"}
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/shared/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,QAAQ,CAAA;AACzB,eAAO,MAAM,iBAAiB,YAAY,CAAA;AAC1C,eAAO,MAAM,YAAY,kBAAkB,CAAA;AAC3C,eAAO,MAAM,UAAU,gBAAgB,CAAA;AACvC,eAAO,MAAM,QAAQ,cAAc,CAAA"}
|
package/esm/shared/constants.js
CHANGED
package/esm/shared/types.d.ts
CHANGED
|
@@ -15,7 +15,10 @@ export interface ImageSnapshotIdOptions {
|
|
|
15
15
|
* @param id The id of the snapshot.
|
|
16
16
|
* @param index The index of the snapshot.
|
|
17
17
|
*/
|
|
18
|
-
customizeSnapshotId?: (
|
|
18
|
+
customizeSnapshotId?: (context: {
|
|
19
|
+
id: string;
|
|
20
|
+
index: number;
|
|
21
|
+
}) => string;
|
|
19
22
|
}
|
|
20
23
|
export type ComparisonMethod = 'pixel' | 'ssim';
|
|
21
24
|
export type FailureThresholdOptions = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,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,EAAE,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/shared/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AACnD,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,iBAAiB,GAAG,SAAS,CAAA;CAC3C,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,3 +1,8 @@
|
|
|
1
1
|
import type { BrowserCommandContext } from 'vitest/node';
|
|
2
|
+
import type { PartialBrowserCommandContext } from '../server/vis_context.types.ts';
|
|
2
3
|
export declare const stubBrowserCommandContext: (stub?: import("type-plus").RecursivePartial<BrowserCommandContext> | undefined) => BrowserCommandContext;
|
|
4
|
+
export declare function createStubPartialBrowserCommandContext({ root, testPath }: {
|
|
5
|
+
root: string;
|
|
6
|
+
testPath: string;
|
|
7
|
+
}): (stub?: import("type-plus").RecursivePartial<PartialBrowserCommandContext> | undefined) => PartialBrowserCommandContext;
|
|
3
8
|
//# sourceMappingURL=stubBrowserCommandContext.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stubBrowserCommandContext.d.ts","sourceRoot":"","sources":["../../src/testing/stubBrowserCommandContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"stubBrowserCommandContext.d.ts","sourceRoot":"","sources":["../../src/testing/stubBrowserCommandContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAA;AAElF,eAAO,MAAM,yBAAyB,2GAAwC,CAAA;AAE9E,wBAAgB,sCAAsC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,2HAY5G"}
|
|
@@ -1,2 +1,15 @@
|
|
|
1
1
|
import { stub } from 'type-plus';
|
|
2
2
|
export const stubBrowserCommandContext = stub.build({});
|
|
3
|
+
export function createStubPartialBrowserCommandContext({ root, testPath }) {
|
|
4
|
+
return stub.build({
|
|
5
|
+
project: {
|
|
6
|
+
config: {
|
|
7
|
+
root,
|
|
8
|
+
},
|
|
9
|
+
},
|
|
10
|
+
provider: {
|
|
11
|
+
options: {},
|
|
12
|
+
},
|
|
13
|
+
testPath,
|
|
14
|
+
});
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-plugin-vis",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Vitest visual testing plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"image-snapshot",
|
|
@@ -77,21 +77,21 @@
|
|
|
77
77
|
"@types/is-ci": "^3.0.4",
|
|
78
78
|
"@types/pngjs": "^6.0.5",
|
|
79
79
|
"@types/react": "^19.0.2",
|
|
80
|
-
"@vitest/browser": "^3.0.
|
|
81
|
-
"@vitest/coverage-istanbul": "^3.0.
|
|
82
|
-
"@vitest/coverage-v8": "^3.0.
|
|
83
|
-
"@vitest/expect": "^3.0.
|
|
80
|
+
"@vitest/browser": "^3.0.5",
|
|
81
|
+
"@vitest/coverage-istanbul": "^3.0.5",
|
|
82
|
+
"@vitest/coverage-v8": "^3.0.5",
|
|
83
|
+
"@vitest/expect": "^3.0.5",
|
|
84
84
|
"cross-env": "^7.0.3",
|
|
85
85
|
"never-fail": "^1.1.4",
|
|
86
86
|
"npm-run-all2": "^7.0.2",
|
|
87
|
-
"playwright": "
|
|
87
|
+
"playwright": "latest",
|
|
88
88
|
"react": "^19.0.0",
|
|
89
89
|
"react-dom": "^19.0.0",
|
|
90
90
|
"typescript": "^5.7.2",
|
|
91
91
|
"vite": "^6.0.9",
|
|
92
|
-
"vitest": "^3.0.
|
|
92
|
+
"vitest": "^3.0.5",
|
|
93
93
|
"vitest-browser-react": "^0.0.4",
|
|
94
|
-
"webdriverio": "
|
|
94
|
+
"webdriverio": "latest"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
97
|
"@vitest/browser": "^2 || ^3",
|
package/readme.md
CHANGED
|
@@ -29,11 +29,19 @@ import { vis } from 'vitest-plugin-vis/config'
|
|
|
29
29
|
export default defineConfig({
|
|
30
30
|
plugins: [vis()],
|
|
31
31
|
test: {
|
|
32
|
+
// vitest v2
|
|
32
33
|
browser: {
|
|
33
|
-
// typical browser config
|
|
34
34
|
enabled: true,
|
|
35
35
|
provider: 'playwright',
|
|
36
|
-
name: 'chromium'
|
|
36
|
+
name: 'chromium',
|
|
37
|
+
},
|
|
38
|
+
// vitest v3
|
|
39
|
+
browser: {
|
|
40
|
+
enabled: true,
|
|
41
|
+
provider: 'playwright',
|
|
42
|
+
instances: [
|
|
43
|
+
{ browser: 'chromium' }
|
|
44
|
+
]
|
|
37
45
|
}
|
|
38
46
|
}
|
|
39
47
|
})
|
|
@@ -47,8 +55,9 @@ This default configuration will:
|
|
|
47
55
|
- Timeout for image comparison is set to `30000 ms`.
|
|
48
56
|
- Local (non-CI) image snapshots are saved in the `<root>/__vis__/local` directory.
|
|
49
57
|
- CI image snapshots are saved in the `<root>/__vis__/<process.platform>` directory.
|
|
50
|
-
- Image snapshots of the current test run are saved in the `<root>/__vis__
|
|
51
|
-
- Diff images are saved in the `<root>/__vis__
|
|
58
|
+
- Image snapshots of the current test run are saved in the `<root>/__vis__/*/__results__` directory.
|
|
59
|
+
- Diff images are saved in the `<root>/__vis__/*/__diffs__` directory.
|
|
60
|
+
- Baseline images are saved in the `<root>/__vis__/*/__baselines__` directory.
|
|
52
61
|
|
|
53
62
|
You can customize the configuration:
|
|
54
63
|
|
|
@@ -61,8 +70,15 @@ export default defineConfig({
|
|
|
61
70
|
plugins: [
|
|
62
71
|
vis({
|
|
63
72
|
preset: 'auto',
|
|
64
|
-
snapshotRootDir:
|
|
65
|
-
|
|
73
|
+
snapshotRootDir: ({
|
|
74
|
+
ci, // true if running on CI
|
|
75
|
+
platform, // process.platform
|
|
76
|
+
providerName, // 'playwright' or 'webdriverio'
|
|
77
|
+
browserName,
|
|
78
|
+
screenshotFailures, // from `browser` config
|
|
79
|
+
screenshotDirectory, // from `browser` config
|
|
80
|
+
}) => `__vis__/${ci ? platform : 'local'}`,
|
|
81
|
+
platform: '...', // {process.platform} or `local` (deprecated use `snapshotRootDir` instead)
|
|
66
82
|
customizeSnapshotSubpath: (subpath) => trimCommonFolder(subpath),
|
|
67
83
|
customizeSnapshotId: (id, index) => `${id}-${index}`,
|
|
68
84
|
// set a default subject (e.g. 'subject') to capture image snapshot
|
|
@@ -92,8 +108,7 @@ import { vis } from 'vitest-plugin-vis/config'
|
|
|
92
108
|
|
|
93
109
|
export default defineConfig({
|
|
94
110
|
plugins: [
|
|
95
|
-
|
|
96
|
-
vis({}) // or vis({ preset: 'none' })
|
|
111
|
+
vis({ preset: 'none' })
|
|
97
112
|
],
|
|
98
113
|
test:{
|
|
99
114
|
browser:{/* ... */},
|
|
@@ -113,8 +128,8 @@ vis.presets.manual()
|
|
|
113
128
|
// or capture image snapshot for all rendering tests
|
|
114
129
|
// for multiple themes (light and dark in this example)
|
|
115
130
|
vis.presets.theme({
|
|
116
|
-
light() { document.body.classList.remove('dark') },
|
|
117
|
-
dark() { document.body.classList.add('dark') },
|
|
131
|
+
async light() { document.body.classList.remove('dark') },
|
|
132
|
+
async dark() { document.body.classList.add('dark') },
|
|
118
133
|
})
|
|
119
134
|
```
|
|
120
135
|
|
|
@@ -123,7 +138,7 @@ vis.presets.theme({
|
|
|
123
138
|
The main usage of this addon is to use the `toMatchImageSnapshot` matcher.
|
|
124
139
|
|
|
125
140
|
Since it is exposed under the `expect` object of `vitest`,
|
|
126
|
-
you typically do not need to import `
|
|
141
|
+
you typically do not need to import `vitest-plugin-vis` directly.
|
|
127
142
|
|
|
128
143
|
Because of this, TypeScript may not recognize the matcher.
|
|
129
144
|
To address this, you can add the following to your `tsconfig.json`:
|
|
@@ -198,6 +213,22 @@ setAutoSnapshotOptions({
|
|
|
198
213
|
})
|
|
199
214
|
```
|
|
200
215
|
|
|
216
|
+
You can also provide additional options, which you can use during theme to enable/disable snapshot for each theme:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
setAutoSnapshotOptions({
|
|
220
|
+
skipDark: true
|
|
221
|
+
})
|
|
222
|
+
|
|
223
|
+
// in vitest.setup.ts
|
|
224
|
+
vis.presets.theme({
|
|
225
|
+
async dark(options) {
|
|
226
|
+
if (options.skipDark) return false
|
|
227
|
+
document.body.classList.add('dark')
|
|
228
|
+
},
|
|
229
|
+
})
|
|
230
|
+
```
|
|
231
|
+
|
|
201
232
|
### Manual Snapshot
|
|
202
233
|
|
|
203
234
|
You can also set the preset to `manual` and compare snapshots manually:
|
|
@@ -218,9 +249,9 @@ export default defineConfig({
|
|
|
218
249
|
// some.test.ts
|
|
219
250
|
import 'vitest-browser-react' // do this in your setup file to get `page.render`
|
|
220
251
|
import { page } from '@vitest/browser/context'
|
|
221
|
-
import {
|
|
252
|
+
import { it } from 'vitest'
|
|
222
253
|
|
|
223
|
-
it('manual snapshot', async () => {
|
|
254
|
+
it('manual snapshot', async ({ expect }) => {
|
|
224
255
|
page.render(<div data-testid="subject">hello world</div>)
|
|
225
256
|
await expect(document.body).toMatchImageSnapshot(/* options */)
|
|
226
257
|
// or
|
|
@@ -235,9 +266,9 @@ You can customize the snapshot comparison options per assertion:
|
|
|
235
266
|
// some.test.ts
|
|
236
267
|
import 'vitest-browser-react' // do this in your setup file to get `page.render`
|
|
237
268
|
import { page } from '@vitest/browser/context'
|
|
238
|
-
import {
|
|
269
|
+
import { it } from 'vitest'
|
|
239
270
|
|
|
240
|
-
it('manual snapshot with options', async () => {
|
|
271
|
+
it('manual snapshot with options', async ({ expect }) => {
|
|
241
272
|
page.render(<div data-testid="subject">hello world</div>)
|
|
242
273
|
const subject = page.getByTestId('subject')
|
|
243
274
|
await expect(subject).toMatchImageSnapshot({
|
|
@@ -258,9 +289,9 @@ While less common, you can also check if a snapshot exists:
|
|
|
258
289
|
|
|
259
290
|
```ts
|
|
260
291
|
import { page } from '@vitest/browser/context'
|
|
261
|
-
import {
|
|
292
|
+
import { it } from 'vitest'
|
|
262
293
|
|
|
263
|
-
it('Has Snapshot', async () => {
|
|
294
|
+
it('Has Snapshot', async ({ expect }) => {
|
|
264
295
|
const hasSnapshot = await page.hasImageSnapshot(/* options */)
|
|
265
296
|
if (!hasSnapshot) {
|
|
266
297
|
// do something
|
|
@@ -279,8 +310,8 @@ The local snapshots, current run results, and diffs should be ignored by git.
|
|
|
279
310
|
Add the following lines to your `.gitignore` file:
|
|
280
311
|
|
|
281
312
|
```sh
|
|
282
|
-
**/__vis__
|
|
283
|
-
**/__vis__
|
|
313
|
+
**/__vis__/**/__diffs__
|
|
314
|
+
**/__vis__/**/__results__
|
|
284
315
|
**/__vis__/local
|
|
285
316
|
```
|
|
286
317
|
|
|
@@ -107,6 +107,6 @@ async function parseImageSnapshotOptions(
|
|
|
107
107
|
) {
|
|
108
108
|
const index = await commands.imageSnapshotNextIndex(taskId)
|
|
109
109
|
const { customizeSnapshotId, ...rest } = options
|
|
110
|
-
const snapshotFileId = customizeSnapshotId!(taskId, index)
|
|
110
|
+
const snapshotFileId = customizeSnapshotId!({ id: taskId, index })
|
|
111
111
|
return { ...rest, snapshotFileId }
|
|
112
112
|
}
|
|
@@ -26,7 +26,7 @@ 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!(taskId, index)))
|
|
29
|
+
.then((index) => commands.hasImageSnapshot(taskId, options.customizeSnapshotId!({ id: taskId, index })))
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
return commands.hasImageSnapshot(taskId)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NAME } from '../shared/constants.ts'
|
|
2
2
|
import type { MetaTask, SnapshotMeta } from './snapshot_options.ts'
|
|
3
3
|
|
|
4
|
-
export function getAutoSnapshotOptions(task: MetaTask):
|
|
4
|
+
export function getAutoSnapshotOptions<M extends SnapshotMeta<any> = SnapshotMeta<any>>(task: MetaTask): M | undefined {
|
|
5
5
|
if (!task) return
|
|
6
6
|
|
|
7
7
|
const list: any[] = []
|
|
@@ -6,6 +6,7 @@ import type { ToMatchImageSnapshotOptions } from './expect/to_match_image_snapsh
|
|
|
6
6
|
export type SnapshotMeta<M extends ComparisonMethod> = ToMatchImageSnapshotOptions<M> &
|
|
7
7
|
AutoSnapshotOptions & {
|
|
8
8
|
enable?: boolean | undefined
|
|
9
|
+
[key: string]: unknown
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
type Suite = { meta: Record<string, any>; suite?: Suite | undefined }
|
package/src/config/types.ts
CHANGED
|
@@ -22,22 +22,34 @@ export type VisOptions<M extends ComparisonMethod = 'pixel'> = ImageSnapshotTime
|
|
|
22
22
|
* <snapshotRootDir>/<platform>/<snapshotSubpath>/<snapshotId>.png
|
|
23
23
|
*
|
|
24
24
|
* Default: undefined
|
|
25
|
+
*
|
|
26
|
+
* @deprecated Use `snapshotRootDir` instead.
|
|
25
27
|
*/
|
|
26
28
|
platform?: string | undefined
|
|
27
29
|
/**
|
|
28
30
|
* The snapshot folder relative to the root of the project.
|
|
29
31
|
*
|
|
30
|
-
* Default: `__vis__`
|
|
32
|
+
* Default: `{projectRoot}/__vis__/{platform}`
|
|
31
33
|
*/
|
|
32
|
-
snapshotRootDir?:
|
|
34
|
+
snapshotRootDir?:
|
|
35
|
+
| string
|
|
36
|
+
| ((context: {
|
|
37
|
+
ci: boolean
|
|
38
|
+
browserName: string
|
|
39
|
+
providerName: string
|
|
40
|
+
platform: string
|
|
41
|
+
screenshotFailures: boolean | undefined
|
|
42
|
+
screenshotDirectory: string | undefined
|
|
43
|
+
}) => string)
|
|
44
|
+
| undefined
|
|
33
45
|
/**
|
|
34
46
|
* Customize the snapshot subpath.
|
|
35
47
|
*
|
|
36
48
|
* The snapshot subpath is used along with `snapshotRootDir` to determine the folder of the snapshots:
|
|
37
49
|
*
|
|
38
|
-
* - baseline: `<snapshotRootDir
|
|
39
|
-
* - result: `<snapshotRootDir>/
|
|
40
|
-
* - diff: `<snapshotRootDir>/
|
|
50
|
+
* - baseline: `<snapshotRootDir>/baselines/<snapshotSubpath>/<snapshotId>.png`
|
|
51
|
+
* - result: `<snapshotRootDir>/results/<snapshotSubpath>/<snapshotId>.png`
|
|
52
|
+
* - diff: `<snapshotRootDir>/diffs/<snapshotSubpath>/<snapshotId>.png`
|
|
41
53
|
*
|
|
42
54
|
* Typically, you place your test files either in a dedicated `tests` folder or in the `src` folder along with your source code.
|
|
43
55
|
* By default, the snapshot subpath is the test file path with that folder removed to reduces nesting of the snapshot folders.
|
|
@@ -17,6 +17,6 @@ export const setupVisSuite: BrowserCommand<[]> = async (
|
|
|
17
17
|
context,
|
|
18
18
|
): Promise<{ subjectDataTestId: string | undefined }> => {
|
|
19
19
|
assertTestPathDefined(context, 'setupVisSuite')
|
|
20
|
-
|
|
21
|
-
return visContext.setupSuite(context)
|
|
20
|
+
// using props not currently listed in the types
|
|
21
|
+
return visContext.setupSuite(context as any)
|
|
22
22
|
}
|
|
@@ -1,11 +1,26 @@
|
|
|
1
|
+
import ci from 'is-ci'
|
|
1
2
|
import type { VisOptions } from '../config/types.ts'
|
|
2
3
|
import { SNAPSHOT_ROOT_DIR } from '../shared/constants.ts'
|
|
3
4
|
import { trimCommonFolder } from '../shared/trim_common_folder.ts'
|
|
5
|
+
import type { PartialBrowserCommandContext } from './vis_context.types.ts'
|
|
4
6
|
|
|
5
|
-
export function resolveSnapshotRootDir(
|
|
6
|
-
|
|
7
|
+
export function resolveSnapshotRootDir(suite: PartialBrowserCommandContext, options: VisOptions) {
|
|
8
|
+
if (!options.snapshotRootDir) return getSnapshotRootDir(SNAPSHOT_ROOT_DIR, options.platform)
|
|
9
|
+
const snapshotRootDir = options.snapshotRootDir
|
|
10
|
+
if (typeof snapshotRootDir === 'string') return getSnapshotRootDir(snapshotRootDir, options.platform!)
|
|
11
|
+
return snapshotRootDir({
|
|
12
|
+
ci,
|
|
13
|
+
browserName: suite.provider.browserName,
|
|
14
|
+
providerName: suite.provider.name,
|
|
15
|
+
platform: process.platform,
|
|
16
|
+
screenshotFailures: suite.provider.options.screenshotFailures,
|
|
17
|
+
screenshotDirectory: suite.provider.options.screenshotDirectory,
|
|
18
|
+
})
|
|
7
19
|
}
|
|
8
20
|
|
|
21
|
+
function getSnapshotRootDir(snapshotRootDir: string, platform: string | undefined = process.platform) {
|
|
22
|
+
return `${snapshotRootDir}/${ci ? platform : 'local'}`
|
|
23
|
+
}
|
|
9
24
|
export function getSnapshotSubpath(suiteName: string, options: Pick<VisOptions, 'customizeSnapshotSubpath'>) {
|
|
10
25
|
const customizeSnapshotSubpath = options.customizeSnapshotSubpath ?? trimCommonFolder
|
|
11
26
|
return customizeSnapshotSubpath(suiteName)
|
|
@@ -1,25 +1,11 @@
|
|
|
1
1
|
import { join, relative } from 'pathe'
|
|
2
2
|
import { pick } from 'type-plus'
|
|
3
3
|
import type { VisOptions } from '../config/types.ts'
|
|
4
|
-
import { DIFF_DIR, RESULT_DIR } from '../shared/constants.ts'
|
|
4
|
+
import { BASELINE_DIR, DIFF_DIR, RESULT_DIR } from '../shared/constants.ts'
|
|
5
5
|
import { file } from './file.ts'
|
|
6
6
|
import { getSnapshotSubpath, resolveSnapshotRootDir } from './snapshot_path.ts'
|
|
7
7
|
import { ctx } from './vis_context.ctx.ts'
|
|
8
|
-
import type { VisState } from './vis_context.types.ts'
|
|
9
|
-
|
|
10
|
-
export type PartialBrowserCommandContext = {
|
|
11
|
-
project: {
|
|
12
|
-
config: {
|
|
13
|
-
root: string
|
|
14
|
-
snapshotOptions: {
|
|
15
|
-
updateSnapshot: 'all' | 'new' | 'none'
|
|
16
|
-
}
|
|
17
|
-
testTimeout: number
|
|
18
|
-
hookTimeout: number
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
testPath: string
|
|
22
|
-
}
|
|
8
|
+
import type { PartialBrowserCommandContext, VisState } from './vis_context.types.ts'
|
|
23
9
|
|
|
24
10
|
export function createVisContext() {
|
|
25
11
|
let visOptions: VisOptions<any> = {}
|
|
@@ -85,8 +71,11 @@ export function createVisContext() {
|
|
|
85
71
|
getSnapshotFilename(info: { taskId: string; task: { count: number } }, snapshotFileId: string | undefined) {
|
|
86
72
|
if (snapshotFileId) return `${snapshotFileId}.png`
|
|
87
73
|
|
|
88
|
-
const customizeSnapshotId = visOptions.customizeSnapshotId ?? ((id, index) => `${id}-${index}`)
|
|
89
|
-
return `${customizeSnapshotId(
|
|
74
|
+
const customizeSnapshotId = visOptions.customizeSnapshotId ?? (({ id, index }) => `${id}-${index}`)
|
|
75
|
+
return `${customizeSnapshotId({
|
|
76
|
+
id: info.taskId,
|
|
77
|
+
index: info.task.count,
|
|
78
|
+
})}.png`
|
|
90
79
|
},
|
|
91
80
|
getSuiteInfo(testPath: string, taskId: string) {
|
|
92
81
|
const suiteId = getSuiteId(state, testPath, visOptions)
|
|
@@ -109,17 +98,15 @@ async function setupState(
|
|
|
109
98
|
suite: PartialBrowserCommandContext,
|
|
110
99
|
visOptions: Pick<VisOptions, 'snapshotRootDir' | 'platform' | 'subjectDataTestId'>,
|
|
111
100
|
) {
|
|
112
|
-
const snapshotRootDir = resolveSnapshotRootDir(visOptions)
|
|
101
|
+
const snapshotRootDir = resolveSnapshotRootDir(suite, visOptions)
|
|
113
102
|
const projectPath = suite.project.config.root
|
|
114
|
-
const platform = visOptions.platform ?? ctx.getSnapshotPlatform()
|
|
115
103
|
|
|
116
104
|
const state = {
|
|
117
105
|
projectPath,
|
|
118
|
-
platform,
|
|
119
106
|
testTimeout: suite.project.config.testTimeout,
|
|
120
107
|
hookTimeout: suite.project.config.hookTimeout,
|
|
121
108
|
snapshotRootDir,
|
|
122
|
-
snapshotBaselineDir: join(snapshotRootDir,
|
|
109
|
+
snapshotBaselineDir: join(snapshotRootDir, BASELINE_DIR),
|
|
123
110
|
snapshotResultDir: join(snapshotRootDir, RESULT_DIR),
|
|
124
111
|
snapshotDiffDir: join(snapshotRootDir, DIFF_DIR),
|
|
125
112
|
snapshotRootPath: join(projectPath, snapshotRootDir),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type VisState = {
|
|
2
2
|
projectPath: string
|
|
3
|
-
platform: string
|
|
3
|
+
// platform: string
|
|
4
4
|
testTimeout: number
|
|
5
5
|
hookTimeout: number
|
|
6
6
|
snapshotRootDir: string
|
|
@@ -24,3 +24,25 @@ export type VisState = {
|
|
|
24
24
|
}
|
|
25
25
|
>
|
|
26
26
|
}
|
|
27
|
+
export type PartialBrowserCommandContext = {
|
|
28
|
+
project: {
|
|
29
|
+
config: {
|
|
30
|
+
root: string
|
|
31
|
+
snapshotOptions: {
|
|
32
|
+
updateSnapshot: 'all' | 'new' | 'none'
|
|
33
|
+
}
|
|
34
|
+
testTimeout: number
|
|
35
|
+
hookTimeout: number
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
provider: {
|
|
39
|
+
name: string
|
|
40
|
+
browserName: string
|
|
41
|
+
options: {
|
|
42
|
+
headless: boolean | undefined
|
|
43
|
+
screenshotFailures: boolean | undefined
|
|
44
|
+
screenshotDirectory: string | undefined
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
testPath: string
|
|
48
|
+
}
|
package/src/setup/create_vis.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import dedent from 'dedent'
|
|
2
2
|
import { afterEach, beforeAll } from 'vitest'
|
|
3
|
-
import { toTaskId } from '../client.ts'
|
|
3
|
+
import { 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
6
|
import { getAutoSnapshotOptions } from '../client/snapshot_options.internal.js'
|
|
7
7
|
import type { SetupVisSuiteCommand } from '../server/commands/setup_vis_suite.ts'
|
|
8
8
|
|
|
9
|
-
export function createVis(commands: SetupVisSuiteCommand) {
|
|
9
|
+
export function createVis<M extends SnapshotMeta<any>>(commands: SetupVisSuiteCommand) {
|
|
10
10
|
let subjectDataTestId: string | undefined
|
|
11
11
|
/**
|
|
12
12
|
* Visual test configuration on the client side.
|
|
@@ -43,7 +43,7 @@ export function createVis(commands: SetupVisSuiteCommand) {
|
|
|
43
43
|
* })
|
|
44
44
|
* ```
|
|
45
45
|
*/
|
|
46
|
-
theme(themes: Record<string, () =>
|
|
46
|
+
theme(themes: Record<string, (options: M) => Promise<boolean> | Promise<void> | boolean | void>) {
|
|
47
47
|
beforeAll(vis.beforeAll.setup)
|
|
48
48
|
afterEach(vis.afterEach.matchPerTheme(themes))
|
|
49
49
|
},
|
|
@@ -64,24 +64,26 @@ export function createVis(commands: SetupVisSuiteCommand) {
|
|
|
64
64
|
|
|
65
65
|
await test!.context.expect(getSubject(meta?.subjectDataTestId ?? subjectDataTestId)).toMatchImageSnapshot(meta)
|
|
66
66
|
},
|
|
67
|
-
matchPerTheme(themes: Record<string, () => Promise<void> | void>) {
|
|
67
|
+
matchPerTheme(themes: Record<string, (options: M) => Promise<boolean> | Promise<void> | boolean | void>) {
|
|
68
68
|
return async function matchImageSnapshot() {
|
|
69
69
|
const test = ctx.getCurrentTest()
|
|
70
70
|
if ((test?.result?.errors?.length ?? 0) > 0) return
|
|
71
71
|
|
|
72
|
-
const meta = getAutoSnapshotOptions(test)
|
|
72
|
+
const meta = getAutoSnapshotOptions<M>(test)
|
|
73
73
|
if (!shouldTakeSnapshot(meta)) return
|
|
74
74
|
const errors: any[] = []
|
|
75
75
|
for (const themeId in themes) {
|
|
76
76
|
try {
|
|
77
|
-
await new Promise((a) => setTimeout(
|
|
77
|
+
await new Promise((a) => setTimeout(a, 10))
|
|
78
|
+
const r = await themes[themeId]!(meta!)
|
|
79
|
+
if (r === false) continue
|
|
78
80
|
await test!.context
|
|
79
81
|
.expect(getSubject(meta?.subjectDataTestId ?? subjectDataTestId))
|
|
80
82
|
.toMatchImageSnapshot({
|
|
81
83
|
...meta,
|
|
82
84
|
customizeSnapshotId: meta?.customizeSnapshotId
|
|
83
|
-
? (id, index) => `${meta.customizeSnapshotId!(id, index)}-${themeId}`
|
|
84
|
-
: (id) => `${id}-${themeId}`,
|
|
85
|
+
? ({ id, index }) => `${meta.customizeSnapshotId!({ id, index })}-${themeId}`
|
|
86
|
+
: ({ id }) => `${id}-${themeId}`,
|
|
85
87
|
})
|
|
86
88
|
} catch (error) {
|
|
87
89
|
errors.push([themeId, error])
|
package/src/shared/constants.ts
CHANGED
package/src/shared/types.ts
CHANGED
|
@@ -17,7 +17,12 @@ export interface ImageSnapshotIdOptions {
|
|
|
17
17
|
* @param id The id of the snapshot.
|
|
18
18
|
* @param index The index of the snapshot.
|
|
19
19
|
*/
|
|
20
|
-
customizeSnapshotId?: (
|
|
20
|
+
customizeSnapshotId?: (context: {
|
|
21
|
+
id: string
|
|
22
|
+
// width: number
|
|
23
|
+
// height: number
|
|
24
|
+
index: number
|
|
25
|
+
}) => string
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
export type ComparisonMethod = 'pixel' | 'ssim'
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import { stub } from 'type-plus'
|
|
2
2
|
import type { BrowserCommandContext } from 'vitest/node'
|
|
3
|
+
import type { PartialBrowserCommandContext } from '../server/vis_context.types.ts'
|
|
3
4
|
|
|
4
5
|
export const stubBrowserCommandContext = stub.build<BrowserCommandContext>({})
|
|
6
|
+
|
|
7
|
+
export function createStubPartialBrowserCommandContext({ root, testPath }: { root: string; testPath: string }) {
|
|
8
|
+
return stub.build<PartialBrowserCommandContext>({
|
|
9
|
+
project: {
|
|
10
|
+
config: {
|
|
11
|
+
root,
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
provider: {
|
|
15
|
+
options: {},
|
|
16
|
+
},
|
|
17
|
+
testPath,
|
|
18
|
+
})
|
|
19
|
+
}
|