{"version":3,"sources":["../src/index.ts","../src/write-archive/index.ts","../src/utils/logger.ts","../src/write-archive/archive-file.ts","../src/write-archive/dom-snapshot.ts","../src/playwright-api/takeArchive.ts","../src/utils/analytics.ts","../src/resource-archive/index.ts","../src/playwright-api/createResourceArchive.ts","../src/playwright-api/makeTest.ts"],"names":["test","base","expect","outputFile","ensureDir","outputJson","join","logger","process","env","LOG","console","log","_args","warn","mime","path","createHash","ArchiveFile","constructor","url","response","pageUrl","shortenedFileNameLength","URL","originalSrc","toString","toFileSystemPath","sanitizedSrc","pathname","preserveExternalDomain","ensureNonDirectory","encodeQueryString","truncateFileName","removeSpecialChars","addExtension","endsWith","queryString","search","safeQueryString","hash","pathPieces","split","rebuiltPieces","forEach","piece","index","shortName","length","substring","push","startsWith","replace","contentType","nameWithExtension","extname","fileExtension","getExtension","fullUrl","origin","encodeURIComponent","host","name","update","digest","NodeType","srcset","CSS_URL_REGEX","DOMSnapshot","snapshot","Buffer","isBuffer","bufferAsString","JSON","parse","mapAssetPaths","sourceMap","transformedSnapshot","mapNode","stringify","node","mapNodeAttributes","mapTextElement","childNodes","Promise","all","map","childNode","type","Element","attributes","src","sourceVal","has","get","style","cssText","mappedCssText","mapCssUrls","_cssText","tagName","srcsetValue","currentSrc","mapSrcsetUrls","sizes","Text","isStyle","textContent","match","cssUrl","parsedSrcset","set","sanitize","__name","string","toLowerCase","writeTestResult","e2eTestInfo","domSnapshots","archive","chromaticStorybookParams","title","outputDir","finalOutputDir","archiveDir","Map","Object","entries","archiveFile","origSrcPath","fileSystemPath","body","domSnapshot","mappedSnapshot","writeStoriesFile","errors","filter","r","fromEntries","storiesFilename","stories","keys","parameters","server","id","chromatic","readFileSync","dedent","rrweb","require","resolve","takeArchive","page","nameOrTestInfo","maybeTestInfo","testInfo","Error","attachments","a","on","msg","text","evaluate","attach","Analytics","analytics","writeKey","SEGMENT_WRITE_KEY","userId","track","event","properties","hasTrackedRun","trackRun","hasTrackedComplete","trackComplete","Watcher","cdpClient","allowedDomains","client","allowedArchiveOrigins","firstUrl","domain","watch","requestPaused","bind","send","clientSend","request","method","params","error","requestId","responseStatusCode","responseStatusText","responseErrorReason","responseHeaders","requestUrl","isRequestFromAllowedDomain","includes","interceptResponse","result","base64Encoded","contentTypeHeader","find","isFirstRequest","statusCode","statusText","from","value","idle","networkTimeoutMs","DEFAULT_GLOBAL_RESOURCE_ARCHIVE_TIMEOUT_MS","globalNetworkTimerId","globalNetworkResolver","globalNetworkTimeout","setTimeout","networkIdlePromise","waitForLoadState","finally","clearTimeout","race","createResourceArchive","networkTimeout","allowedArchiveDomains","context","newCDPSession","watcher","makeTest","extend","delay","undefined","option","diffIncludeAntiAliasing","diffThreshold","disableAutoCapture","forcedColors","pauseAnimationAtEnd","prefersReducedMotion","resourceArchiveTimeout","save","use","browser","browserType","completeArchive","resourceArchive","snapshots","viewports","viewportSize","width","auto"],"mappings":"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","sourcesContent":["import { test as base, expect } from '@playwright/test';\n\nimport { makeTest } from './playwright-api/makeTest';\n\nexport const test = makeTest(base);\nexport { expect };\n\nexport { takeArchive } from './playwright-api/takeArchive';\nexport type { ChromaticConfig } from './types';\n","import { outputFile, ensureDir, outputJson } from 'fs-extra';\nimport { join } from 'path';\nimport { logger } from '../utils/logger';\nimport { ArchiveFile } from './archive-file';\nimport { DOMSnapshot } from './dom-snapshot';\nimport type { ResourceArchive } from '../resource-archive';\nimport type { ChromaticStorybookParameters } from '../types';\n\n// @storybook/csf's sanitize function, we could import this\nexport const sanitize = (string: string) => {\n  return (\n    string\n      .toLowerCase()\n      // eslint-disable-next-line no-useless-escape\n      .replace(/[ ’–—―′¿'`~!@#$%^&*()_|+\\-=?;:'\",.<>\\{\\}\\[\\]\\\\\\/]/gi, '-')\n      .replace(/-+/g, '-')\n      .replace(/^-+/, '')\n      .replace(/-+$/, '')\n  );\n};\n\n// We write a collection of DOM snapshots and a resource archive in the following locations:\n// <test-title>.stories.json\n// archive/<test-title>.json\n// archive/<file>.<ext>\n\ninterface E2ETestInfo {\n  title: string;\n  outputDir: string;\n  pageUrl: string;\n}\n\nexport async function writeTestResult(\n  e2eTestInfo: E2ETestInfo,\n  domSnapshots: Record<string, Buffer>,\n  archive: ResourceArchive,\n  chromaticStorybookParams: ChromaticStorybookParameters\n) {\n  const { title, outputDir, pageUrl } = e2eTestInfo;\n  // outputDir gives us the test-specific subfolder (https://playwright.dev/docs/api/class-testconfig#test-config-output-dir);\n  // we want to write one level above that\n  const finalOutputDir = join(outputDir, '..', 'chromatic-archives');\n\n  const archiveDir = join(finalOutputDir, 'archive');\n\n  await ensureDir(finalOutputDir);\n\n  logger.log(`Writing test results for \"${title}\"`);\n\n  // Used to store any changes to the asset paths made in order to save them to the file system\n  // so that we can update the `src` attributes in the DOM snapshots.\n  const sourceMap = new Map<string, string>();\n\n  await Promise.all(\n    Object.entries(archive).map(async ([url, response]) => {\n      if ('error' in response) return;\n\n      const archiveFile = new ArchiveFile(url, response, pageUrl);\n      const origSrcPath = archiveFile.originalSrc();\n      const fileSystemPath = archiveFile.toFileSystemPath();\n\n      if (origSrcPath !== fileSystemPath) {\n        sourceMap.set(origSrcPath, fileSystemPath);\n      }\n\n      await outputFile(join(archiveDir, fileSystemPath), response.body);\n    })\n  );\n\n  await Promise.all(\n    await Object.entries(domSnapshots).map(async ([name, domSnapshot]) => {\n      // XXX_jwir3: We go through our stories here and map any instances that are found in\n      //            the keys of the source map to their respective values.\n      const snapshot = new DOMSnapshot(domSnapshot);\n      const mappedSnapshot = await snapshot.mapAssetPaths(sourceMap);\n\n      await outputFile(\n        join(archiveDir, `${sanitize(title)}-${sanitize(name)}.snapshot.json`),\n        mappedSnapshot\n      );\n    })\n  );\n\n  await writeStoriesFile(\n    join(finalOutputDir, `${sanitize(title)}.stories.json`),\n    title,\n    domSnapshots,\n    chromaticStorybookParams\n  );\n\n  const errors = Object.entries(archive).filter(([, r]) => 'error' in r);\n  if (errors.length > 0) {\n    logger.log(`Encountered ${errors.length} errors archiving resources, writing to 'errors.json'`);\n    await outputJson(join(archiveDir, `errors.json`), {\n      errors: Object.fromEntries(errors),\n    });\n  }\n}\n\nasync function writeStoriesFile(\n  storiesFilename: string,\n  title: string,\n  domSnapshots: Record<string, Buffer>,\n  chromaticStorybookParams: ChromaticStorybookParameters\n) {\n  logger.log(`Writing ${storiesFilename}`);\n  await outputJson(storiesFilename, {\n    title,\n    stories: Object.keys(domSnapshots).map((name) => ({\n      name,\n      parameters: {\n        server: { id: `${sanitize(title)}-${sanitize(name)}.snapshot.json` },\n        chromatic: {\n          ...chromaticStorybookParams,\n        },\n      },\n    })),\n  });\n}\n","export const logger = process.env.LOG\n  ? console\n  : { log: (..._args: any[]) => {}, warn: (..._args: any[]) => {} };\n","import mime from 'mime';\nimport path from 'path';\nimport { createHash } from 'node:crypto';\nimport { logger } from '../utils/logger';\nimport type { ArchiveResponse, UrlString } from '../resource-archive';\n\n/**\n * Handles converting the full URL of assets loaded during a test\n * captured in the resource archive into a file system friendly path so\n * that they can be safely written to the file system.\n *\n * This includes:\n * - ensuring the path has a file name to avoid colliding with a directory\n * - encoding the query string into the file name to avoid overwriting files\n *   that have a different response due to the query string\n * - truncating file names and path parts that are too big for some file systems\n * - removing problematic characters\n * - ensuring the file name has an extension\n */\nexport class ArchiveFile {\n  url: URL;\n\n  response: ArchiveResponse;\n\n  shortenedFileNameLength: number;\n\n  pageUrl: URL;\n\n  constructor(url: UrlString, response: ArchiveResponse, pageUrl: UrlString) {\n    this.url = new URL(url);\n    this.response = response;\n    this.shortenedFileNameLength = 250;\n    this.pageUrl = new URL(pageUrl);\n  }\n\n  originalSrc() {\n    return this.url.toString();\n  }\n\n  toFileSystemPath() {\n    let sanitizedSrc = this.url.pathname;\n\n    sanitizedSrc = this.preserveExternalDomain(this.url);\n    sanitizedSrc = this.ensureNonDirectory(sanitizedSrc);\n    sanitizedSrc = this.encodeQueryString(sanitizedSrc);\n    sanitizedSrc = this.truncateFileName(sanitizedSrc);\n    sanitizedSrc = this.removeSpecialChars(sanitizedSrc);\n    sanitizedSrc = this.addExtension(sanitizedSrc);\n\n    return sanitizedSrc;\n  }\n\n  private ensureNonDirectory(pathname: string) {\n    return pathname.endsWith('/') ? `${pathname}index.html` : pathname;\n  }\n\n  private encodeQueryString(pathname: string) {\n    const queryString = this.url.search;\n    if (!queryString) return pathname;\n\n    const safeQueryString = this.hash(queryString);\n    return `${pathname}-${safeQueryString}`;\n  }\n\n  private truncateFileName(pathname: string) {\n    // Split this path to get all of the pieces\n    const pathPieces = pathname.split('/');\n    const rebuiltPieces: Array<string> = [];\n\n    // Shorten each individual piece\n    pathPieces.forEach((piece, index) => {\n      let shortName: string = piece;\n      if (piece.length > this.shortenedFileNameLength) {\n        shortName = piece.substring(0, this.shortenedFileNameLength);\n        logger.log(`Chunk of filename '${piece}' is too long. Shortening to '${shortName}'`);\n      }\n\n      rebuiltPieces.push(shortName);\n    });\n\n    // Re-join the pieces\n    return `${pathname.startsWith('/') ? '/' : ''}${path.join(...rebuiltPieces)}`;\n  }\n\n  private removeSpecialChars(pathname: string) {\n    // The storybook server seems to have a problem with percents in file names\n    return pathname.replace(/[%]/g, '');\n  }\n\n  private addExtension(pathname: string) {\n    if ('error' in this.response || !this.response.contentType) return pathname;\n\n    // Add an extension if needed\n    let nameWithExtension = pathname;\n    if (!path.extname(nameWithExtension)) {\n      const fileExtension = mime.getExtension(this.response.contentType);\n      if (fileExtension) {\n        nameWithExtension = `${pathname}.${fileExtension}`;\n      }\n    }\n\n    return nameWithExtension;\n  }\n\n  private preserveExternalDomain(fullUrl: URL) {\n    if (fullUrl.origin === this.pageUrl.origin) {\n      return fullUrl.pathname;\n    }\n\n    // Windows doesn't support colons in file names\n    const encodedHost = encodeURIComponent(fullUrl.host);\n\n    return `/${encodedHost}/${fullUrl.pathname}`;\n  }\n\n  private hash(name: string) {\n    return createHash('md5').update(name).digest('hex');\n  }\n}\n","/* eslint-disable no-underscore-dangle */\n/* eslint-disable no-param-reassign */\nimport type { serializedNodeWithId } from 'rrweb-snapshot';\nimport { NodeType } from 'rrweb-snapshot';\nimport srcset from 'srcset';\n\n// Matches `url(...)` function in CSS text, excluding data URLs\nconst CSS_URL_REGEX = /url\\((?!['\"]?(?:data):)['\"]?([^'\")]*)['\"]?\\)/gi;\n\n/**\n * Wraps a snapshot from rrweb and handles post-processing to remap asset paths.\n */\nexport class DOMSnapshot {\n  snapshot: serializedNodeWithId;\n\n  constructor(snapshot: Buffer | string) {\n    if (Buffer.isBuffer(snapshot)) {\n      const bufferAsString = snapshot.toString('utf-8');\n      this.snapshot = JSON.parse(bufferAsString);\n    } else {\n      this.snapshot = JSON.parse(snapshot);\n    }\n  }\n\n  async mapAssetPaths(sourceMap: Map<string, string>) {\n    const transformedSnapshot = await this.mapNode(this.snapshot, sourceMap);\n    return JSON.stringify(transformedSnapshot);\n  }\n\n  private async mapNode(node: serializedNodeWithId, sourceMap: Map<string, string>) {\n    node = this.mapNodeAttributes(node, sourceMap);\n    node = this.mapTextElement(node, sourceMap);\n\n    if ('childNodes' in node) {\n      node.childNodes = await Promise.all(\n        node.childNodes.map(async (childNode) => {\n          return this.mapNode(childNode, sourceMap);\n        })\n      );\n    }\n\n    return node;\n  }\n\n  private mapNodeAttributes(node: serializedNodeWithId, sourceMap: Map<string, string>) {\n    if (node.type === NodeType.Element) {\n      if (node.attributes.src) {\n        const sourceVal = node.attributes.src as string;\n        if (sourceMap.has(sourceVal)) {\n          node.attributes.src = sourceMap.get(sourceVal);\n        }\n      }\n\n      if (node.attributes.style) {\n        const cssText = node.attributes.style as string;\n        const mappedCssText = this.mapCssUrls(cssText, sourceMap);\n        node.attributes.style = mappedCssText;\n      }\n\n      // This is how rrweb stores the contents of an external stylesheet\n      // that it will put into a `style` tag on render\n      if (node.attributes._cssText) {\n        const cssText = node.attributes._cssText as string;\n        const mappedCssText = this.mapCssUrls(cssText, sourceMap);\n        node.attributes._cssText = mappedCssText;\n      }\n\n      // When an image tag has the `srcset` attributes, the browser will choose one of the images\n      // from the `srcset` list to load at render time based on the viewport size. To support this,\n      // we parse the URLs in the `srcset` attribute and try to find a match in the asset map.\n      // If a match is found, we'll overwrite the `src` attribute with the mapped asset path,\n      // and we'll remove the `srcset` and `sizes` attributes because we'll only have captured\n      // the one asset that the browser decided to load when this was rendered. We don't want\n      // the browser to try to load one of the others when this snapshot is rendered in Chromatic\n      // because we won't have archived them.\n      if (node.tagName === 'img' && node.attributes.srcset) {\n        const srcsetValue = node.attributes.srcset as string;\n        const currentSrc = this.mapSrcsetUrls(srcsetValue, sourceMap);\n        if (currentSrc) {\n          node.attributes.src = currentSrc;\n\n          // Remove srcset attributes since we'll only have the one that\n          // loaded on render archived\n          delete node.attributes.srcset;\n          delete node.attributes.sizes;\n        }\n      }\n    }\n\n    return node;\n  }\n\n  private mapTextElement(node: serializedNodeWithId, sourceMap: Map<string, string>) {\n    if (node.type === NodeType.Text && node.isStyle) {\n      if (node.textContent) {\n        const mappedCssText = this.mapCssUrls(node.textContent, sourceMap);\n        node.textContent = mappedCssText;\n      }\n    }\n\n    return node;\n  }\n\n  private mapCssUrls(cssText: string, sourceMap: Map<string, string>) {\n    return cssText.replace(CSS_URL_REGEX, (match, fullUrl) => {\n      let cssUrl = match;\n      if (sourceMap.has(fullUrl)) {\n        cssUrl = match.replace(fullUrl, sourceMap.get(fullUrl));\n      }\n      return cssUrl;\n    });\n  }\n\n  private mapSrcsetUrls(srcsetValue: string, sourceMap: Map<string, string>) {\n    const parsedSrcset = srcset.parse(srcsetValue);\n    let currentSrc;\n    parsedSrcset.forEach((set) => {\n      if (sourceMap.has(set.url)) {\n        currentSrc = sourceMap.get(set.url);\n      }\n    });\n\n    return currentSrc;\n  }\n}\n/* eslint-enable no-underscore-dangle */\n/* eslint-enable no-param-reassign */\n","import type { Page, TestInfo } from '@playwright/test';\nimport { readFileSync } from 'fs';\nimport type { elementNode } from 'rrweb-snapshot';\n\nimport dedent from 'ts-dedent';\n\nimport { logger } from '../utils/logger';\n\nconst rrweb = readFileSync(require.resolve('rrweb-snapshot/dist/rrweb-snapshot.js'), 'utf8');\n\nexport const contentType = 'application/rrweb.snapshot+json';\n\nasync function takeArchive(page: Page, testInfo: TestInfo): Promise<void>;\nasync function takeArchive(page: Page, name: string, testInfo: TestInfo): Promise<void>;\nasync function takeArchive(\n  page: Page,\n  nameOrTestInfo: string | TestInfo,\n  maybeTestInfo?: TestInfo\n): Promise<void> {\n  let name: string;\n  let testInfo: TestInfo;\n  if (typeof nameOrTestInfo === 'string') {\n    if (!maybeTestInfo) throw new Error('Incorrect usage');\n    testInfo = maybeTestInfo;\n    name = nameOrTestInfo;\n  } else {\n    testInfo = nameOrTestInfo;\n    const number = testInfo.attachments.filter((a) => a.contentType === contentType).length + 1;\n    name = `Snapshot #${number}`;\n  }\n\n  page.on('console', (msg) => {\n    logger.log(`CONSOLE: \"${msg.text()}\"`);\n  });\n\n  // Serialize and capture the DOM\n  const domSnapshot: elementNode = await page.evaluate(dedent`\n    ${rrweb};\n    rrwebSnapshot.snapshot(document);\n  `);\n\n  testInfo.attach(name, { contentType, body: JSON.stringify(domSnapshot) });\n}\n\nexport { takeArchive };\n","import { Analytics, TrackParams } from '@segment/analytics-node';\n\nconst analytics = new Analytics({\n  writeKey: process.env.SEGMENT_WRITE_KEY || 'cwWhBWso2gwzyenW29CnCp30Kjz4DogE',\n});\n\n// We don't attempt to identify users\nconst userId = '@chromaui/test-archiver run';\n\nexport function track(event: string, properties: TrackParams['properties']) {\n  analytics.track({ userId, event, properties });\n}\n\nlet hasTrackedRun = false;\nexport function trackRun(properties: TrackParams['properties'] = {}) {\n  if (hasTrackedRun) return;\n  track('run', properties);\n  hasTrackedRun = true;\n}\n\nlet hasTrackedComplete = false;\nexport function trackComplete(properties: TrackParams['properties'] = {}) {\n  if (hasTrackedComplete) return;\n  track('complete', properties);\n  hasTrackedComplete = true;\n}\n","import type { Protocol } from 'playwright-core/types/protocol';\nimport { logger } from '../utils/logger';\n\nexport type UrlString = string;\n\nexport type ArchiveResponse =\n  | {\n      statusCode: number;\n      statusText?: string;\n      body: Buffer;\n      contentType?: string;\n    }\n  | {\n      error: Error;\n    };\n\nexport type ResourceArchive = Record<UrlString, ArchiveResponse>;\n\n// a custom interface that satisfies both playwright's CDPSession and chrome-remote-interface's CDP.Client types.\ninterface CDPClient {\n  on: (eventName: keyof Protocol.Events, handlerFunction: (params?: any) => void) => void;\n  send: (eventName: keyof Protocol.CommandParameters, payload?: any) => Promise<any>;\n}\n\nexport class Watcher {\n  public archive: ResourceArchive = {};\n\n  private client: CDPClient;\n\n  /** \n   Specifies which domains (origins) we should archive resources for (by default we only archive same-origin resources).\n   Useful in situations where the environment running the archived storybook (e.g. in CI) may be restricted to an intranet or other domain restrictions\n  */\n  private allowedArchiveOrigins: string[];\n\n  /**\n   * We assume the first URL loaded after @watch is called is the base URL of the\n   * page and we only save resources that are loaded from the same protocol/host/port combination.\n   * We also skip archiving this page because we only care about resources requested by this page.\n   */\n  private firstUrl: URL;\n\n  constructor(cdpClient: CDPClient, allowedDomains?: string[]) {\n    this.client = cdpClient;\n    // tack on the protocol so we can properly check if requests are cross-origin\n    this.allowedArchiveOrigins = (allowedDomains || []).map((domain) => `https://${domain}`);\n  }\n\n  async watch() {\n    this.client.on('Fetch.requestPaused', this.requestPaused.bind(this));\n    await this.client.send('Fetch.enable');\n  }\n\n  async clientSend<T extends keyof Protocol.CommandParameters>(\n    request: Protocol.Network.Request,\n    method: T,\n    params?: Protocol.CommandParameters[T]\n  ): Promise<Protocol.CommandReturnValues[T] | null> {\n    try {\n      return await this.client.send(method, params);\n    } catch (error) {\n      logger.log('Client error', request.url, error);\n      this.archive[request.url] = { error };\n      return null;\n    }\n  }\n\n  async requestPaused({\n    requestId,\n    request,\n    responseStatusCode,\n    responseStatusText,\n    responseErrorReason,\n    responseHeaders,\n  }: Protocol.Fetch.requestPausedPayload) {\n    // We only need to capture assets that will render when the DOM snapshot is rendered,\n    // so we only need to handle GET requests.\n    if (!request.method.match(/get/i)) {\n      await this.clientSend(request, 'Fetch.continueRequest', { requestId });\n      return;\n    }\n\n    // There's no reponse body for us to archive on 304s\n    if (responseStatusCode === 304) {\n      await this.clientSend(request, 'Fetch.continueRequest', { requestId });\n      return;\n    }\n\n    const requestUrl = new URL(request.url);\n\n    this.firstUrl ??= requestUrl;\n\n    const isRequestFromAllowedDomain =\n      requestUrl.origin === this.firstUrl.origin ||\n      this.allowedArchiveOrigins.includes(requestUrl.origin);\n\n    logger.log(\n      'requestPaused',\n      requestUrl.toString(),\n      responseStatusCode || responseErrorReason ? 'response' : 'request',\n      this.firstUrl.toString(),\n      isRequestFromAllowedDomain\n    );\n\n    // Pausing at response stage with an error, simply ignore\n    if (responseErrorReason) {\n      logger.log(`Got response error: ${responseErrorReason}`);\n      await this.clientSend(request, 'Fetch.continueRequest', { requestId });\n      return;\n    }\n\n    // Pausing a response stage with a response\n    if (responseStatusCode) {\n      if ([301, 302, 307, 308].includes(responseStatusCode)) {\n        await this.clientSend(request, 'Fetch.continueRequest', {\n          requestId,\n          interceptResponse: true,\n        });\n        return;\n      }\n\n      const result = await this.clientSend(request, 'Fetch.getResponseBody', {\n        requestId,\n      });\n      // Something has gone wrong and will be logged above\n      if (result === null) {\n        return;\n      }\n      const { body, base64Encoded } = result;\n\n      // If the Content-Type header is present, let's capture it.\n      const contentTypeHeader: Protocol.Fetch.HeaderEntry = responseHeaders.find(\n        ({ name }) => name.toLowerCase() === 'content-type'\n      );\n\n      // No need to capture the response of the top level page request\n      const isFirstRequest = requestUrl.toString() === this.firstUrl.toString();\n      if (isRequestFromAllowedDomain && !isFirstRequest) {\n        this.archive[request.url] = {\n          statusCode: responseStatusCode,\n          statusText: responseStatusText,\n          body: Buffer.from(body, base64Encoded ? 'base64' : 'utf8'),\n          contentType: contentTypeHeader?.value,\n        };\n      }\n\n      await this.clientSend(request, 'Fetch.continueRequest', { requestId });\n      return;\n    }\n\n    await this.clientSend(request, 'Fetch.continueRequest', {\n      requestId,\n      interceptResponse: true,\n    });\n  }\n}\n","import type { Page } from 'playwright';\nimport { Watcher, ResourceArchive } from '../resource-archive';\nimport { DEFAULT_GLOBAL_RESOURCE_ARCHIVE_TIMEOUT_MS } from '../constants';\nimport { logger } from '../utils/logger';\n\nconst idle = async (page: Page, networkTimeoutMs = DEFAULT_GLOBAL_RESOURCE_ARCHIVE_TIMEOUT_MS) => {\n  let globalNetworkTimerId: null | ReturnType<typeof setTimeout> = null;\n  let globalNetworkResolver: null | (() => void) = null;\n  // XXX_jwir3: The way this works is as follows:\n  // There are two promises created here. They wrap two separate timers, and we await on a race of both Promises.\n\n  // The first promise wraps a global timeout, where all requests MUST complete before that timeout has passed.\n  // If the timeout passes, an error is thrown. This promise can only throw errors, it cannot resolve successfully.\n  const globalNetworkTimeout = new Promise<void>((resolve) => {\n    globalNetworkResolver = resolve;\n\n    globalNetworkTimerId = setTimeout(() => {\n      logger.warn(`Global timeout of ${networkTimeoutMs}ms reached`);\n      globalNetworkResolver();\n    }, networkTimeoutMs);\n  });\n\n  // The second promise wraps a network idle timeout. This uses playwright's built-in functionality to detect when the network\n  // is idle.\n  const networkIdlePromise = page.waitForLoadState('networkidle').finally(() => {\n    clearTimeout(globalNetworkTimerId);\n  });\n\n  await Promise.race([globalNetworkTimeout, networkIdlePromise]);\n};\n\nexport const createResourceArchive = async ({\n  page,\n  networkTimeout,\n  allowedArchiveDomains,\n}: {\n  page: Page;\n  networkTimeout?: number;\n  allowedArchiveDomains?: string[];\n}): Promise<() => Promise<ResourceArchive>> => {\n  const cdpClient = await page.context().newCDPSession(page);\n\n  const watcher = new Watcher(cdpClient, allowedArchiveDomains);\n  await watcher.watch();\n\n  return async () => {\n    await idle(page, networkTimeout ?? DEFAULT_GLOBAL_RESOURCE_ARCHIVE_TIMEOUT_MS);\n\n    return watcher.archive;\n  };\n};\n","import type {\n  TestType,\n  PlaywrightTestArgs,\n  PlaywrightTestOptions,\n  PlaywrightWorkerArgs,\n  PlaywrightWorkerOptions,\n} from '@playwright/test';\nimport type { ChromaticConfig } from '../types';\nimport { writeTestResult } from '../write-archive';\nimport { contentType, takeArchive } from './takeArchive';\nimport { trackComplete, trackRun } from '../utils/analytics';\nimport { DEFAULT_GLOBAL_RESOURCE_ARCHIVE_TIMEOUT_MS } from '../constants';\nimport { createResourceArchive } from './createResourceArchive';\n\n// We do this slightly odd thing (makeTest) to avoid importing playwright multiple times when\n// linking this package. To avoid the main entry, you can:\n//\n//   import { makeTest } from '@chromaui/test-archiver/src/playwright-api/makeTest';\n//   import { takeSnapshot as snapshot } from '@chromaui/test-archiver/src/playwright-api/takeSnapshot';\nexport const makeTest = (\n  base: TestType<\n    PlaywrightTestArgs & PlaywrightTestOptions,\n    PlaywrightWorkerArgs & PlaywrightWorkerOptions\n  >\n) =>\n  base.extend<ChromaticConfig & { save: void }>({\n    // ChromaticConfig defaults\n    delay: [undefined, { option: true }],\n    diffIncludeAntiAliasing: [undefined, { option: true }],\n    diffThreshold: [undefined, { option: true }],\n    disableAutoCapture: [false, { option: true }],\n    forcedColors: [undefined, { option: true }],\n    pauseAnimationAtEnd: [undefined, { option: true }],\n    prefersReducedMotion: [undefined, { option: true }],\n    resourceArchiveTimeout: [DEFAULT_GLOBAL_RESOURCE_ARCHIVE_TIMEOUT_MS, { option: true }],\n    allowedArchiveDomains: [[], { option: true }],\n\n    save: [\n      async (\n        {\n          page,\n          delay,\n          diffIncludeAntiAliasing,\n          diffThreshold,\n          disableAutoCapture,\n          forcedColors,\n          pauseAnimationAtEnd,\n          prefersReducedMotion,\n          resourceArchiveTimeout,\n          allowedArchiveDomains,\n        },\n        use,\n        testInfo\n      ) => {\n        trackRun();\n\n        // CDP only works in Chromium, so we only capture archives in Chromium.\n        // We can later snapshot them in different browsers in the cloud.\n        // TODO: I'm not sure if this is the best way to detect the browser version, but\n        // it seems to work\n        if (page.context().browser().browserType().name() !== 'chromium') {\n          await use();\n          return;\n        }\n\n        const completeArchive = await createResourceArchive({\n          page,\n          networkTimeout: resourceArchiveTimeout,\n          allowedArchiveDomains,\n        });\n        await use();\n\n        if (!disableAutoCapture) {\n          await takeArchive(page, testInfo);\n        }\n\n        const resourceArchive = await completeArchive();\n\n        const snapshots = Object.fromEntries(\n          testInfo.attachments\n            .filter((a) => a.contentType === contentType && !!a.body)\n            .map(({ name, body }) => [name, body])\n        ) as Record<string, Buffer>;\n\n        const chromaticStorybookParams = {\n          ...(delay && { delay }),\n          ...(diffIncludeAntiAliasing && { diffIncludeAntiAliasing }),\n          ...(diffThreshold && { diffThreshold }),\n          ...(forcedColors && { forcedColors }),\n          ...(pauseAnimationAtEnd && { pauseAnimationAtEnd }),\n          ...(prefersReducedMotion && { prefersReducedMotion }),\n          viewports: [page.viewportSize().width],\n        };\n\n        await writeTestResult(\n          { ...testInfo, pageUrl: page.url() },\n          snapshots,\n          resourceArchive,\n          chromaticStorybookParams\n        );\n\n        trackComplete();\n      },\n      { auto: true },\n    ],\n  });\n"]}