@chromatic-com/cypress 0.9.1 → 0.10.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.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["CDP","Version","writeArchives","__name","testTitlePath","domSnapshots","resourceArchive","chromaticStorybookParams","pageUrl","viewport","outputDir","allSnapshots","Object","fromEntries","map","name","snapshot","index","Buffer","from","JSON","stringify","writeTestResult","titlePath","resourceArchiver","host","port","setupNetworkListener","allowedDomains","webSocketDebuggerUrl","cdp","target","ResourceArchiver","watch","err","console","log","saveArchives","archiveInfo","Promise","resolve","archive","then","prepareArchives","action","payload","onBeforeBrowserLaunch","browser","launchOptions","config","isTextTerminal","hostArg","args","find","arg","startsWith","split","portArg","portString","process","env","ELECTRON_EXTRA_LAUNCH_ARGS","item","Error","parseInt","installPlugin","on"],"mappings":"iFACA,OAAOA,IAAOC,WAAAA,OAAe,q3+CA6B7B,IAAMC,GAAgBC,EAAA,MAAO,CAC3BC,cAAAA,EACAC,aAAAA,EACAC,gBAAAA,EACAC,yBAAAA,EACAC,QAAAA,EACAC,SAAAA,EACAC,UAAAA,CAAS,IACW,CACpB,IAAMC,EAAeC,OAAOC,YAE1BR,EAAaS,IAAI,CAAC,CAAEC,KAAAA,EAAMC,SAAAA,CAAQ,EAAIC,IAAU,CAC9CF,GAAQ,aAAaE,EAAQ,CAAA,GAC7BC,OAAOC,KAAKC,KAAKC,UAAUL,CAAAA,CAAAA,EAC5B,CAAA,EAGH,MAAMM,EACJ,CACEC,UAAWnB,EACXM,UAAAA,EACAF,QAAAA,EACAC,SAAAA,CACF,EACAE,EACAL,EACAC,CAAAA,CAEJ,EA5BsB,iBAkClBiB,EAAqC,KAErCC,GAAO,GACPC,GAAO,EAELC,GAAuBxB,EAAA,MAAO,CAClCyB,eAAAA,CAAc,IAGf,CACC,GAAI,CACF,GAAM,CAAEC,qBAAAA,CAAoB,EAAK,MAAM5B,GAAQ,CAC7CwB,KAAAA,GACAC,KAAAA,EACF,CAAA,EAEMI,EAAM,MAAM9B,GAAI,CACpB+B,OAAQF,CACV,CAAA,EAEKL,IACHA,EAAmB,IAAIQ,EAAiBF,EAAKF,CAAAA,EAC7C,MAAMJ,EAAiBS,MAAK,EAEhC,OAASC,EAAK,CACZC,QAAQC,IAAI,MAAOF,CAAAA,CACrB,CAEA,OAAO,IACT,EAxB6B,wBA0BvBG,GAAelC,EAACmC,GACb,IAAIC,QAASC,GAKXtC,GAAc,CAAE,GAAGoC,EAAahC,gBAAiBkB,EAAiBiB,OAAQ,CAAA,EAAGC,KAAK,IAAA,CACvFF,EAAQ,IAAA,CACV,CAAA,CACF,EATmB,gBAoBRG,GAAkBxC,EAAA,MAAO,CAAEyC,OAAAA,EAAQC,QAAAA,CAAO,IAAc,CACnE,OAAQD,EAAAA,CACN,IAAK,yBACH,OAAOjB,GAAqBkB,CAAAA,EAC9B,IAAK,gBACH,OAAOR,GAAaQ,CAAAA,EACtB,QACE,OAAO,IACX,CACF,EAT+B,mBAYlBC,GAAwB3C,EAAA,CAGnC4C,EACAC,EACAC,IAAAA,CAGA,GAAI,CAACA,EAAOC,eACV,OAAOF,EAGT,IAAMG,EAAUH,EAAcI,KAAKC,KAAMC,GAAQA,EAAIC,WAAW,6BAAA,CAAA,EAChE9B,GAAO0B,EAAUA,EAAQK,MAAM,GAAA,EAAK,CAAA,EAAK,YAEzC,IAAMC,EAAUT,EAAcI,KAAKC,KAAMC,GAAQA,EAAIC,WAAW,0BAAA,CAAA,EAC5DG,EAAa,GAEjB,GAAID,EACF,CAAA,CAAGC,CAAAA,EAAcD,EAAQD,MAAM,GAAA,UACtBG,QAAQC,IAAIC,2BAMrB,CAAA,CAAGH,CAAAA,EAHWC,QAAQC,IAAIC,2BAA2BL,MAAM,GAAA,EAAKH,KAAMS,GACpEA,EAAKP,WAAW,yBAAA,CAAA,EAEKC,MAAM,GAAA,MAE7B,OAAM,IAAIO,MACR;2FAAA,EAIJrC,OAAAA,GAAOsC,SAASN,EAAY,EAAA,EAErBV,CACT,EApCqC,yBAsCxBiB,GAAgB9D,EAAA,CAAC+D,EAA0BjB,IAAAA,CAEtDiB,EAAG,OAAQ,CACTvB,gBAAAA,EACF,CAAA,EACAuB,EACE,wBACA,CAACnB,EAA0BC,IAAAA,CACzBF,GAAsBC,EAASC,EAAeC,CAAAA,CAChD,CAAA,CAEJ,EAX6B","sourcesContent":["import type { elementNode } from '@chromaui/rrweb-snapshot';\nimport CDP, { Version } from 'chrome-remote-interface';\nimport {\n ResourceArchiver,\n writeTestResult,\n ChromaticStorybookParameters,\n ResourceArchive,\n Viewport,\n} from '@chromatic-com/shared-e2e';\n\ninterface CypressSnapshot {\n // the name of the snapshot (optionally provided for manual snapshots, never provided for automatic snapshots)\n name?: string;\n // the DOM snapshot\n snapshot: elementNode;\n}\n\ninterface WriteParams {\n testTitlePath: string[];\n domSnapshots: CypressSnapshot[];\n chromaticStorybookParams: ChromaticStorybookParameters;\n pageUrl: string;\n viewport: Viewport;\n outputDir: string;\n}\n\ninterface WriteArchivesParams extends WriteParams {\n resourceArchive: ResourceArchive;\n}\n\nconst writeArchives = async ({\n testTitlePath,\n domSnapshots,\n resourceArchive,\n chromaticStorybookParams,\n pageUrl,\n viewport,\n outputDir,\n}: WriteArchivesParams) => {\n const allSnapshots = Object.fromEntries(\n // manual snapshots can be given a name; otherwise, just use the snapshot's place in line as the name\n domSnapshots.map(({ name, snapshot }, index) => [\n name ?? `Snapshot #${index + 1}`,\n Buffer.from(JSON.stringify(snapshot)),\n ])\n );\n\n await writeTestResult(\n {\n titlePath: testTitlePath,\n outputDir,\n pageUrl,\n viewport,\n },\n allSnapshots,\n resourceArchive,\n chromaticStorybookParams\n );\n};\n\n// using a single ResourceArchiver instance across all tests (for the test run)\n// each time a test completes, we'll save to disk whatever archives are there at that point.\n// This should be safe since the same resource from the same URL should be the same during the entire test run.\n// Cypress doesn't give us a way to share variables between the \"before test\" and \"after test\" lifecycle events on the server.\nlet resourceArchiver: ResourceArchiver = null;\n\nlet host = '';\nlet port = 0;\n\nconst setupNetworkListener = async ({\n allowedDomains,\n}: {\n allowedDomains?: string[];\n}): Promise<null> => {\n try {\n const { webSocketDebuggerUrl } = await Version({\n host,\n port,\n });\n\n const cdp = await CDP({\n target: webSocketDebuggerUrl,\n });\n\n if (!resourceArchiver) {\n resourceArchiver = new ResourceArchiver(cdp, allowedDomains);\n await resourceArchiver.watch();\n }\n } catch (err) {\n console.log('err', err);\n }\n\n return null;\n};\n\nconst saveArchives = (archiveInfo: WriteParams) => {\n return new Promise((resolve) => {\n // the resourceArchiver's archives come from the server, everything else (DOM snapshots, test info, etc) comes from the browser\n // notice we're not calling + awaiting resourceArchiver.idle() here...\n // that's because in Cypress, cy.visit() waits until all resources have loaded before finishing\n // so at this point (after the test) we're confident that the resources are all there already without having to wait more\n return writeArchives({ ...archiveInfo, resourceArchive: resourceArchiver.archive }).then(() => {\n resolve(null);\n });\n });\n};\n\ninterface TaskParams {\n action: 'setup-network-listener' | 'save-archives';\n payload?: any;\n}\n\n// Handles all server-side tasks, dispatching each to its proper handler.\n// Why? So users don't have to register all these individual tasks\n// (they can just import and register prepareArchives)\nexport const prepareArchives = async ({ action, payload }: TaskParams) => {\n switch (action) {\n case 'setup-network-listener':\n return setupNetworkListener(payload);\n case 'save-archives':\n return saveArchives(payload);\n default:\n return null;\n }\n};\n\n// We use this lifecycle hook because we need to know what host and port Chrome Devtools Protocol is listening at.\nexport const onBeforeBrowserLaunch = (\n // we don't use the browser parameter but we're keeping it here in case we'd ever need to read from it\n // (this way users wouldn't have to change their cypress.config file as it's already passed to us)\n browser: Cypress.Browser,\n launchOptions: Cypress.BeforeBrowserLaunchOptions,\n config: Cypress.PluginConfigOptions\n) => {\n // don't take snapshots when running `cypress open`\n if (!config.isTextTerminal) {\n return launchOptions;\n }\n\n const hostArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-address='));\n host = hostArg ? hostArg.split('=')[1] : '127.0.0.1';\n\n const portArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-port='));\n let portString = '';\n\n if (portArg) {\n [, portString] = portArg.split('=');\n } else if (process.env.ELECTRON_EXTRA_LAUNCH_ARGS) {\n // Electron doesn't pass along the address and port in the launch options, so we need to read the port from the\n // environment variable that we'll require the user to use (this assumes the host will be 127.0.0.1).\n const entry = process.env.ELECTRON_EXTRA_LAUNCH_ARGS.split(' ').find((item) =>\n item.startsWith('--remote-debugging-port')\n );\n [, portString] = entry.split('=');\n } else {\n throw new Error(\n 'Please provide a port number \\nExample: ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=<port-number> yarn cypress run'\n );\n }\n\n port = parseInt(portString, 10);\n\n return launchOptions;\n};\n\nexport const installPlugin = (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => {\n // these events are run on the server (in Node)\n on('task', {\n prepareArchives,\n });\n on(\n 'before:browser:launch',\n (browser: Cypress.Browser, launchOptions: Cypress.BeforeBrowserLaunchOptions) => {\n onBeforeBrowserLaunch(browser, launchOptions, config);\n }\n );\n};\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["CDP","Version","writeArchives","__name","testTitlePath","domSnapshots","resourceArchive","chromaticStorybookParams","pageUrl","viewport","outputDir","allSnapshots","Object","fromEntries","map","name","snapshot","index","Buffer","from","JSON","stringify","writeTestResult","titlePath","resourceArchiver","host","port","setupNetworkListener","allowedDomains","webSocketDebuggerUrl","cdp","target","ResourceArchiver","watch","err","console","log","saveArchives","archiveInfo","Promise","resolve","archive","then","prepareArchives","action","payload","onBeforeBrowserLaunch","browser","launchOptions","config","isTextTerminal","hostArg","args","find","arg","startsWith","split","portArg","portString","process","env","ELECTRON_EXTRA_LAUNCH_ARGS","item","Error","parseInt","installPlugin","on"],"mappings":"iFAAA,OAAOA,IAAOC,WAAAA,OAAe,q3+CAuB7B,IAAMC,GAAgBC,EAAA,MAAO,CAC3BC,cAAAA,EACAC,aAAAA,EACAC,gBAAAA,EACAC,yBAAAA,EACAC,QAAAA,EACAC,SAAAA,EACAC,UAAAA,CAAS,IACW,CACpB,IAAMC,EAAeC,OAAOC,YAE1BR,EAAaS,IAAI,CAAC,CAAEC,KAAAA,EAAMC,SAAAA,CAAQ,EAAIC,IAAU,CAC9CF,GAAQ,aAAaE,EAAQ,CAAA,GAC7BC,OAAOC,KAAKC,KAAKC,UAAUL,CAAAA,CAAAA,EAC5B,CAAA,EAGH,MAAMM,EACJ,CACEC,UAAWnB,EACXM,UAAAA,EACAF,QAAAA,EACAC,SAAAA,CACF,EACAE,EACAL,EACAC,CAAAA,CAEJ,EA5BsB,iBAkClBiB,EAAqC,KAErCC,GAAO,GACPC,GAAO,EAELC,GAAuBxB,EAAA,MAAO,CAClCyB,eAAAA,CAAc,IAGf,CACC,GAAI,CACF,GAAM,CAAEC,qBAAAA,CAAoB,EAAK,MAAM5B,GAAQ,CAC7CwB,KAAAA,GACAC,KAAAA,EACF,CAAA,EAEMI,EAAM,MAAM9B,GAAI,CACpB+B,OAAQF,CACV,CAAA,EAEKL,IACHA,EAAmB,IAAIQ,EAAiBF,EAAKF,CAAAA,EAC7C,MAAMJ,EAAiBS,MAAK,EAEhC,OAASC,EAAK,CACZC,QAAQC,IAAI,MAAOF,CAAAA,CACrB,CAEA,OAAO,IACT,EAxB6B,wBA0BvBG,GAAelC,EAACmC,GACb,IAAIC,QAASC,GAKXtC,GAAc,CAAE,GAAGoC,EAAahC,gBAAiBkB,EAAiBiB,OAAQ,CAAA,EAAGC,KAAK,IAAA,CACvFF,EAAQ,IAAA,CACV,CAAA,CACF,EATmB,gBAoBRG,GAAkBxC,EAAA,MAAO,CAAEyC,OAAAA,EAAQC,QAAAA,CAAO,IAAc,CACnE,OAAQD,EAAAA,CACN,IAAK,yBACH,OAAOjB,GAAqBkB,CAAAA,EAC9B,IAAK,gBACH,OAAOR,GAAaQ,CAAAA,EACtB,QACE,OAAO,IACX,CACF,EAT+B,mBAYlBC,GAAwB3C,EAAA,CAGnC4C,EACAC,EACAC,IAAAA,CAGA,GAAI,CAACA,EAAOC,eACV,OAAOF,EAGT,IAAMG,EAAUH,EAAcI,KAAKC,KAAMC,GAAQA,EAAIC,WAAW,6BAAA,CAAA,EAChE9B,GAAO0B,EAAUA,EAAQK,MAAM,GAAA,EAAK,CAAA,EAAK,YAEzC,IAAMC,EAAUT,EAAcI,KAAKC,KAAMC,GAAQA,EAAIC,WAAW,0BAAA,CAAA,EAC5DG,EAAa,GAEjB,GAAID,EACF,CAAA,CAAGC,CAAAA,EAAcD,EAAQD,MAAM,GAAA,UACtBG,QAAQC,IAAIC,2BAMrB,CAAA,CAAGH,CAAAA,EAHWC,QAAQC,IAAIC,2BAA2BL,MAAM,GAAA,EAAKH,KAAMS,GACpEA,EAAKP,WAAW,yBAAA,CAAA,EAEKC,MAAM,GAAA,MAE7B,OAAM,IAAIO,MACR;2FAAA,EAIJrC,OAAAA,GAAOsC,SAASN,EAAY,EAAA,EAErBV,CACT,EApCqC,yBAsCxBiB,GAAgB9D,EAAA,CAAC+D,EAA0BjB,IAAAA,CAEtDiB,EAAG,OAAQ,CACTvB,gBAAAA,EACF,CAAA,EACAuB,EACE,wBACA,CAACnB,EAA0BC,IAAAA,CACzBF,GAAsBC,EAASC,EAAeC,CAAAA,CAChD,CAAA,CAEJ,EAX6B","sourcesContent":["import CDP, { Version } from 'chrome-remote-interface';\nimport {\n ResourceArchiver,\n writeTestResult,\n ChromaticStorybookParameters,\n ResourceArchive,\n Viewport,\n} from '@chromatic-com/shared-e2e';\nimport { CypressSnapshot } from './types';\n\ninterface WriteParams {\n testTitlePath: string[];\n domSnapshots: CypressSnapshot[];\n chromaticStorybookParams: ChromaticStorybookParameters;\n pageUrl: string;\n viewport: Viewport;\n outputDir: string;\n}\n\ninterface WriteArchivesParams extends WriteParams {\n resourceArchive: ResourceArchive;\n}\n\nconst writeArchives = async ({\n testTitlePath,\n domSnapshots,\n resourceArchive,\n chromaticStorybookParams,\n pageUrl,\n viewport,\n outputDir,\n}: WriteArchivesParams) => {\n const allSnapshots = Object.fromEntries(\n // manual snapshots can be given a name; otherwise, just use the snapshot's place in line as the name\n domSnapshots.map(({ name, snapshot }, index) => [\n name ?? `Snapshot #${index + 1}`,\n Buffer.from(JSON.stringify(snapshot)),\n ])\n );\n\n await writeTestResult(\n {\n titlePath: testTitlePath,\n outputDir,\n pageUrl,\n viewport,\n },\n allSnapshots,\n resourceArchive,\n chromaticStorybookParams\n );\n};\n\n// using a single ResourceArchiver instance across all tests (for the test run)\n// each time a test completes, we'll save to disk whatever archives are there at that point.\n// This should be safe since the same resource from the same URL should be the same during the entire test run.\n// Cypress doesn't give us a way to share variables between the \"before test\" and \"after test\" lifecycle events on the server.\nlet resourceArchiver: ResourceArchiver = null;\n\nlet host = '';\nlet port = 0;\n\nconst setupNetworkListener = async ({\n allowedDomains,\n}: {\n allowedDomains?: string[];\n}): Promise<null> => {\n try {\n const { webSocketDebuggerUrl } = await Version({\n host,\n port,\n });\n\n const cdp = await CDP({\n target: webSocketDebuggerUrl,\n });\n\n if (!resourceArchiver) {\n resourceArchiver = new ResourceArchiver(cdp, allowedDomains);\n await resourceArchiver.watch();\n }\n } catch (err) {\n console.log('err', err);\n }\n\n return null;\n};\n\nconst saveArchives = (archiveInfo: WriteParams) => {\n return new Promise((resolve) => {\n // the resourceArchiver's archives come from the server, everything else (DOM snapshots, test info, etc) comes from the browser\n // notice we're not calling + awaiting resourceArchiver.idle() here...\n // that's because in Cypress, cy.visit() waits until all resources have loaded before finishing\n // so at this point (after the test) we're confident that the resources are all there already without having to wait more\n return writeArchives({ ...archiveInfo, resourceArchive: resourceArchiver.archive }).then(() => {\n resolve(null);\n });\n });\n};\n\ninterface TaskParams {\n action: 'setup-network-listener' | 'save-archives';\n payload?: any;\n}\n\n// Handles all server-side tasks, dispatching each to its proper handler.\n// Why? So users don't have to register all these individual tasks\n// (they can just import and register prepareArchives)\nexport const prepareArchives = async ({ action, payload }: TaskParams) => {\n switch (action) {\n case 'setup-network-listener':\n return setupNetworkListener(payload);\n case 'save-archives':\n return saveArchives(payload);\n default:\n return null;\n }\n};\n\n// We use this lifecycle hook because we need to know what host and port Chrome Devtools Protocol is listening at.\nexport const onBeforeBrowserLaunch = (\n // we don't use the browser parameter but we're keeping it here in case we'd ever need to read from it\n // (this way users wouldn't have to change their cypress.config file as it's already passed to us)\n browser: Cypress.Browser,\n launchOptions: Cypress.BeforeBrowserLaunchOptions,\n config: Cypress.PluginConfigOptions\n) => {\n // don't take snapshots when running `cypress open`\n if (!config.isTextTerminal) {\n return launchOptions;\n }\n\n const hostArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-address='));\n host = hostArg ? hostArg.split('=')[1] : '127.0.0.1';\n\n const portArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-port='));\n let portString = '';\n\n if (portArg) {\n [, portString] = portArg.split('=');\n } else if (process.env.ELECTRON_EXTRA_LAUNCH_ARGS) {\n // Electron doesn't pass along the address and port in the launch options, so we need to read the port from the\n // environment variable that we'll require the user to use (this assumes the host will be 127.0.0.1).\n const entry = process.env.ELECTRON_EXTRA_LAUNCH_ARGS.split(' ').find((item) =>\n item.startsWith('--remote-debugging-port')\n );\n [, portString] = entry.split('=');\n } else {\n throw new Error(\n 'Please provide a port number \\nExample: ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=<port-number> yarn cypress run'\n );\n }\n\n port = parseInt(portString, 10);\n\n return launchOptions;\n};\n\nexport const installPlugin = (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => {\n // these events are run on the server (in Node)\n on('task', {\n prepareArchives,\n });\n on(\n 'before:browser:launch',\n (browser: Cypress.Browser, launchOptions: Cypress.BeforeBrowserLaunchOptions) => {\n onBeforeBrowserLaunch(browser, launchOptions, config);\n }\n );\n};\n"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["CDP","Version","writeArchives","__name","testTitlePath","domSnapshots","resourceArchive","chromaticStorybookParams","pageUrl","viewport","outputDir","allSnapshots","Object","fromEntries","map","name","snapshot","index","Buffer","from","JSON","stringify","writeTestResult","titlePath","resourceArchiver","host","port","setupNetworkListener","allowedDomains","webSocketDebuggerUrl","cdp","target","ResourceArchiver","watch","err","console","log","saveArchives","archiveInfo","Promise","resolve","archive","then","prepareArchives","action","payload","onBeforeBrowserLaunch","browser","launchOptions","config","isTextTerminal","hostArg","args","find","arg","startsWith","split","portArg","portString","process","env","ELECTRON_EXTRA_LAUNCH_ARGS","item","Error","parseInt","installPlugin","on"],"mappings":"iFACA,OAAOA,IAAOC,WAAAA,OAAe,q3+CA6B7B,IAAMC,GAAgBC,EAAA,MAAO,CAC3BC,cAAAA,EACAC,aAAAA,EACAC,gBAAAA,EACAC,yBAAAA,EACAC,QAAAA,EACAC,SAAAA,EACAC,UAAAA,CAAS,IACW,CACpB,IAAMC,EAAeC,OAAOC,YAE1BR,EAAaS,IAAI,CAAC,CAAEC,KAAAA,EAAMC,SAAAA,CAAQ,EAAIC,IAAU,CAC9CF,GAAQ,aAAaE,EAAQ,CAAA,GAC7BC,OAAOC,KAAKC,KAAKC,UAAUL,CAAAA,CAAAA,EAC5B,CAAA,EAGH,MAAMM,EACJ,CACEC,UAAWnB,EACXM,UAAAA,EACAF,QAAAA,EACAC,SAAAA,CACF,EACAE,EACAL,EACAC,CAAAA,CAEJ,EA5BsB,iBAkClBiB,EAAqC,KAErCC,GAAO,GACPC,GAAO,EAELC,GAAuBxB,EAAA,MAAO,CAClCyB,eAAAA,CAAc,IAGf,CACC,GAAI,CACF,GAAM,CAAEC,qBAAAA,CAAoB,EAAK,MAAM5B,GAAQ,CAC7CwB,KAAAA,GACAC,KAAAA,EACF,CAAA,EAEMI,EAAM,MAAM9B,GAAI,CACpB+B,OAAQF,CACV,CAAA,EAEKL,IACHA,EAAmB,IAAIQ,EAAiBF,EAAKF,CAAAA,EAC7C,MAAMJ,EAAiBS,MAAK,EAEhC,OAASC,EAAK,CACZC,QAAQC,IAAI,MAAOF,CAAAA,CACrB,CAEA,OAAO,IACT,EAxB6B,wBA0BvBG,GAAelC,EAACmC,GACb,IAAIC,QAASC,GAKXtC,GAAc,CAAE,GAAGoC,EAAahC,gBAAiBkB,EAAiBiB,OAAQ,CAAA,EAAGC,KAAK,IAAA,CACvFF,EAAQ,IAAA,CACV,CAAA,CACF,EATmB,gBAoBRG,GAAkBxC,EAAA,MAAO,CAAEyC,OAAAA,EAAQC,QAAAA,CAAO,IAAc,CACnE,OAAQD,EAAAA,CACN,IAAK,yBACH,OAAOjB,GAAqBkB,CAAAA,EAC9B,IAAK,gBACH,OAAOR,GAAaQ,CAAAA,EACtB,QACE,OAAO,IACX,CACF,EAT+B,mBAYlBC,GAAwB3C,EAAA,CAGnC4C,EACAC,EACAC,IAAAA,CAGA,GAAI,CAACA,EAAOC,eACV,OAAOF,EAGT,IAAMG,EAAUH,EAAcI,KAAKC,KAAMC,GAAQA,EAAIC,WAAW,6BAAA,CAAA,EAChE9B,GAAO0B,EAAUA,EAAQK,MAAM,GAAA,EAAK,CAAA,EAAK,YAEzC,IAAMC,EAAUT,EAAcI,KAAKC,KAAMC,GAAQA,EAAIC,WAAW,0BAAA,CAAA,EAC5DG,EAAa,GAEjB,GAAID,EACF,CAAA,CAAGC,CAAAA,EAAcD,EAAQD,MAAM,GAAA,UACtBG,QAAQC,IAAIC,2BAMrB,CAAA,CAAGH,CAAAA,EAHWC,QAAQC,IAAIC,2BAA2BL,MAAM,GAAA,EAAKH,KAAMS,GACpEA,EAAKP,WAAW,yBAAA,CAAA,EAEKC,MAAM,GAAA,MAE7B,OAAM,IAAIO,MACR;2FAAA,EAIJrC,OAAAA,GAAOsC,SAASN,EAAY,EAAA,EAErBV,CACT,EApCqC,yBAsCxBiB,GAAgB9D,EAAA,CAAC+D,EAA0BjB,IAAAA,CAEtDiB,EAAG,OAAQ,CACTvB,gBAAAA,EACF,CAAA,EACAuB,EACE,wBACA,CAACnB,EAA0BC,IAAAA,CACzBF,GAAsBC,EAASC,EAAeC,CAAAA,CAChD,CAAA,CAEJ,EAX6B","sourcesContent":["import type { elementNode } from '@chromaui/rrweb-snapshot';\nimport CDP, { Version } from 'chrome-remote-interface';\nimport {\n ResourceArchiver,\n writeTestResult,\n ChromaticStorybookParameters,\n ResourceArchive,\n Viewport,\n} from '@chromatic-com/shared-e2e';\n\ninterface CypressSnapshot {\n // the name of the snapshot (optionally provided for manual snapshots, never provided for automatic snapshots)\n name?: string;\n // the DOM snapshot\n snapshot: elementNode;\n}\n\ninterface WriteParams {\n testTitlePath: string[];\n domSnapshots: CypressSnapshot[];\n chromaticStorybookParams: ChromaticStorybookParameters;\n pageUrl: string;\n viewport: Viewport;\n outputDir: string;\n}\n\ninterface WriteArchivesParams extends WriteParams {\n resourceArchive: ResourceArchive;\n}\n\nconst writeArchives = async ({\n testTitlePath,\n domSnapshots,\n resourceArchive,\n chromaticStorybookParams,\n pageUrl,\n viewport,\n outputDir,\n}: WriteArchivesParams) => {\n const allSnapshots = Object.fromEntries(\n // manual snapshots can be given a name; otherwise, just use the snapshot's place in line as the name\n domSnapshots.map(({ name, snapshot }, index) => [\n name ?? `Snapshot #${index + 1}`,\n Buffer.from(JSON.stringify(snapshot)),\n ])\n );\n\n await writeTestResult(\n {\n titlePath: testTitlePath,\n outputDir,\n pageUrl,\n viewport,\n },\n allSnapshots,\n resourceArchive,\n chromaticStorybookParams\n );\n};\n\n// using a single ResourceArchiver instance across all tests (for the test run)\n// each time a test completes, we'll save to disk whatever archives are there at that point.\n// This should be safe since the same resource from the same URL should be the same during the entire test run.\n// Cypress doesn't give us a way to share variables between the \"before test\" and \"after test\" lifecycle events on the server.\nlet resourceArchiver: ResourceArchiver = null;\n\nlet host = '';\nlet port = 0;\n\nconst setupNetworkListener = async ({\n allowedDomains,\n}: {\n allowedDomains?: string[];\n}): Promise<null> => {\n try {\n const { webSocketDebuggerUrl } = await Version({\n host,\n port,\n });\n\n const cdp = await CDP({\n target: webSocketDebuggerUrl,\n });\n\n if (!resourceArchiver) {\n resourceArchiver = new ResourceArchiver(cdp, allowedDomains);\n await resourceArchiver.watch();\n }\n } catch (err) {\n console.log('err', err);\n }\n\n return null;\n};\n\nconst saveArchives = (archiveInfo: WriteParams) => {\n return new Promise((resolve) => {\n // the resourceArchiver's archives come from the server, everything else (DOM snapshots, test info, etc) comes from the browser\n // notice we're not calling + awaiting resourceArchiver.idle() here...\n // that's because in Cypress, cy.visit() waits until all resources have loaded before finishing\n // so at this point (after the test) we're confident that the resources are all there already without having to wait more\n return writeArchives({ ...archiveInfo, resourceArchive: resourceArchiver.archive }).then(() => {\n resolve(null);\n });\n });\n};\n\ninterface TaskParams {\n action: 'setup-network-listener' | 'save-archives';\n payload?: any;\n}\n\n// Handles all server-side tasks, dispatching each to its proper handler.\n// Why? So users don't have to register all these individual tasks\n// (they can just import and register prepareArchives)\nexport const prepareArchives = async ({ action, payload }: TaskParams) => {\n switch (action) {\n case 'setup-network-listener':\n return setupNetworkListener(payload);\n case 'save-archives':\n return saveArchives(payload);\n default:\n return null;\n }\n};\n\n// We use this lifecycle hook because we need to know what host and port Chrome Devtools Protocol is listening at.\nexport const onBeforeBrowserLaunch = (\n // we don't use the browser parameter but we're keeping it here in case we'd ever need to read from it\n // (this way users wouldn't have to change their cypress.config file as it's already passed to us)\n browser: Cypress.Browser,\n launchOptions: Cypress.BeforeBrowserLaunchOptions,\n config: Cypress.PluginConfigOptions\n) => {\n // don't take snapshots when running `cypress open`\n if (!config.isTextTerminal) {\n return launchOptions;\n }\n\n const hostArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-address='));\n host = hostArg ? hostArg.split('=')[1] : '127.0.0.1';\n\n const portArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-port='));\n let portString = '';\n\n if (portArg) {\n [, portString] = portArg.split('=');\n } else if (process.env.ELECTRON_EXTRA_LAUNCH_ARGS) {\n // Electron doesn't pass along the address and port in the launch options, so we need to read the port from the\n // environment variable that we'll require the user to use (this assumes the host will be 127.0.0.1).\n const entry = process.env.ELECTRON_EXTRA_LAUNCH_ARGS.split(' ').find((item) =>\n item.startsWith('--remote-debugging-port')\n );\n [, portString] = entry.split('=');\n } else {\n throw new Error(\n 'Please provide a port number \\nExample: ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=<port-number> yarn cypress run'\n );\n }\n\n port = parseInt(portString, 10);\n\n return launchOptions;\n};\n\nexport const installPlugin = (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => {\n // these events are run on the server (in Node)\n on('task', {\n prepareArchives,\n });\n on(\n 'before:browser:launch',\n (browser: Cypress.Browser, launchOptions: Cypress.BeforeBrowserLaunchOptions) => {\n onBeforeBrowserLaunch(browser, launchOptions, config);\n }\n );\n};\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["CDP","Version","writeArchives","__name","testTitlePath","domSnapshots","resourceArchive","chromaticStorybookParams","pageUrl","viewport","outputDir","allSnapshots","Object","fromEntries","map","name","snapshot","index","Buffer","from","JSON","stringify","writeTestResult","titlePath","resourceArchiver","host","port","setupNetworkListener","allowedDomains","webSocketDebuggerUrl","cdp","target","ResourceArchiver","watch","err","console","log","saveArchives","archiveInfo","Promise","resolve","archive","then","prepareArchives","action","payload","onBeforeBrowserLaunch","browser","launchOptions","config","isTextTerminal","hostArg","args","find","arg","startsWith","split","portArg","portString","process","env","ELECTRON_EXTRA_LAUNCH_ARGS","item","Error","parseInt","installPlugin","on"],"mappings":"iFAAA,OAAOA,IAAOC,WAAAA,OAAe,q3+CAuB7B,IAAMC,GAAgBC,EAAA,MAAO,CAC3BC,cAAAA,EACAC,aAAAA,EACAC,gBAAAA,EACAC,yBAAAA,EACAC,QAAAA,EACAC,SAAAA,EACAC,UAAAA,CAAS,IACW,CACpB,IAAMC,EAAeC,OAAOC,YAE1BR,EAAaS,IAAI,CAAC,CAAEC,KAAAA,EAAMC,SAAAA,CAAQ,EAAIC,IAAU,CAC9CF,GAAQ,aAAaE,EAAQ,CAAA,GAC7BC,OAAOC,KAAKC,KAAKC,UAAUL,CAAAA,CAAAA,EAC5B,CAAA,EAGH,MAAMM,EACJ,CACEC,UAAWnB,EACXM,UAAAA,EACAF,QAAAA,EACAC,SAAAA,CACF,EACAE,EACAL,EACAC,CAAAA,CAEJ,EA5BsB,iBAkClBiB,EAAqC,KAErCC,GAAO,GACPC,GAAO,EAELC,GAAuBxB,EAAA,MAAO,CAClCyB,eAAAA,CAAc,IAGf,CACC,GAAI,CACF,GAAM,CAAEC,qBAAAA,CAAoB,EAAK,MAAM5B,GAAQ,CAC7CwB,KAAAA,GACAC,KAAAA,EACF,CAAA,EAEMI,EAAM,MAAM9B,GAAI,CACpB+B,OAAQF,CACV,CAAA,EAEKL,IACHA,EAAmB,IAAIQ,EAAiBF,EAAKF,CAAAA,EAC7C,MAAMJ,EAAiBS,MAAK,EAEhC,OAASC,EAAK,CACZC,QAAQC,IAAI,MAAOF,CAAAA,CACrB,CAEA,OAAO,IACT,EAxB6B,wBA0BvBG,GAAelC,EAACmC,GACb,IAAIC,QAASC,GAKXtC,GAAc,CAAE,GAAGoC,EAAahC,gBAAiBkB,EAAiBiB,OAAQ,CAAA,EAAGC,KAAK,IAAA,CACvFF,EAAQ,IAAA,CACV,CAAA,CACF,EATmB,gBAoBRG,GAAkBxC,EAAA,MAAO,CAAEyC,OAAAA,EAAQC,QAAAA,CAAO,IAAc,CACnE,OAAQD,EAAAA,CACN,IAAK,yBACH,OAAOjB,GAAqBkB,CAAAA,EAC9B,IAAK,gBACH,OAAOR,GAAaQ,CAAAA,EACtB,QACE,OAAO,IACX,CACF,EAT+B,mBAYlBC,GAAwB3C,EAAA,CAGnC4C,EACAC,EACAC,IAAAA,CAGA,GAAI,CAACA,EAAOC,eACV,OAAOF,EAGT,IAAMG,EAAUH,EAAcI,KAAKC,KAAMC,GAAQA,EAAIC,WAAW,6BAAA,CAAA,EAChE9B,GAAO0B,EAAUA,EAAQK,MAAM,GAAA,EAAK,CAAA,EAAK,YAEzC,IAAMC,EAAUT,EAAcI,KAAKC,KAAMC,GAAQA,EAAIC,WAAW,0BAAA,CAAA,EAC5DG,EAAa,GAEjB,GAAID,EACF,CAAA,CAAGC,CAAAA,EAAcD,EAAQD,MAAM,GAAA,UACtBG,QAAQC,IAAIC,2BAMrB,CAAA,CAAGH,CAAAA,EAHWC,QAAQC,IAAIC,2BAA2BL,MAAM,GAAA,EAAKH,KAAMS,GACpEA,EAAKP,WAAW,yBAAA,CAAA,EAEKC,MAAM,GAAA,MAE7B,OAAM,IAAIO,MACR;2FAAA,EAIJrC,OAAAA,GAAOsC,SAASN,EAAY,EAAA,EAErBV,CACT,EApCqC,yBAsCxBiB,GAAgB9D,EAAA,CAAC+D,EAA0BjB,IAAAA,CAEtDiB,EAAG,OAAQ,CACTvB,gBAAAA,EACF,CAAA,EACAuB,EACE,wBACA,CAACnB,EAA0BC,IAAAA,CACzBF,GAAsBC,EAASC,EAAeC,CAAAA,CAChD,CAAA,CAEJ,EAX6B","sourcesContent":["import CDP, { Version } from 'chrome-remote-interface';\nimport {\n ResourceArchiver,\n writeTestResult,\n ChromaticStorybookParameters,\n ResourceArchive,\n Viewport,\n} from '@chromatic-com/shared-e2e';\nimport { CypressSnapshot } from './types';\n\ninterface WriteParams {\n testTitlePath: string[];\n domSnapshots: CypressSnapshot[];\n chromaticStorybookParams: ChromaticStorybookParameters;\n pageUrl: string;\n viewport: Viewport;\n outputDir: string;\n}\n\ninterface WriteArchivesParams extends WriteParams {\n resourceArchive: ResourceArchive;\n}\n\nconst writeArchives = async ({\n testTitlePath,\n domSnapshots,\n resourceArchive,\n chromaticStorybookParams,\n pageUrl,\n viewport,\n outputDir,\n}: WriteArchivesParams) => {\n const allSnapshots = Object.fromEntries(\n // manual snapshots can be given a name; otherwise, just use the snapshot's place in line as the name\n domSnapshots.map(({ name, snapshot }, index) => [\n name ?? `Snapshot #${index + 1}`,\n Buffer.from(JSON.stringify(snapshot)),\n ])\n );\n\n await writeTestResult(\n {\n titlePath: testTitlePath,\n outputDir,\n pageUrl,\n viewport,\n },\n allSnapshots,\n resourceArchive,\n chromaticStorybookParams\n );\n};\n\n// using a single ResourceArchiver instance across all tests (for the test run)\n// each time a test completes, we'll save to disk whatever archives are there at that point.\n// This should be safe since the same resource from the same URL should be the same during the entire test run.\n// Cypress doesn't give us a way to share variables between the \"before test\" and \"after test\" lifecycle events on the server.\nlet resourceArchiver: ResourceArchiver = null;\n\nlet host = '';\nlet port = 0;\n\nconst setupNetworkListener = async ({\n allowedDomains,\n}: {\n allowedDomains?: string[];\n}): Promise<null> => {\n try {\n const { webSocketDebuggerUrl } = await Version({\n host,\n port,\n });\n\n const cdp = await CDP({\n target: webSocketDebuggerUrl,\n });\n\n if (!resourceArchiver) {\n resourceArchiver = new ResourceArchiver(cdp, allowedDomains);\n await resourceArchiver.watch();\n }\n } catch (err) {\n console.log('err', err);\n }\n\n return null;\n};\n\nconst saveArchives = (archiveInfo: WriteParams) => {\n return new Promise((resolve) => {\n // the resourceArchiver's archives come from the server, everything else (DOM snapshots, test info, etc) comes from the browser\n // notice we're not calling + awaiting resourceArchiver.idle() here...\n // that's because in Cypress, cy.visit() waits until all resources have loaded before finishing\n // so at this point (after the test) we're confident that the resources are all there already without having to wait more\n return writeArchives({ ...archiveInfo, resourceArchive: resourceArchiver.archive }).then(() => {\n resolve(null);\n });\n });\n};\n\ninterface TaskParams {\n action: 'setup-network-listener' | 'save-archives';\n payload?: any;\n}\n\n// Handles all server-side tasks, dispatching each to its proper handler.\n// Why? So users don't have to register all these individual tasks\n// (they can just import and register prepareArchives)\nexport const prepareArchives = async ({ action, payload }: TaskParams) => {\n switch (action) {\n case 'setup-network-listener':\n return setupNetworkListener(payload);\n case 'save-archives':\n return saveArchives(payload);\n default:\n return null;\n }\n};\n\n// We use this lifecycle hook because we need to know what host and port Chrome Devtools Protocol is listening at.\nexport const onBeforeBrowserLaunch = (\n // we don't use the browser parameter but we're keeping it here in case we'd ever need to read from it\n // (this way users wouldn't have to change their cypress.config file as it's already passed to us)\n browser: Cypress.Browser,\n launchOptions: Cypress.BeforeBrowserLaunchOptions,\n config: Cypress.PluginConfigOptions\n) => {\n // don't take snapshots when running `cypress open`\n if (!config.isTextTerminal) {\n return launchOptions;\n }\n\n const hostArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-address='));\n host = hostArg ? hostArg.split('=')[1] : '127.0.0.1';\n\n const portArg = launchOptions.args.find((arg) => arg.startsWith('--remote-debugging-port='));\n let portString = '';\n\n if (portArg) {\n [, portString] = portArg.split('=');\n } else if (process.env.ELECTRON_EXTRA_LAUNCH_ARGS) {\n // Electron doesn't pass along the address and port in the launch options, so we need to read the port from the\n // environment variable that we'll require the user to use (this assumes the host will be 127.0.0.1).\n const entry = process.env.ELECTRON_EXTRA_LAUNCH_ARGS.split(' ').find((item) =>\n item.startsWith('--remote-debugging-port')\n );\n [, portString] = entry.split('=');\n } else {\n throw new Error(\n 'Please provide a port number \\nExample: ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=<port-number> yarn cypress run'\n );\n }\n\n port = parseInt(portString, 10);\n\n return launchOptions;\n};\n\nexport const installPlugin = (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => {\n // these events are run on the server (in Node)\n on('task', {\n prepareArchives,\n });\n on(\n 'before:browser:launch',\n (browser: Cypress.Browser, launchOptions: Cypress.BeforeBrowserLaunchOptions) => {\n onBeforeBrowserLaunch(browser, launchOptions, config);\n }\n );\n};\n"]}
package/dist/support.js CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  var rrwebSnapshot = require('@chromaui/rrweb-snapshot');
4
4
 
5
- Cypress.Commands.add("takeSnapshot",e=>{Cypress.config("isTextTerminal")&&cy.document().then(s=>{let r=rrwebSnapshot.snapshot(s);cy.get("@manualSnapshots").then(o=>[...o,{name:e,snapshot:r}]).as("manualSnapshots");});});beforeEach(()=>{Cypress.config("isTextTerminal")&&(cy.wrap([]).as("manualSnapshots"),cy.task("prepareArchives",{action:"setup-network-listener",payload:{allowedDomains:Cypress.env("assetDomains")}}));});afterEach(()=>{Cypress.config("isTextTerminal")&&cy.document().then(e=>{let s=Cypress.env("disableAutoSnapshot")?[]:[{snapshot:rrwebSnapshot.snapshot(e)}];cy.get("@manualSnapshots").then((r=[])=>{cy.url().then(o=>{cy.task("prepareArchives",{action:"save-archives",payload:{testTitlePath:[Cypress.spec.relativeToCommonRoot,...Cypress.currentTest.titlePath],domSnapshots:[...r,...s],chromaticStorybookParams:{...Cypress.env("diffThreshold")&&{diffThreshold:Cypress.env("diffThreshold")},...Cypress.env("delay")&&{delay:Cypress.env("delay")},...Cypress.env("diffIncludeAntiAliasing")&&{diffIncludeAntiAliasing:Cypress.env("diffIncludeAntiAliasing")},...Cypress.env("diffThreshold")&&{diffThreshold:Cypress.env("diffThreshold")},...Cypress.env("forcedColors")&&{forcedColors:Cypress.env("forcedColors")},...Cypress.env("pauseAnimationAtEnd")&&{pauseAnimationAtEnd:Cypress.env("pauseAnimationAtEnd")},...Cypress.env("prefersReducedMotion")&&{prefersReducedMotion:Cypress.env("prefersReducedMotion")},...Cypress.env("cropToViewport")&&{cropToViewport:Cypress.env("cropToViewport")},...Cypress.env("ignoreSelectors")&&{ignoreSelectors:Cypress.env("ignoreSelectors")}},pageUrl:o,viewport:{height:Cypress.config("viewportHeight"),width:Cypress.config("viewportWidth")},outputDir:Cypress.config("downloadsFolder")}});});});});});
5
+ var m=Object.defineProperty;var a=(t,e)=>m(t,"name",{value:e,configurable:!0});var n=a((t,e)=>new Promise(r=>{!e&&Cypress.env("disableAutoSnapshot")&&r(null);let o=rrwebSnapshot.snapshot(t),d=a(async p=>{let c=await(await fetch(p)).blob();return new Promise((h,f)=>{let i=new FileReader;i.onloadend=()=>h(i.result),i.onerror=f,i.readAsDataURL(c);})},"toDataURL"),l=a(async p=>{await Promise.all(p.childNodes.map(async s=>{if(s.tagName==="img"&&s.attributes.src?.startsWith("blob:")){let c=await d(s.attributes.src);s.attributes.src=c;}s.childNodes?.length&&await l(s);}));},"replaceBlobUrls");l(o).then(()=>{r({snapshot:o});});}),"takeSnapshot");Cypress.Commands.add("takeSnapshot",t=>{Cypress.config("isTextTerminal")&&cy.document().then(e=>{cy.wrap(n(e,!0)).then(r=>{cy.get("@manualSnapshots").then(o=>[...o,{...r,name:t}]).as("manualSnapshots");});});});var y=a(t=>({...t("diffThreshold")&&{diffThreshold:t("diffThreshold")},...t("delay")&&{delay:t("delay")},...t("diffIncludeAntiAliasing")&&{diffIncludeAntiAliasing:t("diffIncludeAntiAliasing")},...t("diffThreshold")&&{diffThreshold:t("diffThreshold")},...t("forcedColors")&&{forcedColors:t("forcedColors")},...t("pauseAnimationAtEnd")&&{pauseAnimationAtEnd:t("pauseAnimationAtEnd")},...t("prefersReducedMotion")&&{prefersReducedMotion:t("prefersReducedMotion")},...t("cropToViewport")&&{cropToViewport:t("cropToViewport")},...t("ignoreSelectors")&&{ignoreSelectors:t("ignoreSelectors")}}),"buildChromaticParams");beforeEach(()=>{Cypress.config("isTextTerminal")&&(cy.wrap([]).as("manualSnapshots"),cy.task("prepareArchives",{action:"setup-network-listener",payload:{allowedDomains:Cypress.env("assetDomains")}}));});afterEach(()=>{Cypress.config("isTextTerminal")&&cy.document().then(t=>{cy.wrap(n(t)).then(e=>{cy.get("@manualSnapshots").then((r=[])=>{cy.url().then(o=>{cy.task("prepareArchives",{action:"save-archives",payload:{testTitlePath:[Cypress.spec.relativeToCommonRoot,...Cypress.currentTest.titlePath],domSnapshots:[...r,...e?[e]:[]],chromaticStorybookParams:y(Cypress.env),pageUrl:o,viewport:{height:Cypress.config("viewportHeight"),width:Cypress.config("viewportWidth")},outputDir:Cypress.config("downloadsFolder")}});});});});});});
6
6
  //# sourceMappingURL=out.js.map
7
7
  //# sourceMappingURL=support.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/support.ts","../src/commands.ts"],"names":["snapshot","Cypress","Commands","add","name","config","cy","document","then","doc","manualSnapshot","get","snapshots","as","beforeEach","wrap","task","action","payload","allowedDomains","env","afterEach","automaticSnapshots","manualSnapshots","url","testTitlePath","spec","relativeToCommonRoot","currentTest","titlePath","domSnapshots","chromaticStorybookParams","diffThreshold","delay","diffIncludeAntiAliasing","forcedColors","pauseAnimationAtEnd","prefersReducedMotion","cropToViewport","ignoreSelectors","pageUrl","viewport","height","width","outputDir"],"mappings":"AAAA,OAASA,YAAAA,MAAgB,2BCAzB,OAASA,YAAAA,MAAgB,2BAkBzBC,QAAQC,SAASC,IAAI,eAAiBC,GAAAA,CAE/BH,QAAQI,OAAO,gBAAA,GAIpBC,GAAGC,SAAQ,EAAGC,KAAMC,GAAAA,CAElB,IAAMC,EAAiBV,EAASS,CAAAA,EAEhCH,GAAGK,IAAI,kBAAA,EAEJH,KAAMI,GACE,IAAIA,EAAW,CAAER,KAAAA,EAAMJ,SAAUU,CAAe,EACzD,EACCG,GAAG,iBAAA,CACR,CAAA,CACF,CAAA,ED/BAC,WAAW,IAAA,CAEJb,QAAQI,OAAO,gBAAA,IAMpBC,GAAGS,KAAK,CAAA,CAAE,EAAEF,GAAG,iBAAA,EACfP,GAAGU,KAAK,kBAAmB,CACzBC,OAAQ,yBACRC,QAAS,CAAEC,eAAgBlB,QAAQmB,IAAI,cAAA,CAAgB,CACzD,CAAA,EACF,CAAA,EAEAC,UAAU,IAAA,CAEHpB,QAAQI,OAAO,gBAAA,GAIpBC,GAAGC,SAAQ,EAAGC,KAAMC,GAAAA,CAClB,IAAMa,EAAsBrB,QAAQmB,IAAI,qBAAA,EAEpC,CAAA,EADA,CAAC,CAAEpB,SAAUA,EAASS,CAAAA,CAAK,GAG/BH,GAAGK,IAAI,kBAAA,EAAoBH,KAAK,CAACe,EAAkB,CAAA,IAAE,CACnDjB,GAAGkB,IAAG,EAAGhB,KAAMgB,GAAAA,CAEblB,GAAGU,KAAK,kBAAmB,CACzBC,OAAQ,gBACRC,QAAS,CAEPO,cAAe,CAACxB,QAAQyB,KAAKC,wBAAyB1B,QAAQ2B,YAAYC,WAC1EC,aAAc,IAAIP,KAAoBD,GACtCS,yBAA0B,CACxB,GAAI9B,QAAQmB,IAAI,eAAA,GAAoB,CAAEY,cAAe/B,QAAQmB,IAAI,eAAA,CAAiB,EAClF,GAAInB,QAAQmB,IAAI,OAAA,GAAY,CAAEa,MAAOhC,QAAQmB,IAAI,OAAA,CAAS,EAC1D,GAAInB,QAAQmB,IAAI,yBAAA,GAA8B,CAC5Cc,wBAAyBjC,QAAQmB,IAAI,yBAAA,CACvC,EACA,GAAInB,QAAQmB,IAAI,eAAA,GAAoB,CAAEY,cAAe/B,QAAQmB,IAAI,eAAA,CAAiB,EAClF,GAAInB,QAAQmB,IAAI,cAAA,GAAmB,CAAEe,aAAclC,QAAQmB,IAAI,cAAA,CAAgB,EAC/E,GAAInB,QAAQmB,IAAI,qBAAA,GAA0B,CACxCgB,oBAAqBnC,QAAQmB,IAAI,qBAAA,CACnC,EACA,GAAInB,QAAQmB,IAAI,sBAAA,GAA2B,CACzCiB,qBAAsBpC,QAAQmB,IAAI,sBAAA,CACpC,EACA,GAAInB,QAAQmB,IAAI,gBAAA,GAAqB,CACnCkB,eAAgBrC,QAAQmB,IAAI,gBAAA,CAC9B,EACA,GAAInB,QAAQmB,IAAI,iBAAA,GAAsB,CACpCmB,gBAAiBtC,QAAQmB,IAAI,iBAAA,CAC/B,CACF,EACAoB,QAAShB,EACTiB,SAAU,CACRC,OAAQzC,QAAQI,OAAO,gBAAA,EACvBsC,MAAO1C,QAAQI,OAAO,eAAA,CACxB,EACAuC,UAAW3C,QAAQI,OAAO,iBAAA,CAC5B,CACF,CAAA,CACF,CAAA,CACF,CAAA,CACF,CAAA,CACF,CAAA","sourcesContent":["import { snapshot } from '@chromaui/rrweb-snapshot';\nimport './commands';\n\n// these client-side lifecycle hooks will be added to the user's Cypress suite\nbeforeEach(() => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n // this \"manualSnapshots\" variable will be available before, during, and after the test,\n // then cleaned up before the next test is run\n // (see https://docs.cypress.io/guides/core-concepts/variables-and-aliases#Aliases)\n cy.wrap([]).as('manualSnapshots');\n cy.task('prepareArchives', {\n action: 'setup-network-listener',\n payload: { allowedDomains: Cypress.env('assetDomains') },\n });\n});\n\nafterEach(() => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n // can we be sure this always fires after all the requests are back?\n cy.document().then((doc) => {\n const automaticSnapshots = !Cypress.env('disableAutoSnapshot')\n ? [{ snapshot: snapshot(doc) }]\n : [];\n // @ts-expect-error will fix when Cypress has its own package\n cy.get('@manualSnapshots').then((manualSnapshots = []) => {\n cy.url().then((url) => {\n // pass the snapshot to the server to write to disk\n cy.task('prepareArchives', {\n action: 'save-archives',\n payload: {\n // @ts-expect-error relativeToCommonRoot is on spec (but undocumented)\n testTitlePath: [Cypress.spec.relativeToCommonRoot, ...Cypress.currentTest.titlePath],\n domSnapshots: [...manualSnapshots, ...automaticSnapshots],\n chromaticStorybookParams: {\n ...(Cypress.env('diffThreshold') && { diffThreshold: Cypress.env('diffThreshold') }),\n ...(Cypress.env('delay') && { delay: Cypress.env('delay') }),\n ...(Cypress.env('diffIncludeAntiAliasing') && {\n diffIncludeAntiAliasing: Cypress.env('diffIncludeAntiAliasing'),\n }),\n ...(Cypress.env('diffThreshold') && { diffThreshold: Cypress.env('diffThreshold') }),\n ...(Cypress.env('forcedColors') && { forcedColors: Cypress.env('forcedColors') }),\n ...(Cypress.env('pauseAnimationAtEnd') && {\n pauseAnimationAtEnd: Cypress.env('pauseAnimationAtEnd'),\n }),\n ...(Cypress.env('prefersReducedMotion') && {\n prefersReducedMotion: Cypress.env('prefersReducedMotion'),\n }),\n ...(Cypress.env('cropToViewport') && {\n cropToViewport: Cypress.env('cropToViewport'),\n }),\n ...(Cypress.env('ignoreSelectors') && {\n ignoreSelectors: Cypress.env('ignoreSelectors'),\n }),\n },\n pageUrl: url,\n viewport: {\n height: Cypress.config('viewportHeight'),\n width: Cypress.config('viewportWidth'),\n },\n outputDir: Cypress.config('downloadsFolder'),\n },\n });\n });\n });\n });\n});\n","import { snapshot } from '@chromaui/rrweb-snapshot';\nimport type { elementNode } from '@chromaui/rrweb-snapshot';\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Cypress {\n interface Chainable {\n /**\n * Method for taking a manual snapshot with Chromatic\n *\n *\n * @param {string} name - Use to apply a custom name to the snapshot (optional)\n */\n takeSnapshot(name?: string): Chainable<any>;\n }\n }\n}\n\nCypress.Commands.add('takeSnapshot', (name?: string) => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n\n cy.document().then((doc) => {\n // here, handle the source map\n const manualSnapshot = snapshot(doc);\n // reassign manualSnapshots so it includes this new snapshot\n cy.get('@manualSnapshots')\n // @ts-expect-error will fix when Cypress has its own package\n .then((snapshots: elementNode[]) => {\n return [...snapshots, { name, snapshot: manualSnapshot }];\n })\n .as('manualSnapshots');\n });\n});\n"]}
1
+ {"version":3,"sources":["../src/takeSnapshot.ts","../src/commands.ts","../src/support.ts"],"names":["snapshot","takeSnapshot","__name","doc","isManualSnapshot","Promise","resolve","Cypress","env","domSnapshot","toDataURL","url","blob","fetch","resolveFileRead","reject","reader","FileReader","onloadend","result","onerror","readAsDataURL","replaceBlobUrls","node","all","childNodes","map","childNode","tagName","attributes","src","startsWith","base64Url","length","then","Commands","add","name","config","cy","document","wrap","takeChromaticSnapshot","manualSnapshot","get","snapshots","as","buildChromaticParams","diffThreshold","delay","diffIncludeAntiAliasing","forcedColors","pauseAnimationAtEnd","prefersReducedMotion","cropToViewport","ignoreSelectors","beforeEach","task","action","payload","allowedDomains","afterEach","automaticSnapshot","manualSnapshots","testTitlePath","spec","relativeToCommonRoot","currentTest","titlePath","domSnapshots","chromaticStorybookParams","pageUrl","viewport","height","width","outputDir"],"mappings":"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","sourcesContent":["import { serializedNodeWithId, snapshot } from '@chromaui/rrweb-snapshot';\nimport { CypressSnapshot } from './types';\n\nexport const takeSnapshot = (\n doc: Document,\n isManualSnapshot?: boolean\n): Promise<CypressSnapshot | null> => {\n return new Promise((resolve) => {\n if (!isManualSnapshot && Cypress.env('disableAutoSnapshot')) {\n resolve(null);\n }\n\n const domSnapshot = snapshot(doc);\n // do some post-processing on the snapshot\n const toDataURL = async (url: string) => {\n // read contents of the blob URL\n const response = await fetch(url);\n const blob = await response.blob();\n return new Promise((resolveFileRead, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => resolveFileRead(reader.result);\n reader.onerror = reject;\n // convert the blob to base64 string\n reader.readAsDataURL(blob);\n });\n };\n\n const replaceBlobUrls = async (node: serializedNodeWithId) => {\n await Promise.all(\n // @ts-expect-error we assume childNodes will be on there\n node.childNodes.map(async (childNode) => {\n if (childNode.tagName === 'img' && childNode.attributes.src?.startsWith('blob:')) {\n const base64Url = await toDataURL(childNode.attributes.src);\n // eslint-disable-next-line no-param-reassign\n childNode.attributes.src = base64Url;\n }\n\n if (childNode.childNodes?.length) {\n await replaceBlobUrls(childNode);\n }\n })\n );\n };\n\n replaceBlobUrls(domSnapshot).then(() => {\n resolve({ snapshot: domSnapshot });\n });\n });\n};\n","import { takeSnapshot as takeChromaticSnapshot } from './takeSnapshot';\nimport { CypressSnapshot } from './types';\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Cypress {\n interface Chainable {\n /**\n * Method for taking a manual snapshot with Chromatic\n *\n *\n * @param {string} name - Use to apply a custom name to the snapshot (optional)\n */\n takeSnapshot(name?: string): Chainable<any>;\n }\n }\n}\n\nCypress.Commands.add('takeSnapshot', (name?: string) => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n\n cy.document().then((doc) => {\n // here, handle the source map\n\n cy.wrap(takeChromaticSnapshot(doc, true)).then((manualSnapshot: CypressSnapshot) => {\n // reassign manualSnapshots so it includes this new snapshot\n cy.get('@manualSnapshots')\n // @ts-expect-error will fix when Cypress has its own package\n .then((snapshots: CypressSnapshot[]) => {\n return [...snapshots, { ...manualSnapshot, name }];\n })\n .as('manualSnapshots');\n });\n });\n});\n","import './commands';\nimport { takeSnapshot } from './takeSnapshot';\nimport { CypressSnapshot } from './types';\n\nconst buildChromaticParams = (env: Cypress.Cypress['env']) => ({\n ...(env('diffThreshold') && {\n diffThreshold: env('diffThreshold'),\n }),\n ...(env('delay') && { delay: env('delay') }),\n ...(env('diffIncludeAntiAliasing') && {\n diffIncludeAntiAliasing: env('diffIncludeAntiAliasing'),\n }),\n ...(env('diffThreshold') && {\n diffThreshold: env('diffThreshold'),\n }),\n ...(env('forcedColors') && { forcedColors: env('forcedColors') }),\n ...(env('pauseAnimationAtEnd') && {\n pauseAnimationAtEnd: env('pauseAnimationAtEnd'),\n }),\n ...(env('prefersReducedMotion') && {\n prefersReducedMotion: env('prefersReducedMotion'),\n }),\n ...(env('cropToViewport') && {\n cropToViewport: env('cropToViewport'),\n }),\n ...(env('ignoreSelectors') && {\n ignoreSelectors: env('ignoreSelectors'),\n }),\n});\n\n// these client-side lifecycle hooks will be added to the user's Cypress suite\nbeforeEach(() => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n // this \"manualSnapshots\" variable will be available before, during, and after the test,\n // then cleaned up before the next test is run\n // (see https://docs.cypress.io/guides/core-concepts/variables-and-aliases#Aliases)\n cy.wrap([]).as('manualSnapshots');\n cy.task('prepareArchives', {\n action: 'setup-network-listener',\n payload: { allowedDomains: Cypress.env('assetDomains') },\n });\n});\n\nafterEach(() => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n // can we be sure this always fires after all the requests are back?\n cy.document().then((doc) => {\n cy.wrap(takeSnapshot(doc)).then((automaticSnapshot: CypressSnapshot) => {\n // @ts-expect-error will fix when Cypress has its own package\n cy.get('@manualSnapshots').then((manualSnapshots = []) => {\n cy.url().then((url) => {\n // pass the snapshot to the server to write to disk\n cy.task('prepareArchives', {\n action: 'save-archives',\n payload: {\n // @ts-expect-error relativeToCommonRoot is on spec (but undocumented)\n testTitlePath: [Cypress.spec.relativeToCommonRoot, ...Cypress.currentTest.titlePath],\n domSnapshots: [...manualSnapshots, ...(automaticSnapshot ? [automaticSnapshot] : [])],\n chromaticStorybookParams: buildChromaticParams(Cypress.env),\n pageUrl: url,\n viewport: {\n height: Cypress.config('viewportHeight'),\n width: Cypress.config('viewportWidth'),\n },\n outputDir: Cypress.config('downloadsFolder'),\n },\n });\n });\n });\n });\n });\n});\n"]}
package/dist/support.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { snapshot } from '@chromaui/rrweb-snapshot';
2
2
 
3
- Cypress.Commands.add("takeSnapshot",e=>{Cypress.config("isTextTerminal")&&cy.document().then(s=>{let r=snapshot(s);cy.get("@manualSnapshots").then(o=>[...o,{name:e,snapshot:r}]).as("manualSnapshots");});});beforeEach(()=>{Cypress.config("isTextTerminal")&&(cy.wrap([]).as("manualSnapshots"),cy.task("prepareArchives",{action:"setup-network-listener",payload:{allowedDomains:Cypress.env("assetDomains")}}));});afterEach(()=>{Cypress.config("isTextTerminal")&&cy.document().then(e=>{let s=Cypress.env("disableAutoSnapshot")?[]:[{snapshot:snapshot(e)}];cy.get("@manualSnapshots").then((r=[])=>{cy.url().then(o=>{cy.task("prepareArchives",{action:"save-archives",payload:{testTitlePath:[Cypress.spec.relativeToCommonRoot,...Cypress.currentTest.titlePath],domSnapshots:[...r,...s],chromaticStorybookParams:{...Cypress.env("diffThreshold")&&{diffThreshold:Cypress.env("diffThreshold")},...Cypress.env("delay")&&{delay:Cypress.env("delay")},...Cypress.env("diffIncludeAntiAliasing")&&{diffIncludeAntiAliasing:Cypress.env("diffIncludeAntiAliasing")},...Cypress.env("diffThreshold")&&{diffThreshold:Cypress.env("diffThreshold")},...Cypress.env("forcedColors")&&{forcedColors:Cypress.env("forcedColors")},...Cypress.env("pauseAnimationAtEnd")&&{pauseAnimationAtEnd:Cypress.env("pauseAnimationAtEnd")},...Cypress.env("prefersReducedMotion")&&{prefersReducedMotion:Cypress.env("prefersReducedMotion")},...Cypress.env("cropToViewport")&&{cropToViewport:Cypress.env("cropToViewport")},...Cypress.env("ignoreSelectors")&&{ignoreSelectors:Cypress.env("ignoreSelectors")}},pageUrl:o,viewport:{height:Cypress.config("viewportHeight"),width:Cypress.config("viewportWidth")},outputDir:Cypress.config("downloadsFolder")}});});});});});
3
+ var m=Object.defineProperty;var a=(t,e)=>m(t,"name",{value:e,configurable:!0});var n=a((t,e)=>new Promise(r=>{!e&&Cypress.env("disableAutoSnapshot")&&r(null);let o=snapshot(t),d=a(async p=>{let c=await(await fetch(p)).blob();return new Promise((h,f)=>{let i=new FileReader;i.onloadend=()=>h(i.result),i.onerror=f,i.readAsDataURL(c);})},"toDataURL"),l=a(async p=>{await Promise.all(p.childNodes.map(async s=>{if(s.tagName==="img"&&s.attributes.src?.startsWith("blob:")){let c=await d(s.attributes.src);s.attributes.src=c;}s.childNodes?.length&&await l(s);}));},"replaceBlobUrls");l(o).then(()=>{r({snapshot:o});});}),"takeSnapshot");Cypress.Commands.add("takeSnapshot",t=>{Cypress.config("isTextTerminal")&&cy.document().then(e=>{cy.wrap(n(e,!0)).then(r=>{cy.get("@manualSnapshots").then(o=>[...o,{...r,name:t}]).as("manualSnapshots");});});});var y=a(t=>({...t("diffThreshold")&&{diffThreshold:t("diffThreshold")},...t("delay")&&{delay:t("delay")},...t("diffIncludeAntiAliasing")&&{diffIncludeAntiAliasing:t("diffIncludeAntiAliasing")},...t("diffThreshold")&&{diffThreshold:t("diffThreshold")},...t("forcedColors")&&{forcedColors:t("forcedColors")},...t("pauseAnimationAtEnd")&&{pauseAnimationAtEnd:t("pauseAnimationAtEnd")},...t("prefersReducedMotion")&&{prefersReducedMotion:t("prefersReducedMotion")},...t("cropToViewport")&&{cropToViewport:t("cropToViewport")},...t("ignoreSelectors")&&{ignoreSelectors:t("ignoreSelectors")}}),"buildChromaticParams");beforeEach(()=>{Cypress.config("isTextTerminal")&&(cy.wrap([]).as("manualSnapshots"),cy.task("prepareArchives",{action:"setup-network-listener",payload:{allowedDomains:Cypress.env("assetDomains")}}));});afterEach(()=>{Cypress.config("isTextTerminal")&&cy.document().then(t=>{cy.wrap(n(t)).then(e=>{cy.get("@manualSnapshots").then((r=[])=>{cy.url().then(o=>{cy.task("prepareArchives",{action:"save-archives",payload:{testTitlePath:[Cypress.spec.relativeToCommonRoot,...Cypress.currentTest.titlePath],domSnapshots:[...r,...e?[e]:[]],chromaticStorybookParams:y(Cypress.env),pageUrl:o,viewport:{height:Cypress.config("viewportHeight"),width:Cypress.config("viewportWidth")},outputDir:Cypress.config("downloadsFolder")}});});});});});});
4
4
  //# sourceMappingURL=out.js.map
5
5
  //# sourceMappingURL=support.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/support.ts","../src/commands.ts"],"names":["snapshot","Cypress","Commands","add","name","config","cy","document","then","doc","manualSnapshot","get","snapshots","as","beforeEach","wrap","task","action","payload","allowedDomains","env","afterEach","automaticSnapshots","manualSnapshots","url","testTitlePath","spec","relativeToCommonRoot","currentTest","titlePath","domSnapshots","chromaticStorybookParams","diffThreshold","delay","diffIncludeAntiAliasing","forcedColors","pauseAnimationAtEnd","prefersReducedMotion","cropToViewport","ignoreSelectors","pageUrl","viewport","height","width","outputDir"],"mappings":"AAAA,OAASA,YAAAA,MAAgB,2BCAzB,OAASA,YAAAA,MAAgB,2BAkBzBC,QAAQC,SAASC,IAAI,eAAiBC,GAAAA,CAE/BH,QAAQI,OAAO,gBAAA,GAIpBC,GAAGC,SAAQ,EAAGC,KAAMC,GAAAA,CAElB,IAAMC,EAAiBV,EAASS,CAAAA,EAEhCH,GAAGK,IAAI,kBAAA,EAEJH,KAAMI,GACE,IAAIA,EAAW,CAAER,KAAAA,EAAMJ,SAAUU,CAAe,EACzD,EACCG,GAAG,iBAAA,CACR,CAAA,CACF,CAAA,ED/BAC,WAAW,IAAA,CAEJb,QAAQI,OAAO,gBAAA,IAMpBC,GAAGS,KAAK,CAAA,CAAE,EAAEF,GAAG,iBAAA,EACfP,GAAGU,KAAK,kBAAmB,CACzBC,OAAQ,yBACRC,QAAS,CAAEC,eAAgBlB,QAAQmB,IAAI,cAAA,CAAgB,CACzD,CAAA,EACF,CAAA,EAEAC,UAAU,IAAA,CAEHpB,QAAQI,OAAO,gBAAA,GAIpBC,GAAGC,SAAQ,EAAGC,KAAMC,GAAAA,CAClB,IAAMa,EAAsBrB,QAAQmB,IAAI,qBAAA,EAEpC,CAAA,EADA,CAAC,CAAEpB,SAAUA,EAASS,CAAAA,CAAK,GAG/BH,GAAGK,IAAI,kBAAA,EAAoBH,KAAK,CAACe,EAAkB,CAAA,IAAE,CACnDjB,GAAGkB,IAAG,EAAGhB,KAAMgB,GAAAA,CAEblB,GAAGU,KAAK,kBAAmB,CACzBC,OAAQ,gBACRC,QAAS,CAEPO,cAAe,CAACxB,QAAQyB,KAAKC,wBAAyB1B,QAAQ2B,YAAYC,WAC1EC,aAAc,IAAIP,KAAoBD,GACtCS,yBAA0B,CACxB,GAAI9B,QAAQmB,IAAI,eAAA,GAAoB,CAAEY,cAAe/B,QAAQmB,IAAI,eAAA,CAAiB,EAClF,GAAInB,QAAQmB,IAAI,OAAA,GAAY,CAAEa,MAAOhC,QAAQmB,IAAI,OAAA,CAAS,EAC1D,GAAInB,QAAQmB,IAAI,yBAAA,GAA8B,CAC5Cc,wBAAyBjC,QAAQmB,IAAI,yBAAA,CACvC,EACA,GAAInB,QAAQmB,IAAI,eAAA,GAAoB,CAAEY,cAAe/B,QAAQmB,IAAI,eAAA,CAAiB,EAClF,GAAInB,QAAQmB,IAAI,cAAA,GAAmB,CAAEe,aAAclC,QAAQmB,IAAI,cAAA,CAAgB,EAC/E,GAAInB,QAAQmB,IAAI,qBAAA,GAA0B,CACxCgB,oBAAqBnC,QAAQmB,IAAI,qBAAA,CACnC,EACA,GAAInB,QAAQmB,IAAI,sBAAA,GAA2B,CACzCiB,qBAAsBpC,QAAQmB,IAAI,sBAAA,CACpC,EACA,GAAInB,QAAQmB,IAAI,gBAAA,GAAqB,CACnCkB,eAAgBrC,QAAQmB,IAAI,gBAAA,CAC9B,EACA,GAAInB,QAAQmB,IAAI,iBAAA,GAAsB,CACpCmB,gBAAiBtC,QAAQmB,IAAI,iBAAA,CAC/B,CACF,EACAoB,QAAShB,EACTiB,SAAU,CACRC,OAAQzC,QAAQI,OAAO,gBAAA,EACvBsC,MAAO1C,QAAQI,OAAO,eAAA,CACxB,EACAuC,UAAW3C,QAAQI,OAAO,iBAAA,CAC5B,CACF,CAAA,CACF,CAAA,CACF,CAAA,CACF,CAAA,CACF,CAAA","sourcesContent":["import { snapshot } from '@chromaui/rrweb-snapshot';\nimport './commands';\n\n// these client-side lifecycle hooks will be added to the user's Cypress suite\nbeforeEach(() => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n // this \"manualSnapshots\" variable will be available before, during, and after the test,\n // then cleaned up before the next test is run\n // (see https://docs.cypress.io/guides/core-concepts/variables-and-aliases#Aliases)\n cy.wrap([]).as('manualSnapshots');\n cy.task('prepareArchives', {\n action: 'setup-network-listener',\n payload: { allowedDomains: Cypress.env('assetDomains') },\n });\n});\n\nafterEach(() => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n // can we be sure this always fires after all the requests are back?\n cy.document().then((doc) => {\n const automaticSnapshots = !Cypress.env('disableAutoSnapshot')\n ? [{ snapshot: snapshot(doc) }]\n : [];\n // @ts-expect-error will fix when Cypress has its own package\n cy.get('@manualSnapshots').then((manualSnapshots = []) => {\n cy.url().then((url) => {\n // pass the snapshot to the server to write to disk\n cy.task('prepareArchives', {\n action: 'save-archives',\n payload: {\n // @ts-expect-error relativeToCommonRoot is on spec (but undocumented)\n testTitlePath: [Cypress.spec.relativeToCommonRoot, ...Cypress.currentTest.titlePath],\n domSnapshots: [...manualSnapshots, ...automaticSnapshots],\n chromaticStorybookParams: {\n ...(Cypress.env('diffThreshold') && { diffThreshold: Cypress.env('diffThreshold') }),\n ...(Cypress.env('delay') && { delay: Cypress.env('delay') }),\n ...(Cypress.env('diffIncludeAntiAliasing') && {\n diffIncludeAntiAliasing: Cypress.env('diffIncludeAntiAliasing'),\n }),\n ...(Cypress.env('diffThreshold') && { diffThreshold: Cypress.env('diffThreshold') }),\n ...(Cypress.env('forcedColors') && { forcedColors: Cypress.env('forcedColors') }),\n ...(Cypress.env('pauseAnimationAtEnd') && {\n pauseAnimationAtEnd: Cypress.env('pauseAnimationAtEnd'),\n }),\n ...(Cypress.env('prefersReducedMotion') && {\n prefersReducedMotion: Cypress.env('prefersReducedMotion'),\n }),\n ...(Cypress.env('cropToViewport') && {\n cropToViewport: Cypress.env('cropToViewport'),\n }),\n ...(Cypress.env('ignoreSelectors') && {\n ignoreSelectors: Cypress.env('ignoreSelectors'),\n }),\n },\n pageUrl: url,\n viewport: {\n height: Cypress.config('viewportHeight'),\n width: Cypress.config('viewportWidth'),\n },\n outputDir: Cypress.config('downloadsFolder'),\n },\n });\n });\n });\n });\n});\n","import { snapshot } from '@chromaui/rrweb-snapshot';\nimport type { elementNode } from '@chromaui/rrweb-snapshot';\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Cypress {\n interface Chainable {\n /**\n * Method for taking a manual snapshot with Chromatic\n *\n *\n * @param {string} name - Use to apply a custom name to the snapshot (optional)\n */\n takeSnapshot(name?: string): Chainable<any>;\n }\n }\n}\n\nCypress.Commands.add('takeSnapshot', (name?: string) => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n\n cy.document().then((doc) => {\n // here, handle the source map\n const manualSnapshot = snapshot(doc);\n // reassign manualSnapshots so it includes this new snapshot\n cy.get('@manualSnapshots')\n // @ts-expect-error will fix when Cypress has its own package\n .then((snapshots: elementNode[]) => {\n return [...snapshots, { name, snapshot: manualSnapshot }];\n })\n .as('manualSnapshots');\n });\n});\n"]}
1
+ {"version":3,"sources":["../src/takeSnapshot.ts","../src/commands.ts","../src/support.ts"],"names":["snapshot","takeSnapshot","__name","doc","isManualSnapshot","Promise","resolve","Cypress","env","domSnapshot","toDataURL","url","blob","fetch","resolveFileRead","reject","reader","FileReader","onloadend","result","onerror","readAsDataURL","replaceBlobUrls","node","all","childNodes","map","childNode","tagName","attributes","src","startsWith","base64Url","length","then","Commands","add","name","config","cy","document","wrap","takeChromaticSnapshot","manualSnapshot","get","snapshots","as","buildChromaticParams","diffThreshold","delay","diffIncludeAntiAliasing","forcedColors","pauseAnimationAtEnd","prefersReducedMotion","cropToViewport","ignoreSelectors","beforeEach","task","action","payload","allowedDomains","afterEach","automaticSnapshot","manualSnapshots","testTitlePath","spec","relativeToCommonRoot","currentTest","titlePath","domSnapshots","chromaticStorybookParams","pageUrl","viewport","height","width","outputDir"],"mappings":"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","sourcesContent":["import { serializedNodeWithId, snapshot } from '@chromaui/rrweb-snapshot';\nimport { CypressSnapshot } from './types';\n\nexport const takeSnapshot = (\n doc: Document,\n isManualSnapshot?: boolean\n): Promise<CypressSnapshot | null> => {\n return new Promise((resolve) => {\n if (!isManualSnapshot && Cypress.env('disableAutoSnapshot')) {\n resolve(null);\n }\n\n const domSnapshot = snapshot(doc);\n // do some post-processing on the snapshot\n const toDataURL = async (url: string) => {\n // read contents of the blob URL\n const response = await fetch(url);\n const blob = await response.blob();\n return new Promise((resolveFileRead, reject) => {\n const reader = new FileReader();\n reader.onloadend = () => resolveFileRead(reader.result);\n reader.onerror = reject;\n // convert the blob to base64 string\n reader.readAsDataURL(blob);\n });\n };\n\n const replaceBlobUrls = async (node: serializedNodeWithId) => {\n await Promise.all(\n // @ts-expect-error we assume childNodes will be on there\n node.childNodes.map(async (childNode) => {\n if (childNode.tagName === 'img' && childNode.attributes.src?.startsWith('blob:')) {\n const base64Url = await toDataURL(childNode.attributes.src);\n // eslint-disable-next-line no-param-reassign\n childNode.attributes.src = base64Url;\n }\n\n if (childNode.childNodes?.length) {\n await replaceBlobUrls(childNode);\n }\n })\n );\n };\n\n replaceBlobUrls(domSnapshot).then(() => {\n resolve({ snapshot: domSnapshot });\n });\n });\n};\n","import { takeSnapshot as takeChromaticSnapshot } from './takeSnapshot';\nimport { CypressSnapshot } from './types';\n\ndeclare global {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Cypress {\n interface Chainable {\n /**\n * Method for taking a manual snapshot with Chromatic\n *\n *\n * @param {string} name - Use to apply a custom name to the snapshot (optional)\n */\n takeSnapshot(name?: string): Chainable<any>;\n }\n }\n}\n\nCypress.Commands.add('takeSnapshot', (name?: string) => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n\n cy.document().then((doc) => {\n // here, handle the source map\n\n cy.wrap(takeChromaticSnapshot(doc, true)).then((manualSnapshot: CypressSnapshot) => {\n // reassign manualSnapshots so it includes this new snapshot\n cy.get('@manualSnapshots')\n // @ts-expect-error will fix when Cypress has its own package\n .then((snapshots: CypressSnapshot[]) => {\n return [...snapshots, { ...manualSnapshot, name }];\n })\n .as('manualSnapshots');\n });\n });\n});\n","import './commands';\nimport { takeSnapshot } from './takeSnapshot';\nimport { CypressSnapshot } from './types';\n\nconst buildChromaticParams = (env: Cypress.Cypress['env']) => ({\n ...(env('diffThreshold') && {\n diffThreshold: env('diffThreshold'),\n }),\n ...(env('delay') && { delay: env('delay') }),\n ...(env('diffIncludeAntiAliasing') && {\n diffIncludeAntiAliasing: env('diffIncludeAntiAliasing'),\n }),\n ...(env('diffThreshold') && {\n diffThreshold: env('diffThreshold'),\n }),\n ...(env('forcedColors') && { forcedColors: env('forcedColors') }),\n ...(env('pauseAnimationAtEnd') && {\n pauseAnimationAtEnd: env('pauseAnimationAtEnd'),\n }),\n ...(env('prefersReducedMotion') && {\n prefersReducedMotion: env('prefersReducedMotion'),\n }),\n ...(env('cropToViewport') && {\n cropToViewport: env('cropToViewport'),\n }),\n ...(env('ignoreSelectors') && {\n ignoreSelectors: env('ignoreSelectors'),\n }),\n});\n\n// these client-side lifecycle hooks will be added to the user's Cypress suite\nbeforeEach(() => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n // this \"manualSnapshots\" variable will be available before, during, and after the test,\n // then cleaned up before the next test is run\n // (see https://docs.cypress.io/guides/core-concepts/variables-and-aliases#Aliases)\n cy.wrap([]).as('manualSnapshots');\n cy.task('prepareArchives', {\n action: 'setup-network-listener',\n payload: { allowedDomains: Cypress.env('assetDomains') },\n });\n});\n\nafterEach(() => {\n // don't take snapshots when running `cypress open`\n if (!Cypress.config('isTextTerminal')) {\n return;\n }\n // can we be sure this always fires after all the requests are back?\n cy.document().then((doc) => {\n cy.wrap(takeSnapshot(doc)).then((automaticSnapshot: CypressSnapshot) => {\n // @ts-expect-error will fix when Cypress has its own package\n cy.get('@manualSnapshots').then((manualSnapshots = []) => {\n cy.url().then((url) => {\n // pass the snapshot to the server to write to disk\n cy.task('prepareArchives', {\n action: 'save-archives',\n payload: {\n // @ts-expect-error relativeToCommonRoot is on spec (but undocumented)\n testTitlePath: [Cypress.spec.relativeToCommonRoot, ...Cypress.currentTest.titlePath],\n domSnapshots: [...manualSnapshots, ...(automaticSnapshot ? [automaticSnapshot] : [])],\n chromaticStorybookParams: buildChromaticParams(Cypress.env),\n pageUrl: url,\n viewport: {\n height: Cypress.config('viewportHeight'),\n width: Cypress.config('viewportWidth'),\n },\n outputDir: Cypress.config('downloadsFolder'),\n },\n });\n });\n });\n });\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chromatic-com/cypress",
3
- "version": "0.9.1",
3
+ "version": "0.10.0",
4
4
  "description": "Chromatic Visual Regression Testing for Cypress",
5
5
  "repository": {
6
6
  "type": "git",
@@ -51,11 +51,9 @@
51
51
  "clean": "rimraf ./dist",
52
52
  "prebuild": "yarn clean",
53
53
  "build": "yarn prebuild && tsup",
54
- "build:watch": "yarn build --watch",
55
54
  "test:cypress": "start-server-and-test 'yarn run dev:server' 3000 'yarn run test:do-cypress'",
56
55
  "test:do-cypress": "ELECTRON_EXTRA_LAUNCH_ARGS=--remote-debugging-port=8192 cypress run --project tests",
57
56
  "test:unit": "jest --passWithNoTests --coverage",
58
- "start": "yarn build:watch",
59
57
  "lint": "eslint src/*",
60
58
  "prettier": "prettier"
61
59
  },