vitest-plugin-vis
Version:
Vitest visual testing plugin
1 lines • 32.4 kB
Source Map (JSON)
{"version":3,"file":"config.mjs","names":["visOptions: Record<string, VisOptions<any> | undefined>","suites: VisSuites","visOptions","r: ImageSnapshotResult[]","r","visOptions","hasImageSnapshot: ExtendedBrowserCommand<Parameters<HasImageSnapshotCommand['hasImageSnapshot']>>","imageSnapshotNextIndex: ExtendedBrowserCommand<\n\tParameters<ImageSnapshotNextIndexCommand['imageSnapshotNextIndex']>\n>","loadImageSnapshotResults: ExtendedBrowserCommand<[taskId: string]>","mkdirp","writeFile","prepareImageSnapshotComparison: ExtendedBrowserCommand<\n\tParameters<PrepareImageSnapshotComparisonCommand['prepareImageSnapshotComparison']>\n>","preparePageImageSnapshotComparison: ExtendedBrowserCommand<\n\tParameters<PreparePageImageSnapshotComparisonCommand['preparePageImageSnapshotComparison']>\n>","setupVisSuite: ExtendedBrowserCommand<[]>"],"sources":["../src/server/externals/file.ts","../src/server/project.ts","../src/server/vis_options.ts","../src/server/vis_server_context.deps.ts","../src/server/suite.ts","../src/server/vis_server_context.logic.ts","../src/server/vis_server_context.ts","../src/server/commands/_assertions.ts","../src/server/commands/has_image_snapshot.ts","../src/server/commands/image_snapshot_next_index.ts","../src/server/commands/load_image_snapshot_results.ts","../src/server/browser_provider/playwright.ts","../src/server/browser_provider/webdriverio.ts","../src/server/browser_provider/browser_api.ts","../src/server/snapshot_writer.ts","../src/server/snapshot.ts","../src/server/commands/prepare_image_snapshot_comparison.ts","../src/server/commands/prepare_page_image_snapshot_comparison.ts","../src/server/commands/setup_vis_suite.ts","../src/server/commands/commands.ts","../src/config/vis.ts"],"sourcesContent":["import { glob, type GlobOptionsWithFileTypesUnset } from 'glob'\nimport { mkdirp } from 'mkdirp'\nimport { readFile, stat, writeFile } from 'node:fs/promises'\n\nexport const file = {\n\texistFile(filePath: string) {\n\t\treturn stat(filePath)\n\t\t\t.then((s) => s.isFile())\n\t\t\t.catch(() => false)\n\t},\n\tglob(pattern: string | string[], options?: GlobOptionsWithFileTypesUnset | undefined) {\n\t\treturn glob(pattern, options)\n\t},\n\tmkdirp: mkdirp as (path: string) => Promise<string | undefined>,\n\tasync tryReadFile(filePath: string | undefined): Promise<Buffer | undefined> {\n\t\tif (!filePath) return undefined\n\n\t\treturn readFile(filePath).catch(() => undefined)\n\t},\n\twriteFile,\n}\n","import type { ExtendedBrowserCommandContext } from './vis_server_context.types.ts'\n\nexport function getProjectName(context: ExtendedBrowserCommandContext) {\n\tconst { runner } = context.project\n\tconst name = runner.config.name\n\tif (typeof name === 'string') {\n\t\treturn name\n\t}\n\t// if (typeof name === 'object' && 'label' in name) {\n\t// \treturn (name as { label: string }).label\n\t// }\n\treturn undefined\n}\n\nexport function getProjectRoot(context: { project: { config: { root: string } } }) {\n\treturn context.project.config.root\n}\n","import type { TestUserConfig } from 'vitest/node'\nimport type { VisOptions } from '../config/types.ts'\nimport { assertSnapshotKeyWithoutDash } from '../shared/asserts.ts'\nimport { getProjectName } from './project.ts'\nimport type { ExtendedBrowserCommandContext } from './vis_server_context.types.ts'\n\nconst DEFAULT_PROJECT_NAME = '__default'\n\nconst visOptions: Record<string, VisOptions<any> | undefined> = {}\n\nexport function setVisOption(\n\tuserConfig: { test?: { name?: TestUserConfig['name'] | undefined } },\n\toptions: VisOptions<any> | undefined,\n) {\n\tassertSnapshotKeyWithoutDash(options?.snapshotKey)\n\n\tconst name = userConfig.test?.name\n\tconst id = typeof name === 'string' ? name : typeof name === 'object' ? name?.label : DEFAULT_PROJECT_NAME\n\tvisOptions[id] = options\n}\n\nexport function getVisOption(context: ExtendedBrowserCommandContext) {\n\tconst id = getProjectName(context) ?? DEFAULT_PROJECT_NAME\n\treturn visOptions[id] ?? {}\n}\n","/* v8 ignore start */\nimport ci from 'is-ci'\nimport { rimraf } from 'rimraf'\n\nexport const deps = {\n\trimraf,\n\tgetSnapshotPlatform() {\n\t\treturn ci ? process.platform : 'local'\n\t},\n}\n/* v8 ignore end */\n","import { join, relative } from 'pathe'\nimport type { VisOptions } from '../config/types.ts'\nimport { BASELINE_DIR, DIFF_DIR, RESULT_DIR } from '../shared/constants.ts'\nimport { getProjectName, getProjectRoot } from './project.ts'\nimport { getSnapshotSubpath, resolveSnapshotRootDir } from './snapshot_path.ts'\nimport { getVisOption } from './vis_options.ts'\nimport { deps } from './vis_server_context.deps.ts'\nimport type { ExtendedBrowserCommandContext, VisSuite, VisSuites } from './vis_server_context.types.ts'\n\nconst suites: VisSuites = {}\n\n/**\n * Setup suite is called on each test file's beforeAll hook.\n * Test files include vitest test files and storybook story files.\n * It needs to make sure there is no race condition between the test files.\n */\nexport async function setupSuite(browserContext: ExtendedBrowserCommandContext) {\n\tconst suiteId = getSuiteId(browserContext)\n\tconst visOptions = getVisOption(browserContext)\n\n\tif (!suites[suiteId]) {\n\t\tsuites[suiteId] = createSuite(browserContext, visOptions)\n\t}\n\n\tconst suite = await suites[suiteId]\n\n\tconst { taskSubpath, baselineDir, diffDir, resultDir, tasks } = createModule(\n\t\tsuite,\n\t\tbrowserContext.testPath!,\n\t\tvisOptions,\n\t)\n\tsuite.modules[taskSubpath] = { baselineDir, diffDir, resultDir, tasks }\n\n\tawait Promise.allSettled([deps.rimraf(diffDir), deps.rimraf(resultDir)])\n\treturn {\n\t\tcomparisonMethod: visOptions.comparisonMethod,\n\t\tdiffOptions: visOptions.diffOptions,\n\t\tfailureThreshold: visOptions.failureThreshold,\n\t\tfailureThresholdType: visOptions.failureThresholdType,\n\t\tsnapshotKey: visOptions.snapshotKey,\n\t\tsubject: visOptions.subject,\n\t\ttimeout: visOptions.timeout,\n\t}\n}\n\n/**\n * Suite ID also contains the project name to make it unique\n * across different projects.\n */\nexport function getSuiteId(context: ExtendedBrowserCommandContext) {\n\treturn `${getProjectName(context)}/${context.project.config.name}`\n}\n\nasync function createSuite(\n\tbrowserContext: ExtendedBrowserCommandContext,\n\tvisOptions: Pick<VisOptions, 'snapshotRootDir' | 'subject'>,\n) {\n\t// console.log('visOptions', visOptions)\n\tconst snapshotRootDir = resolveSnapshotRootDir(browserContext, visOptions)\n\tconst projectRoot = getProjectRoot(browserContext)\n\n\tconst state = {\n\t\tprojectRoot,\n\t\ttestTimeout: browserContext.project.config.testTimeout,\n\t\thookTimeout: browserContext.project.config.hookTimeout,\n\t\tsnapshotRootDir,\n\t\tsnapshotBaselineDir: join(snapshotRootDir, BASELINE_DIR),\n\t\tsnapshotResultDir: join(snapshotRootDir, RESULT_DIR),\n\t\tsnapshotDiffDir: join(snapshotRootDir, DIFF_DIR),\n\t\tsnapshotRootPath: join(projectRoot, snapshotRootDir),\n\t\tsubject: visOptions.subject,\n\t\tmodules: {},\n\t}\n\tawait Promise.allSettled([deps.rimraf(join(state.snapshotDiffDir)), deps.rimraf(join(state.snapshotResultDir))])\n\treturn state\n}\n\nexport function createModule(state: VisSuite, testPath: string, options: Pick<VisOptions, 'snapshotSubpath'>) {\n\tconst taskSubpath = getTaskSubpath(state, testPath, options)\n\treturn {\n\t\ttaskSubpath,\n\t\tbaselineDir: join(state.snapshotBaselineDir, taskSubpath),\n\t\tresultDir: join(state.snapshotResultDir, taskSubpath),\n\t\tdiffDir: join(state.snapshotDiffDir, taskSubpath),\n\t\ttasks: {},\n\t}\n}\n\nexport function getTaskSubpath(state: VisSuite, testPath: string, options: Pick<VisOptions, 'snapshotSubpath'>) {\n\treturn getSnapshotSubpath(relative(state.projectRoot, testPath), options)\n}\n\nexport function getSuite(context: ExtendedBrowserCommandContext) {\n\treturn suites[getSuiteId(context)]!\n}\n","import { basename, join, resolve } from 'pathe'\nimport { pick } from 'type-plus'\nimport type { ImageSnapshotKeyOptions } from '../client-api.ts'\nimport type { ImageSnapshotResult } from '../shared/commands.types.ts'\nimport { file } from './externals/file.ts'\nimport { getSuite, getTaskSubpath } from './suite.ts'\nimport { getVisOption } from './vis_options.ts'\nimport type { ExtendedBrowserCommandContext } from './vis_server_context.types.ts'\n\nexport function createVisServerContext() {\n\tconst context = {\n\t\tasync getSnapshotResults(browserContext: ExtendedBrowserCommandContext, taskId: string) {\n\t\t\tconst suiteInfo = await context.getSuiteInfo(browserContext, taskId)\n\t\t\tconst baselines = await file.glob(join(suiteInfo.projectRoot, suiteInfo.baselineDir, `${suiteInfo.taskId}-*.png`))\n\t\t\tconst results = await file.glob(join(suiteInfo.projectRoot, suiteInfo.resultDir, `${suiteInfo.taskId}-*.png`))\n\t\t\tconst diffs = await file.glob(join(suiteInfo.projectRoot, suiteInfo.diffDir, `${suiteInfo.taskId}-*.png`))\n\n\t\t\tconst r: ImageSnapshotResult[] = []\n\t\t\tawait Promise.all(\n\t\t\t\tbaselines.map(async (baselinePath) => {\n\t\t\t\t\tconst filename = basename(baselinePath)\n\t\t\t\t\tconst resultPath = results.find((r) => r.endsWith(filename))\n\t\t\t\t\tconst diffPath = diffs.find((d) => d.endsWith(filename))\n\n\t\t\t\t\tconst baselineBuffer = await file.tryReadFile(baselinePath)\n\t\t\t\t\tconst resultBuffer = await file.tryReadFile(resultPath)\n\t\t\t\t\tconst diffBuffer = await file.tryReadFile(diffPath)\n\t\t\t\t\tr.push({\n\t\t\t\t\t\tfilename,\n\t\t\t\t\t\tbaseline: baselineBuffer?.toString('base64'),\n\t\t\t\t\t\tresult: resultBuffer?.toString('base64'),\n\t\t\t\t\t\tdiff: diffBuffer?.toString('base64'),\n\t\t\t\t\t})\n\t\t\t\t}),\n\t\t\t)\n\t\t\treturn r\n\t\t},\n\t\tasync getSnapshotInfo(\n\t\t\tbrowserContext: ExtendedBrowserCommandContext,\n\t\t\ttaskId: string,\n\t\t\toptions?: ImageSnapshotKeyOptions,\n\t\t) {\n\t\t\tconst suiteInfo = await context.getSuiteInfo(browserContext, taskId)\n\t\t\tconst snapshotFilename = context.getSnapshotFilename(browserContext, suiteInfo, options?.snapshotKey)\n\n\t\t\tconst { baselineDir, resultDir, diffDir, task } = suiteInfo\n\n\t\t\ttask.count = task.count + 1\n\t\t\tconst baselinePath = join(baselineDir, snapshotFilename)\n\t\t\tconst resultPath = join(resultDir, snapshotFilename)\n\t\t\tconst diffPath = join(diffDir, snapshotFilename)\n\n\t\t\treturn {\n\t\t\t\t...pick(\n\t\t\t\t\tgetVisOption(browserContext),\n\t\t\t\t\t'comparisonMethod',\n\t\t\t\t\t'diffOptions',\n\t\t\t\t\t'failureThreshold',\n\t\t\t\t\t'failureThresholdType',\n\t\t\t\t\t'timeout',\n\t\t\t\t),\n\t\t\t\tbaselinePath,\n\t\t\t\tresultPath,\n\t\t\t\tdiffPath,\n\t\t\t}\n\t\t},\n\n\t\tasync getTaskCount(browserContext: ExtendedBrowserCommandContext, taskId: string) {\n\t\t\treturn (await context.getSuiteInfo(browserContext, taskId)).task.count\n\t\t},\n\t\tasync hasImageSnapshot(\n\t\t\tbrowserContext: ExtendedBrowserCommandContext,\n\t\t\ttaskId: string,\n\t\t\tsnapshotKey: string | undefined,\n\t\t) {\n\t\t\tconst info = await context.getSuiteInfo(browserContext, taskId)\n\n\t\t\treturn file.existFile(\n\t\t\t\tresolve(info.projectRoot, info.baselineDir, context.getSnapshotFilename(browserContext, info, snapshotKey)),\n\t\t\t)\n\t\t},\n\t\tgetSnapshotFilename(\n\t\t\tbrowserContext: ExtendedBrowserCommandContext,\n\t\t\tinfo: { taskId: string; task: { count: number } },\n\t\t\tsnapshotKey: string | undefined,\n\t\t) {\n\t\t\tif (snapshotKey) return `${info.taskId}-${snapshotKey}.png`\n\t\t\tconst visOptions = getVisOption(browserContext)\n\t\t\tif (typeof visOptions.snapshotKey === 'string') {\n\t\t\t\treturn `${info.taskId}-${visOptions.snapshotKey}.png`\n\t\t\t}\n\t\t\treturn `${info.taskId}-${info.task.count}.png`\n\t\t},\n\t\tasync getSuiteInfo(browserContext: ExtendedBrowserCommandContext, taskId: string) {\n\t\t\tconst projectState = await getSuite(browserContext)\n\t\t\tconst visOptions = getVisOption(browserContext)\n\t\t\tconst moduleId = getTaskSubpath(projectState, browserContext.testPath, visOptions)\n\t\t\tconst m = projectState.modules[moduleId]!\n\t\t\tconst task = (m.tasks[taskId] = m.tasks[taskId] ?? { count: 1 })\n\t\t\treturn {\n\t\t\t\tprojectRoot: projectState.projectRoot,\n\t\t\t\tsuiteId: moduleId,\n\t\t\t\ttaskId,\n\t\t\t\tbaselineDir: m.baselineDir,\n\t\t\t\tresultDir: m.resultDir,\n\t\t\t\tdiffDir: m.diffDir,\n\t\t\t\ttask,\n\t\t\t}\n\t\t},\n\t}\n\treturn context\n}\n","import { createVisServerContext } from './vis_server_context.logic.ts'\n\nexport const visServerContext = createVisServerContext()\n","import { isAbsolute } from 'pathe'\nimport type { RequiredPick } from 'type-plus'\nimport type { ExtendedBrowserCommandContext } from '../vis_server_context.types.ts'\n\nexport function assertTestPathDefined(\n\tcontext: ExtendedBrowserCommandContext,\n\tcommandName: string,\n): asserts context is RequiredPick<ExtendedBrowserCommandContext, 'testPath'> {\n\tif (!context.testPath) {\n\t\tthrow new Error(`'commands.${commandName}' requires testPath to be defined`)\n\t}\n}\n\nexport function assertIsRelativePath(relativeFilePath: string, propName: string) {\n\tif (isAbsolute(relativeFilePath)) {\n\t\tthrow new Error(`'${propName}' must be a relative path`)\n\t}\n}\n","import type { HasImageSnapshotCommand } from '../../shared/commands.types.ts'\nimport { visServerContext } from '../vis_server_context.ts'\nimport type { ExtendedBrowserCommand } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const hasImageSnapshot: ExtendedBrowserCommand<Parameters<HasImageSnapshotCommand['hasImageSnapshot']>> = async (\n\tcontext,\n\ttaskId,\n\tsnapshotKey,\n) => {\n\tassertTestPathDefined(context, 'hasImageSnapshot')\n\n\treturn visServerContext.hasImageSnapshot(context as any, taskId, snapshotKey)\n}\n","import type { ImageSnapshotNextIndexCommand } from '../../shared/commands.types.ts'\nimport { visServerContext } from '../vis_server_context.ts'\nimport type { ExtendedBrowserCommand } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const imageSnapshotNextIndex: ExtendedBrowserCommand<\n\tParameters<ImageSnapshotNextIndexCommand['imageSnapshotNextIndex']>\n> = async (context, taskId) => {\n\tassertTestPathDefined(context, 'imageSnapshotNextIndex')\n\n\treturn visServerContext.getTaskCount(context, taskId)\n}\n","import type { ImageSnapshotResult } from '../../shared/commands.types.ts'\nimport { visServerContext } from '../vis_server_context.ts'\nimport type { ExtendedBrowserCommand } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const loadImageSnapshotResults: ExtendedBrowserCommand<[taskId: string]> = async (\n\tcontext,\n\ttaskId,\n): Promise<ImageSnapshotResult[]> => {\n\tassertTestPathDefined(context, 'loadImageSnapshotResults')\n\treturn visServerContext.getSnapshotResults(context, taskId)\n}\n","import { resolve } from 'pathe'\nimport type { ExtendedBrowserCommandContext } from '../vis_server_context.types.ts'\nimport type { BrowserApi } from './types.ts'\n\nexport function playwright(context: ExtendedBrowserCommandContext): BrowserApi {\n\tconst { page, iframe } = context\n\n\treturn {\n\t\tasync takeScreenshot(projectRoot, relativeFilePath, selector, options) {\n\t\t\tconst subject = iframe.locator(selector)\n\t\t\treturn subject.screenshot({\n\t\t\t\tpath: resolve(projectRoot, relativeFilePath),\n\t\t\t\t...options,\n\t\t\t})\n\t\t},\n\t\tasync takePageScreenshot(projectRoot, relativeFilePath, options) {\n\t\t\treturn page.screenshot({\n\t\t\t\tpath: resolve(projectRoot, relativeFilePath),\n\t\t\t\t...options,\n\t\t\t})\n\t\t},\n\t}\n}\n","import type { ExtendedBrowserCommandContext } from '../vis_server_context.types.ts'\nimport type { BrowserApi } from './types.ts'\n\nexport function webdriverio(context: ExtendedBrowserCommandContext): BrowserApi {\n\tconst { browser } = context\n\treturn {\n\t\tasync takeScreenshot(_projectRoot, relativeFilePath, selector) {\n\t\t\tconst element = await browser.$(`${selector}`)\n\t\t\treturn element.saveScreenshot(relativeFilePath)\n\t\t},\n\t\tasync takePageScreenshot(_projectRoot, relativeFilePath, options) {\n\t\t\treturn browser.saveScreenshot(relativeFilePath, options)\n\t\t},\n\t}\n}\n","import type { ExtendedBrowserCommandContext } from '../vis_server_context.types.ts'\nimport { playwright } from './playwright.ts'\nimport { webdriverio } from './webdriverio.ts'\n\nexport function browserApi(context: ExtendedBrowserCommandContext) {\n\tif (context.provider.name === 'playwright') {\n\t\treturn playwright(context)\n\t}\n\tif (context.provider.name === 'webdriverio') {\n\t\treturn webdriverio(context)\n\t}\n\tthrow new Error(`Unsupported provider: ${context.provider.name}`)\n}\n","import { dirname } from 'pathe'\nimport { file } from './externals/file.ts'\n\nexport interface SnapshotWriter {\n\twriteBase64(filePath: string, data: string): Promise<void>\n\twriteBuffer(filePath: string, data: Buffer): Promise<void>\n}\n\nexport function createSnapshotWriter({ mkdirp, writeFile } = file): SnapshotWriter {\n\treturn {\n\t\tasync writeBase64(filePath: string, data: string) {\n\t\t\tawait mkdirp(dirname(filePath))\n\t\t\tawait writeFile(filePath, data, { encoding: 'base64' })\n\t\t},\n\t\tasync writeBuffer(filePath, data) {\n\t\t\tawait mkdirp(dirname(filePath))\n\t\t\tawait writeFile(filePath, data)\n\t\t},\n\t}\n}\n\nexport const snapshotWriter = createSnapshotWriter()\n","import { mkdirp } from 'mkdirp'\nimport { dirname, resolve } from 'pathe'\nimport { isBase64String } from '../shared/base64.ts'\nimport type { ImageSnapshotTimeoutOptions, PageImageSnapshotOptions } from '../shared/types.ts'\nimport { browserApi } from './browser_provider/browser_api.ts'\nimport { snapshotWriter } from './snapshot_writer.ts'\nimport type { ExtendedBrowserCommandContext } from './vis_server_context.types.ts'\n\nexport async function takeSnapshot(\n\tcontext: ExtendedBrowserCommandContext,\n\tprojectRoot: string,\n\trelativeFilePath: string,\n\tsubject: string,\n\toptions: ImageSnapshotTimeoutOptions | undefined,\n) {\n\tif (isBase64String(subject)) {\n\t\tawait snapshotWriter.writeBase64(resolve(projectRoot, relativeFilePath), subject)\n\t\treturn Buffer.from(subject, 'base64')\n\t}\n\treturn takeSnapshotByBrowser(context, projectRoot, relativeFilePath, subject, options)\n}\n\nexport async function takeSnapshotByBrowser(\n\tcontext: ExtendedBrowserCommandContext,\n\tprojectRoot: string,\n\trelativeFilePath: string,\n\tsubject: string,\n\toptions: ImageSnapshotTimeoutOptions | undefined,\n) {\n\tconst filePath = resolve(projectRoot, relativeFilePath)\n\tawait mkdirp(dirname(filePath))\n\tconst browser = browserApi(context)\n\treturn browser.takeScreenshot(projectRoot, relativeFilePath, subject ?? 'body', {\n\t\ttimeout: options?.timeout,\n\t})\n}\n\nexport async function takePageSnapshot(\n\tcontext: ExtendedBrowserCommandContext,\n\tprojectRoot: string,\n\trelativeFilePath: string,\n\toptions: (PageImageSnapshotOptions & ImageSnapshotTimeoutOptions) | undefined,\n) {\n\tconst filePath = resolve(projectRoot, relativeFilePath)\n\tawait mkdirp(dirname(filePath))\n\tconst browser = browserApi(context)\n\treturn browser.takePageScreenshot(projectRoot, relativeFilePath, {\n\t\ttimeout: options?.timeout,\n\t\tfullPage: options?.fullPage,\n\t})\n}\n","import { resolve } from 'pathe'\nimport { isBase64String } from '../../shared/base64.ts'\nimport type { PrepareImageSnapshotComparisonCommand } from '../../shared/commands.types.ts'\nimport type { ImageSnapshotComparisonInfo } from '../../shared/types.ts'\nimport { file } from '../externals/file.ts'\nimport { getProjectRoot } from '../project.ts'\nimport { takeSnapshot, takeSnapshotByBrowser } from '../snapshot.ts'\nimport { snapshotWriter } from '../snapshot_writer.ts'\nimport { visServerContext } from '../vis_server_context.ts'\nimport type { ExtendedBrowserCommand } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const prepareImageSnapshotComparison: ExtendedBrowserCommand<\n\tParameters<PrepareImageSnapshotComparisonCommand['prepareImageSnapshotComparison']>\n> = async (context, taskId, subject, options): Promise<ImageSnapshotComparisonInfo | undefined> => {\n\tassertTestPathDefined(context, 'prepareImageSnapshotComparison')\n\t// vitest:browser passes in `null` when not defined\n\tif (!options) options = {}\n\toptions.timeout = options.timeout ?? 30000\n\n\tconst projectRoot = getProjectRoot(context)\n\tconst info = await visServerContext.getSnapshotInfo(context, taskId, options)\n\n\tconst baselineBuffer = await file.tryReadFile(resolve(projectRoot, info.baselinePath))\n\tif (!baselineBuffer) {\n\t\tif (isBase64String(subject)) {\n\t\t\tawait snapshotWriter.writeBase64(resolve(projectRoot, info.baselinePath), subject)\n\t\t} else {\n\t\t\tawait takeSnapshotByBrowser(context, projectRoot, info.baselinePath, subject, options)\n\t\t}\n\t\treturn\n\t}\n\n\tconst resultBuffer = await takeSnapshot(context, projectRoot, info.resultPath, subject, options)\n\treturn {\n\t\t...info,\n\t\tprojectRoot,\n\t\tbaseline: baselineBuffer.toString('base64'),\n\t\tresult: resultBuffer.toString('base64'),\n\t}\n}\n","import { resolve } from 'pathe'\nimport type { PreparePageImageSnapshotComparisonCommand } from '../../shared/commands.types.ts'\nimport { file } from '../externals/file.ts'\nimport { getProjectRoot } from '../project.ts'\nimport { takePageSnapshot } from '../snapshot.ts'\nimport { visServerContext } from '../vis_server_context.ts'\nimport type { ExtendedBrowserCommand } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const preparePageImageSnapshotComparison: ExtendedBrowserCommand<\n\tParameters<PreparePageImageSnapshotComparisonCommand['preparePageImageSnapshotComparison']>\n> = async (context, taskId, options) => {\n\tassertTestPathDefined(context, 'preparePageImageSnapshotComparison')\n\t// vitest:browser passes in `null` when not defined\n\tif (!options) options = {}\n\toptions.timeout = options.timeout ?? 30000\n\n\tconst projectRoot = getProjectRoot(context)\n\tconst info = await visServerContext.getSnapshotInfo(context, taskId, options)\n\tconst baselineBuffer = await file.tryReadFile(resolve(projectRoot, info.baselinePath))\n\tif (!baselineBuffer) {\n\t\tawait takePageSnapshot(context, projectRoot, info.baselinePath, options)\n\t\treturn\n\t}\n\n\tconst resultBuffer = await takePageSnapshot(context, projectRoot, info.resultPath, options)\n\treturn {\n\t\t...info,\n\t\tprojectRoot,\n\t\tbaseline: baselineBuffer.toString('base64'),\n\t\tresult: resultBuffer.toString('base64'),\n\t}\n}\n","import { setupSuite } from '../suite.ts'\nimport type { ExtendedBrowserCommand, ExtendedBrowserCommandContext } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const setupVisSuite: ExtendedBrowserCommand<[]> = async (context): Promise<{ subject: string | undefined }> => {\n\tassertTestPathDefined(context, 'setupVisSuite')\n\treturn setupSuite(context as unknown as ExtendedBrowserCommandContext)\n}\n","import { hasImageSnapshot } from './has_image_snapshot.ts'\nimport { imageSnapshotNextIndex } from './image_snapshot_next_index.ts'\nimport { loadImageSnapshotResults } from './load_image_snapshot_results.ts'\nimport { prepareImageSnapshotComparison } from './prepare_image_snapshot_comparison.ts'\nimport { preparePageImageSnapshotComparison } from './prepare_page_image_snapshot_comparison.ts'\nimport { setupVisSuite } from './setup_vis_suite.ts'\n\nexport const commands = {\n\tsetupVisSuite,\n\timageSnapshotNextIndex,\n\thasImageSnapshot,\n\tpreparePageImageSnapshotComparison,\n\tprepareImageSnapshotComparison,\n\tloadImageSnapshotResults,\n}\n","import type { Plugin } from 'vitest/config'\nimport { commands } from '../server/commands/commands.ts'\nimport { setVisOption } from '../server/vis_options.ts'\nimport { NAME } from '../shared/constants.ts'\nimport type { ComparisonMethod } from '../shared/types.ts'\nimport type { VisOptions } from './types.ts'\n\n/**\n * Create a Vite plugin for visual testing.\n *\n * If options are not provided, the plugin will use the default options,\n * which enables the `auto` preset.\n */\nexport function vis<M extends ComparisonMethod = 'pixel'>(options: VisOptions<M> = { preset: 'auto' } as any) {\n\treturn {\n\t\tname: NAME,\n\t\tconfig(this: unknown, userConfig) {\n\t\t\tsetVisOption(userConfig, options)\n\t\t\tconst preset = options?.preset\n\t\t\treturn {\n\t\t\t\ttest: {\n\t\t\t\t\tbrowser: {\n\t\t\t\t\t\tname: undefined,\n\t\t\t\t\t\tcommands: commands as any,\n\t\t\t\t\t},\n\t\t\t\t\tsetupFiles: preset && preset !== 'none' && preset !== 'custom' ? [`vitest-plugin-vis/presets/${preset}`] : [],\n\t\t\t\t},\n\t\t\t}\n\t\t},\n\t} satisfies Plugin\n}\n"],"mappings":"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"}