storybook-addon-vis 0.6.0 → 0.6.2

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.
@@ -0,0 +1,8 @@
1
+ import {getByAltText,getByRole,getByTestId,getByText,getByTitle,getByPlaceholderText,getByLabelText}from'@storybook/test';import {join}from'pathe';import E from'dedent';import M from'pixelmatch';var r={name:"",testFilepath:"",taskName:"",snapshot:{}};var g=Symbol.for("storybook-addon-vis/imageSnapshot"),u=Symbol.for("storybook-addon-vis/imageSnapshotStub");async function y(t){let e=r.snapshot[r.testFilepath][r.id].index,a=t?.customizeSnapshotId?`${t.customizeSnapshotId(r.id,e)}.png`:`${r.id}-${e}.png`,o=join(r.baselineDir,a);return h.existFile(o)}function f(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 s=n.getContext("2d");s.drawImage(o,0,0),e(s.getImageData(0,0,n.width,n.height));},o.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(o=>{e.toBlob(n=>{let s=new FileReader;s.onload=()=>{o(s.result);},s.readAsDataURL(n);});})}async function I(t){let e=r.snapshot[r.testFilepath][r.id].index++,a=t?.customizeSnapshotId?`${t.customizeSnapshotId(r.id,e)}.png`:`${r.id}-${e}.png`,o=join(r.baselineDir,a),n=join(r.resultDir,a),s=join(r.diffDir,a),i=await this.screenshot({base64:!0,path:n,element:t?.element,timeout:t?.timeout??3e3}),m=await f(i.base64);return {type:g,snapshotFilename:a,baselinePath:o,resultPath:n,diffPath:s,base64:i.base64,image:m}}var c;globalThis.__vitest_browser__&&import('@vitest/browser/context').then(t=>{c=t,A.extend({imageSnapshot:I,hasImageSnapshot:y});});function st(){return c?.cdp()??{}}var h=new Proxy({},{get(t,e){return t[e]??(c?.commands)[e]}}),A=new Proxy({getByAltText:getByAltText,getByRole:getByRole,getByTestId:getByTestId,getByText:getByText,getByTitle:getByTitle,getByPlaceholder:getByPlaceholderText,getByLabelText:getByLabelText},{get(t,e){return t[e]??c?.page?(c?.page)[e]:()=>{if(e==="imageSnapshot")return {type:u};console.info(`\`page.${e.toString()}\` does not exist when running in browser`);}}}),w=new Proxy({},{get(t,e){return t[e]??(c?.server)[e]}});function b(t){return !!t&&t.type===g}function P(t){if(!b(t))throw new Error("Expected subject to be an image snapshot")}function mt(t){return `${t.replace(/[^a-z0-9]/gi,"-").toLowerCase()}`}var D=({width:t,height:e})=>a=>{if(a.width===t&&a.height===e)return a;let o=(s,i)=>i<=a.height&&s<=a.width,n=new ImageData(t,e,{colorSpace:a.colorSpace});for(let s=0;s<e;s++)for(let i=0;i<t;i++){let m=t*s+i<<2;if(o(i,s)){let p=a.width*s+i<<2;n.data[m]=a.data[p],n.data[m+1]=a.data[p+1],n.data[m+2]=a.data[p+2],n.data[m+3]=a.data[p+3];}else n.data[m]=0,n.data[m+1]=0,n.data[m+2]=0,n.data[m+3]=64;}return n};async function yt(t,e){let a=await t;if(a?.type===u)return l;if(!b(a)){if(a?.path&&a?.base64)return {pass:!1,actual:a,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:a,message:()=>`\`toMatchImageSnapshot()\` expects the subject to be an element, locator, or result of \`page.imageSnapshot()\`, but got: \`${a}\``}}P(a);let o=await j(a.baselinePath);if(!o)return await h.copyFile(a.resultPath,a.baselinePath),l;let n=await f(o),[s,i]=k(a.image,n),{pass:m,diffAmount:p,diffImage:d}=U(i,s,e);return m?l:w.config.snapshotOptions.updateSnapshot==="all"?(await $(`${a.baselinePath}`,s),l):(await $(`${a.diffPath}`,d),{pass:!1,message:()=>E`Snapshot \`${r.taskName}\` mismatched
2
+
3
+ ${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`}.`}
4
+
5
+ Expected: '${a.baselinePath}'
6
+ Actual: '${a.resultPath}'
7
+ Difference: '${a.diffPath}'`})}var l={pass:!0,message:()=>""};function j(t){return h.readFile(t,{encoding:"base64"}).catch(()=>{})}async function $(t,e){let a=(await x(e)).split(",")[1];return h.writeFile(t,a,"base64url")}function k(t,e){if(_(t,e))return [t,e];let a=L(t,e),o=D(a);return [o(t),o(e)]}function L(t,e){let a=Math.max(t.width,e.width),o=Math.max(t.height,e.height);return {width:a,height:o}}function _(t,e){return t.width===e.width&&t.height===e.height}function U(t,e,{failureThreshold:a=0,failureThresholdType:o="pixel",diffOptions:n}={}){let{width:s,height:i}=e,m=new ImageData(s,i),p=M(e.data,t.data,m.data,s,i,n),d=N({failureThresholdType:o,width:s,height:i},p);return {pass:d<=a,diffAmount:d,diffImage:m}}function N({failureThresholdType:t,width:e,height:a},o){switch(t){case"pixel":return o;case"percent":return o/(e*a)*100;default:throw new Error(`Invalid failureThresholdType: ${t}`)}}export{r as a,st as b,h as c,A as d,w as e,mt as f,yt as g};//# sourceMappingURL=chunk-B3UZM6KO.js.map
8
+ //# sourceMappingURL=chunk-B3UZM6KO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/state.ts","../src/@vitest/browser/constants.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/@vitest/browser/image_snapshot.logic.ts","../src/image_resizer.ts","../src/expect.to_match_image_snapshot.ts"],"names":["state","imageSnapshotSymbol","imageSnapshotStubSymbol","hasImageSnapshot","options","index","snapshotFilename","baselinePath","join","commands","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","imageSnapshot","resultPath","diffPath","screenshot","image","m","page","cdp","target","prop","getByAltText","getByRole","getByTestId","getByText","getByTitle","getByPlaceholderText","getByLabelText","server","isImageSnapshot","subject","assertImageSnapshot","toSnapshotId","taskName","createImageResizer","width","height","inArea","x","y","result","idx","old","toMatchImageSnapshot","actual","success","baseline","tryReadSnapshot","originalImage","resultImage","baselineImage","alignImagesToSameSize","pass","diffAmount","diffImage","compareImage","writeSnapshot","dedent","path","content","image1","image2","isSameSize","size","getMaxSize","resize","failureThreshold","failureThresholdType","diffOptions","pixelDiff","pixelmatch","toThresholdUnit"],"mappings":"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","file":"chunk-B3UZM6KO.js","sourcesContent":["export const state = {\n\tname: '',\n\ttestFilepath: '',\n\ttaskName: '',\n\tsnapshot: {},\n} as {\n\tid: string\n\tname: string\n\ttestFilepath: string\n\ttestFilename: string\n\tprojectDir: string\n\tbaselineDir: string\n\tresultDir: string\n\tdiffDir: string\n\ttaskName: string\n\tsnapshot: Record<string, 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","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 index = state.snapshot[state.testFilepath][state.id]!.index\n\tconst snapshotFilename = options?.customizeSnapshotId\n\t\t? `${options.customizeSnapshotId(state.id, index)}.png`\n\t\t: `${state.id}-${index}.png`\n\tconst baselinePath = join(state.baselineDir, snapshotFilename)\n\treturn commands.existFile(baselinePath)\n}\n","export function toImageData(base64: string) {\n\treturn new Promise<ImageData>((resolve, reject) => {\n\t\tconst img = new Image()\n\t\timg.src = `data:image/png;base64,${base64}`\n\n\t\timg.onload = () => {\n\t\t\tconst canvas = document.createElement('canvas')\n\t\t\tcanvas.width = img.width\n\t\t\tcanvas.height = img.height\n\t\t\tconst ctx = canvas.getContext('2d')!\n\t\t\tctx.drawImage(img, 0, 0)\n\t\t\tresolve(ctx.getImageData(0, 0, canvas.width, canvas.height))\n\t\t}\n\n\t\timg.onerror = () => {\n\t\t\treject(new Error('Failed to load image'))\n\t\t}\n\t})\n}\n\nexport async function toDataURL(imageData: ImageData) {\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = imageData.width\n\tcanvas.height = imageData.height\n\tconst ctx = canvas.getContext('2d')!\n\tctx.putImageData(imageData, 0, 0)\n\treturn new Promise<string>((resolve) => {\n\t\tcanvas.toBlob((blob) => {\n\t\t\tconst reader = new FileReader()\n\t\t\treader.onload = () => {\n\t\t\t\tresolve(reader.result as string)\n\t\t\t}\n\t\t\treader.readAsDataURL(blob!)\n\t\t})\n\t})\n}\n","import type { BrowserPage } from '@vitest/browser/context'\nimport { join } from 'pathe'\nimport { 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 index = state.snapshot[state.testFilepath][state.id]!.index++\n\tconst snapshotFilename = options?.customizeSnapshotId\n\t\t? `${options.customizeSnapshotId(state.id, index)}.png`\n\t\t: `${state.id}-${index}.png`\n\tconst baselinePath = join(state.baselineDir, snapshotFilename)\n\tconst resultPath = join(state.resultDir, snapshotFilename)\n\tconst diffPath = join(state.diffDir, snapshotFilename)\n\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t\ttimeout: options?.timeout ?? 3000,\n\t})\n\tconst image = await toImageData(screenshot.base64)\n\n\treturn {\n\t\ttype: imageSnapshotSymbol,\n\t\tsnapshotFilename,\n\t\tbaselinePath,\n\t\tresultPath,\n\t\tdiffPath,\n\t\tbase64: screenshot.base64,\n\t\timage,\n\t}\n}\n","import {\n\tgetByAltText,\n\tgetByLabelText,\n\tgetByPlaceholderText,\n\tgetByRole,\n\tgetByTestId,\n\tgetByText,\n\tgetByTitle,\n} from '@storybook/test'\nimport type { BrowserCommands, BrowserPage, CDPSession, Platform } from '@vitest/browser/context'\nimport type { SerializedConfig } from 'vitest'\nimport { imageSnapshotStubSymbol } from './constants'\nimport { hasImageSnapshot } from './page.has_image_snapshot'\nimport { imageSnapshot } from './page.image_snapshot'\n\nlet ctx: Awaited<typeof import('@vitest/browser/context')>\n\nif ((globalThis as any).__vitest_browser__) {\n\timport('@vitest/browser/context').then((m) => {\n\t\tctx = m\n\t\tpage.extend({ imageSnapshot, hasImageSnapshot })\n\t})\n}\n\n// TODO: stub the functions within the `CDPSession`\n// when we better understands the use cases\nexport function cdp(): CDPSession {\n\treturn ctx?.cdp() ?? ({} as any)\n}\n\nexport const commands = new Proxy<BrowserCommands>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.commands as any)[prop]\n\t},\n})\n\nexport const page = new Proxy<BrowserPage>(\n\t{\n\t\tgetByAltText,\n\t\tgetByRole,\n\t\tgetByTestId,\n\t\tgetByText,\n\t\tgetByTitle,\n\t\tgetByPlaceholder: getByPlaceholderText,\n\t\tgetByLabelText,\n\t} as any,\n\t{\n\t\tget(target, prop) {\n\t\t\treturn ((target as any)[prop] ?? ctx?.page)\n\t\t\t\t? (ctx?.page as any)[prop]\n\t\t\t\t: () => {\n\t\t\t\t\t\tif (prop === 'imageSnapshot') {\n\t\t\t\t\t\t\treturn { type: imageSnapshotStubSymbol }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.info(`\\`page.${prop.toString()}\\` does not exist when running in browser`)\n\t\t\t\t\t}\n\t\t},\n\t},\n)\n\nexport const server = new Proxy<{\n\t/**\n\t * Platform the Vitest server is running on.\n\t * The same as calling `process.platform` on the server.\n\t */\n\tplatform: Platform\n\t/**\n\t * Runtime version of the Vitest server.\n\t * The same as calling `process.version` on the server.\n\t */\n\tversion: string\n\t/**\n\t * Name of the browser provider.\n\t */\n\tprovider: string\n\t/**\n\t * Name of the current browser.\n\t */\n\tbrowser: string\n\t/**\n\t * Available commands for the browser.\n\t * @see {@link https://vitest.dev/guide/browser/commands}\n\t */\n\tcommands: BrowserCommands\n\t/**\n\t * Serialized test config.\n\t */\n\tconfig: SerializedConfig\n}>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.server as any)[prop]\n\t},\n})\n","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","export const createImageResizer =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tfor (let x = 0; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (inArea(x, y)) {\n\t\t\t\t\tconst old = (image.width * y + x) << 2\n\t\t\t\t\tresult.data[idx] = image.data[old]\n\t\t\t\t\tresult.data[idx + 1] = image.data[old + 1]\n\t\t\t\t\tresult.data[idx + 2] = image.data[old + 2]\n\t\t\t\t\tresult.data[idx + 3] = image.data[old + 3]\n\t\t\t\t} else {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n// slower\nexport const createImageResizer2 =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tconst copyBytes = image.width * 4\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tresult.data.set(image.data.slice(y * image.width, copyBytes), y * width * 4)\n\t\t\tfor (let x = image.width; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (!inArea(x, y)) {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n","import type { AsyncExpectationResult } from '@vitest/expect'\nimport dedent from 'dedent'\nimport pixelmatch, { type PixelmatchOptions } from 'pixelmatch'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands, 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\tfailureThreshold?: number | undefined\n}\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: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t'`toMatchImageSnapshot()` expects the subject to be the result of `page.imageSnapshot()`, but seems like you are using `page.screenshot()`?',\n\t\t\t}\n\t\t}\n\n\t\tif (typeof subject !== 'object') {\n\t\t\treturn {\n\t\t\t\tpass: false,\n\t\t\t\tactual: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t`\\`toMatchImageSnapshot()\\` expects the subject to be an element, locator, or result of \\`page.imageSnapshot()\\`, but got: \\`${subject}\\``,\n\t\t\t}\n\t\t}\n\t\t// subject = await page.imageSnapshot({ element: actual })\n\t}\n\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\tif (server.config.snapshotOptions.updateSnapshot === 'all') {\n\t\t\tawait writeSnapshot(`${subject.baselinePath}`, resultImage)\n\t\t\treturn success\n\t\t}\n\t\tawait writeSnapshot(`${subject.diffPath}`, diffImage)\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tmessage: () =>\n\t\t\t\tdedent`Snapshot \\`${state.taskName}\\` mismatched\n\n\t\t\t${\n\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\t\t\t\t\t? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`\n\t\t\t\t\t\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\t\t}\n\n\t\t\tExpected: '${subject.baselinePath}'\n\t\t\tActual: '${subject.resultPath}'\n\t\t\tDifference: '${subject.diffPath}'`,\n\t\t}\n\t}\n\treturn success\n}\n\nconst success = {\n\tpass: true,\n\tmessage: () => '',\n}\n\nfunction tryReadSnapshot(path: string): Promise<string | undefined> {\n\treturn commands.readFile(path, { encoding: 'base64' }).catch(() => undefined)\n}\nasync function writeSnapshot(path: string, image: ImageData) {\n\tconst content = (await toDataURL(image)).split(',')[1]!\n\treturn commands.writeFile(path, content, 'base64url')\n}\n\nfunction alignImagesToSameSize(image1: ImageData, image2: ImageData): [image1: ImageData, image2: ImageData] {\n\tif (isSameSize(image1, image2)) {\n\t\treturn [image1, image2]\n\t}\n\tconst size = getMaxSize(image1, image2)\n\tconst resize = createImageResizer(size)\n\treturn [resize(image1), resize(image2)]\n}\n\nfunction getMaxSize(image1: ImageData, image2: ImageData) {\n\tconst width = Math.max(image1.width, image2.width)\n\tconst height = Math.max(image1.height, image2.height)\n\treturn { width, height }\n}\n\nfunction isSameSize(image1: ImageData, image2: ImageData) {\n\treturn image1.width === image2.width && image1.height === image2.height\n}\n\nfunction compareImage(\n\tbaselineImage: ImageData,\n\tresultImage: ImageData,\n\t{ failureThreshold = 0, failureThresholdType = 'pixel', diffOptions }: MatchImageSnapshotOptions = {},\n) {\n\tconst { width, height } = resultImage\n\tconst diffImage = new ImageData(width, height)\n\n\tconst pixelDiff = pixelmatch(resultImage.data, baselineImage.data, diffImage.data, width, height, diffOptions)\n\tconst diffAmount = toThresholdUnit({ failureThresholdType, width, height }, pixelDiff)\n\n\treturn {\n\t\tpass: diffAmount <= failureThreshold,\n\t\tdiffAmount,\n\t\tdiffImage,\n\t}\n}\n\nfunction toThresholdUnit(\n\t{ failureThresholdType, width, height }: { failureThresholdType: 'pixel' | 'percent'; width: number; height: number },\n\tpixelDiff: number,\n): number {\n\tswitch (failureThresholdType) {\n\t\tcase 'pixel':\n\t\t\treturn pixelDiff\n\t\tcase 'percent':\n\t\t\treturn (pixelDiff / (width * height)) * 100\n\t\tdefault:\n\t\t\tthrow new Error(`Invalid failureThresholdType: ${failureThresholdType}`)\n\t}\n}\n"]}
@@ -1,6 +1,7 @@
1
- import * as vitest_node from 'vitest/node';
2
- import { BrowserPage } from '@vitest/browser/context';
1
+ import { BrowserPage, CDPSession, BrowserCommands, Platform } from '@vitest/browser/context';
2
+ import { AsyncExpectationResult } from '@vitest/expect';
3
3
  import { PixelmatchOptions } from 'pixelmatch';
