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