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.
- package/dist/{chunk-3ZUXLFHJ.js → chunk-EYGWSOX2.js} +53 -6
- package/dist/chunk-EYGWSOX2.js.map +1 -0
- package/dist/chunk-FLRCYKBG.js +17 -0
- package/dist/chunk-FLRCYKBG.js.map +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/preview-C2OcE4hp.d.ts +6 -0
- package/dist/preview.d.ts +2 -2
- package/dist/preview.js +3 -3
- package/dist/preview.js.map +1 -1
- package/dist/{context-BX71asjt.d.ts → types-CqNsMDsD.d.ts} +13 -1
- package/dist/vitest-setup.d.ts +20 -26
- package/dist/vitest-setup.js +42 -11
- package/dist/vitest-setup.js.map +1 -1
- package/package.json +2 -1
- package/readme.md +64 -78
- package/src/@vitest/browser/context.ts +3 -0
- package/src/expect.to_match_image_snapshot.spec.ts +9 -0
- package/src/expect.to_match_image_snapshot.ts +4 -3
- package/src/index.ts +1 -0
- package/src/param.stories.tsx +1 -0
- package/src/per_story.stories.tsx +1 -1
- package/src/preview.ts +2 -2
- package/src/should_take_snapshot.ts +13 -0
- package/src/state.ts +1 -0
- package/src/storybook/preview.ts +12 -4
- package/src/types.ts +11 -0
- package/src/vis.spec.ts +13 -0
- package/src/vis.ts +65 -0
- package/src/vitest-setup.spec.ts +0 -13
- package/src/vitest-setup.ts +4 -41
- package/dist/chunk-3NZUGVTR.js +0 -12
- package/dist/chunk-3NZUGVTR.js.map +0 -1
- package/dist/chunk-3ZUXLFHJ.js.map +0 -1
- package/dist/chunk-M4DV45WL.js +0 -48
- package/dist/chunk-M4DV45WL.js.map +0 -1
- package/dist/preview-BsZVLryd.d.ts +0 -5
- package/src/tags.spec.ts +0 -32
- 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
|
-
|
|
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-
|
|
250
|
-
//# sourceMappingURL=chunk-
|
|
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 './
|
|
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 './
|
|
3
|
-
export {
|
|
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 {
|
|
2
|
-
export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-
|
|
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":"
|
|
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 {
|
|
1
|
+
import { s as storybookPreviewVis } from './preview-C2OcE4hp.js';
|
|
2
2
|
import 'storybook/internal/types';
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
export {
|
|
6
|
+
export { storybookPreviewVis as default };
|
package/dist/preview.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import './chunk-
|
|
1
|
+
import { storybookPreviewVis } from './chunk-FLRCYKBG.js';
|
|
2
|
+
import './chunk-EYGWSOX2.js';
|
|
3
3
|
|
|
4
4
|
// src/preview.ts
|
|
5
|
-
var preview_default =
|
|
5
|
+
var preview_default = storybookPreviewVis;
|
|
6
6
|
|
|
7
7
|
export { preview_default as default };
|
|
8
8
|
//# sourceMappingURL=preview.js.map
|
package/dist/preview.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/preview.ts"],"names":[],"mappings":";;;;AAEA,IAAO,eAAQ,GAAA","file":"preview.js","sourcesContent":["import {
|
|
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
|
-
|
|
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 };
|
package/dist/vitest-setup.d.ts
CHANGED
|
@@ -1,40 +1,34 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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 };
|
package/dist/vitest-setup.js
CHANGED
|
@@ -1,17 +1,28 @@
|
|
|
1
|
-
import { commands } from './chunk-
|
|
2
|
-
export { cdp, commands, page, server, toMatchImageSnapshot } from './chunk-
|
|
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/
|
|
7
|
-
function shouldTakeSnapshot(
|
|
8
|
-
if (!
|
|
9
|
-
return
|
|
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/
|
|
13
|
-
function
|
|
14
|
-
|
|
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 {
|
|
68
|
+
export { createVisConfig, shouldTakeSnapshot };
|
|
38
69
|
//# sourceMappingURL=vitest-setup.js.map
|
|
39
70
|
//# sourceMappingURL=vitest-setup.js.map
|
package/dist/vitest-setup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/
|
|
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.
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
54
|
-
You
|
|
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 {
|
|
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
|
-
|
|
81
|
-
|
|
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
|
|
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 {
|
|
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:
|
|
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
|
-
|
|
108
|
-
|
|
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
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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
|
-
|
|
120
|
-
|
|
121
|
-
|
|
103
|
+
// Take image snapshot automatically for this story
|
|
104
|
+
tags: ['snapshot'],
|
|
105
|
+
// ...
|
|
122
106
|
}
|
|
123
107
|
```
|
|
124
108
|
|
|
125
|
-
You can
|
|
109
|
+
You can disable snapshot capturing with the `!snapshot` tag,
|
|
110
|
+
much like `!test`.
|
|
126
111
|
|
|
127
112
|
```ts
|
|
128
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
|
141
|
-
much like `!test`.
|
|
126
|
+
You can provide options to the `toMatchImageSnapshot` matcher using parameters.
|
|
142
127
|
|
|
143
128
|
```ts
|
|
144
|
-
|
|
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
|
-
|
|
152
|
-
|
|
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
|
|
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
|
-
|
|
170
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
69
|
+
subject = await page.imageSnapshot({ element: subject })
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
assertImageSnapshot(subject)
|
package/src/index.ts
CHANGED
package/src/param.stories.tsx
CHANGED
package/src/preview.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { storybookPreviewVis } from './storybook/preview'
|
|
2
2
|
|
|
3
|
-
export default
|
|
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
|
package/src/storybook/preview.ts
CHANGED
|
@@ -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
|
|
5
|
-
|
|
6
|
-
|
|
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
|
+
}
|
package/src/vis.spec.ts
ADDED
|
@@ -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
|
+
}
|
package/src/vitest-setup.spec.ts
CHANGED
|
@@ -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
|
-
})
|
package/src/vitest-setup.ts
CHANGED
|
@@ -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 './
|
|
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
|
-
|
|
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
|
-
}
|
|
45
|
-
}
|
|
7
|
+
// may not be needed
|
|
8
|
+
export * from './expect.to_match_image_snapshot.js'
|
package/dist/chunk-3NZUGVTR.js
DELETED
|
@@ -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"]}
|
package/dist/chunk-M4DV45WL.js
DELETED
|
@@ -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"]}
|
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
|
-
}
|