storybook-addon-vis 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -4,6 +4,13 @@ import { CDPSession, BrowserCommands, BrowserPage, Platform } from '@vitest/brow
4
4
  import { SerializedConfig } from 'vitest';
5
5
 
6
6
  type ImageSnapshotOptions = {
7
+ /**
8
+ * Customize the filename of the snapshot.
9
+ *
10
+ * @param id The id of the snapshot.
11
+ * @param index The index of the snapshot.
12
+ */
13
+ customizeFilename?: (id: string, index: number) => string;
7
14
  element?: Element;
8
15
  };
9
16
  type ImageSnapshot = {
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
- import j from'dedent';import z 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"),d=Symbol.for("storybook-addon-vis/imageSnapshotStub");function f(t){return new Promise((e,a)=>{let o=new Image;o.src=`data:image/png;base64,${t}`,o.onload=()=>{let r=document.createElement("canvas");r.width=o.width,r.height=o.height;let s=r.getContext("2d");s.drawImage(o,0,0),e(s.getImageData(0,0,r.width,r.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(r=>{let s=new FileReader;s.onload=()=>{o(s.result);},s.readAsDataURL(r);});})}var p={name:"",testFilepath:"",taskName:"",snapshot:{}};async function x(t){let e=`${p.snapshotId}-${p.snapshot[p.snapshotId].index++}.png`,a=join(p.baselineDir,e),o=join(p.resultDir,e),r=join(p.diffDir,e),s=await this.screenshot({base64:!0,path:o,element:t?.element}),n=await f(s.base64);return {type:g,snapshotFilename:e,baselinePath:a,resultPath:o,diffPath:r,base64:s.base64,image:n}}var c;globalThis.__vitest_browser__&&import('@vitest/browser/context').then(t=>{c=t,R.extend({imageSnapshot:x});});function X(){return c?.cdp()??{}}var u=new Proxy({},{get(t,e){return t[e]??(c?.commands)[e]}}),R=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:d};console.info(`\`page.${e.toString()}\` does not exist when running in browser`);}}}),Y=new Proxy({},{get(t,e){return t[e]??(c?.server)[e]}});function y(t){return !!t&&t.type===g}function S(t){if(!y(t))throw new Error("Expected subject to be an image snapshot")}var I=({width:t,height:e})=>a=>{if(a.width===t&&a.height===e)return a;let o=(s,n)=>n<=a.height&&s<=a.width,r=new ImageData(t,e,{colorSpace:a.colorSpace});for(let s=0;s<e;s++)for(let n=0;n<t;n++){let i=t*s+n<<2;if(o(n,s)){let m=a.width*s+n<<2;r.data[i]=a.data[m],r.data[i+1]=a.data[m+1],r.data[i+2]=a.data[m+2],r.data[i+3]=a.data[m+3];}else r.data[i]=0,r.data[i+1]=0,r.data[i+2]=0,r.data[i+3]=64;}return r};async function ct(t,e){let a=await t;if(a?.type===d)return b;if(!y(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 o=await M(a.baselinePath);if(!o)return await u.copyFile(a.resultPath,a.baselinePath),b;let r=await f(o),[s,n]=C(a.image,r),{pass:i,diffAmount:m,diffImage:h}=k(n,s,e);return i?b:(await A(`${a.diffPath}`,h),{pass:!1,actual:t,message:()=>j`${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`}.`}
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"),d=Symbol.for("storybook-addon-vis/imageSnapshotStub");function f(t){return new Promise((e,a)=>{let o=new Image;o.src=`data:image/png;base64,${t}`,o.onload=()=>{let r=document.createElement("canvas");r.width=o.width,r.height=o.height;let n=r.getContext("2d");n.drawImage(o,0,0),e(n.getImageData(0,0,r.width,r.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(r=>{let n=new FileReader;n.onload=()=>{o(n.result);},n.readAsDataURL(r);});})}var c={name:"",testFilepath:"",taskName:"",snapshot:{}};async function w(t){let e=c.snapshot[c.id].index++,a=t?.customizeFilename?`${t.customizeFilename(c.id,e)}.png`:`${c.id}-${e}.png`,o=join(c.baselineDir,a),r=join(c.resultDir,a),n=join(c.diffDir,a),s=await this.screenshot({base64:!0,path:r,element:t?.element}),i=await f(s.base64);return {type:g,snapshotFilename:a,baselinePath:o,resultPath:r,diffPath:n,base64:s.base64,image:i}}var p;globalThis.__vitest_browser__&&import('@vitest/browser/context').then(t=>{p=t,E.extend({imageSnapshot:w});});function X(){return p?.cdp()??{}}var u=new Proxy({},{get(t,e){return t[e]??(p?.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]??p?.page?(p?.page)[e]:()=>{if(e==="imageSnapshot")return {type:d};console.info(`\`page.${e.toString()}\` does not exist when running in browser`);}}}),Y=new Proxy({},{get(t,e){return t[e]??(p?.server)[e]}});function y(t){return !!t&&t.type===g}function S(t){if(!y(t))throw new Error("Expected subject to be an image snapshot")}var I=({width:t,height:e})=>a=>{if(a.width===t&&a.height===e)return a;let o=(n,s)=>s<=a.height&&n<=a.width,r=new ImageData(t,e,{colorSpace:a.colorSpace});for(let n=0;n<e;n++)for(let s=0;s<t;s++){let i=t*n+s<<2;if(o(s,n)){let m=a.width*n+s<<2;r.data[i]=a.data[m],r.data[i+1]=a.data[m+1],r.data[i+2]=a.data[m+2],r.data[i+3]=a.data[m+3];}else r.data[i]=0,r.data[i+1]=0,r.data[i+2]=0,r.data[i+3]=64;}return r};async function pt(t,e){let a=await t;if(a?.type===d)return b;if(!y(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 o=await F(a.baselinePath);if(!o)return await u.copyFile(a.resultPath,a.baselinePath),b;let r=await f(o),[n,s]=A(a.image,r),{pass:i,diffAmount:m,diffImage:h}=k(s,n,e);return i?b:(await M(`${a.diffPath}`,h),{pass:!1,actual:t,message:()=>R`${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}'
5
- Difference: '${a.diffPath}'`})}var b={pass:!0,message:()=>""};function M(t){return u.readFile(t,{encoding:"base64"}).catch(()=>{})}async function A(t,e){let a=(await w(e)).split(",")[1];return u.writeFile(t,a,"base64url")}function C(t,e){if(O(t,e))return [t,e];let a=F(t,e),o=I(a);return [o(t),o(e)]}function F(t,e){let a=Math.max(t.width,e.width),o=Math.max(t.height,e.height);return {width:a,height:o}}function O(t,e){return t.width===e.width&&t.height===e.height}function k(t,e,{failureThreshold:a=0,failureThresholdType:o="pixel",diffOptions:r}={}){let{width:s,height:n}=e,i=new ImageData(s,n),m=z(e.data,t.data,i.data,s,n,r),h=L({failureThresholdType:o,width:s,height:n},m);return {pass:h<=a,diffAmount:h,diffImage:i}}function L({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 gt(t){return {snapshot:t}}export{X as cdp,u as commands,gt as defineSnapshotParam,R as page,Y as server,ct as toMatchImageSnapshot};//# sourceMappingURL=index.js.map
5
+ Difference: '${a.diffPath}'`})}var b={pass:!0,message:()=>""};function F(t){return u.readFile(t,{encoding:"base64"}).catch(()=>{})}async function M(t,e){let a=(await x(e)).split(",")[1];return u.writeFile(t,a,"base64url")}function A(t,e){if(O(t,e))return [t,e];let a=C(t,e),o=I(a);return [o(t),o(e)]}function C(t,e){let a=Math.max(t.width,e.width),o=Math.max(t.height,e.height);return {width:a,height:o}}function O(t,e){return t.width===e.width&&t.height===e.height}function k(t,e,{failureThreshold:a=0,failureThresholdType:o="pixel",diffOptions:r}={}){let{width:n,height:s}=e,i=new ImageData(n,s),m=j(e.data,t.data,i.data,n,s,r),h=L({failureThresholdType:o,width:n,height:s},m);return {pass:h<=a,diffAmount:h,diffImage:i}}function L({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 gt(t){return {snapshot:t}}export{X as cdp,u as commands,gt as defineSnapshotParam,E as page,Y as server,pt as toMatchImageSnapshot};//# sourceMappingURL=index.js.map
6
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/@vitest/browser/context.ts","../src/image_data.ts","../src/state.ts","../src/@vitest/browser/page.image_snapshot.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","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","state","imageSnapshot","options","snapshotFilename","baselinePath","join","resultPath","diffPath","screenshot","image","m","page","cdp","commands","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","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() ?? ({} as any)\n}\n\nexport const commands = new Proxy<BrowserCommands>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.commands as any)[prop]\n\t},\n})\n\nexport const page = new Proxy<BrowserPage>(\n\t{\n\t\tgetByAltText,\n\t\tgetByRole,\n\t\tgetByTestId,\n\t\tgetByText,\n\t\tgetByTitle,\n\t\tgetByPlaceholder: getByPlaceholderText,\n\t\tgetByLabelText,\n\t} as any,\n\t{\n\t\tget(target, prop) {\n\t\t\treturn ((target as any)[prop] ?? ctx?.page)\n\t\t\t\t? (ctx?.page as any)[prop]\n\t\t\t\t: () => {\n\t\t\t\t\t\tif (prop === 'imageSnapshot') {\n\t\t\t\t\t\t\treturn { type: imageSnapshotStubSymbol }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.info(`\\`page.${prop.toString()}\\` does not exist when running in browser`)\n\t\t\t\t\t}\n\t\t},\n\t},\n)\n\nexport const server = new Proxy<{\n\t/**\n\t * Platform the Vitest server is running on.\n\t * The same as calling `process.platform` on the server.\n\t */\n\tplatform: Platform\n\t/**\n\t * Runtime version of the Vitest server.\n\t * The same as calling `process.version` on the server.\n\t */\n\tversion: string\n\t/**\n\t * Name of the browser provider.\n\t */\n\tprovider: string\n\t/**\n\t * Name of the current browser.\n\t */\n\tbrowser: string\n\t/**\n\t * Available commands for the browser.\n\t * @see {@link https://vitest.dev/guide/browser/commands}\n\t */\n\tcommands: BrowserCommands\n\t/**\n\t * Serialized test config.\n\t */\n\tconfig: SerializedConfig\n}>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.server as any)[prop]\n\t},\n})\n","export 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\tsnapshotId: string\n\tsnapshot: Record<string, { index: number }>\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 async function imageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions): Promise<ImageSnapshot> {\n\tconst snapshotFilename = `${state.snapshotId}-${state.snapshot[state.snapshotId]!.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})\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 { 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 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'\nimport { createImageResizer } from './image_resizer.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\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 './@vitest/browser/types'\n\nexport function defineSnapshotParam(snapshot: MatchImageSnapshotOptions) {\n\treturn { snapshot }\n}\n"]}
1
+ {"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/@vitest/browser/context.ts","../src/image_data.ts","../src/state.ts","../src/@vitest/browser/page.image_snapshot.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","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","state","imageSnapshot","options","index","snapshotFilename","baselinePath","join","resultPath","diffPath","screenshot","image","m","page","cdp","commands","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","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() ?? ({} as any)\n}\n\nexport const commands = new Proxy<BrowserCommands>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.commands as any)[prop]\n\t},\n})\n\nexport const page = new Proxy<BrowserPage>(\n\t{\n\t\tgetByAltText,\n\t\tgetByRole,\n\t\tgetByTestId,\n\t\tgetByText,\n\t\tgetByTitle,\n\t\tgetByPlaceholder: getByPlaceholderText,\n\t\tgetByLabelText,\n\t} as any,\n\t{\n\t\tget(target, prop) {\n\t\t\treturn ((target as any)[prop] ?? ctx?.page)\n\t\t\t\t? (ctx?.page as any)[prop]\n\t\t\t\t: () => {\n\t\t\t\t\t\tif (prop === 'imageSnapshot') {\n\t\t\t\t\t\t\treturn { type: imageSnapshotStubSymbol }\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconsole.info(`\\`page.${prop.toString()}\\` does not exist when running in browser`)\n\t\t\t\t\t}\n\t\t},\n\t},\n)\n\nexport const server = new Proxy<{\n\t/**\n\t * Platform the Vitest server is running on.\n\t * The same as calling `process.platform` on the server.\n\t */\n\tplatform: Platform\n\t/**\n\t * Runtime version of the Vitest server.\n\t * The same as calling `process.version` on the server.\n\t */\n\tversion: string\n\t/**\n\t * Name of the browser provider.\n\t */\n\tprovider: string\n\t/**\n\t * Name of the current browser.\n\t */\n\tbrowser: string\n\t/**\n\t * Available commands for the browser.\n\t * @see {@link https://vitest.dev/guide/browser/commands}\n\t */\n\tcommands: BrowserCommands\n\t/**\n\t * Serialized test config.\n\t */\n\tconfig: SerializedConfig\n}>({} as any, {\n\tget(target, prop) {\n\t\treturn (target as any)[prop] ?? (ctx?.server as any)[prop]\n\t},\n})\n","export 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\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 { toImageData } from '../../image_data.js'\nimport { state } from '../../state.js'\nimport { imageSnapshotSymbol } from './constants.js'\nimport type { ImageSnapshot, ImageSnapshotOptions } from './types.js'\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?.customizeFilename\n\t\t? `${options.customizeFilename(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})\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 { 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 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'\nimport { createImageResizer } from './image_resizer.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\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 './@vitest/browser/types'\n\nexport function defineSnapshotParam(snapshot: MatchImageSnapshotOptions) {\n\treturn { snapshot }\n}\n"]}
@@ -2,6 +2,13 @@ import * as vitest_node from 'vitest/node';
2
2
  import { PixelmatchOptions } from 'pixelmatch';
3
3
 
4
4
  type ImageSnapshotOptions = {
5
+ /**
6
+ * Customize the filename of the snapshot.
7
+ *
8
+ * @param id The id of the snapshot.
9
+ * @param index The index of the snapshot.
10
+ */
11
+ customizeFilename?: (id: string, index: number) => string;
5
12
  element?: Element;
6
13
  };
7
14
  type ImageSnapshot = {
@@ -4,6 +4,13 @@ import { SerializedConfig } from 'vitest';
4
4
  import { AsyncExpectationResult } from '@vitest/expect';
5
5
 
6
6
  type ImageSnapshotOptions = {
7
+ /**
8
+ * Customize the filename of the snapshot.
9
+ *
10
+ * @param id The id of the snapshot.
11
+ * @param index The index of the snapshot.
12
+ */
13
+ customizeFilename?: (id: string, index: number) => string;
7
14
  element?: Element;
8
15
  };
9
16
  type ImageSnapshot = {
@@ -1,6 +1,6 @@
1
- import {join,basename,dirname,relative}from'pathe';import {getByAltText,getByRole,getByTestId,getByText,getByTitle,getByPlaceholderText,getByLabelText}from'@storybook/test';import z from'dedent';import A from'pixelmatch';var g=Symbol.for("storybook-addon-vis/imageSnapshot"),d=Symbol.for("storybook-addon-vis/imageSnapshotStub");function l(t){return new Promise((e,a)=>{let s=new Image;s.src=`data:image/png;base64,${t}`,s.onload=()=>{let n=document.createElement("canvas");n.width=s.width,n.height=s.height;let r=n.getContext("2d");r.drawImage(s,0,0),e(r.getImageData(0,0,n.width,n.height));},s.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(s=>{e.toBlob(n=>{let r=new FileReader;r.onload=()=>{s(r.result);},r.readAsDataURL(n);});})}var o={name:"",testFilepath:"",taskName:"",snapshot:{}};async function I(t){let e=`${o.snapshotId}-${o.snapshot[o.snapshotId].index++}.png`,a=join(o.baselineDir,e),s=join(o.resultDir,e),n=join(o.diffDir,e),r=await this.screenshot({base64:!0,path:s,element:t?.element}),i=await l(r.base64);return {type:g,snapshotFilename:e,baselinePath:a,resultPath:s,diffPath:n,base64:r.base64,image:i}}var c;globalThis.__vitest_browser__&&import('@vitest/browser/context').then(t=>{c=t,R.extend({imageSnapshot:I});});function at(){return c?.cdp()??{}}var h=new Proxy({},{get(t,e){return t[e]??(c?.commands)[e]}}),R=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:d};console.info(`\`page.${e.toString()}\` does not exist when running in browser`);}}}),st=new Proxy({},{get(t,e){return t[e]??(c?.server)[e]}});function b(t){return !!t&&t.type===g}function D(t){if(!b(t))throw new Error("Expected subject to be an image snapshot")}function P(t){return `${t.replace(/[^a-z0-9]/gi,"-").toLowerCase()}`}var $=({width:t,height:e})=>a=>{if(a.width===t&&a.height===e)return a;let s=(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(s(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 lt(t,e){let a=await t;if(a?.type===d)return x;if(!b(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}\``}}D(a);let s=await k(a.baselinePath);if(!s)return await h.copyFile(a.resultPath,a.baselinePath),x;let n=await l(s),[r,i]=M(a.image,n),{pass:m,diffAmount:p,diffImage:f}=N(i,r,e);return m?x:(await C(`${a.diffPath}`,f),{pass:!1,actual:t,message:()=>z`${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`}.`}
1
+ import {join,basename,dirname,relative}from'pathe';import {getByAltText,getByRole,getByTestId,getByText,getByTitle,getByPlaceholderText,getByLabelText}from'@storybook/test';import R from'dedent';import A from'pixelmatch';var g=Symbol.for("storybook-addon-vis/imageSnapshot"),d=Symbol.for("storybook-addon-vis/imageSnapshotStub");function l(t){return new Promise((e,a)=>{let n=new Image;n.src=`data:image/png;base64,${t}`,n.onload=()=>{let r=document.createElement("canvas");r.width=n.width,r.height=n.height;let o=r.getContext("2d");o.drawImage(n,0,0),e(o.getImageData(0,0,r.width,r.height));},n.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(n=>{e.toBlob(r=>{let o=new FileReader;o.onload=()=>{n(o.result);},o.readAsDataURL(r);});})}var s={name:"",testFilepath:"",taskName:"",snapshot:{}};async function I(t){let e=s.snapshot[s.id].index++,a=t?.customizeFilename?`${t.customizeFilename(s.id,e)}.png`:`${s.id}-${e}.png`,n=join(s.baselineDir,a),r=join(s.resultDir,a),o=join(s.diffDir,a),i=await this.screenshot({base64:!0,path:r,element:t?.element}),m=await l(i.base64);return {type:g,snapshotFilename:a,baselinePath:n,resultPath:r,diffPath:o,base64:i.base64,image:m}}var h;globalThis.__vitest_browser__&&import('@vitest/browser/context').then(t=>{h=t,E.extend({imageSnapshot:I});});function at(){return h?.cdp()??{}}var c=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:d};console.info(`\`page.${e.toString()}\` does not exist when running in browser`);}}}),st=new Proxy({},{get(t,e){return t[e]??(h?.server)[e]}});function b(t){return !!t&&t.type===g}function D(t){if(!b(t))throw new Error("Expected subject to be an image snapshot")}function P(t){return `${t.replace(/[^a-z0-9]/gi,"-").toLowerCase()}`}var $=({width:t,height:e})=>a=>{if(a.width===t&&a.height===e)return a;let n=(o,i)=>i<=a.height&&o<=a.width,r=new ImageData(t,e,{colorSpace:a.colorSpace});for(let o=0;o<e;o++)for(let i=0;i<t;i++){let m=t*o+i<<2;if(n(i,o)){let p=a.width*o+i<<2;r.data[m]=a.data[p],r.data[m+1]=a.data[p+1],r.data[m+2]=a.data[p+2],r.data[m+3]=a.data[p+3];}else r.data[m]=0,r.data[m+1]=0,r.data[m+2]=0,r.data[m+3]=64;}return r};async function lt(t,e){let a=await t;if(a?.type===d)return x;if(!b(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}\``}}D(a);let n=await k(a.baselinePath);if(!n)return await c.copyFile(a.resultPath,a.baselinePath),x;let r=await l(n),[o,i]=M(a.image,r),{pass:m,diffAmount:p,diffImage:f}=N(i,o,e);return m?x:(await C(`${a.diffPath}`,f),{pass:!1,actual:t,message:()=>R`${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}'
5
- Difference: '${a.diffPath}'`})}var x={pass:!0,message:()=>""};function k(t){return h.readFile(t,{encoding:"base64"}).catch(()=>{})}async function C(t,e){let a=(await S(e)).split(",")[1];return h.writeFile(t,a,"base64url")}function M(t,e){if(L(t,e))return [t,e];let a=O(t,e),s=$(a);return [s(t),s(e)]}function O(t,e){let a=Math.max(t.width,e.width),s=Math.max(t.height,e.height);return {width:a,height:s}}function L(t,e){return t.width===e.width&&t.height===e.height}function N(t,e,{failureThreshold:a=0,failureThresholdType:s="pixel",diffOptions:n}={}){let{width:r,height:i}=e,m=new ImageData(r,i),p=A(e.data,t.data,m.data,r,i,n),f=U({failureThresholdType:s,width:r,height:i},p);return {pass:f<=a,diffAmount:f,diffImage:m}}function U({failureThresholdType:t,width:e,height:a},s){switch(t){case"pixel":return s;case"percent":return s/(e*a)*100;default:throw new Error(`Invalid failureThresholdType: ${t}`)}}function yt(t){return t.story?.tags?t.story.tags.lastIndexOf("!snapshot")<t.story.tags.lastIndexOf("snapshot"):!1}async function Dt(t,e){o.name=t.name,o.testFilepath=t.file.filepath,o.testFilename=basename(o.testFilepath),o.projectDir=o.testFilepath.slice(0,-o.name.length);let a=join(o.projectDir,e?.snapshotPath??`__snapshots__/${await h.getSnapshotPlatform()}`),s=dirname(o.testFilepath);o.baselineDir=relative(s,join(a,o.testFilename)),o.resultDir=relative(s,join(a,"__results__",o.testFilename)),o.diffDir=relative(s,join(a,"__diff_output__",o.testFilename)),await h.rmDir(o.resultDir),await h.rmDir(o.diffDir);}function Pt(t){o.taskName=t.task.name;let e=o.snapshotId=P(o.taskName);o.snapshot[e]=o.snapshot[e]??{index:1};}export{at as cdp,h as commands,Dt as configureSnapshotBeforeAll,Pt as configureSnapshotBeforeEach,R as page,st as server,yt as shouldTakeSnapshot,lt as toMatchImageSnapshot};//# sourceMappingURL=vitest-setup.js.map
5
+ Difference: '${a.diffPath}'`})}var x={pass:!0,message:()=>""};function k(t){return c.readFile(t,{encoding:"base64"}).catch(()=>{})}async function C(t,e){let a=(await S(e)).split(",")[1];return c.writeFile(t,a,"base64url")}function M(t,e){if(L(t,e))return [t,e];let a=O(t,e),n=$(a);return [n(t),n(e)]}function O(t,e){let a=Math.max(t.width,e.width),n=Math.max(t.height,e.height);return {width:a,height:n}}function L(t,e){return t.width===e.width&&t.height===e.height}function N(t,e,{failureThreshold:a=0,failureThresholdType:n="pixel",diffOptions:r}={}){let{width:o,height:i}=e,m=new ImageData(o,i),p=A(e.data,t.data,m.data,o,i,r),f=U({failureThresholdType:n,width:o,height:i},p);return {pass:f<=a,diffAmount:f,diffImage:m}}function U({failureThresholdType:t,width:e,height:a},n){switch(t){case"pixel":return n;case"percent":return n/(e*a)*100;default:throw new Error(`Invalid failureThresholdType: ${t}`)}}function yt(t){return t.story?.tags?t.story.tags.lastIndexOf("!snapshot")<t.story.tags.lastIndexOf("snapshot"):!1}async function Dt(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 c.getSnapshotPlatform()}`),n=dirname(s.testFilepath);s.baselineDir=relative(n,join(a,s.testFilename)),s.resultDir=relative(n,join(a,"__results__",s.testFilename)),s.diffDir=relative(n,join(a,"__diff_output__",s.testFilename)),await c.rmDir(s.resultDir),await c.rmDir(s.diffDir);}function Pt(t){s.taskName=t.task.name;let e=s.id=P(s.taskName);s.snapshot[e]=s.snapshot[e]??{index:1};}export{at as cdp,c as commands,Dt as configureSnapshotBeforeAll,Pt as configureSnapshotBeforeEach,E as page,st as server,yt as shouldTakeSnapshot,lt as toMatchImageSnapshot};//# sourceMappingURL=vitest-setup.js.map
6
6
  //# sourceMappingURL=vitest-setup.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/@vitest/browser/page.image_snapshot.ts","../src/image_data.ts","../src/state.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","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","state","imageSnapshot","options","snapshotFilename","baselinePath","join","resultPath","diffPath","screenshot","image","m","page","cdp","commands","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","snapshotId"],"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 { toImageData } from '../../image_data.js'\nimport { state } from '../../state.js'\nimport { imageSnapshotSymbol } from './constants.js'\nimport type { ImageSnapshot, ImageSnapshotOptions } from './types.js'\n\nexport async function imageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions): Promise<ImageSnapshot> {\n\tconst snapshotFilename = `${state.snapshotId}-${state.snapshot[state.snapshotId]!.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})\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","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\tsnapshotId: string\n\tsnapshot: Record<string, { index: number }>\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() ?? ({} 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 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'\nimport { createImageResizer } from './image_resizer.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\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 snapshotId = (state.snapshotId = toSnapshotId(state.taskName))\n\tstate.snapshot[snapshotId] = state.snapshot[snapshotId] ?? { index: 1 }\n}\n"]}
