vitest-plugin-vis 1.1.0 → 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.
Files changed (66) hide show
  1. package/esm/client/match_image_snapshot.d.ts.map +1 -1
  2. package/esm/client/match_image_snapshot.js +4 -2
  3. package/esm/config/vis.d.ts +1 -1
  4. package/esm/presets/auto.d.ts +1 -1
  5. package/esm/presets/auto.d.ts.map +1 -1
  6. package/esm/presets/auto.js +0 -1
  7. package/esm/presets/manual.d.ts +1 -1
  8. package/esm/presets/manual.d.ts.map +1 -1
  9. package/esm/presets/manual.js +0 -1
  10. package/esm/server/commands/_assertions.d.ts +5 -0
  11. package/esm/server/commands/_assertions.d.ts.map +1 -0
  12. package/esm/server/commands/_assertions.js +11 -0
  13. package/esm/server/commands/extend.d.ts +1 -1
  14. package/esm/server/commands/has_image_snapshot.d.ts.map +1 -1
  15. package/esm/server/commands/has_image_snapshot.js +2 -3
  16. package/esm/server/commands/image_snapshot_next_index.d.ts.map +1 -1
  17. package/esm/server/commands/image_snapshot_next_index.js +2 -3
  18. package/esm/server/commands/match_image_snapshot.d.ts.map +1 -1
  19. package/esm/server/commands/match_image_snapshot.js +6 -5
  20. package/esm/server/commands/prepare_image_snapshot_comparison.d.ts.map +1 -1
  21. package/esm/server/commands/prepare_image_snapshot_comparison.js +2 -3
  22. package/esm/server/commands/setup_vis_suite.d.ts.map +1 -1
  23. package/esm/server/commands/setup_vis_suite.js +2 -0
  24. package/esm/server/commands/write_image_snapshot.d.ts +8 -2
  25. package/esm/server/commands/write_image_snapshot.d.ts.map +1 -1
  26. package/esm/server/commands/write_image_snapshot.js +6 -7
  27. package/esm/server/vis_context.d.ts.map +1 -1
  28. package/esm/server/vis_context.logic.d.ts +1 -1
  29. package/esm/server/vis_context.logic.d.ts.map +1 -1
  30. package/esm/shared/compare_image.d.ts +6 -0
  31. package/esm/shared/compare_image.d.ts.map +1 -0
  32. package/esm/shared/compare_image.js +10 -0
  33. package/esm/testing/stubBrowserCommandContext.d.ts +3 -0
  34. package/esm/testing/stubBrowserCommandContext.d.ts.map +1 -0
  35. package/esm/testing/stubBrowserCommandContext.js +2 -0
  36. package/esm/testing.d.ts +1 -0
  37. package/esm/testing.d.ts.map +1 -1
  38. package/esm/testing.js +1 -0
  39. package/package.json +2 -1
  40. package/readme.md +20 -2
  41. package/src/client/match_image_snapshot.ts +11 -2
  42. package/src/presets/auto.ts +0 -1
  43. package/src/presets/manual.ts +0 -1
  44. package/src/server/commands/_assertions.ts +18 -0
  45. package/src/server/commands/has_image_snapshot.ts +2 -3
  46. package/src/server/commands/image_snapshot_next_index.ts +2 -3
  47. package/src/server/commands/match_image_snapshot.ts +13 -6
  48. package/src/server/commands/prepare_image_snapshot_comparison.ts +2 -3
  49. package/src/server/commands/setup_vis_suite.ts +3 -0
  50. package/src/server/commands/write_image_snapshot.ts +17 -11
  51. package/src/server/vis_context.logic.ts +2 -2
  52. package/src/shared/compare_image.ts +20 -0
  53. package/src/testing/stubBrowserCommandContext.ts +4 -0
  54. package/src/testing.ts +1 -0
  55. package/esm/client/compare_image.d.ts +0 -7
  56. package/esm/client/compare_image.d.ts.map +0 -1
  57. package/esm/client/compare_image.js +0 -13
  58. package/esm/server/commands/is_ci.d.ts +0 -2
  59. package/esm/server/commands/is_ci.d.ts.map +0 -1
  60. package/esm/server/commands/is_ci.js +0 -6
  61. package/esm/server/compare_image.d.ts +0 -8
  62. package/esm/server/compare_image.d.ts.map +0 -1
  63. package/esm/server/compare_image.js +0 -16
  64. package/src/client/compare_image.ts +0 -21
  65. package/src/server/commands/is_ci.ts +0 -7
  66. package/src/server/compare_image.ts +0 -34
