vitest-plugin-vis 1.0.5 → 1.2.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/align_images.d.ts +2 -0
- package/esm/client/align_images.d.ts.map +1 -0
- package/esm/client/align_images.js +10 -0
- package/esm/client/expect/to_match_image_snapshot.d.ts.map +1 -1
- package/esm/client/expect/to_match_image_snapshot.js +2 -15
- package/esm/client/image_data.d.ts +3 -0
- package/esm/client/image_data.d.ts.map +1 -0
- package/esm/client/image_data.js +33 -0
- package/esm/client/image_resizer.d.ts +9 -0
- package/esm/client/image_resizer.d.ts.map +1 -0
- package/esm/client/image_resizer.js +49 -0
- package/esm/client/match_image_snapshot.d.ts +8 -0
- package/esm/client/match_image_snapshot.d.ts.map +1 -0
- package/esm/client/match_image_snapshot.js +65 -0
- package/esm/client/match_image_snapshot.logic.d.ts +3 -0
- package/esm/client/match_image_snapshot.logic.d.ts.map +1 -0
- package/esm/client/match_image_snapshot.logic.js +11 -0
- package/esm/client/task_id.d.ts +3 -0
- package/esm/client/task_id.d.ts.map +1 -1
- package/esm/client/task_id.js +3 -0
- package/esm/client/vitest_browser_context_proxy.d.ts +32 -0
- package/esm/client/vitest_browser_context_proxy.d.ts.map +1 -0
- package/esm/client/vitest_browser_context_proxy.js +11 -0
- package/esm/client.d.ts +1 -1
- package/esm/client.d.ts.map +1 -1
- package/esm/client.js +1 -1
- package/esm/config/vis.d.ts +2 -0
- package/esm/config/vis.d.ts.map +1 -1
- package/esm/presets/auto.d.ts +1 -1
- package/esm/presets/auto.d.ts.map +1 -1
- package/esm/presets/auto.js +0 -1
- package/esm/presets/manual.d.ts +1 -1
- package/esm/presets/manual.d.ts.map +1 -1
- package/esm/presets/manual.js +0 -1
- package/esm/server/browser_provider/playwright.js +1 -1
- package/esm/server/browser_provider/webdriverio.js +1 -1
- package/esm/server/commands/_assertions.d.ts +5 -0
- package/esm/server/commands/_assertions.d.ts.map +1 -0
- package/esm/server/commands/_assertions.js +11 -0
- package/esm/server/commands/extend.d.ts +5 -1
- package/esm/server/commands/extend.d.ts.map +1 -1
- package/esm/server/commands/extend.js +4 -0
- package/esm/server/commands/has_image_snapshot.d.ts.map +1 -1
- package/esm/server/commands/has_image_snapshot.js +2 -3
- package/esm/server/commands/image_snapshot_next_index.d.ts.map +1 -1
- package/esm/server/commands/image_snapshot_next_index.js +2 -3
- package/esm/server/commands/match_image_snapshot.d.ts.map +1 -1
- package/esm/server/commands/match_image_snapshot.js +9 -31
- package/esm/server/commands/prepare_image_snapshot_comparison.d.ts +20 -0
- package/esm/server/commands/prepare_image_snapshot_comparison.d.ts.map +1 -0
- package/esm/server/commands/prepare_image_snapshot_comparison.js +31 -0
- package/esm/server/commands/setup_vis_suite.d.ts.map +1 -1
- package/esm/server/commands/setup_vis_suite.js +2 -0
- package/esm/server/commands/write_image_snapshot.d.ts +12 -0
- package/esm/server/commands/write_image_snapshot.d.ts.map +1 -0
- package/esm/server/commands/write_image_snapshot.js +9 -0
- package/esm/server/snapshot.d.ts +7 -0
- package/esm/server/snapshot.d.ts.map +1 -0
- package/esm/server/snapshot.js +27 -0
- package/esm/server/vis_context.d.ts.map +1 -1
- package/esm/server/vis_context.logic.d.ts +1 -1
- package/esm/server/vis_context.logic.d.ts.map +1 -1
- package/esm/shared/compare_image.d.ts +6 -0
- package/esm/shared/compare_image.d.ts.map +1 -0
- package/esm/shared/compare_image.js +10 -0
- package/esm/shared/convert_threshold_unit.js +1 -1
- package/esm/testing/stubBrowserCommandContext.d.ts +3 -0
- package/esm/testing/stubBrowserCommandContext.d.ts.map +1 -0
- package/esm/testing/stubBrowserCommandContext.js +2 -0
- package/esm/testing.d.ts +1 -0
- package/esm/testing.d.ts.map +1 -1
- package/esm/testing.js +1 -0
- package/package.json +3 -2
- package/readme.md +22 -4
- package/src/client/align_images.ts +11 -0
- package/src/client/expect/to_match_image_snapshot.ts +2 -17
- package/src/client/image_data.ts +36 -0
- package/src/client/image_resizer.ts +59 -0
- package/src/client/match_image_snapshot.logic.ts +13 -0
- package/src/client/match_image_snapshot.ts +87 -0
- package/src/client/task_id.ts +3 -0
- package/src/client/vitest_browser_context_proxy.ts +44 -0
- package/src/client.ts +1 -1
- package/src/presets/auto.ts +0 -1
- package/src/presets/manual.ts +0 -1
- package/src/server/browser_provider/playwright.ts +1 -1
- package/src/server/browser_provider/webdriverio.ts +1 -1
- package/src/server/commands/_assertions.ts +18 -0
- package/src/server/commands/extend.ts +9 -0
- package/src/server/commands/has_image_snapshot.ts +2 -3
- package/src/server/commands/image_snapshot_next_index.ts +2 -3
- package/src/server/commands/match_image_snapshot.ts +17 -46
- package/src/server/commands/prepare_image_snapshot_comparison.ts +54 -0
- package/src/server/commands/setup_vis_suite.ts +3 -0
- package/src/server/commands/write_image_snapshot.ts +26 -0
- package/src/server/snapshot.ts +42 -0
- package/src/server/vis_context.logic.ts +2 -2
- package/src/shared/compare_image.ts +20 -0
- package/src/shared/convert_threshold_unit.ts +1 -1
- package/src/testing/stubBrowserCommandContext.ts +4 -0
- package/src/testing.ts +1 -0
- package/esm/client/expect/to_match_image_snapshot.logic.d.ts +0 -2
- package/esm/client/expect/to_match_image_snapshot.logic.d.ts.map +0 -1
- package/esm/client/expect/to_match_image_snapshot.logic.js +0 -11
- package/esm/server/commands/is_ci.d.ts +0 -2
- package/esm/server/commands/is_ci.d.ts.map +0 -1
- package/esm/server/commands/is_ci.js +0 -6
- package/esm/server/compare_image.d.ts +0 -8
- package/esm/server/compare_image.d.ts.map +0 -1
- package/esm/server/compare_image.js +0 -16
- package/src/client/expect/to_match_image_snapshot.logic.ts +0 -11
- package/src/server/commands/is_ci.ts +0 -7
- package/src/server/compare_image.ts +0 -34
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { BrowserCommand } from 'vitest/node';
|
|
2
|
+
import type { MatchImageSnapshotOptions } from './match_image_snapshot.ts';
|
|
3
|
+
type ImageSnapshotComparisonInfo = {
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
baselinePath: string;
|
|
6
|
+
resultPath: string;
|
|
7
|
+
diffPath: string;
|
|
8
|
+
baseline: string;
|
|
9
|
+
result: string;
|
|
10
|
+
};
|
|
11
|
+
export interface PrepareImageSnapshotComparisonCommand {
|
|
12
|
+
prepareImageSnapshotComparison: (taskId: string | undefined, subject: string, options?: MatchImageSnapshotOptions | undefined) => Promise<ImageSnapshotComparisonInfo | undefined>;
|
|
13
|
+
}
|
|
14
|
+
export declare const prepareImageSnapshotComparison: BrowserCommand<[
|
|
15
|
+
taskId: string,
|
|
16
|
+
snapshotId: string,
|
|
17
|
+
options?: MatchImageSnapshotOptions | undefined
|
|
18
|
+
]>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=prepare_image_snapshot_comparison.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare_image_snapshot_comparison.d.ts","sourceRoot":"","sources":["../../../src/server/commands/prepare_image_snapshot_comparison.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAMjD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAA;AAE1E,KAAK,2BAA2B,GAAG;IAClC,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,EAAE,MAAM,CAAA;IACpB,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,WAAW,qCAAqC;IACrD,8BAA8B,EAAE,CAC/B,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS,KAC3C,OAAO,CAAC,2BAA2B,GAAG,SAAS,CAAC,CAAA;CACrD;AAED,eAAO,MAAM,8BAA8B,EAAE,cAAc,CAC1D;IAAC,MAAM,EAAE,MAAM;IAAE,UAAU,EAAE,MAAM;IAAE,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS;CAAC,CA0BrF,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import ci from 'is-ci';
|
|
2
|
+
import { isBase64String } from "../../shared/base64.js";
|
|
3
|
+
import { file } from "../file.js";
|
|
4
|
+
import { takeSnapshot, takeSnapshotByBrowser, writeSnapshot } from "../snapshot.js";
|
|
5
|
+
import { visContext } from "../vis_context.js";
|
|
6
|
+
import { assertTestPathDefined } from "./_assertions.js";
|
|
7
|
+
export const prepareImageSnapshotComparison = async (context, taskId, subject, options) => {
|
|
8
|
+
assertTestPathDefined(context, 'prepareImageSnapshotComparison');
|
|
9
|
+
// vitest:browser passes in `null` when not defined
|
|
10
|
+
if (!options)
|
|
11
|
+
options = {};
|
|
12
|
+
options.timeout = options.timeout ?? (ci ? 30000 : 5000);
|
|
13
|
+
const info = visContext.getSnapshotInfo(context.testPath, taskId, options);
|
|
14
|
+
const baselineBuffer = await file.tryReadFile(info.baselinePath);
|
|
15
|
+
if (!baselineBuffer) {
|
|
16
|
+
if (isBase64String(subject)) {
|
|
17
|
+
await writeSnapshot(info.baselinePath, subject);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
await takeSnapshotByBrowser(context, info.baselinePath, subject, options);
|
|
21
|
+
}
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const resultBuffer = await takeSnapshot(context, info.resultPath, subject, options);
|
|
25
|
+
return {
|
|
26
|
+
...info,
|
|
27
|
+
projectRoot: context.project.runner.root,
|
|
28
|
+
baseline: baselineBuffer.toString('base64'),
|
|
29
|
+
result: resultBuffer.toString('base64'),
|
|
30
|
+
};
|
|
31
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"setup_vis_suite.d.ts","sourceRoot":"","sources":["../../../src/server/commands/setup_vis_suite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"setup_vis_suite.d.ts","sourceRoot":"","sources":["../../../src/server/commands/setup_vis_suite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,MAAM,WAAW,oBAAoB;IACpC;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAClC;AAED,eAAO,MAAM,aAAa,EAAE,cAAc,CAAC,EAAE,CAI5C,CAAA"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { BrowserCommand } from 'vitest/node';
|
|
2
|
+
export interface WriteImageSnapshotCommand {
|
|
3
|
+
/**
|
|
4
|
+
* Writes the image snapshot to the file system.
|
|
5
|
+
* @param relativeFilePath The file path to write the image snapshot to.
|
|
6
|
+
* It is relative to the project root.
|
|
7
|
+
* @param imageBase64 The base64 encoded image.
|
|
8
|
+
*/
|
|
9
|
+
writeImageSnapshot(relativeFilePath: string, imageBase64: string): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export declare const writeImageSnapshot: BrowserCommand<[relativeFilePath: string, imageBase64: string]>;
|
|
12
|
+
//# sourceMappingURL=write_image_snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"write_image_snapshot.d.ts","sourceRoot":"","sources":["../../../src/server/commands/write_image_snapshot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,MAAM,WAAW,yBAAyB;IACzC;;;;;OAKG;IACH,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAChF;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,CAAC,gBAAgB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAS9F,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { mkdirp } from 'mkdirp';
|
|
2
|
+
import { dirname, isAbsolute } from 'pathe';
|
|
3
|
+
import { file } from "../file.js";
|
|
4
|
+
import { assertIsRelativePath } from "./_assertions.js";
|
|
5
|
+
export const writeImageSnapshot = async (_, relativeFilePath, imageBase64) => {
|
|
6
|
+
assertIsRelativePath(relativeFilePath, 'relativeFilePath');
|
|
7
|
+
await mkdirp(dirname(relativeFilePath));
|
|
8
|
+
await file.writeFile(relativeFilePath, imageBase64, { encoding: 'base64' });
|
|
9
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BrowserCommandContext } from 'vitest/node';
|
|
2
|
+
import type { ImageSnapshotTimeoutOptions } from '../client.ts';
|
|
3
|
+
export declare function takeSnapshot(context: BrowserCommandContext, filePath: string, subject: string, options: ImageSnapshotTimeoutOptions | undefined): Promise<Buffer<ArrayBufferLike>>;
|
|
4
|
+
export declare function takeSnapshotByBrowser(context: BrowserCommandContext, filePath: string, subject: string, options: ImageSnapshotTimeoutOptions | undefined): Promise<Buffer<ArrayBufferLike>>;
|
|
5
|
+
export declare function writeSnapshot(filePath: string, subject: string): Promise<void>;
|
|
6
|
+
export declare function writeSnapshotBuffer(filePath: string, subject: Buffer): Promise<void>;
|
|
7
|
+
//# sourceMappingURL=snapshot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.d.ts","sourceRoot":"","sources":["../../src/server/snapshot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAA;AAK/D,wBAAsB,YAAY,CACjC,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,2BAA2B,GAAG,SAAS,oCAOhD;AAED,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE,qBAAqB,EAC9B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,2BAA2B,GAAG,SAAS,oCAOhD;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAGpE;AACD,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,iBAG1E"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { mkdirp } from 'mkdirp';
|
|
2
|
+
import { dirname } from 'pathe';
|
|
3
|
+
import { isBase64String } from "../shared/base64.js";
|
|
4
|
+
import { browserApi } from "./browser_provider/browser_api.js";
|
|
5
|
+
import { file } from './file.js';
|
|
6
|
+
export async function takeSnapshot(context, filePath, subject, options) {
|
|
7
|
+
if (isBase64String(subject)) {
|
|
8
|
+
await writeSnapshot(filePath, subject);
|
|
9
|
+
return Buffer.from(subject, 'base64');
|
|
10
|
+
}
|
|
11
|
+
return takeSnapshotByBrowser(context, filePath, subject, options);
|
|
12
|
+
}
|
|
13
|
+
export async function takeSnapshotByBrowser(context, filePath, subject, options) {
|
|
14
|
+
await mkdirp(dirname(filePath));
|
|
15
|
+
const browser = browserApi(context);
|
|
16
|
+
return browser.takeScreenshot(filePath, subject ?? 'body', {
|
|
17
|
+
timeout: options?.timeout,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
export async function writeSnapshot(filePath, subject) {
|
|
21
|
+
await mkdirp(dirname(filePath));
|
|
22
|
+
await file.writeFile(filePath, subject, { encoding: 'base64' });
|
|
23
|
+
}
|
|
24
|
+
export async function writeSnapshotBuffer(filePath, subject) {
|
|
25
|
+
await mkdirp(dirname(filePath));
|
|
26
|
+
await file.writeFile(filePath, subject);
|
|
27
|
+
}
|
|
@@ -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;;;;;;;sBAC+hD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;CAD3gD,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vis_context.logic.d.ts","sourceRoot":"","sources":["../../src/server/vis_context.logic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAKpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAEtD,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,MAAM,
|
|
1
|
+
{"version":3,"file":"vis_context.logic.d.ts","sourceRoot":"","sources":["../../src/server/vis_context.logic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAKpD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAEtD,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,MAAM,CAAA;CAChB,CAAA;AAED,wBAAgB,gBAAgB;yBAMV,UAAU;;;;IAY9B;;;;OAIG;wBACuB,4BAA4B;8BAY5B,MAAM,QAAQ,MAAM,YAAY;QAAE,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;;;;;2BAiB1E,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;2BAMlF,MAAM,UAAU,MAAM;;;;;;;;;;EAe9C;AAuBD,wBAAgB,UAAU,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,UAEhF;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU;;;;;;;;EAWjF"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { ImageSnapshotCompareOptions } from './types.ts';
|
|
2
|
+
export declare function compareImage(img1: Uint8ClampedArray | Buffer, img2: Uint8ClampedArray | Buffer, output: Uint8ClampedArray | Buffer, width: number, height: number, { failureThreshold, failureThresholdType, diffOptions }?: ImageSnapshotCompareOptions): {
|
|
3
|
+
pass: boolean;
|
|
4
|
+
diffAmount: number;
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=compare_image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare_image.d.ts","sourceRoot":"","sources":["../../src/shared/compare_image.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAA;AAE7D,wBAAgB,YAAY,CAC3B,IAAI,EAAE,iBAAiB,GAAG,MAAM,EAChC,IAAI,EAAE,iBAAiB,GAAG,MAAM,EAChC,MAAM,EAAE,iBAAiB,GAAG,MAAM,EAClC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,EAAE,gBAAoB,EAAE,oBAA8B,EAAE,WAAW,EAAE,GAAE,2BAAgC;;;EASvG"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import pixelmatch from 'pixelmatch';
|
|
2
|
+
import { convertThresholdUnit } from "./convert_threshold_unit.js";
|
|
3
|
+
export function compareImage(img1, img2, output, width, height, { failureThreshold = 0, failureThresholdType = 'pixel', diffOptions } = {}) {
|
|
4
|
+
const pixelDiff = pixelmatch(img1, img2, output, width, height, diffOptions);
|
|
5
|
+
const diffAmount = convertThresholdUnit({ failureThresholdType, width, height }, pixelDiff);
|
|
6
|
+
return {
|
|
7
|
+
pass: diffAmount <= failureThreshold,
|
|
8
|
+
diffAmount,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -5,6 +5,6 @@ export function convertThresholdUnit({ failureThresholdType, width, height }, pi
|
|
|
5
5
|
case 'percent':
|
|
6
6
|
return (pixelDiff / (width * height)) * 100;
|
|
7
7
|
default:
|
|
8
|
-
throw new Error(`
|
|
8
|
+
throw new Error(`Unsupported failureThresholdType: ${failureThresholdType}`);
|
|
9
9
|
}
|
|
10
10
|
}
|
|
@@ -0,0 +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;AAExD,eAAO,MAAM,yBAAyB,2GAAwC,CAAA"}
|
package/esm/testing.d.ts
CHANGED
package/esm/testing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA"}
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,cAAc,wBAAwB,CAAA;AACtC,cAAc,wCAAwC,CAAA"}
|
package/esm/testing.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest-plugin-vis",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Vitest visual testing plugin",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"image-snapshot",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"dedent": "^1.5.1",
|
|
65
65
|
"is-ci": "^4.0.0",
|
|
66
66
|
"mkdirp": "^3.0.1",
|
|
67
|
-
"pathe": "^
|
|
67
|
+
"pathe": "^2.0.0",
|
|
68
68
|
"pixelmatch": "^6.0.0",
|
|
69
69
|
"pngjs": "^7.0.0",
|
|
70
70
|
"rimraf": "^6.0.1",
|
|
@@ -80,6 +80,7 @@
|
|
|
80
80
|
"@vitest/coverage-v8": "^2.1.8",
|
|
81
81
|
"@vitest/expect": "^2.1.8",
|
|
82
82
|
"cross-env": "^7.0.3",
|
|
83
|
+
"never-fail": "^1.1.4",
|
|
83
84
|
"npm-run-all2": "^7.0.2",
|
|
84
85
|
"playwright": "^1.49.0",
|
|
85
86
|
"react": "^19.0.0",
|
package/readme.md
CHANGED
|
@@ -53,14 +53,14 @@ You can customize the configuration:
|
|
|
53
53
|
```ts
|
|
54
54
|
// vitest.config.ts
|
|
55
55
|
import { defineConfig } from 'vitest/config'
|
|
56
|
-
import { vis } from 'vitest-plugin-vis/config'
|
|
56
|
+
import { vis, trimCommonFolder } from 'vitest-plugin-vis/config'
|
|
57
57
|
|
|
58
58
|
export default defineConfig({
|
|
59
59
|
plugins: [
|
|
60
60
|
vis({
|
|
61
61
|
preset: 'auto',
|
|
62
62
|
snapshotRootDir: '__vis__',
|
|
63
|
-
customizeSnapshotSubpath: (subpath) => subpath,
|
|
63
|
+
customizeSnapshotSubpath: (subpath) => trimCommonFolder(subpath),
|
|
64
64
|
customizeSnapshotId: (id, index) => `${id}-${index}`,
|
|
65
65
|
diffOptions: undefined, // pixelmatch options
|
|
66
66
|
failureThresholdType: 'pixel',
|
|
@@ -103,7 +103,7 @@ vis.presets.auto()
|
|
|
103
103
|
vis.presets.manual()
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
// or capture image snapshot for all
|
|
106
|
+
// or capture image snapshot for all rendering tests
|
|
107
107
|
// for multiple themes (light and dark in this example)
|
|
108
108
|
vis.presets.theme({
|
|
109
109
|
light() { document.body.classList.remove('dark') },
|
|
@@ -115,7 +115,8 @@ vis.presets.theme({
|
|
|
115
115
|
|
|
116
116
|
### Auto Snapshot
|
|
117
117
|
|
|
118
|
-
By default, the plugin will use the `auto` preset,
|
|
118
|
+
By default, the plugin will use the `auto` preset,
|
|
119
|
+
which will take a snapshot at the end of each test.
|
|
119
120
|
|
|
120
121
|
You can control how the auto snapshot is taken using the `setAutoSnapshotOptions` function:
|
|
121
122
|
|
|
@@ -153,6 +154,23 @@ describe('nested scope', () => {
|
|
|
153
154
|
})
|
|
154
155
|
```
|
|
155
156
|
|
|
157
|
+
The options are similar to `expect(...).toMatchImageSnapshot(options)`:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
// enable/disable auto snapshot
|
|
161
|
+
setAutoSnapshotOptions(true /* or false */)
|
|
162
|
+
|
|
163
|
+
// detailed options
|
|
164
|
+
setAutoSnapshotOptions({
|
|
165
|
+
enable: true,
|
|
166
|
+
customizeSnapshotId: (id, index) => `${id}-custom-${index}`,
|
|
167
|
+
diffOptions: { threshold: 0.01 } // pixelmatch options,
|
|
168
|
+
failureThreshold: 0.01,
|
|
169
|
+
failureThresholdType: 'percent',
|
|
170
|
+
timeout: 15000
|
|
171
|
+
})
|
|
172
|
+
```
|
|
173
|
+
|
|
156
174
|
### Manual Snapshot
|
|
157
175
|
|
|
158
176
|
You can also set the preset to `manual` and compare snapshots manually:
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { getMaxSize } from '../shared/get_max_size.ts'
|
|
2
|
+
import { isSameSize } from '../shared/is_same_size.ts'
|
|
3
|
+
import { createImageResizer } from './image_resizer.ts'
|
|
4
|
+
|
|
5
|
+
export function alignImagesToSameSize(image1: ImageData, image2: ImageData): [image1: ImageData, image2: ImageData] {
|
|
6
|
+
if (isSameSize(image1, image2)) return [image1, image2] as const
|
|
7
|
+
|
|
8
|
+
const size = getMaxSize(image1, image2)
|
|
9
|
+
const resize = createImageResizer(size)
|
|
10
|
+
return [resize(image1), resize(image2)] as const
|
|
11
|
+
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
|
-
import { commands } from '@vitest/browser/context'
|
|
2
1
|
import type { AsyncExpectationResult } from '@vitest/expect'
|
|
3
2
|
import { ctx } from '../ctx.ts'
|
|
4
|
-
import {
|
|
3
|
+
import { matchImageSnapshot } from '../match_image_snapshot.ts'
|
|
5
4
|
import { success } from './expectation_result.ts'
|
|
6
|
-
import { parseImageSnapshotSubject } from './to_match_image_snapshot.logic.ts'
|
|
7
5
|
import type { ToMatchImageSnapshotOptions } from './to_match_image_snapshot.types.ts'
|
|
8
6
|
|
|
9
7
|
export function toMatchImageSnapshot(
|
|
@@ -26,18 +24,5 @@ export function toMatchImageSnapshot(
|
|
|
26
24
|
)
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
const taskId = toTaskId(test)
|
|
31
|
-
return toMatchImageSnapshotAsync(taskId, s, options).then(() => success)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
async function toMatchImageSnapshotAsync(taskId: string, subject: string, options?: ToMatchImageSnapshotOptions) {
|
|
35
|
-
if (options?.customizeSnapshotId) {
|
|
36
|
-
const index = await commands.imageSnapshotNextIndex(taskId)
|
|
37
|
-
const { customizeSnapshotId, ...rest } = options
|
|
38
|
-
const snapshotFileId = customizeSnapshotId(taskId, index)
|
|
39
|
-
return commands.matchImageSnapshot(taskId, subject, { ...rest, snapshotFileId })
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
return commands.matchImageSnapshot(taskId, subject, options)
|
|
27
|
+
return matchImageSnapshot(test, subject, options).then(() => success)
|
|
43
28
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export function toImageData(base64: string) {
|
|
2
|
+
return new Promise<ImageData>((resolve, reject) => {
|
|
3
|
+
const img = new Image()
|
|
4
|
+
img.src = `data:image/png;base64,${base64}`
|
|
5
|
+
|
|
6
|
+
img.onload = () => {
|
|
7
|
+
const canvas = document.createElement('canvas')
|
|
8
|
+
canvas.width = img.width
|
|
9
|
+
canvas.height = img.height
|
|
10
|
+
const ctx = canvas.getContext('2d')!
|
|
11
|
+
ctx.drawImage(img, 0, 0)
|
|
12
|
+
resolve(ctx.getImageData(0, 0, canvas.width, canvas.height))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
img.onerror = () => {
|
|
16
|
+
reject(new Error('Failed to load image'))
|
|
17
|
+
}
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function toDataURL(imageData: ImageData) {
|
|
22
|
+
const canvas = document.createElement('canvas')
|
|
23
|
+
canvas.width = imageData.width
|
|
24
|
+
canvas.height = imageData.height
|
|
25
|
+
const ctx = canvas.getContext('2d')!
|
|
26
|
+
ctx.putImageData(imageData, 0, 0)
|
|
27
|
+
return new Promise<string>((resolve) => {
|
|
28
|
+
canvas.toBlob((blob) => {
|
|
29
|
+
const reader = new FileReader()
|
|
30
|
+
reader.onload = () => {
|
|
31
|
+
resolve(reader.result as string)
|
|
32
|
+
}
|
|
33
|
+
reader.readAsDataURL(blob!)
|
|
34
|
+
})
|
|
35
|
+
})
|
|
36
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { isSameSize } from '../shared/is_same_size.ts'
|
|
2
|
+
|
|
3
|
+
export const createImageResizer =
|
|
4
|
+
({ width, height }: { width: number; height: number }) =>
|
|
5
|
+
(image: ImageData) => {
|
|
6
|
+
if (isSameSize(image, { width, height })) return image
|
|
7
|
+
|
|
8
|
+
const inArea = (x: number, y: number) => y <= image.height && x <= image.width
|
|
9
|
+
|
|
10
|
+
const data = new Uint8ClampedArray(width * height * 4)
|
|
11
|
+
const result = new ImageData(data, width, height, { colorSpace: image.colorSpace })
|
|
12
|
+
|
|
13
|
+
for (let y = 0; y < height; y++) {
|
|
14
|
+
for (let x = 0; x < width; x++) {
|
|
15
|
+
const idx = (width * y + x) << 2
|
|
16
|
+
if (inArea(x, y)) {
|
|
17
|
+
const old = (image.width * y + x) << 2
|
|
18
|
+
result.data[idx] = image.data[old]!
|
|
19
|
+
result.data[idx + 1] = image.data[old + 1]!
|
|
20
|
+
result.data[idx + 2] = image.data[old + 2]!
|
|
21
|
+
result.data[idx + 3] = image.data[old + 3]!
|
|
22
|
+
} else {
|
|
23
|
+
result.data[idx] = 0
|
|
24
|
+
result.data[idx + 1] = 0
|
|
25
|
+
result.data[idx + 2] = 0
|
|
26
|
+
result.data[idx + 3] = 64
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return result
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// slower
|
|
35
|
+
export const createImageResizer2 =
|
|
36
|
+
({ width, height }: { width: number; height: number }) =>
|
|
37
|
+
(image: ImageData) => {
|
|
38
|
+
if (isSameSize(image, { width, height })) return image
|
|
39
|
+
|
|
40
|
+
const inArea = (x: number, y: number) => y <= image.height && x <= image.width
|
|
41
|
+
|
|
42
|
+
const data = new Uint8ClampedArray(width * height * 4)
|
|
43
|
+
const result = new ImageData(data, width, height, { colorSpace: image.colorSpace })
|
|
44
|
+
|
|
45
|
+
const copyBytes = image.width * 4
|
|
46
|
+
for (let y = 0; y < height; y++) {
|
|
47
|
+
result.data.set(image.data.slice(y * image.width, copyBytes), y * width * 4)
|
|
48
|
+
for (let x = image.width; x < width; x++) {
|
|
49
|
+
const idx = (width * y + x) << 2
|
|
50
|
+
if (!inArea(x, y)) {
|
|
51
|
+
result.data[idx] = 0
|
|
52
|
+
result.data[idx + 1] = 0
|
|
53
|
+
result.data[idx + 2] = 0
|
|
54
|
+
result.data[idx + 3] = 64
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return result
|
|
59
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ToMatchImageSnapshotOptions } from './expect/to_match_image_snapshot.types.ts'
|
|
2
|
+
|
|
3
|
+
export function prettifyOptions(options: ToMatchImageSnapshotOptions | undefined) {
|
|
4
|
+
if (!options) return 'none'
|
|
5
|
+
|
|
6
|
+
return [
|
|
7
|
+
`failureThreshold: ${options.failureThreshold ?? 0} ${options.failureThresholdType ?? 'pixel'}`,
|
|
8
|
+
options.diffOptions ? `diffOptions: ${JSON.stringify(options.diffOptions)}` : '',
|
|
9
|
+
options.timeout ? `timeout: ${options.timeout} ms` : '',
|
|
10
|
+
]
|
|
11
|
+
.filter(Boolean)
|
|
12
|
+
.join(', ')
|
|
13
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { commands } from '@vitest/browser/context'
|
|
2
|
+
import dedent from 'dedent'
|
|
3
|
+
import { resolve } from 'pathe'
|
|
4
|
+
import type { Task } from 'vitest'
|
|
5
|
+
import { isBase64String } from '../shared/base64.ts'
|
|
6
|
+
import { compareImage } from '../shared/compare_image.ts'
|
|
7
|
+
import { alignImagesToSameSize } from './align_images.ts'
|
|
8
|
+
import type { ToMatchImageSnapshotOptions } from './expect/to_match_image_snapshot.types.ts'
|
|
9
|
+
import { toDataURL, toImageData } from './image_data.ts'
|
|
10
|
+
import { prettifyOptions } from './match_image_snapshot.logic.ts'
|
|
11
|
+
import { convertElementToCssSelector } from './selector.ts'
|
|
12
|
+
import { toTaskId } from './task_id.ts'
|
|
13
|
+
import { server } from './vitest_browser_context_proxy.ts'
|
|
14
|
+
|
|
15
|
+
export async function matchImageSnapshot(test: Task, subject: any, options?: ToMatchImageSnapshotOptions) {
|
|
16
|
+
const taskId = toTaskId(test)
|
|
17
|
+
const info = await commands.prepareImageSnapshotComparison(
|
|
18
|
+
taskId,
|
|
19
|
+
parseImageSnapshotSubject(subject),
|
|
20
|
+
options?.customizeSnapshotId ? await parseImageSnapshotOptions(taskId, options) : options,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if (!info) return
|
|
24
|
+
|
|
25
|
+
const baselineImage = await toImageData(info.baseline)
|
|
26
|
+
const resultImage = await toImageData(info.result)
|
|
27
|
+
const [baselineAlignedImage, resultAlignedImage] = alignImagesToSameSize(baselineImage, resultImage)
|
|
28
|
+
const { width, height } = baselineAlignedImage
|
|
29
|
+
const diffImage = new ImageData(width, height)
|
|
30
|
+
const { pass, diffAmount } = compareImage(
|
|
31
|
+
baselineAlignedImage.data,
|
|
32
|
+
resultAlignedImage.data,
|
|
33
|
+
diffImage.data,
|
|
34
|
+
width,
|
|
35
|
+
height,
|
|
36
|
+
options,
|
|
37
|
+
)
|
|
38
|
+
if (pass) return
|
|
39
|
+
if (server.config.snapshotOptions.updateSnapshot === 'all') {
|
|
40
|
+
await writeSnapshot(info.baselinePath, resultImage)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
await writeSnapshot(info.diffPath, diffImage)
|
|
45
|
+
|
|
46
|
+
throw new Error(
|
|
47
|
+
dedent`Snapshot \`${taskId}\` mismatched
|
|
48
|
+
|
|
49
|
+
${
|
|
50
|
+
options?.failureThreshold
|
|
51
|
+
? options?.failureThresholdType === 'percent'
|
|
52
|
+
? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`
|
|
53
|
+
: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`
|
|
54
|
+
: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Options: ${prettifyOptions(options)}
|
|
58
|
+
|
|
59
|
+
Expected: ${resolve(info.projectRoot, info.baselinePath)}
|
|
60
|
+
Actual: ${resolve(info.projectRoot, info.resultPath)}
|
|
61
|
+
Difference: ${resolve(info.projectRoot, info.diffPath)}`,
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @deprecated internalized. Use `matchImageSnapshot` directly instead.
|
|
67
|
+
*/
|
|
68
|
+
export function parseImageSnapshotSubject(subject: any) {
|
|
69
|
+
if (subject instanceof Element) return convertElementToCssSelector(subject)
|
|
70
|
+
if (subject?.['selector']) return subject['selector']
|
|
71
|
+
if (isBase64String(subject)) return subject
|
|
72
|
+
throw new Error(
|
|
73
|
+
`'toMatchImageSnapshot()' expects the subject to be an element, locator, or image encoded in base64 string, but got: ${subject}`,
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function writeSnapshot(path: string, image: ImageData) {
|
|
78
|
+
const content = (await toDataURL(image)).split(',')[1]!
|
|
79
|
+
return commands.writeImageSnapshot(path, content)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async function parseImageSnapshotOptions(taskId: string, options: ToMatchImageSnapshotOptions) {
|
|
83
|
+
const index = await commands.imageSnapshotNextIndex(taskId)
|
|
84
|
+
const { customizeSnapshotId, ...rest } = options
|
|
85
|
+
const snapshotFileId = customizeSnapshotId!(taskId, index)
|
|
86
|
+
return { ...rest, snapshotFileId }
|
|
87
|
+
}
|
package/src/client/task_id.ts
CHANGED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { BrowserCommands, Platform } from '@vitest/browser/context'
|
|
2
|
+
import type { SerializedConfig } from 'vitest'
|
|
3
|
+
|
|
4
|
+
let ctx: Awaited<typeof import('@vitest/browser/context')>
|
|
5
|
+
|
|
6
|
+
if ((globalThis as any).__vitest_browser__) {
|
|
7
|
+
import('@vitest/browser/context').then((m) => {
|
|
8
|
+
ctx = m
|
|
9
|
+
})
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const server = new Proxy<{
|
|
13
|
+
/**
|
|
14
|
+
* Platform the Vitest server is running on.
|
|
15
|
+
* The same as calling `process.platform` on the server.
|
|
16
|
+
*/
|
|
17
|
+
platform: Platform
|
|
18
|
+
/**
|
|
19
|
+
* Runtime version of the Vitest server.
|
|
20
|
+
* The same as calling `process.version` on the server.
|
|
21
|
+
*/
|
|
22
|
+
version: string
|
|
23
|
+
/**
|
|
24
|
+
* Name of the browser provider.
|
|
25
|
+
*/
|
|
26
|
+
provider: string
|
|
27
|
+
/**
|
|
28
|
+
* Name of the current browser.
|
|
29
|
+
*/
|
|
30
|
+
browser: string
|
|
31
|
+
/**
|
|
32
|
+
* Available commands for the browser.
|
|
33
|
+
* @see {@link https://vitest.dev/guide/browser/commands}
|
|
34
|
+
*/
|
|
35
|
+
commands: BrowserCommands
|
|
36
|
+
/**
|
|
37
|
+
* Serialized test config.
|
|
38
|
+
*/
|
|
39
|
+
config: SerializedConfig
|
|
40
|
+
}>({} as any, {
|
|
41
|
+
get(target, prop) {
|
|
42
|
+
return (target as any)[prop] ?? (ctx?.server as any)[prop]
|
|
43
|
+
},
|
|
44
|
+
})
|
package/src/client.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export * from './client/expect/expectation_result.ts'
|
|
2
|
-
export * from './client/expect/to_match_image_snapshot.logic.ts'
|
|
3
2
|
export type * from './client/expect/to_match_image_snapshot.types.ts'
|
|
3
|
+
export * from './client/match_image_snapshot.ts'
|
|
4
4
|
export * from './client/should_take_snapshot.ts'
|
|
5
5
|
export * from './client/snapshot_options.ts'
|
|
6
6
|
export * from './client/task_id.ts'
|
package/src/presets/auto.ts
CHANGED
package/src/presets/manual.ts
CHANGED
|
@@ -5,7 +5,7 @@ export function playwright(context: BrowserCommandContext): BrowserApi {
|
|
|
5
5
|
return {
|
|
6
6
|
async takeScreenshot(filePath, selector, options) {
|
|
7
7
|
// The `Locator` type from `vitest` has less props than the `Locator` in `playwright`
|
|
8
|
-
const subject = context.iframe.locator(selector
|
|
8
|
+
const subject = context.iframe.locator(selector)
|
|
9
9
|
return subject.screenshot({
|
|
10
10
|
timeout: options?.timeout,
|
|
11
11
|
path: filePath,
|