1
+ {"version":3,"sources":["../src/@vitest/browser/constants.ts","../src/image_data.ts","../src/state.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","toImageData","base64","resolve","reject","img","canvas","ctx","toDataURL","imageData","blob","reader","state","imageSnapshot","options","index","snapshotFilename","baselinePath","join","resultPath","diffPath","screenshot","image","m","page","cdp","commands","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","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\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 { toImageData } from '../../image_data.js'\nimport { state } from '../../state.js'\nimport { imageSnapshotSymbol } from './constants.js'\nimport type { ImageSnapshot, ImageSnapshotOptions } from './types.js'\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?.customizeFilename\n\t\t? `${options.customizeFilename(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})\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 { 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() ?? ({} 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 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'\nimport { createImageResizer } from './image_resizer.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\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\t// console.log('configureSnapshotBeforeEach', ctx.task)\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storybook-addon-vis",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Storybook Vitest Image Snapshot addon",
5
5
  "keywords": [
6
6
  "image-snapshot",
@@ -5,8 +5,14 @@ import { state } from '../../state.js'
5
5
  import { imageSnapshotSymbol } from './constants.js'
6
6
  import type { ImageSnapshot, ImageSnapshotOptions } from './types.js'
7
7
 
8
- export async function imageSnapshot(this: BrowserPage, options?: ImageSnapshotOptions): Promise<ImageSnapshot> {
9
- const snapshotFilename = `${state.snapshotId}-${state.snapshot[state.snapshotId]!.index++}.png`
8
+ export async function imageSnapshot(
9
+ this: BrowserPage,
10
+ options?: ImageSnapshotOptions | undefined,
11
+ ): Promise<ImageSnapshot> {
12
+ const index = state.snapshot[state.id]!.index++
13
+ const snapshotFilename = options?.customizeFilename
14
+ ? `${options.customizeFilename(state.id, index)}.png`
15
+ : `${state.id}-${index}.png`
10
16
  const baselinePath = join(state.baselineDir, snapshotFilename)
11
17
  const resultPath = join(state.resultDir, snapshotFilename)
12
18
  const diffPath = join(state.diffDir, snapshotFilename)
@@ -2,6 +2,13 @@
2
2
  import type { PixelmatchOptions } from 'pixelmatch'
3
3
 
4
4
  export type ImageSnapshotOptions = {
5
+ /**
6
+ * Customize the filename of the snapshot.
7
+ *
8
+ * @param id The id of the snapshot.
9
+ * @param index The index of the snapshot.
10
+ */
11
+ customizeFilename?: (id: string, index: number) => string
5
12
  element?: Element //| Locator
6
13
  }
7
14
 
@@ -2,6 +2,7 @@ import { composeStories } from '@storybook/react'
2
2
  import { assertType } from 'type-plus'
3
3
  import { expect, it } from 'vitest'
4
4
  import { page } from './@vitest/browser/context.js'
5
+ import { toSnapshotId } from './@vitest/browser/image_snapshot.logic.js'
5
6
  import * as ImageDataStories from './image_data.stories.js'
6
7
 
7
8
  const { ConversionRoundtrip } = composeStories(ImageDataStories)
@@ -43,3 +44,13 @@ it('should work with page.imageSnapshot()', async () => {
43
44
  await ConversionRoundtrip.run()
44
45
  await expect(page.imageSnapshot()).toMatchImageSnapshot()
45
46
  })
47
+
48
+ it('can customize snapshot filename', async ({ task }) => {
49
+ await ConversionRoundtrip.run()
50
+ const result = await page.imageSnapshot({
51
+ customizeFilename(id, index) {
52
+ return `${id}-custom-${index}`
53
+ },
54
+ })
55
+ expect(result.snapshotFilename).toEqual(`${toSnapshotId(task.name)}-custom-1.png`)
56
+ })
@@ -1,6 +1,6 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react'
2
2
  import { expect } from '@storybook/test'
3
- import { commands, page } from './@vitest/browser/context'
3
+ import { commands, page, server } from './@vitest/browser/context'
4
4
  import { UNI_PNG_URL } from './testing/constants'
5
5
 
6
6
  export default {
@@ -17,6 +17,7 @@ export const Success: StoryObj = {
17
17
  }
18
18
 
19
19
  export const Failed: StoryObj = {
20
+ tags: ['!test'],
20
21
  render() {
21
22
  return (
22
23
  <>
@@ -29,7 +30,9 @@ export const Failed: StoryObj = {
29
30
  await expect(page.imageSnapshot())
30
31
  .toMatchImageSnapshot()
31
32
  .then(
32
- () => new Error('Should not reach'),
33
+ () => {
34
+ throw new Error('Should not reach')
35
+ },
33
36
  (error) => expect(error.message).toMatch(/Expected image to match but was differ by \d+ pixels./),
34
37
  )
35
38
  },
@@ -46,16 +49,24 @@ export const Element: StoryObj = {
46
49
  }
47
50
 
48
51
  export const DifferentSize: StoryObj = {
49
- render() {
50
- // return <img style={{ width: 128, height: 128 }} src={UNI_PNG_URL} />
51
- return <img style={{ width: 256, height: 256 }} src={UNI_PNG_URL} />
52
+ tags: ['!test'],
53
+ loaders: [
54
+ async () => {
55
+ return { updateSnapshot: server.config.snapshotOptions.updateSnapshot }
56
+ },
57
+ ],
58
+ render(_, { loaded: { updateSnapshot } }) {
59
+ const style = updateSnapshot === 'all' ? { width: 128, height: 128 } : { width: 256, height: 256 }
60
+ return <img style={style} src={UNI_PNG_URL} />
52
61
  },
53
62
  async play({ canvas }) {
54
63
  const image = await canvas.getByRole('img')
55
64
  await expect(page.imageSnapshot({ element: image }))
56
65
  .toMatchImageSnapshot()
57
66
  .then(
58
- () => new Error('Should not reach'),
67
+ () => {
68
+ throw new Error('Should not reach')
69
+ },
59
70
  (error) => expect(error.message).toMatch(/Expected image to match but was differ by \d+ pixels./),
60
71
  )
61
72
  },
@@ -73,6 +84,7 @@ export const MeetFailureThreshold: StoryObj = {
73
84
  }
74
85
 
75
86
  export const FailureThreshold: StoryObj = {
87
+ tags: ['!test'],
76
88
  render: () => <div data-testid="subject">unit test</div>,
77
89
  // render: () => <div data-testid="subject">unit text</div>,
78
90
  async play({ canvas }) {
@@ -82,7 +94,9 @@ export const FailureThreshold: StoryObj = {
82
94
  failureThreshold: 10,
83
95
  })
84
96
  .then(
85
- () => new Error('Should not reach'),
97
+ () => {
98
+ throw new Error('Should not reach')
99
+ },
86
100
  (error) =>
87
101
  expect(error.message).toMatch(/Expected image to match within 10 pixels but was differ by \d+ pixels./),
88
102
  )
@@ -90,6 +104,7 @@ export const FailureThreshold: StoryObj = {
90
104
  }
91
105
 
92
106
  export const FailureThresholdByPercentage: StoryObj = {
107
+ tags: ['!test'],
93
108
  // render: () => <div data-testid="subject">unit test</div>,
94
109
  render: () => <div data-testid="subject">unit text</div>,
95
110
  async play({ canvas }) {
@@ -100,7 +115,9 @@ export const FailureThresholdByPercentage: StoryObj = {
100
115
  failureThresholdType: 'percent',
101
116
  })
102
117
  .then(
103
- () => new Error('Should not reach'),
118
+ () => {
119
+ throw new Error('Should not reach')
120
+ },
104
121
  (error) => expect(error.message).toMatch(/Expected image to match within 0.1% but was differ by \d+\.\d+%/),
105
122
  )
106
123
  },
@@ -119,6 +136,7 @@ export const MeetFailureThresholdByPercentage: StoryObj = {
119
136
  }
120
137
 
121
138
  export const ExactFailureThresholdByPercentage: StoryObj = {
139
+ tags: ['!test'],
122
140
  // render: () => <div data-testid="subject">unit test</div>,
123
141
  render: () => <div data-testid="subject">unit text</div>,
124
142
  async play({ canvas }) {
@@ -128,7 +146,9 @@ export const ExactFailureThresholdByPercentage: StoryObj = {
128
146
  failureThresholdType: 'percent',
129
147
  })
130
148
  .then(
131
- () => new Error('Should not reach'),
149
+ () => {
150
+ throw new Error('Should not reach')
151
+ },
132
152
  async (error) => {
133
153
  expect(error.message).toMatch(/Expected image to match but was differ by \d+\.\d+%/)
134
154
  expect(error.message).toMatch(
package/src/state.ts CHANGED
@@ -4,6 +4,7 @@ export const state = {
4
4
  taskName: '',
5
5
  snapshot: {},
6
6
  } as {
7
+ id: string
7
8
  name: string
8
9
  testFilepath: string
9
10
  testFilename: string
@@ -12,6 +13,5 @@ export const state = {
12
13
  resultDir: string
13
14
  diffDir: string
14
15
  taskName: string
15
- snapshotId: string
16
16
  snapshot: Record<string, { index: number }>
17
17
  }
@@ -42,7 +42,8 @@ export async function configureSnapshotBeforeAll(
42
42
  }
43
43
 
44
44
  export function configureSnapshotBeforeEach(ctx: { task: { name: string } }) {
45
+ // console.log('configureSnapshotBeforeEach', ctx.task)
45
46
  state.taskName = ctx.task.name
46
- const snapshotId = (state.snapshotId = toSnapshotId(state.taskName))
47
- state.snapshot[snapshotId] = state.snapshot[snapshotId] ?? { index: 1 }
47
+ const id = (state.id = toSnapshotId(state.taskName))
48
+ state.snapshot[id] = state.snapshot[id] ?? { index: 1 }
48
49
  }