@vizejs/vite-plugin-musea 0.405.0 → 0.406.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/a11y/index.d.mts +1 -1
- package/dist/cli/index.d.mts +3 -1
- package/dist/cli/index.d.mts.map +1 -1
- package/dist/cli/index.mjs +22 -3
- package/dist/cli/index.mjs.map +1 -1
- package/dist/{index-X16io2wM.d.mts → index-DzE3Pi5c.d.mts} +2 -2
- package/dist/{index-X16io2wM.d.mts.map → index-DzE3Pi5c.d.mts.map} +1 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +1 -1
- package/dist/{plugin-_rDO6Wej.d.mts → plugin-CIrKPW8K.d.mts} +2 -2
- package/dist/{plugin-_rDO6Wej.d.mts.map → plugin-CIrKPW8K.d.mts.map} +1 -1
- package/dist/{vrt-DC8PNsYN.d.mts → vrt-6lGTi1kM.d.mts} +4 -3
- package/dist/vrt-6lGTi1kM.d.mts.map +1 -0
- package/dist/{vrt-DJ_qLgan.d.mts → vrt-DVub2B6r.d.mts} +6 -1
- package/dist/vrt-DVub2B6r.d.mts.map +1 -0
- package/dist/{vrt-DYTmYF1X.mjs → vrt-Dr5Gs71k.mjs} +46 -19
- package/dist/vrt-Dr5Gs71k.mjs.map +1 -0
- package/dist/vrt.d.mts +2 -2
- package/dist/vrt.mjs +2 -2
- package/package.json +4 -4
- package/dist/vrt-DC8PNsYN.d.mts.map +0 -1
- package/dist/vrt-DJ_qLgan.d.mts.map +0 -1
- package/dist/vrt-DYTmYF1X.mjs.map +0 -1
package/dist/a11y/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as generateA11yHtmlReport, i as computeA11ySummary, n as MuseaA11yRunner, o as generateA11yJsonReport, r as resolveAxeSource, t as A11ySummary } from "../index-
|
|
1
|
+
import { a as generateA11yHtmlReport, i as computeA11ySummary, n as MuseaA11yRunner, o as generateA11yJsonReport, r as resolveAxeSource, t as A11ySummary } from "../index-DzE3Pi5c.mjs";
|
|
2
2
|
export { A11ySummary, MuseaA11yRunner, computeA11ySummary, generateA11yHtmlReport, generateA11yJsonReport, resolveAxeSource };
|
package/dist/cli/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as MuseaVrtOptions } from "../plugin-
|
|
1
|
+
import { i as MuseaVrtOptions } from "../plugin-CIrKPW8K.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/cli/index.d.ts
|
|
4
4
|
type Command = "run" | "approve" | "clean" | "generate";
|
|
@@ -9,6 +9,8 @@ interface CliOptions {
|
|
|
9
9
|
output: string;
|
|
10
10
|
threshold: number;
|
|
11
11
|
thresholdProvided: boolean;
|
|
12
|
+
workers: number;
|
|
13
|
+
workersProvided: boolean;
|
|
12
14
|
json: boolean;
|
|
13
15
|
ci: boolean;
|
|
14
16
|
a11y: boolean;
|
package/dist/cli/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/cli/index.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/cli/index.ts"],"mappings":";;;KAgCK,OAAA;AAAA,UAEY,UAAA;EACf,OAAA,EAAS,OAAA;EACT,MAAA;EACA,MAAA;EACA,MAAA;EACA,SAAA;EACA,iBAAA;EACA,OAAA;EACA,eAAA;EACA,IAAA;EACA,EAAA;EACA,IAAA;EACA,IAAA;EACA,OAAA;EACA,OAAA;EACA,aAAA;EACA,GAAA,GAAM,eAAe;AAAA;AAAA,iBAGP,SAAA,CAAU,IAAA,aAAiB,UAAU"}
|
package/dist/cli/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { n as readMuseaOptions, r as processMuseaArtFile } from "../options-CGA4BetG.mjs";
|
|
3
|
-
import { i as MuseaVrtRunner, n as generateVrtJsonReport, r as generateVrtReport } from "../vrt-
|
|
3
|
+
import { a as normalizeVrtWorkerCount, i as MuseaVrtRunner, n as generateVrtJsonReport, r as generateVrtReport } from "../vrt-Dr5Gs71k.mjs";
|
|
4
4
|
import fs from "node:fs";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -48,18 +48,21 @@ async function parseArtFile(filePath) {
|
|
|
48
48
|
* Contains the runVrt, runApprove, runClean, and runGenerate command implementations.
|
|
49
49
|
*/
|
|
50
50
|
function hasCiBlockingVrtResult(summary) {
|
|
51
|
-
return summary.failed > 0
|
|
51
|
+
return summary.failed > 0;
|
|
52
52
|
}
|
|
53
53
|
function isArtFileInput(filePath) {
|
|
54
54
|
return filePath.endsWith(".art.vue");
|
|
55
55
|
}
|
|
56
56
|
function createVrtOptions(options) {
|
|
57
57
|
const configured = options.vrt ?? {};
|
|
58
|
-
|
|
58
|
+
const vrtOptions = {
|
|
59
59
|
...configured,
|
|
60
60
|
snapshotDir: path.join(options.output, "snapshots"),
|
|
61
61
|
threshold: options.thresholdProvided ? options.threshold : configured.threshold ?? options.threshold
|
|
62
62
|
};
|
|
63
|
+
const workers = options.workersProvided ? options.workers : configured.workers;
|
|
64
|
+
if (workers !== void 0) vrtOptions.workers = normalizeVrtWorkerCount(workers);
|
|
65
|
+
return vrtOptions;
|
|
63
66
|
}
|
|
64
67
|
async function runVrt(options, artFiles) {
|
|
65
68
|
const totalVariants = artFiles.reduce((sum, art) => sum + art.variants.filter((v) => !v.skipVrt).length, 0);
|
|
@@ -263,6 +266,7 @@ async function fileExists(filePath) {
|
|
|
263
266
|
* -c, --config Path to vite config (default: vite.config.ts)
|
|
264
267
|
* -o, --output Output directory for reports (default: .vize)
|
|
265
268
|
* -t, --threshold Diff threshold percentage (default: 0.1)
|
|
269
|
+
* -w, --workers Number of concurrent captures (default: 1)
|
|
266
270
|
* --json Output JSON report instead of HTML
|
|
267
271
|
* --ci CI mode - exit with non-zero code on failures
|
|
268
272
|
* --a11y Run accessibility audits alongside VRT
|
|
@@ -276,6 +280,8 @@ function parseArgs(args) {
|
|
|
276
280
|
output: ".vize",
|
|
277
281
|
threshold: .1,
|
|
278
282
|
thresholdProvided: false,
|
|
283
|
+
workers: 1,
|
|
284
|
+
workersProvided: false,
|
|
279
285
|
json: false,
|
|
280
286
|
ci: false,
|
|
281
287
|
a11y: false,
|
|
@@ -322,6 +328,11 @@ function parseArgs(args) {
|
|
|
322
328
|
options.threshold = parseThreshold(args[++i]);
|
|
323
329
|
options.thresholdProvided = true;
|
|
324
330
|
break;
|
|
331
|
+
case "-w":
|
|
332
|
+
case "--workers":
|
|
333
|
+
options.workers = parseWorkers(args[++i]);
|
|
334
|
+
options.workersProvided = true;
|
|
335
|
+
break;
|
|
325
336
|
case "--json":
|
|
326
337
|
options.json = true;
|
|
327
338
|
break;
|
|
@@ -346,6 +357,10 @@ function parseThreshold(value) {
|
|
|
346
357
|
const threshold = Number.parseFloat(value ?? "");
|
|
347
358
|
return Number.isFinite(threshold) ? threshold : .1;
|
|
348
359
|
}
|
|
360
|
+
function parseWorkers(value) {
|
|
361
|
+
const workers = Number.parseInt(value ?? "", 10);
|
|
362
|
+
return Number.isFinite(workers) ? Math.max(1, workers) : 1;
|
|
363
|
+
}
|
|
349
364
|
function printHelp() {
|
|
350
365
|
console.log(`
|
|
351
366
|
Musea VRT - Visual Regression Testing for Component Gallery
|
|
@@ -366,6 +381,7 @@ Options:
|
|
|
366
381
|
-o, --output <dir> Output directory for reports (default: .vize)
|
|
367
382
|
-t, --threshold <n> Diff threshold percentage (default: 0.1)
|
|
368
383
|
-b, --base-url <url> Base URL for dev server (default: http://localhost:5173)
|
|
384
|
+
-w, --workers <n> Number of concurrent captures (default: 1)
|
|
369
385
|
--json Output JSON report instead of HTML
|
|
370
386
|
--ci CI mode - exit with non-zero code on failures
|
|
371
387
|
--a11y Run accessibility audits alongside VRT
|
|
@@ -387,6 +403,9 @@ Examples:
|
|
|
387
403
|
# Run with accessibility audits
|
|
388
404
|
musea-vrt --a11y
|
|
389
405
|
|
|
406
|
+
# Capture variants concurrently
|
|
407
|
+
musea-vrt --workers 8
|
|
408
|
+
|
|
390
409
|
# Approve all failed snapshots
|
|
391
410
|
musea-vrt approve
|
|
392
411
|
|
package/dist/cli/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/utils.ts","../../src/cli/commands.ts","../../src/cli/config.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * CLI utility functions for art file scanning and parsing.\n *\n * Extracted from cli.ts to keep file sizes manageable.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { processMuseaArtFile } from \"../plugin/art-processing.js\";\nimport type { ArtFileInfo } from \"../types/index.js\";\n\n/** Recursively scan a directory for .art.vue files. */\nexport async function scanArtFiles(root: string): Promise<string[]> {\n const files: string[] = [];\n\n async function scan(dir: string): Promise<void> {\n let entries: fs.Dirent[];\n try {\n entries = await fs.promises.readdir(dir, { withFileTypes: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EACCES\") {\n return;\n }\n throw error;\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n // Skip node_modules and dist\n if (entry.name === \"node_modules\" || entry.name === \"dist\") {\n continue;\n }\n\n if (entry.isDirectory()) {\n await scan(fullPath);\n } else if (entry.isFile() && entry.name.endsWith(\".art.vue\")) {\n files.push(fullPath);\n }\n }\n }\n\n await scan(root);\n return files;\n}\n\n/** Parse a single .art.vue file into an ArtFileInfo structure. */\nexport async function parseArtFile(filePath: string): Promise<ArtFileInfo | null> {\n return processMuseaArtFile(filePath, {\n root: path.dirname(filePath),\n command: \"serve\",\n });\n}\n","/**\n * CLI command handlers for the Musea VRT tool.\n *\n * Extracted from cli.ts to keep file sizes manageable.\n * Contains the runVrt, runApprove, runClean, and runGenerate command implementations.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { MuseaVrtRunner, generateVrtReport, generateVrtJsonReport } from \"../vrt.js\";\nimport type { ExtendedVrtOptions, VrtSummary } from \"../vrt.js\";\nimport type { ArtFileInfo } from \"../types/index.js\";\n\nimport type { CliOptions } from \"./index.js\";\n\nexport function hasCiBlockingVrtResult(summary: VrtSummary): boolean {\n return summary.failed > 0 || summary.skipped > 0;\n}\n\nexport function isArtFileInput(filePath: string): boolean {\n return filePath.endsWith(\".art.vue\");\n}\n\nexport function createVrtOptions(options: CliOptions): ExtendedVrtOptions {\n const configured = options.vrt ?? {};\n return {\n ...configured,\n snapshotDir: path.join(options.output, \"snapshots\"),\n threshold: options.thresholdProvided\n ? options.threshold\n : (configured.threshold ?? options.threshold),\n };\n}\n\nexport async function runVrt(options: CliOptions, artFiles: ArtFileInfo[]): Promise<void> {\n const totalVariants = artFiles.reduce(\n (sum, art) => sum + art.variants.filter((v) => !v.skipVrt).length,\n 0,\n );\n\n console.log(` Testing ${totalVariants} variant(s) across ${artFiles.length} art file(s)\\n`);\n\n const runner = new MuseaVrtRunner({\n ...createVrtOptions(options),\n ci: options.ci ? { failOnDiff: true, jsonReport: options.json } : undefined,\n });\n\n try {\n console.log(\" Launching browser...\");\n await runner.init();\n\n console.log(\" Running visual regression tests...\\n\");\n\n // Run tests\n const results = await runner.runAllTests(artFiles, options.baseUrl);\n const summary = runner.getSummary(results);\n\n // Print results\n console.log(\" Results:\");\n console.log(\" ---------\");\n console.log(` Passed: ${summary.passed}`);\n console.log(` Failed: ${summary.failed}`);\n console.log(` New: ${summary.new}`);\n console.log(` Skipped: ${summary.skipped}`);\n console.log(` Total: ${summary.total}`);\n console.log(` Duration: ${(summary.duration / 1000).toFixed(2)}s\\n`);\n\n // Run a11y audits if requested\n if (options.a11y) {\n console.log(\" Running accessibility audits...\\n\");\n try {\n const { MuseaA11yRunner } = await import(\"../a11y/index.js\");\n const a11yRunner = new MuseaA11yRunner();\n const a11yResults = await a11yRunner.runAudits(artFiles, options.baseUrl, runner);\n const a11ySummary = a11yRunner.getSummary(a11yResults);\n\n console.log(\" A11y Results:\");\n console.log(\" -------------\");\n console.log(` Components: ${a11ySummary.totalComponents}`);\n console.log(` Variants: ${a11ySummary.totalVariants}`);\n console.log(` Violations: ${a11ySummary.totalViolations}`);\n console.log(` Critical: ${a11ySummary.criticalCount}`);\n console.log(` Serious: ${a11ySummary.seriousCount}`);\n console.log(` Not audited: ${a11ySummary.erroredVariants}\\n`);\n\n // Generate a11y report\n const reportDir = options.output;\n await fs.promises.mkdir(reportDir, { recursive: true });\n\n if (options.json) {\n const a11yJson = a11yRunner.generateJsonReport(a11yResults);\n const a11yPath = path.join(reportDir, \"a11y-report.json\");\n await fs.promises.writeFile(a11yPath, a11yJson);\n console.log(` A11y JSON report: ${a11yPath}\\n`);\n } else {\n const a11yHtml = a11yRunner.generateHtmlReport(a11yResults);\n const a11yPath = path.join(reportDir, \"a11y-report.html\");\n await fs.promises.writeFile(a11yPath, a11yHtml);\n console.log(` A11y HTML report: ${a11yPath}\\n`);\n }\n\n // CI mode - exit with error on critical/serious violations. A variant\n // whose audit never ran fails too, but under its own message: it is\n // not an accessibility finding, and reporting it as one sends people\n // looking for a component defect that does not exist.\n if (options.ci && a11ySummary.erroredVariants > 0) {\n console.log(\n ` CI mode: ${a11ySummary.erroredVariants} variant(s) could not be audited\\n`,\n );\n process.exit(1);\n }\n if (options.ci && (a11ySummary.criticalCount > 0 || a11ySummary.seriousCount > 0)) {\n console.log(\" CI mode: Accessibility violations found\\n\");\n process.exit(1);\n }\n } catch (e) {\n console.warn(\" A11y audits skipped:\", e instanceof Error ? e.message : String(e));\n console.warn(\" Make sure axe-core is installed: npm install axe-core\\n\");\n }\n }\n\n // Update baselines if requested\n if (options.update) {\n console.log(\" Updating baselines...\");\n const updated = await runner.updateBaselines(results);\n console.log(` Updated ${updated} baseline(s)\\n`);\n }\n\n // Generate VRT report\n const reportDir = options.output;\n await fs.promises.mkdir(reportDir, { recursive: true });\n\n if (options.json) {\n const jsonReport = generateVrtJsonReport(results, summary);\n const jsonPath = path.join(reportDir, \"vrt-report.json\");\n await fs.promises.writeFile(jsonPath, jsonReport);\n console.log(` JSON report: ${jsonPath}\\n`);\n } else {\n const htmlReport = generateVrtReport(results, summary);\n const htmlPath = path.join(reportDir, \"vrt-report.html\");\n await fs.promises.writeFile(htmlPath, htmlReport);\n console.log(` HTML report: ${htmlPath}\\n`);\n }\n\n // CI mode - exit with error if visual diffs or capture/runtime errors occurred.\n if (options.ci && hasCiBlockingVrtResult(summary)) {\n console.log(\" CI mode: Exiting with error due to failures\\n\");\n process.exit(1);\n }\n } finally {\n await runner.close();\n }\n}\n\nexport async function runApprove(options: CliOptions, artFiles: ArtFileInfo[]): Promise<void> {\n const runner = new MuseaVrtRunner(createVrtOptions(options));\n\n try {\n console.log(\" Launching browser...\");\n await runner.init();\n\n console.log(\" Running tests to find diffs...\\n\");\n\n const results = await runner.runAllTests(artFiles, options.baseUrl);\n const failed = results.filter((r) => !r.passed && !r.error);\n\n if (failed.length === 0) {\n console.log(\" No failed tests to approve.\\n\");\n return;\n }\n\n const pattern = options.pattern;\n if (pattern) {\n console.log(` Approving snapshots matching: ${pattern}\\n`);\n } else {\n console.log(` Approving all ${failed.length} failed snapshot(s)...\\n`);\n }\n\n const approved = await runner.approveResults(results, pattern);\n console.log(` Approved ${approved} snapshot(s)\\n`);\n } finally {\n await runner.close();\n }\n}\n\nexport async function runClean(options: CliOptions, artFiles: ArtFileInfo[]): Promise<void> {\n const runner = new MuseaVrtRunner(createVrtOptions(options));\n\n console.log(\" Scanning for orphaned snapshots...\\n\");\n\n const cleaned = await runner.cleanOrphans(artFiles);\n\n if (cleaned === 0) {\n console.log(\" No orphaned snapshots found.\\n\");\n } else {\n console.log(`\\n Cleaned ${cleaned} orphaned snapshot(s)\\n`);\n }\n}\n\nexport async function runGenerate(options: CliOptions): Promise<void> {\n if (!options.componentPath) {\n console.error(\" Error: Missing component path.\");\n console.error(\" Usage: musea-vrt generate <component.vue>\\n\");\n process.exit(1);\n }\n\n const componentPath = path.resolve(options.componentPath);\n if (isArtFileInput(componentPath)) {\n console.error(\n \" Error: musea-vrt generate expects a source .vue component, not a .art.vue file.\",\n );\n console.error(\" Pass the component file that the art file should wrap.\\n\");\n process.exit(1);\n }\n\n // Check file exists\n try {\n await fs.promises.access(componentPath);\n } catch {\n console.error(` Error: File not found: ${componentPath}\\n`);\n process.exit(1);\n }\n\n console.log(` Generating art file for: ${path.relative(process.cwd(), componentPath)}\\n`);\n\n try {\n const { writeArtFile } = await import(\"../autogen/index.js\");\n const outputPath = await writeArtFile(componentPath);\n const relOutput = path.relative(process.cwd(), outputPath);\n\n console.log(` Generated: ${relOutput}\\n`);\n } catch (e) {\n console.error(\" Generation failed:\", e instanceof Error ? e.message : String(e));\n process.exit(1);\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { loadConfigFromFile, type PluginOption } from \"vite\";\n\nimport type { MuseaVrtOptions } from \"../types/index.js\";\nimport { readMuseaOptions } from \"../plugin/options.js\";\n\nexport async function loadMuseaVrtOptions(\n configPath: string,\n cwd = process.cwd(),\n): Promise<MuseaVrtOptions | undefined> {\n const resolvedConfigPath = path.isAbsolute(configPath)\n ? configPath\n : path.resolve(cwd, configPath);\n if (!(await fileExists(resolvedConfigPath))) return undefined;\n\n const loaded = await loadConfigFromFile(\n {\n command: \"serve\",\n mode: \"development\",\n isPreview: false,\n isSsrBuild: false,\n },\n resolvedConfigPath,\n );\n if (!loaded) return undefined;\n\n const plugins: unknown[] = [];\n await collectPlugins(loaded.config.plugins, plugins);\n\n for (const plugin of plugins) {\n const options = readMuseaOptions(plugin);\n if (options?.vrt) return options.vrt;\n }\n return undefined;\n}\n\nasync function collectPlugins(\n input: PluginOption | Promise<PluginOption> | undefined,\n plugins: unknown[],\n) {\n const resolved = await input;\n if (!resolved) return;\n\n if (Array.isArray(resolved)) {\n for (const item of resolved) {\n await collectPlugins(item, plugins);\n }\n return;\n }\n\n plugins.push(resolved);\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.promises.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n","#!/usr/bin/env node\n/**\n * Musea CLI\n *\n * Usage:\n * musea-vrt [command] [options]\n *\n * Commands:\n * (default) Run VRT tests\n * approve [pat] Approve failed snapshots (optionally filtered by pattern)\n * clean Remove orphaned snapshots\n *\n * Options:\n * -u, --update Update baseline snapshots\n * -c, --config Path to vite config (default: vite.config.ts)\n * -o, --output Output directory for reports (default: .vize)\n * -t, --threshold Diff threshold percentage (default: 0.1)\n * --json Output JSON report instead of HTML\n * --ci CI mode - exit with non-zero code on failures\n * --a11y Run accessibility audits alongside VRT\n * -h, --help Show help\n */\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ArtFileInfo } from \"../types/index.js\";\nimport { scanArtFiles, parseArtFile } from \"./utils.js\";\nimport { runVrt, runApprove, runClean, runGenerate } from \"./commands.js\";\nimport { loadMuseaVrtOptions } from \"./config.js\";\nimport type { MuseaVrtOptions } from \"../types/index.js\";\n\ntype Command = \"run\" | \"approve\" | \"clean\" | \"generate\";\n\nexport interface CliOptions {\n command: Command;\n update: boolean;\n config: string;\n output: string;\n threshold: number;\n thresholdProvided: boolean;\n json: boolean;\n ci: boolean;\n a11y: boolean;\n help: boolean;\n baseUrl: string;\n pattern?: string;\n componentPath?: string;\n vrt?: MuseaVrtOptions;\n}\n\nexport function parseArgs(args: string[]): CliOptions {\n const options: CliOptions = {\n command: \"run\",\n update: false,\n config: \"vite.config.ts\",\n output: \".vize\",\n threshold: 0.1,\n thresholdProvided: false,\n json: false,\n ci: false,\n a11y: false,\n help: false,\n baseUrl: \"http://localhost:5173\",\n };\n\n let i = 0;\n\n // Check for subcommand as first arg\n if (args.length > 0 && !args[0].startsWith(\"-\")) {\n const sub = args[0];\n if (sub === \"approve\") {\n options.command = \"approve\";\n i = 1;\n // Optional pattern argument after approve\n if (args.length > 1 && !args[1].startsWith(\"-\")) {\n options.pattern = args[1];\n i = 2;\n }\n } else if (sub === \"clean\") {\n options.command = \"clean\";\n i = 1;\n } else if (sub === \"generate\") {\n options.command = \"generate\";\n i = 1;\n // Required component path argument\n if (args.length > 1 && !args[1].startsWith(\"-\")) {\n options.componentPath = args[1];\n i = 2;\n }\n }\n }\n\n for (; i < args.length; i++) {\n const arg = args[i];\n switch (arg) {\n case \"-u\":\n case \"--update\":\n options.update = true;\n break;\n case \"-c\":\n case \"--config\":\n options.config = args[++i] || \"vite.config.ts\";\n break;\n case \"-o\":\n case \"--output\":\n options.output = args[++i] || \".vize\";\n break;\n case \"-t\":\n case \"--threshold\":\n options.threshold = parseThreshold(args[++i]);\n options.thresholdProvided = true;\n break;\n case \"--json\":\n options.json = true;\n break;\n case \"--ci\":\n options.ci = true;\n break;\n case \"--a11y\":\n options.a11y = true;\n break;\n case \"-b\":\n case \"--base-url\":\n options.baseUrl = args[++i] || \"http://localhost:5173\";\n break;\n case \"-h\":\n case \"--help\":\n options.help = true;\n break;\n }\n }\n\n return options;\n}\n\nfunction parseThreshold(value: string | undefined): number {\n const threshold = Number.parseFloat(value ?? \"\");\n return Number.isFinite(threshold) ? threshold : 0.1;\n}\n\nfunction printHelp(): void {\n console.log(`\nMusea VRT - Visual Regression Testing for Component Gallery\n\nUsage:\n musea-vrt [command] [options]\n\nCommands:\n (default) Run VRT tests\n approve [pattern] Approve failed snapshots and update baselines\n Optional pattern filters which snapshots to approve\n clean Remove orphaned snapshots (no matching art/variant)\n generate <component> Auto-generate .art.vue from a Vue component\n\nOptions:\n -u, --update Update baseline snapshots with current screenshots\n -c, --config <path> Path to vite config file (default: vite.config.ts)\n -o, --output <dir> Output directory for reports (default: .vize)\n -t, --threshold <n> Diff threshold percentage (default: 0.1)\n -b, --base-url <url> Base URL for dev server (default: http://localhost:5173)\n --json Output JSON report instead of HTML\n --ci CI mode - exit with non-zero code on failures\n --a11y Run accessibility audits alongside VRT\n -h, --help Show this help message\n\nExamples:\n # Run VRT tests\n musea-vrt\n\n # Update baseline snapshots\n musea-vrt -u\n\n # Run with custom threshold\n musea-vrt -t 0.5\n\n # CI mode with JSON output\n musea-vrt --ci --json\n\n # Run with accessibility audits\n musea-vrt --a11y\n\n # Approve all failed snapshots\n musea-vrt approve\n\n # Approve specific snapshots by pattern\n musea-vrt approve \"Button/*\"\n\n # Clean orphaned snapshots\n musea-vrt clean\n\n # Auto-generate .art.vue from component\n musea-vrt generate src/components/Button.vue\n\n # Custom base URL\n musea-vrt -b http://localhost:3000\n`);\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const options = parseArgs(args);\n\n if (options.help) {\n printHelp();\n process.exit(0);\n }\n\n const cwd = process.cwd();\n\n console.log(\"\\n Musea VRT\");\n console.log(\" =========\\n\");\n\n // Handle generate command early (doesn't need art file scanning)\n if (options.command === \"generate\") {\n try {\n await runGenerate(options);\n } catch (error) {\n console.error(\"\\n Error:\", error);\n process.exit(1);\n }\n return;\n }\n\n options.vrt = await loadMuseaVrtOptions(options.config, cwd);\n\n // Scan for art files\n console.log(\" Scanning for art files...\");\n const artFilePaths = await scanArtFiles(cwd);\n\n if (artFilePaths.length === 0) {\n console.log(\" No art files found.\\n\");\n process.exit(0);\n }\n\n console.log(` Found ${artFilePaths.length} art file(s)\\n`);\n\n // Parse art files\n const artFiles: ArtFileInfo[] = [];\n for (const filePath of artFilePaths) {\n const art = await parseArtFile(filePath);\n if (art) {\n artFiles.push(art);\n }\n }\n\n try {\n switch (options.command) {\n case \"run\":\n await runVrt(options, artFiles);\n break;\n case \"approve\":\n await runApprove(options, artFiles);\n break;\n case \"clean\":\n await runClean(options, artFiles);\n break;\n case \"generate\":\n // Handled above before art file scanning\n break;\n }\n } catch (error) {\n console.error(\"\\n Error:\", error);\n process.exit(1);\n }\n}\n\nif (isDirectRun()) {\n main().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n });\n}\n\nfunction isDirectRun(): boolean {\n const entrypoint = process.argv[1];\n return entrypoint ? fileURLToPath(import.meta.url) === path.resolve(entrypoint) : false;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/utils.ts","../../src/cli/commands.ts","../../src/cli/config.ts","../../src/cli/index.ts"],"sourcesContent":["/**\n * CLI utility functions for art file scanning and parsing.\n *\n * Extracted from cli.ts to keep file sizes manageable.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { processMuseaArtFile } from \"../plugin/art-processing.js\";\nimport type { ArtFileInfo } from \"../types/index.js\";\n\n/** Recursively scan a directory for .art.vue files. */\nexport async function scanArtFiles(root: string): Promise<string[]> {\n const files: string[] = [];\n\n async function scan(dir: string): Promise<void> {\n let entries: fs.Dirent[];\n try {\n entries = await fs.promises.readdir(dir, { withFileTypes: true });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"EACCES\") {\n return;\n }\n throw error;\n }\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n\n // Skip node_modules and dist\n if (entry.name === \"node_modules\" || entry.name === \"dist\") {\n continue;\n }\n\n if (entry.isDirectory()) {\n await scan(fullPath);\n } else if (entry.isFile() && entry.name.endsWith(\".art.vue\")) {\n files.push(fullPath);\n }\n }\n }\n\n await scan(root);\n return files;\n}\n\n/** Parse a single .art.vue file into an ArtFileInfo structure. */\nexport async function parseArtFile(filePath: string): Promise<ArtFileInfo | null> {\n return processMuseaArtFile(filePath, {\n root: path.dirname(filePath),\n command: \"serve\",\n });\n}\n","/**\n * CLI command handlers for the Musea VRT tool.\n *\n * Extracted from cli.ts to keep file sizes manageable.\n * Contains the runVrt, runApprove, runClean, and runGenerate command implementations.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { MuseaVrtRunner, generateVrtReport, generateVrtJsonReport } from \"../vrt.js\";\nimport type { ExtendedVrtOptions, VrtSummary } from \"../vrt.js\";\nimport { normalizeVrtWorkerCount } from \"../vrt.js\";\nimport type { ArtFileInfo } from \"../types/index.js\";\n\nimport type { CliOptions } from \"./index.js\";\n\nexport function hasCiBlockingVrtResult(summary: VrtSummary): boolean {\n return summary.failed > 0;\n}\n\nexport function isArtFileInput(filePath: string): boolean {\n return filePath.endsWith(\".art.vue\");\n}\n\nexport function createVrtOptions(options: CliOptions): ExtendedVrtOptions {\n const configured = options.vrt ?? {};\n const vrtOptions: ExtendedVrtOptions = {\n ...configured,\n snapshotDir: path.join(options.output, \"snapshots\"),\n threshold: options.thresholdProvided\n ? options.threshold\n : (configured.threshold ?? options.threshold),\n };\n const workers = options.workersProvided ? options.workers : configured.workers;\n if (workers !== undefined) {\n vrtOptions.workers = normalizeVrtWorkerCount(workers);\n }\n return vrtOptions;\n}\n\nexport async function runVrt(options: CliOptions, artFiles: ArtFileInfo[]): Promise<void> {\n const totalVariants = artFiles.reduce(\n (sum, art) => sum + art.variants.filter((v) => !v.skipVrt).length,\n 0,\n );\n\n console.log(` Testing ${totalVariants} variant(s) across ${artFiles.length} art file(s)\\n`);\n\n const runner = new MuseaVrtRunner({\n ...createVrtOptions(options),\n ci: options.ci ? { failOnDiff: true, jsonReport: options.json } : undefined,\n });\n\n try {\n console.log(\" Launching browser...\");\n await runner.init();\n\n console.log(\" Running visual regression tests...\\n\");\n\n // Run tests\n const results = await runner.runAllTests(artFiles, options.baseUrl);\n const summary = runner.getSummary(results);\n\n // Print results\n console.log(\" Results:\");\n console.log(\" ---------\");\n console.log(` Passed: ${summary.passed}`);\n console.log(` Failed: ${summary.failed}`);\n console.log(` New: ${summary.new}`);\n console.log(` Skipped: ${summary.skipped}`);\n console.log(` Total: ${summary.total}`);\n console.log(` Duration: ${(summary.duration / 1000).toFixed(2)}s\\n`);\n\n // Run a11y audits if requested\n if (options.a11y) {\n console.log(\" Running accessibility audits...\\n\");\n try {\n const { MuseaA11yRunner } = await import(\"../a11y/index.js\");\n const a11yRunner = new MuseaA11yRunner();\n const a11yResults = await a11yRunner.runAudits(artFiles, options.baseUrl, runner);\n const a11ySummary = a11yRunner.getSummary(a11yResults);\n\n console.log(\" A11y Results:\");\n console.log(\" -------------\");\n console.log(` Components: ${a11ySummary.totalComponents}`);\n console.log(` Variants: ${a11ySummary.totalVariants}`);\n console.log(` Violations: ${a11ySummary.totalViolations}`);\n console.log(` Critical: ${a11ySummary.criticalCount}`);\n console.log(` Serious: ${a11ySummary.seriousCount}`);\n console.log(` Not audited: ${a11ySummary.erroredVariants}\\n`);\n\n // Generate a11y report\n const reportDir = options.output;\n await fs.promises.mkdir(reportDir, { recursive: true });\n\n if (options.json) {\n const a11yJson = a11yRunner.generateJsonReport(a11yResults);\n const a11yPath = path.join(reportDir, \"a11y-report.json\");\n await fs.promises.writeFile(a11yPath, a11yJson);\n console.log(` A11y JSON report: ${a11yPath}\\n`);\n } else {\n const a11yHtml = a11yRunner.generateHtmlReport(a11yResults);\n const a11yPath = path.join(reportDir, \"a11y-report.html\");\n await fs.promises.writeFile(a11yPath, a11yHtml);\n console.log(` A11y HTML report: ${a11yPath}\\n`);\n }\n\n // CI mode - exit with error on critical/serious violations. A variant\n // whose audit never ran fails too, but under its own message: it is\n // not an accessibility finding, and reporting it as one sends people\n // looking for a component defect that does not exist.\n if (options.ci && a11ySummary.erroredVariants > 0) {\n console.log(\n ` CI mode: ${a11ySummary.erroredVariants} variant(s) could not be audited\\n`,\n );\n process.exit(1);\n }\n if (options.ci && (a11ySummary.criticalCount > 0 || a11ySummary.seriousCount > 0)) {\n console.log(\" CI mode: Accessibility violations found\\n\");\n process.exit(1);\n }\n } catch (e) {\n console.warn(\" A11y audits skipped:\", e instanceof Error ? e.message : String(e));\n console.warn(\" Make sure axe-core is installed: npm install axe-core\\n\");\n }\n }\n\n // Update baselines if requested\n if (options.update) {\n console.log(\" Updating baselines...\");\n const updated = await runner.updateBaselines(results);\n console.log(` Updated ${updated} baseline(s)\\n`);\n }\n\n // Generate VRT report\n const reportDir = options.output;\n await fs.promises.mkdir(reportDir, { recursive: true });\n\n if (options.json) {\n const jsonReport = generateVrtJsonReport(results, summary);\n const jsonPath = path.join(reportDir, \"vrt-report.json\");\n await fs.promises.writeFile(jsonPath, jsonReport);\n console.log(` JSON report: ${jsonPath}\\n`);\n } else {\n const htmlReport = generateVrtReport(results, summary);\n const htmlPath = path.join(reportDir, \"vrt-report.html\");\n await fs.promises.writeFile(htmlPath, htmlReport);\n console.log(` HTML report: ${htmlPath}\\n`);\n }\n\n // CI mode - exit with error if visual diffs occurred.\n if (options.ci && hasCiBlockingVrtResult(summary)) {\n console.log(\" CI mode: Exiting with error due to failures\\n\");\n process.exit(1);\n }\n } finally {\n await runner.close();\n }\n}\n\nexport async function runApprove(options: CliOptions, artFiles: ArtFileInfo[]): Promise<void> {\n const runner = new MuseaVrtRunner(createVrtOptions(options));\n\n try {\n console.log(\" Launching browser...\");\n await runner.init();\n\n console.log(\" Running tests to find diffs...\\n\");\n\n const results = await runner.runAllTests(artFiles, options.baseUrl);\n const failed = results.filter((r) => !r.passed && !r.error);\n\n if (failed.length === 0) {\n console.log(\" No failed tests to approve.\\n\");\n return;\n }\n\n const pattern = options.pattern;\n if (pattern) {\n console.log(` Approving snapshots matching: ${pattern}\\n`);\n } else {\n console.log(` Approving all ${failed.length} failed snapshot(s)...\\n`);\n }\n\n const approved = await runner.approveResults(results, pattern);\n console.log(` Approved ${approved} snapshot(s)\\n`);\n } finally {\n await runner.close();\n }\n}\n\nexport async function runClean(options: CliOptions, artFiles: ArtFileInfo[]): Promise<void> {\n const runner = new MuseaVrtRunner(createVrtOptions(options));\n\n console.log(\" Scanning for orphaned snapshots...\\n\");\n\n const cleaned = await runner.cleanOrphans(artFiles);\n\n if (cleaned === 0) {\n console.log(\" No orphaned snapshots found.\\n\");\n } else {\n console.log(`\\n Cleaned ${cleaned} orphaned snapshot(s)\\n`);\n }\n}\n\nexport async function runGenerate(options: CliOptions): Promise<void> {\n if (!options.componentPath) {\n console.error(\" Error: Missing component path.\");\n console.error(\" Usage: musea-vrt generate <component.vue>\\n\");\n process.exit(1);\n }\n\n const componentPath = path.resolve(options.componentPath);\n if (isArtFileInput(componentPath)) {\n console.error(\n \" Error: musea-vrt generate expects a source .vue component, not a .art.vue file.\",\n );\n console.error(\" Pass the component file that the art file should wrap.\\n\");\n process.exit(1);\n }\n\n // Check file exists\n try {\n await fs.promises.access(componentPath);\n } catch {\n console.error(` Error: File not found: ${componentPath}\\n`);\n process.exit(1);\n }\n\n console.log(` Generating art file for: ${path.relative(process.cwd(), componentPath)}\\n`);\n\n try {\n const { writeArtFile } = await import(\"../autogen/index.js\");\n const outputPath = await writeArtFile(componentPath);\n const relOutput = path.relative(process.cwd(), outputPath);\n\n console.log(` Generated: ${relOutput}\\n`);\n } catch (e) {\n console.error(\" Generation failed:\", e instanceof Error ? e.message : String(e));\n process.exit(1);\n }\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { loadConfigFromFile, type PluginOption } from \"vite\";\n\nimport type { MuseaVrtOptions } from \"../types/index.js\";\nimport { readMuseaOptions } from \"../plugin/options.js\";\n\nexport async function loadMuseaVrtOptions(\n configPath: string,\n cwd = process.cwd(),\n): Promise<MuseaVrtOptions | undefined> {\n const resolvedConfigPath = path.isAbsolute(configPath)\n ? configPath\n : path.resolve(cwd, configPath);\n if (!(await fileExists(resolvedConfigPath))) return undefined;\n\n const loaded = await loadConfigFromFile(\n {\n command: \"serve\",\n mode: \"development\",\n isPreview: false,\n isSsrBuild: false,\n },\n resolvedConfigPath,\n );\n if (!loaded) return undefined;\n\n const plugins: unknown[] = [];\n await collectPlugins(loaded.config.plugins, plugins);\n\n for (const plugin of plugins) {\n const options = readMuseaOptions(plugin);\n if (options?.vrt) return options.vrt;\n }\n return undefined;\n}\n\nasync function collectPlugins(\n input: PluginOption | Promise<PluginOption> | undefined,\n plugins: unknown[],\n) {\n const resolved = await input;\n if (!resolved) return;\n\n if (Array.isArray(resolved)) {\n for (const item of resolved) {\n await collectPlugins(item, plugins);\n }\n return;\n }\n\n plugins.push(resolved);\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.promises.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n","#!/usr/bin/env node\n/**\n * Musea CLI\n *\n * Usage:\n * musea-vrt [command] [options]\n *\n * Commands:\n * (default) Run VRT tests\n * approve [pat] Approve failed snapshots (optionally filtered by pattern)\n * clean Remove orphaned snapshots\n *\n * Options:\n * -u, --update Update baseline snapshots\n * -c, --config Path to vite config (default: vite.config.ts)\n * -o, --output Output directory for reports (default: .vize)\n * -t, --threshold Diff threshold percentage (default: 0.1)\n * -w, --workers Number of concurrent captures (default: 1)\n * --json Output JSON report instead of HTML\n * --ci CI mode - exit with non-zero code on failures\n * --a11y Run accessibility audits alongside VRT\n * -h, --help Show help\n */\n\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport type { ArtFileInfo } from \"../types/index.js\";\nimport { scanArtFiles, parseArtFile } from \"./utils.js\";\nimport { runVrt, runApprove, runClean, runGenerate } from \"./commands.js\";\nimport { loadMuseaVrtOptions } from \"./config.js\";\nimport type { MuseaVrtOptions } from \"../types/index.js\";\n\ntype Command = \"run\" | \"approve\" | \"clean\" | \"generate\";\n\nexport interface CliOptions {\n command: Command;\n update: boolean;\n config: string;\n output: string;\n threshold: number;\n thresholdProvided: boolean;\n workers: number;\n workersProvided: boolean;\n json: boolean;\n ci: boolean;\n a11y: boolean;\n help: boolean;\n baseUrl: string;\n pattern?: string;\n componentPath?: string;\n vrt?: MuseaVrtOptions;\n}\n\nexport function parseArgs(args: string[]): CliOptions {\n const options: CliOptions = {\n command: \"run\",\n update: false,\n config: \"vite.config.ts\",\n output: \".vize\",\n threshold: 0.1,\n thresholdProvided: false,\n workers: 1,\n workersProvided: false,\n json: false,\n ci: false,\n a11y: false,\n help: false,\n baseUrl: \"http://localhost:5173\",\n };\n\n let i = 0;\n\n // Check for subcommand as first arg\n if (args.length > 0 && !args[0].startsWith(\"-\")) {\n const sub = args[0];\n if (sub === \"approve\") {\n options.command = \"approve\";\n i = 1;\n // Optional pattern argument after approve\n if (args.length > 1 && !args[1].startsWith(\"-\")) {\n options.pattern = args[1];\n i = 2;\n }\n } else if (sub === \"clean\") {\n options.command = \"clean\";\n i = 1;\n } else if (sub === \"generate\") {\n options.command = \"generate\";\n i = 1;\n // Required component path argument\n if (args.length > 1 && !args[1].startsWith(\"-\")) {\n options.componentPath = args[1];\n i = 2;\n }\n }\n }\n\n for (; i < args.length; i++) {\n const arg = args[i];\n switch (arg) {\n case \"-u\":\n case \"--update\":\n options.update = true;\n break;\n case \"-c\":\n case \"--config\":\n options.config = args[++i] || \"vite.config.ts\";\n break;\n case \"-o\":\n case \"--output\":\n options.output = args[++i] || \".vize\";\n break;\n case \"-t\":\n case \"--threshold\":\n options.threshold = parseThreshold(args[++i]);\n options.thresholdProvided = true;\n break;\n case \"-w\":\n case \"--workers\":\n options.workers = parseWorkers(args[++i]);\n options.workersProvided = true;\n break;\n case \"--json\":\n options.json = true;\n break;\n case \"--ci\":\n options.ci = true;\n break;\n case \"--a11y\":\n options.a11y = true;\n break;\n case \"-b\":\n case \"--base-url\":\n options.baseUrl = args[++i] || \"http://localhost:5173\";\n break;\n case \"-h\":\n case \"--help\":\n options.help = true;\n break;\n }\n }\n\n return options;\n}\n\nfunction parseThreshold(value: string | undefined): number {\n const threshold = Number.parseFloat(value ?? \"\");\n return Number.isFinite(threshold) ? threshold : 0.1;\n}\n\nfunction parseWorkers(value: string | undefined): number {\n const workers = Number.parseInt(value ?? \"\", 10);\n return Number.isFinite(workers) ? Math.max(1, workers) : 1;\n}\n\nfunction printHelp(): void {\n console.log(`\nMusea VRT - Visual Regression Testing for Component Gallery\n\nUsage:\n musea-vrt [command] [options]\n\nCommands:\n (default) Run VRT tests\n approve [pattern] Approve failed snapshots and update baselines\n Optional pattern filters which snapshots to approve\n clean Remove orphaned snapshots (no matching art/variant)\n generate <component> Auto-generate .art.vue from a Vue component\n\nOptions:\n -u, --update Update baseline snapshots with current screenshots\n -c, --config <path> Path to vite config file (default: vite.config.ts)\n -o, --output <dir> Output directory for reports (default: .vize)\n -t, --threshold <n> Diff threshold percentage (default: 0.1)\n -b, --base-url <url> Base URL for dev server (default: http://localhost:5173)\n -w, --workers <n> Number of concurrent captures (default: 1)\n --json Output JSON report instead of HTML\n --ci CI mode - exit with non-zero code on failures\n --a11y Run accessibility audits alongside VRT\n -h, --help Show this help message\n\nExamples:\n # Run VRT tests\n musea-vrt\n\n # Update baseline snapshots\n musea-vrt -u\n\n # Run with custom threshold\n musea-vrt -t 0.5\n\n # CI mode with JSON output\n musea-vrt --ci --json\n\n # Run with accessibility audits\n musea-vrt --a11y\n\n # Capture variants concurrently\n musea-vrt --workers 8\n\n # Approve all failed snapshots\n musea-vrt approve\n\n # Approve specific snapshots by pattern\n musea-vrt approve \"Button/*\"\n\n # Clean orphaned snapshots\n musea-vrt clean\n\n # Auto-generate .art.vue from component\n musea-vrt generate src/components/Button.vue\n\n # Custom base URL\n musea-vrt -b http://localhost:3000\n`);\n}\n\nasync function main(): Promise<void> {\n const args = process.argv.slice(2);\n const options = parseArgs(args);\n\n if (options.help) {\n printHelp();\n process.exit(0);\n }\n\n const cwd = process.cwd();\n\n console.log(\"\\n Musea VRT\");\n console.log(\" =========\\n\");\n\n // Handle generate command early (doesn't need art file scanning)\n if (options.command === \"generate\") {\n try {\n await runGenerate(options);\n } catch (error) {\n console.error(\"\\n Error:\", error);\n process.exit(1);\n }\n return;\n }\n\n options.vrt = await loadMuseaVrtOptions(options.config, cwd);\n\n // Scan for art files\n console.log(\" Scanning for art files...\");\n const artFilePaths = await scanArtFiles(cwd);\n\n if (artFilePaths.length === 0) {\n console.log(\" No art files found.\\n\");\n process.exit(0);\n }\n\n console.log(` Found ${artFilePaths.length} art file(s)\\n`);\n\n // Parse art files\n const artFiles: ArtFileInfo[] = [];\n for (const filePath of artFilePaths) {\n const art = await parseArtFile(filePath);\n if (art) {\n artFiles.push(art);\n }\n }\n\n try {\n switch (options.command) {\n case \"run\":\n await runVrt(options, artFiles);\n break;\n case \"approve\":\n await runApprove(options, artFiles);\n break;\n case \"clean\":\n await runClean(options, artFiles);\n break;\n case \"generate\":\n // Handled above before art file scanning\n break;\n }\n } catch (error) {\n console.error(\"\\n Error:\", error);\n process.exit(1);\n }\n}\n\nif (isDirectRun()) {\n main().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n });\n}\n\nfunction isDirectRun(): boolean {\n const entrypoint = process.argv[1];\n return entrypoint ? fileURLToPath(import.meta.url) === path.resolve(entrypoint) : false;\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as ArtFileInfo, r as MuseaVrtRunner } from "./vrt-6lGTi1kM.mjs";
|
|
2
2
|
import { Page } from "playwright";
|
|
3
3
|
|
|
4
4
|
//#region src/types/api.d.ts
|
|
@@ -167,4 +167,4 @@ declare class MuseaA11yRunner {
|
|
|
167
167
|
}
|
|
168
168
|
//#endregion
|
|
169
169
|
export { generateA11yHtmlReport as a, A11yResult as c, computeA11ySummary as i, AnalysisApiResponse as l, MuseaA11yRunner as n, generateA11yJsonReport as o, resolveAxeSource as r, A11yOptions as s, A11ySummary as t, PaletteApiResponse as u };
|
|
170
|
-
//# sourceMappingURL=index-
|
|
170
|
+
//# sourceMappingURL=index-DzE3Pi5c.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index-
|
|
1
|
+
{"version":3,"file":"index-DzE3Pi5c.d.mts","names":[],"sources":["../src/types/api.ts","../src/a11y/report.ts","../src/a11y/index.ts"],"mappings":";;;;;;;UAGiB,kBAAA;EACf,KAAA;EACA,QAAA,EAAU,cAAc;EACxB,MAAA;EACA,IAAA;EACA,UAAA;AAAA;;;;UAMe,cAAA;EACf,IAAA;EACA,OAAA,EAAS,WAAA;EACT,aAAA;EACA,WAAA;EACA,QAAA;EACA,OAAA,EAAS,KAAK;IAAG,KAAA;IAAe,KAAA;EAAA;EAChC,KAAA;IAAU,GAAA;IAAa,GAAA;IAAa,IAAA;EAAA;EACpC,KAAA;AAAA;;;;KAMU,WAAA;;;AANL;UAuBU,mBAAA;EACf,KAAA,EAAO,KAAK;IACV,IAAA;IACA,IAAA;IACA,QAAA;IACA,aAAA;EAAA;EAEF,KAAA;AAAA;;;;UAUe,WAAA;EAbb;EAeF,OAAA;EAZA;EAcA,YAAA;EAdK;EAgBL,YAAA;EAN0B;EAQ1B,KAAA;AAAA;;;;UAMe,UAAA;EACf,OAAA;EACA,WAAA;EACA,UAAA,EAAY,aAAa;EACzB,MAAA;EACA,UAAA;EAFyB;;;;;;;;EAWzB,KAAA;AAAA;AAMF;;;AAAA,UAAiB,aAAA;EACf,EAAA;EACA,MAAA;EACA,WAAA;EACA,OAAA;EACA,KAAA;AAAA;;;;;;iBCvFc,kBAAA,CAAmB,OAAA,EAAS,UAAA,KAAe,WAAW;;;;iBAmBtD,sBAAA,CAAuB,OAAA,EAAS,UAAA,IAAc,OAAA,EAAS,WAAW;ADnBlF;;;AAAA,iBCgLgB,sBAAA,CAAuB,OAAqB,EAAZ,UAAU;;;;;;UCvKzC,WAAA;EACf,eAAA;EACA,aAAA;EFjBU;EEmBV,eAAA;EACA,eAAA;EACA,aAAA;EACA,YAAA;EACA,aAAA;EACA,UAAA;AAAA;;;;;;;;;;;;iBA6BoB,gBAAA,IAAoB,OAAO;;;AFvC1C;cEgEM,eAAA;EAAA,QACH,OAAA;cAEI,OAAA,GAAS,WAAA;EF7DA;AAAA;AAiBvB;;EEyDQ,SAAA,CACJ,QAAA,EAAU,WAAA,IACV,OAAA,UACA,SAAA,GAAY,cAAA,GACX,OAAA,CAAQ,UAAA;EF5DC;;;EEoHN,SAAA,CAAU,IAAA,EAAM,IAAA,EAAM,OAAA,UAAiB,WAAA,WAAsB,OAAA,CAAQ,UAAA;EFlHzE;;;EEqJF,UAAA,CAAW,OAAA,EAAS,UAAA,KAAe,WAAA;EFjJ9B;AAAA;AAUP;EE8IE,kBAAA,CAAmB,OAAA,EAAS,UAAA;;;;EAQ5B,kBAAA,CAAmB,OAAA,EAAS,UAAA;EFhJ5B;;;EAAA,QEuJQ,eAAA;EAAA,QAqCA,eAAA;AAAA"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { a as VrtOptions, i as ViewportConfig, n as CiConfig, r as ComparisonConfig, t as CaptureConfig } from "./vrt-
|
|
2
|
-
import { a as MuseaTokenPreviewConfig, c as ResolvedTokenPreview, d as StyleDictionaryConfig, f as StyleDictionaryOutput, i as MuseaVrtOptions, l as resolveTokenPreview, m as parseTokens, n as MuseaTheme, o as MuseaTokenPreviewKind, p as TokenCategory, r as MuseaThemeColors, s as MuseaTokenPreviewRule, t as MuseaOptions, u as DesignToken } from "./plugin-
|
|
3
|
-
import { c as
|
|
4
|
-
import { c as A11yResult, l as AnalysisApiResponse, n as MuseaA11yRunner, s as A11yOptions, t as A11ySummary, u as PaletteApiResponse } from "./index-
|
|
1
|
+
import { a as VrtOptions, i as ViewportConfig, n as CiConfig, r as ComparisonConfig, t as CaptureConfig } from "./vrt-DVub2B6r.mjs";
|
|
2
|
+
import { a as MuseaTokenPreviewConfig, c as ResolvedTokenPreview, d as StyleDictionaryConfig, f as StyleDictionaryOutput, i as MuseaVrtOptions, l as resolveTokenPreview, m as parseTokens, n as MuseaTheme, o as MuseaTokenPreviewKind, p as TokenCategory, r as MuseaThemeColors, s as MuseaTokenPreviewRule, t as MuseaOptions, u as DesignToken } from "./plugin-CIrKPW8K.mjs";
|
|
3
|
+
import { c as VrtSummary, d as ArtVariant, f as CsfOutput, l as ArtFileInfo, n as generateVrtReport, r as MuseaVrtRunner, s as VrtResult, t as generateVrtJsonReport, u as ArtMetadata } from "./vrt-6lGTi1kM.mjs";
|
|
4
|
+
import { c as A11yResult, l as AnalysisApiResponse, n as MuseaA11yRunner, s as A11yOptions, t as A11ySummary, u as PaletteApiResponse } from "./index-DzE3Pi5c.mjs";
|
|
5
5
|
import { AutogenOptions, AutogenOutput, GeneratedVariant, PropDefinition, generateArtFile, writeArtFile } from "./autogen/index.mjs";
|
|
6
6
|
import { Plugin } from "vite";
|
|
7
7
|
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { A as resolveInsideAny, C as parseJsonBody, D as assertVueSourcePath, E as assertArtVueSourcePath, F as loadNative, M as resolveUrlPathInside, N as HttpError, O as decodeUrlComponent, P as analyzeSfcFallback, S as createDevSessionToken, T as validateDevApiRequest, _ as scanArtFiles, a as parseScriptSetupForArt, c as resolveArtComponent, d as resolveReadableArtPath, f as buildThemeConfig, g as resolveScanRoots, h as matchGlob, i as generateArtModule, j as resolveTrustedSourcePath, k as resolveInside, l as allowedSourceRoots, m as generateStorybookFiles, o as rewriteRelativeImportStatement, p as escapeHtml, r as processMuseaArtFile, s as expandSelfTag, t as attachMuseaOptions, u as resolveComponentSourcePath, v as shouldProcess, w as serializeScriptValue, x as collectRequestBody, y as toPascalCase } from "./options-CGA4BetG.mjs";
|
|
2
2
|
import { a as generateGalleryGlobalsScript, i as generateDevGlobalsScript, n as generateGalleryScript, r as generateGalleryModule, t as generateGalleryBody } from "./template-B_y7_OsF.mjs";
|
|
3
|
-
import { i as MuseaVrtRunner, n as generateVrtJsonReport, r as generateVrtReport } from "./vrt-
|
|
3
|
+
import { i as MuseaVrtRunner, n as generateVrtJsonReport, r as generateVrtReport } from "./vrt-Dr5Gs71k.mjs";
|
|
4
4
|
import { t as MuseaA11yRunner } from "./a11y-Ci9EEv6F.mjs";
|
|
5
5
|
import { n as writeArtFile, t as generateArtFile } from "./autogen-B-N0cJbZ.mjs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as VrtOptions, n as CiConfig, r as ComparisonConfig, t as CaptureConfig } from "./vrt-
|
|
1
|
+
import { a as VrtOptions, n as CiConfig, r as ComparisonConfig, t as CaptureConfig } from "./vrt-DVub2B6r.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/tokens/parser.d.ts
|
|
4
4
|
/**
|
|
@@ -256,4 +256,4 @@ interface MuseaOptions {
|
|
|
256
256
|
}
|
|
257
257
|
//#endregion
|
|
258
258
|
export { MuseaTokenPreviewConfig as a, ResolvedTokenPreview as c, StyleDictionaryConfig as d, StyleDictionaryOutput as f, MuseaVrtOptions as i, resolveTokenPreview as l, parseTokens as m, MuseaTheme as n, MuseaTokenPreviewKind as o, TokenCategory as p, MuseaThemeColors as r, MuseaTokenPreviewRule as s, MuseaOptions as t, DesignToken as u };
|
|
259
|
-
//# sourceMappingURL=plugin-
|
|
259
|
+
//# sourceMappingURL=plugin-CIrKPW8K.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-
|
|
1
|
+
{"version":3,"file":"plugin-CIrKPW8K.d.mts","names":[],"sources":["../src/tokens/parser.ts","../src/tokens/preview.ts","../src/types/plugin.ts"],"mappings":";;;;;;AAYA;;;;;UAAiB,WAAA;EACf,KAAA;EACA,IAAA;EACA,WAAA;EACA,UAAA,GAAa,MAAM;EACnB,KAAA;EACA,UAAA;EACA,cAAA;AAAA;AAMF;;;AAAA,UAAiB,aAAA;EACf,IAAA;EACA,MAAA,EAAQ,MAAA,SAAe,WAAA;EACvB,aAAA,GAAgB,aAAA;AAAA;;;;UAMD,qBAAA;EACf,UAAA,EAAY,aAAa;EACzB,QAAA;IACE,IAAA;IACA,OAAA;IACA,WAAA;EAAA;AAAA;;;;UAOa,qBAAA;EATb;;;EAaF,UAAA;EAXa;AAOf;;;EAUE,YAAA;EANA;;;;EAYA,SAAA;EAK2B;AAAA;AAM7B;EANE,UAAA,GAAa,cAAc;AAAA;;;;KAMjB,cAAA,IAAkB,KAAA,EAAO,WAAA,EAAa,IAAA,eAAmB,WAAW;;;AAAA;iBAK1D,WAAA,CAAY,UAAA,WAAqB,OAAO,CAAC,aAAA;;;KC5EnD,qBAAA;AAAA,UAaK,qBAAA;EACf,IAAA,EAAM,qBAAqB;EAC3B,IAAA;EACA,YAAA;EACA,YAAA;EACA,qBAAA;AAAA;AAAA,UAGe,uBAAA;EDPf;;;;ECYA,KAAA,GAAQ,qBAAA;EDTM;ECWd,aAAA,GAAgB,qBAAqB;AAAA;AAAA,UAGtB,oBAAA;EACf,IAAA,EAAM,qBAAA;EACN,KAAA;EACA,SAAA;EACA,SAAA;IACE,IAAA;IACA,KAAA,GAAQ,WAAW;IACnB,KAAA;EAAA;AAAA;AAAA,UAIa,wBAAA;EACf,SAAA;EACA,KAAA,EAAO,WAAA;EACP,QAAA,GAAW,MAAA,SAAe,WAAA;EAC1B,MAAA,GAAS,uBAAA;AAAA;AAAA,iBASK,mBAAA,CAAoB,KAAA,EAAO,wBAAA,GAA2B,oBAAoB;;;KCtD9E,eAAA;AFSZ;;;;AAAA,UEHiB,gBAAA;EACf,SAAA;EACA,WAAA;EACA,UAAA;EACA,UAAA;EACA,MAAA;EACA,WAAA;EACA,cAAA;EACA,YAAA;EACA,IAAA;EACA,aAAA;EACA,SAAA;EACA,MAAA;EACA,YAAA;EACA,OAAA;EACA,KAAA;EACA,IAAA;EACA,OAAA;EACA,MAAA;AAAA;;;;UAMe,UAAA;EFLc;EEO7B,IAAA;EFDe;EEGf,IAAA;;EAEA,MAAA,EAAQ,gBAAgB;AAAA;AAAA,UAGT,eAAA,SAAwB,UAAA;EACvC,OAAA,GAAU,aAAA;EACV,UAAA,GAAa,gBAAA;EACb,EAAA,GAAK,QAAA;AAAA;;AFNQ;AAOf;UEKiB,YAAA;;;;;EAKf,OAAA;EFWA;;;AAA2B;EEL3B,OAAA;EFWwB;;;;EELxB,QAAA;EFKgD;;;AAA8B;EEC9E,eAAA;EFI+B;;;;EEE/B,eAAA;EFF6D;;AAAa;;;;EEU1E,SAAA;EDtF+B;;;EC2F/B,GAAA,GAAM,eAAA;ED9ES;;;;;ECqFf,UAAA;EDnFA;;;;ECyFA,aAAA,GAAgB,uBAAA;EDtFK;AAGvB;;;;;;;;;EC+FE,WAAA;EDrFe;;;;;;;;EC+Ff,KAAA,iCAAsC,UAAA,GAAa,UAAA;ED1FjD;;;;;AAEK;AAIT;;;;ECgGE,UAAA;ED7FW;;;;;;;;;;ECyGX,YAAA;EDxGS;;AAAuB;AASlC;ECqGE,UAAA,GAAa,eAAA;;;;;;;ADrG2E;EC8GxF,kBAAA;AAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as VrtOptions, i as ViewportConfig, n as CiConfig, r as ComparisonConfig, t as CaptureConfig } from "./vrt-
|
|
1
|
+
import { a as VrtOptions, i as ViewportConfig, n as CiConfig, r as ComparisonConfig, t as CaptureConfig } from "./vrt-DVub2B6r.mjs";
|
|
2
2
|
import { Browser, BrowserContext, Page } from "playwright";
|
|
3
3
|
|
|
4
4
|
//#region src/types/art.d.ts
|
|
@@ -194,6 +194,7 @@ declare class MuseaVrtRunner {
|
|
|
194
194
|
*/
|
|
195
195
|
getSummary(results: VrtResult[]): VrtSummary;
|
|
196
196
|
}
|
|
197
|
+
declare function normalizeVrtWorkerCount(workers: number | undefined): number;
|
|
197
198
|
//#endregion
|
|
198
199
|
//#region src/vrt/report.d.ts
|
|
199
200
|
/**
|
|
@@ -206,5 +207,5 @@ declare function generateVrtReport(results: VrtResult[], summary: VrtSummary): s
|
|
|
206
207
|
*/
|
|
207
208
|
declare function generateVrtJsonReport(results: VrtResult[], summary: VrtSummary): string;
|
|
208
209
|
//#endregion
|
|
209
|
-
export {
|
|
210
|
-
//# sourceMappingURL=vrt-
|
|
210
|
+
export { ExtendedVrtOptions as a, VrtSummary as c, ArtVariant as d, CsfOutput as f, normalizeVrtWorkerCount as i, ArtFileInfo as l, generateVrtReport as n, PixelCompareOptions as o, MuseaVrtRunner as r, VrtResult as s, generateVrtJsonReport as t, ArtMetadata as u };
|
|
211
|
+
//# sourceMappingURL=vrt-6lGTi1kM.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vrt-6lGTi1kM.d.mts","names":[],"sources":["../src/types/art.ts","../src/vrt/types.ts","../src/vrt/runner.ts","../src/vrt/report.ts"],"mappings":"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"}
|
|
@@ -31,6 +31,11 @@ interface VrtOptions {
|
|
|
31
31
|
* @default [{ width: 1280, height: 720 }, { width: 375, height: 667 }]
|
|
32
32
|
*/
|
|
33
33
|
viewports?: ViewportConfig[];
|
|
34
|
+
/**
|
|
35
|
+
* Number of variants/viewports to capture concurrently.
|
|
36
|
+
* @default 1
|
|
37
|
+
*/
|
|
38
|
+
workers?: number;
|
|
34
39
|
}
|
|
35
40
|
/**
|
|
36
41
|
* Screenshot capture configuration.
|
|
@@ -81,4 +86,4 @@ interface CiConfig {
|
|
|
81
86
|
}
|
|
82
87
|
//#endregion
|
|
83
88
|
export { VrtOptions as a, ViewportConfig as i, CiConfig as n, ComparisonConfig as r, CaptureConfig as t };
|
|
84
|
-
//# sourceMappingURL=vrt-
|
|
89
|
+
//# sourceMappingURL=vrt-DVub2B6r.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vrt-DVub2B6r.d.mts","names":[],"sources":["../src/types/vrt.ts"],"mappings":";;AAGA;;UAAiB,cAAA;EAAc;EAE7B,KAAA;EAEA;EAAA,MAAA;EAIA;EAFA,iBAAA;EAEI;EAAJ,IAAA;AAAA;;;;UAMe,UAAA;EAiBf;;;;EAZA,WAAA;EAwBe;;;;EAlBf,SAAA;EAsBA;;;;EAhBA,SAAA,GAAY,cAAc;EAwBd;AAAA;AAMd;;EAxBE,OAAA;AAAA;;;;UAMe,aAAA;EA0BD;EAxBd,QAAA;EAwBoC;EAtBpC,cAAA;EAsBqC;EApBrC,UAAA;EA0BuB;EAxBvB,YAAA;EAwBuB;EAtBvB,YAAA;EA0BA;EAxBA,YAAA;AAAA;;AA4BO;;UAtBQ,gBAAA;;EAEf,YAAA;;EAEA,KAAA;;EAEA,SAAA;;EAEA,SAAA;IAAc,CAAA;IAAW,CAAA;IAAW,CAAA;EAAA;AAAA;;;;UAMrB,QAAA;;EAEf,UAAA;;EAEA,gBAAA;;EAEA,UAAA;;EAEA,OAAA;AAAA"}
|
|
@@ -323,7 +323,8 @@ var MuseaVrtRunner = class {
|
|
|
323
323
|
width: 375,
|
|
324
324
|
height: 667,
|
|
325
325
|
name: "mobile"
|
|
326
|
-
}]
|
|
326
|
+
}],
|
|
327
|
+
workers: normalizeVrtWorkerCount(options.workers)
|
|
327
328
|
};
|
|
328
329
|
this.capture = {
|
|
329
330
|
fullPage: options.capture?.fullPage ?? false,
|
|
@@ -386,24 +387,18 @@ var MuseaVrtRunner = class {
|
|
|
386
387
|
*/
|
|
387
388
|
async runAllTests(artFiles, baseUrl) {
|
|
388
389
|
if (!this.browser) throw new Error("VRT runner not initialized. Call init() first.");
|
|
389
|
-
const results = [];
|
|
390
390
|
const retries = this.ci.retries ?? 0;
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
if (result.passed || result.isNew || !result.error) break;
|
|
400
|
-
attempts++;
|
|
401
|
-
if (attempts <= retries) console.log(`[vrt] Retry ${attempts}/${retries}: ${path.basename(art.path)}/${variant.name}`);
|
|
402
|
-
}
|
|
403
|
-
if (result) results.push(result);
|
|
391
|
+
return runJobsWithWorkers(createVrtJobs(artFiles, this.options.viewports), this.options.workers, async (job) => {
|
|
392
|
+
let result = null;
|
|
393
|
+
let attempts = 0;
|
|
394
|
+
while (attempts <= retries) {
|
|
395
|
+
result = await this.captureAndCompare(job.art, job.variantName, job.viewport, baseUrl);
|
|
396
|
+
if (result.passed || result.isNew || !result.error) break;
|
|
397
|
+
attempts++;
|
|
398
|
+
if (attempts <= retries) console.log(`[vrt] Retry ${attempts}/${retries}: ${path.basename(job.art.path)}/${job.variantName}`);
|
|
404
399
|
}
|
|
405
|
-
|
|
406
|
-
|
|
400
|
+
return result;
|
|
401
|
+
});
|
|
407
402
|
}
|
|
408
403
|
/**
|
|
409
404
|
* Run VRT tests - alias used by the plugin API that accepts options.
|
|
@@ -497,6 +492,38 @@ var MuseaVrtRunner = class {
|
|
|
497
492
|
return computeSummary(results, this.startTime);
|
|
498
493
|
}
|
|
499
494
|
};
|
|
495
|
+
function normalizeVrtWorkerCount(workers) {
|
|
496
|
+
if (workers === void 0 || !Number.isFinite(workers)) return 1;
|
|
497
|
+
return Math.max(1, Math.floor(workers));
|
|
498
|
+
}
|
|
499
|
+
function createVrtJobs(artFiles, defaultViewports) {
|
|
500
|
+
const jobs = [];
|
|
501
|
+
for (const art of artFiles) for (const variant of art.variants) {
|
|
502
|
+
if (variant.skipVrt) continue;
|
|
503
|
+
const viewports = variant.args?.viewport ? [variant.args.viewport] : defaultViewports;
|
|
504
|
+
for (const viewport of viewports) jobs.push({
|
|
505
|
+
art,
|
|
506
|
+
variantName: variant.name,
|
|
507
|
+
viewport
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
return jobs;
|
|
511
|
+
}
|
|
512
|
+
async function runJobsWithWorkers(jobs, workerCount, runJob) {
|
|
513
|
+
const results = Array.from({ length: jobs.length }, () => null);
|
|
514
|
+
let nextJobIndex = 0;
|
|
515
|
+
async function runWorker() {
|
|
516
|
+
while (true) {
|
|
517
|
+
const jobIndex = nextJobIndex++;
|
|
518
|
+
const job = jobs[jobIndex];
|
|
519
|
+
if (!job) return;
|
|
520
|
+
results[jobIndex] = await runJob(job);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
const activeWorkers = Math.min(normalizeVrtWorkerCount(workerCount), jobs.length);
|
|
524
|
+
await Promise.all(Array.from({ length: activeWorkers }, () => runWorker()));
|
|
525
|
+
return results.filter((result) => result !== null);
|
|
526
|
+
}
|
|
500
527
|
//#endregion
|
|
501
528
|
//#region src/vrt/report.ts
|
|
502
529
|
/**
|
|
@@ -763,6 +790,6 @@ function generateVrtJsonReport(results, summary) {
|
|
|
763
790
|
//#region src/vrt.ts
|
|
764
791
|
var vrt_default = MuseaVrtRunner;
|
|
765
792
|
//#endregion
|
|
766
|
-
export { MuseaVrtRunner as i, generateVrtJsonReport as n, generateVrtReport as r, vrt_default as t };
|
|
793
|
+
export { normalizeVrtWorkerCount as a, MuseaVrtRunner as i, generateVrtJsonReport as n, generateVrtReport as r, vrt_default as t };
|
|
767
794
|
|
|
768
|
-
//# sourceMappingURL=vrt-
|
|
795
|
+
//# sourceMappingURL=vrt-Dr5Gs71k.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vrt-Dr5Gs71k.mjs","names":[],"sources":["../src/vrt/comparison.ts","../src/vrt/utils.ts","../src/vrt/runner-comparison.ts","../src/vrt/runner.ts","../src/vrt/report.ts","../src/vrt.ts"],"sourcesContent":["/**\n * Pixel comparison utilities for VRT.\n *\n * Provides PNG reading/writing, color delta calculation using YIQ color space,\n * and anti-aliasing detection for pixel-level image comparison.\n */\n\nimport fs from \"node:fs\";\nimport { PNG } from \"pngjs\";\n\n/**\n * Read PNG file and return PNG object.\n */\nexport async function readPng(filepath: string): Promise<PNG> {\n return new Promise((resolve, reject) => {\n fs.createReadStream(filepath)\n .pipe(new PNG())\n .on(\"parsed\", function (this: PNG) {\n resolve(this);\n })\n .on(\"error\", reject);\n });\n}\n\n/**\n * Write PNG object to file.\n */\nexport async function writePng(png: PNG, filepath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n png.pack().pipe(fs.createWriteStream(filepath)).on(\"finish\", resolve).on(\"error\", reject);\n });\n}\n\n/**\n * Calculate color delta using YIQ color space.\n */\nexport function colorDelta(\n r1: number,\n g1: number,\n b1: number,\n a1: number,\n r2: number,\n g2: number,\n b2: number,\n a2: number,\n): number {\n if (a1 !== 255) {\n r1 = blend(r1, 255, a1 / 255);\n g1 = blend(g1, 255, a1 / 255);\n b1 = blend(b1, 255, a1 / 255);\n }\n if (a2 !== 255) {\n r2 = blend(r2, 255, a2 / 255);\n g2 = blend(g2, 255, a2 / 255);\n b2 = blend(b2, 255, a2 / 255);\n }\n\n const y1 = r1 * 0.29889531 + g1 * 0.58662247 + b1 * 0.11448223;\n const i1 = r1 * 0.59597799 - g1 * 0.2741761 - b1 * 0.32180189;\n const q1 = r1 * 0.21147017 - g1 * 0.52261711 + b1 * 0.31114694;\n\n const y2 = r2 * 0.29889531 + g2 * 0.58662247 + b2 * 0.11448223;\n const i2 = r2 * 0.59597799 - g2 * 0.2741761 - b2 * 0.32180189;\n const q2 = r2 * 0.21147017 - g2 * 0.52261711 + b2 * 0.31114694;\n\n const dy = y1 - y2;\n const di = i1 - i2;\n const dq = q1 - q2;\n\n return dy * dy * 0.5053 + di * di * 0.299 + dq * dq * 0.1957;\n}\n\n/**\n * Blend foreground with background using alpha.\n */\nfunction blend(fg: number, bg: number, alpha: number): number {\n return bg + (fg - bg) * alpha;\n}\n\n/**\n * Check if file exists.\n */\nexport async function fileExists(filepath: string): Promise<boolean> {\n try {\n await fs.promises.access(filepath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Simple anti-aliasing detection.\n * A pixel is likely anti-aliased if its neighbors have high contrast in opposite directions.\n */\nexport function isAntiAliased(\n img1: PNG,\n img2: PNG,\n x: number,\n y: number,\n width: number,\n height: number,\n): boolean {\n const minX = Math.max(0, x - 1);\n const maxX = Math.min(width - 1, x + 1);\n const minY = Math.max(0, y - 1);\n const maxY = Math.min(height - 1, y + 1);\n\n let zeroes = 0;\n let positives = 0;\n let negatives = 0;\n\n for (let ny = minY; ny <= maxY; ny++) {\n for (let nx = minX; nx <= maxX; nx++) {\n if (nx === x && ny === y) continue;\n const idx = (ny * width + nx) * 4;\n\n const delta = colorDelta(\n img1.data[idx],\n img1.data[idx + 1],\n img1.data[idx + 2],\n img1.data[idx + 3],\n img2.data[idx],\n img2.data[idx + 1],\n img2.data[idx + 2],\n img2.data[idx + 3],\n );\n\n if (delta === 0) {\n zeroes++;\n } else if (delta > 0) {\n positives++;\n } else {\n negatives++;\n }\n }\n }\n\n // If neighbors are mixed (some match, some differ), it's likely AA\n return zeroes > 0 && (positives > 0 || negatives > 0) && positives + negatives < 4;\n}\n\n/**\n * Simple glob matching for pattern-based filtering.\n */\nexport function matchGlob(filepath: string, pattern: string): boolean {\n const regex = pattern\n .replace(/\\./g, \"\\\\.\")\n .replace(/\\*\\*/g, \".*\")\n .replace(/\\*(?!\\*)/g, \"[^/]*\");\n return new RegExp(`^${regex}$`).test(filepath);\n}\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","/**\n * Utility functions for VRT runner.\n *\n * Includes summary generation, URL builders, and helpers\n * used across the runner modules.\n */\n\nimport type { VrtResult, VrtSummary } from \"./types.js\";\n\n/**\n * Build URL for variant preview.\n */\nexport function buildVariantUrl(baseUrl: string, artPath: string, variantName: string): string {\n const encodedPath = encodeURIComponent(artPath);\n const encodedVariant = encodeURIComponent(variantName);\n return `${baseUrl}/__musea__/preview?art=${encodedPath}&variant=${encodedVariant}`;\n}\n\n/**\n * Compute VRT summary statistics from a list of results.\n */\nexport function computeSummary(results: VrtResult[], startTime: number): VrtSummary {\n return {\n total: results.length,\n passed: results.filter((r) => r.passed && !r.isNew).length,\n failed: results.filter((r) => !r.passed && !r.error).length,\n new: results.filter((r) => r.isNew).length,\n skipped: results.filter((r) => r.error).length,\n duration: Date.now() - startTime,\n };\n}\n","/**\n * Screenshot capture and pixel comparison logic for VRT.\n *\n * Contains the captureAndCompare flow (browser context, navigation,\n * element hiding/masking, screenshot, baseline check) and the\n * pixel-level compareImages implementation.\n */\n\nimport type { BrowserContext, Page } from \"playwright\";\nimport type { ArtFileInfo, ViewportConfig } from \"../types/index.js\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { PNG } from \"pngjs\";\n\nimport { readPng, writePng, colorDelta, isAntiAliased, fileExists } from \"./comparison.js\";\nimport type { VrtResult } from \"./types.js\";\nimport type { MuseaVrtRunner } from \"./runner.js\";\nimport { buildVariantUrl } from \"./utils.js\";\n\n/**\n * Capture screenshot and compare with baseline.\n *\n * Standalone function that operates on a MuseaVrtRunner instance.\n */\nexport async function captureAndCompare(\n runner: MuseaVrtRunner,\n art: ArtFileInfo,\n variantName: string,\n viewport: ViewportConfig,\n baseUrl: string,\n): Promise<VrtResult> {\n const browser = runner.getBrowser();\n if (!browser) {\n throw new Error(\"VRT runner not initialized. Call init() first.\");\n }\n\n const options = runner.getOptions();\n const capture = runner.getCapture();\n const comparison = runner.getComparison();\n\n const snapshotDir = options.snapshotDir;\n const artBaseName = path.basename(art.path, \".art.vue\");\n const viewportName = viewport.name || `${viewport.width}x${viewport.height}`;\n const snapshotName = `${artBaseName}--${variantName}--${viewportName}.png`;\n const snapshotPath = path.join(snapshotDir, snapshotName);\n const currentPath = path.join(snapshotDir, \"current\", snapshotName);\n const diffPath = path.join(snapshotDir, \"diff\", snapshotName);\n\n // Ensure directories exist\n await fs.promises.mkdir(path.dirname(snapshotPath), { recursive: true });\n await fs.promises.mkdir(path.join(snapshotDir, \"current\"), { recursive: true });\n await fs.promises.mkdir(path.join(snapshotDir, \"diff\"), { recursive: true });\n\n let context: BrowserContext | null = null;\n let page: Page | null = null;\n\n try {\n context = await browser.newContext({\n viewport: {\n width: viewport.width,\n height: viewport.height,\n },\n deviceScaleFactor: viewport.deviceScaleFactor ?? 1,\n });\n page = await context.newPage();\n\n // Navigate to variant preview URL\n const variantUrl = buildVariantUrl(baseUrl, art.path, variantName);\n const waitUntil = capture.waitForNetwork ? (\"networkidle\" as const) : (\"load\" as const);\n await page.goto(variantUrl, { waitUntil });\n\n // Wait for content to render\n await page.waitForSelector(capture.waitSelector, { timeout: 10000 });\n\n // Additional wait for animations to settle\n await page.waitForTimeout(capture.settleTime);\n\n // Hide elements before capture\n if (capture.hideElements.length > 0) {\n for (const selector of capture.hideElements) {\n await page.evaluate((sel) => {\n document.querySelectorAll(sel).forEach((el) => {\n (el as HTMLElement).style.visibility = \"hidden\";\n });\n }, selector);\n }\n }\n\n // Mask elements before capture (replace with colored box)\n if (capture.maskElements.length > 0) {\n for (const selector of capture.maskElements) {\n await page.evaluate((sel) => {\n document.querySelectorAll(sel).forEach((el) => {\n const htmlEl = el as HTMLElement;\n htmlEl.style.background = \"#ff00ff\";\n htmlEl.style.color = \"transparent\";\n htmlEl.innerHTML = \"\";\n });\n }, selector);\n }\n }\n\n // Take screenshot\n await page.screenshot({\n path: currentPath,\n fullPage: capture.fullPage,\n });\n\n // Check if baseline exists\n const hasBaseline = await fileExists(snapshotPath);\n\n if (!hasBaseline) {\n // First run - save as baseline\n await fs.promises.copyFile(currentPath, snapshotPath);\n return {\n artPath: art.path,\n variantName,\n viewport,\n passed: true,\n snapshotPath,\n currentPath,\n isNew: true,\n };\n }\n\n // Compare images using pixel comparison\n const comparisonResult = await compareImages(snapshotPath, currentPath, diffPath, comparison);\n\n const passed = comparisonResult.diffPercentage <= options.threshold;\n\n return {\n artPath: art.path,\n variantName,\n viewport,\n passed,\n snapshotPath,\n currentPath,\n diffPath: passed ? undefined : diffPath,\n diffPercentage: comparisonResult.diffPercentage,\n diffPixels: comparisonResult.diffPixels,\n totalPixels: comparisonResult.totalPixels,\n };\n } catch (error) {\n return {\n artPath: art.path,\n variantName,\n viewport,\n passed: false,\n snapshotPath,\n error: error instanceof Error ? error.message : String(error),\n };\n } finally {\n if (page) await page.close();\n if (context) await context.close();\n }\n}\n\n/**\n * Compare two PNG images and generate a diff image.\n * Returns pixel difference statistics.\n */\nexport async function compareImages(\n baselinePath: string,\n currentPath: string,\n diffPath: string,\n comparison: {\n antiAliasing?: boolean;\n alpha?: boolean;\n diffColor?: { r: number; g: number; b: number };\n },\n): Promise<{ diffPixels: number; totalPixels: number; diffPercentage: number }> {\n const baseline = await readPng(baselinePath);\n const current = await readPng(currentPath);\n\n // Handle size mismatch\n if (baseline.width !== current.width || baseline.height !== current.height) {\n const width = Math.max(baseline.width, current.width);\n const height = Math.max(baseline.height, current.height);\n const diff = new PNG({ width, height });\n\n // Fill with red to indicate size mismatch\n for (let i = 0; i < diff.data.length; i += 4) {\n diff.data[i] = 255; // R\n diff.data[i + 1] = 0; // G\n diff.data[i + 2] = 0; // B\n diff.data[i + 3] = 255; // A\n }\n\n await writePng(diff, diffPath);\n\n return {\n diffPixels: width * height,\n totalPixels: width * height,\n diffPercentage: 100,\n };\n }\n\n const width = baseline.width;\n const height = baseline.height;\n const totalPixels = width * height;\n const diff = new PNG({ width, height });\n\n const useAntiAliasing = comparison.antiAliasing ?? true;\n const useAlpha = comparison.alpha ?? true;\n const diffColor = comparison.diffColor ?? { r: 255, g: 0, b: 0 };\n\n // Pixel comparison\n let diffPixels = 0;\n const threshold = 0.1; // Color difference threshold\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = (y * width + x) * 4;\n\n const r1 = baseline.data[idx];\n const g1 = baseline.data[idx + 1];\n const b1 = baseline.data[idx + 2];\n const a1 = useAlpha ? baseline.data[idx + 3] : 255;\n\n const r2 = current.data[idx];\n const g2 = current.data[idx + 1];\n const b2 = current.data[idx + 2];\n const a2 = useAlpha ? current.data[idx + 3] : 255;\n\n // Calculate color difference using YIQ color space\n const delta = colorDelta(r1, g1, b1, a1, r2, g2, b2, a2);\n\n if (delta > threshold * 255 * 255) {\n // Anti-aliasing detection: check if pixel is likely AA\n if (useAntiAliasing && isAntiAliased(baseline, current, x, y, width, height)) {\n // Mark as AA (yellow)\n diffPixels++;\n diff.data[idx] = 255;\n diff.data[idx + 1] = 200;\n diff.data[idx + 2] = 0;\n diff.data[idx + 3] = 128;\n } else {\n // Mark as different\n diffPixels++;\n diff.data[idx] = diffColor.r;\n diff.data[idx + 1] = diffColor.g;\n diff.data[idx + 2] = diffColor.b;\n diff.data[idx + 3] = 255;\n }\n } else {\n // Grayscale for matching pixels\n const gray = Math.round((r2 + g2 + b2) / 3);\n diff.data[idx] = gray;\n diff.data[idx + 1] = gray;\n diff.data[idx + 2] = gray;\n diff.data[idx + 3] = 128; // Semi-transparent\n }\n }\n }\n\n // Only write diff if there are differences\n if (diffPixels > 0) {\n await writePng(diff, diffPath);\n }\n\n const diffPercentage = (diffPixels / totalPixels) * 100;\n\n return {\n diffPixels,\n totalPixels,\n diffPercentage,\n };\n}\n","/**\n * VRT runner using Playwright for browser automation.\n *\n * Manages browser lifecycle, screenshot capture, and baseline comparison\n * for visual regression testing of Musea art file variants.\n */\n\nimport type { Browser, BrowserContext, Page } from \"playwright\";\nimport type {\n ArtFileInfo,\n VrtOptions,\n ViewportConfig,\n CaptureConfig,\n ComparisonConfig,\n CiConfig,\n} from \"../types/index.js\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { fileExists, matchGlob } from \"./comparison.js\";\nimport { captureAndCompare } from \"./runner-comparison.js\";\nimport { computeSummary } from \"./utils.js\";\n\nexport type { VrtResult, VrtSummary, ExtendedVrtOptions, PixelCompareOptions } from \"./types.js\";\n\nimport type { VrtResult, VrtSummary, ExtendedVrtOptions } from \"./types.js\";\n\ntype VrtJob = {\n art: ArtFileInfo;\n variantName: string;\n viewport: ViewportConfig;\n};\n\n/**\n * VRT runner using Playwright.\n */\nexport class MuseaVrtRunner {\n private options: Required<VrtOptions>;\n private capture: Required<CaptureConfig>;\n private comparison: ComparisonConfig;\n private ci: CiConfig;\n private browser: Browser | null = null;\n private startTime: number = 0;\n\n constructor(options: ExtendedVrtOptions = {}) {\n this.options = {\n snapshotDir: options.snapshotDir ?? \".vize/snapshots\",\n threshold: options.threshold ?? 0.1,\n viewports: options.viewports ?? [\n { width: 1280, height: 720, name: \"desktop\" },\n { width: 375, height: 667, name: \"mobile\" },\n ],\n workers: normalizeVrtWorkerCount(options.workers),\n };\n this.capture = {\n fullPage: options.capture?.fullPage ?? false,\n waitForNetwork: options.capture?.waitForNetwork ?? true,\n settleTime: options.capture?.settleTime ?? 100,\n waitSelector: options.capture?.waitSelector ?? \".musea-variant\",\n hideElements: options.capture?.hideElements ?? [],\n maskElements: options.capture?.maskElements ?? [],\n };\n this.comparison = options.comparison ?? {};\n this.ci = options.ci ?? {};\n }\n\n // --- Internal accessors used by runner-comparison ---\n\n /** @internal */\n getBrowser(): Browser | null {\n return this.browser;\n }\n\n /** @internal */\n getOptions(): Required<VrtOptions> {\n return this.options;\n }\n\n /** @internal */\n getCapture(): Required<CaptureConfig> {\n return this.capture;\n }\n\n /** @internal */\n getComparison(): ComparisonConfig {\n return this.comparison;\n }\n\n /**\n * Initialize Playwright browser.\n */\n async init(): Promise<void> {\n const { chromium } = await import(\"playwright\");\n this.browser = await chromium.launch({ headless: true });\n this.startTime = Date.now();\n }\n\n /**\n * Close browser and cleanup.\n */\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n }\n }\n\n /**\n * Alias for init() - used by the plugin API.\n */\n async start(): Promise<void> {\n return this.init();\n }\n\n /**\n * Alias for close() - used by the plugin API.\n */\n async stop(): Promise<void> {\n return this.close();\n }\n\n /**\n * Run VRT tests for all Art files.\n */\n async runAllTests(artFiles: ArtFileInfo[], baseUrl: string): Promise<VrtResult[]> {\n if (!this.browser) {\n throw new Error(\"VRT runner not initialized. Call init() first.\");\n }\n\n const retries = this.ci.retries ?? 0;\n const jobs = createVrtJobs(artFiles, this.options.viewports);\n\n return runJobsWithWorkers(jobs, this.options.workers, async (job) => {\n let result: VrtResult | null = null;\n let attempts = 0;\n\n while (attempts <= retries) {\n result = await this.captureAndCompare(job.art, job.variantName, job.viewport, baseUrl);\n if (result.passed || result.isNew || !result.error) {\n break;\n }\n attempts++;\n if (attempts <= retries) {\n console.log(\n `[vrt] Retry ${attempts}/${retries}: ${path.basename(job.art.path)}/${job.variantName}`,\n );\n }\n }\n\n return result;\n });\n }\n\n /**\n * Run VRT tests - alias used by the plugin API that accepts options.\n */\n async runTests(\n artFiles: ArtFileInfo[],\n baseUrl: string,\n _options?: { updateSnapshots?: boolean },\n ): Promise<VrtResult[]> {\n const results = await this.runAllTests(artFiles, baseUrl);\n if (_options?.updateSnapshots) {\n await this.updateBaselines(results);\n }\n return results;\n }\n\n /**\n * Capture screenshot and compare with baseline.\n */\n async captureAndCompare(\n art: ArtFileInfo,\n variantName: string,\n viewport: ViewportConfig,\n baseUrl: string,\n ): Promise<VrtResult> {\n return captureAndCompare(this, art, variantName, viewport, baseUrl);\n }\n\n /**\n * Get the Playwright Page for external use (e.g., a11y auditing).\n */\n async createPage(viewport: ViewportConfig): Promise<{ page: Page; context: BrowserContext }> {\n if (!this.browser) {\n throw new Error(\"VRT runner not initialized. Call init() first.\");\n }\n const context = await this.browser.newContext({\n viewport: { width: viewport.width, height: viewport.height },\n deviceScaleFactor: viewport.deviceScaleFactor ?? 1,\n });\n const page = await context.newPage();\n return { page, context };\n }\n\n /**\n * Update baseline snapshots with current screenshots.\n */\n async updateBaselines(results: VrtResult[]): Promise<number> {\n let updated = 0;\n const snapshotDir = this.options.snapshotDir;\n const currentDir = path.join(snapshotDir, \"current\");\n\n for (const result of results) {\n const currentPath = path.join(currentDir, path.basename(result.snapshotPath));\n\n if (await fileExists(currentPath)) {\n await fs.promises.copyFile(currentPath, result.snapshotPath);\n updated++;\n console.log(`[vrt] Updated: ${path.basename(result.snapshotPath)}`);\n }\n }\n\n return updated;\n }\n\n /**\n * Approve specific failed results (update their baselines).\n */\n async approveResults(results: VrtResult[], pattern?: string): Promise<number> {\n const toApprove = pattern\n ? results.filter((r) => {\n const name = `${path.basename(r.artPath, \".art.vue\")}/${r.variantName}`;\n return name.includes(pattern) || matchGlob(name, pattern);\n })\n : results.filter((r) => !r.passed && !r.error);\n\n return this.updateBaselines(toApprove);\n }\n\n /**\n * Clean orphaned snapshots (no corresponding art/variant).\n */\n async cleanOrphans(artFiles: ArtFileInfo[]): Promise<number> {\n const snapshotDir = this.options.snapshotDir;\n let cleaned = 0;\n\n try {\n const files = await fs.promises.readdir(snapshotDir);\n const validNames = new Set<string>();\n\n for (const art of artFiles) {\n const artBaseName = path.basename(art.path, \".art.vue\");\n for (const variant of art.variants) {\n if (variant.skipVrt) continue;\n for (const viewport of this.options.viewports) {\n const viewportName = viewport.name || `${viewport.width}x${viewport.height}`;\n validNames.add(`${artBaseName}--${variant.name}--${viewportName}.png`);\n }\n }\n }\n\n for (const file of files) {\n if (file.endsWith(\".png\") && !validNames.has(file)) {\n await fs.promises.unlink(path.join(snapshotDir, file));\n cleaned++;\n console.log(`[vrt] Cleaned: ${file}`);\n }\n }\n } catch {\n // Directory may not exist yet\n }\n\n return cleaned;\n }\n\n /**\n * Get VRT summary statistics.\n */\n getSummary(results: VrtResult[]): VrtSummary {\n return computeSummary(results, this.startTime);\n }\n}\n\nexport function normalizeVrtWorkerCount(workers: number | undefined): number {\n if (workers === undefined || !Number.isFinite(workers)) {\n return 1;\n }\n return Math.max(1, Math.floor(workers));\n}\n\nfunction createVrtJobs(artFiles: ArtFileInfo[], defaultViewports: ViewportConfig[]): VrtJob[] {\n const jobs: VrtJob[] = [];\n for (const art of artFiles) {\n for (const variant of art.variants) {\n if (variant.skipVrt) {\n continue;\n }\n\n const viewports = variant.args?.viewport\n ? [variant.args.viewport as ViewportConfig]\n : defaultViewports;\n\n for (const viewport of viewports) {\n jobs.push({ art, variantName: variant.name, viewport });\n }\n }\n }\n return jobs;\n}\n\nasync function runJobsWithWorkers<T>(\n jobs: readonly VrtJob[],\n workerCount: number,\n runJob: (job: VrtJob) => Promise<T | null>,\n): Promise<T[]> {\n const results = Array.from<T | null>({ length: jobs.length }, () => null);\n let nextJobIndex = 0;\n\n async function runWorker(): Promise<void> {\n while (true) {\n const jobIndex = nextJobIndex++;\n const job = jobs[jobIndex];\n if (!job) {\n return;\n }\n results[jobIndex] = await runJob(job);\n }\n }\n\n const activeWorkers = Math.min(normalizeVrtWorkerCount(workerCount), jobs.length);\n await Promise.all(Array.from({ length: activeWorkers }, () => runWorker()));\n return results.filter((result): result is T => result !== null);\n}\n","/**\n * VRT report generation for Musea.\n *\n * Generates HTML and JSON reports from VRT results for visual review\n * and CI integration.\n */\n\nimport path from \"node:path\";\n\nimport type { VrtResult, VrtSummary } from \"./types.js\";\nimport { escapeHtml } from \"./comparison.js\";\n\n/**\n * Generate VRT report in HTML format.\n * Supports side-by-side, overlay, and slider comparison modes.\n */\nexport function generateVrtReport(results: VrtResult[], summary: VrtSummary): string {\n const formatDuration = (ms: number): string => {\n if (ms < 1000) return `${ms}ms`;\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n if (minutes === 0) return `${seconds}s`;\n return `${minutes}m ${seconds % 60}s`;\n };\n\n const timestamp = new Date().toLocaleString(\"ja-JP\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>VRT Report - Musea</title>\n <style>\n :root {\n --musea-bg-primary: #0d0d0d;\n --musea-bg-secondary: #1a1815;\n --musea-bg-tertiary: #252220;\n --musea-accent: #a34828;\n --musea-accent-hover: #c45a32;\n --musea-text: #e6e9f0;\n --musea-text-muted: #7b8494;\n --musea-border: #3a3530;\n --musea-success: #4ade80;\n --musea-error: #f87171;\n --musea-info: #60a5fa;\n --musea-warning: #fbbf24;\n }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n background: var(--musea-bg-primary);\n color: var(--musea-text);\n min-height: 100vh;\n line-height: 1.5;\n }\n\n .header {\n background: var(--musea-bg-secondary);\n border-bottom: 1px solid var(--musea-border);\n padding: 1rem 2rem;\n display: flex;\n align-items: center;\n justify-content: space-between;\n position: sticky;\n top: 0;\n z-index: 100;\n }\n .header-left { display: flex; align-items: center; gap: 1rem; }\n .logo { font-size: 1.25rem; font-weight: 700; color: var(--musea-accent); }\n .header-title { color: var(--musea-text-muted); font-size: 0.875rem; }\n .header-meta { display: flex; align-items: center; gap: 1.5rem; font-size: 0.8125rem; color: var(--musea-text-muted); }\n .header-meta span { display: flex; align-items: center; gap: 0.375rem; }\n\n .main { max-width: 1400px; margin: 0 auto; padding: 2rem; }\n\n .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1rem; margin-bottom: 2rem; }\n .stat { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 8px; padding: 1.25rem; position: relative; overflow: hidden; }\n .stat::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; }\n .stat.passed::before { background: var(--musea-success); }\n .stat.failed::before { background: var(--musea-error); }\n .stat.new::before { background: var(--musea-info); }\n .stat.skipped::before { background: var(--musea-warning); }\n .stat-value { font-size: 2rem; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1; margin-bottom: 0.25rem; }\n .stat.passed .stat-value { color: var(--musea-success); }\n .stat.failed .stat-value { color: var(--musea-error); }\n .stat.new .stat-value { color: var(--musea-info); }\n .stat.skipped .stat-value { color: var(--musea-warning); }\n .stat-label { color: var(--musea-text-muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 500; }\n\n .filters { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; flex-wrap: wrap; }\n .filter-btn { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); color: var(--musea-text); padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; font-size: 0.8125rem; font-weight: 500; transition: all 0.15s ease; }\n .filter-btn:hover { background: var(--musea-bg-tertiary); border-color: var(--musea-text-muted); }\n .filter-btn.active { background: var(--musea-accent); border-color: var(--musea-accent); color: #fff; }\n .filter-btn .count { opacity: 0.7; margin-left: 0.25rem; }\n\n /* Comparison mode toggle */\n .compare-modes { display: flex; gap: 0.25rem; margin-bottom: 1.5rem; background: var(--musea-bg-secondary); border-radius: 6px; padding: 0.25rem; width: fit-content; }\n .compare-mode-btn { background: none; border: none; color: var(--musea-text-muted); padding: 0.375rem 0.75rem; border-radius: 4px; cursor: pointer; font-size: 0.75rem; font-weight: 500; transition: all 0.15s ease; }\n .compare-mode-btn.active { background: var(--musea-bg-tertiary); color: var(--musea-text); }\n\n .results { display: flex; flex-direction: column; gap: 0.75rem; }\n .result { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 8px; overflow: hidden; transition: border-color 0.15s ease; }\n .result:hover { border-color: var(--musea-text-muted); }\n .result-header { padding: 1rem 1.25rem; display: flex; justify-content: space-between; align-items: center; cursor: pointer; border-left: 3px solid transparent; background: var(--musea-bg-tertiary); }\n .result.passed .result-header { border-left-color: var(--musea-success); }\n .result.failed .result-header { border-left-color: var(--musea-error); }\n .result.new .result-header { border-left-color: var(--musea-info); }\n .result.error .result-header { border-left-color: var(--musea-warning); }\n\n .result-info { display: flex; align-items: center; gap: 1rem; }\n .result-name { font-weight: 600; font-size: 0.9375rem; }\n .result-meta { color: var(--musea-text-muted); font-size: 0.8125rem; padding: 0.125rem 0.5rem; background: var(--musea-bg-secondary); border-radius: 4px; }\n .result-badge { padding: 0.25rem 0.625rem; border-radius: 4px; font-size: 0.6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }\n .result.passed .result-badge { background: rgba(74, 222, 128, 0.15); color: var(--musea-success); }\n .result.failed .result-badge { background: rgba(248, 113, 113, 0.15); color: var(--musea-error); }\n .result.new .result-badge { background: rgba(96, 165, 250, 0.15); color: var(--musea-info); }\n .result.error .result-badge { background: rgba(251, 191, 36, 0.15); color: var(--musea-warning); }\n\n .result-body { border-top: 1px solid var(--musea-border); }\n .result-details { padding: 0.875rem 1.25rem; font-size: 0.8125rem; color: var(--musea-text-muted); font-family: 'SF Mono', 'Fira Code', monospace; background: var(--musea-bg-primary); }\n .result-details.error { color: var(--musea-error); }\n\n .result-images { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; padding: 1.25rem; background: var(--musea-bg-primary); }\n .result-images.overlay { grid-template-columns: 1fr; }\n .image-container { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 6px; overflow: hidden; }\n .image-label { padding: 0.625rem 0.875rem; font-size: 0.6875rem; font-weight: 600; color: var(--musea-text-muted); text-transform: uppercase; letter-spacing: 0.08em; background: var(--musea-bg-tertiary); border-bottom: 1px solid var(--musea-border); }\n .image-wrapper { padding: 0.5rem; background: repeating-conic-gradient(var(--musea-bg-tertiary) 0% 25%, var(--musea-bg-secondary) 0% 50%) 50% / 16px 16px; }\n .image-container img { width: 100%; height: auto; display: block; border-radius: 2px; }\n\n /* Slider comparison */\n .slider-compare { position: relative; overflow: hidden; }\n .slider-compare img { display: block; width: 100%; }\n .slider-overlay { position: absolute; top: 0; left: 0; bottom: 0; overflow: hidden; border-right: 2px solid var(--musea-accent); }\n .slider-overlay img { display: block; min-width: 100%; height: 100%; object-fit: cover; }\n\n .empty-state { text-align: center; padding: 4rem 2rem; color: var(--musea-text-muted); }\n .all-passed { background: rgba(74, 222, 128, 0.1); border: 1px solid rgba(74, 222, 128, 0.2); border-radius: 8px; padding: 1.5rem; text-align: center; margin-bottom: 1.5rem; }\n .all-passed-icon { font-size: 2.5rem; margin-bottom: 0.5rem; }\n .all-passed-text { color: var(--musea-success); font-weight: 600; }\n </style>\n</head>\n<body>\n <header class=\"header\">\n <div class=\"header-left\">\n <div class=\"logo\">Musea</div>\n <span class=\"header-title\">Visual Regression Report</span>\n </div>\n <div class=\"header-meta\">\n <span>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/>\n </svg>\n ${formatDuration(summary.duration)}\n </span>\n <span>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/>\n </svg>\n ${timestamp}\n </span>\n </div>\n </header>\n\n <main class=\"main\">\n <div class=\"summary\">\n <div class=\"stat passed\"><div class=\"stat-value\">${summary.passed}</div><div class=\"stat-label\">Passed</div></div>\n <div class=\"stat failed\"><div class=\"stat-value\">${summary.failed}</div><div class=\"stat-label\">Failed</div></div>\n <div class=\"stat new\"><div class=\"stat-value\">${summary.new}</div><div class=\"stat-label\">New</div></div>\n <div class=\"stat skipped\"><div class=\"stat-value\">${summary.skipped}</div><div class=\"stat-label\">Skipped</div></div>\n </div>\n\n ${\n summary.failed === 0 && summary.skipped === 0 && summary.total > 0\n ? `<div class=\"all-passed\">\n <div class=\"all-passed-icon\">✓</div>\n <div class=\"all-passed-text\">All ${summary.total} visual tests passed</div>\n </div>`\n : \"\"\n }\n\n <div class=\"filters\">\n <button class=\"filter-btn active\" data-filter=\"all\">All<span class=\"count\">(${summary.total})</span></button>\n <button class=\"filter-btn\" data-filter=\"failed\">Failed<span class=\"count\">(${summary.failed})</span></button>\n <button class=\"filter-btn\" data-filter=\"passed\">Passed<span class=\"count\">(${summary.passed})</span></button>\n <button class=\"filter-btn\" data-filter=\"new\">New<span class=\"count\">(${summary.new})</span></button>\n </div>\n\n <div class=\"compare-modes\">\n <button class=\"compare-mode-btn active\" data-mode=\"side-by-side\">Side by Side</button>\n <button class=\"compare-mode-btn\" data-mode=\"overlay\">Overlay</button>\n <button class=\"compare-mode-btn\" data-mode=\"slider\">Slider</button>\n </div>\n\n <div class=\"results\">\n ${\n results.length === 0\n ? `<div class=\"empty-state\"><p>No visual tests found</p></div>`\n : results\n .map((r) => {\n const status = r.error ? \"error\" : r.isNew ? \"new\" : r.passed ? \"passed\" : \"failed\";\n const badge = r.error ? \"Error\" : r.isNew ? \"New\" : r.passed ? \"Passed\" : \"Failed\";\n const artName = path.basename(r.artPath, \".art.vue\");\n const viewportName = r.viewport.name || `${r.viewport.width}×${r.viewport.height}`;\n\n let details = \"\";\n if (r.error) {\n details = `<div class=\"result-details error\">${escapeHtml(r.error)}</div>`;\n } else if (r.diffPercentage !== undefined) {\n const diffFormatted = r.diffPercentage.toFixed(3);\n const pixelsFormatted = r.diffPixels?.toLocaleString() ?? \"0\";\n const totalFormatted = r.totalPixels?.toLocaleString() ?? \"0\";\n details = `<div class=\"result-details\">diff: ${diffFormatted}% (${pixelsFormatted} / ${totalFormatted} pixels)</div>`;\n }\n\n let images = \"\";\n if (!r.error && !r.passed && r.diffPath) {\n images = `<div class=\"result-images\" data-baseline=\"file://${r.snapshotPath}\" data-current=\"file://${r.currentPath}\" data-diff=\"file://${r.diffPath}\">\n ${r.snapshotPath ? `<div class=\"image-container\"><div class=\"image-label\">Baseline</div><div class=\"image-wrapper\"><img src=\"file://${r.snapshotPath}\" alt=\"Baseline\" loading=\"lazy\" /></div></div>` : \"\"}\n ${r.currentPath ? `<div class=\"image-container\"><div class=\"image-label\">Current</div><div class=\"image-wrapper\"><img src=\"file://${r.currentPath}\" alt=\"Current\" loading=\"lazy\" /></div></div>` : \"\"}\n ${r.diffPath ? `<div class=\"image-container\"><div class=\"image-label\">Diff</div><div class=\"image-wrapper\"><img src=\"file://${r.diffPath}\" alt=\"Diff\" loading=\"lazy\" /></div></div>` : \"\"}\n </div>`;\n }\n\n const hasBody = details || images;\n\n return `<div class=\"result ${status}\" data-status=\"${status}\">\n <div class=\"result-header\">\n <div class=\"result-info\">\n <div class=\"result-name\">${escapeHtml(artName)} / ${escapeHtml(r.variantName)}</div>\n <div class=\"result-meta\">${escapeHtml(viewportName)}</div>\n </div>\n <span class=\"result-badge\">${badge}</span>\n </div>\n ${hasBody ? `<div class=\"result-body\">${details}${images}</div>` : \"\"}\n </div>`;\n })\n .join(\"\")\n }\n </div>\n </main>\n\n <script>\n // Filter buttons\n document.querySelectorAll('.filter-btn').forEach(btn => {\n btn.addEventListener('click', () => {\n document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n const filter = btn.dataset.filter;\n document.querySelectorAll('.result').forEach(result => {\n result.style.display = (filter === 'all' || result.dataset.status === filter) ? 'block' : 'none';\n });\n });\n });\n\n // Compare mode buttons\n document.querySelectorAll('.compare-mode-btn').forEach(btn => {\n btn.addEventListener('click', () => {\n document.querySelectorAll('.compare-mode-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n // Mode switching would update result-images display; this is a static report for now\n });\n });\n </script>\n</body>\n</html>`;\n\n return html;\n}\n\n/**\n * Generate VRT JSON report for CI integration.\n */\nexport function generateVrtJsonReport(results: VrtResult[], summary: VrtSummary): string {\n return JSON.stringify(\n {\n timestamp: new Date().toISOString(),\n summary,\n results: results.map((r) => ({\n art: path.basename(r.artPath, \".art.vue\"),\n variant: r.variantName,\n viewport: r.viewport.name || `${r.viewport.width}x${r.viewport.height}`,\n status: r.error ? \"error\" : r.isNew ? \"new\" : r.passed ? \"passed\" : \"failed\",\n diffPercentage: r.diffPercentage,\n error: r.error,\n })),\n },\n null,\n 2,\n );\n}\n","/**\n * Visual Regression Testing (VRT) module for Musea.\n * Uses Playwright for browser automation and pixel comparison.\n *\n * This file re-exports from the split VRT submodules for backward compatibility.\n */\n\nexport {\n MuseaVrtRunner,\n normalizeVrtWorkerCount,\n type VrtResult,\n type VrtSummary,\n type ExtendedVrtOptions,\n type PixelCompareOptions,\n} from \"./vrt/runner.js\";\n\nexport { generateVrtReport, generateVrtJsonReport } from \"./vrt/report.js\";\n\nimport { MuseaVrtRunner } from \"./vrt/runner.js\";\nexport default MuseaVrtRunner;\n"],"mappings":"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"}
|
package/dist/vrt.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { type ExtendedVrtOptions, MuseaVrtRunner, MuseaVrtRunner as default, type PixelCompareOptions, type VrtResult, type VrtSummary, generateVrtJsonReport, generateVrtReport };
|
|
1
|
+
import { a as ExtendedVrtOptions, c as VrtSummary, i as normalizeVrtWorkerCount, n as generateVrtReport, o as PixelCompareOptions, r as MuseaVrtRunner, s as VrtResult, t as generateVrtJsonReport } from "./vrt-6lGTi1kM.mjs";
|
|
2
|
+
export { type ExtendedVrtOptions, MuseaVrtRunner, MuseaVrtRunner as default, type PixelCompareOptions, type VrtResult, type VrtSummary, generateVrtJsonReport, generateVrtReport, normalizeVrtWorkerCount };
|
package/dist/vrt.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as MuseaVrtRunner, n as generateVrtJsonReport, r as generateVrtReport, t as vrt_default } from "./vrt-
|
|
2
|
-
export { MuseaVrtRunner, vrt_default as default, generateVrtJsonReport, generateVrtReport };
|
|
1
|
+
import { a as normalizeVrtWorkerCount, i as MuseaVrtRunner, n as generateVrtJsonReport, r as generateVrtReport, t as vrt_default } from "./vrt-Dr5Gs71k.mjs";
|
|
2
|
+
export { MuseaVrtRunner, vrt_default as default, generateVrtJsonReport, generateVrtReport, normalizeVrtWorkerCount };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vizejs/vite-plugin-musea",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.406.0",
|
|
4
4
|
"description": "Vite plugin for Musea - Component gallery for Vue components",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"component-gallery",
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
"access": "public"
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
-
"@vizejs/native": "0.
|
|
65
|
+
"@vizejs/native": "0.406.0",
|
|
66
66
|
"pngjs": "7.0.0"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@types/node": "25.9.2",
|
|
72
72
|
"@types/pngjs": "6.0.5",
|
|
73
73
|
"@vitejs/plugin-vue": "6.0.7",
|
|
74
|
-
"@vizejs/vite-plugin": "0.
|
|
74
|
+
"@vizejs/vite-plugin": "0.406.0",
|
|
75
75
|
"highlight.js": "11.11.1",
|
|
76
76
|
"marked": "18.0.9",
|
|
77
77
|
"marked-highlight": "2.2.4",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"vue-router": "4.5.1"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
|
-
"@vizejs/vite-plugin": "0.
|
|
87
|
+
"@vizejs/vite-plugin": "0.406.0",
|
|
88
88
|
"axe-core": "^4.7.0",
|
|
89
89
|
"playwright": "^1.40.0",
|
|
90
90
|
"vite": "^7.3.0 || ^8.0.0"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vrt-DC8PNsYN.d.mts","names":[],"sources":["../src/types/art.ts","../src/vrt/types.ts","../src/vrt/runner.ts","../src/vrt/report.ts"],"mappings":";;;;;;;UAGiB,WAAA;EACf,KAAA;EACA,WAAA;EACA,SAAA;EACA,QAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA;EAHA;EAKA,YAAA;AAAA;;;;UAMe,UAAA;EACf,IAAA;EACA,QAAA;EACA,SAAA;EACA,OAAA;EACA,IAAA,GAAO,MAAM;AAAA;;;;UAME,WAAA;EANF;EAQb,IAAA;EAFe;EAIf,QAAA,EAAU,WAAA;;EAEV,QAAA,EAAU,UAAU;EAJpB;EAMA,cAAA;EAJU;EAMV,kBAAA;EAJU;EAMV,mBAAA;EAFA;EAIA,SAAA;EAAA;EAEA,UAAA;EAEA;EAAA,WAAA;EAIA;EAFA,QAAA;EAEa;EAAb,aAAA;AAAA;;;AAUQ;UAJO,SAAA;;EAEf,IAAA;EC5Ce;ED8Cf,QAAQ;AAAA;;;AA1DV;;;AAAA,UCYiB,SAAA;EACf,OAAA;EACA,WAAA;EACA,QAAA,EAAU,cAAc;EACxB,MAAA;EACA,YAAA;EACA,WAAA;EACA,QAAA;EACA,cAAA;EACA,UAAA;EACA,WAAA;EACA,KAAA;EACA,KAAA;AAAA;;;;UAMe,UAAA;EACf,KAAA;EACA,MAAA;EACA,MAAA;EACA,GAAA;EACA,OAAA;EACA,QAAA;AAAA;;;;UAMe,kBAAA,SAA2B,UAAA;EAC1C,OAAA,GAAU,aAAA;EACV,UAAA,GAAa,gBAAA;EACb,EAAA,GAAK,QAAA;EDXL;ECaA,IAAA;AAAA;;;;UAMe,mBAAA;EDLf;ECOA,SAAA;EDPa;ECSb,SAAA;EDHwB;ECKxB,KAAA;EDHA;ECKA,SAAA;IAAc,CAAA;IAAW,CAAA;IAAW,CAAA;EAAA;AAAA;;;;;;cClCzB,cAAA;EAAA,QACH,OAAA;EAAA,QACA,OAAA;EAAA,QACA,UAAA;EAAA,QACA,EAAA;EAAA,QACA,OAAA;EAAA,QACA,SAAA;cAEI,OAAA,GAAS,kBAAA;EFfR;EEuCb,UAAA,IAAc,OAAA;EF1Cd;EE+CA,UAAA,IAAc,QAAA,CAAS,UAAA;EF7CvB;EEkDA,UAAA,IAAc,QAAA,CAAS,aAAA;EFjDhB;EEsDP,aAAA,IAAiB,gBAAA;EFtDJ;AAMf;;EEuDQ,IAAA,IAAQ,OAAA;EFjDM;;;EE0Dd,KAAA,IAAS,OAAA;EF1Df;;;EEoEM,KAAA,IAAS,OAAA;EF9Df;;;EEqEM,IAAA,IAAQ,OAAA;EF7Dd;;;EEoEM,WAAA,CAAY,QAAA,EAAU,WAAA,IAAe,OAAA,WAAkB,OAAA,CAAQ,SAAA;EF5DtD;;;EE6GT,QAAA,CACJ,QAAA,EAAU,WAAA,IACV,OAAA,UACA,QAAA;IAAa,eAAA;EAAA,IACZ,OAAA,CAAQ,SAAA;;;AD3Jb;ECsKQ,iBAAA,CACJ,GAAA,EAAK,WAAA,EACL,WAAA,UACA,QAAA,EAAU,cAAA,EACV,OAAA,WACC,OAAA,CAAQ,SAAA;;;;EAOL,UAAA,CAAW,QAAA,EAAU,cAAA,GAAiB,OAAA;IAAU,IAAA,EAAM,IAAA;IAAM,OAAA,EAAS,cAAA;EAAA;ED7K3E;;;EC4LM,eAAA,CAAgB,OAAA,EAAS,SAAA,KAAc,OAAA;EDxL7C;;;EC6MM,cAAA,CAAe,OAAA,EAAS,SAAA,IAAa,OAAA,YAAmB,OAAA;ED1MzD;AAAA;AAMP;ECkNQ,YAAA,CAAa,QAAA,EAAU,WAAA,KAAgB,OAAA;;;;EAoC7C,UAAA,CAAW,OAAA,EAAS,SAAA,KAAc,UAAA;AAAA;;;;;;;iBCvQpB,iBAAA,CAAkB,OAAA,EAAS,SAAA,IAAa,OAAA,EAAS,UAAU;;;;iBAuQ3D,qBAAA,CAAsB,OAAA,EAAS,SAAA,IAAa,OAAA,EAAS,UAAU"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vrt-DJ_qLgan.d.mts","names":[],"sources":["../src/types/vrt.ts"],"mappings":";;AAGA;;UAAiB,cAAA;EAAc;EAE7B,KAAA;EAEA;EAAA,MAAA;EAIA;EAFA,iBAAA;EAEI;EAAJ,IAAA;AAAA;;;;UAMe,UAAA;EAiBf;;;AAA0B;EAZ1B,WAAA;EAkB4B;;;;EAZ5B,SAAA;EAkBA;;;;EAZA,SAAA,GAAY,cAAc;AAAA;AAwB5B;;;AAAA,UAlBiB,aAAA;EAoBf;EAlBA,QAAA;EAsBA;EApBA,cAAA;EAsBc;EApBd,UAAA;EAoBoC;EAlBpC,YAAA;EAkBqC;EAhBrC,YAAA;EAsBuB;EApBvB,YAAA;AAAA;;;;UAMe,gBAAA;EAsBR;EApBP,YAAA;;EAEA,KAAA;;EAEA,SAAA;;EAEA,SAAA;IAAc,CAAA;IAAW,CAAA;IAAW,CAAA;EAAA;AAAA;;;;UAMrB,QAAA;;EAEf,UAAA;;EAEA,gBAAA;;EAEA,UAAA;;EAEA,OAAA;AAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"vrt-DYTmYF1X.mjs","names":[],"sources":["../src/vrt/comparison.ts","../src/vrt/utils.ts","../src/vrt/runner-comparison.ts","../src/vrt/runner.ts","../src/vrt/report.ts","../src/vrt.ts"],"sourcesContent":["/**\n * Pixel comparison utilities for VRT.\n *\n * Provides PNG reading/writing, color delta calculation using YIQ color space,\n * and anti-aliasing detection for pixel-level image comparison.\n */\n\nimport fs from \"node:fs\";\nimport { PNG } from \"pngjs\";\n\n/**\n * Read PNG file and return PNG object.\n */\nexport async function readPng(filepath: string): Promise<PNG> {\n return new Promise((resolve, reject) => {\n fs.createReadStream(filepath)\n .pipe(new PNG())\n .on(\"parsed\", function (this: PNG) {\n resolve(this);\n })\n .on(\"error\", reject);\n });\n}\n\n/**\n * Write PNG object to file.\n */\nexport async function writePng(png: PNG, filepath: string): Promise<void> {\n return new Promise((resolve, reject) => {\n png.pack().pipe(fs.createWriteStream(filepath)).on(\"finish\", resolve).on(\"error\", reject);\n });\n}\n\n/**\n * Calculate color delta using YIQ color space.\n */\nexport function colorDelta(\n r1: number,\n g1: number,\n b1: number,\n a1: number,\n r2: number,\n g2: number,\n b2: number,\n a2: number,\n): number {\n if (a1 !== 255) {\n r1 = blend(r1, 255, a1 / 255);\n g1 = blend(g1, 255, a1 / 255);\n b1 = blend(b1, 255, a1 / 255);\n }\n if (a2 !== 255) {\n r2 = blend(r2, 255, a2 / 255);\n g2 = blend(g2, 255, a2 / 255);\n b2 = blend(b2, 255, a2 / 255);\n }\n\n const y1 = r1 * 0.29889531 + g1 * 0.58662247 + b1 * 0.11448223;\n const i1 = r1 * 0.59597799 - g1 * 0.2741761 - b1 * 0.32180189;\n const q1 = r1 * 0.21147017 - g1 * 0.52261711 + b1 * 0.31114694;\n\n const y2 = r2 * 0.29889531 + g2 * 0.58662247 + b2 * 0.11448223;\n const i2 = r2 * 0.59597799 - g2 * 0.2741761 - b2 * 0.32180189;\n const q2 = r2 * 0.21147017 - g2 * 0.52261711 + b2 * 0.31114694;\n\n const dy = y1 - y2;\n const di = i1 - i2;\n const dq = q1 - q2;\n\n return dy * dy * 0.5053 + di * di * 0.299 + dq * dq * 0.1957;\n}\n\n/**\n * Blend foreground with background using alpha.\n */\nfunction blend(fg: number, bg: number, alpha: number): number {\n return bg + (fg - bg) * alpha;\n}\n\n/**\n * Check if file exists.\n */\nexport async function fileExists(filepath: string): Promise<boolean> {\n try {\n await fs.promises.access(filepath);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Simple anti-aliasing detection.\n * A pixel is likely anti-aliased if its neighbors have high contrast in opposite directions.\n */\nexport function isAntiAliased(\n img1: PNG,\n img2: PNG,\n x: number,\n y: number,\n width: number,\n height: number,\n): boolean {\n const minX = Math.max(0, x - 1);\n const maxX = Math.min(width - 1, x + 1);\n const minY = Math.max(0, y - 1);\n const maxY = Math.min(height - 1, y + 1);\n\n let zeroes = 0;\n let positives = 0;\n let negatives = 0;\n\n for (let ny = minY; ny <= maxY; ny++) {\n for (let nx = minX; nx <= maxX; nx++) {\n if (nx === x && ny === y) continue;\n const idx = (ny * width + nx) * 4;\n\n const delta = colorDelta(\n img1.data[idx],\n img1.data[idx + 1],\n img1.data[idx + 2],\n img1.data[idx + 3],\n img2.data[idx],\n img2.data[idx + 1],\n img2.data[idx + 2],\n img2.data[idx + 3],\n );\n\n if (delta === 0) {\n zeroes++;\n } else if (delta > 0) {\n positives++;\n } else {\n negatives++;\n }\n }\n }\n\n // If neighbors are mixed (some match, some differ), it's likely AA\n return zeroes > 0 && (positives > 0 || negatives > 0) && positives + negatives < 4;\n}\n\n/**\n * Simple glob matching for pattern-based filtering.\n */\nexport function matchGlob(filepath: string, pattern: string): boolean {\n const regex = pattern\n .replace(/\\./g, \"\\\\.\")\n .replace(/\\*\\*/g, \".*\")\n .replace(/\\*(?!\\*)/g, \"[^/]*\");\n return new RegExp(`^${regex}$`).test(filepath);\n}\n\n/**\n * Escape HTML special characters.\n */\nexport function escapeHtml(str: string): string {\n return str\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n","/**\n * Utility functions for VRT runner.\n *\n * Includes summary generation, URL builders, and helpers\n * used across the runner modules.\n */\n\nimport type { VrtResult, VrtSummary } from \"./types.js\";\n\n/**\n * Build URL for variant preview.\n */\nexport function buildVariantUrl(baseUrl: string, artPath: string, variantName: string): string {\n const encodedPath = encodeURIComponent(artPath);\n const encodedVariant = encodeURIComponent(variantName);\n return `${baseUrl}/__musea__/preview?art=${encodedPath}&variant=${encodedVariant}`;\n}\n\n/**\n * Compute VRT summary statistics from a list of results.\n */\nexport function computeSummary(results: VrtResult[], startTime: number): VrtSummary {\n return {\n total: results.length,\n passed: results.filter((r) => r.passed && !r.isNew).length,\n failed: results.filter((r) => !r.passed && !r.error).length,\n new: results.filter((r) => r.isNew).length,\n skipped: results.filter((r) => r.error).length,\n duration: Date.now() - startTime,\n };\n}\n","/**\n * Screenshot capture and pixel comparison logic for VRT.\n *\n * Contains the captureAndCompare flow (browser context, navigation,\n * element hiding/masking, screenshot, baseline check) and the\n * pixel-level compareImages implementation.\n */\n\nimport type { BrowserContext, Page } from \"playwright\";\nimport type { ArtFileInfo, ViewportConfig } from \"../types/index.js\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { PNG } from \"pngjs\";\n\nimport { readPng, writePng, colorDelta, isAntiAliased, fileExists } from \"./comparison.js\";\nimport type { VrtResult } from \"./types.js\";\nimport type { MuseaVrtRunner } from \"./runner.js\";\nimport { buildVariantUrl } from \"./utils.js\";\n\n/**\n * Capture screenshot and compare with baseline.\n *\n * Standalone function that operates on a MuseaVrtRunner instance.\n */\nexport async function captureAndCompare(\n runner: MuseaVrtRunner,\n art: ArtFileInfo,\n variantName: string,\n viewport: ViewportConfig,\n baseUrl: string,\n): Promise<VrtResult> {\n const browser = runner.getBrowser();\n if (!browser) {\n throw new Error(\"VRT runner not initialized. Call init() first.\");\n }\n\n const options = runner.getOptions();\n const capture = runner.getCapture();\n const comparison = runner.getComparison();\n\n const snapshotDir = options.snapshotDir;\n const artBaseName = path.basename(art.path, \".art.vue\");\n const viewportName = viewport.name || `${viewport.width}x${viewport.height}`;\n const snapshotName = `${artBaseName}--${variantName}--${viewportName}.png`;\n const snapshotPath = path.join(snapshotDir, snapshotName);\n const currentPath = path.join(snapshotDir, \"current\", snapshotName);\n const diffPath = path.join(snapshotDir, \"diff\", snapshotName);\n\n // Ensure directories exist\n await fs.promises.mkdir(path.dirname(snapshotPath), { recursive: true });\n await fs.promises.mkdir(path.join(snapshotDir, \"current\"), { recursive: true });\n await fs.promises.mkdir(path.join(snapshotDir, \"diff\"), { recursive: true });\n\n let context: BrowserContext | null = null;\n let page: Page | null = null;\n\n try {\n context = await browser.newContext({\n viewport: {\n width: viewport.width,\n height: viewport.height,\n },\n deviceScaleFactor: viewport.deviceScaleFactor ?? 1,\n });\n page = await context.newPage();\n\n // Navigate to variant preview URL\n const variantUrl = buildVariantUrl(baseUrl, art.path, variantName);\n const waitUntil = capture.waitForNetwork ? (\"networkidle\" as const) : (\"load\" as const);\n await page.goto(variantUrl, { waitUntil });\n\n // Wait for content to render\n await page.waitForSelector(capture.waitSelector, { timeout: 10000 });\n\n // Additional wait for animations to settle\n await page.waitForTimeout(capture.settleTime);\n\n // Hide elements before capture\n if (capture.hideElements.length > 0) {\n for (const selector of capture.hideElements) {\n await page.evaluate((sel) => {\n document.querySelectorAll(sel).forEach((el) => {\n (el as HTMLElement).style.visibility = \"hidden\";\n });\n }, selector);\n }\n }\n\n // Mask elements before capture (replace with colored box)\n if (capture.maskElements.length > 0) {\n for (const selector of capture.maskElements) {\n await page.evaluate((sel) => {\n document.querySelectorAll(sel).forEach((el) => {\n const htmlEl = el as HTMLElement;\n htmlEl.style.background = \"#ff00ff\";\n htmlEl.style.color = \"transparent\";\n htmlEl.innerHTML = \"\";\n });\n }, selector);\n }\n }\n\n // Take screenshot\n await page.screenshot({\n path: currentPath,\n fullPage: capture.fullPage,\n });\n\n // Check if baseline exists\n const hasBaseline = await fileExists(snapshotPath);\n\n if (!hasBaseline) {\n // First run - save as baseline\n await fs.promises.copyFile(currentPath, snapshotPath);\n return {\n artPath: art.path,\n variantName,\n viewport,\n passed: true,\n snapshotPath,\n currentPath,\n isNew: true,\n };\n }\n\n // Compare images using pixel comparison\n const comparisonResult = await compareImages(snapshotPath, currentPath, diffPath, comparison);\n\n const passed = comparisonResult.diffPercentage <= options.threshold;\n\n return {\n artPath: art.path,\n variantName,\n viewport,\n passed,\n snapshotPath,\n currentPath,\n diffPath: passed ? undefined : diffPath,\n diffPercentage: comparisonResult.diffPercentage,\n diffPixels: comparisonResult.diffPixels,\n totalPixels: comparisonResult.totalPixels,\n };\n } catch (error) {\n return {\n artPath: art.path,\n variantName,\n viewport,\n passed: false,\n snapshotPath,\n error: error instanceof Error ? error.message : String(error),\n };\n } finally {\n if (page) await page.close();\n if (context) await context.close();\n }\n}\n\n/**\n * Compare two PNG images and generate a diff image.\n * Returns pixel difference statistics.\n */\nexport async function compareImages(\n baselinePath: string,\n currentPath: string,\n diffPath: string,\n comparison: {\n antiAliasing?: boolean;\n alpha?: boolean;\n diffColor?: { r: number; g: number; b: number };\n },\n): Promise<{ diffPixels: number; totalPixels: number; diffPercentage: number }> {\n const baseline = await readPng(baselinePath);\n const current = await readPng(currentPath);\n\n // Handle size mismatch\n if (baseline.width !== current.width || baseline.height !== current.height) {\n const width = Math.max(baseline.width, current.width);\n const height = Math.max(baseline.height, current.height);\n const diff = new PNG({ width, height });\n\n // Fill with red to indicate size mismatch\n for (let i = 0; i < diff.data.length; i += 4) {\n diff.data[i] = 255; // R\n diff.data[i + 1] = 0; // G\n diff.data[i + 2] = 0; // B\n diff.data[i + 3] = 255; // A\n }\n\n await writePng(diff, diffPath);\n\n return {\n diffPixels: width * height,\n totalPixels: width * height,\n diffPercentage: 100,\n };\n }\n\n const width = baseline.width;\n const height = baseline.height;\n const totalPixels = width * height;\n const diff = new PNG({ width, height });\n\n const useAntiAliasing = comparison.antiAliasing ?? true;\n const useAlpha = comparison.alpha ?? true;\n const diffColor = comparison.diffColor ?? { r: 255, g: 0, b: 0 };\n\n // Pixel comparison\n let diffPixels = 0;\n const threshold = 0.1; // Color difference threshold\n\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) {\n const idx = (y * width + x) * 4;\n\n const r1 = baseline.data[idx];\n const g1 = baseline.data[idx + 1];\n const b1 = baseline.data[idx + 2];\n const a1 = useAlpha ? baseline.data[idx + 3] : 255;\n\n const r2 = current.data[idx];\n const g2 = current.data[idx + 1];\n const b2 = current.data[idx + 2];\n const a2 = useAlpha ? current.data[idx + 3] : 255;\n\n // Calculate color difference using YIQ color space\n const delta = colorDelta(r1, g1, b1, a1, r2, g2, b2, a2);\n\n if (delta > threshold * 255 * 255) {\n // Anti-aliasing detection: check if pixel is likely AA\n if (useAntiAliasing && isAntiAliased(baseline, current, x, y, width, height)) {\n // Mark as AA (yellow)\n diffPixels++;\n diff.data[idx] = 255;\n diff.data[idx + 1] = 200;\n diff.data[idx + 2] = 0;\n diff.data[idx + 3] = 128;\n } else {\n // Mark as different\n diffPixels++;\n diff.data[idx] = diffColor.r;\n diff.data[idx + 1] = diffColor.g;\n diff.data[idx + 2] = diffColor.b;\n diff.data[idx + 3] = 255;\n }\n } else {\n // Grayscale for matching pixels\n const gray = Math.round((r2 + g2 + b2) / 3);\n diff.data[idx] = gray;\n diff.data[idx + 1] = gray;\n diff.data[idx + 2] = gray;\n diff.data[idx + 3] = 128; // Semi-transparent\n }\n }\n }\n\n // Only write diff if there are differences\n if (diffPixels > 0) {\n await writePng(diff, diffPath);\n }\n\n const diffPercentage = (diffPixels / totalPixels) * 100;\n\n return {\n diffPixels,\n totalPixels,\n diffPercentage,\n };\n}\n","/**\n * VRT runner using Playwright for browser automation.\n *\n * Manages browser lifecycle, screenshot capture, and baseline comparison\n * for visual regression testing of Musea art file variants.\n */\n\nimport type { Browser, BrowserContext, Page } from \"playwright\";\nimport type {\n ArtFileInfo,\n VrtOptions,\n ViewportConfig,\n CaptureConfig,\n ComparisonConfig,\n CiConfig,\n} from \"../types/index.js\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { fileExists, matchGlob } from \"./comparison.js\";\nimport { captureAndCompare } from \"./runner-comparison.js\";\nimport { computeSummary } from \"./utils.js\";\n\nexport type { VrtResult, VrtSummary, ExtendedVrtOptions, PixelCompareOptions } from \"./types.js\";\n\nimport type { VrtResult, VrtSummary, ExtendedVrtOptions } from \"./types.js\";\n\n/**\n * VRT runner using Playwright.\n */\nexport class MuseaVrtRunner {\n private options: Required<VrtOptions>;\n private capture: Required<CaptureConfig>;\n private comparison: ComparisonConfig;\n private ci: CiConfig;\n private browser: Browser | null = null;\n private startTime: number = 0;\n\n constructor(options: ExtendedVrtOptions = {}) {\n this.options = {\n snapshotDir: options.snapshotDir ?? \".vize/snapshots\",\n threshold: options.threshold ?? 0.1,\n viewports: options.viewports ?? [\n { width: 1280, height: 720, name: \"desktop\" },\n { width: 375, height: 667, name: \"mobile\" },\n ],\n };\n this.capture = {\n fullPage: options.capture?.fullPage ?? false,\n waitForNetwork: options.capture?.waitForNetwork ?? true,\n settleTime: options.capture?.settleTime ?? 100,\n waitSelector: options.capture?.waitSelector ?? \".musea-variant\",\n hideElements: options.capture?.hideElements ?? [],\n maskElements: options.capture?.maskElements ?? [],\n };\n this.comparison = options.comparison ?? {};\n this.ci = options.ci ?? {};\n }\n\n // --- Internal accessors used by runner-comparison ---\n\n /** @internal */\n getBrowser(): Browser | null {\n return this.browser;\n }\n\n /** @internal */\n getOptions(): Required<VrtOptions> {\n return this.options;\n }\n\n /** @internal */\n getCapture(): Required<CaptureConfig> {\n return this.capture;\n }\n\n /** @internal */\n getComparison(): ComparisonConfig {\n return this.comparison;\n }\n\n /**\n * Initialize Playwright browser.\n */\n async init(): Promise<void> {\n const { chromium } = await import(\"playwright\");\n this.browser = await chromium.launch({ headless: true });\n this.startTime = Date.now();\n }\n\n /**\n * Close browser and cleanup.\n */\n async close(): Promise<void> {\n if (this.browser) {\n await this.browser.close();\n this.browser = null;\n }\n }\n\n /**\n * Alias for init() - used by the plugin API.\n */\n async start(): Promise<void> {\n return this.init();\n }\n\n /**\n * Alias for close() - used by the plugin API.\n */\n async stop(): Promise<void> {\n return this.close();\n }\n\n /**\n * Run VRT tests for all Art files.\n */\n async runAllTests(artFiles: ArtFileInfo[], baseUrl: string): Promise<VrtResult[]> {\n if (!this.browser) {\n throw new Error(\"VRT runner not initialized. Call init() first.\");\n }\n\n const results: VrtResult[] = [];\n const retries = this.ci.retries ?? 0;\n\n for (const art of artFiles) {\n for (const variant of art.variants) {\n if (variant.skipVrt) {\n continue;\n }\n\n // Determine viewports: use per-variant viewport if defined, else global viewports\n const viewports = variant.args?.viewport\n ? [variant.args.viewport as ViewportConfig]\n : this.options.viewports;\n\n for (const viewport of viewports) {\n let result: VrtResult | null = null;\n let attempts = 0;\n\n while (attempts <= retries) {\n result = await this.captureAndCompare(art, variant.name, viewport, baseUrl);\n if (result.passed || result.isNew || !result.error) {\n break;\n }\n attempts++;\n if (attempts <= retries) {\n console.log(\n `[vrt] Retry ${attempts}/${retries}: ${path.basename(art.path)}/${variant.name}`,\n );\n }\n }\n\n if (result) {\n results.push(result);\n }\n }\n }\n }\n\n return results;\n }\n\n /**\n * Run VRT tests - alias used by the plugin API that accepts options.\n */\n async runTests(\n artFiles: ArtFileInfo[],\n baseUrl: string,\n _options?: { updateSnapshots?: boolean },\n ): Promise<VrtResult[]> {\n const results = await this.runAllTests(artFiles, baseUrl);\n if (_options?.updateSnapshots) {\n await this.updateBaselines(results);\n }\n return results;\n }\n\n /**\n * Capture screenshot and compare with baseline.\n */\n async captureAndCompare(\n art: ArtFileInfo,\n variantName: string,\n viewport: ViewportConfig,\n baseUrl: string,\n ): Promise<VrtResult> {\n return captureAndCompare(this, art, variantName, viewport, baseUrl);\n }\n\n /**\n * Get the Playwright Page for external use (e.g., a11y auditing).\n */\n async createPage(viewport: ViewportConfig): Promise<{ page: Page; context: BrowserContext }> {\n if (!this.browser) {\n throw new Error(\"VRT runner not initialized. Call init() first.\");\n }\n const context = await this.browser.newContext({\n viewport: { width: viewport.width, height: viewport.height },\n deviceScaleFactor: viewport.deviceScaleFactor ?? 1,\n });\n const page = await context.newPage();\n return { page, context };\n }\n\n /**\n * Update baseline snapshots with current screenshots.\n */\n async updateBaselines(results: VrtResult[]): Promise<number> {\n let updated = 0;\n const snapshotDir = this.options.snapshotDir;\n const currentDir = path.join(snapshotDir, \"current\");\n\n for (const result of results) {\n const currentPath = path.join(currentDir, path.basename(result.snapshotPath));\n\n if (await fileExists(currentPath)) {\n await fs.promises.copyFile(currentPath, result.snapshotPath);\n updated++;\n console.log(`[vrt] Updated: ${path.basename(result.snapshotPath)}`);\n }\n }\n\n return updated;\n }\n\n /**\n * Approve specific failed results (update their baselines).\n */\n async approveResults(results: VrtResult[], pattern?: string): Promise<number> {\n const toApprove = pattern\n ? results.filter((r) => {\n const name = `${path.basename(r.artPath, \".art.vue\")}/${r.variantName}`;\n return name.includes(pattern) || matchGlob(name, pattern);\n })\n : results.filter((r) => !r.passed && !r.error);\n\n return this.updateBaselines(toApprove);\n }\n\n /**\n * Clean orphaned snapshots (no corresponding art/variant).\n */\n async cleanOrphans(artFiles: ArtFileInfo[]): Promise<number> {\n const snapshotDir = this.options.snapshotDir;\n let cleaned = 0;\n\n try {\n const files = await fs.promises.readdir(snapshotDir);\n const validNames = new Set<string>();\n\n for (const art of artFiles) {\n const artBaseName = path.basename(art.path, \".art.vue\");\n for (const variant of art.variants) {\n if (variant.skipVrt) continue;\n for (const viewport of this.options.viewports) {\n const viewportName = viewport.name || `${viewport.width}x${viewport.height}`;\n validNames.add(`${artBaseName}--${variant.name}--${viewportName}.png`);\n }\n }\n }\n\n for (const file of files) {\n if (file.endsWith(\".png\") && !validNames.has(file)) {\n await fs.promises.unlink(path.join(snapshotDir, file));\n cleaned++;\n console.log(`[vrt] Cleaned: ${file}`);\n }\n }\n } catch {\n // Directory may not exist yet\n }\n\n return cleaned;\n }\n\n /**\n * Get VRT summary statistics.\n */\n getSummary(results: VrtResult[]): VrtSummary {\n return computeSummary(results, this.startTime);\n }\n}\n","/**\n * VRT report generation for Musea.\n *\n * Generates HTML and JSON reports from VRT results for visual review\n * and CI integration.\n */\n\nimport path from \"node:path\";\n\nimport type { VrtResult, VrtSummary } from \"./types.js\";\nimport { escapeHtml } from \"./comparison.js\";\n\n/**\n * Generate VRT report in HTML format.\n * Supports side-by-side, overlay, and slider comparison modes.\n */\nexport function generateVrtReport(results: VrtResult[], summary: VrtSummary): string {\n const formatDuration = (ms: number): string => {\n if (ms < 1000) return `${ms}ms`;\n const seconds = Math.floor(ms / 1000);\n const minutes = Math.floor(seconds / 60);\n if (minutes === 0) return `${seconds}s`;\n return `${minutes}m ${seconds % 60}s`;\n };\n\n const timestamp = new Date().toLocaleString(\"ja-JP\", {\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n\n const html = `<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <title>VRT Report - Musea</title>\n <style>\n :root {\n --musea-bg-primary: #0d0d0d;\n --musea-bg-secondary: #1a1815;\n --musea-bg-tertiary: #252220;\n --musea-accent: #a34828;\n --musea-accent-hover: #c45a32;\n --musea-text: #e6e9f0;\n --musea-text-muted: #7b8494;\n --musea-border: #3a3530;\n --musea-success: #4ade80;\n --musea-error: #f87171;\n --musea-info: #60a5fa;\n --musea-warning: #fbbf24;\n }\n * { box-sizing: border-box; margin: 0; padding: 0; }\n body {\n font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;\n background: var(--musea-bg-primary);\n color: var(--musea-text);\n min-height: 100vh;\n line-height: 1.5;\n }\n\n .header {\n background: var(--musea-bg-secondary);\n border-bottom: 1px solid var(--musea-border);\n padding: 1rem 2rem;\n display: flex;\n align-items: center;\n justify-content: space-between;\n position: sticky;\n top: 0;\n z-index: 100;\n }\n .header-left { display: flex; align-items: center; gap: 1rem; }\n .logo { font-size: 1.25rem; font-weight: 700; color: var(--musea-accent); }\n .header-title { color: var(--musea-text-muted); font-size: 0.875rem; }\n .header-meta { display: flex; align-items: center; gap: 1.5rem; font-size: 0.8125rem; color: var(--musea-text-muted); }\n .header-meta span { display: flex; align-items: center; gap: 0.375rem; }\n\n .main { max-width: 1400px; margin: 0 auto; padding: 2rem; }\n\n .summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 1rem; margin-bottom: 2rem; }\n .stat { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 8px; padding: 1.25rem; position: relative; overflow: hidden; }\n .stat::before { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; }\n .stat.passed::before { background: var(--musea-success); }\n .stat.failed::before { background: var(--musea-error); }\n .stat.new::before { background: var(--musea-info); }\n .stat.skipped::before { background: var(--musea-warning); }\n .stat-value { font-size: 2rem; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1; margin-bottom: 0.25rem; }\n .stat.passed .stat-value { color: var(--musea-success); }\n .stat.failed .stat-value { color: var(--musea-error); }\n .stat.new .stat-value { color: var(--musea-info); }\n .stat.skipped .stat-value { color: var(--musea-warning); }\n .stat-label { color: var(--musea-text-muted); font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.08em; font-weight: 500; }\n\n .filters { display: flex; gap: 0.5rem; margin-bottom: 1.5rem; flex-wrap: wrap; }\n .filter-btn { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); color: var(--musea-text); padding: 0.5rem 1rem; border-radius: 6px; cursor: pointer; font-size: 0.8125rem; font-weight: 500; transition: all 0.15s ease; }\n .filter-btn:hover { background: var(--musea-bg-tertiary); border-color: var(--musea-text-muted); }\n .filter-btn.active { background: var(--musea-accent); border-color: var(--musea-accent); color: #fff; }\n .filter-btn .count { opacity: 0.7; margin-left: 0.25rem; }\n\n /* Comparison mode toggle */\n .compare-modes { display: flex; gap: 0.25rem; margin-bottom: 1.5rem; background: var(--musea-bg-secondary); border-radius: 6px; padding: 0.25rem; width: fit-content; }\n .compare-mode-btn { background: none; border: none; color: var(--musea-text-muted); padding: 0.375rem 0.75rem; border-radius: 4px; cursor: pointer; font-size: 0.75rem; font-weight: 500; transition: all 0.15s ease; }\n .compare-mode-btn.active { background: var(--musea-bg-tertiary); color: var(--musea-text); }\n\n .results { display: flex; flex-direction: column; gap: 0.75rem; }\n .result { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 8px; overflow: hidden; transition: border-color 0.15s ease; }\n .result:hover { border-color: var(--musea-text-muted); }\n .result-header { padding: 1rem 1.25rem; display: flex; justify-content: space-between; align-items: center; cursor: pointer; border-left: 3px solid transparent; background: var(--musea-bg-tertiary); }\n .result.passed .result-header { border-left-color: var(--musea-success); }\n .result.failed .result-header { border-left-color: var(--musea-error); }\n .result.new .result-header { border-left-color: var(--musea-info); }\n .result.error .result-header { border-left-color: var(--musea-warning); }\n\n .result-info { display: flex; align-items: center; gap: 1rem; }\n .result-name { font-weight: 600; font-size: 0.9375rem; }\n .result-meta { color: var(--musea-text-muted); font-size: 0.8125rem; padding: 0.125rem 0.5rem; background: var(--musea-bg-secondary); border-radius: 4px; }\n .result-badge { padding: 0.25rem 0.625rem; border-radius: 4px; font-size: 0.6875rem; font-weight: 600; text-transform: uppercase; letter-spacing: 0.04em; }\n .result.passed .result-badge { background: rgba(74, 222, 128, 0.15); color: var(--musea-success); }\n .result.failed .result-badge { background: rgba(248, 113, 113, 0.15); color: var(--musea-error); }\n .result.new .result-badge { background: rgba(96, 165, 250, 0.15); color: var(--musea-info); }\n .result.error .result-badge { background: rgba(251, 191, 36, 0.15); color: var(--musea-warning); }\n\n .result-body { border-top: 1px solid var(--musea-border); }\n .result-details { padding: 0.875rem 1.25rem; font-size: 0.8125rem; color: var(--musea-text-muted); font-family: 'SF Mono', 'Fira Code', monospace; background: var(--musea-bg-primary); }\n .result-details.error { color: var(--musea-error); }\n\n .result-images { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1rem; padding: 1.25rem; background: var(--musea-bg-primary); }\n .result-images.overlay { grid-template-columns: 1fr; }\n .image-container { background: var(--musea-bg-secondary); border: 1px solid var(--musea-border); border-radius: 6px; overflow: hidden; }\n .image-label { padding: 0.625rem 0.875rem; font-size: 0.6875rem; font-weight: 600; color: var(--musea-text-muted); text-transform: uppercase; letter-spacing: 0.08em; background: var(--musea-bg-tertiary); border-bottom: 1px solid var(--musea-border); }\n .image-wrapper { padding: 0.5rem; background: repeating-conic-gradient(var(--musea-bg-tertiary) 0% 25%, var(--musea-bg-secondary) 0% 50%) 50% / 16px 16px; }\n .image-container img { width: 100%; height: auto; display: block; border-radius: 2px; }\n\n /* Slider comparison */\n .slider-compare { position: relative; overflow: hidden; }\n .slider-compare img { display: block; width: 100%; }\n .slider-overlay { position: absolute; top: 0; left: 0; bottom: 0; overflow: hidden; border-right: 2px solid var(--musea-accent); }\n .slider-overlay img { display: block; min-width: 100%; height: 100%; object-fit: cover; }\n\n .empty-state { text-align: center; padding: 4rem 2rem; color: var(--musea-text-muted); }\n .all-passed { background: rgba(74, 222, 128, 0.1); border: 1px solid rgba(74, 222, 128, 0.2); border-radius: 8px; padding: 1.5rem; text-align: center; margin-bottom: 1.5rem; }\n .all-passed-icon { font-size: 2.5rem; margin-bottom: 0.5rem; }\n .all-passed-text { color: var(--musea-success); font-weight: 600; }\n </style>\n</head>\n<body>\n <header class=\"header\">\n <div class=\"header-left\">\n <div class=\"logo\">Musea</div>\n <span class=\"header-title\">Visual Regression Report</span>\n </div>\n <div class=\"header-meta\">\n <span>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><polyline points=\"12 6 12 12 16 14\"/>\n </svg>\n ${formatDuration(summary.duration)}\n </span>\n <span>\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/><line x1=\"16\" y1=\"2\" x2=\"16\" y2=\"6\"/><line x1=\"8\" y1=\"2\" x2=\"8\" y2=\"6\"/><line x1=\"3\" y1=\"10\" x2=\"21\" y2=\"10\"/>\n </svg>\n ${timestamp}\n </span>\n </div>\n </header>\n\n <main class=\"main\">\n <div class=\"summary\">\n <div class=\"stat passed\"><div class=\"stat-value\">${summary.passed}</div><div class=\"stat-label\">Passed</div></div>\n <div class=\"stat failed\"><div class=\"stat-value\">${summary.failed}</div><div class=\"stat-label\">Failed</div></div>\n <div class=\"stat new\"><div class=\"stat-value\">${summary.new}</div><div class=\"stat-label\">New</div></div>\n <div class=\"stat skipped\"><div class=\"stat-value\">${summary.skipped}</div><div class=\"stat-label\">Skipped</div></div>\n </div>\n\n ${\n summary.failed === 0 && summary.skipped === 0 && summary.total > 0\n ? `<div class=\"all-passed\">\n <div class=\"all-passed-icon\">✓</div>\n <div class=\"all-passed-text\">All ${summary.total} visual tests passed</div>\n </div>`\n : \"\"\n }\n\n <div class=\"filters\">\n <button class=\"filter-btn active\" data-filter=\"all\">All<span class=\"count\">(${summary.total})</span></button>\n <button class=\"filter-btn\" data-filter=\"failed\">Failed<span class=\"count\">(${summary.failed})</span></button>\n <button class=\"filter-btn\" data-filter=\"passed\">Passed<span class=\"count\">(${summary.passed})</span></button>\n <button class=\"filter-btn\" data-filter=\"new\">New<span class=\"count\">(${summary.new})</span></button>\n </div>\n\n <div class=\"compare-modes\">\n <button class=\"compare-mode-btn active\" data-mode=\"side-by-side\">Side by Side</button>\n <button class=\"compare-mode-btn\" data-mode=\"overlay\">Overlay</button>\n <button class=\"compare-mode-btn\" data-mode=\"slider\">Slider</button>\n </div>\n\n <div class=\"results\">\n ${\n results.length === 0\n ? `<div class=\"empty-state\"><p>No visual tests found</p></div>`\n : results\n .map((r) => {\n const status = r.error ? \"error\" : r.isNew ? \"new\" : r.passed ? \"passed\" : \"failed\";\n const badge = r.error ? \"Error\" : r.isNew ? \"New\" : r.passed ? \"Passed\" : \"Failed\";\n const artName = path.basename(r.artPath, \".art.vue\");\n const viewportName = r.viewport.name || `${r.viewport.width}×${r.viewport.height}`;\n\n let details = \"\";\n if (r.error) {\n details = `<div class=\"result-details error\">${escapeHtml(r.error)}</div>`;\n } else if (r.diffPercentage !== undefined) {\n const diffFormatted = r.diffPercentage.toFixed(3);\n const pixelsFormatted = r.diffPixels?.toLocaleString() ?? \"0\";\n const totalFormatted = r.totalPixels?.toLocaleString() ?? \"0\";\n details = `<div class=\"result-details\">diff: ${diffFormatted}% (${pixelsFormatted} / ${totalFormatted} pixels)</div>`;\n }\n\n let images = \"\";\n if (!r.error && !r.passed && r.diffPath) {\n images = `<div class=\"result-images\" data-baseline=\"file://${r.snapshotPath}\" data-current=\"file://${r.currentPath}\" data-diff=\"file://${r.diffPath}\">\n ${r.snapshotPath ? `<div class=\"image-container\"><div class=\"image-label\">Baseline</div><div class=\"image-wrapper\"><img src=\"file://${r.snapshotPath}\" alt=\"Baseline\" loading=\"lazy\" /></div></div>` : \"\"}\n ${r.currentPath ? `<div class=\"image-container\"><div class=\"image-label\">Current</div><div class=\"image-wrapper\"><img src=\"file://${r.currentPath}\" alt=\"Current\" loading=\"lazy\" /></div></div>` : \"\"}\n ${r.diffPath ? `<div class=\"image-container\"><div class=\"image-label\">Diff</div><div class=\"image-wrapper\"><img src=\"file://${r.diffPath}\" alt=\"Diff\" loading=\"lazy\" /></div></div>` : \"\"}\n </div>`;\n }\n\n const hasBody = details || images;\n\n return `<div class=\"result ${status}\" data-status=\"${status}\">\n <div class=\"result-header\">\n <div class=\"result-info\">\n <div class=\"result-name\">${escapeHtml(artName)} / ${escapeHtml(r.variantName)}</div>\n <div class=\"result-meta\">${escapeHtml(viewportName)}</div>\n </div>\n <span class=\"result-badge\">${badge}</span>\n </div>\n ${hasBody ? `<div class=\"result-body\">${details}${images}</div>` : \"\"}\n </div>`;\n })\n .join(\"\")\n }\n </div>\n </main>\n\n <script>\n // Filter buttons\n document.querySelectorAll('.filter-btn').forEach(btn => {\n btn.addEventListener('click', () => {\n document.querySelectorAll('.filter-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n const filter = btn.dataset.filter;\n document.querySelectorAll('.result').forEach(result => {\n result.style.display = (filter === 'all' || result.dataset.status === filter) ? 'block' : 'none';\n });\n });\n });\n\n // Compare mode buttons\n document.querySelectorAll('.compare-mode-btn').forEach(btn => {\n btn.addEventListener('click', () => {\n document.querySelectorAll('.compare-mode-btn').forEach(b => b.classList.remove('active'));\n btn.classList.add('active');\n // Mode switching would update result-images display; this is a static report for now\n });\n });\n </script>\n</body>\n</html>`;\n\n return html;\n}\n\n/**\n * Generate VRT JSON report for CI integration.\n */\nexport function generateVrtJsonReport(results: VrtResult[], summary: VrtSummary): string {\n return JSON.stringify(\n {\n timestamp: new Date().toISOString(),\n summary,\n results: results.map((r) => ({\n art: path.basename(r.artPath, \".art.vue\"),\n variant: r.variantName,\n viewport: r.viewport.name || `${r.viewport.width}x${r.viewport.height}`,\n status: r.error ? \"error\" : r.isNew ? \"new\" : r.passed ? \"passed\" : \"failed\",\n diffPercentage: r.diffPercentage,\n error: r.error,\n })),\n },\n null,\n 2,\n );\n}\n","/**\n * Visual Regression Testing (VRT) module for Musea.\n * Uses Playwright for browser automation and pixel comparison.\n *\n * This file re-exports from the split VRT submodules for backward compatibility.\n */\n\nexport {\n MuseaVrtRunner,\n type VrtResult,\n type VrtSummary,\n type ExtendedVrtOptions,\n type PixelCompareOptions,\n} from \"./vrt/runner.js\";\n\nexport { generateVrtReport, generateVrtJsonReport } from \"./vrt/report.js\";\n\nimport { MuseaVrtRunner } from \"./vrt/runner.js\";\nexport default MuseaVrtRunner;\n"],"mappings":"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"}
|