storybook-addon-vis 0.8.0 → 0.9.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 (40) hide show
  1. package/dist/{chunk-3ZUXLFHJ.js → chunk-EYGWSOX2.js} +53 -6
  2. package/dist/chunk-EYGWSOX2.js.map +1 -0
  3. package/dist/chunk-FLRCYKBG.js +17 -0
  4. package/dist/chunk-FLRCYKBG.js.map +1 -0
  5. package/dist/index.d.ts +3 -3
  6. package/dist/index.js +2 -3
  7. package/dist/index.js.map +1 -1
  8. package/dist/preview-C2OcE4hp.d.ts +6 -0
  9. package/dist/preview.d.ts +2 -2
  10. package/dist/preview.js +3 -3
  11. package/dist/preview.js.map +1 -1
  12. package/dist/{context-BX71asjt.d.ts → types-CqNsMDsD.d.ts} +13 -1
  13. package/dist/vitest-setup.d.ts +20 -26
  14. package/dist/vitest-setup.js +42 -11
  15. package/dist/vitest-setup.js.map +1 -1
  16. package/package.json +2 -1
  17. package/readme.md +64 -78
  18. package/src/@vitest/browser/context.ts +3 -0
  19. package/src/expect.to_match_image_snapshot.spec.ts +9 -0
  20. package/src/expect.to_match_image_snapshot.ts +4 -3
  21. package/src/index.ts +1 -0
  22. package/src/param.stories.tsx +1 -0
  23. package/src/per_story.stories.tsx +1 -1
  24. package/src/preview.ts +2 -2
  25. package/src/should_take_snapshot.ts +13 -0
  26. package/src/state.ts +1 -0
  27. package/src/storybook/preview.ts +12 -4
  28. package/src/types.ts +11 -0
  29. package/src/vis.spec.ts +13 -0
  30. package/src/vis.ts +65 -0
  31. package/src/vitest-setup.spec.ts +0 -13
  32. package/src/vitest-setup.ts +4 -41
  33. package/dist/chunk-3NZUGVTR.js +0 -12
  34. package/dist/chunk-3NZUGVTR.js.map +0 -1
  35. package/dist/chunk-3ZUXLFHJ.js.map +0 -1
  36. package/dist/chunk-M4DV45WL.js +0 -48
  37. package/dist/chunk-M4DV45WL.js.map +0 -1
  38. package/dist/preview-BsZVLryd.d.ts +0 -5
  39. package/src/tags.spec.ts +0 -32
  40. package/src/tags.ts +0 -11
@@ -1,11 +1,53 @@
1
- import { imageSnapshotStubSymbol, state, imageSnapshotSymbol, isImageSnapshot, assertImageSnapshot } from './chunk-M4DV45WL.js';
2
1
  import { getByAltText, getByRole, getByTestId, getByText, getByTitle, getByPlaceholderText, getByLabelText } from '@storybook/test';
3
- import 'pathe';
2
+ import { join } from 'pathe';
4
3
  import { getCurrentTest } from 'vitest/suite';
5
4
  import dedent from 'dedent';
6
5
  import pixelmatch from 'pixelmatch';
7
6
  import { required } from 'type-plus';
8
7
 
8
+ // src/@vitest/browser/context.ts
9
+
10
+ // src/@vitest/browser/constants.ts
11
+ var imageSnapshotSymbol = Symbol.for("storybook-addon-vis/imageSnapshot");
12
+ var imageSnapshotStubSymbol = Symbol.for("storybook-addon-vis/imageSnapshotStub");
13
+
14
+ // src/@vitest/browser/image_snapshot.logic.ts
15
+ function isImageSnapshot(subject) {
16
+ return !!subject && subject.type === imageSnapshotSymbol;
17
+ }
18
+ function assertImageSnapshot(subject) {
19
+ if (!isImageSnapshot(subject)) {
20
+ throw new Error("Expected subject to be an image snapshot");
21
+ }
22
+ }
23
+ function toSnapshotId(taskName) {
24
+ return `${taskName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}`;
25
+ }
26
+
27
+ // src/state.ts
28
+ var state = {
29
+ name: "",
30
+ testFilepath: "",
31
+ taskName: "",
32
+ snapshot: {},
33
+ getSnapshotFilePaths(options) {
34
+ const test = getCurrentTest();
35
+ state.taskName = test.name;
36
+ const id = state.id = toSnapshotId(state.taskName);
37
+ state.snapshot[state.testFilepath][id] = state.snapshot[state.testFilepath][id] ?? { index: 1 };
38
+ const index = state.snapshot[state.testFilepath][state.id].index;
39
+ const snapshotFilename = options?.customizeSnapshotId ? `${options.customizeSnapshotId(state.id, index)}.png` : `${state.id}-${index}.png`;
40
+ const baselinePath = join(state.baselineDir, snapshotFilename);
41
+ const resultPath = join(state.resultDir, snapshotFilename);
42
+ const diffPath = join(state.diffDir, snapshotFilename);
43
+ return { snapshotFilename, baselinePath, resultPath, diffPath };
44
+ },
45
+ incrementSnapshotIndex() {
46
+ state.snapshot[state.testFilepath][state.id].index++;
47
+ }
48
+ };
49
+
50
+ // src/@vitest/browser/page.has_image_snapshot.ts
9
51
  async function hasImageSnapshot(options) {
10
52
  const { baselinePath } = state.getSnapshotFilePaths(options);
11
53
  return commands.existFile(baselinePath);
@@ -100,6 +142,9 @@ var page = new Proxy(
100
142
  if (prop === "imageSnapshot") {
101
143
  return { type: imageSnapshotStubSymbol };
102
144
  }
145
+ if (prop === "hasImageSnapshot") {
146
+ return false;
147
+ }
103
148
  console.info(`\`page.${prop.toString()}\` does not exist when running in browser`);
104
149
  };
105
150
  }
@@ -142,12 +187,13 @@ var createImageResizer = ({ width, height }) => (image) => {
142
187
  async function toMatchImageSnapshot(actual, options) {
143
188
  const promise = toMatchImageSnapshotInternal(actual, options);
144
189
  const test = getCurrentTest();
190
+ if (!test) return promise;
145
191
  test.promises ??= [];
146
192
  test.promises.push(promise);
147
193
  return promise;
148
194
  }
149
195
  async function toMatchImageSnapshotInternal(actual, options) {
150
- const subject = await actual;
196
+ let subject = await actual;
151
197
  if (subject?.type === imageSnapshotStubSymbol) {
152
198
  return success;
153
199
  }
@@ -166,6 +212,7 @@ async function toMatchImageSnapshotInternal(actual, options) {
166
212
  message: () => `\`toMatchImageSnapshot()\` expects the subject to be an element, locator, or result of \`page.imageSnapshot()\`, but got: \`${subject}\``
167
213
  };
168
214
  }
215
+ subject = await page.imageSnapshot({ element: subject });
169
216
  }
170
217
  assertImageSnapshot(subject);
171
218
  const baseline = await tryReadSnapshot(subject.baselinePath);
@@ -245,6 +292,6 @@ function toThresholdUnit({ failureThresholdType, width, height }, pixelDiff) {
245
292
  }
246
293
  }
247
294
 
