storybook-addon-vis 0.0.8 → 0.0.10
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/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/vitest-setup.js +1 -1
- package/dist/vitest-setup.js.map +1 -1
- package/package.json +1 -1
- package/readme.md +93 -216
- package/src/@vitest/browser/context.ts +4 -4
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import F from'dedent';import M from'pixelmatch';import {getByAltText,getByRole,getByTestId,getByText,getByTitle,getByPlaceholderText,getByLabelText}from'@storybook/test';import {basename,join}from'pathe';var g=Symbol.for("storybook-addon-vis/imageSnapshot"),d=Symbol.for("storybook-addon-vis/imageSnapshotStub");function f(t){return new Promise((e,a)=>{let n=new Image;n.src=`data:image/png;base64,${t}`,n.onload=()=>{let o=document.createElement("canvas");o.width=n.width,o.height=n.height;let r=o.getContext("2d");r.drawImage(n,0,0),e(r.getImageData(0,0,o.width,o.height));},n.onerror=()=>{a(new Error("Failed to load image"));};})}async function x(t){let e=document.createElement("canvas");return e.width=t.width,e.height=t.height,e.getContext("2d").putImageData(t,0,0),new Promise(n=>{e.toBlob(o=>{let r=new FileReader;r.onload=()=>{n(r.result);},r.readAsDataURL(o);});})}var p={name:"",testFilepath:"",taskName:"",snapshot:{}};function u(t){return !!t&&t.type===g}function S(t){if(!u(t))throw new Error("Expected subject to be an image snapshot")}function w(t){return `${t.replace(/[^a-z0-9]/gi,"-").toLowerCase()}`}async function I(t){let e=basename(p.testFilepath),a=`${w(p.taskName)}-${p.snapshot[p.taskName].index++}.png`,n=join(p.baselineDir,e,a),o=join(p.resultDir,e,a),r=join(p.diffDir,e,a),s=await this.screenshot({base64:!0,path:o,element:t?.element}),i=await f(s.base64);return {type:g,testfilename:e,snapshotFilename:a,baselinePath:n,resultPath:o,diffPath:r,base64:s.base64,image:i}}var h;globalThis.__vitest_browser__&&
|
|
1
|
+
import F from'dedent';import M from'pixelmatch';import {getByAltText,getByRole,getByTestId,getByText,getByTitle,getByPlaceholderText,getByLabelText}from'@storybook/test';import {basename,join}from'pathe';var g=Symbol.for("storybook-addon-vis/imageSnapshot"),d=Symbol.for("storybook-addon-vis/imageSnapshotStub");function f(t){return new Promise((e,a)=>{let n=new Image;n.src=`data:image/png;base64,${t}`,n.onload=()=>{let o=document.createElement("canvas");o.width=n.width,o.height=n.height;let r=o.getContext("2d");r.drawImage(n,0,0),e(r.getImageData(0,0,o.width,o.height));},n.onerror=()=>{a(new Error("Failed to load image"));};})}async function x(t){let e=document.createElement("canvas");return e.width=t.width,e.height=t.height,e.getContext("2d").putImageData(t,0,0),new Promise(n=>{e.toBlob(o=>{let r=new FileReader;r.onload=()=>{n(r.result);},r.readAsDataURL(o);});})}var p={name:"",testFilepath:"",taskName:"",snapshot:{}};function u(t){return !!t&&t.type===g}function S(t){if(!u(t))throw new Error("Expected subject to be an image snapshot")}function w(t){return `${t.replace(/[^a-z0-9]/gi,"-").toLowerCase()}`}async function I(t){let e=basename(p.testFilepath),a=`${w(p.taskName)}-${p.snapshot[p.taskName].index++}.png`,n=join(p.baselineDir,e,a),o=join(p.resultDir,e,a),r=join(p.diffDir,e,a),s=await this.screenshot({base64:!0,path:o,element:t?.element}),i=await f(s.base64);return {type:g,testfilename:e,snapshotFilename:a,baselinePath:n,resultPath:o,diffPath:r,base64:s.base64,image:i}}var h;globalThis.__vitest_browser__&&import('@vitest/browser/context').then(t=>{h=t,C.extend({imageSnapshot:I});});function at(){return h?.cdp()??{}}var l=new Proxy({},{get(t,e){return t[e]??(h?.commands)[e]}}),C=new Proxy({getByAltText:getByAltText,getByRole:getByRole,getByTestId:getByTestId,getByText:getByText,getByTitle:getByTitle,getByPlaceholder:getByPlaceholderText,getByLabelText:getByLabelText},{get(t,e){return t[e]??h?.page?(h?.page)[e]:()=>{if(e==="imageSnapshot")return {type:d};console.info(`\`page.${e.toString()}\` does not exist when running in browser`);}}}),nt=new Proxy({},{get(t,e){return t[e]??(h?.server)[e]}});async function ct(t,e){let a=await t;if(a?.type===d)return b;if(!u(a)){if(a?.path&&a?.base64)return {pass:!1,actual:t,message:()=>"`toMatchImageSnapshot()` expects the subject to be the result of `page.imageSnapshot()`, but seems like you are using `page.screenshot()`?"};if(typeof a!="object")return {pass:!1,actual:t,message:()=>`\`toMatchImageSnapshot()\` expects the subject to be an element, locator, or result of \`page.imageSnapshot()\`, but got: \`${t}\``}}S(a);let n=await R(a.baselinePath);if(!n)return await l.copyFile(a.resultPath,a.baselinePath),b;let o=await f(n),[r,s]=A(a.image,o),{pass:i,diffAmount:m,diffImage:c}=_(s,r,e);return i?b:(await z(`${a.diffPath}`,c),{pass:!1,actual:t,message:()=>F`${e?.failureThreshold?e?.failureThresholdType==="percent"?`Expected image to match within ${e.failureThreshold}% but was differ by ${m}%.`:`Expected image to match within ${e.failureThreshold} pixels but was differ by ${m} pixels.`:`Expected image to match but was differ by ${e?.failureThresholdType==="percent"?`${m}%`:`${m} pixels`}.`}
|
|
2
2
|
|
|
3
3
|
Expected: '${a.baselinePath}'
|
|
4
4
|
Actual: '${a.resultPath}'
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/image_data.ts","../src/state.ts","../src/@vitest/browser/image_snapshot.logic.ts","../src/@vitest/browser/page.image_snapshot.ts","../src/@vitest/browser/context.ts","../src/expect.to_match_image_snapshot.ts","../src/param.ts"],"names":["imageSnapshotSymbol","imageSnapshotStubSymbol","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","state","isImageSnapshot","subject","assertImageSnapshot","toSnapshotId","taskName","imageSnapshot","options","testfilename","basename","snapshotFilename","baselinePath","join","resultPath","diffPath","screenshot","image","cdp","commands","target","prop","page","getByAltText","getByRole","getByTestId","getByText","getByTitle","getByPlaceholderText","getByLabelText","server","toMatchImageSnapshot","actual","success","baseline","tryReadSnapshot","originalImage","resultImage","baselineImage","alignImagesToSameSize","pass","diffAmount","diffImage","compareImage","writeSnapshot","dedent","path","content","image1","image2","isSameSize","size","getMaxSize","resize","createImageResizer","width","height","inArea","x","y","result","idx","old","failureThreshold","failureThresholdType","diffOptions","pixelDiff","pixelmatch","toThresholdUnit","defineSnapshotProjectParam","snapshot","defineSnapshotParam"],"mappings":"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","file":"index.js","sourcesContent":["export const imageSnapshotSymbol = Symbol.for('storybook-addon-vis/imageSnapshot')\n\nexport const imageSnapshotStubSymbol = Symbol.for('storybook-addon-vis/imageSnapshotStub')\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","export const state = {\n\tname: '',\n\ttestFilepath: '',\n\ttaskName: '',\n\tsnapshot: {},\n} as {\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\tsnapshot: Record<string, { index: number }>\n}\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 type { BrowserPage } from '@vitest/browser/context'\nimport { basename, join } from 'pathe'\nimport { toImageData } from '../../image_data.js'\nimport { state } from '../../state.js'\nimport { imageSnapshotSymbol } from './constants.js'\nimport { toSnapshotId } from './image_snapshot.logic.js'\nimport type { ImageSnapshot, ImageSnapshotOptions } from './types.js'\n\nexport async function imageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions): Promise<ImageSnapshot> {\n\t// const rootDir = server.config.root\n\tconst testfilename = basename(state.testFilepath)\n\tconst snapshotFilename = `${toSnapshotId(state.taskName)}-${state.snapshot[state.taskName]!.index++}.png`\n\tconst baselinePath = join(state.baselineDir, testfilename, snapshotFilename)\n\tconst resultPath = join(state.resultDir, testfilename, snapshotFilename)\n\tconst diffPath = join(state.diffDir, testfilename, snapshotFilename)\n\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t})\n\tconst image = await toImageData(screenshot.base64)\n\n\treturn {\n\t\ttype: imageSnapshotSymbol,\n\t\t// rootDir,\n\t\ttestfilename,\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 { imageSnapshot } from './page.image_snapshot'\n\nlet ctx: Awaited<typeof import('@vitest/browser/context')>\n\nif ((globalThis as any).__vitest_browser__) {\n\tctx = await import('@vitest/browser/context')\n\t// import('@vitest/browser/context').then((m) => (ctx = m))\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() ?? {}\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\npage.extend({ imageSnapshot })\n","import type { AsyncExpectationResult } from '@vitest/expect'\nimport dedent from 'dedent'\nimport pixelmatch from 'pixelmatch'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands } from './@vitest/browser/context.js'\nimport { assertImageSnapshot, isImageSnapshot } from './@vitest/browser/image_snapshot.logic.js'\nimport type { MatchImageSnapshotOptions } from './@vitest/browser/types.js'\nimport { toDataURL, toImageData } from './image_data.js'\n\nexport async function toMatchImageSnapshot(\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,\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,\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: \\`${actual}\\``,\n\t\t\t}\n\t\t}\n\t\t// subject = await page.imageSnapshot({ element: actual })\n\t}\n\tassertImageSnapshot(subject)\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\tconst { pass, diffAmount, diffImage } = compareImage(baselineImage, resultImage, options)\n\tif (!pass) {\n\t\tawait writeSnapshot(`${subject.diffPath}`, diffImage)\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tactual,\n\t\t\tmessage: () =>\n\t\t\t\tdedent`${\n\t\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\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\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t\t: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\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\nconst 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\t\treturn result\n\t}\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","import type { PixelmatchOptions } from 'pixelmatch'\n\n/**\n * The project parameters for the snapshot.\n *\n * These parameters are set per project in the `.storybook/preview.ts` file.\n */\nexport type SnapshotProjectParam = {\n\t/**\n\t * The snapshot folder relative to the root of the project.\n\t */\n\tsnapshotPath: string\n\t/**\n\t * The baseline folder under the snapshot folder specified in `snapshotPath`.\n\t */\n\tbaseline: string\n\t/**\n\t * The result folder under the snapshot folder specified in `snapshotPath`.\n\t *\n\t * This folder stores the snapshot of the current test run.\n\t */\n\tresult: string\n\t/**\n\t * The diff folder under the snapshot folder specified in `snapshotPath`.\n\t *\n\t * This folder stores the diff of the current test run if the test fails.\n\t */\n\tdiff: string\n}\n\nexport function defineSnapshotProjectParam(snapshot: Partial<SnapshotProjectParam>) {\n\treturn { snapshot }\n}\n\nexport type SnapshotStoryParam = {\n\t/**\n\t * Add a delay to the story snapshot comparison to allow for async rendering.\n\t *\n\t * This only apply to the automatic snapshot taken when the story is rendered.\n\t * In general, it's a good idea to not rely on this delay,\n\t * and instead use the make sure your component is fully rendered before the `play()` function returns.\n\t */\n\tdelay?: number | undefined\n\t/**\n\t * The method by which images are compared.\n\t * `pixelmatch` does a pixel by pixel comparison, whereas `ssim` does a structural similarity comparison.\n\t * @default 'pixelmatch'\n\t */\n\tcomparisonMethod?: 'pixelmatch' | 'ssim' | undefined\n\t/**\n\t * Custom config passed to 'pixelmatch' or 'ssim'\n\t */\n\tcustomDiffConfig?: PixelmatchOptions | undefined\n\tfailureThreshold?: number | undefined\n\tfailureThresholdType?: 'percent' | 'pixel' | undefined\n\tblur?: number | undefined\n}\n\nexport function defineSnapshotParam(snapshot: SnapshotStoryParam) {\n\treturn { snapshot }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/image_data.ts","../src/state.ts","../src/@vitest/browser/image_snapshot.logic.ts","../src/@vitest/browser/page.image_snapshot.ts","../src/@vitest/browser/context.ts","../src/expect.to_match_image_snapshot.ts","../src/param.ts"],"names":["imageSnapshotSymbol","imageSnapshotStubSymbol","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","state","isImageSnapshot","subject","assertImageSnapshot","toSnapshotId","taskName","imageSnapshot","options","testfilename","basename","snapshotFilename","baselinePath","join","resultPath","diffPath","screenshot","image","m","page","cdp","commands","target","prop","getByAltText","getByRole","getByTestId","getByText","getByTitle","getByPlaceholderText","getByLabelText","server","toMatchImageSnapshot","actual","success","baseline","tryReadSnapshot","originalImage","resultImage","baselineImage","alignImagesToSameSize","pass","diffAmount","diffImage","compareImage","writeSnapshot","dedent","path","content","image1","image2","isSameSize","size","getMaxSize","resize","createImageResizer","width","height","inArea","x","y","result","idx","old","failureThreshold","failureThresholdType","diffOptions","pixelDiff","pixelmatch","toThresholdUnit","defineSnapshotProjectParam","snapshot","defineSnapshotParam"],"mappings":"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","file":"index.js","sourcesContent":["export const imageSnapshotSymbol = Symbol.for('storybook-addon-vis/imageSnapshot')\n\nexport const imageSnapshotStubSymbol = Symbol.for('storybook-addon-vis/imageSnapshotStub')\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","export const state = {\n\tname: '',\n\ttestFilepath: '',\n\ttaskName: '',\n\tsnapshot: {},\n} as {\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\tsnapshot: Record<string, { index: number }>\n}\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 type { BrowserPage } from '@vitest/browser/context'\nimport { basename, join } from 'pathe'\nimport { toImageData } from '../../image_data.js'\nimport { state } from '../../state.js'\nimport { imageSnapshotSymbol } from './constants.js'\nimport { toSnapshotId } from './image_snapshot.logic.js'\nimport type { ImageSnapshot, ImageSnapshotOptions } from './types.js'\n\nexport async function imageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions): Promise<ImageSnapshot> {\n\t// const rootDir = server.config.root\n\tconst testfilename = basename(state.testFilepath)\n\tconst snapshotFilename = `${toSnapshotId(state.taskName)}-${state.snapshot[state.taskName]!.index++}.png`\n\tconst baselinePath = join(state.baselineDir, testfilename, snapshotFilename)\n\tconst resultPath = join(state.resultDir, testfilename, snapshotFilename)\n\tconst diffPath = join(state.diffDir, testfilename, snapshotFilename)\n\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t})\n\tconst image = await toImageData(screenshot.base64)\n\n\treturn {\n\t\ttype: imageSnapshotSymbol,\n\t\t// rootDir,\n\t\ttestfilename,\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 { 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 })\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() ?? {}\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","import type { AsyncExpectationResult } from '@vitest/expect'\nimport dedent from 'dedent'\nimport pixelmatch from 'pixelmatch'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands } from './@vitest/browser/context.js'\nimport { assertImageSnapshot, isImageSnapshot } from './@vitest/browser/image_snapshot.logic.js'\nimport type { MatchImageSnapshotOptions } from './@vitest/browser/types.js'\nimport { toDataURL, toImageData } from './image_data.js'\n\nexport async function toMatchImageSnapshot(\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,\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,\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: \\`${actual}\\``,\n\t\t\t}\n\t\t}\n\t\t// subject = await page.imageSnapshot({ element: actual })\n\t}\n\tassertImageSnapshot(subject)\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\tconst { pass, diffAmount, diffImage } = compareImage(baselineImage, resultImage, options)\n\tif (!pass) {\n\t\tawait writeSnapshot(`${subject.diffPath}`, diffImage)\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tactual,\n\t\t\tmessage: () =>\n\t\t\t\tdedent`${\n\t\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\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\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t\t: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\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\nconst 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\t\treturn result\n\t}\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","import type { PixelmatchOptions } from 'pixelmatch'\n\n/**\n * The project parameters for the snapshot.\n *\n * These parameters are set per project in the `.storybook/preview.ts` file.\n */\nexport type SnapshotProjectParam = {\n\t/**\n\t * The snapshot folder relative to the root of the project.\n\t */\n\tsnapshotPath: string\n\t/**\n\t * The baseline folder under the snapshot folder specified in `snapshotPath`.\n\t */\n\tbaseline: string\n\t/**\n\t * The result folder under the snapshot folder specified in `snapshotPath`.\n\t *\n\t * This folder stores the snapshot of the current test run.\n\t */\n\tresult: string\n\t/**\n\t * The diff folder under the snapshot folder specified in `snapshotPath`.\n\t *\n\t * This folder stores the diff of the current test run if the test fails.\n\t */\n\tdiff: string\n}\n\nexport function defineSnapshotProjectParam(snapshot: Partial<SnapshotProjectParam>) {\n\treturn { snapshot }\n}\n\nexport type SnapshotStoryParam = {\n\t/**\n\t * Add a delay to the story snapshot comparison to allow for async rendering.\n\t *\n\t * This only apply to the automatic snapshot taken when the story is rendered.\n\t * In general, it's a good idea to not rely on this delay,\n\t * and instead use the make sure your component is fully rendered before the `play()` function returns.\n\t */\n\tdelay?: number | undefined\n\t/**\n\t * The method by which images are compared.\n\t * `pixelmatch` does a pixel by pixel comparison, whereas `ssim` does a structural similarity comparison.\n\t * @default 'pixelmatch'\n\t */\n\tcomparisonMethod?: 'pixelmatch' | 'ssim' | undefined\n\t/**\n\t * Custom config passed to 'pixelmatch' or 'ssim'\n\t */\n\tcustomDiffConfig?: PixelmatchOptions | undefined\n\tfailureThreshold?: number | undefined\n\tfailureThresholdType?: 'percent' | 'pixel' | undefined\n\tblur?: number | undefined\n}\n\nexport function defineSnapshotParam(snapshot: SnapshotStoryParam) {\n\treturn { snapshot }\n}\n"]}
|
package/dist/vitest-setup.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {basename,join,dirname,relative}from'pathe';import {getByAltText,getByRole,getByTestId,getByText,getByTitle,getByPlaceholderText,getByLabelText}from'@storybook/test';import A from'dedent';import C from'pixelmatch';var s={name:"",testFilepath:"",taskName:"",snapshot:{}};var g=Symbol.for("storybook-addon-vis/imageSnapshot"),f=Symbol.for("storybook-addon-vis/imageSnapshotStub");function l(t){return new Promise((e,a)=>{let o=new Image;o.src=`data:image/png;base64,${t}`,o.onload=()=>{let n=document.createElement("canvas");n.width=o.width,n.height=o.height;let r=n.getContext("2d");r.drawImage(o,0,0),e(r.getImageData(0,0,n.width,n.height));},o.onerror=()=>{a(new Error("Failed to load image"));};})}async function S(t){let e=document.createElement("canvas");return e.width=t.width,e.height=t.height,e.getContext("2d").putImageData(t,0,0),new Promise(o=>{e.toBlob(n=>{let r=new FileReader;r.onload=()=>{o(r.result);},r.readAsDataURL(n);});})}function u(t){return !!t&&t.type===g}function I(t){if(!u(t))throw new Error("Expected subject to be an image snapshot")}function D(t){return `${t.replace(/[^a-z0-9]/gi,"-").toLowerCase()}`}async function P(t){let e=basename(s.testFilepath),a=`${D(s.taskName)}-${s.snapshot[s.taskName].index++}.png`,o=join(s.baselineDir,e,a),n=join(s.resultDir,e,a),r=join(s.diffDir,e,a),i=await this.screenshot({base64:!0,path:n,element:t?.element}),m=await l(i.base64);return {type:g,testfilename:e,snapshotFilename:a,baselinePath:o,resultPath:n,diffPath:r,base64:i.base64,image:m}}var h;globalThis.__vitest_browser__&&
|
|
1
|
+
import {basename,join,dirname,relative}from'pathe';import {getByAltText,getByRole,getByTestId,getByText,getByTitle,getByPlaceholderText,getByLabelText}from'@storybook/test';import A from'dedent';import C from'pixelmatch';var s={name:"",testFilepath:"",taskName:"",snapshot:{}};var g=Symbol.for("storybook-addon-vis/imageSnapshot"),f=Symbol.for("storybook-addon-vis/imageSnapshotStub");function l(t){return new Promise((e,a)=>{let o=new Image;o.src=`data:image/png;base64,${t}`,o.onload=()=>{let n=document.createElement("canvas");n.width=o.width,n.height=o.height;let r=n.getContext("2d");r.drawImage(o,0,0),e(r.getImageData(0,0,n.width,n.height));},o.onerror=()=>{a(new Error("Failed to load image"));};})}async function S(t){let e=document.createElement("canvas");return e.width=t.width,e.height=t.height,e.getContext("2d").putImageData(t,0,0),new Promise(o=>{e.toBlob(n=>{let r=new FileReader;r.onload=()=>{o(r.result);},r.readAsDataURL(n);});})}function u(t){return !!t&&t.type===g}function I(t){if(!u(t))throw new Error("Expected subject to be an image snapshot")}function D(t){return `${t.replace(/[^a-z0-9]/gi,"-").toLowerCase()}`}async function P(t){let e=basename(s.testFilepath),a=`${D(s.taskName)}-${s.snapshot[s.taskName].index++}.png`,o=join(s.baselineDir,e,a),n=join(s.resultDir,e,a),r=join(s.diffDir,e,a),i=await this.screenshot({base64:!0,path:n,element:t?.element}),m=await l(i.base64);return {type:g,testfilename:e,snapshotFilename:a,baselinePath:o,resultPath:n,diffPath:r,base64:i.base64,image:m}}var h;globalThis.__vitest_browser__&&import('@vitest/browser/context').then(t=>{h=t,E.extend({imageSnapshot:P});});function rt(){return h?.cdp()??{}}var d=new Proxy({},{get(t,e){return t[e]??(h?.commands)[e]}}),E=new Proxy({getByAltText:getByAltText,getByRole:getByRole,getByTestId:getByTestId,getByText:getByText,getByTitle:getByTitle,getByPlaceholder:getByPlaceholderText,getByLabelText:getByLabelText},{get(t,e){return t[e]??h?.page?(h?.page)[e]:()=>{if(e==="imageSnapshot")return {type:f};console.info(`\`page.${e.toString()}\` does not exist when running in browser`);}}}),it=new Proxy({},{get(t,e){return t[e]??(h?.server)[e]}});async function dt(t,e){let a=await t;if(a?.type===f)return b;if(!u(a)){if(a?.path&&a?.base64)return {pass:!1,actual:t,message:()=>"`toMatchImageSnapshot()` expects the subject to be the result of `page.imageSnapshot()`, but seems like you are using `page.screenshot()`?"};if(typeof a!="object")return {pass:!1,actual:t,message:()=>`\`toMatchImageSnapshot()\` expects the subject to be an element, locator, or result of \`page.imageSnapshot()\`, but got: \`${t}\``}}I(a);let o=await R(a.baselinePath);if(!o)return await d.copyFile(a.resultPath,a.baselinePath),b;let n=await l(o),[r,i]=M(a.image,n),{pass:m,diffAmount:p,diffImage:c}=U(i,r,e);return m?b:(await z(`${a.diffPath}`,c),{pass:!1,actual:t,message:()=>A`${e?.failureThreshold?e?.failureThresholdType==="percent"?`Expected image to match within ${e.failureThreshold}% but was differ by ${p}%.`:`Expected image to match within ${e.failureThreshold} pixels but was differ by ${p} pixels.`:`Expected image to match but was differ by ${e?.failureThresholdType==="percent"?`${p}%`:`${p} pixels`}.`}
|
|
2
2
|
|
|
3
3
|
Expected: '${a.baselinePath}'
|
|
4
4
|
Actual: '${a.resultPath}'
|
package/dist/vitest-setup.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/state.ts","../src/@vitest/browser/constants.ts","../src/image_data.ts","../src/@vitest/browser/image_snapshot.logic.ts","../src/@vitest/browser/page.image_snapshot.ts","../src/@vitest/browser/context.ts","../src/expect.to_match_image_snapshot.ts","../src/tags.ts","../src/vitest-setup.ts"],"names":["state","imageSnapshotSymbol","imageSnapshotStubSymbol","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","isImageSnapshot","subject","assertImageSnapshot","toSnapshotId","taskName","imageSnapshot","options","testfilename","basename","snapshotFilename","baselinePath","join","resultPath","diffPath","screenshot","image","cdp","commands","target","prop","page","getByAltText","getByRole","getByTestId","getByText","getByTitle","getByPlaceholderText","getByLabelText","server","toMatchImageSnapshot","actual","success","baseline","tryReadSnapshot","originalImage","resultImage","baselineImage","alignImagesToSameSize","pass","diffAmount","diffImage","compareImage","writeSnapshot","dedent","path","content","image1","image2","isSameSize","size","getMaxSize","resize","createImageResizer","width","height","inArea","x","y","result","idx","old","failureThreshold","failureThresholdType","diffOptions","pixelDiff","pixelmatch","toThresholdUnit","shouldTakeSnapshot","configureSnapshotBeforeAll","suite","currentDir","dirname","relative","configureSnapshotBeforeEach"],"mappings":"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","file":"vitest-setup.js","sourcesContent":["export const state = {\n\tname: '',\n\ttestFilepath: '',\n\ttaskName: '',\n\tsnapshot: {},\n} as {\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\tsnapshot: Record<string, { index: number }>\n}\n","export const imageSnapshotSymbol = Symbol.for('storybook-addon-vis/imageSnapshot')\n\nexport const imageSnapshotStubSymbol = Symbol.for('storybook-addon-vis/imageSnapshotStub')\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 { 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 type { BrowserPage } from '@vitest/browser/context'\nimport { basename, join } from 'pathe'\nimport { toImageData } from '../../image_data.js'\nimport { state } from '../../state.js'\nimport { imageSnapshotSymbol } from './constants.js'\nimport { toSnapshotId } from './image_snapshot.logic.js'\nimport type { ImageSnapshot, ImageSnapshotOptions } from './types.js'\n\nexport async function imageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions): Promise<ImageSnapshot> {\n\t// const rootDir = server.config.root\n\tconst testfilename = basename(state.testFilepath)\n\tconst snapshotFilename = `${toSnapshotId(state.taskName)}-${state.snapshot[state.taskName]!.index++}.png`\n\tconst baselinePath = join(state.baselineDir, testfilename, snapshotFilename)\n\tconst resultPath = join(state.resultDir, testfilename, snapshotFilename)\n\tconst diffPath = join(state.diffDir, testfilename, snapshotFilename)\n\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t})\n\tconst image = await toImageData(screenshot.base64)\n\n\treturn {\n\t\ttype: imageSnapshotSymbol,\n\t\t// rootDir,\n\t\ttestfilename,\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 { imageSnapshot } from './page.image_snapshot'\n\nlet ctx: Awaited<typeof import('@vitest/browser/context')>\n\nif ((globalThis as any).__vitest_browser__) {\n\tctx = await import('@vitest/browser/context')\n\t// import('@vitest/browser/context').then((m) => (ctx = m))\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() ?? {}\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\npage.extend({ imageSnapshot })\n","import type { AsyncExpectationResult } from '@vitest/expect'\nimport dedent from 'dedent'\nimport pixelmatch from 'pixelmatch'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands } from './@vitest/browser/context.js'\nimport { assertImageSnapshot, isImageSnapshot } from './@vitest/browser/image_snapshot.logic.js'\nimport type { MatchImageSnapshotOptions } from './@vitest/browser/types.js'\nimport { toDataURL, toImageData } from './image_data.js'\n\nexport async function toMatchImageSnapshot(\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,\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,\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: \\`${actual}\\``,\n\t\t\t}\n\t\t}\n\t\t// subject = await page.imageSnapshot({ element: actual })\n\t}\n\tassertImageSnapshot(subject)\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\tconst { pass, diffAmount, diffImage } = compareImage(baselineImage, resultImage, options)\n\tif (!pass) {\n\t\tawait writeSnapshot(`${subject.diffPath}`, diffImage)\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tactual,\n\t\t\tmessage: () =>\n\t\t\t\tdedent`${\n\t\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\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\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t\t: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\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\nconst 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\t\treturn result\n\t}\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","/**\n * Determine should snapshot be taken.\n *\n * not story: false\n * no `snapshot` tag: false\n * disabled by `!snapshot` tag: false\n */\nexport function shouldTakeSnapshot(ctx: { story?: { tags: string[] } }) {\n\tif (!ctx.story?.tags) return false\n\treturn ctx.story.tags.lastIndexOf('!snapshot') < ctx.story.tags.lastIndexOf('snapshot')\n}\n","import { basename, dirname, join, relative } from 'pathe'\nimport './augment.js'\nimport { state } from './state.js'\n\nexport * from './@vitest/browser/context.js'\nexport * from './expect.to_match_image_snapshot.js'\nexport * from './tags.js'\n\nexport function configureSnapshotBeforeAll(suite: { file: { filepath: string }; name: string }) {\n\tstate.name = suite.name\n\tstate.testFilepath = suite.file.filepath\n\tstate.testFilename = basename(state.testFilepath)\n\tstate.projectDir = state.testFilepath.slice(0, -state.name.length)\n\tconst currentDir = dirname(state.testFilepath)\n\tstate.baselineDir = relative(currentDir, join(state.projectDir, '__snapshots__'))\n\tstate.resultDir = relative(currentDir, join(state.projectDir, '__snapshots__', '__results__'))\n\tstate.diffDir = relative(currentDir, join(state.projectDir, '__snapshots__', '__diff_output__'))\n}\n\nexport function configureSnapshotBeforeEach(ctx: { task: { name: string } }) {\n\tstate.taskName = ctx.task.name\n\tstate.snapshot[state.taskName] = state.snapshot[state.taskName] ?? { index: 1 }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/state.ts","../src/@vitest/browser/constants.ts","../src/image_data.ts","../src/@vitest/browser/image_snapshot.logic.ts","../src/@vitest/browser/page.image_snapshot.ts","../src/@vitest/browser/context.ts","../src/expect.to_match_image_snapshot.ts","../src/tags.ts","../src/vitest-setup.ts"],"names":["state","imageSnapshotSymbol","imageSnapshotStubSymbol","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","isImageSnapshot","subject","assertImageSnapshot","toSnapshotId","taskName","imageSnapshot","options","testfilename","basename","snapshotFilename","baselinePath","join","resultPath","diffPath","screenshot","image","m","page","cdp","commands","target","prop","getByAltText","getByRole","getByTestId","getByText","getByTitle","getByPlaceholderText","getByLabelText","server","toMatchImageSnapshot","actual","success","baseline","tryReadSnapshot","originalImage","resultImage","baselineImage","alignImagesToSameSize","pass","diffAmount","diffImage","compareImage","writeSnapshot","dedent","path","content","image1","image2","isSameSize","size","getMaxSize","resize","createImageResizer","width","height","inArea","x","y","result","idx","old","failureThreshold","failureThresholdType","diffOptions","pixelDiff","pixelmatch","toThresholdUnit","shouldTakeSnapshot","configureSnapshotBeforeAll","suite","currentDir","dirname","relative","configureSnapshotBeforeEach"],"mappings":"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","file":"vitest-setup.js","sourcesContent":["export const state = {\n\tname: '',\n\ttestFilepath: '',\n\ttaskName: '',\n\tsnapshot: {},\n} as {\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\tsnapshot: Record<string, { index: number }>\n}\n","export const imageSnapshotSymbol = Symbol.for('storybook-addon-vis/imageSnapshot')\n\nexport const imageSnapshotStubSymbol = Symbol.for('storybook-addon-vis/imageSnapshotStub')\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 { 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 type { BrowserPage } from '@vitest/browser/context'\nimport { basename, join } from 'pathe'\nimport { toImageData } from '../../image_data.js'\nimport { state } from '../../state.js'\nimport { imageSnapshotSymbol } from './constants.js'\nimport { toSnapshotId } from './image_snapshot.logic.js'\nimport type { ImageSnapshot, ImageSnapshotOptions } from './types.js'\n\nexport async function imageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions): Promise<ImageSnapshot> {\n\t// const rootDir = server.config.root\n\tconst testfilename = basename(state.testFilepath)\n\tconst snapshotFilename = `${toSnapshotId(state.taskName)}-${state.snapshot[state.taskName]!.index++}.png`\n\tconst baselinePath = join(state.baselineDir, testfilename, snapshotFilename)\n\tconst resultPath = join(state.resultDir, testfilename, snapshotFilename)\n\tconst diffPath = join(state.diffDir, testfilename, snapshotFilename)\n\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t})\n\tconst image = await toImageData(screenshot.base64)\n\n\treturn {\n\t\ttype: imageSnapshotSymbol,\n\t\t// rootDir,\n\t\ttestfilename,\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 { 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 })\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() ?? {}\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","import type { AsyncExpectationResult } from '@vitest/expect'\nimport dedent from 'dedent'\nimport pixelmatch from 'pixelmatch'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands } from './@vitest/browser/context.js'\nimport { assertImageSnapshot, isImageSnapshot } from './@vitest/browser/image_snapshot.logic.js'\nimport type { MatchImageSnapshotOptions } from './@vitest/browser/types.js'\nimport { toDataURL, toImageData } from './image_data.js'\n\nexport async function toMatchImageSnapshot(\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,\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,\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: \\`${actual}\\``,\n\t\t\t}\n\t\t}\n\t\t// subject = await page.imageSnapshot({ element: actual })\n\t}\n\tassertImageSnapshot(subject)\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\tconst { pass, diffAmount, diffImage } = compareImage(baselineImage, resultImage, options)\n\tif (!pass) {\n\t\tawait writeSnapshot(`${subject.diffPath}`, diffImage)\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tactual,\n\t\t\tmessage: () =>\n\t\t\t\tdedent`${\n\t\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\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\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t\t: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\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\nconst 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\t\treturn result\n\t}\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","/**\n * Determine should snapshot be taken.\n *\n * not story: false\n * no `snapshot` tag: false\n * disabled by `!snapshot` tag: false\n */\nexport function shouldTakeSnapshot(ctx: { story?: { tags: string[] } }) {\n\tif (!ctx.story?.tags) return false\n\treturn ctx.story.tags.lastIndexOf('!snapshot') < ctx.story.tags.lastIndexOf('snapshot')\n}\n","import { basename, dirname, join, relative } from 'pathe'\nimport './augment.js'\nimport { state } from './state.js'\n\nexport * from './@vitest/browser/context.js'\nexport * from './expect.to_match_image_snapshot.js'\nexport * from './tags.js'\n\nexport function configureSnapshotBeforeAll(suite: { file: { filepath: string }; name: string }) {\n\tstate.name = suite.name\n\tstate.testFilepath = suite.file.filepath\n\tstate.testFilename = basename(state.testFilepath)\n\tstate.projectDir = state.testFilepath.slice(0, -state.name.length)\n\tconst currentDir = dirname(state.testFilepath)\n\tstate.baselineDir = relative(currentDir, join(state.projectDir, '__snapshots__'))\n\tstate.resultDir = relative(currentDir, join(state.projectDir, '__snapshots__', '__results__'))\n\tstate.diffDir = relative(currentDir, join(state.projectDir, '__snapshots__', '__diff_output__'))\n}\n\nexport function configureSnapshotBeforeEach(ctx: { task: { name: string } }) {\n\tstate.taskName = ctx.task.name\n\tstate.snapshot[state.taskName] = state.snapshot[state.taskName] ?? { index: 1 }\n}\n"]}
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -13,7 +13,7 @@ Since it is running in actual browser, [jest-image-snapshot] does not work.
|
|
|
13
13
|
This add-on provides similar functionality to [jest-image-snapshot].
|
|
14
14
|
In addition, you can capture image snapshot manually.
|
|
15
15
|
|
|
16
|
-
##
|
|
16
|
+
## Install
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
19
|
npm install --save-dev storybook-addon-vis
|
|
@@ -23,264 +23,141 @@ pnpm add --save-dev storybook-addon-vis
|
|
|
23
23
|
yarn add --save-dev storybook-addon-vis
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
##
|
|
26
|
+
## Config
|
|
27
27
|
|
|
28
28
|
This add-on provides features on both [storybook] and [vitest],
|
|
29
29
|
thus you need to add it to both [storybook] and [vitest].
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
// .storybook/main.ts
|
|
33
|
-
const config: StorybookConfig = {
|
|
34
|
-
addons: ['storybook-addon-vis'],
|
|
35
|
-
}
|
|
31
|
+
On [vitest], you need to add `storybook-addon-vis/vitest-plugin` plugin in `vitest.config.ts`.
|
|
36
32
|
|
|
37
|
-
|
|
33
|
+
```ts
|
|
34
|
+
// vite.config.ts or vitest.config.ts
|
|
35
|
+
import { storybookTest } from '@storybook/experimental-addon-test/vitest-plugin'
|
|
38
36
|
import { defineConfig } from 'vitest/config'
|
|
39
37
|
import { storybookVis } from 'storybook-addon-vis/vitest-plugin'
|
|
40
38
|
|
|
41
39
|
export default defineConfig({
|
|
42
|
-
plugins: [
|
|
40
|
+
plugins: [
|
|
41
|
+
storybookTest(),
|
|
42
|
+
storybookVis()],
|
|
43
43
|
test: {
|
|
44
|
+
browser: {
|
|
45
|
+
// ...
|
|
46
|
+
}
|
|
44
47
|
setupFiles: ['./vitest.setup.ts'],
|
|
45
48
|
}
|
|
46
49
|
})
|
|
47
|
-
|
|
48
|
-
// vitest.setup.ts
|
|
49
|
-
import 'storybook-addon-vis/vitest-setup'
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
- `npm run start` runs babel in watch mode and starts Storybook
|
|
55
|
-
- `npm run build` build and package your addon code
|
|
56
|
-
|
|
57
|
-
### Switch from TypeScript to JavaScript
|
|
58
|
-
|
|
59
|
-
Don't want to use TypeScript? We offer a handy eject command: `npm run eject-ts`
|
|
60
|
-
|
|
61
|
-
This will convert all code to JS. It is a destructive process, so we recommended running this before you start writing any code.
|
|
62
|
-
|
|
63
|
-
## What's included?
|
|
64
|
-
|
|
65
|
-

|
|
66
|
-
|
|
67
|
-
The addon code lives in `src`. It demonstrates all core addon related concepts. The three [UI paradigms](https://storybook.js.org/docs/react/addons/addon-types#ui-based-addons)
|
|
68
|
-
|
|
69
|
-
- `src/Tool.tsx`
|
|
70
|
-
- `src/Panel.tsx`
|
|
71
|
-
- `src/Tab.tsx`
|
|
72
|
-
|
|
73
|
-
Which, along with the addon itself, are registered in `src/manager.ts`.
|
|
74
|
-
|
|
75
|
-
Managing State and interacting with a story:
|
|
76
|
-
|
|
77
|
-
- `src/withGlobals.ts` & `src/Tool.tsx` demonstrates how to use `useGlobals` to manage global state and modify the contents of a Story.
|
|
78
|
-
- `src/withRoundTrip.ts` & `src/Panel.tsx` demonstrates two-way communication using channels.
|
|
79
|
-
- `src/Tab.tsx` demonstrates how to use `useParameter` to access the current story's parameters.
|
|
80
|
-
|
|
81
|
-
Your addon might use one or more of these patterns. Feel free to delete unused code. Update `src/manager.ts` and `src/preview.ts` accordingly.
|
|
82
|
-
|
|
83
|
-
Lastly, configure you addon name in `src/constants.ts`.
|
|
84
|
-
|
|
85
|
-
### Bundling
|
|
86
|
-
|
|
87
|
-
Addons can interact with a Storybook project in multiple ways. It is recommended to familiarize yourself with [the basics](https://storybook.js.org/docs/react/addons/introduction) before getting started.
|
|
88
|
-
|
|
89
|
-
- Manager entries are used to add UI or behavior to the Storybook manager UI.
|
|
90
|
-
- Preview entries are used to add UI or behavior to the preview iframe where stories are rendered.
|
|
91
|
-
- Presets are used to modify the Storybook configuration, similar to how [users can configure their `main.ts` configurations](https://storybook.js.org/docs/react/api/main-config).
|
|
52
|
+
Also note that you need to add `vitest.setup.ts` file to set up the test environment.
|
|
53
|
+
You need to do that anyway when you set up [storybook] with [vitest].
|
|
92
54
|
|
|
93
|
-
|
|
55
|
+
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.
|
|
94
56
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
To help the community use your addon and understand its capabilities, please document it thoroughly.
|
|
118
|
-
|
|
119
|
-
To get started, replace this README with the content in this sample template, modeled after how essential addons (like [Actions](https://storybook.js.org/docs/essentials/actions)) are documented. Then update the content to describe your addon.
|
|
120
|
-
|
|
121
|
-
### Sample documentation template
|
|
122
|
-
|
|
123
|
-
````md
|
|
124
|
-
# My Addon
|
|
57
|
+
```ts
|
|
58
|
+
// vitest.setup.ts
|
|
59
|
+
import { type StoryContext, setProjectAnnotations } from '@storybook/react'
|
|
60
|
+
import { afterEach, beforeAll, beforeEach, expect } from 'vitest'
|
|
61
|
+
import {
|
|
62
|
+
configureSnapshotBeforeAll,
|
|
63
|
+
configureSnapshotBeforeEach,
|
|
64
|
+
page,
|
|
65
|
+
shouldTakeSnapshot,
|
|
66
|
+
toMatchImageSnapshot,
|
|
67
|
+
} from 'storybook-addon-vis/vitest-setup.js'
|
|
68
|
+
import * as projectAnnotations from './preview'
|
|
69
|
+
|
|
70
|
+
const project = setProjectAnnotations([projectAnnotations])
|
|
71
|
+
|
|
72
|
+
expect.extend({ toMatchImageSnapshot })
|
|
73
|
+
|
|
74
|
+
beforeAll((suite) => {
|
|
75
|
+
project.beforeAll()
|
|
76
|
+
configureSnapshotBeforeAll(suite)
|
|
77
|
+
})
|
|
125
78
|
|
|
126
|
-
|
|
79
|
+
beforeEach(configureSnapshotBeforeEach)
|
|
127
80
|
|
|
128
|
-
|
|
81
|
+
afterEach<{ story?: StoryContext }>(async (ctx) => {
|
|
82
|
+
if (!shouldTakeSnapshot(ctx)) return
|
|
129
83
|
|
|
130
|
-
|
|
131
|
-
|
|
84
|
+
await expect(page.imageSnapshot()).toMatchImageSnapshot()
|
|
85
|
+
})
|
|
132
86
|
```
|
|
133
87
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
```js
|
|
137
|
-
// .storybook/main.ts
|
|
88
|
+
On [storybook], you need to extend the `expect` from `@storybook/test`
|
|
138
89
|
|
|
139
|
-
|
|
140
|
-
|
|
90
|
+
```ts
|
|
91
|
+
// .storybook/preview.tsx
|
|
92
|
+
import { expect } from '@storybook/test'
|
|
93
|
+
import { toMatchImageSnapshot } from '../src/index.js'
|
|
141
94
|
|
|
142
|
-
|
|
143
|
-
// ...rest of config
|
|
144
|
-
addons: [
|
|
145
|
-
'@storybook/addon-essentials'
|
|
146
|
-
'my-addon', // 👈 register the addon here
|
|
147
|
-
],
|
|
148
|
-
};
|
|
95
|
+
expect.extend({ toMatchImageSnapshot })
|
|
149
96
|
|
|
150
|
-
|
|
97
|
+
// ...
|
|
151
98
|
```
|
|
152
99
|
|
|
153
|
-
## Usage
|
|
154
|
-
|
|
155
|
-
The primary way to use this addon is to define the `exampleParameter` parameter. You can do this the
|
|
156
|
-
component level, as below, to affect all stories in the file, or you can do it for a single story.
|
|
157
|
-
|
|
158
|
-
```js
|
|
159
|
-
// Button.stories.ts
|
|
100
|
+
## Usage - automatic snapshot
|
|
160
101
|
|
|
161
|
-
|
|
162
|
-
|
|
102
|
+
The `afterEach` hook above is how to capture the image snapshot automatically,
|
|
103
|
+
when you add a `snapshot` tag to the story.
|
|
163
104
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
// See API section below for available parameters
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
};
|
|
105
|
+
```ts
|
|
106
|
+
// story.tsx
|
|
107
|
+
export default {
|
|
108
|
+
title: 'Button',
|
|
109
|
+
// Take image snapshot automatically for all stories in this file
|
|
110
|
+
tags: ['snapshot']
|
|
111
|
+
}
|
|
175
112
|
|
|
176
|
-
export
|
|
113
|
+
export const MyStory = {
|
|
114
|
+
// Take image snapshot automatically for this story
|
|
115
|
+
tags: ['snapshot'],
|
|
116
|
+
// ...
|
|
117
|
+
}
|
|
177
118
|
```
|
|
178
119
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
## API
|
|
182
|
-
|
|
183
|
-
### Parameters
|
|
184
|
-
|
|
185
|
-
This addon contributes the following parameters to Storybook, under the `myAddon` namespace:
|
|
186
|
-
|
|
187
|
-
#### `disable`
|
|
188
|
-
|
|
189
|
-
Type: `boolean`
|
|
190
|
-
|
|
191
|
-
Disable this addon's behavior. This parameter is most useful to allow overriding at more specific
|
|
192
|
-
levels. For example, if this parameter is set to true at the project level, it could then be
|
|
193
|
-
re-enabled by setting it to false at the meta (component) or story level.
|
|
194
|
-
|
|
195
|
-
### Options
|
|
196
|
-
|
|
197
|
-
When registering this addon, you can configure it with the following options, which are passed when
|
|
198
|
-
registering the addon, like so:
|
|
120
|
+
You can disable snapshot capturing with the `!snapshot` tag,
|
|
121
|
+
much like `!test`.
|
|
199
122
|
|
|
200
123
|
```ts
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
const config: StorybookConfig = {
|
|
207
|
-
// ...rest of config
|
|
208
|
-
addons: [
|
|
209
|
-
'@storybook/essentials',
|
|
210
|
-
{
|
|
211
|
-
name: 'my-addon',
|
|
212
|
-
options: {
|
|
213
|
-
// 👈 options for my-addon go here
|
|
214
|
-
},
|
|
215
|
-
},
|
|
216
|
-
],
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
export default config;
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
#### `useExperimentalBehavior`
|
|
223
|
-
|
|
224
|
-
Type: `boolean`
|
|
225
|
-
|
|
226
|
-
Enable experimental behavior to...
|
|
227
|
-
|
|
228
|
-
````
|
|
229
|
-
|
|
230
|
-
## Release Management
|
|
231
|
-
|
|
232
|
-
### Setup
|
|
233
|
-
|
|
234
|
-
This project is configured to use [auto](https://github.com/intuit/auto) for release management. It generates a changelog and pushes it to both GitHub and npm. Therefore, you need to configure access to both:
|
|
235
|
-
|
|
236
|
-
- [`NPM_TOKEN`](https://docs.npmjs.com/creating-and-viewing-access-tokens#creating-access-tokens) Create a token with both _Read and Publish_ permissions.
|
|
237
|
-
- [`GH_TOKEN`](https://github.com/settings/tokens) Create a token with the `repo` scope.
|
|
238
|
-
|
|
239
|
-
Then open your `package.json` and edit the following fields:
|
|
240
|
-
|
|
241
|
-
- `name`
|
|
242
|
-
- `author`
|
|
243
|
-
- `repository`
|
|
244
|
-
|
|
245
|
-
#### Local
|
|
246
|
-
|
|
247
|
-
To use `auto` locally create a `.env` file at the root of your project and add your tokens to it:
|
|
248
|
-
|
|
249
|
-
```bash
|
|
250
|
-
GH_TOKEN=<value you just got from GitHub>
|
|
251
|
-
NPM_TOKEN=<value you just got from npm>
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
Lastly, **create labels on GitHub**. You’ll use these labels in the future when making changes to the package.
|
|
124
|
+
export default {
|
|
125
|
+
title: 'Button',
|
|
126
|
+
tags: ['snapshot']
|
|
127
|
+
}
|
|
255
128
|
|
|
256
|
-
|
|
257
|
-
|
|
129
|
+
export const MyStory = {
|
|
130
|
+
// Disable image snapshot for this story
|
|
131
|
+
tags: ['!snapshot'],
|
|
132
|
+
// ...
|
|
133
|
+
}
|
|
258
134
|
```
|
|
259
135
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
#### GitHub Actions
|
|
136
|
+
## Usage - manual snapshot
|
|
263
137
|
|
|
264
|
-
|
|
138
|
+
Besides automatic snapshot, you can capture image snapshot manually.
|
|
265
139
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
140
|
+
```ts
|
|
141
|
+
import { page } from 'storybook-addon-vis'
|
|
142
|
+
import { expect } from '@storybook/test'
|
|
269
143
|
|
|
270
|
-
|
|
144
|
+
export const PageSnapshot = {
|
|
145
|
+
// ...
|
|
146
|
+
async play() {
|
|
147
|
+
expect(page.imageSnapshot()).toMatchImageSnapshot()
|
|
148
|
+
}
|
|
149
|
+
}
|
|
271
150
|
|
|
272
|
-
|
|
273
|
-
|
|
151
|
+
export const ElementSnapshot = {
|
|
152
|
+
// ...
|
|
153
|
+
async play({ canvas }) {
|
|
154
|
+
const element = await canvas.getByTestid('subject')
|
|
155
|
+
await expect(page.imageSnapshot([ element ])).toMatchImageSnapshot()
|
|
156
|
+
}
|
|
157
|
+
}
|
|
274
158
|
```
|
|
275
159
|
|
|
276
|
-
That will:
|
|
277
|
-
|
|
278
|
-
- Build and package the addon code
|
|
279
|
-
- Bump the version
|
|
280
|
-
- Push a release to GitHub and npm
|
|
281
|
-
- Push a changelog to GitHub
|
|
282
|
-
|
|
283
|
-
|
|
284
160
|
[jest-image-snapshot]: https://github.com/americanexpress/jest-image-snapshot
|
|
161
|
+
[storybook-addon-vis]: https://github.com/repobuddy/storybook-addon-vis
|
|
285
162
|
[storybook]: https://storybook.js.org
|
|
286
163
|
[vitest]: https://vitest.dev/
|
|
@@ -15,8 +15,10 @@ import { imageSnapshot } from './page.image_snapshot'
|
|
|
15
15
|
let ctx: Awaited<typeof import('@vitest/browser/context')>
|
|
16
16
|
|
|
17
17
|
if ((globalThis as any).__vitest_browser__) {
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
import('@vitest/browser/context').then((m) => {
|
|
19
|
+
ctx = m
|
|
20
|
+
page.extend({ imageSnapshot })
|
|
21
|
+
})
|
|
20
22
|
}
|
|
21
23
|
|
|
22
24
|
// TODO: stub the functions within the `CDPSession`
|
|
@@ -88,5 +90,3 @@ export const server = new Proxy<{
|
|
|
88
90
|
return (target as any)[prop] ?? (ctx?.server as any)[prop]
|
|
89
91
|
},
|
|
90
92
|
})
|
|
91
|
-
|
|
92
|
-
page.extend({ imageSnapshot })
|