@@ -1 +1 @@
1
- {"version":3,"file":"match_image_snapshot.d.ts","sourceRoot":"","sources":["../../src/client/match_image_snapshot.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAIlC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAA;AAO5F,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,2BAA2B,iBAuCvG;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,GAAG,OAOrD"}
1
+ {"version":3,"file":"match_image_snapshot.d.ts","sourceRoot":"","sources":["../../src/client/match_image_snapshot.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAIlC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2CAA2C,CAAA;AAO5F,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,2BAA2B,iBAgDvG;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,GAAG,OAOrD"}
@@ -2,8 +2,8 @@ import { commands } from '@vitest/browser/context';
2
2
  import dedent from 'dedent';
3
3
  import { resolve } from 'pathe';
4
4
  import { isBase64String } from "../shared/base64.js";
5
+ import { compareImage } from "../shared/compare_image.js";
5
6
  import { alignImagesToSameSize } from "./align_images.js";
6
- import { compareImage } from "./compare_image.js";
7
7
  import { toDataURL, toImageData } from "./image_data.js";
8
8
  import { prettifyOptions } from "./match_image_snapshot.logic.js";
9
9
  import { convertElementToCssSelector } from "./selector.js";
@@ -17,7 +17,9 @@ export async function matchImageSnapshot(test, subject, options) {
17
17
  const baselineImage = await toImageData(info.baseline);
18
18
  const resultImage = await toImageData(info.result);
19
19
  const [baselineAlignedImage, resultAlignedImage] = alignImagesToSameSize(baselineImage, resultImage);
20
- const { pass, diffAmount, diffImage } = compareImage(baselineAlignedImage, resultAlignedImage, options);
20
+ const { width, height } = baselineAlignedImage;
21
+ const diffImage = new ImageData(width, height);
22
+ const { pass, diffAmount } = compareImage(baselineAlignedImage.data, resultAlignedImage.data, diffImage.data, width, height, options);
21
23
  if (pass)
22
24
  return;
23
25
  if (server.config.snapshotOptions.updateSnapshot === 'all') {
@@ -17,7 +17,7 @@ export declare function vis(options?: VisOptions): {
17
17
  hasImageSnapshot: import("vitest/node").BrowserCommand<[taskId: string, snapshotId?: string | undefined]>;
18
18
  matchImageSnapshot: import("vitest/node").BrowserCommand<[taskId: string, subject: string, options?: import("../commands.ts").MatchImageSnapshotOptions | undefined]>;
19
19
  prepareImageSnapshotComparison: import("vitest/node").BrowserCommand<[taskId: string, snapshotId: string, options?: import("../commands.ts").MatchImageSnapshotOptions | undefined]>;
20
- writeImageSnapshot: import("vitest/node").BrowserCommand<[filePath: string, base64: string]>;
20
+ writeImageSnapshot: import("vitest/node").BrowserCommand<[relativeFilePath: string, imageBase64: string]>;
21
21
  };
22
22
  };
23
23
  setupFiles: string[] | undefined;
@@ -1,2 +1,2 @@
1
- import 'vitest-browser-react';
1
+ export {};
2
2
  //# sourceMappingURL=auto.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../../src/presets/auto.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAA"}
1
+ {"version":3,"file":"auto.d.ts","sourceRoot":"","sources":["../../src/presets/auto.ts"],"names":[],"mappings":""}
@@ -1,3 +1,2 @@
1
- import 'vitest-browser-react';
2
1
  import { vis } from "../setup/vis.js";
3
2
  vis.presets.auto();
@@ -1,2 +1,2 @@
1
- import 'vitest-browser-react';
1
+ export {};
2
2
  //# sourceMappingURL=manual.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"manual.d.ts","sourceRoot":"","sources":["../../src/presets/manual.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAA"}
1
+ {"version":3,"file":"manual.d.ts","sourceRoot":"","sources":["../../src/presets/manual.ts"],"names":[],"mappings":""}
@@ -1,3 +1,2 @@
1
- import 'vitest-browser-react';
2
1
  import { vis } from "../setup/vis.js";
3
2
  vis.presets.manual();
@@ -0,0 +1,5 @@
1
+ import type { RequiredPick } from 'type-plus';
2
+ import type { BrowserCommandContext } from 'vitest/node';
3
+ export declare function assertTestPathDefined(context: BrowserCommandContext, commandName: string): asserts context is RequiredPick<BrowserCommandContext, 'testPath'>;
4
+ export declare function assertIsRelativePath(relativeFilePath: string, propName: string): void;
5
+ //# sourceMappingURL=_assertions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"_assertions.d.ts","sourceRoot":"","sources":["../../../src/server/commands/_assertions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC7C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AAExD,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,qBAAqB,EAC9B,WAAW,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,IAAI,YAAY,CAAC,qBAAqB,EAAE,UAAU,CAAC,CAIpE;AAED,wBAAgB,oBAAoB,CAAC,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAI9E"}
@@ -0,0 +1,11 @@
1
+ import { isAbsolute } from 'pathe';
2
+ export function assertTestPathDefined(context, commandName) {
3
+ if (!context.testPath) {
4
+ throw new Error(`'commands.${commandName}' requires testPath to be defined`);
5
+ }
6
+ }
7
+ export function assertIsRelativePath(relativeFilePath, propName) {
8
+ if (isAbsolute(relativeFilePath)) {
9
+ throw new Error(`'${propName}' must be a relative path`);
10
+ }
11
+ }
@@ -14,6 +14,6 @@ export declare const commands: {
14
14
  hasImageSnapshot: import("vitest/node").BrowserCommand<[taskId: string, snapshotId?: string | undefined]>;
15
15
  matchImageSnapshot: import("vitest/node").BrowserCommand<[taskId: string, subject: string, options?: import("./match_image_snapshot.ts").MatchImageSnapshotOptions | undefined]>;
16
16
  prepareImageSnapshotComparison: import("vitest/node").BrowserCommand<[taskId: string, snapshotId: string, options?: import("./match_image_snapshot.ts").MatchImageSnapshotOptions | undefined]>;
17
- writeImageSnapshot: import("vitest/node").BrowserCommand<[filePath: string, base64: string]>;
17
+ writeImageSnapshot: import("vitest/node").BrowserCommand<[relativeFilePath: string, imageBase64: string]>;
18
18
  };
19
19
  //# sourceMappingURL=extend.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"has_image_snapshot.d.ts","sourceRoot":"","sources":["../../../src/server/commands/has_image_snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD,MAAM,WAAW,uBAAuB;IACvC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACnF;AAED,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,CAU9F,CAAA"}
1
+ {"version":3,"file":"has_image_snapshot.d.ts","sourceRoot":"","sources":["../../../src/server/commands/has_image_snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,MAAM,WAAW,uBAAuB;IACvC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACnF;AAED,eAAO,MAAM,gBAAgB,EAAE,cAAc,CAAC,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC,CAQ9F,CAAA"}
@@ -1,7 +1,6 @@
1
1
  import { visContext } from "../vis_context.js";
2
+ import { assertTestPathDefined } from "./_assertions.js";
2
3
  export const hasImageSnapshot = async (context, taskId, snapshotId) => {
3
- if (!context.testPath) {
4
- throw new Error('Cannot take snapshot without testPath');
5
- }
4
+ assertTestPathDefined(context, 'hasImageSnapshot');
6
5
  return visContext.hasImageSnapshot(context.testPath, taskId, snapshotId);
7
6
  };
@@ -1 +1 @@
1
- {"version":3,"file":"image_snapshot_next_index.d.ts","sourceRoot":"","sources":["../../../src/server/commands/image_snapshot_next_index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAGjD,MAAM,WAAW,6BAA6B;IAC7C;;OAEG;IACH,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACvD;AAED,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAMnE,CAAA"}
1
+ {"version":3,"file":"image_snapshot_next_index.d.ts","sourceRoot":"","sources":["../../../src/server/commands/image_snapshot_next_index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,MAAM,WAAW,6BAA6B;IAC7C;;OAEG;IACH,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;CACvD;AAED,eAAO,MAAM,sBAAsB,EAAE,cAAc,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAInE,CAAA"}
@@ -1,7 +1,6 @@
1
1
  import { visContext } from "../vis_context.js";
2
+ import { assertTestPathDefined } from "./_assertions.js";
2
3
  export const imageSnapshotNextIndex = async (context, taskId) => {
3
- if (!context.testPath) {
4
- throw new Error('Cannot take snapshot without testPath');
5
- }
4
+ assertTestPathDefined(context, 'imageSnapshotNextIndex');
6
5
  return visContext.getTaskCount(context.testPath, taskId);
7
6
  };
@@ -1 +1 @@
1
- {"version":3,"file":"match_image_snapshot.d.ts","sourceRoot":"","sources":["../../../src/server/commands/match_image_snapshot.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD,OAAO,KAAK,EACX,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,MAAM,uBAAuB,CAAA;AAM9B,MAAM,WAAW,yBAAyB;IACzC,kBAAkB,EAAE,CACnB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS,KAC3C,OAAO,CAAC,IAAI,CAAC,CAAA;CAClB;AAED,MAAM,WAAW,yBAChB,SAAQ,2BAA2B,EAClC,sBAAsB,EACtB,2BAA2B;IAC5B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAC9C;IAAC,MAAM,EAAE,MAAM;IAAE,OAAO,EAAE,MAAM;IAAE,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS;CAAC,CAiElF,CAAA"}
1
+ {"version":3,"file":"match_image_snapshot.d.ts","sourceRoot":"","sources":["../../../src/server/commands/match_image_snapshot.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAKjD,OAAO,KAAK,EACX,2BAA2B,EAC3B,sBAAsB,EACtB,2BAA2B,EAC3B,MAAM,uBAAuB,CAAA;AAM9B,MAAM,WAAW,yBAAyB;IACzC,kBAAkB,EAAE,CACnB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS,KAC3C,OAAO,CAAC,IAAI,CAAC,CAAA;CAClB;AAED,MAAM,WAAW,yBAChB,SAAQ,2BAA2B,EAClC,sBAAsB,EACtB,2BAA2B;IAC5B;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;CACnC;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAC9C;IAAC,MAAM,EAAE,MAAM;IAAE,OAAO,EAAE,MAAM;IAAE,OAAO,CAAC,EAAE,yBAAyB,GAAG,SAAS;CAAC,CAuElF,CAAA"}
@@ -3,16 +3,15 @@ import ci from 'is-ci';
3
3
  import { resolve } from 'pathe';
4
4
  import { PNG } from 'pngjs';
5
5
  import { isBase64String } from "../../shared/base64.js";
6
+ import { compareImage } from "../../shared/compare_image.js";
6
7
  import { getMaxSize } from "../../shared/get_max_size.js";
7
8
  import { isSameSize } from "../../shared/is_same_size.js";
8
- import { compareImage } from "../compare_image.js";
9
9
  import { file } from "../file.js";
10
10
  import { takeSnapshot, takeSnapshotByBrowser, writeSnapshot, writeSnapshotBuffer } from "../snapshot.js";
11
11
  import { visContext } from "../vis_context.js";
12
+ import { assertTestPathDefined } from "./_assertions.js";
12
13
  export const matchImageSnapshot = async (context, taskId, subject, options) => {
13
- if (!context.testPath) {
14
- throw new Error('Cannot match snapshot without testPath');
15
- }
14
+ assertTestPathDefined(context, 'matchImageSnapshot');
16
15
  // vitest:browser passes in `null` when not defined
17
16
  if (!options)
18
17
  options = {};
@@ -32,7 +31,9 @@ export const matchImageSnapshot = async (context, taskId, subject, options) => {
32
31
  const baselineImage = PNG.sync.read(baselineBuffer, { skipRescale: true, checkCRC: false });
33
32
  const resultImage = PNG.sync.read(resultBuffer, { skipRescale: true, checkCRC: false });
34
33
  const [baselineAlignedImage, resultAlignedImage] = alignImageSizes(baselineImage, resultImage);
35
- const { pass, diffAmount, diffImage } = compareImage(baselineAlignedImage, resultAlignedImage, options);
34
+ const { width, height } = baselineAlignedImage;
35
+ const diffImage = new PNG({ width, height });
36
+ const { pass, diffAmount } = compareImage(baselineAlignedImage.data, resultAlignedImage.data, diffImage.data, width, height, options);
36
37
  if (pass) {
37
38
  if (sizeNotChanged(baselineImage, baselineAlignedImage) && sizeNotChanged(resultImage, resultAlignedImage)) {
38
39
  return;
@@ -1 +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;AAKjD,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,CA4BrF,CAAA"}
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"}
@@ -3,10 +3,9 @@ import { isBase64String } from "../../shared/base64.js";
3
3
  import { file } from "../file.js";
4
4
  import { takeSnapshot, takeSnapshotByBrowser, writeSnapshot } from "../snapshot.js";
5
5
  import { visContext } from "../vis_context.js";
6
+ import { assertTestPathDefined } from "./_assertions.js";
6
7
  export const prepareImageSnapshotComparison = async (context, taskId, subject, options) => {
7
- if (!context.testPath) {
8
- throw new Error('Cannot match snapshot without testPath');
9
- }
8
+ assertTestPathDefined(context, 'prepareImageSnapshotComparison');
10
9
  // vitest:browser passes in `null` when not defined
11
10
  if (!options)
12
11
  options = {};
@@ -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;AAGjD,MAAM,WAAW,oBAAoB;IACpC;;;;;;OAMG;IACH,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAClC;AAED,eAAO,MAAM,aAAa,EAAE,cAAc,CAAC,EAAE,CAE5C,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"}
@@ -1,4 +1,6 @@
1
1
  import { visContext } from "../vis_context.js";
2
+ import { assertTestPathDefined } from "./_assertions.js";
2
3
  export const setupVisSuite = async (context) => {
4
+ assertTestPathDefined(context, 'setupVisSuite');
3
5
  await visContext.setupSuite(context);
4
6
  };
@@ -1,6 +1,12 @@
1
1
  import type { BrowserCommand } from 'vitest/node';
2
2
  export interface WriteImageSnapshotCommand {
3
- writeImageSnapshot(taskId: string, snapshotId?: string | undefined): Promise<boolean>;
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>;
4
10
  }
5
- export declare const writeImageSnapshot: BrowserCommand<[filePath: string, base64: string]>;
11
+ export declare const writeImageSnapshot: BrowserCommand<[relativeFilePath: string, imageBase64: string]>;
6
12
  //# sourceMappingURL=write_image_snapshot.d.ts.map
@@ -1 +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;AAGjD,MAAM,WAAW,yBAAyB;IACzC,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;CACrF;AAED,eAAO,MAAM,kBAAkB,EAAE,cAAc,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAUjF,CAAA"}
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"}
@@ -1,10 +1,9 @@
1
1
  import { mkdirp } from 'mkdirp';
2
- import { dirname } from 'pathe';
2
+ import { dirname, isAbsolute } from 'pathe';
3
3
  import { file } from "../file.js";
4
- export const writeImageSnapshot = async (context, filePath, base64) => {
5
- if (!context.testPath) {
6
- throw new Error('Cannot take snapshot without testPath');
7
- }
8
- await mkdirp(dirname(filePath));
9
- await file.writeFile(filePath, base64, { encoding: 'base64' });
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' });
10
9
  };
@@ -1 +1 @@
1
- {"version":3,"file":"vis_context.d.ts","sourceRoot":"","sources":["../../src/server/vis_context.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU;;;;;;;sBAC4iD,CAAC;;;;;;;;;;;;;;;;;;;;;;;;CADxhD,CAAA"}
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"}
@@ -11,7 +11,7 @@ export type PartialBrowserCommandContext = {
11
11
  hookTimeout: number;
12
12
  };
13
13
  };
14
- testPath: string | undefined;
14
+ testPath: string;
15
15
  };
16
16
  export declare function createVisContext(): {
17
17
  setOptions(options?: VisOptions): void;
@@ -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,GAAG,SAAS,CAAA;CAC5B,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"}
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
+ }
@@ -0,0 +1,3 @@
1
+ import type { BrowserCommandContext } from 'vitest/node';
2
+ export declare const stubBrowserCommandContext: (stub?: import("type-plus").RecursivePartial<BrowserCommandContext> | undefined) => BrowserCommandContext;
3
+ //# sourceMappingURL=stubBrowserCommandContext.d.ts.map
@@ -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"}
@@ -0,0 +1,2 @@
1
+ import { stub } from 'type-plus';
2
+ export const stubBrowserCommandContext = stub.build({});
package/esm/testing.d.ts CHANGED
@@ -1,2 +1,3 @@
1
1
  export * from './testing/constants.ts';
2
+ export * from './testing/stubBrowserCommandContext.ts';
2
3
  //# sourceMappingURL=testing.d.ts.map
@@ -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
@@ -1 +1,2 @@
1
1
  export * from "./testing/constants.js";
2
+ export * from "./testing/stubBrowserCommandContext.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest-plugin-vis",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Vitest visual testing plugin",
5
5
  "keywords": [
6
6
  "image-snapshot",
@@ -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
@@ -103,7 +103,7 @@ vis.presets.auto()
103
103
  vis.presets.manual()
104
104
 
105
105
 
106
- // or capture image snapshot for all stories with `snapshot` tag,
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, which will take a snapshot at the end of each test.
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:
@@ -3,8 +3,8 @@ import dedent from 'dedent'
3
3
  import { resolve } from 'pathe'
4
4
  import type { Task } from 'vitest'
5
5
  import { isBase64String } from '../shared/base64.ts'
6
+ import { compareImage } from '../shared/compare_image.ts'
6
7
  import { alignImagesToSameSize } from './align_images.ts'
7
- import { compareImage } from './compare_image.ts'
8
8
  import type { ToMatchImageSnapshotOptions } from './expect/to_match_image_snapshot.types.ts'
9
9
  import { toDataURL, toImageData } from './image_data.ts'
10
10
  import { prettifyOptions } from './match_image_snapshot.logic.ts'
@@ -25,7 +25,16 @@ export async function matchImageSnapshot(test: Task, subject: any, options?: ToM
25
25
  const baselineImage = await toImageData(info.baseline)
26
26
  const resultImage = await toImageData(info.result)
27
27
  const [baselineAlignedImage, resultAlignedImage] = alignImagesToSameSize(baselineImage, resultImage)
28
- const { pass, diffAmount, diffImage } = compareImage(baselineAlignedImage, resultAlignedImage, options)
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
+ )
29
38
  if (pass) return
30
39
  if (server.config.snapshotOptions.updateSnapshot === 'all') {
31
40
  await writeSnapshot(info.baselinePath, resultImage)
@@ -1,4 +1,3 @@
1
- import 'vitest-browser-react'
2
1
  import { vis } from '../setup/vis.ts'
3
2
 
4
3
  vis.presets.auto()
@@ -1,4 +1,3 @@
1
- import 'vitest-browser-react'
2
1
  import { vis } from '../setup/vis.ts'
3
2
 
4
3
  vis.presets.manual()
@@ -0,0 +1,18 @@
1
+ import { isAbsolute } from 'pathe'
2
+ import type { RequiredPick } from 'type-plus'
3
+ import type { BrowserCommandContext } from 'vitest/node'
4
+
5
+ export function assertTestPathDefined(
6
+ context: BrowserCommandContext,
7
+ commandName: string,
8
+ ): asserts context is RequiredPick<BrowserCommandContext, 'testPath'> {
9
+ if (!context.testPath) {
10
+ throw new Error(`'commands.${commandName}' requires testPath to be defined`)
11
+ }
12
+ }
13
+
14
+ export function assertIsRelativePath(relativeFilePath: string, propName: string) {
15
+ if (isAbsolute(relativeFilePath)) {
16
+ throw new Error(`'${propName}' must be a relative path`)
17
+ }
18
+ }
@@ -1,5 +1,6 @@
1
1
  import type { BrowserCommand } from 'vitest/node'
2
2
  import { visContext } from '../vis_context.ts'
3
+ import { assertTestPathDefined } from './_assertions.ts'
3
4
 
4
5
  export interface HasImageSnapshotCommand {
5
6
  hasImageSnapshot(taskId: string, snapshotId?: string | undefined): Promise<boolean>
@@ -10,9 +11,7 @@ export const hasImageSnapshot: BrowserCommand<[taskId: string, snapshotId?: stri
10
11
  taskId,
11
12
  snapshotId,
12
13
  ) => {
13
- if (!context.testPath) {
14
- throw new Error('Cannot take snapshot without testPath')
15
- }
14
+ assertTestPathDefined(context, 'hasImageSnapshot')
16
15
 
17
16
  return visContext.hasImageSnapshot(context.testPath, taskId, snapshotId)
18
17
  }
@@ -1,5 +1,6 @@
1
1
  import type { BrowserCommand } from 'vitest/node'
2
2
  import { visContext } from '../vis_context.ts'
3
+ import { assertTestPathDefined } from './_assertions.ts'
3
4
 
4
5
  export interface ImageSnapshotNextIndexCommand {
5
6
  /**
@@ -9,9 +10,7 @@ export interface ImageSnapshotNextIndexCommand {
9
10
  }
10
11
 
11
12
  export const imageSnapshotNextIndex: BrowserCommand<[taskId: string]> = async (context, taskId) => {
12
- if (!context.testPath) {
13
- throw new Error('Cannot take snapshot without testPath')
14
- }
13
+ assertTestPathDefined(context, 'imageSnapshotNextIndex')
15
14
 
16
15
  return visContext.getTaskCount(context.testPath, taskId)
17
16
  }
@@ -4,6 +4,7 @@ import { resolve } from 'pathe'
4
4
  import { PNG } from 'pngjs'
5
5
  import type { BrowserCommand } from 'vitest/node'
6
6
  import { isBase64String } from '../../shared/base64.ts'
7
+ import { compareImage } from '../../shared/compare_image.ts'
7
8
  import { getMaxSize } from '../../shared/get_max_size.ts'
8
9
  import { isSameSize } from '../../shared/is_same_size.ts'
9
10
  import type {
@@ -11,10 +12,10 @@ import type {
11
12
  ImageSnapshotIdOptions,
12
13
  ImageSnapshotTimeoutOptions,
13
14
  } from '../../shared/types.ts'
14
- import { compareImage } from '../compare_image.ts'
15
15
  import { file } from '../file.ts'
16
16
  import { takeSnapshot, takeSnapshotByBrowser, writeSnapshot, writeSnapshotBuffer } from '../snapshot.ts'
17
17
  import { visContext } from '../vis_context.ts'
18
+ import { assertTestPathDefined } from './_assertions.ts'
18
19
 
19
20
  export interface MatchImageSnapshotCommand {
20
21
  matchImageSnapshot: (
@@ -37,9 +38,7 @@ export interface MatchImageSnapshotOptions
37
38
  export const matchImageSnapshot: BrowserCommand<
38
39
  [taskId: string, subject: string, options?: MatchImageSnapshotOptions | undefined]
39
40
  > = async (context, taskId, subject, options) => {
40
- if (!context.testPath) {
41
- throw new Error('Cannot match snapshot without testPath')
42
- }
41
+ assertTestPathDefined(context, 'matchImageSnapshot')
43
42
 
44
43
  // vitest:browser passes in `null` when not defined
45
44
  if (!options) options = {}
@@ -60,8 +59,16 @@ export const matchImageSnapshot: BrowserCommand<
60
59
  const baselineImage = PNG.sync.read(baselineBuffer, { skipRescale: true, checkCRC: false })
61
60
  const resultImage = PNG.sync.read(resultBuffer, { skipRescale: true, checkCRC: false })
62
61
  const [baselineAlignedImage, resultAlignedImage] = alignImageSizes(baselineImage, resultImage)
63
-
64
- const { pass, diffAmount, diffImage } = compareImage(baselineAlignedImage, resultAlignedImage, options)
62
+ const { width, height } = baselineAlignedImage
63
+ const diffImage = new PNG({ width, height })
64
+ const { pass, diffAmount } = compareImage(
65
+ baselineAlignedImage.data,
66
+ resultAlignedImage.data,
67
+ diffImage.data,
68
+ width,
69
+ height,
70
+ options,
71
+ )
65
72
  if (pass) {
66
73
  if (sizeNotChanged(baselineImage, baselineAlignedImage) && sizeNotChanged(resultImage, resultAlignedImage)) {
67
74
  return
@@ -4,6 +4,7 @@ import { isBase64String } from '../../shared/base64.ts'
4
4
  import { file } from '../file.ts'
5
5
  import { takeSnapshot, takeSnapshotByBrowser, writeSnapshot } from '../snapshot.ts'
6
6
  import { visContext } from '../vis_context.ts'
7
+ import { assertTestPathDefined } from './_assertions.ts'
7
8
  import type { MatchImageSnapshotOptions } from './match_image_snapshot.ts'
8
9
 
9
10
  type ImageSnapshotComparisonInfo = {
@@ -26,9 +27,7 @@ export interface PrepareImageSnapshotComparisonCommand {
26
27
  export const prepareImageSnapshotComparison: BrowserCommand<
27
28
  [taskId: string, snapshotId: string, options?: MatchImageSnapshotOptions | undefined]
28
29
  > = async (context, taskId, subject, options) => {
29
- if (!context.testPath) {
30
- throw new Error('Cannot match snapshot without testPath')
31
- }
30
+ assertTestPathDefined(context, 'prepareImageSnapshotComparison')
32
31
 
33
32
  // vitest:browser passes in `null` when not defined
34
33
  if (!options) options = {}
@@ -1,5 +1,6 @@
1
1
  import type { BrowserCommand } from 'vitest/node'
2
2
  import { visContext } from '../vis_context.ts'
3
+ import { assertTestPathDefined } from './_assertions.ts'
3
4
 
4
5
  export interface SetupVisSuiteCommand {
5
6
  /**
@@ -13,5 +14,7 @@ export interface SetupVisSuiteCommand {
13
14
  }
14
15
 
15
16
  export const setupVisSuite: BrowserCommand<[]> = async (context) => {
17
+ assertTestPathDefined(context, 'setupVisSuite')
18
+
16
19
  await visContext.setupSuite(context)
17
20
  }
@@ -1,20 +1,26 @@
1
1
  import { mkdirp } from 'mkdirp'
2
- import { dirname } from 'pathe'
2
+ import { dirname, isAbsolute } from 'pathe'
3
3
  import type { BrowserCommand } from 'vitest/node'
4
4
  import { file } from '../file.ts'
5
+ import { assertIsRelativePath } from './_assertions.ts'
5
6
 
6
7
  export interface WriteImageSnapshotCommand {
7
- writeImageSnapshot(taskId: string, snapshotId?: string | undefined): Promise<boolean>
8
+ /**
9
+ * Writes the image snapshot to the file system.
10
+ * @param relativeFilePath The file path to write the image snapshot to.
11
+ * It is relative to the project root.
12
+ * @param imageBase64 The base64 encoded image.
13
+ */
14
+ writeImageSnapshot(relativeFilePath: string, imageBase64: string): Promise<void>
8
15
  }
9
16
 
10
- export const writeImageSnapshot: BrowserCommand<[filePath: string, base64: string]> = async (
11
- context,
12
- filePath,
13
- base64,
17
+ export const writeImageSnapshot: BrowserCommand<[relativeFilePath: string, imageBase64: string]> = async (
18
+ _,
19
+ relativeFilePath,
20
+ imageBase64,
14
21
  ) => {
15
- if (!context.testPath) {
16
- throw new Error('Cannot take snapshot without testPath')
17
- }
18
- await mkdirp(dirname(filePath))
19
- await file.writeFile(filePath, base64, { encoding: 'base64' })
22
+ assertIsRelativePath(relativeFilePath, 'relativeFilePath')
23
+
24
+ await mkdirp(dirname(relativeFilePath))
25
+ await file.writeFile(relativeFilePath, imageBase64, { encoding: 'base64' })
20
26
  }
@@ -17,7 +17,7 @@ export type PartialBrowserCommandContext = {
17
17
  hookTimeout: number
18
18
  }
19
19
  }
20
- testPath: string | undefined
20
+ testPath: string
21
21
  }
22
22
 
23
23
  export function createVisContext() {
@@ -51,7 +51,7 @@ export function createVisContext() {
51
51
  await globalStateReady
52
52
  }
53
53
 
54
- const { suiteId, suite } = createSuite(state, context.testPath!, visOptions)
54
+ const { suiteId, suite } = createSuite(state, context.testPath, visOptions)
55
55
  state.suites[suiteId] = suite
56
56
  await Promise.allSettled([ctx.rimraf(suite.diffDir), ctx.rimraf(suite.resultDir)])
57
57
  },
@@ -0,0 +1,20 @@
1
+ import pixelmatch from 'pixelmatch'
2
+ import { convertThresholdUnit } from './convert_threshold_unit.ts'
3
+ import type { ImageSnapshotCompareOptions } from './types.ts'
4
+
5
+ export function compareImage(
6
+ img1: Uint8ClampedArray | Buffer,
7
+ img2: Uint8ClampedArray | Buffer,
8
+ output: Uint8ClampedArray | Buffer,
9
+ width: number,
10
+ height: number,
11
+ { failureThreshold = 0, failureThresholdType = 'pixel', diffOptions }: ImageSnapshotCompareOptions = {},
12
+ ) {
13
+ const pixelDiff = pixelmatch(img1, img2, output, width, height, diffOptions)
14
+ const diffAmount = convertThresholdUnit({ failureThresholdType, width, height }, pixelDiff)
15
+
16
+ return {
17
+ pass: diffAmount <= failureThreshold,
18
+ diffAmount,
19
+ }
20
+ }
@@ -0,0 +1,4 @@
1
+ import { stub } from 'type-plus'
2
+ import type { BrowserCommandContext } from 'vitest/node'
3
+
4
+ export const stubBrowserCommandContext = stub.build<BrowserCommandContext>({})
package/src/testing.ts CHANGED
@@ -1 +1,2 @@
1
1
  export * from './testing/constants.ts'
2
+ export * from './testing/stubBrowserCommandContext.ts'
@@ -1,7 +0,0 @@
1
- import type { ImageSnapshotCompareOptions } from '../shared/types.ts';
2
- export declare function compareImage(baselineImage: ImageData, resultImage: ImageData, { failureThreshold, failureThresholdType, diffOptions }?: ImageSnapshotCompareOptions): {
3
- pass: boolean;
4
- diffAmount: number;
5
- diffImage: ImageData;
6
- };
7
- //# sourceMappingURL=compare_image.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"compare_image.d.ts","sourceRoot":"","sources":["../../src/client/compare_image.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAA;AAErE,wBAAgB,YAAY,CAC3B,aAAa,EAAE,SAAS,EACxB,WAAW,EAAE,SAAS,EACtB,EAAE,gBAAoB,EAAE,oBAA8B,EAAE,WAAW,EAAE,GAAE,2BAAgC;;;;EAavG"}
@@ -1,13 +0,0 @@
1
- import pixelmatch from 'pixelmatch';
2
- import { convertThresholdUnit } from "../shared/convert_threshold_unit.js";
3
- export function compareImage(baselineImage, resultImage, { failureThreshold = 0, failureThresholdType = 'pixel', diffOptions } = {}) {
4
- const { width, height } = resultImage;
5
- const diffImage = new ImageData(width, height);
6
- const pixelDiff = pixelmatch(resultImage.data, baselineImage.data, diffImage.data, width, height, diffOptions);
7
- const diffAmount = convertThresholdUnit({ failureThresholdType, width, height }, pixelDiff);
8
- return {
9
- pass: diffAmount <= failureThreshold,
10
- diffAmount,
11
- diffImage,
12
- };
13
- }
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=is_ci.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"is_ci.d.ts","sourceRoot":"","sources":["../../../src/server/commands/is_ci.ts"],"names":[],"mappings":""}
@@ -1,6 +0,0 @@
1
- // import ci from 'is-ci'
2
- export {};
3
- // export interface IsCICommand {
4
- // isCI: () => Promise<boolean>
5
- // }
6
- // export const isCI = async () => ci
@@ -1,8 +0,0 @@
1
- import { PNG } from 'pngjs';
2
- import type { ImageSnapshotCompareOptions } from '../shared/types.ts';
3
- export declare function compareImage(baselineImage: PNG, resultImage: PNG, { failureThreshold, failureThresholdType, diffOptions }?: ImageSnapshotCompareOptions): {
4
- pass: boolean;
5
- diffAmount: number;
6
- diffImage: PNG;
7
- };
8
- //# sourceMappingURL=compare_image.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"compare_image.d.ts","sourceRoot":"","sources":["../../src/server/compare_image.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAE3B,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,oBAAoB,CAAA;AAErE,wBAAgB,YAAY,CAC3B,aAAa,EAAE,GAAG,EAClB,WAAW,EAAE,GAAG,EAChB,EAAE,gBAAoB,EAAE,oBAA8B,EAAE,WAAW,EAAE,GAAE,2BAAgC;;;;EAyBvG"}
@@ -1,16 +0,0 @@
1
- import pixelmatch from 'pixelmatch';
2
- import { PNG } from 'pngjs';
3
- import { convertThresholdUnit } from "../shared/convert_threshold_unit.js";
4
- export function compareImage(baselineImage, resultImage, { failureThreshold = 0, failureThresholdType = 'pixel', diffOptions } = {}) {
5
- const diffImage = new PNG({
6
- width: baselineImage.width,
7
- height: baselineImage.height,
8
- });
9
- const pixelDiff = pixelmatch(resultImage.data, baselineImage.data, diffImage.data, baselineImage.width, baselineImage.height, diffOptions);
10
- const diffAmount = convertThresholdUnit({ failureThresholdType, width: baselineImage.width, height: baselineImage.height }, pixelDiff);
11
- return {
12
- pass: diffAmount <= failureThreshold,
13
- diffAmount,
14
- diffImage,
15
- };
16
- }
@@ -1,21 +0,0 @@
1
- import pixelmatch from 'pixelmatch'
2
- import { convertThresholdUnit } from '../shared/convert_threshold_unit.ts'
3
- import type { ImageSnapshotCompareOptions } from '../shared/types.ts'
4
-
5
- export function compareImage(
6
- baselineImage: ImageData,
7
- resultImage: ImageData,
8
- { failureThreshold = 0, failureThresholdType = 'pixel', diffOptions }: ImageSnapshotCompareOptions = {},
9
- ) {
10
- const { width, height } = resultImage
11
- const diffImage = new ImageData(width, height)
12
-
13
- const pixelDiff = pixelmatch(resultImage.data, baselineImage.data, diffImage.data, width, height, diffOptions)
14
- const diffAmount = convertThresholdUnit({ failureThresholdType, width, height }, pixelDiff)
15
-
16
- return {
17
- pass: diffAmount <= failureThreshold,
18
- diffAmount,
19
- diffImage,
20
- }
21
- }
@@ -1,7 +0,0 @@
1
- // import ci from 'is-ci'
2
-
3
- // export interface IsCICommand {
4
- // isCI: () => Promise<boolean>
5
- // }
6
-
7
- // export const isCI = async () => ci
@@ -1,34 +0,0 @@
1
- import pixelmatch from 'pixelmatch'
2
- import { PNG } from 'pngjs'
3
- import { convertThresholdUnit } from '../shared/convert_threshold_unit.ts'
4
- import type { ImageSnapshotCompareOptions } from '../shared/types.ts'
5
-
6
- export function compareImage(
7
- baselineImage: PNG,
8
- resultImage: PNG,
9
- { failureThreshold = 0, failureThresholdType = 'pixel', diffOptions }: ImageSnapshotCompareOptions = {},
10
- ) {
11
- const diffImage = new PNG({
12
- width: baselineImage.width,
13
- height: baselineImage.height,
14
- })
15
-
16
- const pixelDiff = pixelmatch(
17
- resultImage.data,
18
- baselineImage.data,
19
- diffImage.data,
20
- baselineImage.width,
21
- baselineImage.height,
22
- diffOptions,
23
- )
24
- const diffAmount = convertThresholdUnit(
25
- { failureThresholdType, width: baselineImage.width, height: baselineImage.height },
26
- pixelDiff,
27
- )
28
-
29
- return {
30
- pass: diffAmount <= failureThreshold,
31
- diffAmount,
32
- diffImage,
33
- }
34
- }