248
- export { cdp, commands, page, server, toMatchImageSnapshot };
249
- //# sourceMappingURL=chunk-3ZUXLFHJ.js.map
250
- //# sourceMappingURL=chunk-3ZUXLFHJ.js.map
295
+ export { cdp, commands, page, server, state, toMatchImageSnapshot };
296
+ //# sourceMappingURL=chunk-EYGWSOX2.js.map
297
+ //# sourceMappingURL=chunk-EYGWSOX2.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":["ctx","getCurrentTest"],"mappings":"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","file":"chunk-EYGWSOX2.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 { join } from 'pathe'\nimport { getCurrentTest } from 'vitest/suite'\nimport { toSnapshotId } from './@vitest/browser/image_snapshot.logic'\nimport type { ImageSnapshotOptions } from './@vitest/browser/types'\nimport type { MatchImageSnapshotOptions } from './expect.to_match_image_snapshot'\n\nexport const state = {\n\tname: '',\n\ttestFilepath: '',\n\ttaskName: '',\n\tsnapshot: {},\n\tgetSnapshotFilePaths(options?: ImageSnapshotOptions | undefined) {\n\t\tconst test = getCurrentTest()\n\t\tstate.taskName = test.name\n\t\tconst id = (state.id = toSnapshotId(state.taskName))\n\t\tstate.snapshot[state.testFilepath][id] = state.snapshot[state.testFilepath][id] ?? { index: 1 }\n\n\t\tconst index = state.snapshot[state.testFilepath][state.id]!.index\n\t\tconst snapshotFilename = options?.customizeSnapshotId\n\t\t\t? `${options.customizeSnapshotId(state.id, index)}.png`\n\t\t\t: `${state.id}-${index}.png`\n\t\tconst baselinePath = join(state.baselineDir, snapshotFilename)\n\t\tconst resultPath = join(state.resultDir, snapshotFilename)\n\t\tconst diffPath = join(state.diffDir, snapshotFilename)\n\t\treturn { snapshotFilename, baselinePath, resultPath, diffPath }\n\t},\n\tincrementSnapshotIndex() {\n\t\tstate.snapshot[state.testFilepath][state.id]!.index++\n\t},\n} as unknown as {\n\tid: string\n\tname: string\n\ttestFilepath: string\n\ttestFilename: string\n\tprojectDir: string\n\tbaselineDir: string\n\tresultDir: string\n\tdiffDir: string\n\ttaskName: string\n\tparameters: {\n\t\tsnapshot?: MatchImageSnapshotOptions | undefined\n\t\t[key: string]: any\n\t}\n\ttags: string[]\n\tsnapshot: Record<string, Record<string, { index: number }>>\n\tgetSnapshotFilePaths(options?: ImageSnapshotOptions | undefined): {\n\t\tsnapshotFilename: string\n\t\tbaselinePath: string\n\t\tresultPath: string\n\t\tdiffPath: string\n\t}\n\tincrementSnapshotIndex(): void\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 { join } from 'pathe'\nimport { getCurrentTest } from 'vitest/suite'\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\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t\ttimeout: options?.timeout ?? 3000,\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 pixelmatch, { type PixelmatchOptions } from 'pixelmatch'\nimport { required } from 'type-plus'\nimport { getCurrentTest } from 'vitest/suite'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands, page, server } from './@vitest/browser/context.js'\nimport { assertImageSnapshot, isImageSnapshot, toSnapshotId } 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\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\toptions = required(state.parameters?.snapshot, options)\n\tconst { pass, diffAmount, diffImage } = compareImage(baselineImage, resultImage, options)\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.taskName}\\` 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 image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\t\t}\n\n\t\t\tExpected: '${subject.baselinePath}'\n\t\t\tActual: '${subject.resultPath}'\n\t\t\tDifference: '${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"]}
@@ -0,0 +1,17 @@
1
+ import { toMatchImageSnapshot, state } from './chunk-EYGWSOX2.js';
2
+ import { expect } from '@storybook/test';
3
+
4
+ var storybookPreviewVis = defineVisPreview();
5
+ function defineVisPreview() {
6
+ expect.extend({ toMatchImageSnapshot });
7
+ return {
8
+ beforeEach(ctx) {
9
+ state.tags = ctx.tags;
10
+ state.parameters = ctx.parameters;
11
+ }
12
+ };
13
+ }
14
+
15
+ export { defineVisPreview, storybookPreviewVis };
16
+ //# sourceMappingURL=chunk-FLRCYKBG.js.map
17
+ //# sourceMappingURL=chunk-FLRCYKBG.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/storybook/preview.ts"],"names":[],"mappings":";;;AAKO,IAAM,sBAAsB,gBAAiB;AAE7C,SAAS,gBAA8D,GAAA;AAC7E,EAAO,MAAA,CAAA,MAAA,CAAO,EAAE,oBAAA,EAAsB,CAAA;AACtC,EAAO,OAAA;AAAA,IACN,WAAW,GAAmB,EAAA;AAC7B,MAAA,KAAA,CAAM,OAAO,GAAI,CAAA,IAAA;AACjB,MAAA,KAAA,CAAM,aAAa,GAAI,CAAA,UAAA;AAAA;AACxB,GACD;AACD","file":"chunk-FLRCYKBG.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\nexport const storybookPreviewVis = defineVisPreview()\n\nexport function defineVisPreview<R extends Renderer>(): ProjectAnnotations<R> {\n\texpect.extend({ toMatchImageSnapshot })\n\treturn {\n\t\tbeforeEach(ctx: StoryContext) {\n\t\t\tstate.tags = ctx.tags\n\t\t\tstate.parameters = ctx.parameters\n\t\t},\n\t}\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { M as MatchImageSnapshotOptions } from './context-BX71asjt.js';
2
- export { C as CustomizeSnapshotIdOptions, d as ImageSnapshot, I as ImageSnapshotMatcher, b as ImageSnapshotOptions, c as cdp, a as commands, p as page, s as server, t as toMatchImageSnapshot } from './context-BX71asjt.js';
3
- export { v as visStorybookPreview } from './preview-BsZVLryd.js';
1
+ import { M as MatchImageSnapshotOptions } from './types-CqNsMDsD.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-CqNsMDsD.js';
3
+ export { d as defineVisPreview, s as storybookPreviewVis } from './preview-C2OcE4hp.js';
4
4
  import '@vitest/browser/context';
5
5
  import '@vitest/expect';
6
6
  import 'pixelmatch';
package/dist/index.js CHANGED
@@ -1,6 +1,5 @@
1
- export { visStorybookPreview } from './chunk-3NZUGVTR.js';
2
- export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-3ZUXLFHJ.js';
3
- import './chunk-M4DV45WL.js';
1
+ export { defineVisPreview, storybookPreviewVis } from './chunk-FLRCYKBG.js';
2
+ export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-EYGWSOX2.js';
4
3
 
5
4
  // src/param.ts
6
5
  function defineSnapshotParam(snapshot) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/param.ts"],"names":[],"mappings":";;;;;AAEO,SAAS,oBAAoB,QAAqC,EAAA;AACxE,EAAA,OAAO,EAAE,QAAS,EAAA;AACnB","file":"index.js","sourcesContent":["import type { MatchImageSnapshotOptions } from './expect.to_match_image_snapshot'\n\nexport function defineSnapshotParam(snapshot: MatchImageSnapshotOptions) {\n\treturn { snapshot }\n}\n"]}
