storybook-addon-vis 0.12.0 → 0.13.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.
@@ -1,4 +1,4 @@
1
- import { toMatchImageSnapshot, state } from './chunk-JQOEDY6R.js';
1
+ import { toMatchImageSnapshot, state } from './chunk-T3RUFMT2.js';
2
2
  import { expect } from '@storybook/test';
3
3
 
4
4
  expect.extend({ toMatchImageSnapshot });
@@ -12,5 +12,5 @@ function defineVisPreview() {
12
12
  }
13
13
 
14
14
  export { defineVisPreview, storybookPreviewVis };
15
- //# sourceMappingURL=chunk-6WYK3DFU.js.map
16
- //# sourceMappingURL=chunk-6WYK3DFU.js.map
15
+ //# sourceMappingURL=chunk-7MABS6N3.js.map
16
+ //# sourceMappingURL=chunk-7MABS6N3.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/storybook/preview.ts"],"names":[],"mappings":";;;AAKA,MAAO,CAAA,MAAA,CAAO,EAAE,oBAAA,EAAsB,CAAA;AAE/B,IAAM,sBAAsB,gBAAiB;AAE7C,SAAS,gBAAuC,GAAA;AACtD,EAAO,OAAA;AAAA,IACN,WAAW,GAAmB,EAAA;AAC7B,MAAO,OAAA,KAAA,CAAM,WAAW,GAAG,CAAA;AAAA;AAC5B,GACD;AACD","file":"chunk-6WYK3DFU.js","sourcesContent":["import { expect } from '@storybook/test'\nimport type { ProjectAnnotations, Renderer, StoryContext } from 'storybook/internal/types'\nimport { toMatchImageSnapshot } from '../expect.to_match_image_snapshot'\nimport { state } from '../state'\n\nexpect.extend({ toMatchImageSnapshot })\n\nexport const storybookPreviewVis = defineVisPreview()\n\nexport function defineVisPreview<R extends Renderer>() {\n\treturn {\n\t\tbeforeEach(ctx: StoryContext) {\n\t\t\treturn state.setupStory(ctx)\n\t\t},\n\t} satisfies ProjectAnnotations<R>\n}\n"]}
1
+ {"version":3,"sources":["../src/storybook/preview.ts"],"names":[],"mappings":";;;AAKA,MAAO,CAAA,MAAA,CAAO,EAAE,oBAAA,EAAsB,CAAA;AAE/B,IAAM,sBAAsB,gBAAiB;AAE7C,SAAS,gBAAuC,GAAA;AACtD,EAAO,OAAA;AAAA,IACN,WAAW,GAAmB,EAAA;AAC7B,MAAO,OAAA,KAAA,CAAM,WAAW,GAAG,CAAA;AAAA;AAC5B,GACD;AACD","file":"chunk-7MABS6N3.js","sourcesContent":["import { expect } from '@storybook/test'\nimport type { ProjectAnnotations, Renderer, StoryContext } from 'storybook/internal/types'\nimport { toMatchImageSnapshot } from '../expect.to_match_image_snapshot'\nimport { state } from '../state'\n\nexpect.extend({ toMatchImageSnapshot })\n\nexport const storybookPreviewVis = defineVisPreview()\n\nexport function defineVisPreview<R extends Renderer>() {\n\treturn {\n\t\tbeforeEach(ctx: StoryContext) {\n\t\t\treturn state.setupStory(ctx)\n\t\t},\n\t} satisfies ProjectAnnotations<R>\n}\n"]}
@@ -32,7 +32,7 @@ function createStore() {
32
32
  let resultDir;
33
33
  let diffDir;
34
34
  let snapshot;
35
- let suiteOptions = {};
35
+ let suiteOptions = /* @__PURE__ */ Object.create(null);
36
36
  let tags;
37
37
  let parameters;
38
38
  let taskName;
@@ -45,17 +45,17 @@ function createStore() {
45
45
  async setupSuite(suite, options = {}) {
46
46
  testFilepath = suite.file.filepath;
47
47
  const projectDir = testFilepath.slice(0, -suite.name.length);
48
- const snapshotPath = resolveSnapshotPath(await getPlatform(), options);
48
+ const snapshotPath = resolveSnapshotPath(options);
49
49
  const snapshotFullPath = join(projectDir, snapshotPath);
50
50
  currentDir = dirname(testFilepath);
51
51
  const suiteDir = trimSuiteDir(suite.name, options);
52
- baselineDir = relative(currentDir, join(snapshotFullPath, suiteDir));
52
+ baselineDir = relative(currentDir, join(snapshotFullPath, await getPlatform(), suiteDir));
53
53
  resultDir = relative(currentDir, join(snapshotFullPath, "__results__", suiteDir));
54
54
  diffDir = relative(currentDir, join(snapshotFullPath, "__diff_output__", suiteDir));
55
55
  suiteOptions = options;
56
56
  snapshot = snapshot ?? {};
57
57
  if (!snapshot[testFilepath]) {
58
- snapshot[testFilepath] = {};
58
+ snapshot[testFilepath] = /* @__PURE__ */ Object.create(null);
59
59
  await commands.rmDir(resultDir);
60
60
  await commands.rmDir(diffDir);
61
61
  }
@@ -100,8 +100,8 @@ function createStore() {
100
100
  return state2;
101
101
  }
102
102
  var state = createStore();
103
- function resolveSnapshotPath(platform, options) {
104
- return options.snapshotRootDir ?? `__vis__/${platform}`;
103
+ function resolveSnapshotPath(options) {
104
+ return options.snapshotRootDir ?? "__vis__";
105
105
  }
106
106
  function trimSuiteDir(suiteName, options) {
107
107
  const customizeSnapshotSubpath = options.customizeSnapshotSubpath ?? defaultCustomizeSnapshotSubpath;
@@ -368,5 +368,5 @@ function toThresholdUnit({ failureThresholdType, width, height }, pixelDiff) {
368
368
  }
369
369
 
370
370
  export { cdp, commands, page, server, state, toMatchImageSnapshot };
371
- //# sourceMappingURL=chunk-JQOEDY6R.js.map
372
- //# sourceMappingURL=chunk-JQOEDY6R.js.map
371
+ //# sourceMappingURL=chunk-T3RUFMT2.js.map
372
+ //# sourceMappingURL=chunk-T3RUFMT2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/@vitest/browser/image_snapshot.logic.ts","../src/state.ts","../src/@vitest/browser/page.has_image_snapshot.ts","../src/image_data.ts","../src/@vitest/browser/page.image_snapshot.ts","../src/@vitest/browser/context.ts","../src/image_resizer.ts","../src/expect.to_match_image_snapshot.ts"],"names":["state","ctx","id","index","resolve","getCurrentTest"],"mappings":"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","file":"chunk-T3RUFMT2.js","sourcesContent":["export const imageSnapshotSymbol = Symbol.for('storybook-addon-vis/imageSnapshot')\n\nexport const imageSnapshotStubSymbol = Symbol.for('storybook-addon-vis/imageSnapshotStub')\n","import { imageSnapshotSymbol } from './constants.js'\nimport type { ImageSnapshot } from './types.js'\n\nexport function isImageSnapshot(subject: any): subject is ImageSnapshot {\n\treturn !!subject && subject.type === imageSnapshotSymbol\n}\n\nexport function assertImageSnapshot(subject: any): asserts subject is ImageSnapshot {\n\tif (!isImageSnapshot(subject)) {\n\t\tthrow new Error('Expected subject to be an image snapshot')\n\t}\n}\n\nexport function toSnapshotId(taskName: string) {\n\treturn `${taskName.replace(/[^a-z0-9]/gi, '-').toLowerCase()}`\n}\n","import { dirname, join, relative } from 'pathe'\nimport type { StoryContext } from 'storybook/internal/types'\nimport { omit, required } from 'type-plus'\nimport { getCurrentTest } from 'vitest/suite'\nimport { commands } from './@vitest/browser/context.js'\nimport { toSnapshotId } from './@vitest/browser/image_snapshot.logic'\nimport type { ImageSnapshotOptions } from './@vitest/browser/types'\nimport type { MatchImageSnapshotOptions } from './expect.to_match_image_snapshot'\nimport type { VisOptions } from './types.js'\n\nfunction createStore() {\n\t// test suite (runner.beforeAll) states\n\tlet testFilepath: string\n\tlet currentDir: string\n\tlet baselineDir: string\n\tlet resultDir: string\n\tlet diffDir: string\n\tlet snapshot: Record<string, Record<string, { index: number }>>\n\tlet suiteOptions: VisOptions = Object.create(null)\n\n\t// story states (story.beforeEach)\n\tlet tags: string[] | undefined\n\tlet parameters: Record<string, any> | undefined\n\tlet taskName: string\n\tlet id: string\n\n\tconst state = {\n\t\tbaselineDir,\n\t\tresultDir,\n\t\tdiffDir,\n\t\tsnapshot: {},\n\t\tasync setupSuite(suite: { file: { filepath: string }; name: string }, options: VisOptions = {}) {\n\t\t\t// console.debug('setupSuite', suite.name)\n\t\t\ttestFilepath = suite.file.filepath\n\t\t\tconst projectDir = testFilepath.slice(0, -suite.name.length)\n\n\t\t\tconst snapshotPath = resolveSnapshotPath(options)\n\t\t\tconst snapshotFullPath = join(projectDir, snapshotPath)\n\t\t\tcurrentDir = dirname(testFilepath)\n\t\t\tconst suiteDir = trimSuiteDir(suite.name, options)\n\t\t\tbaselineDir = relative(currentDir, join(snapshotFullPath, await getPlatform(), suiteDir))\n\t\t\tresultDir = relative(currentDir, join(snapshotFullPath, '__results__', suiteDir))\n\t\t\tdiffDir = relative(currentDir, join(snapshotFullPath, '__diff_output__', suiteDir))\n\n\t\t\tsuiteOptions = options\n\t\t\tsnapshot = snapshot ?? {}\n\t\t\tif (!snapshot[testFilepath]) {\n\t\t\t\tsnapshot[testFilepath] = Object.create(null)\n\t\t\t\tawait commands.rmDir(resultDir)\n\t\t\t\tawait commands.rmDir(diffDir)\n\t\t\t}\n\t\t},\n\t\tsetupStory(ctx: StoryContext) {\n\t\t\t// console.debug('setupStory', ctx.name)\n\t\t\ttags = ctx.tags\n\t\t\tparameters = ctx.parameters\n\t\t},\n\t\tshouldTakeSnapshot() {\n\t\t\t// console.debug('shouldTakeSnapshot', taskName, tags)\n\t\t\tif (!tags) return false\n\t\t\treturn tags.lastIndexOf('!snapshot') < tags.lastIndexOf('snapshot')\n\t\t},\n\t\tgetName() {\n\t\t\treturn taskName\n\t\t},\n\t\tgetCurrentDir() {\n\t\t\treturn currentDir\n\t\t},\n\t\tgetTimeout(timeout?: number | undefined) {\n\t\t\treturn timeout ?? suiteOptions.timeout ?? 30000\n\t\t},\n\t\tmergeMatchImageSnapshotOptions(options?: MatchImageSnapshotOptions) {\n\t\t\treturn required(omit(suiteOptions, 'snapshotRootDir', 'customizeSnapshotSubpath'), parameters?.snapshot, options)\n\t\t},\n\t\tgetSnapshotFilePaths(options?: ImageSnapshotOptions | undefined) {\n\t\t\tconst test = getCurrentTest()\n\t\t\ttaskName = test.name\n\t\t\tid = toSnapshotId(taskName)\n\t\t\tsnapshot[testFilepath][id] = snapshot[testFilepath][id] ?? { index: 1 }\n\n\t\t\tconst index = snapshot[testFilepath][id]!.index\n\t\t\tconst customizeSnapshotId =\n\t\t\t\tsuiteOptions.customizeSnapshotId ?? options?.customizeSnapshotId ?? ((id, index) => `${id}-${index}`)\n\t\t\tconst snapshotFilename = `${customizeSnapshotId(id, index)}.png`\n\t\t\tconst baselinePath = join(baselineDir, snapshotFilename)\n\t\t\tconst resultPath = join(resultDir, snapshotFilename)\n\t\t\tconst diffPath = join(diffDir, snapshotFilename)\n\t\t\treturn { snapshotFilename, baselinePath, resultPath, diffPath }\n\t\t},\n\t\tincrementSnapshotIndex() {\n\t\t\tsnapshot[testFilepath][id]!.index++\n\t\t},\n\t}\n\treturn state\n}\n\nexport const state = createStore()\n\nfunction resolveSnapshotPath(options: VisOptions) {\n\treturn options.snapshotRootDir ?? '__vis__'\n}\n\nfunction trimSuiteDir(suiteName: string, options: VisOptions) {\n\tconst customizeSnapshotSubpath = options.customizeSnapshotSubpath ?? defaultCustomizeSnapshotSubpath\n\treturn customizeSnapshotSubpath(suiteName)\n}\n\nfunction defaultCustomizeSnapshotSubpath(suiteName: string) {\n\tconst [suiteDir] = suiteName.split('/', 1)\n\tif (['tests', 'test', 'src', 'source', 'js', 'ts', 'lib'].includes(suiteDir))\n\t\treturn suiteName.slice(suiteDir.length + 1)\n\treturn suiteName\n}\n\nlet platformP: Promise<string>\nfunction getPlatform() {\n\tif (platformP) return platformP\n\n\treturn (platformP = commands.getSnapshotPlatform())\n}\n","import type { BrowserPage } from '@vitest/browser/context'\nimport { join } from 'pathe'\nimport { state } from '../../state'\nimport { commands } from './context'\nimport type { CustomizeSnapshotIdOptions } from './types'\n\nexport interface HasImageSnapshotAction {\n\thasImageSnapshot(this: BrowserPage, options?: CustomizeSnapshotIdOptions | undefined): Promise<boolean>\n}\n\n/**\n * Check if the snapshot image exists.\n */\nexport async function hasImageSnapshot(this: BrowserPage, options?: CustomizeSnapshotIdOptions | undefined) {\n\tconst { baselinePath } = state.getSnapshotFilePaths(options)\n\treturn commands.existFile(baselinePath)\n}\n","export function toImageData(base64: string) {\n\treturn new Promise<ImageData>((resolve, reject) => {\n\t\tconst img = new Image()\n\t\timg.src = `data:image/png;base64,${base64}`\n\n\t\timg.onload = () => {\n\t\t\tconst canvas = document.createElement('canvas')\n\t\t\tcanvas.width = img.width\n\t\t\tcanvas.height = img.height\n\t\t\tconst ctx = canvas.getContext('2d')!\n\t\t\tctx.drawImage(img, 0, 0)\n\t\t\tresolve(ctx.getImageData(0, 0, canvas.width, canvas.height))\n\t\t}\n\n\t\timg.onerror = () => {\n\t\t\treject(new Error('Failed to load image'))\n\t\t}\n\t})\n}\n\nexport async function toDataURL(imageData: ImageData) {\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = imageData.width\n\tcanvas.height = imageData.height\n\tconst ctx = canvas.getContext('2d')!\n\tctx.putImageData(imageData, 0, 0)\n\treturn new Promise<string>((resolve) => {\n\t\tcanvas.toBlob((blob) => {\n\t\t\tconst reader = new FileReader()\n\t\t\treader.onload = () => {\n\t\t\t\tresolve(reader.result as string)\n\t\t\t}\n\t\t\treader.readAsDataURL(blob!)\n\t\t})\n\t})\n}\n","import type { BrowserPage } from '@vitest/browser/context'\nimport { toImageData } from '../../image_data.js'\nimport { state } from '../../state.js'\nimport { imageSnapshotSymbol } from './constants.js'\nimport type { ImageSnapshot, ImageSnapshotOptions } from './types.js'\n\nexport interface ImageSnapshotAction {\n\timageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions | undefined): Promise<ImageSnapshot>\n}\n\nexport async function imageSnapshot(\n\tthis: BrowserPage,\n\toptions?: ImageSnapshotOptions | undefined,\n): Promise<ImageSnapshot> {\n\tconst { snapshotFilename, baselinePath, resultPath, diffPath } = state.getSnapshotFilePaths(options)\n\t// console.debug('taking snapshot', state.getName(), snapshotFilename)\n\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t\ttimeout: state.getTimeout(options?.timeout),\n\t})\n\tstate.incrementSnapshotIndex()\n\tconst image = await toImageData(screenshot.base64)\n\n\treturn {\n\t\ttype: imageSnapshotSymbol,\n\t\tsnapshotFilename,\n\t\tbaselinePath,\n\t\tresultPath,\n\t\tdiffPath,\n\t\tbase64: screenshot.base64,\n\t\timage,\n\t}\n}\n","import {\n\tgetByAltText,\n\tgetByLabelText,\n\tgetByPlaceholderText,\n\tgetByRole,\n\tgetByTestId,\n\tgetByText,\n\tgetByTitle,\n} from '@storybook/test'\nimport type { BrowserCommands, BrowserPage, CDPSession, Platform } from '@vitest/browser/context'\nimport type { SerializedConfig } from 'vitest'\nimport { imageSnapshotStubSymbol } from './constants'\nimport { hasImageSnapshot } from './page.has_image_snapshot'\nimport { imageSnapshot } from './page.image_snapshot'\n\nlet ctx: Awaited<typeof import('@vitest/browser/context')>\n\nif ((globalThis as any).__vitest_browser__) {\n\timport('@vitest/browser/context').then((m) => {\n\t\tctx = m\n\t\tpage.extend({ imageSnapshot, hasImageSnapshot })\n\t})\n}\n\n// TODO: stub the functions within the `CDPSession`\n// when we better understands the use cases\nexport function cdp(): CDPSession {\n\treturn ctx?.cdp() ?? ({} as any)\n}\n\nexport const commands = new Proxy<BrowserCommands>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.commands as any)[prop]\n\t},\n})\n\nexport const page = new Proxy<BrowserPage>(\n\t{\n\t\tgetByAltText,\n\t\tgetByRole,\n\t\tgetByTestId,\n\t\tgetByText,\n\t\tgetByTitle,\n\t\tgetByPlaceholder: getByPlaceholderText,\n\t\tgetByLabelText,\n\t} as any,\n\t{\n\t\tget(target, prop) {\n\t\t\treturn ((target as any)[prop] ?? ctx?.page)\n\t\t\t\t? (ctx?.page as any)[prop]\n\t\t\t\t: () => {\n\t\t\t\t\t\tif (prop === 'imageSnapshot') {\n\t\t\t\t\t\t\treturn { type: imageSnapshotStubSymbol }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (prop === 'hasImageSnapshot') {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.info(`\\`page.${prop.toString()}\\` does not exist when running in browser`)\n\t\t\t\t\t}\n\t\t},\n\t},\n)\n\nexport const server = new Proxy<{\n\t/**\n\t * Platform the Vitest server is running on.\n\t * The same as calling `process.platform` on the server.\n\t */\n\tplatform: Platform\n\t/**\n\t * Runtime version of the Vitest server.\n\t * The same as calling `process.version` on the server.\n\t */\n\tversion: string\n\t/**\n\t * Name of the browser provider.\n\t */\n\tprovider: string\n\t/**\n\t * Name of the current browser.\n\t */\n\tbrowser: string\n\t/**\n\t * Available commands for the browser.\n\t * @see {@link https://vitest.dev/guide/browser/commands}\n\t */\n\tcommands: BrowserCommands\n\t/**\n\t * Serialized test config.\n\t */\n\tconfig: SerializedConfig\n}>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.server as any)[prop]\n\t},\n})\n","export const createImageResizer =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tfor (let x = 0; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (inArea(x, y)) {\n\t\t\t\t\tconst old = (image.width * y + x) << 2\n\t\t\t\t\tresult.data[idx] = image.data[old]\n\t\t\t\t\tresult.data[idx + 1] = image.data[old + 1]\n\t\t\t\t\tresult.data[idx + 2] = image.data[old + 2]\n\t\t\t\t\tresult.data[idx + 3] = image.data[old + 3]\n\t\t\t\t} else {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n// slower\nexport const createImageResizer2 =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tconst copyBytes = image.width * 4\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tresult.data.set(image.data.slice(y * image.width, copyBytes), y * width * 4)\n\t\t\tfor (let x = image.width; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (!inArea(x, y)) {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n","import type { AsyncExpectationResult } from '@vitest/expect'\nimport dedent from 'dedent'\nimport { resolve } from 'pathe'\nimport pixelmatch, { type PixelmatchOptions } from 'pixelmatch'\nimport { getCurrentTest } from 'vitest/suite'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands, page, server } from './@vitest/browser/context.js'\nimport { assertImageSnapshot, isImageSnapshot } from './@vitest/browser/image_snapshot.logic.js'\nimport { toDataURL, toImageData } from './image_data.js'\nimport { createImageResizer } from './image_resizer.js'\nimport { state } from './state.js'\n\nexport interface ImageSnapshotMatcher {\n\ttoMatchImageSnapshot(options?: MatchImageSnapshotOptions): Promise<void>\n}\n\nexport type MatchImageSnapshotOptions = {\n\t/**\n\t * Custom options passed to 'pixelmatch'\n\t */\n\tdiffOptions?: PixelmatchOptions | undefined\n\t/**\n\t * Failure threshold should measure in `pixel` or `percent`.\n\t *\n\t * Default is `pixel`.\n\t */\n\tfailureThresholdType?: 'pixel' | 'percent' | undefined\n\t/**\n\t * Failure tolerance threshold.\n\t *\n\t * Default is `0`.\n\t */\n\tfailureThreshold?: number | undefined\n}\n\nexport async function toMatchImageSnapshot(\n\tactual: any,\n\toptions?: MatchImageSnapshotOptions | undefined,\n): AsyncExpectationResult {\n\tconst promise = toMatchImageSnapshotInternal(actual, options)\n\tconst test = getCurrentTest()\n\tif (!test) return promise\n\ttest.promises ??= []\n\ttest.promises.push(promise)\n\treturn promise\n}\n\nasync function toMatchImageSnapshotInternal(\n\tactual: any,\n\toptions?: MatchImageSnapshotOptions | undefined,\n): AsyncExpectationResult {\n\tlet subject = await actual\n\tif (subject?.type === imageSnapshotStubSymbol) {\n\t\treturn success\n\t}\n\tif (!isImageSnapshot(subject)) {\n\t\tif (subject?.path && subject?.base64) {\n\t\t\treturn {\n\t\t\t\tpass: false,\n\t\t\t\tactual: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t'`toMatchImageSnapshot()` expects the subject to be the result of `page.imageSnapshot()`, but seems like you are using `page.screenshot()`?',\n\t\t\t}\n\t\t}\n\n\t\tif (typeof subject !== 'object') {\n\t\t\treturn {\n\t\t\t\tpass: false,\n\t\t\t\tactual: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t`\\`toMatchImageSnapshot()\\` expects the subject to be an element, locator, or result of \\`page.imageSnapshot()\\`, but got: \\`${subject}\\``,\n\t\t\t}\n\t\t}\n\t\tsubject = await page.imageSnapshot({ element: subject })\n\t}\n\n\tassertImageSnapshot(subject)\n\n\tconst baseline = await tryReadSnapshot(subject.baselinePath)\n\tif (!baseline) {\n\t\tawait commands.copyFile(subject.resultPath, subject.baselinePath)\n\t\treturn success\n\t}\n\n\tconst originalImage = await toImageData(baseline)\n\tconst [resultImage, baselineImage] = alignImagesToSameSize(subject.image, originalImage)\n\n\t// const test = getCurrentTest()\n\t// console.log('test', test)\n\tconst { pass, diffAmount, diffImage } = compareImage(\n\t\tbaselineImage,\n\t\tresultImage,\n\t\tstate.mergeMatchImageSnapshotOptions(options),\n\t)\n\n\tif (!pass) {\n\t\tif (server.config.snapshotOptions.updateSnapshot === 'all') {\n\t\t\tawait writeSnapshot(`${subject.baselinePath}`, resultImage)\n\t\t\treturn success\n\t\t}\n\t\tawait writeSnapshot(`${subject.diffPath}`, diffImage)\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tmessage: () =>\n\t\t\t\tdedent`Snapshot \\`${state.getName()}\\` mismatched\n\n\t\t\t${\n\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\t\t\t\t\t? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`\n\t\t\t\t\t\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t: `Expected simage to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\t\t}\n\n\t\t\tExpected: '${resolve(state.getCurrentDir(), subject.baselinePath)}'\n\t\t\tActual: '${resolve(state.getCurrentDir(), subject.resultPath)}'\n\t\t\tDifference: '${resolve(state.getCurrentDir(), subject.diffPath)}'`,\n\t\t}\n\t}\n\treturn success\n}\n\nconst success = {\n\tpass: true,\n\tmessage: () => '',\n}\n\nfunction tryReadSnapshot(path: string): Promise<string | undefined> {\n\treturn commands.readFile(path, { encoding: 'base64' }).catch(() => undefined)\n}\nasync function writeSnapshot(path: string, image: ImageData) {\n\tconst content = (await toDataURL(image)).split(',')[1]!\n\treturn commands.writeFile(path, content, 'base64url')\n}\n\nfunction alignImagesToSameSize(image1: ImageData, image2: ImageData): [image1: ImageData, image2: ImageData] {\n\tif (isSameSize(image1, image2)) {\n\t\treturn [image1, image2]\n\t}\n\tconst size = getMaxSize(image1, image2)\n\tconst resize = createImageResizer(size)\n\treturn [resize(image1), resize(image2)]\n}\n\nfunction getMaxSize(image1: ImageData, image2: ImageData) {\n\tconst width = Math.max(image1.width, image2.width)\n\tconst height = Math.max(image1.height, image2.height)\n\treturn { width, height }\n}\n\nfunction isSameSize(image1: ImageData, image2: ImageData) {\n\treturn image1.width === image2.width && image1.height === image2.height\n}\n\nfunction compareImage(\n\tbaselineImage: ImageData,\n\tresultImage: ImageData,\n\t{ failureThreshold = 0, failureThresholdType = 'pixel', diffOptions }: MatchImageSnapshotOptions = {},\n) {\n\tconst { width, height } = resultImage\n\tconst diffImage = new ImageData(width, height)\n\n\tconst pixelDiff = pixelmatch(resultImage.data, baselineImage.data, diffImage.data, width, height, diffOptions)\n\tconst diffAmount = toThresholdUnit({ failureThresholdType, width, height }, pixelDiff)\n\n\treturn {\n\t\tpass: diffAmount <= failureThreshold,\n\t\tdiffAmount,\n\t\tdiffImage,\n\t}\n}\n\nfunction toThresholdUnit(\n\t{ failureThresholdType, width, height }: { failureThresholdType: 'pixel' | 'percent'; width: number; height: number },\n\tpixelDiff: number,\n): number {\n\tswitch (failureThresholdType) {\n\t\tcase 'pixel':\n\t\t\treturn pixelDiff\n\t\tcase 'percent':\n\t\t\treturn (pixelDiff / (width * height)) * 100\n\t\tdefault:\n\t\t\tthrow new Error(`Invalid failureThresholdType: ${failureThresholdType}`)\n\t}\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { M as MatchImageSnapshotOptions } from './types-DZLg7FIU.js';
2
- export { C as CustomizeSnapshotIdOptions, d as ImageSnapshot, I as ImageSnapshotMatcher, b as ImageSnapshotOptions, V as VisOptions, c as cdp, a as commands, p as page, s as server, t as toMatchImageSnapshot } from './types-DZLg7FIU.js';
1
+ import { M as MatchImageSnapshotOptions } from './types-BrUWBuNW.js';
2
+ export { C as CustomizeSnapshotIdOptions, d as ImageSnapshot, I as ImageSnapshotMatcher, b as ImageSnapshotOptions, V as VisOptions, c as cdp, a as commands, p as page, s as server, t as toMatchImageSnapshot } from './types-BrUWBuNW.js';
3
3
  export { d as defineVisPreview, s as storybookPreviewVis } from './preview-CL-IBwxx.js';
4
4
  import '@vitest/browser/context';
5
5
  import 'vitest';
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- export { defineVisPreview, storybookPreviewVis } from './chunk-6WYK3DFU.js';
2
- export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-JQOEDY6R.js';
1
+ export { defineVisPreview, storybookPreviewVis } from './chunk-7MABS6N3.js';
2
+ export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-T3RUFMT2.js';
3
3
 
4
4
  // src/param.ts
5
5
  function defineSnapshotParam(snapshot) {
package/dist/preview.js CHANGED
@@ -1,5 +1,5 @@
1
- import { storybookPreviewVis } from './chunk-6WYK3DFU.js';
2
- import './chunk-JQOEDY6R.js';
1
+ import { storybookPreviewVis } from './chunk-7MABS6N3.js';
2
+ import './chunk-T3RUFMT2.js';
3
3
 
4
4
  // src/preview.ts
5
5
  var preview_default = storybookPreviewVis;
@@ -148,7 +148,7 @@ interface VisOptions extends MatchImageSnapshotOptions, CustomizeSnapshotIdOptio
148
148
  /**
149
149
  * The snapshot folder relative to the root of the project.
150
150
  *
151
- * Default: `__vis__/<local | CI platform>`
151
+ * Default: `__vis__`
152
152
  */
153
153
  snapshotRootDir?: string | undefined;
154
154
  /**
@@ -156,17 +156,21 @@ interface VisOptions extends MatchImageSnapshotOptions, CustomizeSnapshotIdOptio
156
156
  *
157
157
  * The snapshot subpath is used along with `snapshotRootDir` to determine the folder of the snapshots:
158
158
  *
159
- * - baseline: `<snapshotRootDir>/<snapshotSubpath>/<snapshotId>.png`
160
- * - result: `<snapshotRootDir>/<snapshotSubpath>/__results__/<snapshotId>.png`
161
- * - diff: `<snapshotRootDir>/<snapshotSubpath>/__diff_output__/<snapshotId>.png`
159
+ * - baseline: `<snapshotRootDir>/<platform>/<snapshotSubpath>/<snapshotId>.png`
160
+ * - result: `<snapshotRootDir>/__results__/<snapshotSubpath>/<snapshotId>.png`
161
+ * - diff: `<snapshotRootDir>/__diff_output__/<snapshotSubpath>/<snapshotId>.png`
162
162
  *
163
- * Typically, you place your test files in a `tests` folder or `src` folder along with your source code.
163
+ * Typically, you place your test files either in a dedicated `tests` folder or in the `src` folder along with your source code.
164
164
  * By default, the snapshot subpath is the test file path with that folder removed.
165
165
  * This reduces the nesting of the snapshot folders.
166
166
  *
167
- * If you have a non-typical setup, you can set this to customize the snapshot subpath.
167
+ * If you place your test files in multiple folders,
168
+ * such as in both `tests` and `src` folders,
169
+ * and they might have files with the same name,
170
+ * and create conflicting snapshots,
171
+ * you can set this to customize the snapshot subpath.
168
172
  */
169
- customizeSnapshotSubpath?: (testFilePath: string) => string;
173
+ customizeSnapshotSubpath?: (subPath: string) => string;
170
174
  /**
171
175
  * Timeout for taking the snapshot.
172
176
  *
@@ -1,5 +1,5 @@
1
- import { V as VisOptions } from './types-DZLg7FIU.js';
2
- export { I as ImageSnapshotMatcher, M as MatchImageSnapshotOptions, c as cdp, a as commands, p as page, s as server, t as toMatchImageSnapshot } from './types-DZLg7FIU.js';
1
+ import { V as VisOptions } from './types-BrUWBuNW.js';
2
+ export { I as ImageSnapshotMatcher, M as MatchImageSnapshotOptions, c as cdp, a as commands, p as page, s as server, t as toMatchImageSnapshot } from './types-BrUWBuNW.js';
3
3
  import '@vitest/browser/context';
4
4
  import 'vitest';
5
5
  import '@vitest/expect';
@@ -1,5 +1,5 @@
1
- import { toMatchImageSnapshot, state, page } from './chunk-JQOEDY6R.js';
2
- export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-JQOEDY6R.js';
1
+ import { toMatchImageSnapshot, state, page } from './chunk-T3RUFMT2.js';
2
+ export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-T3RUFMT2.js';
3
3
  import { expect, beforeAll, afterEach } from 'vitest';
4
4
 
5
5
  // src/should_take_snapshot.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storybook-addon-vis",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Storybook Vitest Image Snapshot addon",
5
5
  "keywords": [
6
6
  "image-snapshot",
@@ -64,8 +64,8 @@
64
64
  "@types/react": "^18.2.65",
65
65
  "@types/react-dom": "^18.2.21",
66
66
  "@vitejs/plugin-react": "^4.2.1",
67
- "@vitest/browser": "^2.1.4",
68
- "@vitest/expect": "^2.1.4",
67
+ "@vitest/browser": "^2.1.5",
68
+ "@vitest/expect": "^2.1.5",
69
69
  "boxen": "^8.0.0",
70
70
  "react": "^18.2.0",
71
71
  "react-dom": "^18.2.0",
package/readme.md CHANGED
@@ -37,19 +37,38 @@ import { storybookVis } from 'storybook-addon-vis/vitest-plugin'
37
37
  import { defineConfig } from 'vitest/config'
38
38
 
39
39
  export default defineConfig({
40
- plugins: [
41
- storybookTest(),
42
- storybookVis()
43
- ],
44
- test: {
45
- browser: {
46
- // ...
47
- }
48
- setupFiles: ['./vitest.setup.ts'],
49
- }
40
+ plugins: [
41
+ storybookTest(),
42
+ storybookVis()
43
+ ],
44
+ test: {
45
+ browser: {
46
+ // ...
47
+ },
48
+ global: false, // recommend to set to false
49
+ setupFiles: ['./vitest.setup.ts'],
50
+ }
50
51
  })
51
52
  ```
52
53
 
54
+ Note that we recommend to set `global` to `false`.
55
+ Setting `global` to `true` actually works ok during test,
56
+ although you need to access the functions from the `globalThis` or `window` object.
57
+
58
+ However, the problem is when the story is run within [storybook].
59
+ In [storybook], the `global` field has no effect.
60
+ And in fact, you need to import the functions from `@storybook/test` instead:
61
+
62
+ ```ts
63
+ // some.test.ts
64
+ import { expect } from 'vitest'
65
+
66
+ // some.stories.ts
67
+ import { expect } from '@storybook/test'
68
+ ```
69
+
70
+ Therefore, to avoid confusion, we recommend to set `global` to `false`.
71
+
53
72
  In `vitest.setup.ts`, you can use one of the presets to do the setup for you.
54
73
  You can also use the provided hooks to set up the test environment manually.
55
74
 
@@ -67,8 +86,8 @@ createVisConfig(/* options */).presets.basic()
67
86
  // capture image snapshot for all stories with `snapshot` tag,
68
87
  // for both light and dark themes
69
88
  createVisConfig(/* options */).presets.theme({
70
- light() { document.body.classList.remove('dark') },
71
- dark() { document.body.classList.add('dark') },
89
+ light() { document.body.classList.remove('dark') },
90
+ dark() { document.body.classList.add('dark') },
72
91
  })
73
92
  ```
74
93
 
@@ -79,8 +98,8 @@ On [storybook], you need to register the `beforeEach` hook in `.storybook/previe
79
98
  import { storybookPreviewVis } from 'storybook-addon-vis'
80
99
 
81
100
  export default {
82
- // ...
83
- beforeEach: storybookPreviewVis.beforeEach
101
+ // ...
102
+ beforeEach: storybookPreviewVis.beforeEach
84
103
  }
85
104
  ```
86
105
 
@@ -93,21 +112,21 @@ As with how tags work in [storybook], you can add the tag globally, per story fi
93
112
  ```ts
94
113
  // .storybook/preview.tsx
95
114
  export default {
96
- // Enable image snapshot for all stories
97
- tags: ['snapshot']
115
+ // Enable image snapshot for all stories
116
+ tags: ['snapshot']
98
117
  }
99
118
 
100
119
  // story.tsx
101
120
  export default {
102
- title: 'Button',
103
- // Take image snapshot automatically for all stories in this file
104
- tags: ['snapshot']
121
+ title: 'Button',
122
+ // Take image snapshot automatically for all stories in this file
123
+ tags: ['snapshot']
105
124
  }
106
125
 
107
126
  export const MyStory = {
108
- // Take image snapshot automatically for this story
109
- tags: ['snapshot'],
110
- // ...
127
+ // Take image snapshot automatically for this story
128
+ tags: ['snapshot'],
129
+ // ...
111
130
  }
112
131
  ```
113
132
 
@@ -116,15 +135,15 @@ much like `!test`.
116
135
 
117
136
  ```ts
118
137
  export default {
119
- title: 'Button',
120
- // Enable image snapshot for all stories in this file
121
- tags: ['snapshot']
138
+ title: 'Button',
139
+ // Enable image snapshot for all stories in this file
140
+ tags: ['snapshot']
122
141
  }
123
142
 
124
143
  export const MyStory = {
125
- // Disable image snapshot for this story
126
- tags: ['!snapshot'],
127
- // ...
144
+ // Disable image snapshot for this story
145
+ tags: ['!snapshot'],
146
+ // ...
128
147
  }
129
148
  ```
130
149
 
@@ -134,10 +153,10 @@ You can provide options to the `toMatchImageSnapshot` matcher using parameters.
134
153
  import { defineSnapshotParam } from 'storybook-addon-vis'
135
154
 
136
155
  export const MyStory = {
137
- parameters: defineSnapshotParam({
138
- failureThreshold: 70,
139
- })
140
- // ...
156
+ parameters: defineSnapshotParam({
157
+ failureThreshold: 70,
158
+ })
159
+ // ...
141
160
  }
142
161
  ```
143
162
 
@@ -152,75 +171,105 @@ import { expect } from '@storybook/test'
152
171
  import { page } from 'storybook-addon-vis'
153
172
 
154
173
  export const PageSnapshot = {
155
- // typically you want to disable automatic snapshot when using manual snapshot
156
- tags: ['!snapshot'],
157
- // ...
158
- async play() {
159
- await expect(page.imageSnapshot()).toMatchImageSnapshot()
160
- }
174
+ // typically you want to disable automatic snapshot when using manual snapshot
175
+ tags: ['!snapshot'],
176
+ // ...
177
+ async play() {
178
+ await expect(page.imageSnapshot()).toMatchImageSnapshot()
179
+ }
161
180
  }
162
181
 
163
182
  export const ElementSnapshot = {
164
- // typically you want to disable automatic snapshot when using manual snapshot
165
- tags: ['!snapshot'],
166
- // ...
167
- async play({ canvas }) {
168
- const element = await canvas.getByTestid('subject')
169
- await expect(page.imageSnapshot({ element })).toMatchImageSnapshot()
170
- }
183
+ // typically you want to disable automatic snapshot when using manual snapshot
184
+ tags: ['!snapshot'],
185
+ // ...
186
+ async play({ canvas }) {
187
+ const element = await canvas.getByTestid('subject')
188
+ await expect(page.imageSnapshot({ element })).toMatchImageSnapshot()
189
+ }
171
190
  }
172
191
  ```
173
192
 
174
193
  ## Snapshot folder
175
194
 
176
- The snapshots are stored under the `__vis__` folder next to the test/story file:
177
-
178
- ```sh
179
- ├── __vis__
180
- ├── __diff_output__ # where the diff images are stored
181
- ├── __result__ # where the resulting snapshot of the current run are stored
182
- ├── darwin # snapshot generated on macos by CI
183
- ├── linux # snapshot generated on linux by CI
184
- ├── local # snapshot generated on local machine
185
- └── story.tsx
186
- ├── snapshot-1.png
187
- ├── snapshot-2.png
188
- └── ...
189
- └── story.tsx
195
+ By default, the snapshots are stored under the `__vis__` folder at the root of the project:
196
+
197
+ ```ini
198
+ v __vis__
199
+ ˃ __diff_output__ # where the diff images are stored
200
+ ˃ __result__ # where the resulting snapshot of the current run are stored
201
+ ˃ darwin # snapshot generated on macos by CI
202
+ ˃ linux # snapshot generated on linux by CI
203
+ v local # snapshot generated on local machine
204
+ v button.stories.tsx
205
+ snapshot-1.png
206
+ snapshot-2.png
207
+ v src
208
+ button.stories.tsx
190
209
  ```
191
210
 
192
- This is a similar approach to [playwright]'s `page.screenshot()` which saves the screenshots under the `__screenshot__` folder.
211
+ You can change the snapshot folder by providing the `snapshotDir` option to the `createVisConfig` function.
193
212
 
194
- This is different from [jest-image-snapshot] which stores the snapshots under the `__snapshots__` folder at the root of the project.
213
+ ```ts
214
+ import { createVisConfig } from 'storybook-addon-vis/vitest-setup'
195
215
 
196
- The reason for this is we need to support both [storybook] stories and [vitest] tests at the same time.
197
- Stories have a unique ID enforced by [storybook], and [jest-image-snapshot] uses it to uniquely identify the snapshots.
198
- This makes it possible to store the snapshots in the same folder.
216
+ createVisConfig({
217
+ snapshotRootDir: 'path/to/snapshot'
218
+ })
219
+ ```
199
220
 
200
- With [vitest] tests, we don't have such unique ID (that is stable across runs and humanly readable),
201
- meaning we have to retain the folder structure to uniquely identify the snapshots.
202
- This makes the single folder approach much less attractive.
221
+ Typically, you place your test files either in a dedicated `tests` folder or in the `src` folder along with your source code.
222
+ By default, [storybook-addon-vis] removes that folder to reduces nesting.
203
223
 
204
- You can change the snapshot folder by providing the `snapshotDir` option to the `createVisConfig` function.
224
+ If you place your test files in multiple folders,
225
+ such as in both `tests` and `src` folders,
226
+ you can use `customizeSnapshotSubpath` to customize the snapshot sub-path to avoid conflicts.
205
227
 
206
228
  ```ts
207
229
  import { createVisConfig } from 'storybook-addon-vis/vitest-setup'
208
230
 
209
231
  createVisConfig({
210
- snapshotDir: 'path/to/snapshot'
232
+ // keep the folder structure
233
+ customizeSnapshotSubpath: (subpath) => subpath
211
234
  })
212
235
  ```
213
236
 
237
+ With the above configuration, the snapshot folder structure will look like this:
238
+
239
+ ```ini
240
+ v __vis__
241
+ > # ...
242
+ v local # snapshot generated on local machine
243
+ v examples
244
+ v button.stories.tsx
245
+ snapshot-1.png
246
+ snapshot-2.png
247
+ v src
248
+ v button.stories.tsx
249
+ snapshot-1.png
250
+ snapshot-2.png
251
+ v tests
252
+ v button.stories.tsx
253
+ snapshot-1.png
254
+ snapshot-2.png
255
+ v examples
256
+ button.stories.tsx
257
+ v src
258
+ button.stories.tsx
259
+ v tests
260
+ button.stories.tsx
261
+ ```
262
+
214
263
  ### Ignore snapshot folders
215
264
 
216
265
  Some snapshot folders should be ignored by git.
217
266
 
218
- With the default snapshot folder structure, you might want to add the following to your `.gitignore`:
267
+ With the default snapshot folder structure, you should add the following to your `.gitignore`:
219
268
 
220
- ```sh
269
+ ```ini
221
270
  # .gitignore
222
- **/__vis__/*/__diff_output__
223
- **/__vis__/*/__results__
271
+ **/__vis__/__diff_output__
272
+ **/__vis__/__results__
224
273
  **/__vis__/local
225
274
  ```
226
275
 
@@ -239,7 +288,6 @@ shamefully-hoist=true
239
288
  ```
240
289
 
241
290
  [jest-image-snapshot]: https://github.com/americanexpress/jest-image-snapshot
242
- [playwright]: https://playwright.dev/docs/screenshots
243
291
  [storybook-addon-vis]: https://github.com/repobuddy/storybook-addon-vis
244
292
  [storybook]: https://storybook.js.org
245
293
  [vitest]: https://vitest.dev/
@@ -1,16 +1,12 @@
1
1
  import { expect, it } from 'vitest'
2
- import { commands, page } from './context.js'
2
+ import { page } from './context.js'
3
3
  import { toSnapshotId } from './image_snapshot.logic.js'
4
4
 
5
5
  it('save file under __results__', async ({ task }) => {
6
6
  const f1 = await page.imageSnapshot()
7
7
  const filename = task.file.name.slice('src/'.length)
8
- expect(f1.resultPath).toMatch(
9
- `../../../__vis__/${await commands.getSnapshotPlatform()}/__results__/${filename}/${toSnapshotId(task.name)}-1.png`,
10
- )
8
+ expect(f1.resultPath).toMatch(`../../../__vis__/__results__/${filename}/${toSnapshotId(task.name)}-1.png`)
11
9
 
12
10
  const f2 = await page.imageSnapshot()
13
- expect(f2.resultPath).toMatch(
14
- `../../../__vis__/${await commands.getSnapshotPlatform()}/__results__/${filename}/${toSnapshotId(task.name)}-2.png`,
15
- )
11
+ expect(f2.resultPath).toMatch(`../../../__vis__/__results__/${filename}/${toSnapshotId(task.name)}-2.png`)
16
12
  })
@@ -13,7 +13,7 @@ const { Success } = composeStories(ToMatchStories)
13
13
  const { TakeSnapshot } = composeStories(PerStory)
14
14
 
15
15
  it('should reject if the subject is undefined', async () => {
16
- expect(() => expect(undefined).toMatchImageSnapshot()).rejects.toThrowError(
16
+ await expect(() => expect(undefined).toMatchImageSnapshot()).rejects.toThrowError(
17
17
  '`toMatchImageSnapshot()` expects the subject to be an element, locator, or result of `page.imageSnapshot()`, but got: `undefined`',
18
18
  )
19
19
  })
package/src/state.ts CHANGED
@@ -16,7 +16,7 @@ function createStore() {
16
16
  let resultDir: string
17
17
  let diffDir: string
18
18
  let snapshot: Record<string, Record<string, { index: number }>>
19
- let suiteOptions: VisOptions = {}
19
+ let suiteOptions: VisOptions = Object.create(null)
20
20
 
21
21
  // story states (story.beforeEach)
22
22
  let tags: string[] | undefined
@@ -34,18 +34,18 @@ function createStore() {
34
34
  testFilepath = suite.file.filepath
35
35
  const projectDir = testFilepath.slice(0, -suite.name.length)
36
36
 
37
- const snapshotPath = resolveSnapshotPath(await getPlatform(), options)
37
+ const snapshotPath = resolveSnapshotPath(options)
38
38
  const snapshotFullPath = join(projectDir, snapshotPath)
39
39
  currentDir = dirname(testFilepath)
40
40
  const suiteDir = trimSuiteDir(suite.name, options)
41
- baselineDir = relative(currentDir, join(snapshotFullPath, suiteDir))
41
+ baselineDir = relative(currentDir, join(snapshotFullPath, await getPlatform(), suiteDir))
42
42
  resultDir = relative(currentDir, join(snapshotFullPath, '__results__', suiteDir))
43
43
  diffDir = relative(currentDir, join(snapshotFullPath, '__diff_output__', suiteDir))
44
44
 
45
45
  suiteOptions = options
46
46
  snapshot = snapshot ?? {}
47
47
  if (!snapshot[testFilepath]) {
48
- snapshot[testFilepath] = {}
48
+ snapshot[testFilepath] = Object.create(null)
49
49
  await commands.rmDir(resultDir)
50
50
  await commands.rmDir(diffDir)
51
51
  }
@@ -96,8 +96,8 @@ function createStore() {
96
96
 
97
97
  export const state = createStore()
98
98
 
99
- function resolveSnapshotPath(platform: string, options: VisOptions) {
100
- return options.snapshotRootDir ?? `__vis__/${platform}`
99
+ function resolveSnapshotPath(options: VisOptions) {
100
+ return options.snapshotRootDir ?? '__vis__'
101
101
  }
102
102
 
103
103
  function trimSuiteDir(suiteName: string, options: VisOptions) {
@@ -113,7 +113,7 @@ function defaultCustomizeSnapshotSubpath(suiteName: string) {
113
113
  }
114
114
 
115
115
  let platformP: Promise<string>
116
- function getPlatform(): Promise<string> {
116
+ function getPlatform() {
117
117
  if (platformP) return platformP
118
118
 
119
119
  return (platformP = commands.getSnapshotPlatform())
package/src/types.ts CHANGED
@@ -10,7 +10,7 @@ export interface VisOptions extends MatchImageSnapshotOptions, CustomizeSnapshot
10
10
  /**
11
11
  * The snapshot folder relative to the root of the project.
12
12
  *
13
- * Default: `__vis__/<local | CI platform>`
13
+ * Default: `__vis__`
14
14
  */
15
15
  snapshotRootDir?: string | undefined
16
16
 
@@ -19,17 +19,21 @@ export interface VisOptions extends MatchImageSnapshotOptions, CustomizeSnapshot
19
19
  *
20
20
  * The snapshot subpath is used along with `snapshotRootDir` to determine the folder of the snapshots:
21
21
  *
22
- * - baseline: `<snapshotRootDir>/<snapshotSubpath>/<snapshotId>.png`
23
- * - result: `<snapshotRootDir>/<snapshotSubpath>/__results__/<snapshotId>.png`
24
- * - diff: `<snapshotRootDir>/<snapshotSubpath>/__diff_output__/<snapshotId>.png`
22
+ * - baseline: `<snapshotRootDir>/<platform>/<snapshotSubpath>/<snapshotId>.png`
23
+ * - result: `<snapshotRootDir>/__results__/<snapshotSubpath>/<snapshotId>.png`
24
+ * - diff: `<snapshotRootDir>/__diff_output__/<snapshotSubpath>/<snapshotId>.png`
25
25
  *
26
- * Typically, you place your test files in a `tests` folder or `src` folder along with your source code.
26
+ * Typically, you place your test files either in a dedicated `tests` folder or in the `src` folder along with your source code.
27
27
  * By default, the snapshot subpath is the test file path with that folder removed.
28
28
  * This reduces the nesting of the snapshot folders.
29
29
  *
30
- * If you have a non-typical setup, you can set this to customize the snapshot subpath.
30
+ * If you place your test files in multiple folders,
31
+ * such as in both `tests` and `src` folders,
32
+ * and they might have files with the same name,
33
+ * and create conflicting snapshots,
34
+ * you can set this to customize the snapshot subpath.
31
35
  */
32
- customizeSnapshotSubpath?: (testFilePath: string) => string
36
+ customizeSnapshotSubpath?: (subPath: string) => string
33
37
  /**
34
38
  * Timeout for taking the snapshot.
35
39
  *
package/src/vis.spec.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { expect, it } from 'vitest'
2
+ import { commands } from './@vitest/browser/context.js'
2
3
  import { state } from './state.js'
3
4
  import { createVisConfig } from './vis.js'
4
5
 
@@ -7,7 +8,7 @@ it('can define the snapshot root folder relative to the root of the project', as
7
8
  snapshotRootDir: '_sp_',
8
9
  }).beforeAll({ name: 'some.test.ts', file: { filepath: 'dummy/some.test.ts' } }),
9
10
  expect(state.getSnapshotFilePaths()).toMatchObject({
10
- baselinePath: '_sp_/some.test.ts/can-define-the-snapshot-root-folder-relative-to-the-root-of-the-project-1.png',
11
+ baselinePath: `_sp_/${await commands.getSnapshotPlatform()}/some.test.ts/can-define-the-snapshot-root-folder-relative-to-the-root-of-the-project-1.png`,
11
12
  diffPath:
12
13
  '_sp_/__diff_output__/some.test.ts/can-define-the-snapshot-root-folder-relative-to-the-root-of-the-project-1.png',
13
14
  resultPath:
@@ -42,7 +43,7 @@ it('can define default customizeSnapshotId', async () => {
42
43
  }).beforeAll({ name: 'some.test.ts', file: { filepath: 'dummy/some.test.ts' } })
43
44
 
44
45
  expect(state.getSnapshotFilePaths()).toMatchObject({
45
- baselinePath: '_sp_/some.test.ts/can.png',
46
+ baselinePath: `_sp_/${await commands.getSnapshotPlatform()}/some.test.ts/can.png`,
46
47
  diffPath: '_sp_/__diff_output__/some.test.ts/can.png',
47
48
  resultPath: '_sp_/__results__/some.test.ts/can.png',
48
49
  })
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/@vitest/browser/image_snapshot.logic.ts","../src/state.ts","../src/@vitest/browser/page.has_image_snapshot.ts","../src/image_data.ts","../src/@vitest/browser/page.image_snapshot.ts","../src/@vitest/browser/context.ts","../src/image_resizer.ts","../src/expect.to_match_image_snapshot.ts"],"names":["state","ctx","id","index","resolve","getCurrentTest"],"mappings":"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","file":"chunk-JQOEDY6R.js","sourcesContent":["export const imageSnapshotSymbol = Symbol.for('storybook-addon-vis/imageSnapshot')\n\nexport const imageSnapshotStubSymbol = Symbol.for('storybook-addon-vis/imageSnapshotStub')\n","import { imageSnapshotSymbol } from './constants.js'\nimport type { ImageSnapshot } from './types.js'\n\nexport function isImageSnapshot(subject: any): subject is ImageSnapshot {\n\treturn !!subject && subject.type === imageSnapshotSymbol\n}\n\nexport function assertImageSnapshot(subject: any): asserts subject is ImageSnapshot {\n\tif (!isImageSnapshot(subject)) {\n\t\tthrow new Error('Expected subject to be an image snapshot')\n\t}\n}\n\nexport function toSnapshotId(taskName: string) {\n\treturn `${taskName.replace(/[^a-z0-9]/gi, '-').toLowerCase()}`\n}\n","import { dirname, join, relative } from 'pathe'\nimport type { StoryContext } from 'storybook/internal/types'\nimport { omit, required } from 'type-plus'\nimport { getCurrentTest } from 'vitest/suite'\nimport { commands } from './@vitest/browser/context.js'\nimport { toSnapshotId } from './@vitest/browser/image_snapshot.logic'\nimport type { ImageSnapshotOptions } from './@vitest/browser/types'\nimport type { MatchImageSnapshotOptions } from './expect.to_match_image_snapshot'\nimport type { VisOptions } from './types.js'\n\nfunction createStore() {\n\t// test suite (runner.beforeAll) states\n\tlet testFilepath: string\n\tlet currentDir: string\n\tlet baselineDir: string\n\tlet resultDir: string\n\tlet diffDir: string\n\tlet snapshot: Record<string, Record<string, { index: number }>>\n\tlet suiteOptions: VisOptions = {}\n\n\t// story states (story.beforeEach)\n\tlet tags: string[] | undefined\n\tlet parameters: Record<string, any> | undefined\n\tlet taskName: string\n\tlet id: string\n\n\tconst state = {\n\t\tbaselineDir,\n\t\tresultDir,\n\t\tdiffDir,\n\t\tsnapshot: {},\n\t\tasync setupSuite(suite: { file: { filepath: string }; name: string }, options: VisOptions = {}) {\n\t\t\t// console.debug('setupSuite', suite.name)\n\t\t\ttestFilepath = suite.file.filepath\n\t\t\tconst projectDir = testFilepath.slice(0, -suite.name.length)\n\n\t\t\tconst snapshotPath = resolveSnapshotPath(await getPlatform(), options)\n\t\t\tconst snapshotFullPath = join(projectDir, snapshotPath)\n\t\t\tcurrentDir = dirname(testFilepath)\n\t\t\tconst suiteDir = trimSuiteDir(suite.name, options)\n\t\t\tbaselineDir = relative(currentDir, join(snapshotFullPath, suiteDir))\n\t\t\tresultDir = relative(currentDir, join(snapshotFullPath, '__results__', suiteDir))\n\t\t\tdiffDir = relative(currentDir, join(snapshotFullPath, '__diff_output__', suiteDir))\n\n\t\t\tsuiteOptions = options\n\t\t\tsnapshot = snapshot ?? {}\n\t\t\tif (!snapshot[testFilepath]) {\n\t\t\t\tsnapshot[testFilepath] = {}\n\t\t\t\tawait commands.rmDir(resultDir)\n\t\t\t\tawait commands.rmDir(diffDir)\n\t\t\t}\n\t\t},\n\t\tsetupStory(ctx: StoryContext) {\n\t\t\t// console.debug('setupStory', ctx.name)\n\t\t\ttags = ctx.tags\n\t\t\tparameters = ctx.parameters\n\t\t},\n\t\tshouldTakeSnapshot() {\n\t\t\t// console.debug('shouldTakeSnapshot', taskName, tags)\n\t\t\tif (!tags) return false\n\t\t\treturn tags.lastIndexOf('!snapshot') < tags.lastIndexOf('snapshot')\n\t\t},\n\t\tgetName() {\n\t\t\treturn taskName\n\t\t},\n\t\tgetCurrentDir() {\n\t\t\treturn currentDir\n\t\t},\n\t\tgetTimeout(timeout?: number | undefined) {\n\t\t\treturn timeout ?? suiteOptions.timeout ?? 30000\n\t\t},\n\t\tmergeMatchImageSnapshotOptions(options?: MatchImageSnapshotOptions) {\n\t\t\treturn required(omit(suiteOptions, 'snapshotRootDir', 'customizeSnapshotSubpath'), parameters?.snapshot, options)\n\t\t},\n\t\tgetSnapshotFilePaths(options?: ImageSnapshotOptions | undefined) {\n\t\t\tconst test = getCurrentTest()\n\t\t\ttaskName = test.name\n\t\t\tid = toSnapshotId(taskName)\n\t\t\tsnapshot[testFilepath][id] = snapshot[testFilepath][id] ?? { index: 1 }\n\n\t\t\tconst index = snapshot[testFilepath][id]!.index\n\t\t\tconst customizeSnapshotId =\n\t\t\t\tsuiteOptions.customizeSnapshotId ?? options?.customizeSnapshotId ?? ((id, index) => `${id}-${index}`)\n\t\t\tconst snapshotFilename = `${customizeSnapshotId(id, index)}.png`\n\t\t\tconst baselinePath = join(baselineDir, snapshotFilename)\n\t\t\tconst resultPath = join(resultDir, snapshotFilename)\n\t\t\tconst diffPath = join(diffDir, snapshotFilename)\n\t\t\treturn { snapshotFilename, baselinePath, resultPath, diffPath }\n\t\t},\n\t\tincrementSnapshotIndex() {\n\t\t\tsnapshot[testFilepath][id]!.index++\n\t\t},\n\t}\n\treturn state\n}\n\nexport const state = createStore()\n\nfunction resolveSnapshotPath(platform: string, options: VisOptions) {\n\treturn options.snapshotRootDir ?? `__vis__/${platform}`\n}\n\nfunction trimSuiteDir(suiteName: string, options: VisOptions) {\n\tconst customizeSnapshotSubpath = options.customizeSnapshotSubpath ?? defaultCustomizeSnapshotSubpath\n\treturn customizeSnapshotSubpath(suiteName)\n}\n\nfunction defaultCustomizeSnapshotSubpath(suiteName: string) {\n\tconst [suiteDir] = suiteName.split('/', 1)\n\tif (['tests', 'test', 'src', 'source', 'js', 'ts', 'lib'].includes(suiteDir))\n\t\treturn suiteName.slice(suiteDir.length + 1)\n\treturn suiteName\n}\n\nlet platformP: Promise<string>\nfunction getPlatform(): Promise<string> {\n\tif (platformP) return platformP\n\n\treturn (platformP = commands.getSnapshotPlatform())\n}\n","import type { BrowserPage } from '@vitest/browser/context'\nimport { join } from 'pathe'\nimport { state } from '../../state'\nimport { commands } from './context'\nimport type { CustomizeSnapshotIdOptions } from './types'\n\nexport interface HasImageSnapshotAction {\n\thasImageSnapshot(this: BrowserPage, options?: CustomizeSnapshotIdOptions | undefined): Promise<boolean>\n}\n\n/**\n * Check if the snapshot image exists.\n */\nexport async function hasImageSnapshot(this: BrowserPage, options?: CustomizeSnapshotIdOptions | undefined) {\n\tconst { baselinePath } = state.getSnapshotFilePaths(options)\n\treturn commands.existFile(baselinePath)\n}\n","export function toImageData(base64: string) {\n\treturn new Promise<ImageData>((resolve, reject) => {\n\t\tconst img = new Image()\n\t\timg.src = `data:image/png;base64,${base64}`\n\n\t\timg.onload = () => {\n\t\t\tconst canvas = document.createElement('canvas')\n\t\t\tcanvas.width = img.width\n\t\t\tcanvas.height = img.height\n\t\t\tconst ctx = canvas.getContext('2d')!\n\t\t\tctx.drawImage(img, 0, 0)\n\t\t\tresolve(ctx.getImageData(0, 0, canvas.width, canvas.height))\n\t\t}\n\n\t\timg.onerror = () => {\n\t\t\treject(new Error('Failed to load image'))\n\t\t}\n\t})\n}\n\nexport async function toDataURL(imageData: ImageData) {\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = imageData.width\n\tcanvas.height = imageData.height\n\tconst ctx = canvas.getContext('2d')!\n\tctx.putImageData(imageData, 0, 0)\n\treturn new Promise<string>((resolve) => {\n\t\tcanvas.toBlob((blob) => {\n\t\t\tconst reader = new FileReader()\n\t\t\treader.onload = () => {\n\t\t\t\tresolve(reader.result as string)\n\t\t\t}\n\t\t\treader.readAsDataURL(blob!)\n\t\t})\n\t})\n}\n","import type { BrowserPage } from '@vitest/browser/context'\nimport { toImageData } from '../../image_data.js'\nimport { state } from '../../state.js'\nimport { imageSnapshotSymbol } from './constants.js'\nimport type { ImageSnapshot, ImageSnapshotOptions } from './types.js'\n\nexport interface ImageSnapshotAction {\n\timageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions | undefined): Promise<ImageSnapshot>\n}\n\nexport async function imageSnapshot(\n\tthis: BrowserPage,\n\toptions?: ImageSnapshotOptions | undefined,\n): Promise<ImageSnapshot> {\n\tconst { snapshotFilename, baselinePath, resultPath, diffPath } = state.getSnapshotFilePaths(options)\n\t// console.debug('taking snapshot', state.getName(), snapshotFilename)\n\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t\ttimeout: state.getTimeout(options?.timeout),\n\t})\n\tstate.incrementSnapshotIndex()\n\tconst image = await toImageData(screenshot.base64)\n\n\treturn {\n\t\ttype: imageSnapshotSymbol,\n\t\tsnapshotFilename,\n\t\tbaselinePath,\n\t\tresultPath,\n\t\tdiffPath,\n\t\tbase64: screenshot.base64,\n\t\timage,\n\t}\n}\n","import {\n\tgetByAltText,\n\tgetByLabelText,\n\tgetByPlaceholderText,\n\tgetByRole,\n\tgetByTestId,\n\tgetByText,\n\tgetByTitle,\n} from '@storybook/test'\nimport type { BrowserCommands, BrowserPage, CDPSession, Platform } from '@vitest/browser/context'\nimport type { SerializedConfig } from 'vitest'\nimport { imageSnapshotStubSymbol } from './constants'\nimport { hasImageSnapshot } from './page.has_image_snapshot'\nimport { imageSnapshot } from './page.image_snapshot'\n\nlet ctx: Awaited<typeof import('@vitest/browser/context')>\n\nif ((globalThis as any).__vitest_browser__) {\n\timport('@vitest/browser/context').then((m) => {\n\t\tctx = m\n\t\tpage.extend({ imageSnapshot, hasImageSnapshot })\n\t})\n}\n\n// TODO: stub the functions within the `CDPSession`\n// when we better understands the use cases\nexport function cdp(): CDPSession {\n\treturn ctx?.cdp() ?? ({} as any)\n}\n\nexport const commands = new Proxy<BrowserCommands>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.commands as any)[prop]\n\t},\n})\n\nexport const page = new Proxy<BrowserPage>(\n\t{\n\t\tgetByAltText,\n\t\tgetByRole,\n\t\tgetByTestId,\n\t\tgetByText,\n\t\tgetByTitle,\n\t\tgetByPlaceholder: getByPlaceholderText,\n\t\tgetByLabelText,\n\t} as any,\n\t{\n\t\tget(target, prop) {\n\t\t\treturn ((target as any)[prop] ?? ctx?.page)\n\t\t\t\t? (ctx?.page as any)[prop]\n\t\t\t\t: () => {\n\t\t\t\t\t\tif (prop === 'imageSnapshot') {\n\t\t\t\t\t\t\treturn { type: imageSnapshotStubSymbol }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (prop === 'hasImageSnapshot') {\n\t\t\t\t\t\t\treturn false\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.info(`\\`page.${prop.toString()}\\` does not exist when running in browser`)\n\t\t\t\t\t}\n\t\t},\n\t},\n)\n\nexport const server = new Proxy<{\n\t/**\n\t * Platform the Vitest server is running on.\n\t * The same as calling `process.platform` on the server.\n\t */\n\tplatform: Platform\n\t/**\n\t * Runtime version of the Vitest server.\n\t * The same as calling `process.version` on the server.\n\t */\n\tversion: string\n\t/**\n\t * Name of the browser provider.\n\t */\n\tprovider: string\n\t/**\n\t * Name of the current browser.\n\t */\n\tbrowser: string\n\t/**\n\t * Available commands for the browser.\n\t * @see {@link https://vitest.dev/guide/browser/commands}\n\t */\n\tcommands: BrowserCommands\n\t/**\n\t * Serialized test config.\n\t */\n\tconfig: SerializedConfig\n}>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.server as any)[prop]\n\t},\n})\n","export const createImageResizer =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tfor (let x = 0; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (inArea(x, y)) {\n\t\t\t\t\tconst old = (image.width * y + x) << 2\n\t\t\t\t\tresult.data[idx] = image.data[old]\n\t\t\t\t\tresult.data[idx + 1] = image.data[old + 1]\n\t\t\t\t\tresult.data[idx + 2] = image.data[old + 2]\n\t\t\t\t\tresult.data[idx + 3] = image.data[old + 3]\n\t\t\t\t} else {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n// slower\nexport const createImageResizer2 =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tconst copyBytes = image.width * 4\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tresult.data.set(image.data.slice(y * image.width, copyBytes), y * width * 4)\n\t\t\tfor (let x = image.width; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (!inArea(x, y)) {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n","import type { AsyncExpectationResult } from '@vitest/expect'\nimport dedent from 'dedent'\nimport { resolve } from 'pathe'\nimport pixelmatch, { type PixelmatchOptions } from 'pixelmatch'\nimport { getCurrentTest } from 'vitest/suite'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands, page, server } from './@vitest/browser/context.js'\nimport { assertImageSnapshot, isImageSnapshot } from './@vitest/browser/image_snapshot.logic.js'\nimport { toDataURL, toImageData } from './image_data.js'\nimport { createImageResizer } from './image_resizer.js'\nimport { state } from './state.js'\n\nexport interface ImageSnapshotMatcher {\n\ttoMatchImageSnapshot(options?: MatchImageSnapshotOptions): Promise<void>\n}\n\nexport type MatchImageSnapshotOptions = {\n\t/**\n\t * Custom options passed to 'pixelmatch'\n\t */\n\tdiffOptions?: PixelmatchOptions | undefined\n\t/**\n\t * Failure threshold should measure in `pixel` or `percent`.\n\t *\n\t * Default is `pixel`.\n\t */\n\tfailureThresholdType?: 'pixel' | 'percent' | undefined\n\t/**\n\t * Failure tolerance threshold.\n\t *\n\t * Default is `0`.\n\t */\n\tfailureThreshold?: number | undefined\n}\n\nexport async function toMatchImageSnapshot(\n\tactual: any,\n\toptions?: MatchImageSnapshotOptions | undefined,\n): AsyncExpectationResult {\n\tconst promise = toMatchImageSnapshotInternal(actual, options)\n\tconst test = getCurrentTest()\n\tif (!test) return promise\n\ttest.promises ??= []\n\ttest.promises.push(promise)\n\treturn promise\n}\n\nasync function toMatchImageSnapshotInternal(\n\tactual: any,\n\toptions?: MatchImageSnapshotOptions | undefined,\n): AsyncExpectationResult {\n\tlet subject = await actual\n\tif (subject?.type === imageSnapshotStubSymbol) {\n\t\treturn success\n\t}\n\tif (!isImageSnapshot(subject)) {\n\t\tif (subject?.path && subject?.base64) {\n\t\t\treturn {\n\t\t\t\tpass: false,\n\t\t\t\tactual: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t'`toMatchImageSnapshot()` expects the subject to be the result of `page.imageSnapshot()`, but seems like you are using `page.screenshot()`?',\n\t\t\t}\n\t\t}\n\n\t\tif (typeof subject !== 'object') {\n\t\t\treturn {\n\t\t\t\tpass: false,\n\t\t\t\tactual: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t`\\`toMatchImageSnapshot()\\` expects the subject to be an element, locator, or result of \\`page.imageSnapshot()\\`, but got: \\`${subject}\\``,\n\t\t\t}\n\t\t}\n\t\tsubject = await page.imageSnapshot({ element: subject })\n\t}\n\n\tassertImageSnapshot(subject)\n\n\tconst baseline = await tryReadSnapshot(subject.baselinePath)\n\tif (!baseline) {\n\t\tawait commands.copyFile(subject.resultPath, subject.baselinePath)\n\t\treturn success\n\t}\n\n\tconst originalImage = await toImageData(baseline)\n\tconst [resultImage, baselineImage] = alignImagesToSameSize(subject.image, originalImage)\n\n\t// const test = getCurrentTest()\n\t// console.log('test', test)\n\tconst { pass, diffAmount, diffImage } = compareImage(\n\t\tbaselineImage,\n\t\tresultImage,\n\t\tstate.mergeMatchImageSnapshotOptions(options),\n\t)\n\n\tif (!pass) {\n\t\tif (server.config.snapshotOptions.updateSnapshot === 'all') {\n\t\t\tawait writeSnapshot(`${subject.baselinePath}`, resultImage)\n\t\t\treturn success\n\t\t}\n\t\tawait writeSnapshot(`${subject.diffPath}`, diffImage)\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tmessage: () =>\n\t\t\t\tdedent`Snapshot \\`${state.getName()}\\` mismatched\n\n\t\t\t${\n\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\t\t\t\t\t? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`\n\t\t\t\t\t\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t: `Expected simage to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\t\t}\n\n\t\t\tExpected: '${resolve(state.getCurrentDir(), subject.baselinePath)}'\n\t\t\tActual: '${resolve(state.getCurrentDir(), subject.resultPath)}'\n\t\t\tDifference: '${resolve(state.getCurrentDir(), subject.diffPath)}'`,\n\t\t}\n\t}\n\treturn success\n}\n\nconst success = {\n\tpass: true,\n\tmessage: () => '',\n}\n\nfunction tryReadSnapshot(path: string): Promise<string | undefined> {\n\treturn commands.readFile(path, { encoding: 'base64' }).catch(() => undefined)\n}\nasync function writeSnapshot(path: string, image: ImageData) {\n\tconst content = (await toDataURL(image)).split(',')[1]!\n\treturn commands.writeFile(path, content, 'base64url')\n}\n\nfunction alignImagesToSameSize(image1: ImageData, image2: ImageData): [image1: ImageData, image2: ImageData] {\n\tif (isSameSize(image1, image2)) {\n\t\treturn [image1, image2]\n\t}\n\tconst size = getMaxSize(image1, image2)\n\tconst resize = createImageResizer(size)\n\treturn [resize(image1), resize(image2)]\n}\n\nfunction getMaxSize(image1: ImageData, image2: ImageData) {\n\tconst width = Math.max(image1.width, image2.width)\n\tconst height = Math.max(image1.height, image2.height)\n\treturn { width, height }\n}\n\nfunction isSameSize(image1: ImageData, image2: ImageData) {\n\treturn image1.width === image2.width && image1.height === image2.height\n}\n\nfunction compareImage(\n\tbaselineImage: ImageData,\n\tresultImage: ImageData,\n\t{ failureThreshold = 0, failureThresholdType = 'pixel', diffOptions }: MatchImageSnapshotOptions = {},\n) {\n\tconst { width, height } = resultImage\n\tconst diffImage = new ImageData(width, height)\n\n\tconst pixelDiff = pixelmatch(resultImage.data, baselineImage.data, diffImage.data, width, height, diffOptions)\n\tconst diffAmount = toThresholdUnit({ failureThresholdType, width, height }, pixelDiff)\n\n\treturn {\n\t\tpass: diffAmount <= failureThreshold,\n\t\tdiffAmount,\n\t\tdiffImage,\n\t}\n}\n\nfunction toThresholdUnit(\n\t{ failureThresholdType, width, height }: { failureThresholdType: 'pixel' | 'percent'; width: number; height: number },\n\tpixelDiff: number,\n): number {\n\tswitch (failureThresholdType) {\n\t\tcase 'pixel':\n\t\t\treturn pixelDiff\n\t\tcase 'percent':\n\t\t\treturn (pixelDiff / (width * height)) * 100\n\t\tdefault:\n\t\t\tthrow new Error(`Invalid failureThresholdType: ${failureThresholdType}`)\n\t}\n}\n"]}