4
+ import { SerializedConfig } from 'vitest';
4
5
 
5
6
  interface ImageSnapshotOptions extends CustomizeSnapshotIdOptions {
6
7
  element?: Element;
@@ -51,6 +52,7 @@ type MatchImageSnapshotOptions = {
51
52
  failureThresholdType?: 'pixel' | 'percent' | undefined;
52
53
  failureThreshold?: number | undefined;
53
54
  };
55
+ declare function toMatchImageSnapshot(actual: any, options?: MatchImageSnapshotOptions | undefined): AsyncExpectationResult;
54
56
 
55
57
  interface CopyFileCommand {
56
58
  copyFile(src: string, dest: string): Promise<void>;
@@ -94,23 +96,37 @@ declare global {
94
96
  }
95
97
  }
96
98
 
97
- declare function storybookVis(): {
98
- name: string;
99
- config(this: void): {
100
- test: {
101
- browser: {
102
- name: string;
103
- commands: {
104
- existDir: vitest_node.BrowserCommand<[path: string]>;
105
- existFile: vitest_node.BrowserCommand<[path: string]>;
106
- copyFile: vitest_node.BrowserCommand<[src: string, dest: string]>;
107
- rmDir: vitest_node.BrowserCommand<[path: string]>;
108
- isCI: () => Promise<boolean>;
109
- getSnapshotPlatform: () => Promise<NodeJS.Platform | "local">;
110
- };
111
- };
112
- };
113
- };
99
+ declare function cdp(): CDPSession;
100
+ declare const commands: BrowserCommands;
101
+ declare const page: BrowserPage;
102
+ declare const server: {
103
+ /**
104
+ * Platform the Vitest server is running on.
105
+ * The same as calling `process.platform` on the server.
106
+ */
107
+ platform: Platform;
108
+ /**
109
+ * Runtime version of the Vitest server.
110
+ * The same as calling `process.version` on the server.
111
+ */
112
+ version: string;
113
+ /**
114
+ * Name of the browser provider.
115
+ */
116
+ provider: string;
117
+ /**
118
+ * Name of the current browser.
119
+ */
120
+ browser: string;
121
+ /**
122
+ * Available commands for the browser.
123
+ * @see {@link https://vitest.dev/guide/browser/commands}
124
+ */
125
+ commands: BrowserCommands;
126
+ /**
127
+ * Serialized test config.
128
+ */
129
+ config: SerializedConfig;
114
130
  };
115
131
 
116
- export { storybookVis };
132
+ export { type CustomizeSnapshotIdOptions as C, type ImageSnapshotMatcher as I, type MatchImageSnapshotOptions as M, commands as a, type ImageSnapshotOptions as b, cdp as c, type ImageSnapshot as d, page as p, server as s, toMatchImageSnapshot as t };
package/dist/index.d.ts CHANGED
@@ -1,136 +1,12 @@
1
- import { BrowserPage, CDPSession, BrowserCommands, Platform } from '@vitest/browser/context';
2
- import { AsyncExpectationResult } from '@vitest/expect';
3
- import { PixelmatchOptions } from 'pixelmatch';
4
- import { SerializedConfig } from 'vitest';
5
-
6
- interface ImageSnapshotOptions extends CustomizeSnapshotIdOptions {
7
- element?: Element;
8
- timeout?: number | undefined;
9
- }
10
- type CustomizeSnapshotIdOptions = {
11
- /**
12
- * Customize the snapshot id. This is used as the filename of the snapshot:
13
- *
14
- * `${snapshotId}.png`
15
- *
16
- * @param id The id of the snapshot.
17
- * @param index The index of the snapshot.
18
- */
19
- customizeSnapshotId?: (id: string, index: number) => string;
20
- };
21
- type ImageSnapshot = {
22
- type: symbol;
23
- snapshotFilename: string;
24
- baselinePath: string;
25
- resultPath: string;
26
- diffPath: string;
27
- base64: string;
28
- image: ImageData;
29
- };
30
-
31
- interface HasImageSnapshotAction {
32
- hasImageSnapshot(this: BrowserPage, options?: CustomizeSnapshotIdOptions | undefined): Promise<boolean>;
33
- }
34
-
35
- interface ImageSnapshotAction {
36
- imageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions | undefined): Promise<ImageSnapshot>;
37
- }
38
-
39
- interface ImageSnapshotMatcher {
40
- toMatchImageSnapshot(options?: MatchImageSnapshotOptions): Promise<void>;
41
- }
42
- type MatchImageSnapshotOptions = {
43
- /**
44
- * Custom options passed to 'pixelmatch'
45
- */
46
- diffOptions?: PixelmatchOptions | undefined;
47
- /**
48
- * Failure threshold should measure in `pixel` or `percent`.
49
- *
50
- * Default is `pixel`.
51
- */
52
- failureThresholdType?: 'pixel' | 'percent' | undefined;
53
- failureThreshold?: number | undefined;
54
- };
55
- declare function toMatchImageSnapshot(actual: any, options?: MatchImageSnapshotOptions | undefined): AsyncExpectationResult;
56
-
57
- interface CopyFileCommand {
58
- copyFile(src: string, dest: string): Promise<void>;
59
- }
60
-
61
- interface ExistDirCommand {
62
- existDir: (path: string) => Promise<boolean>;
63
- }
64
-
65
- interface ExistFileCommand {
66
- existFile: (path: string) => Promise<boolean>;
67
- }
68
-
69
- interface GetSnapshotPlatformCommand {
70
- /**
71
- * Get the platform id of the snapshot `{platform}[-ci]`.
72
- *
73
- * This is useful to control where the snapshot is stored.
74
- */
75
- getSnapshotPlatform: () => Promise<string>;
76
- }
77
-
78
- interface IsCICommand {
79
- isCI: () => Promise<boolean>;
80
- }
81
-
82
- interface RmDirCommand {
83
- rmDir: (path: string) => Promise<void>;
84
- }
85
-
86
- declare module '@vitest/browser/context' {
87
- interface BrowserPage extends ImageSnapshotAction, HasImageSnapshotAction {
88
- }
89
- interface BrowserCommands extends CopyFileCommand, ExistDirCommand, GetSnapshotPlatformCommand, RmDirCommand, IsCICommand, ExistFileCommand {
90
- }
91
- }
92
- declare global {
93
- namespace jest {
94
- interface Matchers<R, T> extends ImageSnapshotMatcher {
95
- }
96
- }
97
- }
98
-
99
- declare function cdp(): CDPSession;
100
- declare const commands: BrowserCommands;
101
- declare const page: BrowserPage;
102
- declare const server: {
103
- /**
104
- * Platform the Vitest server is running on.
105
- * The same as calling `process.platform` on the server.
106
- */
107
- platform: Platform;
108
- /**
109
- * Runtime version of the Vitest server.
110
- * The same as calling `process.version` on the server.
111
- */
112
- version: string;
113
- /**
114
- * Name of the browser provider.
115
- */
116
- provider: string;
117
- /**
118
- * Name of the current browser.
119
- */
120
- browser: string;
121
- /**
122
- * Available commands for the browser.
123
- * @see {@link https://vitest.dev/guide/browser/commands}
124
- */
125
- commands: BrowserCommands;
126
- /**
127
- * Serialized test config.
128
- */
129
- config: SerializedConfig;
130
- };
1
+ import { M as MatchImageSnapshotOptions } from './context-BX71asjt.js';
2
+ export { C as CustomizeSnapshotIdOptions, d as ImageSnapshot, I as ImageSnapshotMatcher, b as ImageSnapshotOptions, c as cdp, a as commands, p as page, s as server, t as toMatchImageSnapshot } from './context-BX71asjt.js';
3
+ import '@vitest/browser/context';
4
+ import '@vitest/expect';
5
+ import 'pixelmatch';
6
+ import 'vitest';
131
7
 
132
8
  declare function defineSnapshotParam(snapshot: MatchImageSnapshotOptions): {
133
9
  snapshot: MatchImageSnapshotOptions;
134
10
  };
135
11
 
136
- export { type CustomizeSnapshotIdOptions, type ImageSnapshot, type ImageSnapshotMatcher, type ImageSnapshotOptions, type MatchImageSnapshotOptions, cdp, commands, defineSnapshotParam, page, server, toMatchImageSnapshot };
12
+ export { MatchImageSnapshotOptions, defineSnapshotParam };
package/dist/index.js CHANGED
@@ -1,8 +1,2 @@
1
- import R from'dedent';import j from'pixelmatch';import {getByAltText,getByRole,getByTestId,getByText,getByTitle,getByPlaceholderText,getByLabelText}from'@storybook/test';import {join}from'pathe';var g=Symbol.for("storybook-addon-vis/imageSnapshot"),f=Symbol.for("storybook-addon-vis/imageSnapshotStub");var i={name:"",testFilepath:"",taskName:"",snapshot:{}};async function y(t){let e=i.snapshot[i.id].index,a=t?.customizeSnapshotId?`${t.customizeSnapshotId(i.id,e)}.png`:`${i.id}-${e}.png`,o=join(i.baselineDir,a);return h.existFile(o)}function u(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 s=n.getContext("2d");s.drawImage(o,0,0),e(s.getImageData(0,0,n.width,n.height));},o.onerror=()=>{a(new Error("Failed to load image"));};})}async function b(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 s=new FileReader;s.onload=()=>{o(s.result);},s.readAsDataURL(n);});})}async function I(t){let e=i.snapshot[i.id].index++,a=t?.customizeSnapshotId?`${t.customizeSnapshotId(i.id,e)}.png`:`${i.id}-${e}.png`,o=join(i.baselineDir,a),n=join(i.resultDir,a),s=join(i.diffDir,a),r=await this.screenshot({base64:!0,path:n,element:t?.element,timeout:t?.timeout??3e3}),m=await u(r.base64);return {type:g,snapshotFilename:a,baselinePath:o,resultPath:n,diffPath:s,base64:r.base64,image:m}}var c;globalThis.__vitest_browser__&&import('@vitest/browser/context').then(t=>{c=t,E.extend({imageSnapshot:I,hasImageSnapshot:y});});function st(){return c?.cdp()??{}}var h=new Proxy({},{get(t,e){return t[e]??(c?.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]??c?.page?(c?.page)[e]:()=>{if(e==="imageSnapshot")return {type:f};console.info(`\`page.${e.toString()}\` does not exist when running in browser`);}}}),w=new Proxy({},{get(t,e){return t[e]??(c?.server)[e]}});function x(t){return !!t&&t.type===g}function P(t){if(!x(t))throw new Error("Expected subject to be an image snapshot")}var D=({width:t,height:e})=>a=>{if(a.width===t&&a.height===e)return a;let o=(s,r)=>r<=a.height&&s<=a.width,n=new ImageData(t,e,{colorSpace:a.colorSpace});for(let s=0;s<e;s++)for(let r=0;r<t;r++){let m=t*s+r<<2;if(o(r,s)){let p=a.width*s+r<<2;n.data[m]=a.data[p],n.data[m+1]=a.data[p+1],n.data[m+2]=a.data[p+2],n.data[m+3]=a.data[p+3];}else n.data[m]=0,n.data[m+1]=0,n.data[m+2]=0,n.data[m+3]=64;}return n};async function xt(t,e){let a=await t;if(a?.type===f)return l;if(!x(a)){if(a?.path&&a?.base64)return {pass:!1,actual:a,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:a,message:()=>`\`toMatchImageSnapshot()\` expects the subject to be an element, locator, or result of \`page.imageSnapshot()\`, but got: \`${a}\``}}P(a);let o=await F(a.baselinePath);if(!o)return await h.copyFile(a.resultPath,a.baselinePath),l;let n=await u(o),[s,r]=_(a.image,n),{pass:m,diffAmount:p,diffImage:d}=U(r,s,e);return m?l:w.config.snapshotOptions.updateSnapshot==="all"?(await $(`${a.baselinePath}`,s),l):(await $(`${a.diffPath}`,d),{pass:!1,message:()=>R`Snapshot \`${i.taskName}\` mismatched
2
-
3
- ${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`}.`}
4
-
5
- Expected: '${a.baselinePath}'
6
- Actual: '${a.resultPath}'
7
- Difference: '${a.diffPath}'`})}var l={pass:!0,message:()=>""};function F(t){return h.readFile(t,{encoding:"base64"}).catch(()=>{})}async function $(t,e){let a=(await b(e)).split(",")[1];return h.writeFile(t,a,"base64url")}function _(t,e){if(L(t,e))return [t,e];let a=k(t,e),o=D(a);return [o(t),o(e)]}function k(t,e){let a=Math.max(t.width,e.width),o=Math.max(t.height,e.height);return {width:a,height:o}}function L(t,e){return t.width===e.width&&t.height===e.height}function U(t,e,{failureThreshold:a=0,failureThresholdType:o="pixel",diffOptions:n}={}){let{width:s,height:r}=e,m=new ImageData(s,r),p=j(e.data,t.data,m.data,s,r,n),d=N({failureThresholdType:o,width:s,height:r},p);return {pass:d<=a,diffAmount:d,diffImage:m}}function N({failureThresholdType:t,width:e,height:a},o){switch(t){case"pixel":return o;case"percent":return o/(e*a)*100;default:throw new Error(`Invalid failureThresholdType: ${t}`)}}function bt(t){return {snapshot:t}}export{st as cdp,h as commands,bt as defineSnapshotParam,E as page,w as server,xt as toMatchImageSnapshot};//# sourceMappingURL=index.js.map
1
+ export{b as cdp,c as commands,d as page,e as server,g as toMatchImageSnapshot}from'./chunk-B3UZM6KO.js';function m(t){return {snapshot:t}}export{m as defineSnapshotParam};//# sourceMappingURL=index.js.map
8
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/@vitest/browser/constants.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/@vitest/browser/image_snapshot.logic.ts","../src/image_resizer.ts","../src/expect.to_match_image_snapshot.ts","../src/param.ts"],"names":["imageSnapshotSymbol","imageSnapshotStubSymbol","state","hasImageSnapshot","options","index","snapshotFilename","baselinePath","join","commands","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","imageSnapshot","resultPath","diffPath","screenshot","image","m","page","cdp","target","prop","getByAltText","getByRole","getByTestId","getByText","getByTitle","getByPlaceholderText","getByLabelText","server","isImageSnapshot","subject","assertImageSnapshot","createImageResizer","width","height","inArea","x","y","result","idx","old","toMatchImageSnapshot","actual","success","baseline","tryReadSnapshot","originalImage","resultImage","baselineImage","alignImagesToSameSize","pass","diffAmount","diffImage","compareImage","writeSnapshot","dedent","path","content","image1","image2","isSameSize","size","getMaxSize","resize","failureThreshold","failureThresholdType","diffOptions","pixelDiff","pixelmatch","toThresholdUnit","defineSnapshotParam","snapshot"],"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 const state = {\n\tname: '',\n\ttestFilepath: '',\n\ttaskName: '',\n\tsnapshot: {},\n} as {\n\tid: string\n\tname: string\n\ttestFilepath: string\n\ttestFilename: string\n\tprojectDir: string\n\tbaselineDir: string\n\tresultDir: string\n\tdiffDir: string\n\ttaskName: string\n\tsnapshot: Record<string, { index: number }>\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 index = state.snapshot[state.id]!.index\n\tconst snapshotFilename = options?.customizeSnapshotId\n\t\t? `${options.customizeSnapshotId(state.id, index)}.png`\n\t\t: `${state.id}-${index}.png`\n\tconst baselinePath = join(state.baselineDir, snapshotFilename)\n\treturn commands.existFile(baselinePath)\n}\n","export function toImageData(base64: string) {\n\treturn new Promise<ImageData>((resolve, reject) => {\n\t\tconst img = new Image()\n\t\timg.src = `data:image/png;base64,${base64}`\n\n\t\timg.onload = () => {\n\t\t\tconst canvas = document.createElement('canvas')\n\t\t\tcanvas.width = img.width\n\t\t\tcanvas.height = img.height\n\t\t\tconst ctx = canvas.getContext('2d')!\n\t\t\tctx.drawImage(img, 0, 0)\n\t\t\tresolve(ctx.getImageData(0, 0, canvas.width, canvas.height))\n\t\t}\n\n\t\timg.onerror = () => {\n\t\t\treject(new Error('Failed to load image'))\n\t\t}\n\t})\n}\n\nexport async function toDataURL(imageData: ImageData) {\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = imageData.width\n\tcanvas.height = imageData.height\n\tconst ctx = canvas.getContext('2d')!\n\tctx.putImageData(imageData, 0, 0)\n\treturn new Promise<string>((resolve) => {\n\t\tcanvas.toBlob((blob) => {\n\t\t\tconst reader = new FileReader()\n\t\t\treader.onload = () => {\n\t\t\t\tresolve(reader.result as string)\n\t\t\t}\n\t\t\treader.readAsDataURL(blob!)\n\t\t})\n\t})\n}\n","import type { BrowserPage } from '@vitest/browser/context'\nimport { join } from 'pathe'\nimport { 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 index = state.snapshot[state.id]!.index++\n\tconst snapshotFilename = options?.customizeSnapshotId\n\t\t? `${options.customizeSnapshotId(state.id, index)}.png`\n\t\t: `${state.id}-${index}.png`\n\tconst baselinePath = join(state.baselineDir, snapshotFilename)\n\tconst resultPath = join(state.resultDir, snapshotFilename)\n\tconst diffPath = join(state.diffDir, snapshotFilename)\n\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t\ttimeout: options?.timeout ?? 3000,\n\t})\n\tconst image = await toImageData(screenshot.base64)\n\n\treturn {\n\t\ttype: imageSnapshotSymbol,\n\t\tsnapshotFilename,\n\t\tbaselinePath,\n\t\tresultPath,\n\t\tdiffPath,\n\t\tbase64: screenshot.base64,\n\t\timage,\n\t}\n}\n","import {\n\tgetByAltText,\n\tgetByLabelText,\n\tgetByPlaceholderText,\n\tgetByRole,\n\tgetByTestId,\n\tgetByText,\n\tgetByTitle,\n} from '@storybook/test'\nimport type { BrowserCommands, BrowserPage, CDPSession, Platform } from '@vitest/browser/context'\nimport type { SerializedConfig } from 'vitest'\nimport { imageSnapshotStubSymbol } from './constants'\nimport { hasImageSnapshot } from './page.has_image_snapshot'\nimport { imageSnapshot } from './page.image_snapshot'\n\nlet ctx: Awaited<typeof import('@vitest/browser/context')>\n\nif ((globalThis as any).__vitest_browser__) {\n\timport('@vitest/browser/context').then((m) => {\n\t\tctx = m\n\t\tpage.extend({ imageSnapshot, hasImageSnapshot })\n\t})\n}\n\n// TODO: stub the functions within the `CDPSession`\n// when we better understands the use cases\nexport function cdp(): CDPSession {\n\treturn ctx?.cdp() ?? ({} as any)\n}\n\nexport const commands = new Proxy<BrowserCommands>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.commands as any)[prop]\n\t},\n})\n\nexport const page = new Proxy<BrowserPage>(\n\t{\n\t\tgetByAltText,\n\t\tgetByRole,\n\t\tgetByTestId,\n\t\tgetByText,\n\t\tgetByTitle,\n\t\tgetByPlaceholder: getByPlaceholderText,\n\t\tgetByLabelText,\n\t} as any,\n\t{\n\t\tget(target, prop) {\n\t\t\treturn ((target as any)[prop] ?? ctx?.page)\n\t\t\t\t? (ctx?.page as any)[prop]\n\t\t\t\t: () => {\n\t\t\t\t\t\tif (prop === 'imageSnapshot') {\n\t\t\t\t\t\t\treturn { type: imageSnapshotStubSymbol }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.info(`\\`page.${prop.toString()}\\` does not exist when running in browser`)\n\t\t\t\t\t}\n\t\t},\n\t},\n)\n\nexport const server = new Proxy<{\n\t/**\n\t * Platform the Vitest server is running on.\n\t * The same as calling `process.platform` on the server.\n\t */\n\tplatform: Platform\n\t/**\n\t * Runtime version of the Vitest server.\n\t * The same as calling `process.version` on the server.\n\t */\n\tversion: string\n\t/**\n\t * Name of the browser provider.\n\t */\n\tprovider: string\n\t/**\n\t * Name of the current browser.\n\t */\n\tbrowser: string\n\t/**\n\t * Available commands for the browser.\n\t * @see {@link https://vitest.dev/guide/browser/commands}\n\t */\n\tcommands: BrowserCommands\n\t/**\n\t * Serialized test config.\n\t */\n\tconfig: SerializedConfig\n}>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.server as any)[prop]\n\t},\n})\n","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","export const createImageResizer =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tfor (let x = 0; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (inArea(x, y)) {\n\t\t\t\t\tconst old = (image.width * y + x) << 2\n\t\t\t\t\tresult.data[idx] = image.data[old]\n\t\t\t\t\tresult.data[idx + 1] = image.data[old + 1]\n\t\t\t\t\tresult.data[idx + 2] = image.data[old + 2]\n\t\t\t\t\tresult.data[idx + 3] = image.data[old + 3]\n\t\t\t\t} else {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n// slower\nexport const createImageResizer2 =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tconst copyBytes = image.width * 4\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tresult.data.set(image.data.slice(y * image.width, copyBytes), y * width * 4)\n\t\t\tfor (let x = image.width; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (!inArea(x, y)) {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n","import type { AsyncExpectationResult } from '@vitest/expect'\nimport dedent from 'dedent'\nimport pixelmatch, { type PixelmatchOptions } from 'pixelmatch'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands, 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\tfailureThreshold?: number | undefined\n}\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: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t'`toMatchImageSnapshot()` expects the subject to be the result of `page.imageSnapshot()`, but seems like you are using `page.screenshot()`?',\n\t\t\t}\n\t\t}\n\n\t\tif (typeof subject !== 'object') {\n\t\t\treturn {\n\t\t\t\tpass: false,\n\t\t\t\tactual: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t`\\`toMatchImageSnapshot()\\` expects the subject to be an element, locator, or result of \\`page.imageSnapshot()\\`, but got: \\`${subject}\\``,\n\t\t\t}\n\t\t}\n\t\t// subject = await page.imageSnapshot({ element: actual })\n\t}\n\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\tif (server.config.snapshotOptions.updateSnapshot === 'all') {\n\t\t\tawait writeSnapshot(`${subject.baselinePath}`, resultImage)\n\t\t\treturn success\n\t\t}\n\t\tawait writeSnapshot(`${subject.diffPath}`, diffImage)\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tmessage: () =>\n\t\t\t\tdedent`Snapshot \\`${state.taskName}\\` mismatched\n\n\t\t\t${\n\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\t\t\t\t\t? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`\n\t\t\t\t\t\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\t\t}\n\n\t\t\tExpected: '${subject.baselinePath}'\n\t\t\tActual: '${subject.resultPath}'\n\t\t\tDifference: '${subject.diffPath}'`,\n\t\t}\n\t}\n\treturn success\n}\n\nconst success = {\n\tpass: true,\n\tmessage: () => '',\n}\n\nfunction tryReadSnapshot(path: string): Promise<string | undefined> {\n\treturn commands.readFile(path, { encoding: 'base64' }).catch(() => undefined)\n}\nasync function writeSnapshot(path: string, image: ImageData) {\n\tconst content = (await toDataURL(image)).split(',')[1]!\n\treturn commands.writeFile(path, content, 'base64url')\n}\n\nfunction alignImagesToSameSize(image1: ImageData, image2: ImageData): [image1: ImageData, image2: ImageData] {\n\tif (isSameSize(image1, image2)) {\n\t\treturn [image1, image2]\n\t}\n\tconst size = getMaxSize(image1, image2)\n\tconst resize = createImageResizer(size)\n\treturn [resize(image1), resize(image2)]\n}\n\nfunction getMaxSize(image1: ImageData, image2: ImageData) {\n\tconst width = Math.max(image1.width, image2.width)\n\tconst height = Math.max(image1.height, image2.height)\n\treturn { width, height }\n}\n\nfunction isSameSize(image1: ImageData, image2: ImageData) {\n\treturn image1.width === image2.width && image1.height === image2.height\n}\n\nfunction compareImage(\n\tbaselineImage: ImageData,\n\tresultImage: ImageData,\n\t{ failureThreshold = 0, failureThresholdType = 'pixel', diffOptions }: MatchImageSnapshotOptions = {},\n) {\n\tconst { width, height } = resultImage\n\tconst diffImage = new ImageData(width, height)\n\n\tconst pixelDiff = pixelmatch(resultImage.data, baselineImage.data, diffImage.data, width, height, diffOptions)\n\tconst diffAmount = toThresholdUnit({ failureThresholdType, width, height }, pixelDiff)\n\n\treturn {\n\t\tpass: diffAmount <= failureThreshold,\n\t\tdiffAmount,\n\t\tdiffImage,\n\t}\n}\n\nfunction toThresholdUnit(\n\t{ failureThresholdType, width, height }: { failureThresholdType: 'pixel' | 'percent'; width: number; height: number },\n\tpixelDiff: number,\n): number {\n\tswitch (failureThresholdType) {\n\t\tcase 'pixel':\n\t\t\treturn pixelDiff\n\t\tcase 'percent':\n\t\t\treturn (pixelDiff / (width * height)) * 100\n\t\tdefault:\n\t\t\tthrow new Error(`Invalid failureThresholdType: ${failureThresholdType}`)\n\t}\n}\n","import type { MatchImageSnapshotOptions } from './expect.to_match_image_snapshot'\n\nexport function defineSnapshotParam(snapshot: MatchImageSnapshotOptions) {\n\treturn { snapshot }\n}\n"]}
1
+ {"version":3,"sources":["../src/param.ts"],"names":["defineSnapshotParam","snapshot"],"mappings":"wGAEO,SAASA,EAAoBC,CAAqC,CAAA,CACxE,OAAO,CAAE,QAAA,CAAAA,CAAS,CACnB","file":"index.js","sourcesContent":["import type { MatchImageSnapshotOptions } from './expect.to_match_image_snapshot'\n\nexport function defineSnapshotParam(snapshot: MatchImageSnapshotOptions) {\n\treturn { snapshot }\n}\n"]}
@@ -1,133 +1,8 @@
1
- import { BrowserPage, CDPSession, BrowserCommands, Platform } from '@vitest/browser/context';
2
- import { AsyncExpectationResult } from '@vitest/expect';
3
- import { PixelmatchOptions } from 'pixelmatch';
4
- import { SerializedConfig } from 'vitest';
5
-
6
- interface ImageSnapshotOptions extends CustomizeSnapshotIdOptions {
7
- element?: Element;
8
- timeout?: number | undefined;
9
- }
10
- type CustomizeSnapshotIdOptions = {
11
- /**
12
- * Customize the snapshot id. This is used as the filename of the snapshot:
13
- *
14
- * `${snapshotId}.png`
15
- *
16
- * @param id The id of the snapshot.
17
- * @param index The index of the snapshot.
18
- */
19
- customizeSnapshotId?: (id: string, index: number) => string;
20
- };
21
- type ImageSnapshot = {
22
- type: symbol;
23
- snapshotFilename: string;
24
- baselinePath: string;
25
- resultPath: string;
26
- diffPath: string;
27
- base64: string;
28
- image: ImageData;
29
- };
30
-
31
- interface HasImageSnapshotAction {
32
- hasImageSnapshot(this: BrowserPage, options?: CustomizeSnapshotIdOptions | undefined): Promise<boolean>;
33
- }
34
-
35
- interface ImageSnapshotAction {
36
- imageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions | undefined): Promise<ImageSnapshot>;
37
- }
38
-
39
- interface ImageSnapshotMatcher {
40
- toMatchImageSnapshot(options?: MatchImageSnapshotOptions): Promise<void>;
41
- }
42
- type MatchImageSnapshotOptions = {
43
- /**
44
- * Custom options passed to 'pixelmatch'
45
- */
46
- diffOptions?: PixelmatchOptions | undefined;
47
- /**
48
- * Failure threshold should measure in `pixel` or `percent`.
49
- *
50
- * Default is `pixel`.
51
- */
52
- failureThresholdType?: 'pixel' | 'percent' | undefined;
53
- failureThreshold?: number | undefined;
54
- };
55
- declare function toMatchImageSnapshot(actual: any, options?: MatchImageSnapshotOptions | undefined): AsyncExpectationResult;
56
-
57
- interface CopyFileCommand {
58
- copyFile(src: string, dest: string): Promise<void>;
59
- }
60
-
61
- interface ExistDirCommand {
62
- existDir: (path: string) => Promise<boolean>;
63
- }
64
-
65
- interface ExistFileCommand {
66
- existFile: (path: string) => Promise<boolean>;
67
- }
68
-
69
- interface GetSnapshotPlatformCommand {
70
- /**
71
- * Get the platform id of the snapshot `{platform}[-ci]`.
72
- *
73
- * This is useful to control where the snapshot is stored.
74
- */
75
- getSnapshotPlatform: () => Promise<string>;
76
- }
77
-
78
- interface IsCICommand {
79
- isCI: () => Promise<boolean>;
80
- }
81
-
82
- interface RmDirCommand {
83
- rmDir: (path: string) => Promise<void>;
84
- }
85
-
86
- declare module '@vitest/browser/context' {
87
- interface BrowserPage extends ImageSnapshotAction, HasImageSnapshotAction {
88
- }
89
- interface BrowserCommands extends CopyFileCommand, ExistDirCommand, GetSnapshotPlatformCommand, RmDirCommand, IsCICommand, ExistFileCommand {
90
- }
91
- }
92
- declare global {
93
- namespace jest {
94
- interface Matchers<R, T> extends ImageSnapshotMatcher {
95
- }
96
- }
97
- }
98
-
99
- declare function cdp(): CDPSession;
100
- declare const commands: BrowserCommands;
101
- declare const page: BrowserPage;
102
- declare const server: {
103
- /**
104
- * Platform the Vitest server is running on.
105
- * The same as calling `process.platform` on the server.
106
- */
107
- platform: Platform;
108
- /**
109
- * Runtime version of the Vitest server.
110
- * The same as calling `process.version` on the server.
111
- */
112
- version: string;
113
- /**
114
- * Name of the browser provider.
115
- */
116
- provider: string;
117
- /**
118
- * Name of the current browser.
119
- */
120
- browser: string;
121
- /**
122
- * Available commands for the browser.
123
- * @see {@link https://vitest.dev/guide/browser/commands}
124
- */
125
- commands: BrowserCommands;
126
- /**
127
- * Serialized test config.
128
- */
129
- config: SerializedConfig;
130
- };
1
+ export { I as ImageSnapshotMatcher, M as MatchImageSnapshotOptions, c as cdp, a as commands, p as page, s as server, t as toMatchImageSnapshot } from './context-BX71asjt.js';
2
+ import '@vitest/browser/context';
3
+ import '@vitest/expect';
4
+ import 'pixelmatch';
5
+ import 'vitest';
131
6
 