1
+ {"version":3,"sources":["../src/param.ts"],"names":[],"mappings":";;;;AAEO,SAAS,oBAAoB,QAAqC,EAAA;AACxE,EAAA,OAAO,EAAE,QAAS,EAAA;AACnB","file":"index.js","sourcesContent":["import type { MatchImageSnapshotOptions } from './expect.to_match_image_snapshot'\n\nexport function defineSnapshotParam(snapshot: MatchImageSnapshotOptions) {\n\treturn { snapshot }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import { ProjectAnnotations, Renderer } from 'storybook/internal/types';
2
+
3
+ declare const storybookPreviewVis: ProjectAnnotations<Renderer>;
4
+ declare function defineVisPreview<R extends Renderer>(): ProjectAnnotations<R>;
5
+
6
+ export { defineVisPreview as d, storybookPreviewVis as s };
package/dist/preview.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { v as visStorybookPreview } from './preview-BsZVLryd.js';
1
+ import { s as storybookPreviewVis } from './preview-C2OcE4hp.js';
2
2
  import 'storybook/internal/types';
3
3
 
4
4
 
5
5
 
6
- export { visStorybookPreview as default };
6
+ export { storybookPreviewVis as default };
package/dist/preview.js CHANGED
@@ -1,8 +1,8 @@
1
- import { visStorybookPreview } from './chunk-3NZUGVTR.js';
2
- import './chunk-M4DV45WL.js';
1
+ import { storybookPreviewVis } from './chunk-FLRCYKBG.js';
2
+ import './chunk-EYGWSOX2.js';
3
3
 
4
4
  // src/preview.ts
5
- var preview_default = visStorybookPreview;
5
+ var preview_default = storybookPreviewVis;
6
6
 
7
7
  export { preview_default as default };
8
8
  //# sourceMappingURL=preview.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/preview.ts"],"names":[],"mappings":";;;;AAEA,IAAO,eAAQ,GAAA","file":"preview.js","sourcesContent":["import { visStorybookPreview } from './storybook/preview'\n\nexport default visStorybookPreview\n"]}
1
+ {"version":3,"sources":["../src/preview.ts"],"names":[],"mappings":";;;;AAEA,IAAO,eAAQ,GAAA","file":"preview.js","sourcesContent":["import { storybookPreviewVis } from './storybook/preview'\n\nexport default storybookPreviewVis\n"]}
@@ -129,4 +129,16 @@ declare const server: {
129
129
  config: SerializedConfig;
130
130
  };
131
131
 
132
- export { type CustomizeSnapshotIdOptions as C, type ImageSnapshotMatcher as I, type MatchImageSnapshotOptions as M, commands as a, type ImageSnapshotOptions as b, cdp as c, type ImageSnapshot as d, page as p, server as s, toMatchImageSnapshot as t };
132
+ /**
133
+ * The project parameters for the snapshot.
134
+ *
135
+ * These parameters are set per project in the `.storybook/preview.ts` file.
136
+ */
137
+ type VisOptions = {
138
+ /**
139
+ * The snapshot folder relative to the root of the project.
140
+ */
141
+ snapshotPath: string;
142
+ };
143
+
144
+ export { type CustomizeSnapshotIdOptions as C, type ImageSnapshotMatcher as I, type MatchImageSnapshotOptions as M, type VisOptions as V, commands as a, type ImageSnapshotOptions as b, cdp as c, type ImageSnapshot as d, page as p, server as s, toMatchImageSnapshot as t };
@@ -1,40 +1,34 @@
1
- export { I as ImageSnapshotMatcher, M as MatchImageSnapshotOptions, c as cdp, a as commands, p as page, s as server, t as toMatchImageSnapshot } from './context-BX71asjt.js';
1
+ import { V as VisOptions } from './types-CqNsMDsD.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-CqNsMDsD.js';
2
3
  import '@vitest/browser/context';
3
4
  import '@vitest/expect';
4
5
  import 'pixelmatch';
5
6
  import 'vitest';
6
7
 
7
- /**
8
- * Determine should snapshot be taken.
9
- *
10
- * not story: false
11
- * no `snapshot` tag: false
12
- * disabled by `!snapshot` tag: false
13
- */
14
- declare function shouldTakeSnapshot(ctx: {
15
- story?: {
16
- tags: string[];
8
+ declare function createVisConfig(options?: VisOptions): {
9
+ presets: {
10
+ basic(): void;
11
+ theme(themes: Record<string, () => void | Promise<void>>): void;
17
12
  };
18
- }): boolean;
19
-
20
- /**
21
- * The project parameters for the snapshot.
22
- *
23
- * These parameters are set per project in the `.storybook/preview.ts` file.
24
- */
25
- type VisOptions = {
26
- /**
27
- * The snapshot folder relative to the root of the project.
28
- */
29
- snapshotPath: string;
30
- };
31
- declare function setupVitestVis(options?: VisOptions): {
32
13
  beforeAll(suite: {
33
14
  file: {
34
15
  filepath: string;
35
16
  };
36
17
  name: string;
37
18
  }): Promise<void>;
19
+ afterEach: {
20
+ matchImageSnapshot(): Promise<void>;
21
+ matchPerTheme(themes: Record<string, () => Promise<void> | void>): () => Promise<void>;
22
+ };
38
23
  };
39
24
 
40
- export { type VisOptions, setupVitestVis, shouldTakeSnapshot };
25
+ /**
26
+ * Determine should snapshot be taken.
27
+ *
28
+ * not story: false
29
+ * no `snapshot` tag: false
30
+ * disabled by `!snapshot` tag: false
31
+ */
32
+ declare function shouldTakeSnapshot(): boolean;
33
+
34
+ export { createVisConfig, shouldTakeSnapshot };
@@ -1,17 +1,28 @@
1
- import { commands } from './chunk-3ZUXLFHJ.js';
2
- export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-3ZUXLFHJ.js';
3
- import { state } from './chunk-M4DV45WL.js';
1
+ import { state, toMatchImageSnapshot, commands, page } from './chunk-EYGWSOX2.js';
2
+ export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-EYGWSOX2.js';
4
3
  import { basename, join, dirname, relative } from 'pathe';
4
+ import { expect } from 'vitest';
5
5
 
6
- // src/tags.ts
7
- function shouldTakeSnapshot(ctx) {
8
- if (!ctx.story?.tags) return false;
9
- return ctx.story.tags.lastIndexOf("!snapshot") < ctx.story.tags.lastIndexOf("snapshot");
6
+ // src/should_take_snapshot.ts
7
+ function shouldTakeSnapshot() {
8
+ if (!state.tags) return false;
9
+ return state.tags.lastIndexOf("!snapshot") < state.tags.lastIndexOf("snapshot");
10
10
  }
11
11
 
12
- // src/vitest-setup.ts
13
- function setupVitestVis(options) {
14
- return {
12
+ // src/vis.ts
13
+ function createVisConfig(options) {
14
+ expect.extend({ toMatchImageSnapshot });
15
+ const h = {
16
+ presets: {
17
+ basic() {
18
+ beforeAll(h.beforeAll);
19
+ afterEach(h.afterEach.matchImageSnapshot);
20
+ },
21
+ theme(themes) {
22
+ beforeAll(h.beforeAll);
23
+ afterEach(h.afterEach.matchPerTheme(themes));
24
+ }
25
+ },
15
26
  async beforeAll(suite) {
16
27
  state.name = suite.name;
17
28
  state.testFilepath = suite.file.filepath;
@@ -30,10 +41,30 @@ function setupVitestVis(options) {
30
41
  await commands.rmDir(state.resultDir);
31
42
  await commands.rmDir(state.diffDir);
32
43
  }
44
+ },
45
+ afterEach: {
46
+ async matchImageSnapshot() {
47
+ if (!shouldTakeSnapshot()) return;
48
+ const r = await page.imageSnapshot();
49
+ expect(r).toMatchImageSnapshot();
50
+ },
51
+ matchPerTheme(themes) {
52
+ return async function matchImageSnapshot() {
53
+ if (!shouldTakeSnapshot()) return;
54
+ for (const themeId in themes) {
55
+ await themes[themeId]();
56
+ const r = await page.imageSnapshot({
57
+ customizeSnapshotId: (id) => `${id}-${themeId}`
58
+ });
59
+ await expect(r).toMatchImageSnapshot();
60
+ }
61
+ };
62
+ }
33
63
  }
34
64
  };
65
+ return h;
35
66
  }
36
67
 
37
- export { setupVitestVis, shouldTakeSnapshot };
68
+ export { createVisConfig, shouldTakeSnapshot };
38
69
  //# sourceMappingURL=vitest-setup.js.map
39
70
  //# sourceMappingURL=vitest-setup.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tags.ts","../src/vitest-setup.ts"],"names":[],"mappings":";;;;;;AAOO,SAAS,mBAAmB,GAAqC,EAAA;AACvE,EAAA,IAAI,CAAC,GAAA,CAAI,KAAO,EAAA,IAAA,EAAa,OAAA,KAAA;AAC7B,EAAO,OAAA,GAAA,CAAI,KAAM,CAAA,IAAA,CAAK,WAAY,CAAA,WAAW,IAAI,GAAI,CAAA,KAAA,CAAM,IAAK,CAAA,WAAA,CAAY,UAAU,CAAA;AACvF;;;ACWO,SAAS,eAAe,OAAsB,EAAA;AACpD,EAAO,OAAA;AAAA,IACN,MAAM,UAAU,KAAqD,EAAA;AACpE,MAAA,KAAA,CAAM,OAAO,KAAM,CAAA,IAAA;AACnB,MAAM,KAAA,CAAA,YAAA,GAAe,MAAM,IAAK,CAAA,QAAA;AAChC,MAAM,KAAA,CAAA,YAAA,GAAe,QAAS,CAAA,KAAA,CAAM,YAAY,CAAA;AAChD,MAAM,KAAA,CAAA,UAAA,GAAa,MAAM,YAAa,CAAA,KAAA,CAAM,GAAG,CAAC,KAAA,CAAM,KAAK,MAAM,CAAA;AACjE,MAAA,MAAM,YAAe,GAAA,IAAA;AAAA,QACpB,KAAM,CAAA,UAAA;AAAA,QACN,SAAS,YAAgB,IAAA,CAAA,cAAA,EAAiB,MAAM,QAAA,CAAS,qBAAqB,CAAA;AAAA,OAC/E;AACA,MAAM,MAAA,UAAA,GAAa,OAAQ,CAAA,KAAA,CAAM,YAAY,CAAA;AAC7C,MAAA,KAAA,CAAM,cAAc,QAAS,CAAA,UAAA,EAAY,KAAK,YAAc,EAAA,KAAA,CAAM,YAAY,CAAC,CAAA;AAC/E,MAAM,KAAA,CAAA,SAAA,GAAY,SAAS,UAAY,EAAA,IAAA,CAAK,cAAc,aAAe,EAAA,KAAA,CAAM,YAAY,CAAC,CAAA;AAC5F,MAAM,KAAA,CAAA,OAAA,GAAU,SAAS,UAAY,EAAA,IAAA,CAAK,cAAc,iBAAmB,EAAA,KAAA,CAAM,YAAY,CAAC,CAAA;AAE9F,MAAA,IAAI,CAAC,KAAA,CAAM,QAAS,CAAA,KAAA,CAAM,YAAY,CAAG,EAAA;AACxC,QAAA,KAAA,CAAM,QAAS,CAAA,KAAA,CAAM,YAAY,CAAA,GAAI,EAAC;AACtC,QAAM,MAAA,QAAA,CAAS,KAAM,CAAA,KAAA,CAAM,SAAS,CAAA;AACpC,QAAM,MAAA,QAAA,CAAS,KAAM,CAAA,KAAA,CAAM,OAAO,CAAA;AAAA;AACnC;AACD,GACD;AACD","file":"vitest-setup.js","sourcesContent":["/**\n * Determine should snapshot be taken.\n *\n * not story: false\n * no `snapshot` tag: false\n * disabled by `!snapshot` tag: false\n */\nexport function shouldTakeSnapshot(ctx: { story?: { tags: string[] } }) {\n\tif (!ctx.story?.tags) return false\n\treturn ctx.story.tags.lastIndexOf('!snapshot') < ctx.story.tags.lastIndexOf('snapshot')\n}\n","import { basename, dirname, join, relative } from 'pathe'\nimport { commands } from './@vitest/browser/context.js'\nimport './augment.js'\nimport { state } from './state.js'\n\nexport * from './@vitest/browser/context.js'\nexport * from './expect.to_match_image_snapshot.js'\nexport * from './tags.js'\n\n/**\n * The project parameters for the snapshot.\n *\n * These parameters are set per project in the `.storybook/preview.ts` file.\n */\nexport type VisOptions = {\n\t/**\n\t * The snapshot folder relative to the root of the project.\n\t */\n\tsnapshotPath: string\n}\n\nexport function setupVitestVis(options?: VisOptions) {\n\treturn {\n\t\tasync beforeAll(suite: { file: { filepath: string }; name: string }) {\n\t\t\tstate.name = suite.name\n\t\t\tstate.testFilepath = suite.file.filepath\n\t\t\tstate.testFilename = basename(state.testFilepath)\n\t\t\tstate.projectDir = state.testFilepath.slice(0, -state.name.length)\n\t\t\tconst snapshotPath = join(\n\t\t\t\tstate.projectDir,\n\t\t\t\toptions?.snapshotPath ?? `__snapshots__/${await commands.getSnapshotPlatform()}`,\n\t\t\t)\n\t\t\tconst currentDir = dirname(state.testFilepath)\n\t\t\tstate.baselineDir = relative(currentDir, join(snapshotPath, state.testFilename))\n\t\t\tstate.resultDir = relative(currentDir, join(snapshotPath, '__results__', state.testFilename))\n\t\t\tstate.diffDir = relative(currentDir, join(snapshotPath, '__diff_output__', state.testFilename))\n\n\t\t\tif (!state.snapshot[state.testFilepath]) {\n\t\t\t\tstate.snapshot[state.testFilepath] = {}\n\t\t\t\tawait commands.rmDir(state.resultDir)\n\t\t\t\tawait commands.rmDir(state.diffDir)\n\t\t\t}\n\t\t},\n\t}\n}\n"]}
1
+ {"version":3,"sources":["../src/should_take_snapshot.ts","../src/vis.ts"],"names":[],"mappings":"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","file":"vitest-setup.js","sourcesContent":["import { state } from './state'\n\n/**\n * Determine should snapshot be taken.\n *\n * not story: false\n * no `snapshot` tag: false\n * disabled by `!snapshot` tag: false\n */\nexport function shouldTakeSnapshot() {\n\tif (!state.tags) return false\n\treturn state.tags.lastIndexOf('!snapshot') < state.tags.lastIndexOf('snapshot')\n}\n","import { basename, dirname, join, relative } from 'pathe'\nimport { expect } from 'vitest'\nimport { commands, page } from './@vitest/browser/context.js'\nimport './augment.js'\nimport { toMatchImageSnapshot } from './expect.to_match_image_snapshot.js'\nimport { shouldTakeSnapshot } from './should_take_snapshot.js'\nimport { state } from './state.js'\nimport type { VisOptions } from './types.js'\n\nexport function createVisConfig(options?: VisOptions) {\n\texpect.extend({ toMatchImageSnapshot })\n\n\tconst h = {\n\t\tpresets: {\n\t\t\tbasic() {\n\t\t\t\tbeforeAll(h.beforeAll)\n\t\t\t\tafterEach(h.afterEach.matchImageSnapshot)\n\t\t\t},\n\t\t\ttheme(themes: Record<string, () => void | Promise<void>>) {\n\t\t\t\tbeforeAll(h.beforeAll)\n\t\t\t\tafterEach(h.afterEach.matchPerTheme(themes))\n\t\t\t},\n\t\t},\n\t\tasync beforeAll(suite: { file: { filepath: string }; name: string }) {\n\t\t\tstate.name = suite.name\n\t\t\tstate.testFilepath = suite.file.filepath\n\t\t\tstate.testFilename = basename(state.testFilepath)\n\t\t\tstate.projectDir = state.testFilepath.slice(0, -state.name.length)\n\t\t\tconst snapshotPath = join(\n\t\t\t\tstate.projectDir,\n\t\t\t\toptions?.snapshotPath ?? `__snapshots__/${await commands.getSnapshotPlatform()}`,\n\t\t\t)\n\t\t\tconst currentDir = dirname(state.testFilepath)\n\t\t\tstate.baselineDir = relative(currentDir, join(snapshotPath, state.testFilename))\n\t\t\tstate.resultDir = relative(currentDir, join(snapshotPath, '__results__', state.testFilename))\n\t\t\tstate.diffDir = relative(currentDir, join(snapshotPath, '__diff_output__', state.testFilename))\n\n\t\t\tif (!state.snapshot[state.testFilepath]) {\n\t\t\t\tstate.snapshot[state.testFilepath] = {}\n\t\t\t\tawait commands.rmDir(state.resultDir)\n\t\t\t\tawait commands.rmDir(state.diffDir)\n\t\t\t}\n\t\t},\n\t\tafterEach: {\n\t\t\tasync matchImageSnapshot() {\n\t\t\t\tif (!shouldTakeSnapshot()) return\n\t\t\t\tconst r = await page.imageSnapshot()\n\t\t\t\texpect(r).toMatchImageSnapshot()\n\t\t\t},\n\t\t\tmatchPerTheme(themes: Record<string, () => Promise<void> | void>) {\n\t\t\t\treturn async function matchImageSnapshot() {\n\t\t\t\t\tif (!shouldTakeSnapshot()) return\n\t\t\t\t\tfor (const themeId in themes) {\n\t\t\t\t\t\tawait themes[themeId]()\n\t\t\t\t\t\tconst r = await page.imageSnapshot({\n\t\t\t\t\t\t\tcustomizeSnapshotId: (id) => `${id}-${themeId}`,\n\t\t\t\t\t\t})\n\t\t\t\t\t\tawait expect(r).toMatchImageSnapshot()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t},\n\t}\n\treturn h\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storybook-addon-vis",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Storybook Vitest Image Snapshot addon",
5
5
  "keywords": [
6
6
  "image-snapshot",
@@ -57,6 +57,7 @@
57
57
  "@storybook/react": "^8.4.2",
58
58
  "@storybook/react-vite": "^8.4.2",
59
59
  "@storybook/test": "^8.4.2",
60
+ "@testing-library/react": "^16.0.1",
60
61
  "@types/is-ci": "^3.0.4",
61
62
  "@types/pixelmatch": "^5.2.6",
62
63
  "@types/react": "^18.2.65",
package/readme.md CHANGED
@@ -37,66 +37,43 @@ 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
+ setupFiles: ['./vitest.setup.ts'],
49
+ }
50
50
  })
51
51
  ```
52
52
 
53
- Also note that you need to add `vitest.setup.ts` file to set up the test environment.
54
- You need to do that anyway when you set up [storybook] with [vitest].
55
-
56
- In `vitest.setup.ts`, you need to extend [vitest] `expect` and register lifecycle hooks so that [storybook-addon-vis] can access the test context info to capture image snapshot.
53
+ In `vitest.setup.ts`, you can use one of the presets to do the setup for you.
54
+ You can also use the provided hooks to set up the test environment manually.
57
55
 
58
56
  ```ts
59
57
  // vitest.setup.ts
60
- import { type StoryContext, setProjectAnnotations } from '@storybook/react'
61
- import {
62
- page,
63
- setupVis,
64
- shouldTakeSnapshot,
65
- toMatchImageSnapshot,
66
- } from 'storybook-addon-vis/vitest-setup'
67
- import { afterEach, beforeAll, beforeEach, expect } from 'vitest'
58
+ import { setProjectAnnotations } from '@storybook/react'
59
+ import { createVisConfig } from 'storybook-addon-vis/vitest-setup'
68
60
  import * as projectAnnotations from './preview'
69
61
 
70
62
  const project = setProjectAnnotations([projectAnnotations])
71
- const vis = setupVis(/* options */)
72
-
73
- expect.extend({ toMatchImageSnapshot })
74
-
75
- beforeAll(async (suite) => {
76
- project.beforeAll()
77
- await vis.beforeAll(suite)
78
- })
79
63
 
80
- afterEach<{ story?: StoryContext }>(async (ctx) => {
81
- if (!shouldTakeSnapshot(ctx)) return
82
-
83
- expect(page.imageSnapshot()).toMatchImageSnapshot()
84
- })
64
+ // other presets are available
65
+ createVisConfig(/* options */).presets.basic()
85
66
  ```
86
67
 
87
- On [storybook], you need to extend the `expect` from `@storybook/test`,
88
- and register `beforeEach` hook to set up the test environment.
68
+ On [storybook], you need to register `beforeEach` hook to set up the test environment.
89
69
 
90
70
  ```ts
91
71
  // .storybook/preview.tsx
92
- import { expect } from '@storybook/test'
93
- import { toMatchImageSnapshot, visStorybookPreview } from 'storybook-addon-vis'
94
-
95
- expect.extend({ toMatchImageSnapshot })
72
+ import { storybookPreviewVis } from 'storybook-addon-vis'
96
73
 
97
74
  const preview: Preview = {
98
75
  // ...
99
- beforeEach: visStorybookPreview.beforeEach,
76
+ beforeEach: storybookPreviewVis.beforeEach,
100
77
  }
101
78
 
102
79
  export default preview
@@ -104,53 +81,58 @@ export default preview
104
81
 
105
82
  ## Usage - automatic snapshot
106
83
 
107
- The `afterEach` hook above is how to capture the image snapshot automatically,
108
- when you add a `snapshot` tag to the story.
84
+ With the `basic` preset, [storybook-addon-vis] automatically captures image snapshot for stories with `snapshot` tag.
85
+
86
+ As with how tags work in [storybook], you can add the tag globally, per story file, or per story.
109
87
 
110
88
  ```ts
89
+ // .storybook/preview.tsx
90
+ export default {
91
+ // Enable image snapshot for all stories
92
+ tags: ['snapshot']
93
+ }
94
+
111
95
  // story.tsx
112
96
  export default {
113
- title: 'Button',
114
- // Take image snapshot automatically for all stories in this file
115
- tags: ['snapshot']
97
+ title: 'Button',
98
+ // Take image snapshot automatically for all stories in this file
99
+ tags: ['snapshot']
116
100
  }
117
101
 
118
102
  export const MyStory = {
119
- // Take image snapshot automatically for this story
120
- tags: ['snapshot'],
121
- // ...
103
+ // Take image snapshot automatically for this story
104
+ tags: ['snapshot'],
105
+ // ...
122
106
  }
123
107
  ```
124
108
 
125
- You can provide options to the automatic `toMatchImageSnapshot` matcher using parameters.
109
+ You can disable snapshot capturing with the `!snapshot` tag,
110
+ much like `!test`.
126
111
 
127
112
  ```ts
128
- import { defineSnapshotParam } from 'storybook-addon-vis'
113
+ export default {
114
+ title: 'Button',
115
+ // Enable image snapshot for all stories in this file
116
+ tags: ['snapshot']
117
+ }
129
118
 
130
119
  export const MyStory = {
131
- // Take image snapshot automatically for this story
132
- tags: ['snapshot'],
133
- parameters: defineSnapshotParam({
134
- failureThreshold: 70,
135
- })
136
- // ...
120
+ // Disable image snapshot for this story
121
+ tags: ['!snapshot'],
122
+ // ...
137
123
  }
138
124
  ```
139
125
 
140
- You can disable snapshot capturing with the `!snapshot` tag,
141
- much like `!test`.
126
+ You can provide options to the `toMatchImageSnapshot` matcher using parameters.
142
127
 
143
128
  ```ts
144
- export default {
145
- title: 'Button',
146
- // Enable image snapshot for all stories in this file
147
- tags: ['snapshot']
148
- }
129
+ import { defineSnapshotParam } from 'storybook-addon-vis'
149
130
 
150
131
  export const MyStory = {
151
- // Disable image snapshot for this story
152
- tags: ['!snapshot'],
153
- // ...
132
+ parameters: defineSnapshotParam({
133
+ failureThreshold: 70,
134
+ })
135
+ // ...
154
136
  }
155
137
  ```
156
138
 
@@ -161,22 +143,26 @@ Besides automatic snapshot, you can capture image snapshot manually.
161
143
  ```ts
162
144
  import { expect } from '@storybook/test'
163
145
 
164
- // `page` and the like are proxy to `@vitest/browser/context` to work in storybook
146
+ // `page` and the like are proxies of `@vitest/browser/context` to work within storybook
165
147
  import { page } from 'storybook-addon-vis'
166
148
 
167
149
  export const PageSnapshot = {
168
- // ...
169
- async play() {
170
- await expect(page.imageSnapshot()).toMatchImageSnapshot()
171
- }
150
+ // typically you want to disable automatic snapshot when using manual snapshot
151
+ tags: ['!snapshot'],
152
+ // ...
153
+ async play() {
154
+ await expect(page.imageSnapshot()).toMatchImageSnapshot()
155
+ }
172
156
  }
173
157
 
174
158
  export const ElementSnapshot = {
175
- // ...
176
- async play({ canvas }) {
177
- const element = await canvas.getByTestid('subject')
178
- await expect(page.imageSnapshot({ element })).toMatchImageSnapshot()
179
- }
159
+ // typically you want to disable automatic snapshot when using manual snapshot
160
+ tags: ['!snapshot'],
161
+ // ...
162
+ async play({ canvas }) {
163
+ const element = await canvas.getByTestid('subject')
164
+ await expect(page.imageSnapshot({ element })).toMatchImageSnapshot()
165
+ }
180
166
  }
181
167
  ```
182
168
 
@@ -52,6 +52,9 @@ export const page = new Proxy<BrowserPage>(
52
52
  if (prop === 'imageSnapshot') {
53
53
  return { type: imageSnapshotStubSymbol }
54
54
  }
55
+ if (prop === 'hasImageSnapshot') {
56
+ return false
57
+ }
55
58
  console.info(`\`page.${prop.toString()}\` does not exist when running in browser`)
56
59
  }
57
60
  },
@@ -1,13 +1,16 @@
1
1
  import { composeStories } from '@storybook/react'
2
+ import { screen } from '@testing-library/react'
2
3
  import { assertType } from 'type-plus'
3
4
  import { expect, it } from 'vitest'
4
5
  import { page } from './@vitest/browser/context.js'
5
6
  import { toSnapshotId } from './@vitest/browser/image_snapshot.logic.js'
6
7
  import * as ToMatchStories from './expect.to_match_image_snapshot.stories.js'
7
8
  import * as ImageDataStories from './image_data.stories.js'
9
+ import * as PerStory from './per_story.stories.js'
8
10
 
9
11
  const { ConversionRoundtrip } = composeStories(ImageDataStories)
10
12
  const { Success } = composeStories(ToMatchStories)
13
+ const { TakeSnapshot } = composeStories(PerStory)
11
14
 
12
15
  it('should reject if the subject is undefined', async () => {
13
16
  expect(() => expect(undefined).toMatchImageSnapshot()).rejects.toThrowError(
@@ -94,3 +97,9 @@ it(`should fail with 'Snapshot \`{test/story name}\` mismatched`, async ({ task
94
97
  ).toMatchImageSnapshot()
95
98
  }
96
99
  })
100
+
101
+ test('use screen to get element', async () => {
102
+ await TakeSnapshot.run()
103
+ const subject = screen.getByTestId('subject')
104
+ await expect(subject).toMatchImageSnapshot()
105
+ })
@@ -4,7 +4,7 @@ import pixelmatch, { type PixelmatchOptions } from 'pixelmatch'
4
4
  import { required } from 'type-plus'
5
5
  import { getCurrentTest } from 'vitest/suite'
6
6
  import { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'
7
- import { commands, server } from './@vitest/browser/context.js'
7
+ import { commands, page, server } from './@vitest/browser/context.js'
8
8
  import { assertImageSnapshot, isImageSnapshot, toSnapshotId } from './@vitest/browser/image_snapshot.logic.js'
9
9
  import { toDataURL, toImageData } from './image_data.js'
10
10
  import { createImageResizer } from './image_resizer.js'
@@ -34,6 +34,7 @@ export async function toMatchImageSnapshot(
34
34
  ): AsyncExpectationResult {
35
35
  const promise = toMatchImageSnapshotInternal(actual, options)
36
36
  const test = getCurrentTest()
37
+ if (!test) return promise
37
38
  test.promises ??= []
38
39
  test.promises.push(promise)
39
40
  return promise
@@ -43,7 +44,7 @@ async function toMatchImageSnapshotInternal(
43
44
  actual: any,
44
45
  options?: MatchImageSnapshotOptions | undefined,
45
46
  ): AsyncExpectationResult {
46
- const subject = await actual
47
+ let subject = await actual
47
48
  if (subject?.type === imageSnapshotStubSymbol) {
48
49
  return success
49
50
  }
@@ -65,7 +66,7 @@ async function toMatchImageSnapshotInternal(
65
66
  `\`toMatchImageSnapshot()\` expects the subject to be an element, locator, or result of \`page.imageSnapshot()\`, but got: \`${subject}\``,
66
67
  }
67
68
  }
68
- // subject = await page.imageSnapshot({ element: actual })
69
+ subject = await page.imageSnapshot({ element: subject })
69
70
  }
70
71
 
71
72
  assertImageSnapshot(subject)
package/src/index.ts CHANGED
@@ -5,3 +5,4 @@ export * from './@vitest/browser/context.js'
5
5
  export * from './@vitest/browser/types.js'
6
6
  export * from './param.js'
7
7
  export * from './storybook/preview.js'
8
+ export * from './types.js'
@@ -1,4 +1,5 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react'
2
+ import { expect } from '@storybook/test'
2
3
  import { defineSnapshotParam, page } from './index.js'
3
4
 
4
5
  export default {
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'
2
2
 
3
3
  export default {
4
4
  title: 'per story',
5
- render: () => <>unit test</>,
5
+ render: () => <div data-testid="subject">unit test</div>,
6
6
  } satisfies Meta
7
7
 
8
8
  export const TakeSnapshot: StoryObj = {
package/src/preview.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { visStorybookPreview } from './storybook/preview'
1
+ import { storybookPreviewVis } from './storybook/preview'
2
2
 
3
- export default visStorybookPreview
3
+ export default storybookPreviewVis
@@ -0,0 +1,13 @@
1
+ import { state } from './state'
2
+
3
+ /**
4
+ * Determine should snapshot be taken.
5
+ *
6
+ * not story: false
7
+ * no `snapshot` tag: false
8
+ * disabled by `!snapshot` tag: false
9
+ */
10
+ export function shouldTakeSnapshot() {
11
+ if (!state.tags) return false
12
+ return state.tags.lastIndexOf('!snapshot') < state.tags.lastIndexOf('snapshot')
13
+ }
package/src/state.ts CHANGED
@@ -41,6 +41,7 @@ export const state = {
41
41
  snapshot?: MatchImageSnapshotOptions | undefined
42
42
  [key: string]: any
43
43
  }
44
+ tags: string[]
44
45
  snapshot: Record<string, Record<string, { index: number }>>
45
46
  getSnapshotFilePaths(options?: ImageSnapshotOptions | undefined): {
46
47
  snapshotFilename: string
@@ -1,8 +1,16 @@
1
+ import { expect } from '@storybook/test'
1
2
  import type { ProjectAnnotations, Renderer, StoryContext } from 'storybook/internal/types'
3
+ import { toMatchImageSnapshot } from '../expect.to_match_image_snapshot'
2
4
  import { state } from '../state'
3
5
 
4
- export const visStorybookPreview: ProjectAnnotations<Renderer> = {
5
- beforeEach(ctx: StoryContext) {
6
- state.parameters = ctx.parameters
7
- },
6
+ export const storybookPreviewVis = defineVisPreview()
7
+
8
+ export function defineVisPreview<R extends Renderer>(): ProjectAnnotations<R> {
9
+ expect.extend({ toMatchImageSnapshot })
10
+ return {
11
+ beforeEach(ctx: StoryContext) {
12
+ state.tags = ctx.tags
13
+ state.parameters = ctx.parameters
14
+ },
15
+ }
8
16
  }
package/src/types.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The project parameters for the snapshot.
3
+ *
4
+ * These parameters are set per project in the `.storybook/preview.ts` file.
5
+ */
6
+ export type VisOptions = {
7
+ /**
8
+ * The snapshot folder relative to the root of the project.
9
+ */
10
+ snapshotPath: string
11
+ }
@@ -0,0 +1,13 @@
1
+ import { state } from './state.js'
2
+ import { createVisConfig } from './vis.js'
3
+
4
+ it('can define the snapshot root folder relative to the root of the project', async () => {
5
+ await createVisConfig({
6
+ snapshotPath: '_sp_',
7
+ }).beforeAll({ name: 'some.test.ts', file: { filepath: 'dummy/some.test.ts' } }),
8
+ expect(state).toMatchObject({
9
+ baselineDir: '_sp_/some.test.ts',
10
+ diffDir: '_sp_/__diff_output__/some.test.ts',
11
+ resultDir: '_sp_/__results__/some.test.ts',
12
+ })
13
+ })
package/src/vis.ts ADDED
@@ -0,0 +1,65 @@
1
+ import { basename, dirname, join, relative } from 'pathe'
2
+ import { expect } from 'vitest'
3
+ import { commands, page } from './@vitest/browser/context.js'
4
+ import './augment.js'
5
+ import { toMatchImageSnapshot } from './expect.to_match_image_snapshot.js'
6
+ import { shouldTakeSnapshot } from './should_take_snapshot.js'
7
+ import { state } from './state.js'
8
+ import type { VisOptions } from './types.js'
9
+
10
+ export function createVisConfig(options?: VisOptions) {
11
+ expect.extend({ toMatchImageSnapshot })
12
+
13
+ const h = {
14
+ presets: {
15
+ basic() {
16
+ beforeAll(h.beforeAll)
17
+ afterEach(h.afterEach.matchImageSnapshot)
18
+ },
19
+ theme(themes: Record<string, () => void | Promise<void>>) {
20
+ beforeAll(h.beforeAll)
21
+ afterEach(h.afterEach.matchPerTheme(themes))
22
+ },
23
+ },
24
+ async beforeAll(suite: { file: { filepath: string }; name: string }) {
25
+ state.name = suite.name
26
+ state.testFilepath = suite.file.filepath
27
+ state.testFilename = basename(state.testFilepath)
28
+ state.projectDir = state.testFilepath.slice(0, -state.name.length)
29
+ const snapshotPath = join(
30
+ state.projectDir,
31
+ options?.snapshotPath ?? `__snapshots__/${await commands.getSnapshotPlatform()}`,
32
+ )
33
+ const currentDir = dirname(state.testFilepath)
34
+ state.baselineDir = relative(currentDir, join(snapshotPath, state.testFilename))
35
+ state.resultDir = relative(currentDir, join(snapshotPath, '__results__', state.testFilename))
36
+ state.diffDir = relative(currentDir, join(snapshotPath, '__diff_output__', state.testFilename))
37
+
38
+ if (!state.snapshot[state.testFilepath]) {
39
+ state.snapshot[state.testFilepath] = {}
40
+ await commands.rmDir(state.resultDir)
41
+ await commands.rmDir(state.diffDir)
42
+ }
43
+ },
44
+ afterEach: {
45
+ async matchImageSnapshot() {
46
+ if (!shouldTakeSnapshot()) return
47
+ const r = await page.imageSnapshot()
48
+ expect(r).toMatchImageSnapshot()
49
+ },
50
+ matchPerTheme(themes: Record<string, () => Promise<void> | void>) {
51
+ return async function matchImageSnapshot() {
52
+ if (!shouldTakeSnapshot()) return
53
+ for (const themeId in themes) {
54
+ await themes[themeId]()
55
+ const r = await page.imageSnapshot({
56
+ customizeSnapshotId: (id) => `${id}-${themeId}`,
57
+ })
58
+ await expect(r).toMatchImageSnapshot()
59
+ }
60
+ }
61
+ },
62
+ },
63
+ }
64
+ return h
65
+ }
@@ -3,8 +3,6 @@ import { basename } from 'pathe'
3
3
  import { expect, it } from 'vitest'
4
4
  import { commands, page } from './@vitest/browser/context.js'
5
5
  import * as ImageDataStories from './image_data.stories.js'
6
- import { state } from './state.js'
7
- import { setupVitestVis } from './vitest-setup.js'
8
6
 
9
7
  const { ConversionRoundtrip } = composeStories(ImageDataStories)
10
8
 
@@ -19,14 +17,3 @@ it('should delete the results and diffs folder when the during `beforeAll`', asy
19
17
  await ConversionRoundtrip.run()
20
18
  await expect(page.imageSnapshot()).toMatchImageSnapshot()
21
19
  })
22
-
23
- it('can define the snapshot root folder relative to the root of the project', async () => {
24
- await setupVitestVis({
25
- snapshotPath: '_sp_',
26
- }).beforeAll({ name: 'some.test.ts', file: { filepath: 'dummy/some.test.ts' } }),
27
- expect(state).toMatchObject({
28
- baselineDir: '_sp_/some.test.ts',
29
- diffDir: '_sp_/__diff_output__/some.test.ts',
30
- resultDir: '_sp_/__results__/some.test.ts',
31
- })
32
- })
@@ -1,45 +1,8 @@
1
- import { basename, dirname, join, relative } from 'pathe'
2
- import { commands } from './@vitest/browser/context.js'
3
1
  import './augment.js'
4
- import { state } from './state.js'
5
2
 
3
+ export * from './vis.js'
6
4
  export * from './@vitest/browser/context.js'
7
- export * from './expect.to_match_image_snapshot.js'
8
- export * from './tags.js'
9
-
10
- /**
11
- * The project parameters for the snapshot.
12
- *
13
- * These parameters are set per project in the `.storybook/preview.ts` file.
14
- */
15
- export type VisOptions = {
16
- /**
17
- * The snapshot folder relative to the root of the project.
18
- */
19
- snapshotPath: string
20
- }
5
+ export * from './should_take_snapshot.js'
21
6
 
22
- export function setupVitestVis(options?: VisOptions) {
23
- return {
24
- async beforeAll(suite: { file: { filepath: string }; name: string }) {
25
- state.name = suite.name
26
- state.testFilepath = suite.file.filepath
27
- state.testFilename = basename(state.testFilepath)
28
- state.projectDir = state.testFilepath.slice(0, -state.name.length)
29
- const snapshotPath = join(
30
- state.projectDir,
31
- options?.snapshotPath ?? `__snapshots__/${await commands.getSnapshotPlatform()}`,
32
- )
33
- const currentDir = dirname(state.testFilepath)
34
- state.baselineDir = relative(currentDir, join(snapshotPath, state.testFilename))
35
- state.resultDir = relative(currentDir, join(snapshotPath, '__results__', state.testFilename))
36
- state.diffDir = relative(currentDir, join(snapshotPath, '__diff_output__', state.testFilename))
37
-
38
- if (!state.snapshot[state.testFilepath]) {
39
- state.snapshot[state.testFilepath] = {}
40
- await commands.rmDir(state.resultDir)
41
- await commands.rmDir(state.diffDir)
42
- }
43
- },
44
- }
45
- }
7
+ // may not be needed
8
+ export * from './expect.to_match_image_snapshot.js'
@@ -1,12 +0,0 @@
1
- import { state } from './chunk-M4DV45WL.js';
2
-
3
- // src/storybook/preview.ts
4
- var visStorybookPreview = {
5
- beforeEach(ctx) {
6
- state.parameters = ctx.parameters;
7
- }
8
- };
9
-
10
- export { visStorybookPreview };
11
- //# sourceMappingURL=chunk-3NZUGVTR.js.map
12
- //# sourceMappingURL=chunk-3NZUGVTR.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/storybook/preview.ts"],"names":[],"mappings":";;;AAGO,IAAM,mBAAoD,GAAA;AAAA,EAChE,WAAW,GAAmB,EAAA;AAC7B,IAAA,KAAA,CAAM,aAAa,GAAI,CAAA,UAAA;AAAA;AAEzB","file":"chunk-3NZUGVTR.js","sourcesContent":["import type { ProjectAnnotations, Renderer, StoryContext } from 'storybook/internal/types'\nimport { state } from '../state'\n\nexport const visStorybookPreview: ProjectAnnotations<Renderer> = {\n\tbeforeEach(ctx: StoryContext) {\n\t\tstate.parameters = ctx.parameters\n\t},\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../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":["ctx","getCurrentTest"],"mappings":"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","file":"chunk-3ZUXLFHJ.js","sourcesContent":["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 { join } from 'pathe'\nimport { getCurrentTest } from 'vitest/suite'\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\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t\ttimeout: options?.timeout ?? 3000,\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\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 pixelmatch, { type PixelmatchOptions } from 'pixelmatch'\nimport { required } from 'type-plus'\nimport { getCurrentTest } from 'vitest/suite'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands, server } from './@vitest/browser/context.js'\nimport { assertImageSnapshot, isImageSnapshot, toSnapshotId } 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\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\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\tconst 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\t// subject = await page.imageSnapshot({ element: actual })\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\toptions = required(state.parameters?.snapshot, options)\n\tconst { pass, diffAmount, diffImage } = compareImage(baselineImage, resultImage, options)\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.taskName}\\` 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 image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\t\t}\n\n\t\t\tExpected: '${subject.baselinePath}'\n\t\t\tActual: '${subject.resultPath}'\n\t\t\tDifference: '${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"]}
@@ -1,48 +0,0 @@
1
- import { join } from 'pathe';
2
- import { getCurrentTest } from 'vitest/suite';
3
-
4
- // src/state.ts
5
-
6
- // src/@vitest/browser/constants.ts
7
- var imageSnapshotSymbol = Symbol.for("storybook-addon-vis/imageSnapshot");
8
- var imageSnapshotStubSymbol = Symbol.for("storybook-addon-vis/imageSnapshotStub");
9
-
10
- // src/@vitest/browser/image_snapshot.logic.ts
11
- function isImageSnapshot(subject) {
12
- return !!subject && subject.type === imageSnapshotSymbol;
13
- }
14
- function assertImageSnapshot(subject) {
15
- if (!isImageSnapshot(subject)) {
16
- throw new Error("Expected subject to be an image snapshot");
17
- }
18
- }
19
- function toSnapshotId(taskName) {
20
- return `${taskName.replace(/[^a-z0-9]/gi, "-").toLowerCase()}`;
21
- }
22
-
23
- // src/state.ts
24
- var state = {
25
- name: "",
26
- testFilepath: "",
27
- taskName: "",
28
- snapshot: {},
29
- getSnapshotFilePaths(options) {
30
- const test = getCurrentTest();
31
- state.taskName = test.name;
32
- const id = state.id = toSnapshotId(state.taskName);
33
- state.snapshot[state.testFilepath][id] = state.snapshot[state.testFilepath][id] ?? { index: 1 };
34
- const index = state.snapshot[state.testFilepath][state.id].index;
35
- const snapshotFilename = options?.customizeSnapshotId ? `${options.customizeSnapshotId(state.id, index)}.png` : `${state.id}-${index}.png`;
36
- const baselinePath = join(state.baselineDir, snapshotFilename);
37
- const resultPath = join(state.resultDir, snapshotFilename);
38
- const diffPath = join(state.diffDir, snapshotFilename);
39
- return { snapshotFilename, baselinePath, resultPath, diffPath };
40
- },
41
- incrementSnapshotIndex() {
42
- state.snapshot[state.testFilepath][state.id].index++;
43
- }
44
- };
45
-
46
- export { assertImageSnapshot, imageSnapshotStubSymbol, imageSnapshotSymbol, isImageSnapshot, state };
47
- //# sourceMappingURL=chunk-M4DV45WL.js.map
48
- //# sourceMappingURL=chunk-M4DV45WL.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/@vitest/browser/image_snapshot.logic.ts","../src/state.ts"],"names":[],"mappings":";;;;;;AAAa,IAAA,mBAAA,GAAsB,MAAO,CAAA,GAAA,CAAI,mCAAmC;AAEpE,IAAA,uBAAA,GAA0B,MAAO,CAAA,GAAA,CAAI,uCAAuC;;;ACClF,SAAS,gBAAgB,OAAwC,EAAA;AACvE,EAAA,OAAO,CAAC,CAAC,OAAW,IAAA,OAAA,CAAQ,IAAS,KAAA,mBAAA;AACtC;AAEO,SAAS,oBAAoB,OAAgD,EAAA;AACnF,EAAI,IAAA,CAAC,eAAgB,CAAA,OAAO,CAAG,EAAA;AAC9B,IAAM,MAAA,IAAI,MAAM,0CAA0C,CAAA;AAAA;AAE5D;AAEO,SAAS,aAAa,QAAkB,EAAA;AAC9C,EAAA,OAAO,GAAG,QAAS,CAAA,OAAA,CAAQ,eAAe,GAAG,CAAA,CAAE,aAAa,CAAA,CAAA;AAC7D;;;ACTO,IAAM,KAAQ,GAAA;AAAA,EACpB,IAAM,EAAA,EAAA;AAAA,EACN,YAAc,EAAA,EAAA;AAAA,EACd,QAAU,EAAA,EAAA;AAAA,EACV,UAAU,EAAC;AAAA,EACX,qBAAqB,OAA4C,EAAA;AAChE,IAAA,MAAM,OAAO,cAAe,EAAA;AAC5B,IAAA,KAAA,CAAM,WAAW,IAAK,CAAA,IAAA;AACtB,IAAA,MAAM,EAAM,GAAA,KAAA,CAAM,EAAK,GAAA,YAAA,CAAa,MAAM,QAAQ,CAAA;AAClD,IAAA,KAAA,CAAM,QAAS,CAAA,KAAA,CAAM,YAAY,CAAA,CAAE,EAAE,CAAI,GAAA,KAAA,CAAM,QAAS,CAAA,KAAA,CAAM,YAAY,CAAE,CAAA,EAAE,CAAK,IAAA,EAAE,OAAO,CAAE,EAAA;AAE9F,IAAM,MAAA,KAAA,GAAQ,MAAM,QAAS,CAAA,KAAA,CAAM,YAAY,CAAE,CAAA,KAAA,CAAM,EAAE,CAAG,CAAA,KAAA;AAC5D,IAAA,MAAM,gBAAmB,GAAA,OAAA,EAAS,mBAC/B,GAAA,CAAA,EAAG,QAAQ,mBAAoB,CAAA,KAAA,CAAM,EAAI,EAAA,KAAK,CAAC,CAC/C,IAAA,CAAA,GAAA,CAAA,EAAG,KAAM,CAAA,EAAE,IAAI,KAAK,CAAA,IAAA,CAAA;AACvB,IAAA,MAAM,YAAe,GAAA,IAAA,CAAK,KAAM,CAAA,WAAA,EAAa,gBAAgB,CAAA;AAC7D,IAAA,MAAM,UAAa,GAAA,IAAA,CAAK,KAAM,CAAA,SAAA,EAAW,gBAAgB,CAAA;AACzD,IAAA,MAAM,QAAW,GAAA,IAAA,CAAK,KAAM,CAAA,OAAA,EAAS,gBAAgB,CAAA;AACrD,IAAA,OAAO,EAAE,gBAAA,EAAkB,YAAc,EAAA,UAAA,EAAY,QAAS,EAAA;AAAA,GAC/D;AAAA,EACA,sBAAyB,GAAA;AACxB,IAAA,KAAA,CAAM,SAAS,KAAM,CAAA,YAAY,CAAE,CAAA,KAAA,CAAM,EAAE,CAAG,CAAA,KAAA,EAAA;AAAA;AAEhD","file":"chunk-M4DV45WL.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 { join } from 'pathe'\nimport { getCurrentTest } from 'vitest/suite'\nimport { toSnapshotId } from './@vitest/browser/image_snapshot.logic'\nimport type { ImageSnapshotOptions } from './@vitest/browser/types'\nimport type { MatchImageSnapshotOptions } from './expect.to_match_image_snapshot'\n\nexport const state = {\n\tname: '',\n\ttestFilepath: '',\n\ttaskName: '',\n\tsnapshot: {},\n\tgetSnapshotFilePaths(options?: ImageSnapshotOptions | undefined) {\n\t\tconst test = getCurrentTest()\n\t\tstate.taskName = test.name\n\t\tconst id = (state.id = toSnapshotId(state.taskName))\n\t\tstate.snapshot[state.testFilepath][id] = state.snapshot[state.testFilepath][id] ?? { index: 1 }\n\n\t\tconst index = state.snapshot[state.testFilepath][state.id]!.index\n\t\tconst snapshotFilename = options?.customizeSnapshotId\n\t\t\t? `${options.customizeSnapshotId(state.id, index)}.png`\n\t\t\t: `${state.id}-${index}.png`\n\t\tconst baselinePath = join(state.baselineDir, snapshotFilename)\n\t\tconst resultPath = join(state.resultDir, snapshotFilename)\n\t\tconst diffPath = join(state.diffDir, snapshotFilename)\n\t\treturn { snapshotFilename, baselinePath, resultPath, diffPath }\n\t},\n\tincrementSnapshotIndex() {\n\t\tstate.snapshot[state.testFilepath][state.id]!.index++\n\t},\n} as unknown as {\n\tid: string\n\tname: string\n\ttestFilepath: string\n\ttestFilename: string\n\tprojectDir: string\n\tbaselineDir: string\n\tresultDir: string\n\tdiffDir: string\n\ttaskName: string\n\tparameters: {\n\t\tsnapshot?: MatchImageSnapshotOptions | undefined\n\t\t[key: string]: any\n\t}\n\tsnapshot: Record<string, Record<string, { index: number }>>\n\tgetSnapshotFilePaths(options?: ImageSnapshotOptions | undefined): {\n\t\tsnapshotFilename: string\n\t\tbaselinePath: string\n\t\tresultPath: string\n\t\tdiffPath: string\n\t}\n\tincrementSnapshotIndex(): void\n}\n"]}
@@ -1,5 +0,0 @@
1
- import { ProjectAnnotations, Renderer } from 'storybook/internal/types';
2
-
3
- declare const visStorybookPreview: ProjectAnnotations<Renderer>;
4
-
5
- export { visStorybookPreview as v };
package/src/tags.spec.ts DELETED
@@ -1,32 +0,0 @@
1
- import type { StoryContext } from '@storybook/react'
2
- import type { RecursivePartial } from 'type-plus'
3
- import { shouldTakeSnapshot } from './tags'
4
-
5
- it('returns false if it is not a story', () => {
6
- expect(shouldTakeSnapshot(stubTestContext())).toBe(false)
7
- })
8
-
9
- it('returns false if it is a story without tags', () => {
10
- expect(shouldTakeSnapshot(stubTestContext({ story: {} }))).toBe(false)
11
- })
12
-
13
- it('returns true if it has `snapshot` tag', () => {
14
- expect(shouldTakeSnapshot(stubTestContext({ story: { tags: ['snapshot'] } }))).toBe(true)
15
- })
16
-
17
- it('returns false if it has no `snapshot` tag', () => {
18
- expect(shouldTakeSnapshot(stubTestContext({ story: { tags: [] } }))).toBe(false)
19
- })
20
-
21
- it('returns false if it has `!snapshot` tag', () => {
22
- expect(shouldTakeSnapshot(stubTestContext({ story: { tags: ['snapshot', '!snapshot'] } }))).toBe(false)
23
- })
24
-
25
- it('returns true if it has `snapshot` tag after `!snapshot` tags', () => {
26
- expect(shouldTakeSnapshot(stubTestContext({ story: { tags: ['!snapshot', 'snapshot'] } }))).toBe(true)
27
- expect(shouldTakeSnapshot(stubTestContext({ story: { tags: ['snapshot', '!snapshot', 'snapshot'] } }))).toBe(true)
28
- })
29
-
30
- function stubTestContext(ctx: RecursivePartial<{ story: StoryContext }> = {}) {
31
- return ctx as { story?: StoryContext }
32
- }
package/src/tags.ts DELETED
@@ -1,11 +0,0 @@
1
- /**
2
- * Determine should snapshot be taken.
3
- *
4
- * not story: false
5
- * no `snapshot` tag: false
6
- * disabled by `!snapshot` tag: false
7
- */
8
- export function shouldTakeSnapshot(ctx: { story?: { tags: string[] } }) {
9
- if (!ctx.story?.tags) return false
10
- return ctx.story.tags.lastIndexOf('!snapshot') < ctx.story.tags.lastIndexOf('snapshot')
11
- }