132
7
  /**
133
8
  * Determine should snapshot be taken.
@@ -162,7 +37,10 @@ declare function configureSnapshotBeforeAll(suite: {
162
37
  declare function configureSnapshotBeforeEach(ctx: {
163
38
  task: {
164
39
  name: string;
40
+ file: {
41
+ filepath: string;
42
+ };
165
43
  };
166
44
  }): void;
167
45
 
168
- export { type ImageSnapshotMatcher, type MatchImageSnapshotOptions, type VisOptions, cdp, commands, configureSnapshotBeforeAll, configureSnapshotBeforeEach, page, server, shouldTakeSnapshot, toMatchImageSnapshot };
46
+ export { type VisOptions, configureSnapshotBeforeAll, configureSnapshotBeforeEach, shouldTakeSnapshot };
@@ -1,8 +1,2 @@
1
- import {join,basename,dirname,relative}from'pathe';import {getByAltText,getByRole,getByTestId,getByText,getByTitle,getByPlaceholderText,getByLabelText}from'@storybook/test';import R from'dedent';import k from'pixelmatch';var f=Symbol.for("storybook-addon-vis/imageSnapshot"),g=Symbol.for("storybook-addon-vis/imageSnapshotStub");var s={name:"",testFilepath:"",taskName:"",snapshot:{}};async function I(t){let e=s.snapshot[s.id].index,a=t?.customizeSnapshotId?`${t.customizeSnapshotId(s.id,e)}.png`:`${s.id}-${e}.png`,o=join(s.baselineDir,a);return h.existFile(o)}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 w(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);});})}async function D(t){let e=s.snapshot[s.id].index++,a=t?.customizeSnapshotId?`${t.customizeSnapshotId(s.id,e)}.png`:`${s.id}-${e}.png`,o=join(s.baselineDir,a),n=join(s.resultDir,a),r=join(s.diffDir,a),i=await this.screenshot({base64:!0,path:n,element:t?.element,timeout:t?.timeout??3e3}),m=await l(i.base64);return {type:f,snapshotFilename:a,baselinePath:o,resultPath:n,diffPath:r,base64:i.base64,image:m}}var c;globalThis.__vitest_browser__&&import('@vitest/browser/context').then(t=>{c=t,M.extend({imageSnapshot:D,hasImageSnapshot:I});});function ht(){return c?.cdp()??{}}var h=new Proxy({},{get(t,e){return t[e]??(c?.commands)[e]}}),M=new Proxy({getByAltText:getByAltText,getByRole:getByRole,getByTestId:getByTestId,getByText:getByText,getByTitle:getByTitle,getByPlaceholder:getByPlaceholderText,getByLabelText:getByLabelText},{get(t,e){return t[e]??c?.page?(c?.page)[e]:()=>{if(e==="imageSnapshot")return {type:g};console.info(`\`page.${e.toString()}\` does not exist when running in browser`);}}}),P=new Proxy({},{get(t,e){return t[e]??(c?.server)[e]}});function y(t){return !!t&&t.type===f}function $(t){if(!y(t))throw new Error("Expected subject to be an image snapshot")}function B(t){return `${t.replace(/[^a-z0-9]/gi,"-").toLowerCase()}`}var T=({width:t,height:e})=>a=>{if(a.width===t&&a.height===e)return a;let o=(r,i)=>i<=a.height&&r<=a.width,n=new ImageData(t,e,{colorSpace:a.colorSpace});for(let r=0;r<e;r++)for(let i=0;i<t;i++){let m=t*r+i<<2;if(o(i,r)){let p=a.width*r+i<<2;n.data[m]=a.data[p],n.data[m+1]=a.data[p+1],n.data[m+2]=a.data[p+2],n.data[m+3]=a.data[p+3];}else n.data[m]=0,n.data[m+1]=0,n.data[m+2]=0,n.data[m+3]=64;}return n};async function Dt(t,e){let a=await t;if(a?.type===g)return u;if(!y(a)){if(a?.path&&a?.base64)return {pass:!1,actual:a,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:a,message:()=>`\`toMatchImageSnapshot()\` expects the subject to be an element, locator, or result of \`page.imageSnapshot()\`, but got: \`${a}\``}}$(a);let o=await L(a.baselinePath);if(!o)return await h.copyFile(a.resultPath,a.baselinePath),u;let n=await l(o),[r,i]=N(a.image,n),{pass:m,diffAmount:p,diffImage:d}=H(i,r,e);return m?u:P.config.snapshotOptions.updateSnapshot==="all"?(await v(`${a.baselinePath}`,r),u):(await v(`${a.diffPath}`,d),{pass:!1,message:()=>R`Snapshot \`${s.taskName}\` mismatched
2
-
3
- ${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`}.`}
4
-
5
- Expected: '${a.baselinePath}'
6
- Actual: '${a.resultPath}'
7
- Difference: '${a.diffPath}'`})}var u={pass:!0,message:()=>""};function L(t){return h.readFile(t,{encoding:"base64"}).catch(()=>{})}async function v(t,e){let a=(await w(e)).split(",")[1];return h.writeFile(t,a,"base64url")}function N(t,e){if(V(t,e))return [t,e];let a=U(t,e),o=T(a);return [o(t),o(e)]}function U(t,e){let a=Math.max(t.width,e.width),o=Math.max(t.height,e.height);return {width:a,height:o}}function V(t,e){return t.width===e.width&&t.height===e.height}function H(t,e,{failureThreshold:a=0,failureThresholdType:o="pixel",diffOptions:n}={}){let{width:r,height:i}=e,m=new ImageData(r,i),p=k(e.data,t.data,m.data,r,i,n),d=q({failureThresholdType:o,width:r,height:i},p);return {pass:d<=a,diffAmount:d,diffImage:m}}function q({failureThresholdType:t,width:e,height:a},o){switch(t){case"pixel":return o;case"percent":return o/(e*a)*100;default:throw new Error(`Invalid failureThresholdType: ${t}`)}}function $t(t){return t.story?.tags?t.story.tags.lastIndexOf("!snapshot")<t.story.tags.lastIndexOf("snapshot"):!1}async function Ot(t,e){s.name=t.name,s.testFilepath=t.file.filepath,s.testFilename=basename(s.testFilepath),s.projectDir=s.testFilepath.slice(0,-s.name.length);let a=join(s.projectDir,e?.snapshotPath??`__snapshots__/${await h.getSnapshotPlatform()}`),o=dirname(s.testFilepath);s.baselineDir=relative(o,join(a,s.testFilename)),s.resultDir=relative(o,join(a,"__results__",s.testFilename)),s.diffDir=relative(o,join(a,"__diff_output__",s.testFilename)),await h.rmDir(s.resultDir),await h.rmDir(s.diffDir);}function _t(t){s.taskName=t.task.name;let e=s.id=B(s.taskName);s.snapshot[e]=s.snapshot[e]??{index:1};}export{ht as cdp,h as commands,Ot as configureSnapshotBeforeAll,_t as configureSnapshotBeforeEach,M as page,P as server,$t as shouldTakeSnapshot,Dt as toMatchImageSnapshot};//# sourceMappingURL=vitest-setup.js.map
1
+ import {a,c,f}from'./chunk-B3UZM6KO.js';export{b as cdp,c as commands,d as page,e as server,g as toMatchImageSnapshot}from'./chunk-B3UZM6KO.js';import {basename,join,dirname,relative}from'pathe';function u(e){return e.story?.tags?e.story.tags.lastIndexOf("!snapshot")<e.story.tags.lastIndexOf("snapshot"):!1}async function y(e,s){a.name=e.name,a.testFilepath=e.file.filepath,a.testFilename=basename(a.testFilepath),a.projectDir=a.testFilepath.slice(0,-a.name.length);let r=join(a.projectDir,s?.snapshotPath??`__snapshots__/${await c.getSnapshotPlatform()}`),o=dirname(a.testFilepath);a.baselineDir=relative(o,join(r,a.testFilename)),a.resultDir=relative(o,join(r,"__results__",a.testFilename)),a.diffDir=relative(o,join(r,"__diff_output__",a.testFilename)),a.snapshot[a.testFilepath]||(a.snapshot[a.testFilepath]={},await c.rmDir(a.resultDir),await c.rmDir(a.diffDir));}function k(e){a.taskName=e.task.name;let s=a.id=f(a.taskName);a.snapshot[a.testFilepath][s]=a.snapshot[a.testFilepath][s]??{index:1};}export{y as configureSnapshotBeforeAll,k as configureSnapshotBeforeEach,u as shouldTakeSnapshot};//# sourceMappingURL=vitest-setup.js.map
8
2
  //# sourceMappingURL=vitest-setup.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/@vitest/browser/page.has_image_snapshot.ts","../src/state.ts","../src/image_data.ts","../src/@vitest/browser/page.image_snapshot.ts","../src/@vitest/browser/context.ts","../src/@vitest/browser/image_snapshot.logic.ts","../src/image_resizer.ts","../src/expect.to_match_image_snapshot.ts","../src/tags.ts","../src/vitest-setup.ts"],"names":["imageSnapshotSymbol","imageSnapshotStubSymbol","state","hasImageSnapshot","options","index","snapshotFilename","baselinePath","join","commands","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","imageSnapshot","resultPath","diffPath","screenshot","image","m","page","cdp","target","prop","getByAltText","getByRole","getByTestId","getByText","getByTitle","getByPlaceholderText","getByLabelText","server","isImageSnapshot","subject","assertImageSnapshot","toSnapshotId","taskName","createImageResizer","width","height","inArea","x","y","result","idx","old","toMatchImageSnapshot","actual","success","baseline","tryReadSnapshot","originalImage","resultImage","baselineImage","alignImagesToSameSize","pass","diffAmount","diffImage","compareImage","writeSnapshot","dedent","path","content","image1","image2","isSameSize","size","getMaxSize","resize","failureThreshold","failureThresholdType","diffOptions","pixelDiff","pixelmatch","toThresholdUnit","shouldTakeSnapshot","configureSnapshotBeforeAll","suite","basename","snapshotPath","currentDir","dirname","relative","configureSnapshotBeforeEach","id"],"mappings":"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","file":"vitest-setup.js","sourcesContent":["export const imageSnapshotSymbol = Symbol.for('storybook-addon-vis/imageSnapshot')\n\nexport const imageSnapshotStubSymbol = Symbol.for('storybook-addon-vis/imageSnapshotStub')\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 index = state.snapshot[state.id]!.index\n\tconst snapshotFilename = options?.customizeSnapshotId\n\t\t? `${options.customizeSnapshotId(state.id, index)}.png`\n\t\t: `${state.id}-${index}.png`\n\tconst baselinePath = join(state.baselineDir, snapshotFilename)\n\treturn commands.existFile(baselinePath)\n}\n","export const state = {\n\tname: '',\n\ttestFilepath: '',\n\ttaskName: '',\n\tsnapshot: {},\n} as {\n\tid: string\n\tname: string\n\ttestFilepath: string\n\ttestFilename: string\n\tprojectDir: string\n\tbaselineDir: string\n\tresultDir: string\n\tdiffDir: string\n\ttaskName: string\n\tsnapshot: Record<string, { index: number }>\n}\n","export function toImageData(base64: string) {\n\treturn new Promise<ImageData>((resolve, reject) => {\n\t\tconst img = new Image()\n\t\timg.src = `data:image/png;base64,${base64}`\n\n\t\timg.onload = () => {\n\t\t\tconst canvas = document.createElement('canvas')\n\t\t\tcanvas.width = img.width\n\t\t\tcanvas.height = img.height\n\t\t\tconst ctx = canvas.getContext('2d')!\n\t\t\tctx.drawImage(img, 0, 0)\n\t\t\tresolve(ctx.getImageData(0, 0, canvas.width, canvas.height))\n\t\t}\n\n\t\timg.onerror = () => {\n\t\t\treject(new Error('Failed to load image'))\n\t\t}\n\t})\n}\n\nexport async function toDataURL(imageData: ImageData) {\n\tconst canvas = document.createElement('canvas')\n\tcanvas.width = imageData.width\n\tcanvas.height = imageData.height\n\tconst ctx = canvas.getContext('2d')!\n\tctx.putImageData(imageData, 0, 0)\n\treturn new Promise<string>((resolve) => {\n\t\tcanvas.toBlob((blob) => {\n\t\t\tconst reader = new FileReader()\n\t\t\treader.onload = () => {\n\t\t\t\tresolve(reader.result as string)\n\t\t\t}\n\t\t\treader.readAsDataURL(blob!)\n\t\t})\n\t})\n}\n","import type { BrowserPage } from '@vitest/browser/context'\nimport { join } from 'pathe'\nimport { 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 index = state.snapshot[state.id]!.index++\n\tconst snapshotFilename = options?.customizeSnapshotId\n\t\t? `${options.customizeSnapshotId(state.id, index)}.png`\n\t\t: `${state.id}-${index}.png`\n\tconst baselinePath = join(state.baselineDir, snapshotFilename)\n\tconst resultPath = join(state.resultDir, snapshotFilename)\n\tconst diffPath = join(state.diffDir, snapshotFilename)\n\tconst screenshot = await this.screenshot({\n\t\tbase64: true,\n\t\tpath: resultPath,\n\t\telement: options?.element,\n\t\ttimeout: options?.timeout ?? 3000,\n\t})\n\tconst image = await toImageData(screenshot.base64)\n\n\treturn {\n\t\ttype: imageSnapshotSymbol,\n\t\tsnapshotFilename,\n\t\tbaselinePath,\n\t\tresultPath,\n\t\tdiffPath,\n\t\tbase64: screenshot.base64,\n\t\timage,\n\t}\n}\n","import {\n\tgetByAltText,\n\tgetByLabelText,\n\tgetByPlaceholderText,\n\tgetByRole,\n\tgetByTestId,\n\tgetByText,\n\tgetByTitle,\n} from '@storybook/test'\nimport type { BrowserCommands, BrowserPage, CDPSession, Platform } from '@vitest/browser/context'\nimport type { SerializedConfig } from 'vitest'\nimport { imageSnapshotStubSymbol } from './constants'\nimport { hasImageSnapshot } from './page.has_image_snapshot'\nimport { imageSnapshot } from './page.image_snapshot'\n\nlet ctx: Awaited<typeof import('@vitest/browser/context')>\n\nif ((globalThis as any).__vitest_browser__) {\n\timport('@vitest/browser/context').then((m) => {\n\t\tctx = m\n\t\tpage.extend({ imageSnapshot, hasImageSnapshot })\n\t})\n}\n\n// TODO: stub the functions within the `CDPSession`\n// when we better understands the use cases\nexport function cdp(): CDPSession {\n\treturn ctx?.cdp() ?? ({} as any)\n}\n\nexport const commands = new Proxy<BrowserCommands>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.commands as any)[prop]\n\t},\n})\n\nexport const page = new Proxy<BrowserPage>(\n\t{\n\t\tgetByAltText,\n\t\tgetByRole,\n\t\tgetByTestId,\n\t\tgetByText,\n\t\tgetByTitle,\n\t\tgetByPlaceholder: getByPlaceholderText,\n\t\tgetByLabelText,\n\t} as any,\n\t{\n\t\tget(target, prop) {\n\t\t\treturn ((target as any)[prop] ?? ctx?.page)\n\t\t\t\t? (ctx?.page as any)[prop]\n\t\t\t\t: () => {\n\t\t\t\t\t\tif (prop === 'imageSnapshot') {\n\t\t\t\t\t\t\treturn { type: imageSnapshotStubSymbol }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.info(`\\`page.${prop.toString()}\\` does not exist when running in browser`)\n\t\t\t\t\t}\n\t\t},\n\t},\n)\n\nexport const server = new Proxy<{\n\t/**\n\t * Platform the Vitest server is running on.\n\t * The same as calling `process.platform` on the server.\n\t */\n\tplatform: Platform\n\t/**\n\t * Runtime version of the Vitest server.\n\t * The same as calling `process.version` on the server.\n\t */\n\tversion: string\n\t/**\n\t * Name of the browser provider.\n\t */\n\tprovider: string\n\t/**\n\t * Name of the current browser.\n\t */\n\tbrowser: string\n\t/**\n\t * Available commands for the browser.\n\t * @see {@link https://vitest.dev/guide/browser/commands}\n\t */\n\tcommands: BrowserCommands\n\t/**\n\t * Serialized test config.\n\t */\n\tconfig: SerializedConfig\n}>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.server as any)[prop]\n\t},\n})\n","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","export const createImageResizer =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tfor (let x = 0; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (inArea(x, y)) {\n\t\t\t\t\tconst old = (image.width * y + x) << 2\n\t\t\t\t\tresult.data[idx] = image.data[old]\n\t\t\t\t\tresult.data[idx + 1] = image.data[old + 1]\n\t\t\t\t\tresult.data[idx + 2] = image.data[old + 2]\n\t\t\t\t\tresult.data[idx + 3] = image.data[old + 3]\n\t\t\t\t} else {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result\n\t}\n\n// slower\nexport const createImageResizer2 =\n\t({ width, height }: { width: number; height: number }) =>\n\t(image: ImageData) => {\n\t\tif (image.width === width && image.height === height) {\n\t\t\treturn image\n\t\t}\n\t\tconst inArea = (x: number, y: number) => y <= image.height && x <= image.width\n\t\tconst result = new ImageData(width, height, { colorSpace: image.colorSpace })\n\n\t\tconst copyBytes = image.width * 4\n\t\tfor (let y = 0; y < height; y++) {\n\t\t\tresult.data.set(image.data.slice(y * image.width, copyBytes), y * width * 4)\n\t\t\tfor (let x = image.width; x < width; x++) {\n\t\t\t\tconst idx = (width * y + x) << 2\n\t\t\t\tif (!inArea(x, y)) {\n\t\t\t\t\tresult.data[idx] = 0\n\t\t\t\t\tresult.data[idx + 1] = 0\n\t\t\t\t\tresult.data[idx + 2] = 0\n\t\t\t\t\tresult.data[idx + 3] = 64\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result\n\t}\n","import type { AsyncExpectationResult } from '@vitest/expect'\nimport dedent from 'dedent'\nimport pixelmatch, { type PixelmatchOptions } from 'pixelmatch'\nimport { imageSnapshotStubSymbol } from './@vitest/browser/constants.js'\nimport { commands, 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\tfailureThreshold?: number | undefined\n}\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: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t'`toMatchImageSnapshot()` expects the subject to be the result of `page.imageSnapshot()`, but seems like you are using `page.screenshot()`?',\n\t\t\t}\n\t\t}\n\n\t\tif (typeof subject !== 'object') {\n\t\t\treturn {\n\t\t\t\tpass: false,\n\t\t\t\tactual: subject,\n\t\t\t\tmessage: () =>\n\t\t\t\t\t`\\`toMatchImageSnapshot()\\` expects the subject to be an element, locator, or result of \\`page.imageSnapshot()\\`, but got: \\`${subject}\\``,\n\t\t\t}\n\t\t}\n\t\t// subject = await page.imageSnapshot({ element: actual })\n\t}\n\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\tif (server.config.snapshotOptions.updateSnapshot === 'all') {\n\t\t\tawait writeSnapshot(`${subject.baselinePath}`, resultImage)\n\t\t\treturn success\n\t\t}\n\t\tawait writeSnapshot(`${subject.diffPath}`, diffImage)\n\t\treturn {\n\t\t\tpass: false,\n\t\t\tmessage: () =>\n\t\t\t\tdedent`Snapshot \\`${state.taskName}\\` mismatched\n\n\t\t\t${\n\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\t\t\t\t\t? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`\n\t\t\t\t\t\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\t\t}\n\n\t\t\tExpected: '${subject.baselinePath}'\n\t\t\tActual: '${subject.resultPath}'\n\t\t\tDifference: '${subject.diffPath}'`,\n\t\t}\n\t}\n\treturn success\n}\n\nconst success = {\n\tpass: true,\n\tmessage: () => '',\n}\n\nfunction tryReadSnapshot(path: string): Promise<string | undefined> {\n\treturn commands.readFile(path, { encoding: 'base64' }).catch(() => undefined)\n}\nasync function writeSnapshot(path: string, image: ImageData) {\n\tconst content = (await toDataURL(image)).split(',')[1]!\n\treturn commands.writeFile(path, content, 'base64url')\n}\n\nfunction alignImagesToSameSize(image1: ImageData, image2: ImageData): [image1: ImageData, image2: ImageData] {\n\tif (isSameSize(image1, image2)) {\n\t\treturn [image1, image2]\n\t}\n\tconst size = getMaxSize(image1, image2)\n\tconst resize = createImageResizer(size)\n\treturn [resize(image1), resize(image2)]\n}\n\nfunction getMaxSize(image1: ImageData, image2: ImageData) {\n\tconst width = Math.max(image1.width, image2.width)\n\tconst height = Math.max(image1.height, image2.height)\n\treturn { width, height }\n}\n\nfunction isSameSize(image1: ImageData, image2: ImageData) {\n\treturn image1.width === image2.width && image1.height === image2.height\n}\n\nfunction compareImage(\n\tbaselineImage: ImageData,\n\tresultImage: ImageData,\n\t{ failureThreshold = 0, failureThresholdType = 'pixel', diffOptions }: MatchImageSnapshotOptions = {},\n) {\n\tconst { width, height } = resultImage\n\tconst diffImage = new ImageData(width, height)\n\n\tconst pixelDiff = pixelmatch(resultImage.data, baselineImage.data, diffImage.data, width, height, diffOptions)\n\tconst diffAmount = toThresholdUnit({ failureThresholdType, width, height }, pixelDiff)\n\n\treturn {\n\t\tpass: diffAmount <= failureThreshold,\n\t\tdiffAmount,\n\t\tdiffImage,\n\t}\n}\n\nfunction toThresholdUnit(\n\t{ failureThresholdType, width, height }: { failureThresholdType: 'pixel' | 'percent'; width: number; height: number },\n\tpixelDiff: number,\n): number {\n\tswitch (failureThresholdType) {\n\t\tcase 'pixel':\n\t\t\treturn pixelDiff\n\t\tcase 'percent':\n\t\t\treturn (pixelDiff / (width * height)) * 100\n\t\tdefault:\n\t\t\tthrow new Error(`Invalid failureThresholdType: ${failureThresholdType}`)\n\t}\n}\n","/**\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 { commands } from './@vitest/browser/context.js'\nimport { toSnapshotId } from './@vitest/browser/image_snapshot.logic.js'\nimport { state } from './state.js'\n\nexport * from './@vitest/browser/context.js'\nexport * from './expect.to_match_image_snapshot.js'\nexport * from './tags.js'\n\n/**\n * The project parameters for the snapshot.\n *\n * These parameters are set per project in the `.storybook/preview.ts` file.\n */\nexport type VisOptions = {\n\t/**\n\t * The snapshot folder relative to the root of the project.\n\t */\n\tsnapshotPath: string\n}\n\nexport async function configureSnapshotBeforeAll(\n\tsuite: { file: { filepath: string }; name: string },\n\toptions?: VisOptions,\n) {\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 snapshotPath = join(\n\t\tstate.projectDir,\n\t\toptions?.snapshotPath ?? `__snapshots__/${await commands.getSnapshotPlatform()}`,\n\t)\n\tconst currentDir = dirname(state.testFilepath)\n\tstate.baselineDir = relative(currentDir, join(snapshotPath, state.testFilename))\n\tstate.resultDir = relative(currentDir, join(snapshotPath, '__results__', state.testFilename))\n\tstate.diffDir = relative(currentDir, join(snapshotPath, '__diff_output__', state.testFilename))\n\n\tawait commands.rmDir(state.resultDir)\n\tawait commands.rmDir(state.diffDir)\n}\n\nexport function configureSnapshotBeforeEach(ctx: { task: { name: string } }) {\n\tstate.taskName = ctx.task.name\n\tconst id = (state.id = toSnapshotId(state.taskName))\n\tstate.snapshot[id] = state.snapshot[id] ?? { index: 1 }\n}\n"]}
1
+ {"version":3,"sources":["../src/tags.ts","../src/vitest-setup.ts"],"names":["shouldTakeSnapshot","ctx","configureSnapshotBeforeAll","suite","options","state","basename","snapshotPath","join","commands","currentDir","dirname","relative","configureSnapshotBeforeEach","id","toSnapshotId"],"mappings":"mMAOO,SAASA,CAAAA,CAAmBC,EAAqC,CACvE,OAAKA,EAAI,KAAO,EAAA,IAAA,CACTA,EAAI,KAAM,CAAA,IAAA,CAAK,WAAY,CAAA,WAAW,EAAIA,CAAI,CAAA,KAAA,CAAM,KAAK,WAAY,CAAA,UAAU,EADzD,CAE9B,CAAA,CCYA,eAAsBC,CAAAA,CACrBC,EACAC,CACC,CAAA,CACDC,EAAM,IAAOF,CAAAA,CAAAA,CAAM,KACnBE,CAAM,CAAA,YAAA,CAAeF,CAAM,CAAA,IAAA,CAAK,SAChCE,CAAM,CAAA,YAAA,CAAeC,SAASD,CAAM,CAAA,YAAY,EAChDA,CAAM,CAAA,UAAA,CAAaA,CAAM,CAAA,YAAA,CAAa,MAAM,CAAG,CAAA,CAACA,EAAM,IAAK,CAAA,MAAM,EACjE,IAAME,CAAAA,CAAeC,IACpBH,CAAAA,CAAAA,CAAM,WACND,CAAS,EAAA,YAAA,EAAgB,iBAAiB,MAAMK,CAAAA,CAAS,qBAAqB,CAAA,CAC/E,CACMC,CAAAA,CAAAA,CAAaC,QAAQN,CAAM,CAAA,YAAY,EAC7CA,CAAM,CAAA,WAAA,CAAcO,SAASF,CAAYF,CAAAA,IAAAA,CAAKD,CAAcF,CAAAA,CAAAA,CAAM,YAAY,CAAC,CAAA,CAC/EA,EAAM,SAAYO,CAAAA,QAAAA,CAASF,EAAYF,IAAKD,CAAAA,CAAAA,CAAc,aAAeF,CAAAA,CAAAA,CAAM,YAAY,CAAC,CAAA,CAC5FA,EAAM,OAAUO,CAAAA,QAAAA,CAASF,EAAYF,IAAKD,CAAAA,CAAAA,CAAc,iBAAmBF,CAAAA,CAAAA,CAAM,YAAY,CAAC,CAAA,CAEzFA,EAAM,QAASA,CAAAA,CAAAA,CAAM,YAAY,CACrCA,GAAAA,CAAAA,CAAM,QAASA,CAAAA,CAAAA,CAAM,YAAY,CAAI,CAAA,GACrC,MAAMI,CAAAA,CAAS,MAAMJ,CAAM,CAAA,SAAS,CACpC,CAAA,MAAMI,EAAS,KAAMJ,CAAAA,CAAAA,CAAM,OAAO,CAEpC,EAAA,CAEO,SAASQ,CAA4BZ,CAAAA,CAAAA,CAOzC,CACFI,CAAAA,CAAM,SAAWJ,CAAI,CAAA,IAAA,CAAK,KAC1B,IAAMa,CAAAA,CAAMT,EAAM,EAAKU,CAAAA,CAAAA,CAAaV,CAAM,CAAA,QAAQ,EAClDA,CAAM,CAAA,QAAA,CAASA,EAAM,YAAY,CAAA,CAAES,CAAE,CAAIT,CAAAA,CAAAA,CAAM,QAASA,CAAAA,CAAAA,CAAM,YAAY,CAAES,CAAAA,CAAE,GAAK,CAAE,KAAA,CAAO,CAAE,EAC/F","file":"vitest-setup.js","sourcesContent":["/**\n * Determine should snapshot be taken.\n *\n * not story: false\n * no `snapshot` tag: false\n * disabled by `!snapshot` tag: false\n */\nexport function shouldTakeSnapshot(ctx: { story?: { tags: string[] } }) {\n\tif (!ctx.story?.tags) return false\n\treturn ctx.story.tags.lastIndexOf('!snapshot') < ctx.story.tags.lastIndexOf('snapshot')\n}\n","import { basename, dirname, join, relative } from 'pathe'\nimport './augment.js'\nimport { commands } from './@vitest/browser/context.js'\nimport { toSnapshotId } from './@vitest/browser/image_snapshot.logic.js'\nimport { state } from './state.js'\n\nexport * from './@vitest/browser/context.js'\nexport * from './expect.to_match_image_snapshot.js'\nexport * from './tags.js'\n\n/**\n * The project parameters for the snapshot.\n *\n * These parameters are set per project in the `.storybook/preview.ts` file.\n */\nexport type VisOptions = {\n\t/**\n\t * The snapshot folder relative to the root of the project.\n\t */\n\tsnapshotPath: string\n}\n\nexport async function configureSnapshotBeforeAll(\n\tsuite: { file: { filepath: string }; name: string },\n\toptions?: VisOptions,\n) {\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 snapshotPath = join(\n\t\tstate.projectDir,\n\t\toptions?.snapshotPath ?? `__snapshots__/${await commands.getSnapshotPlatform()}`,\n\t)\n\tconst currentDir = dirname(state.testFilepath)\n\tstate.baselineDir = relative(currentDir, join(snapshotPath, state.testFilename))\n\tstate.resultDir = relative(currentDir, join(snapshotPath, '__results__', state.testFilename))\n\tstate.diffDir = relative(currentDir, join(snapshotPath, '__diff_output__', state.testFilename))\n\n\tif (!state.snapshot[state.testFilepath]) {\n\t\tstate.snapshot[state.testFilepath] = {}\n\t\tawait commands.rmDir(state.resultDir)\n\t\tawait commands.rmDir(state.diffDir)\n\t}\n}\n\nexport function configureSnapshotBeforeEach(ctx: {\n\ttask: {\n\t\tname: string\n\t\tfile: {\n\t\t\tfilepath: string\n\t\t}\n\t}\n}) {\n\tstate.taskName = ctx.task.name\n\tconst id = (state.id = toSnapshotId(state.taskName))\n\tstate.snapshot[state.testFilepath][id] = state.snapshot[state.testFilepath][id] ?? { index: 1 }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storybook-addon-vis",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Storybook Vitest Image Snapshot addon",
5
5
  "keywords": [
6
6
  "image-snapshot",
@@ -49,14 +49,14 @@
49
49
  "type-plus": "^8.0.0-beta.6"
50
50
  },
51
51
  "devDependencies": {
52
- "@storybook/addon-essentials": "^8.4.1",
53
- "@storybook/addon-interactions": "^8.4.1",
54
- "@storybook/addon-links": "^8.4.1",
55
- "@storybook/blocks": "^8.4.1",
56
- "@storybook/experimental-addon-test": "^8.4.1",
57
- "@storybook/react": "^8.4.1",
58
- "@storybook/react-vite": "^8.4.1",
59
- "@storybook/test": "^8.4.1",
52
+ "@storybook/addon-essentials": "^8.4.2",
53
+ "@storybook/addon-interactions": "^8.4.2",
54
+ "@storybook/addon-links": "^8.4.2",
55
+ "@storybook/blocks": "^8.4.2",
56
+ "@storybook/experimental-addon-test": "^8.4.2",
57
+ "@storybook/react": "^8.4.2",
58
+ "@storybook/react-vite": "^8.4.2",
59
+ "@storybook/test": "^8.4.2",
60
60
  "@types/is-ci": "^3.0.4",
61
61
  "@types/pixelmatch": "^5.2.6",
62
62
  "@types/react": "^18.2.65",
@@ -64,10 +64,10 @@
64
64
  "@vitejs/plugin-react": "^4.2.1",
65
65
  "@vitest/browser": "^2.1.4",
66
66
  "@vitest/expect": "^2.1.4",
67
- "boxen": "^7.1.1",
67
+ "boxen": "^8.0.0",
68
68
  "react": "^18.2.0",
69
69
  "react-dom": "^18.2.0",
70
- "storybook": "^8.4.1",
70
+ "storybook": "^8.4.2",
71
71
  "tsup": "^8.2.4",
72
72
  "typescript": "^5.5.4",
73
73
  "vite": "^5.3.5",
@@ -84,7 +84,8 @@
84
84
  },
85
85
  "bundler": {
86
86
  "exportEntries": [
87
- "src/index.ts"
87
+ "src/index.ts",
88
+ "src/vitest-setup.ts"
88
89
  ]
89
90
  },
90
91
  "storybook": {
@@ -12,7 +12,7 @@ export interface HasImageSnapshotAction {
12
12
  * Check if the snapshot image exists.
13
13
  */
14
14
  export async function hasImageSnapshot(this: BrowserPage, options?: CustomizeSnapshotIdOptions | undefined) {
15
- const index = state.snapshot[state.id]!.index
15
+ const index = state.snapshot[state.testFilepath][state.id]!.index
16
16
  const snapshotFilename = options?.customizeSnapshotId
17
17
  ? `${options.customizeSnapshotId(state.id, index)}.png`
18
18
  : `${state.id}-${index}.png`
@@ -13,7 +13,7 @@ export async function imageSnapshot(
13
13
  this: BrowserPage,
14
14
  options?: ImageSnapshotOptions | undefined,
15
15
  ): Promise<ImageSnapshot> {
16
- const index = state.snapshot[state.id]!.index++
16
+ const index = state.snapshot[state.testFilepath][state.id]!.index++
17
17
  const snapshotFilename = options?.customizeSnapshotId
18
18
  ? `${options.customizeSnapshotId(state.id, index)}.png`
19
19
  : `${state.id}-${index}.png`
package/src/state.ts CHANGED
@@ -13,5 +13,5 @@ export const state = {
13
13
  resultDir: string
14
14
  diffDir: string
15
15
  taskName: string
16
- snapshot: Record<string, { index: number }>
16
+ snapshot: Record<string, Record<string, { index: number }>>
17
17
  }
@@ -37,12 +37,22 @@ export async function configureSnapshotBeforeAll(
37
37
  state.resultDir = relative(currentDir, join(snapshotPath, '__results__', state.testFilename))
38
38
  state.diffDir = relative(currentDir, join(snapshotPath, '__diff_output__', state.testFilename))
39
39
 
40
- await commands.rmDir(state.resultDir)
41
- await commands.rmDir(state.diffDir)
40
+ if (!state.snapshot[state.testFilepath]) {
41
+ state.snapshot[state.testFilepath] = {}
42
+ await commands.rmDir(state.resultDir)
43
+ await commands.rmDir(state.diffDir)
44
+ }
42
45
  }
43
46
 
44
- export function configureSnapshotBeforeEach(ctx: { task: { name: string } }) {
47
+ export function configureSnapshotBeforeEach(ctx: {
48
+ task: {
49
+ name: string
50
+ file: {
51
+ filepath: string
52
+ }
53
+ }
54
+ }) {
45
55
  state.taskName = ctx.task.name
46
56
  const id = (state.id = toSnapshotId(state.taskName))
47
- state.snapshot[id] = state.snapshot[id] ?? { index: 1 }
57
+ state.snapshot[state.testFilepath][id] = state.snapshot[state.testFilepath][id] ?? { index: 1 }
48
58
  }