vitest-plugin-vis 5.0.0 → 5.1.1

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.
Files changed (109) hide show
  1. package/dist/auto_snapshots/_extract_auto_snapshot_options.mjs +1 -1
  2. package/dist/auto_snapshots/_extract_auto_snapshot_options.mjs.map +1 -1
  3. package/dist/client/external/vitest/vitest_suite_proxy.d.mts +0 -1
  4. package/dist/client/snapshot/compare_image_snapshot.logic.mjs +2 -1
  5. package/dist/client/snapshot/compare_image_snapshot.logic.mjs.map +1 -1
  6. package/dist/client/snapshot/compare_image_snapshot.mjs +1 -1
  7. package/dist/client/snapshot/compare_image_snapshot.mjs.map +1 -1
  8. package/dist/client/suite/auto_snapshot_matcher.d.mts +2 -2
  9. package/dist/client/suite/auto_snapshot_matcher.d.mts.map +1 -1
  10. package/dist/client/suite/auto_snapshot_matcher.mjs +5 -3
  11. package/dist/client/suite/auto_snapshot_matcher.mjs.map +1 -1
  12. package/dist/config/types.d.mts +14 -0
  13. package/dist/{client-api.d.mts → exports/client-api.d.mts} +12 -12
  14. package/dist/exports/client-api.mjs +11 -0
  15. package/dist/exports/config.d.mts +5 -0
  16. package/dist/exports/config.mjs +4 -0
  17. package/dist/exports/presets/auto.mjs +9 -0
  18. package/dist/exports/presets/auto.mjs.map +1 -0
  19. package/dist/exports/presets/manual.mjs +9 -0
  20. package/dist/exports/presets/manual.mjs.map +1 -0
  21. package/dist/exports/server-utils.d.mts +5 -0
  22. package/dist/exports/server-utils.mjs +6 -0
  23. package/dist/exports/setup-api.d.mts +4 -0
  24. package/dist/exports/setup-api.mjs +5 -0
  25. package/dist/exports/setup.d.mts +3 -0
  26. package/dist/exports/setup.mjs +4 -0
  27. package/dist/exports/testing/node.d.mts +4 -0
  28. package/dist/exports/testing/node.mjs +5 -0
  29. package/dist/exports/testing.d.mts +2 -0
  30. package/dist/exports/testing.mjs +3 -0
  31. package/dist/server/commands/prepare_image_snapshot_comparison.mjs.map +1 -1
  32. package/dist/server/commands/prepare_page_image_snapshot_comparison.mjs.map +1 -1
  33. package/dist/server/snapshot_path.d.mts +0 -1
  34. package/dist/server/snapshot_path.d.mts.map +1 -1
  35. package/dist/server/snapshot_path_limits.d.mts +28 -0
  36. package/dist/server/snapshot_path_limits.d.mts.map +1 -0
  37. package/dist/server/snapshot_path_limits.mjs +92 -0
  38. package/dist/server/snapshot_path_limits.mjs.map +1 -0
  39. package/dist/server/suite.mjs +8 -2
  40. package/dist/server/suite.mjs.map +1 -1
  41. package/dist/server/testing/stubBrowserCommandContext.mjs +9 -0
  42. package/dist/server/testing/stubBrowserCommandContext.mjs.map +1 -0
  43. package/dist/server/testing/stubSuite.d.mts +17 -0
  44. package/dist/server/testing/stubSuite.d.mts.map +1 -0
  45. package/dist/server/testing/stubSuite.mjs +23 -0
  46. package/dist/server/testing/stubSuite.mjs.map +1 -0
  47. package/dist/server/testing/stubUserConfig.mjs +8 -0
  48. package/dist/server/testing/stubUserConfig.mjs.map +1 -0
  49. package/dist/server/vis_options.d.mts +120 -0
  50. package/dist/server/vis_options.d.mts.map +1 -0
  51. package/dist/server/vis_options.mjs +7 -1
  52. package/dist/server/vis_options.mjs.map +1 -1
  53. package/dist/server/vis_server_context.logic.mjs +37 -10
  54. package/dist/server/vis_server_context.logic.mjs.map +1 -1
  55. package/dist/server/vis_server_context.types.d.mts +32 -1
  56. package/dist/server/vis_server_context.types.d.mts.map +1 -0
  57. package/dist/setup/create_vis.mjs +6 -6
  58. package/dist/setup/create_vis.mjs.map +1 -1
  59. package/dist/shared/types.d.mts +21 -1
  60. package/dist/shared/types.d.mts.map +1 -1
  61. package/package.json +13 -36
  62. package/readme.md +6 -0
  63. package/src/auto_snapshots/_extract_auto_snapshot_options.ts +1 -1
  64. package/src/client/expect/to_match_image_snapshot.mdx +24 -1
  65. package/src/client/page/to_match_image_snapshot.mdx +23 -1
  66. package/src/client/snapshot/compare_image_snapshot.logic.ts +1 -0
  67. package/src/client/snapshot/compare_image_snapshot.ts +2 -2
  68. package/src/client/suite/auto_snapshot_matcher.ts +10 -2
  69. package/src/config/types.ts +14 -0
  70. package/src/exports/client-api.ts +13 -0
  71. package/src/exports/config.ts +3 -0
  72. package/src/exports/server-utils.ts +7 -0
  73. package/src/exports/setup-api.ts +5 -0
  74. package/src/exports/setup.ts +3 -0
  75. package/src/exports/testing/node.ts +4 -0
  76. package/src/exports/testing.ts +1 -0
  77. package/src/server/commands/prepare_image_snapshot_comparison.ts +2 -2
  78. package/src/server/commands/prepare_page_image_snapshot_comparison.ts +2 -1
  79. package/src/server/snapshot_path_limits.ts +135 -0
  80. package/src/server/suite.ts +19 -4
  81. package/src/server/vis_options.ts +10 -1
  82. package/src/server/vis_server_context.logic.ts +47 -17
  83. package/src/setup/create_vis.ts +12 -11
  84. package/src/shared/types.ts +21 -1
  85. package/dist/client-api.mjs +0 -11
  86. package/dist/config.d.mts +0 -5
  87. package/dist/config.mjs +0 -4
  88. package/dist/presets/auto.mjs +0 -9
  89. package/dist/presets/auto.mjs.map +0 -1
  90. package/dist/presets/manual.mjs +0 -9
  91. package/dist/presets/manual.mjs.map +0 -1
  92. package/dist/server-utils.d.mts +0 -4
  93. package/dist/server-utils.mjs +0 -5
  94. package/dist/setup-api.d.mts +0 -4
  95. package/dist/setup-api.mjs +0 -5
  96. package/dist/setup.d.mts +0 -3
  97. package/dist/setup.mjs +0 -4
  98. package/dist/testing.d.mts +0 -2
  99. package/dist/testing.mjs +0 -3
  100. package/src/client-api.ts +0 -13
  101. package/src/config.ts +0 -3
  102. package/src/server-utils.ts +0 -3
  103. package/src/setup-api.ts +0 -5
  104. package/src/setup.ts +0 -3
  105. package/src/testing.ts +0 -1
  106. /package/dist/{presets → exports/presets}/auto.d.mts +0 -0
  107. /package/dist/{presets → exports/presets}/manual.d.mts +0 -0
  108. /package/src/{presets → exports/presets}/auto.ts +0 -0
  109. /package/src/{presets → exports/presets}/manual.ts +0 -0
@@ -1,5 +1,5 @@
1
1
  import { NAME } from "../shared/constants.mjs";
2
- import "../client-api.mjs";
2
+ import "../exports/client-api.mjs";
3
3
 
4
4
  //#region src/auto_snapshots/_extract_auto_snapshot_options.ts
5
5
  function extractAutoSnapshotOptions(task) {
@@ -1 +1 @@
1
- {"version":3,"file":"_extract_auto_snapshot_options.mjs","names":[],"sources":["../../src/auto_snapshots/_extract_auto_snapshot_options.ts"],"sourcesContent":["import { NAME } from '../client-api.ts'\nimport type { MetaTask } from './auto_snapshot_options.ts'\nimport type { SnapshotMeta } from './snapshot_meta.ts'\n\nexport function extractAutoSnapshotOptions<M extends SnapshotMeta<any> = SnapshotMeta<any>>(\n\ttask: MetaTask,\n): M | undefined {\n\tif (!task) return\n\n\tconst list: any[] = []\n\tlet current = task\n\twhile (current?.suite) {\n\t\tlist.unshift(current.suite.meta)\n\t\tcurrent = current.suite\n\t}\n\tlist.unshift(task.file?.meta)\n\tlist.push(task.meta)\n\treturn list.reduce(\n\t\t(acc, cur) => {\n\t\t\tconst meta = cur?.[NAME]\n\t\t\t// biome-ignore lint/performance/noAccumulatingSpread: I think this is needed\n\t\t\treturn meta ? Object.assign({}, acc, meta) : acc\n\t\t},\n\t\t{ enable: false },\n\t)\n}\n"],"mappings":";;;;AAIA,SAAgB,2BACf,MACgB;AAChB,KAAI,CAAC,KAAM;CAEX,MAAM,OAAc,EAAE;CACtB,IAAI,UAAU;AACd,QAAO,SAAS,OAAO;AACtB,OAAK,QAAQ,QAAQ,MAAM,KAAK;AAChC,YAAU,QAAQ;;AAEnB,MAAK,QAAQ,KAAK,MAAM,KAAK;AAC7B,MAAK,KAAK,KAAK,KAAK;AACpB,QAAO,KAAK,QACV,KAAK,QAAQ;EACb,MAAM,OAAO,MAAM;AAEnB,SAAO,OAAO,OAAO,OAAO,EAAE,EAAE,KAAK,KAAK,GAAG;IAE9C,EAAE,QAAQ,OAAO,CACjB"}
1
+ {"version":3,"file":"_extract_auto_snapshot_options.mjs","names":[],"sources":["../../src/auto_snapshots/_extract_auto_snapshot_options.ts"],"sourcesContent":["import { NAME } from '../exports/client-api.ts'\nimport type { MetaTask } from './auto_snapshot_options.ts'\nimport type { SnapshotMeta } from './snapshot_meta.ts'\n\nexport function extractAutoSnapshotOptions<M extends SnapshotMeta<any> = SnapshotMeta<any>>(\n\ttask: MetaTask,\n): M | undefined {\n\tif (!task) return\n\n\tconst list: any[] = []\n\tlet current = task\n\twhile (current?.suite) {\n\t\tlist.unshift(current.suite.meta)\n\t\tcurrent = current.suite\n\t}\n\tlist.unshift(task.file?.meta)\n\tlist.push(task.meta)\n\treturn list.reduce(\n\t\t(acc, cur) => {\n\t\t\tconst meta = cur?.[NAME]\n\t\t\t// biome-ignore lint/performance/noAccumulatingSpread: I think this is needed\n\t\t\treturn meta ? Object.assign({}, acc, meta) : acc\n\t\t},\n\t\t{ enable: false },\n\t)\n}\n"],"mappings":";;;;AAIA,SAAgB,2BACf,MACgB;AAChB,KAAI,CAAC,KAAM;CAEX,MAAM,OAAc,EAAE;CACtB,IAAI,UAAU;AACd,QAAO,SAAS,OAAO;AACtB,OAAK,QAAQ,QAAQ,MAAM,KAAK;AAChC,YAAU,QAAQ;;AAEnB,MAAK,QAAQ,KAAK,MAAM,KAAK;AAC7B,MAAK,KAAK,KAAK,KAAK;AACpB,QAAO,KAAK,QACV,KAAK,QAAQ;EACb,MAAM,OAAO,MAAM;AAEnB,SAAO,OAAO,OAAO,OAAO,EAAE,EAAE,KAAK,KAAK,GAAG;IAE9C,EAAE,QAAQ,OAAO,CACjB"}
@@ -1,2 +1 @@
1
- import "../../../shared/types.mjs";
2
1
  import "vitest";
@@ -5,7 +5,8 @@ function prettifyOptions(options) {
5
5
  `failureThreshold: ${options.failureThreshold ?? 0} ${options.failureThresholdType ?? "pixels"}`,
6
6
  options.timeout ? `timeout: ${options.timeout} ms` : "",
7
7
  `comparisonMethod: ${options.comparisonMethod ?? "pixel"}`,
8
- options.diffOptions ? `diffOptions: ${JSON.stringify(options.diffOptions)}` : ""
8
+ options.diffOptions ? `diffOptions: ${JSON.stringify(options.diffOptions)}` : "",
9
+ options.createMissingBaseline ? "createMissingBaseline: true" : ""
9
10
  ].filter(Boolean).join("\n ");
10
11
  }
11
12
 
@@ -1 +1 @@
1
- {"version":3,"file":"compare_image_snapshot.logic.mjs","names":[],"sources":["../../../src/client/snapshot/compare_image_snapshot.logic.ts"],"sourcesContent":["import type { ToMatchImageSnapshotOptions } from '../../shared/types.ts'\n\nexport function prettifyOptions(options: ToMatchImageSnapshotOptions<any> | undefined) {\n\tif (!options) return 'none'\n\n\treturn [\n\t\t`failureThreshold: ${options.failureThreshold ?? 0} ${options.failureThresholdType ?? 'pixels'}`,\n\t\toptions.timeout ? `timeout: ${options.timeout} ms` : '',\n\t\t`comparisonMethod: ${options.comparisonMethod ?? 'pixel'}`,\n\t\toptions.diffOptions ? `diffOptions: ${JSON.stringify(options.diffOptions)}` : '',\n\t]\n\t\t.filter(Boolean)\n\t\t.join('\\n ')\n}\n"],"mappings":";AAEA,SAAgB,gBAAgB,SAAuD;AACtF,KAAI,CAAC,QAAS,QAAO;AAErB,QAAO;EACN,qBAAqB,QAAQ,oBAAoB,EAAE,GAAG,QAAQ,wBAAwB;EACtF,QAAQ,UAAU,YAAY,QAAQ,QAAQ,OAAO;EACrD,qBAAqB,QAAQ,oBAAoB;EACjD,QAAQ,cAAc,gBAAgB,KAAK,UAAU,QAAQ,YAAY,KAAK;EAC9E,CACC,OAAO,QAAQ,CACf,KAAK,qBAAqB"}
1
+ {"version":3,"file":"compare_image_snapshot.logic.mjs","names":[],"sources":["../../../src/client/snapshot/compare_image_snapshot.logic.ts"],"sourcesContent":["import type { ToMatchImageSnapshotOptions } from '../../shared/types.ts'\n\nexport function prettifyOptions(options: ToMatchImageSnapshotOptions<any> | undefined) {\n\tif (!options) return 'none'\n\n\treturn [\n\t\t`failureThreshold: ${options.failureThreshold ?? 0} ${options.failureThresholdType ?? 'pixels'}`,\n\t\toptions.timeout ? `timeout: ${options.timeout} ms` : '',\n\t\t`comparisonMethod: ${options.comparisonMethod ?? 'pixel'}`,\n\t\toptions.diffOptions ? `diffOptions: ${JSON.stringify(options.diffOptions)}` : '',\n\t\toptions.createMissingBaseline ? 'createMissingBaseline: true' : '',\n\t]\n\t\t.filter(Boolean)\n\t\t.join('\\n ')\n}\n"],"mappings":";AAEA,SAAgB,gBAAgB,SAAuD;AACtF,KAAI,CAAC,QAAS,QAAO;AAErB,QAAO;EACN,qBAAqB,QAAQ,oBAAoB,EAAE,GAAG,QAAQ,wBAAwB;EACtF,QAAQ,UAAU,YAAY,QAAQ,QAAQ,OAAO;EACrD,qBAAqB,QAAQ,oBAAoB;EACjD,QAAQ,cAAc,gBAAgB,KAAK,UAAU,QAAQ,YAAY,KAAK;EAC9E,QAAQ,wBAAwB,gCAAgC;EAChE,CACC,OAAO,QAAQ,CACf,KAAK,qBAAqB"}
@@ -14,7 +14,7 @@ async function compareImageSnapshot(commands, taskId, info, options) {
14
14
  };
15
15
  const resultImage = await toImageData(info.result);
16
16
  if (!info.baseline) {
17
- if (server.config.snapshotOptions.updateSnapshot === "all") {
17
+ if (server.config.snapshotOptions.updateSnapshot === "all" || options?.createMissingBaseline === true) {
18
18
  await writeSnapshot(commands, resolve(info.projectRoot, info.baselinePath), resultImage);
19
19
  return;
20
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"compare_image_snapshot.mjs","names":[],"sources":["../../../src/client/snapshot/compare_image_snapshot.ts"],"sourcesContent":["import dedent from 'dedent'\nimport { resolve } from 'pathe'\nimport type { BrowserCommands } from 'vitest/browser'\nimport type {\n\tImageSnapshotNextIndexCommand,\n\tPrepareImageSnapshotComparisonCommand,\n} from '../../shared/commands.types.ts'\nimport { compareImage } from '../../shared/compare_image.ts'\nimport type { ImageSnapshotComparisonInfo, ToMatchImageSnapshotOptions } from '../../shared/types.ts'\nimport { toDataURL, toImageData } from '../external/browser/image_data.ts'\nimport { server } from '../external/vitest/vitest_browser_context_proxy.ts'\nimport { alignImagesToSameSize } from '../image/align_images.ts'\nimport { prettifyOptions } from './compare_image_snapshot.logic.ts'\n\nexport async function compareImageSnapshot(\n\tcommands: BrowserCommands & PrepareImageSnapshotComparisonCommand & ImageSnapshotNextIndexCommand,\n\ttaskId: string,\n\tinfo: ImageSnapshotComparisonInfo,\n\toptions?: ToMatchImageSnapshotOptions<any>,\n) {\n\toptions = { ...info, ...options } as any\n\n\tconst resultImage = await toImageData(info.result)\n\n\tif (!info.baseline) {\n\t\tif (server.config.snapshotOptions.updateSnapshot === 'all') {\n\t\t\tawait writeSnapshot(commands, resolve(info.projectRoot, info.baselinePath), resultImage)\n\t\t\treturn\n\t\t}\n\t\tthrow new Error(\n\t\t\tdedent`Snapshot \\`${taskId}\\` has no baseline image. Please review the new snapshot and update the baseline image.\n\n\t\t\t\tOptions: ${prettifyOptions(options)}\n\t\t\t\tActual: ${resolve(info.projectRoot, info.resultPath)}`,\n\t\t)\n\t}\n\n\tconst baselineImage = await toImageData(info.baseline)\n\tconst [baselineAlignedImage, resultAlignedImage] = alignImagesToSameSize(baselineImage, resultImage)\n\tconst { width, height } = baselineAlignedImage\n\tconst diffImage = new ImageData(width, height)\n\tconst { pass, diffAmount } = compareImage(\n\t\tbaselineAlignedImage.data,\n\t\tresultAlignedImage.data,\n\t\tdiffImage.data,\n\t\twidth,\n\t\theight,\n\t\toptions,\n\t)\n\tif (pass) {\n\t\tif (options?.expectToFail) {\n\t\t\tthrow new Error(\n\t\t\t\tdedent`Snapshot \\`${taskId}\\` matched but expected to fail.\n\n\t\t\t\t\t\t\tOptions: ${prettifyOptions(options)}\n\t\t\t\t\t\t\tDiff: ${options.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}\n\n\t\t\t\t\t\t\tExpected: ${resolve(info.projectRoot, info.baselinePath)}\n\t\t\t\t\t\t\tActual: ${resolve(info.projectRoot, info.resultPath)}`,\n\t\t\t)\n\t\t}\n\t\treturn\n\t}\n\tif (server.config.snapshotOptions.updateSnapshot === 'all' && !options?.expectToFail) {\n\t\tawait writeSnapshot(commands, resolve(info.projectRoot, info.baselinePath), resultImage)\n\t\treturn\n\t}\n\n\tawait writeSnapshot(commands, resolve(info.projectRoot, info.diffPath), diffImage)\n\n\tthrow new Error(\n\t\tdedent`Snapshot \\`${taskId}\\` mismatched\n\n\t\t\t\t\t${\n\t\t\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\t\t\t\t\t\t\t? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`\n\t\t\t\t\t\t\t\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t\t\t: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\t\t\t\t}${\n\t\t\t\t\t\tbaselineImage.width !== resultImage.width || baselineImage.height !== resultImage.height\n\t\t\t\t\t\t\t? `\\nThe image size changed from ${baselineImage.width}x${baselineImage.height} to ${resultImage.width}x${resultImage.height}.`\n\t\t\t\t\t\t\t: ''\n\t\t\t\t\t}\n\n\t\t\t\t\tOptions: ${prettifyOptions(options)}\n\n\t\t\t\t\tExpected: ${resolve(info.projectRoot, info.baselinePath)}\n\t\t\t\t\tActual: ${resolve(info.projectRoot, info.resultPath)}\n\t\t\t\t\tDifference: ${resolve(info.projectRoot, info.diffPath)}`,\n\t)\n}\n\nasync function writeSnapshot(commands: BrowserCommands, path: string, image: ImageData) {\n\tconst content = (await toDataURL(image)).split(',')[1]!\n\treturn commands.writeFile(path, content, { encoding: 'base64' })\n}\n"],"mappings":";;;;;;;;;AAcA,eAAsB,qBACrB,UACA,QACA,MACA,SACC;AACD,WAAU;EAAE,GAAG;EAAM,GAAG;EAAS;CAEjC,MAAM,cAAc,MAAM,YAAY,KAAK,OAAO;AAElD,KAAI,CAAC,KAAK,UAAU;AACnB,MAAI,OAAO,OAAO,gBAAgB,mBAAmB,OAAO;AAC3D,SAAM,cAAc,UAAU,QAAQ,KAAK,aAAa,KAAK,aAAa,EAAE,YAAY;AACxF;;AAED,QAAM,IAAI,MACT,MAAM,cAAc,OAAO;;kBAEZ,gBAAgB,QAAQ,CAAC;kBACzB,QAAQ,KAAK,aAAa,KAAK,WAAW,GACzD;;CAGF,MAAM,gBAAgB,MAAM,YAAY,KAAK,SAAS;CACtD,MAAM,CAAC,sBAAsB,sBAAsB,sBAAsB,eAAe,YAAY;CACpG,MAAM,EAAE,OAAO,WAAW;CAC1B,MAAM,YAAY,IAAI,UAAU,OAAO,OAAO;CAC9C,MAAM,EAAE,MAAM,eAAe,aAC5B,qBAAqB,MACrB,mBAAmB,MACnB,UAAU,MACV,OACA,QACA,QACA;AACD,KAAI,MAAM;AACT,MAAI,SAAS,aACZ,OAAM,IAAI,MACT,MAAM,cAAc,OAAO;;qBAEV,gBAAgB,QAAQ,CAAC;qBACzB,QAAQ,yBAAyB,YAAY,GAAG,WAAW,KAAK,GAAG,WAAW,SAAS;;qBAEvF,QAAQ,KAAK,aAAa,KAAK,aAAa,CAAC;qBAC7C,QAAQ,KAAK,aAAa,KAAK,WAAW,GAC3D;AAEF;;AAED,KAAI,OAAO,OAAO,gBAAgB,mBAAmB,SAAS,CAAC,SAAS,cAAc;AACrF,QAAM,cAAc,UAAU,QAAQ,KAAK,aAAa,KAAK,aAAa,EAAE,YAAY;AACxF;;AAGD,OAAM,cAAc,UAAU,QAAQ,KAAK,aAAa,KAAK,SAAS,EAAE,UAAU;AAElF,OAAM,IAAI,MACT,MAAM,cAAc,OAAO;;OAGvB,SAAS,mBACN,SAAS,yBAAyB,YACjC,kCAAkC,QAAQ,iBAAiB,sBAAsB,WAAW,MAC5F,kCAAkC,QAAQ,iBAAiB,4BAA4B,WAAW,YACnG,6CAA6C,SAAS,yBAAyB,YAAY,GAAG,WAAW,KAAK,GAAG,WAAW,SAAS,KAExI,cAAc,UAAU,YAAY,SAAS,cAAc,WAAW,YAAY,SAC/E,iCAAiC,cAAc,MAAM,GAAG,cAAc,OAAO,MAAM,YAAY,MAAM,GAAG,YAAY,OAAO,KAC3H,GACH;;mBAEa,gBAAgB,QAAQ,CAAC;;mBAEzB,QAAQ,KAAK,aAAa,KAAK,aAAa,CAAC;mBAC7C,QAAQ,KAAK,aAAa,KAAK,WAAW,CAAC;mBAC3C,QAAQ,KAAK,aAAa,KAAK,SAAS,GACzD;;AAGF,eAAe,cAAc,UAA2B,MAAc,OAAkB;CACvF,MAAM,WAAW,MAAM,UAAU,MAAM,EAAE,MAAM,IAAI,CAAC;AACpD,QAAO,SAAS,UAAU,MAAM,SAAS,EAAE,UAAU,UAAU,CAAC"}
1
+ {"version":3,"file":"compare_image_snapshot.mjs","names":[],"sources":["../../../src/client/snapshot/compare_image_snapshot.ts"],"sourcesContent":["import dedent from 'dedent'\nimport { resolve } from 'pathe'\nimport type { BrowserCommands } from 'vitest/browser'\nimport type {\n\tImageSnapshotNextIndexCommand,\n\tPrepareImageSnapshotComparisonCommand,\n} from '../../shared/commands.types.ts'\nimport { compareImage } from '../../shared/compare_image.ts'\nimport type { ImageSnapshotComparisonInfo, ToMatchImageSnapshotOptions } from '../../shared/types.ts'\nimport { toDataURL, toImageData } from '../external/browser/image_data.ts'\nimport { server } from '../external/vitest/vitest_browser_context_proxy.ts'\nimport { alignImagesToSameSize } from '../image/align_images.ts'\nimport { prettifyOptions } from './compare_image_snapshot.logic.ts'\n\nexport async function compareImageSnapshot(\n\tcommands: BrowserCommands & PrepareImageSnapshotComparisonCommand & ImageSnapshotNextIndexCommand,\n\ttaskId: string,\n\tinfo: ImageSnapshotComparisonInfo,\n\toptions?: ToMatchImageSnapshotOptions<any>,\n) {\n\toptions = { ...info, ...options } as any\n\n\tconst resultImage = await toImageData(info.result)\n\n\tif (!info.baseline) {\n\t\tif (server.config.snapshotOptions.updateSnapshot === 'all' || options?.createMissingBaseline === true) {\n\t\t\tawait writeSnapshot(commands, resolve(info.projectRoot, info.baselinePath), resultImage)\n\t\t\treturn\n\t\t}\n\t\tthrow new Error(\n\t\t\tdedent`Snapshot \\`${taskId}\\` has no baseline image. Please review the new snapshot and update the baseline image.\n\n\t\t\t\tOptions: ${prettifyOptions(options)}\n\t\t\t\tActual: ${resolve(info.projectRoot, info.resultPath)}`,\n\t\t)\n\t}\n\n\tconst baselineImage = await toImageData(info.baseline)\n\tconst [baselineAlignedImage, resultAlignedImage] = alignImagesToSameSize(baselineImage, resultImage)\n\tconst { width, height } = baselineAlignedImage\n\tconst diffImage = new ImageData(width, height)\n\tconst { pass, diffAmount } = compareImage(\n\t\tbaselineAlignedImage.data,\n\t\tresultAlignedImage.data,\n\t\tdiffImage.data,\n\t\twidth,\n\t\theight,\n\t\toptions as ToMatchImageSnapshotOptions<any>,\n\t)\n\tif (pass) {\n\t\tif (options?.expectToFail) {\n\t\t\tthrow new Error(\n\t\t\t\tdedent`Snapshot \\`${taskId}\\` matched but expected to fail.\n\n\t\t\t\t\t\t\tOptions: ${prettifyOptions(options)}\n\t\t\t\t\t\t\tDiff: ${options.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}\n\n\t\t\t\t\t\t\tExpected: ${resolve(info.projectRoot, info.baselinePath)}\n\t\t\t\t\t\t\tActual: ${resolve(info.projectRoot, info.resultPath)}`,\n\t\t\t)\n\t\t}\n\t\treturn\n\t}\n\tif (server.config.snapshotOptions.updateSnapshot === 'all' && !options?.expectToFail) {\n\t\tawait writeSnapshot(commands, resolve(info.projectRoot, info.baselinePath), resultImage)\n\t\treturn\n\t}\n\n\tawait writeSnapshot(commands, resolve(info.projectRoot, info.diffPath), diffImage)\n\n\tthrow new Error(\n\t\tdedent`Snapshot \\`${taskId}\\` mismatched\n\n\t\t\t\t\t${\n\t\t\t\t\t\toptions?.failureThreshold\n\t\t\t\t\t\t\t? options?.failureThresholdType === 'percent'\n\t\t\t\t\t\t\t\t? `Expected image to match within ${options.failureThreshold}% but was differ by ${diffAmount}%.`\n\t\t\t\t\t\t\t\t: `Expected image to match within ${options.failureThreshold} pixels but was differ by ${diffAmount} pixels.`\n\t\t\t\t\t\t\t: `Expected image to match but was differ by ${options?.failureThresholdType === 'percent' ? `${diffAmount}%` : `${diffAmount} pixels`}.`\n\t\t\t\t\t}${\n\t\t\t\t\t\tbaselineImage.width !== resultImage.width || baselineImage.height !== resultImage.height\n\t\t\t\t\t\t\t? `\\nThe image size changed from ${baselineImage.width}x${baselineImage.height} to ${resultImage.width}x${resultImage.height}.`\n\t\t\t\t\t\t\t: ''\n\t\t\t\t\t}\n\n\t\t\t\t\tOptions: ${prettifyOptions(options)}\n\n\t\t\t\t\tExpected: ${resolve(info.projectRoot, info.baselinePath)}\n\t\t\t\t\tActual: ${resolve(info.projectRoot, info.resultPath)}\n\t\t\t\t\tDifference: ${resolve(info.projectRoot, info.diffPath)}`,\n\t)\n}\n\nasync function writeSnapshot(commands: BrowserCommands, path: string, image: ImageData) {\n\tconst content = (await toDataURL(image)).split(',')[1]!\n\treturn commands.writeFile(path, content, { encoding: 'base64' })\n}\n"],"mappings":";;;;;;;;;AAcA,eAAsB,qBACrB,UACA,QACA,MACA,SACC;AACD,WAAU;EAAE,GAAG;EAAM,GAAG;EAAS;CAEjC,MAAM,cAAc,MAAM,YAAY,KAAK,OAAO;AAElD,KAAI,CAAC,KAAK,UAAU;AACnB,MAAI,OAAO,OAAO,gBAAgB,mBAAmB,SAAS,SAAS,0BAA0B,MAAM;AACtG,SAAM,cAAc,UAAU,QAAQ,KAAK,aAAa,KAAK,aAAa,EAAE,YAAY;AACxF;;AAED,QAAM,IAAI,MACT,MAAM,cAAc,OAAO;;kBAEZ,gBAAgB,QAAQ,CAAC;kBACzB,QAAQ,KAAK,aAAa,KAAK,WAAW,GACzD;;CAGF,MAAM,gBAAgB,MAAM,YAAY,KAAK,SAAS;CACtD,MAAM,CAAC,sBAAsB,sBAAsB,sBAAsB,eAAe,YAAY;CACpG,MAAM,EAAE,OAAO,WAAW;CAC1B,MAAM,YAAY,IAAI,UAAU,OAAO,OAAO;CAC9C,MAAM,EAAE,MAAM,eAAe,aAC5B,qBAAqB,MACrB,mBAAmB,MACnB,UAAU,MACV,OACA,QACA,QACA;AACD,KAAI,MAAM;AACT,MAAI,SAAS,aACZ,OAAM,IAAI,MACT,MAAM,cAAc,OAAO;;qBAEV,gBAAgB,QAAQ,CAAC;qBACzB,QAAQ,yBAAyB,YAAY,GAAG,WAAW,KAAK,GAAG,WAAW,SAAS;;qBAEvF,QAAQ,KAAK,aAAa,KAAK,aAAa,CAAC;qBAC7C,QAAQ,KAAK,aAAa,KAAK,WAAW,GAC3D;AAEF;;AAED,KAAI,OAAO,OAAO,gBAAgB,mBAAmB,SAAS,CAAC,SAAS,cAAc;AACrF,QAAM,cAAc,UAAU,QAAQ,KAAK,aAAa,KAAK,aAAa,EAAE,YAAY;AACxF;;AAGD,OAAM,cAAc,UAAU,QAAQ,KAAK,aAAa,KAAK,SAAS,EAAE,UAAU;AAElF,OAAM,IAAI,MACT,MAAM,cAAc,OAAO;;OAGvB,SAAS,mBACN,SAAS,yBAAyB,YACjC,kCAAkC,QAAQ,iBAAiB,sBAAsB,WAAW,MAC5F,kCAAkC,QAAQ,iBAAiB,4BAA4B,WAAW,YACnG,6CAA6C,SAAS,yBAAyB,YAAY,GAAG,WAAW,KAAK,GAAG,WAAW,SAAS,KAExI,cAAc,UAAU,YAAY,SAAS,cAAc,WAAW,YAAY,SAC/E,iCAAiC,cAAc,MAAM,GAAG,cAAc,OAAO,MAAM,YAAY,MAAM,GAAG,YAAY,OAAO,KAC3H,GACH;;mBAEa,gBAAgB,QAAQ,CAAC;;mBAEzB,QAAQ,KAAK,aAAa,KAAK,aAAa,CAAC;mBAC7C,QAAQ,KAAK,aAAa,KAAK,WAAW,CAAC;mBAC3C,QAAQ,KAAK,aAAa,KAAK,SAAS,GACzD;;AAGF,eAAe,cAAc,UAA2B,MAAc,OAAkB;CACvF,MAAM,WAAW,MAAM,UAAU,MAAM,EAAE,MAAM,IAAI,CAAC;AACpD,QAAO,SAAS,UAAU,MAAM,SAAS,EAAE,UAAU,UAAU,CAAC"}
@@ -1,11 +1,11 @@
1
1
  import { SnapshotMeta } from "../../auto_snapshots/snapshot_meta.mjs";
2
- import { ComparisonMethod } from "../../shared/types.mjs";
2
+ import { ComparisonMethod, SetupVisOptions } from "../../shared/types.mjs";
3
3
  import { SetupVisSuiteCommand } from "../../shared/commands.types.mjs";
4
4
 
5
5
  //#region src/client/suite/auto_snapshot_matcher.d.ts
6
6
  declare function autoSnapshotMatcher<GM extends Record<string, any> | unknown = unknown>(commands: SetupVisSuiteCommand, expect: any): {
7
7
  setup(): Promise<void>;
8
- createMatcher<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(themes: Record<string, boolean | ((options: SnapshotMeta<C> & M & GM) => Promise<boolean> | Promise<void> | boolean | void)>): () => Promise<void>;
8
+ createMatcher<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(themes: Record<string, boolean | ((options: SnapshotMeta<C> & M & GM) => Promise<boolean> | Promise<void> | boolean | void)>, suiteDefaults?: Pick<SetupVisOptions<GM>, "createMissingBaseline"> | undefined): () => Promise<void>;
9
9
  };
10
10
  //#endregion
11
11
  export { autoSnapshotMatcher };
@@ -1 +1 @@
1
- {"version":3,"file":"auto_snapshot_matcher.d.mts","names":[],"sources":["../../../src/client/suite/auto_snapshot_matcher.ts"],"sourcesContent":[],"mappings":";;;;;iBASgB,+BAA+B,mDACpC;;EADK,aAAA,CAAA,UAUU,gBAVS,EAAA,UAUmB,MAVnB,CAAA,MAAA,EAAA,GAAA,CAAA,GAAA,OAAA,GAAA,OAAA,CAAA,CAAA,MAAA,EAWxB,MAXwB,CAAA,MAAA,EAAA,OAAA,GAAA,CAAA,CAAA,OAAA,EAaV,YAbU,CAaG,CAbH,CAAA,GAaQ,CAbR,GAaY,EAbZ,EAAA,GAamB,OAbnB,CAAA,OAAA,CAAA,GAasC,OAbtC,CAAA,IAAA,CAAA,GAAA,OAAA,GAAA,IAAA,CAAA,CAAA,CAAA,EAAA,GAAA,GAc/B,OAd+B,CAAA,IAAA,CAAA;CAAY"}
1
+ {"version":3,"file":"auto_snapshot_matcher.d.mts","names":[],"sources":["../../../src/client/suite/auto_snapshot_matcher.ts"],"sourcesContent":[],"mappings":";;;;;iBASgB,+BAA+B,mDACpC;;EADK,aAAA,CAAA,UAUU,gBAVS,EAAA,UAUmB,MAVnB,CAAA,MAAA,EAAA,GAAA,CAAA,GAAA,OAAA,GAAA,OAAA,CAAA,CAAA,MAAA,EAWxB,MAXwB,CAAA,MAAA,EAAA,OAAA,GAAA,CAAA,CAAA,OAAA,EAaV,YAbU,CAaG,CAbH,CAAA,GAaQ,CAbR,GAaY,EAbZ,EAAA,GAamB,OAbnB,CAAA,OAAA,CAAA,GAasC,OAbtC,CAAA,IAAA,CAAA,GAAA,OAAA,GAAA,IAAA,CAAA,CAAA,EAAA,aAAA,CAAA,EAehB,IAfgB,CAeX,eAfW,CAeK,EAfL,CAAA,EAAA,uBAAA,CAAA,GAAA,SAAA,CAAA,EAAA,GAAA,GAe8C,OAf9C,CAAA,IAAA,CAAA;CAAY"}
@@ -11,7 +11,7 @@ function autoSnapshotMatcher(commands, expect) {
11
11
  async setup() {
12
12
  subject = (await commands.setupVisSuite()).subject;
13
13
  },
14
- createMatcher(themes) {
14
+ createMatcher(themes, suiteDefaults) {
15
15
  return async function matchImageSnapshot() {
16
16
  const test = ctx.getCurrentTest();
17
17
  const meta = extractAutoSnapshotOptions(test);
@@ -21,9 +21,11 @@ function autoSnapshotMatcher(commands, expect) {
21
21
  await new Promise((a) => setTimeout(a, 10));
22
22
  const theme = themes[themeId];
23
23
  if ((typeof theme === "function" ? await theme(meta) : theme) === false) continue;
24
+ const effectiveCreateMissingBaseline = meta != null && Object.hasOwn(meta, "createMissingBaseline") ? meta.createMissingBaseline : suiteDefaults?.createMissingBaseline;
24
25
  await expect(getSubject(meta?.subject ?? subject)).toMatchImageSnapshot({
25
26
  ...meta,
26
- snapshotKey: meta?.snapshotKey ?? themeId
27
+ snapshotKey: meta?.snapshotKey ?? themeId,
28
+ ...effectiveCreateMissingBaseline !== void 0 ? { createMissingBaseline: effectiveCreateMissingBaseline } : {}
27
29
  });
28
30
  } catch (error) {
29
31
  errors.push([themeId, error]);
@@ -31,7 +33,7 @@ function autoSnapshotMatcher(commands, expect) {
31
33
  if (errors.length > 0) {
32
34
  if (errors.length === 1) throw errors[0][1];
33
35
  const taskId = toTaskId(test);
34
- throw new AggregateError(errors, dedent`Snapshot \`${taskId}\` mismatched
36
+ throw new AggregateError(errors.map(([, e]) => e), dedent`Snapshot \`${taskId}\` mismatched
35
37
 
36
38
  ${errors.map(([themeId, error]) => {
37
39
  return `Theme \`${themeId}\` failed: ${error.message}`;
@@ -1 +1 @@
1
- {"version":3,"file":"auto_snapshot_matcher.mjs","names":[],"sources":["../../../src/client/suite/auto_snapshot_matcher.ts"],"sourcesContent":["import dedent from 'dedent'\nimport { extractAutoSnapshotOptions } from '../../auto_snapshots/_extract_auto_snapshot_options.ts'\nimport type { SnapshotMeta } from '../../auto_snapshots/snapshot_meta.ts'\nimport type { SetupVisSuiteCommand } from '../../shared/commands.types.ts'\nimport type { ComparisonMethod } from '../../shared/types.ts'\nimport { shouldTakeSnapshot } from '../snapshot/should_take_snapshot.ts'\nimport { toTaskId } from '../task/task_id.ts'\nimport { ctx } from './_ctx.ts'\n\nexport function autoSnapshotMatcher<GM extends Record<string, any> | unknown = unknown>(\n\tcommands: SetupVisSuiteCommand,\n\texpect: any,\n) {\n\tlet subject: string | undefined\n\n\treturn {\n\t\tasync setup() {\n\t\t\tsubject = (await commands.setupVisSuite()).subject\n\t\t},\n\t\tcreateMatcher<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(\n\t\t\tthemes: Record<\n\t\t\t\tstring,\n\t\t\t\tboolean | ((options: SnapshotMeta<C> & M & GM) => Promise<boolean> | Promise<void> | boolean | void)\n\t\t\t>,\n\t\t) {\n\t\t\treturn async function matchImageSnapshot() {\n\t\t\t\tconst test = ctx.getCurrentTest()\n\n\t\t\t\tconst meta = extractAutoSnapshotOptions(test)\n\t\t\t\tif (!shouldTakeSnapshot(meta)) return\n\t\t\t\tconst errors: Array<[string, Error]> = []\n\t\t\t\tfor (const themeId in themes) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait new Promise((a) => setTimeout(a, 10))\n\t\t\t\t\t\tconst theme = themes[themeId]\n\t\t\t\t\t\tconst r = typeof theme === 'function' ? await theme(meta! as any) : theme\n\t\t\t\t\t\tif (r === false) continue\n\t\t\t\t\t\tawait expect(getSubject(meta?.subject ?? subject)).toMatchImageSnapshot({\n\t\t\t\t\t\t\t...meta,\n\t\t\t\t\t\t\tsnapshotKey: meta?.snapshotKey ?? themeId,\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push([themeId, error as Error])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tif (errors.length === 1) throw errors[0]![1]\n\t\t\t\t\tconst taskId = toTaskId(test!)\n\t\t\t\t\tthrow new AggregateError(\n\t\t\t\t\t\terrors,\n\t\t\t\t\t\tdedent`Snapshot \\`${taskId}\\` mismatched\n\n\t\t\t\t\t${errors\n\t\t\t\t\t\t.map(([themeId, error]) => {\n\t\t\t\t\t\t\treturn `Theme \\`${themeId}\\` failed: ${error.message}`\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join('\\n\\n')}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nfunction getSubject(selectors: string | undefined) {\n\treturn selectors ? (document.querySelector(selectors) ?? document.body) : document.body\n}\n"],"mappings":";;;;;;;AASA,SAAgB,oBACf,UACA,QACC;CACD,IAAI;AAEJ,QAAO;EACN,MAAM,QAAQ;AACb,cAAW,MAAM,SAAS,eAAe,EAAE;;EAE5C,cACC,QAIC;AACD,UAAO,eAAe,qBAAqB;IAC1C,MAAM,OAAO,IAAI,gBAAgB;IAEjC,MAAM,OAAO,2BAA2B,KAAK;AAC7C,QAAI,CAAC,mBAAmB,KAAK,CAAE;IAC/B,MAAM,SAAiC,EAAE;AACzC,SAAK,MAAM,WAAW,OACrB,KAAI;AACH,WAAM,IAAI,SAAS,MAAM,WAAW,GAAG,GAAG,CAAC;KAC3C,MAAM,QAAQ,OAAO;AAErB,UADU,OAAO,UAAU,aAAa,MAAM,MAAM,KAAa,GAAG,WAC1D,MAAO;AACjB,WAAM,OAAO,WAAW,MAAM,WAAW,QAAQ,CAAC,CAAC,qBAAqB;MACvE,GAAG;MACH,aAAa,MAAM,eAAe;MAClC,CAAC;aACM,OAAO;AACf,YAAO,KAAK,CAAC,SAAS,MAAe,CAAC;;AAGxC,QAAI,OAAO,SAAS,GAAG;AACtB,SAAI,OAAO,WAAW,EAAG,OAAM,OAAO,GAAI;KAC1C,MAAM,SAAS,SAAS,KAAM;AAC9B,WAAM,IAAI,eACT,QACA,MAAM,cAAc,OAAO;;OAE1B,OACA,KAAK,CAAC,SAAS,WAAW;AAC1B,aAAO,WAAW,QAAQ,aAAa,MAAM;OAC5C,CACD,KAAK,OAAO,GACb;;;;EAIJ;;AAGF,SAAS,WAAW,WAA+B;AAClD,QAAO,YAAa,SAAS,cAAc,UAAU,IAAI,SAAS,OAAQ,SAAS"}
1
+ {"version":3,"file":"auto_snapshot_matcher.mjs","names":[],"sources":["../../../src/client/suite/auto_snapshot_matcher.ts"],"sourcesContent":["import dedent from 'dedent'\nimport { extractAutoSnapshotOptions } from '../../auto_snapshots/_extract_auto_snapshot_options.ts'\nimport type { SnapshotMeta } from '../../auto_snapshots/snapshot_meta.ts'\nimport type { SetupVisSuiteCommand } from '../../shared/commands.types.ts'\nimport type { ComparisonMethod, SetupVisOptions } from '../../shared/types.ts'\nimport { shouldTakeSnapshot } from '../snapshot/should_take_snapshot.ts'\nimport { toTaskId } from '../task/task_id.ts'\nimport { ctx } from './_ctx.ts'\n\nexport function autoSnapshotMatcher<GM extends Record<string, any> | unknown = unknown>(\n\tcommands: SetupVisSuiteCommand,\n\texpect: any,\n) {\n\tlet subject: string | undefined\n\n\treturn {\n\t\tasync setup() {\n\t\t\tsubject = (await commands.setupVisSuite()).subject\n\t\t},\n\t\tcreateMatcher<C extends ComparisonMethod, M extends Record<string, any> | unknown = unknown>(\n\t\t\tthemes: Record<\n\t\t\t\tstring,\n\t\t\t\tboolean | ((options: SnapshotMeta<C> & M & GM) => Promise<boolean> | Promise<void> | boolean | void)\n\t\t\t>,\n\t\t\tsuiteDefaults?: Pick<SetupVisOptions<GM>, 'createMissingBaseline'> | undefined,\n\t\t) {\n\t\t\treturn async function matchImageSnapshot() {\n\t\t\t\tconst test = ctx.getCurrentTest()\n\n\t\t\t\tconst meta = extractAutoSnapshotOptions(test)\n\t\t\t\tif (!shouldTakeSnapshot(meta)) return\n\t\t\t\tconst errors: Array<[string, Error]> = []\n\t\t\t\tfor (const themeId in themes) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait new Promise((a) => setTimeout(a, 10))\n\t\t\t\t\t\tconst theme = themes[themeId]\n\t\t\t\t\t\tconst r = typeof theme === 'function' ? await theme(meta! as any) : theme\n\t\t\t\t\t\tif (r === false) continue\n\t\t\t\t\t\tconst effectiveCreateMissingBaseline =\n\t\t\t\t\t\t\tmeta != null && Object.hasOwn(meta as object, 'createMissingBaseline')\n\t\t\t\t\t\t\t\t? (meta as { createMissingBaseline?: boolean }).createMissingBaseline\n\t\t\t\t\t\t\t\t: suiteDefaults?.createMissingBaseline\n\t\t\t\t\t\tawait expect(getSubject(meta?.subject ?? subject)).toMatchImageSnapshot({\n\t\t\t\t\t\t\t...meta,\n\t\t\t\t\t\t\tsnapshotKey: meta?.snapshotKey ?? themeId,\n\t\t\t\t\t\t\t...(effectiveCreateMissingBaseline !== undefined\n\t\t\t\t\t\t\t\t? { createMissingBaseline: effectiveCreateMissingBaseline }\n\t\t\t\t\t\t\t\t: {}),\n\t\t\t\t\t\t})\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\terrors.push([themeId, error as Error])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (errors.length > 0) {\n\t\t\t\t\tif (errors.length === 1) throw errors[0]![1]\n\t\t\t\t\tconst taskId = toTaskId(test!)\n\t\t\t\t\tthrow new AggregateError(\n\t\t\t\t\t\terrors.map(([, e]) => e),\n\t\t\t\t\t\tdedent`Snapshot \\`${taskId}\\` mismatched\n\n\t\t\t\t\t${errors\n\t\t\t\t\t\t.map(([themeId, error]) => {\n\t\t\t\t\t\t\treturn `Theme \\`${themeId}\\` failed: ${error.message}`\n\t\t\t\t\t\t})\n\t\t\t\t\t\t.join('\\n\\n')}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n}\n\nfunction getSubject(selectors: string | undefined) {\n\treturn selectors ? (document.querySelector(selectors) ?? document.body) : document.body\n}\n"],"mappings":";;;;;;;AASA,SAAgB,oBACf,UACA,QACC;CACD,IAAI;AAEJ,QAAO;EACN,MAAM,QAAQ;AACb,cAAW,MAAM,SAAS,eAAe,EAAE;;EAE5C,cACC,QAIA,eACC;AACD,UAAO,eAAe,qBAAqB;IAC1C,MAAM,OAAO,IAAI,gBAAgB;IAEjC,MAAM,OAAO,2BAA2B,KAAK;AAC7C,QAAI,CAAC,mBAAmB,KAAK,CAAE;IAC/B,MAAM,SAAiC,EAAE;AACzC,SAAK,MAAM,WAAW,OACrB,KAAI;AACH,WAAM,IAAI,SAAS,MAAM,WAAW,GAAG,GAAG,CAAC;KAC3C,MAAM,QAAQ,OAAO;AAErB,UADU,OAAO,UAAU,aAAa,MAAM,MAAM,KAAa,GAAG,WAC1D,MAAO;KACjB,MAAM,iCACL,QAAQ,QAAQ,OAAO,OAAO,MAAgB,wBAAwB,GAClE,KAA6C,wBAC9C,eAAe;AACnB,WAAM,OAAO,WAAW,MAAM,WAAW,QAAQ,CAAC,CAAC,qBAAqB;MACvE,GAAG;MACH,aAAa,MAAM,eAAe;MAClC,GAAI,mCAAmC,SACpC,EAAE,uBAAuB,gCAAgC,GACzD,EAAE;MACL,CAAC;aACM,OAAO;AACf,YAAO,KAAK,CAAC,SAAS,MAAe,CAAC;;AAGxC,QAAI,OAAO,SAAS,GAAG;AACtB,SAAI,OAAO,WAAW,EAAG,OAAM,OAAO,GAAI;KAC1C,MAAM,SAAS,SAAS,KAAM;AAC9B,WAAM,IAAI,eACT,OAAO,KAAK,GAAG,OAAO,EAAE,EACxB,MAAM,cAAc,OAAO;;OAE1B,OACA,KAAK,CAAC,SAAS,WAAW;AAC1B,aAAO,WAAW,QAAQ,aAAa,MAAM;OAC5C,CACD,KAAK,OAAO,GACb;;;;EAIJ;;AAGF,SAAS,WAAW,WAA+B;AAClD,QAAO,YAAa,SAAS,cAAc,UAAU,IAAI,SAAS,OAAQ,SAAS"}
@@ -54,6 +54,20 @@ type VisOptions<M extends ComparisonMethod = 'pixel'> = (M extends 'ssim' ? {
54
54
  snapshotSubpath?: ((options: {
55
55
  subpath: string;
56
56
  }) => string) | undefined;
57
+ /**
58
+ * When `true`, shortenspaths that would exceed Windows-safe limits (full path length at
59
+ * least 250, below classic `MAX_PATH` 260, or a final path component longer than 255):
60
+ * - After `snapshotRootDir` and `snapshotSubpath`, if the path
61
+ * would exceed that budget, the **final** subpath segment is shortened to
62
+ * `{firstToken}-{hash12}{ext}` (tersify-style).
63
+ * - When writing a baseline, if the **actual** absolute path for the legacy `taskId-key.png` file would
64
+ * exceed the budget, only the final segment of that relative path is shortened the same way (nested
65
+ * `describe` directories from `taskId` stay as-is so `taskId-*.png` globs still work).
66
+ * `snapshotRootDir` itself is not rewritten.
67
+ *
68
+ * @default false
69
+ */
70
+ shortenLongSnapshotPaths?: boolean | undefined;
57
71
  };
58
72
  //#endregion
59
73
  export { VisOptions };
@@ -1,13 +1,13 @@
1
- import { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR } from "./shared/constants.mjs";
2
- import { Awaitable, ComparisonMethod, FailureThresholdOptions, ImageSnapshotCompareOptions, ImageSnapshotComparisonInfo, ImageSnapshotKeyOptions, ImageSnapshotSubjectOptions, ImageSnapshotTimeoutOptions, MatchImageSnapshotOptions, PageImageSnapshotOptions, PixelComparisonOptions, PixelDiffOptions, SetupVisOptions, SnapshotTestMeta, SsimComparisonOptions, SsimDiffOptions, ToMatchImageSnapshotOptions, ToMatchPageImageSnapshotOptions } from "./shared/types.mjs";
3
- import "./server/commands/augment.mjs";
4
- import { MetaTask, setAutoSnapshotOptions } from "./auto_snapshots/auto_snapshot_options.mjs";
5
- import { hasImageSnapshotAction } from "./client/actions/has_image_snapshot_action.mjs";
6
- import { loadImageSnapshotResultsAction } from "./client/actions/load_image_snapshot_results_action.mjs";
7
- import { matchImageSnapshotAction } from "./client/actions/match_image_snapshot_action.mjs";
8
- import { matchPageImageSnapshotAction } from "./client/actions/match_page_image_snapshot_action.mjs";
9
- import { success } from "./client/expect/expectation_result.mjs";
10
- import { ImageSnapshotMatcher } from "./client/expect/to_match_image_snapshot.types.mjs";
11
- import { autoSnapshotMatcher } from "./client/suite/auto_snapshot_matcher.mjs";
12
- import { toTaskId } from "./client/task/task_id.mjs";
1
+ import { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR } from "../shared/constants.mjs";
2
+ import { Awaitable, ComparisonMethod, FailureThresholdOptions, ImageSnapshotCompareOptions, ImageSnapshotComparisonInfo, ImageSnapshotKeyOptions, ImageSnapshotSubjectOptions, ImageSnapshotTimeoutOptions, MatchImageSnapshotOptions, PageImageSnapshotOptions, PixelComparisonOptions, PixelDiffOptions, SetupVisOptions, SnapshotTestMeta, SsimComparisonOptions, SsimDiffOptions, ToMatchImageSnapshotOptions, ToMatchPageImageSnapshotOptions } from "../shared/types.mjs";
3
+ import "../server/commands/augment.mjs";
4
+ import { MetaTask, setAutoSnapshotOptions } from "../auto_snapshots/auto_snapshot_options.mjs";
5
+ import { hasImageSnapshotAction } from "../client/actions/has_image_snapshot_action.mjs";
6
+ import { loadImageSnapshotResultsAction } from "../client/actions/load_image_snapshot_results_action.mjs";
7
+ import { matchImageSnapshotAction } from "../client/actions/match_image_snapshot_action.mjs";
8
+ import { matchPageImageSnapshotAction } from "../client/actions/match_page_image_snapshot_action.mjs";
9
+ import { success } from "../client/expect/expectation_result.mjs";
10
+ import { ImageSnapshotMatcher } from "../client/expect/to_match_image_snapshot.types.mjs";
11
+ import { autoSnapshotMatcher } from "../client/suite/auto_snapshot_matcher.mjs";
12
+ import { toTaskId } from "../client/task/task_id.mjs";
13
13
  export { Awaitable, BASELINE_DIR, ComparisonMethod, DIFF_DIR, FailureThresholdOptions, ImageSnapshotCompareOptions, ImageSnapshotComparisonInfo, ImageSnapshotKeyOptions, ImageSnapshotMatcher, ImageSnapshotSubjectOptions, ImageSnapshotTimeoutOptions, MatchImageSnapshotOptions, MetaTask, NAME, PageImageSnapshotOptions, PixelComparisonOptions, PixelDiffOptions, RESULT_DIR, SNAPSHOT_ROOT_DIR, SetupVisOptions, SnapshotTestMeta, SsimComparisonOptions, SsimDiffOptions, ToMatchImageSnapshotOptions, ToMatchPageImageSnapshotOptions, autoSnapshotMatcher, hasImageSnapshotAction, loadImageSnapshotResultsAction, matchImageSnapshotAction, matchPageImageSnapshotAction, setAutoSnapshotOptions, success, toTaskId };
@@ -0,0 +1,11 @@
1
+ import { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR } from "../shared/constants.mjs";
2
+ import { setAutoSnapshotOptions } from "../auto_snapshots/auto_snapshot_options.mjs";
3
+ import { hasImageSnapshotAction } from "../client/actions/has_image_snapshot_action.mjs";
4
+ import { loadImageSnapshotResultsAction } from "../client/actions/load_image_snapshot_results_action.mjs";
5
+ import { matchImageSnapshotAction } from "../client/actions/match_image_snapshot_action.mjs";
6
+ import { matchPageImageSnapshotAction } from "../client/actions/match_page_image_snapshot_action.mjs";
7
+ import { success } from "../client/expect/expectation_result.mjs";
8
+ import { toTaskId } from "../client/task/task_id.mjs";
9
+ import { autoSnapshotMatcher } from "../client/suite/auto_snapshot_matcher.mjs";
10
+
11
+ export { BASELINE_DIR, DIFF_DIR, NAME, RESULT_DIR, SNAPSHOT_ROOT_DIR, autoSnapshotMatcher, hasImageSnapshotAction, loadImageSnapshotResultsAction, matchImageSnapshotAction, matchPageImageSnapshotAction, setAutoSnapshotOptions, success, toTaskId };
@@ -0,0 +1,5 @@
1
+ import { ComparisonMethod, PixelComparisonOptions, PixelDiffOptions, SsimComparisonOptions, SsimDiffOptions } from "../shared/types.mjs";
2
+ import { VisOptions } from "../config/types.mjs";
3
+ import { vis } from "../config/vis.mjs";
4
+ import { trimCommonFolder } from "../shared/trim_common_folder.mjs";
5
+ export { ComparisonMethod, PixelComparisonOptions, PixelDiffOptions, SsimComparisonOptions, SsimDiffOptions, VisOptions, trimCommonFolder, vis };
@@ -0,0 +1,4 @@
1
+ import { trimCommonFolder } from "../shared/trim_common_folder.mjs";
2
+ import { vis } from "../config/vis.mjs";
3
+
4
+ export { trimCommonFolder, vis };
@@ -0,0 +1,9 @@
1
+ import { vis } from "../../setup/vis.mjs";
2
+ import "../setup.mjs";
3
+
4
+ //#region src/exports/presets/auto.ts
5
+ vis.setup({ auto: true });
6
+
7
+ //#endregion
8
+ export { };
9
+ //# sourceMappingURL=auto.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto.mjs","names":[],"sources":["../../../src/exports/presets/auto.ts"],"sourcesContent":["import { vis } from '../setup.ts'\n\nvis.setup({ auto: true })\n"],"mappings":";;;;AAEA,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { vis } from "../../setup/vis.mjs";
2
+ import "../setup.mjs";
3
+
4
+ //#region src/exports/presets/manual.ts
5
+ vis.setup({ auto: false });
6
+
7
+ //#endregion
8
+ export { };
9
+ //# sourceMappingURL=manual.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manual.mjs","names":[],"sources":["../../../src/exports/presets/manual.ts"],"sourcesContent":["import { vis } from '../setup.ts'\n\nvis.setup({ auto: false })\n"],"mappings":";;;;AAEA,IAAI,MAAM,EAAE,MAAM,OAAO,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { BASELINE_DIR, DIFF_DIR, SNAPSHOT_ROOT_DIR } from "../shared/constants.mjs";
2
+ import { trimCommonFolder } from "../shared/trim_common_folder.mjs";
3
+ import { getSnapshotRootDir } from "../server/snapshot_path.mjs";
4
+ import { resolveLegacySnapshotFileRelativePath, resolveSnapshotSubpathWithinLimits } from "../server/snapshot_path_limits.mjs";
5
+ export { BASELINE_DIR, DIFF_DIR, SNAPSHOT_ROOT_DIR, getSnapshotRootDir, resolveLegacySnapshotFileRelativePath, resolveSnapshotSubpathWithinLimits, trimCommonFolder };
@@ -0,0 +1,6 @@
1
+ import { BASELINE_DIR, DIFF_DIR, SNAPSHOT_ROOT_DIR } from "../shared/constants.mjs";
2
+ import { resolveLegacySnapshotFileRelativePath, resolveSnapshotSubpathWithinLimits } from "../server/snapshot_path_limits.mjs";
3
+ import { trimCommonFolder } from "../shared/trim_common_folder.mjs";
4
+ import { getSnapshotRootDir } from "../server/snapshot_path.mjs";
5
+
6
+ export { BASELINE_DIR, DIFF_DIR, SNAPSHOT_ROOT_DIR, getSnapshotRootDir, resolveLegacySnapshotFileRelativePath, resolveSnapshotSubpathWithinLimits, trimCommonFolder };
@@ -0,0 +1,4 @@
1
+ import "../client/expect/extend.mjs";
2
+ import "../client/page/extend.mjs";
3
+ import { VisClientConfigurator, createVis } from "../setup/create_vis.mjs";
4
+ export { VisClientConfigurator, createVis };
@@ -0,0 +1,5 @@
1
+ import "../client/expect/extend.mjs";
2
+ import "../client/page/extend.mjs";
3
+ import { createVis } from "../setup/create_vis.mjs";
4
+
5
+ export { createVis };
@@ -0,0 +1,3 @@
1
+ import "./setup-api.mjs";
2
+ import { vis } from "../setup/vis.mjs";
3
+ export { vis };
@@ -0,0 +1,4 @@
1
+ import "./setup-api.mjs";
2
+ import { vis } from "../setup/vis.mjs";
3
+
4
+ export { vis };
@@ -0,0 +1,4 @@
1
+ import { UNI_IMAGE_DATA, UNI_PNG_BASE64, UNI_PNG_URL } from "../../testing/constants.mjs";
2
+ import { stubSuite } from "../../server/testing/stubSuite.mjs";
3
+ import { getVisOption } from "../../server/vis_options.mjs";
4
+ export { UNI_IMAGE_DATA, UNI_PNG_BASE64, UNI_PNG_URL, getVisOption, stubSuite };
@@ -0,0 +1,5 @@
1
+ import { getVisOption } from "../../server/vis_options.mjs";
2
+ import { UNI_IMAGE_DATA, UNI_PNG_BASE64, UNI_PNG_URL } from "../../testing/constants.mjs";
3
+ import { stubSuite } from "../../server/testing/stubSuite.mjs";
4
+
5
+ export { UNI_IMAGE_DATA, UNI_PNG_BASE64, UNI_PNG_URL, getVisOption, stubSuite };
@@ -0,0 +1,2 @@
1
+ import { UNI_IMAGE_DATA, UNI_PNG_BASE64, UNI_PNG_URL } from "../testing/constants.mjs";
2
+ export { UNI_IMAGE_DATA, UNI_PNG_BASE64, UNI_PNG_URL };
@@ -0,0 +1,3 @@
1
+ import { UNI_IMAGE_DATA, UNI_PNG_BASE64, UNI_PNG_URL } from "../testing/constants.mjs";
2
+
3
+ export { UNI_IMAGE_DATA, UNI_PNG_BASE64, UNI_PNG_URL };
@@ -1 +1 @@
1
- {"version":3,"file":"prepare_image_snapshot_comparison.mjs","names":[],"sources":["../../../src/server/commands/prepare_image_snapshot_comparison.ts"],"sourcesContent":["import { resolve } from 'pathe'\nimport { isBase64String } from '../../shared/base64.ts'\nimport type { PrepareImageSnapshotComparisonCommand } from '../../shared/commands.types.ts'\nimport type { ImageSnapshotComparisonInfo } from '../../shared/types.ts'\nimport { file } from '../externals/file.ts'\nimport { getProjectRoot } from '../project.ts'\nimport { takeSnapshot } from '../snapshot.ts'\nimport { snapshotWriter } from '../snapshot_writer.ts'\nimport { visServerContext } from '../vis_server_context.ts'\nimport type { ExtendedBrowserCommand } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const prepareImageSnapshotComparison: ExtendedBrowserCommand<\n\tParameters<PrepareImageSnapshotComparisonCommand['prepareImageSnapshotComparison']>\n> = async (context, taskId, subject, options): Promise<ImageSnapshotComparisonInfo | undefined> => {\n\tassertTestPathDefined(context, 'prepareImageSnapshotComparison')\n\t// vitest:browser passes in `null` when not defined\n\tif (!options) options = {}\n\toptions.timeout = options.timeout ?? 30000\n\n\tconst projectRoot = getProjectRoot(context)\n\tconst info = await visServerContext.getSnapshotInfo(context, taskId, options)\n\n\tconst baselineBuffer = await file.tryReadFile(resolve(projectRoot, info.baselinePath))\n\tif (!baselineBuffer && isBase64String(subject)) {\n\t\tawait snapshotWriter.writeBase64(resolve(projectRoot, info.resultPath), subject)\n\t\treturn {\n\t\t\t...info,\n\t\t\tprojectRoot,\n\t\t\tresult: subject,\n\t\t}\n\t}\n\n\tconst resultBuffer = await takeSnapshot(context, projectRoot, info.resultPath, subject, options)\n\treturn {\n\t\t...info,\n\t\tprojectRoot,\n\t\tbaseline: baselineBuffer?.toString('base64'),\n\t\tresult: resultBuffer.toString('base64'),\n\t}\n}\n"],"mappings":";;;;;;;;;;AAYA,MAAa,iCAET,OAAO,SAAS,QAAQ,SAAS,YAA8D;AAClG,uBAAsB,SAAS,iCAAiC;AAEhE,KAAI,CAAC,QAAS,WAAU,EAAE;AAC1B,SAAQ,UAAU,QAAQ,WAAW;CAErC,MAAM,cAAc,eAAe,QAAQ;CAC3C,MAAM,OAAO,MAAM,iBAAiB,gBAAgB,SAAS,QAAQ,QAAQ;CAE7E,MAAM,iBAAiB,MAAM,KAAK,YAAY,QAAQ,aAAa,KAAK,aAAa,CAAC;AACtF,KAAI,CAAC,kBAAkB,eAAe,QAAQ,EAAE;AAC/C,QAAM,eAAe,YAAY,QAAQ,aAAa,KAAK,WAAW,EAAE,QAAQ;AAChF,SAAO;GACN,GAAG;GACH;GACA,QAAQ;GACR;;CAGF,MAAM,eAAe,MAAM,aAAa,SAAS,aAAa,KAAK,YAAY,SAAS,QAAQ;AAChG,QAAO;EACN,GAAG;EACH;EACA,UAAU,gBAAgB,SAAS,SAAS;EAC5C,QAAQ,aAAa,SAAS,SAAS;EACvC"}
1
+ {"version":3,"file":"prepare_image_snapshot_comparison.mjs","names":[],"sources":["../../../src/server/commands/prepare_image_snapshot_comparison.ts"],"sourcesContent":["import { resolve } from 'pathe'\nimport { isBase64String } from '../../shared/base64.ts'\nimport type { PrepareImageSnapshotComparisonCommand } from '../../shared/commands.types.ts'\nimport type { ImageSnapshotComparisonInfo } from '../../shared/types.ts'\nimport { file } from '../externals/file.ts'\nimport { getProjectRoot } from '../project.ts'\nimport { takeSnapshot } from '../snapshot.ts'\nimport { snapshotWriter } from '../snapshot_writer.ts'\nimport { visServerContext } from '../vis_server_context.ts'\nimport type { ExtendedBrowserCommand } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const prepareImageSnapshotComparison: ExtendedBrowserCommand<\n\tParameters<PrepareImageSnapshotComparisonCommand['prepareImageSnapshotComparison']>\n> = async (context, taskId, subject, options): Promise<ImageSnapshotComparisonInfo | undefined> => {\n\tassertTestPathDefined(context, 'prepareImageSnapshotComparison')\n\t// vitest:browser passes in `null` when not defined\n\tif (!options) options = {}\n\toptions.timeout = options.timeout ?? 30000\n\n\tconst projectRoot = getProjectRoot(context)\n\tconst info = await visServerContext.getSnapshotInfo(context, taskId, options)\n\n\tconst baselineBuffer = await file.tryReadFile(resolve(projectRoot, info.baselinePath))\n\tif (!baselineBuffer && isBase64String(subject)) {\n\t\tawait snapshotWriter.writeBase64(resolve(projectRoot, info.resultPath), subject)\n\t\treturn {\n\t\t\t...info,\n\t\t\tprojectRoot,\n\t\t\tresult: subject,\n\t\t} as ImageSnapshotComparisonInfo\n\t}\n\n\tconst resultBuffer = await takeSnapshot(context, projectRoot, info.resultPath, subject, options)\n\treturn {\n\t\t...info,\n\t\tprojectRoot,\n\t\tbaseline: baselineBuffer?.toString('base64'),\n\t\tresult: resultBuffer.toString('base64'),\n\t} as ImageSnapshotComparisonInfo\n}\n"],"mappings":";;;;;;;;;;AAYA,MAAa,iCAET,OAAO,SAAS,QAAQ,SAAS,YAA8D;AAClG,uBAAsB,SAAS,iCAAiC;AAEhE,KAAI,CAAC,QAAS,WAAU,EAAE;AAC1B,SAAQ,UAAU,QAAQ,WAAW;CAErC,MAAM,cAAc,eAAe,QAAQ;CAC3C,MAAM,OAAO,MAAM,iBAAiB,gBAAgB,SAAS,QAAQ,QAAQ;CAE7E,MAAM,iBAAiB,MAAM,KAAK,YAAY,QAAQ,aAAa,KAAK,aAAa,CAAC;AACtF,KAAI,CAAC,kBAAkB,eAAe,QAAQ,EAAE;AAC/C,QAAM,eAAe,YAAY,QAAQ,aAAa,KAAK,WAAW,EAAE,QAAQ;AAChF,SAAO;GACN,GAAG;GACH;GACA,QAAQ;GACR;;CAGF,MAAM,eAAe,MAAM,aAAa,SAAS,aAAa,KAAK,YAAY,SAAS,QAAQ;AAChG,QAAO;EACN,GAAG;EACH;EACA,UAAU,gBAAgB,SAAS,SAAS;EAC5C,QAAQ,aAAa,SAAS,SAAS;EACvC"}
@@ -1 +1 @@
1
- {"version":3,"file":"prepare_page_image_snapshot_comparison.mjs","names":[],"sources":["../../../src/server/commands/prepare_page_image_snapshot_comparison.ts"],"sourcesContent":["import { resolve } from 'pathe'\nimport type { PreparePageImageSnapshotComparisonCommand } from '../../shared/commands.types.ts'\nimport { file } from '../externals/file.ts'\nimport { getProjectRoot } from '../project.ts'\nimport { takePageSnapshot } from '../snapshot.ts'\nimport { visServerContext } from '../vis_server_context.ts'\nimport type { ExtendedBrowserCommand } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const preparePageImageSnapshotComparison: ExtendedBrowserCommand<\n\tParameters<PreparePageImageSnapshotComparisonCommand['preparePageImageSnapshotComparison']>\n> = async (context, taskId, options) => {\n\tassertTestPathDefined(context, 'preparePageImageSnapshotComparison')\n\t// vitest:browser passes in `null` when not defined\n\tif (!options) options = {}\n\toptions.timeout = options.timeout ?? 30000\n\n\tconst projectRoot = getProjectRoot(context)\n\tconst info = await visServerContext.getSnapshotInfo(context, taskId, options)\n\tconst baselineBuffer = await file.tryReadFile(resolve(projectRoot, info.baselinePath))\n\tconst resultBuffer = await takePageSnapshot(context, projectRoot, info.resultPath, options)\n\treturn {\n\t\t...info,\n\t\tprojectRoot,\n\t\tbaseline: baselineBuffer?.toString('base64'),\n\t\tresult: resultBuffer.toString('base64'),\n\t}\n}\n"],"mappings":";;;;;;;;AASA,MAAa,qCAET,OAAO,SAAS,QAAQ,YAAY;AACvC,uBAAsB,SAAS,qCAAqC;AAEpE,KAAI,CAAC,QAAS,WAAU,EAAE;AAC1B,SAAQ,UAAU,QAAQ,WAAW;CAErC,MAAM,cAAc,eAAe,QAAQ;CAC3C,MAAM,OAAO,MAAM,iBAAiB,gBAAgB,SAAS,QAAQ,QAAQ;CAC7E,MAAM,iBAAiB,MAAM,KAAK,YAAY,QAAQ,aAAa,KAAK,aAAa,CAAC;CACtF,MAAM,eAAe,MAAM,iBAAiB,SAAS,aAAa,KAAK,YAAY,QAAQ;AAC3F,QAAO;EACN,GAAG;EACH;EACA,UAAU,gBAAgB,SAAS,SAAS;EAC5C,QAAQ,aAAa,SAAS,SAAS;EACvC"}
1
+ {"version":3,"file":"prepare_page_image_snapshot_comparison.mjs","names":[],"sources":["../../../src/server/commands/prepare_page_image_snapshot_comparison.ts"],"sourcesContent":["import { resolve } from 'pathe'\nimport type { PreparePageImageSnapshotComparisonCommand } from '../../shared/commands.types.ts'\nimport type { ImageSnapshotComparisonInfo } from '../../shared/types.ts'\nimport { file } from '../externals/file.ts'\nimport { getProjectRoot } from '../project.ts'\nimport { takePageSnapshot } from '../snapshot.ts'\nimport { visServerContext } from '../vis_server_context.ts'\nimport type { ExtendedBrowserCommand } from '../vis_server_context.types.ts'\nimport { assertTestPathDefined } from './_assertions.ts'\n\nexport const preparePageImageSnapshotComparison: ExtendedBrowserCommand<\n\tParameters<PreparePageImageSnapshotComparisonCommand['preparePageImageSnapshotComparison']>\n> = async (context, taskId, options) => {\n\tassertTestPathDefined(context, 'preparePageImageSnapshotComparison')\n\t// vitest:browser passes in `null` when not defined\n\tif (!options) options = {}\n\toptions.timeout = options.timeout ?? 30000\n\n\tconst projectRoot = getProjectRoot(context)\n\tconst info = await visServerContext.getSnapshotInfo(context, taskId, options)\n\tconst baselineBuffer = await file.tryReadFile(resolve(projectRoot, info.baselinePath))\n\tconst resultBuffer = await takePageSnapshot(context, projectRoot, info.resultPath, options)\n\treturn {\n\t\t...info,\n\t\tprojectRoot,\n\t\tbaseline: baselineBuffer?.toString('base64'),\n\t\tresult: resultBuffer.toString('base64'),\n\t} as ImageSnapshotComparisonInfo\n}\n"],"mappings":";;;;;;;;AAUA,MAAa,qCAET,OAAO,SAAS,QAAQ,YAAY;AACvC,uBAAsB,SAAS,qCAAqC;AAEpE,KAAI,CAAC,QAAS,WAAU,EAAE;AAC1B,SAAQ,UAAU,QAAQ,WAAW;CAErC,MAAM,cAAc,eAAe,QAAQ;CAC3C,MAAM,OAAO,MAAM,iBAAiB,gBAAgB,SAAS,QAAQ,QAAQ;CAC7E,MAAM,iBAAiB,MAAM,KAAK,YAAY,QAAQ,aAAa,KAAK,aAAa,CAAC;CACtF,MAAM,eAAe,MAAM,iBAAiB,SAAS,aAAa,KAAK,YAAY,QAAQ;AAC3F,QAAO;EACN,GAAG;EACH;EACA,UAAU,gBAAgB,SAAS,SAAS;EAC5C,QAAQ,aAAa,SAAS,SAAS;EACvC"}
@@ -1,4 +1,3 @@
1
- import "../config/types.mjs";
2
1
  import "./vis_server_context.types.mjs";
3
2
 
4
3
  //#region src/server/snapshot_path.d.ts
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot_path.d.mts","names":[],"sources":["../../src/server/snapshot_path.ts"],"sourcesContent":[],"mappings":";;;;;;AAgCA;;;;;;;iBAAgB,kBAAA"}
1
+ {"version":3,"file":"snapshot_path.d.mts","names":[],"sources":["../../src/server/snapshot_path.ts"],"sourcesContent":[],"mappings":";;;;AAgCA;;;;;;;;iBAAgB,kBAAA"}
@@ -0,0 +1,28 @@
1
+ //#region src/server/snapshot_path_limits.d.ts
2
+
3
+ /**
4
+ * Returns {@link rawSnapshotSubpath} or a tersified final segment when `shortenLongSnapshotPaths` is on and
5
+ * `snapshotBaselineRootAbs` + subpath + a minimal `0.png` would exceed {@link legacySnapshotPathExceedsLimit},
6
+ * or a subpath segment exceeds NTFS length. Long snapshot **filenames** (`taskId-key.png`) are shortened
7
+ * separately via {@link resolveLegacySnapshotFileRelativePath}. Baseline, result, and diff trees share the
8
+ * same resolved subpath via {@link getTaskSubpath}.
9
+ */
10
+ declare function resolveSnapshotSubpathWithinLimits(params: {
11
+ snapshotBaselineRootAbs: string;
12
+ rawSnapshotSubpath: string;
13
+ shortenLongSnapshotPaths: boolean;
14
+ }): string;
15
+ /**
16
+ * When `shortenLongSnapshotPaths` is on and the legacy `{@link getLegacySnapshotFilename}` path exceeds
17
+ * {@link legacySnapshotPathExceedsLimit}, rewrites only the final path segment (after the last `/`) like
18
+ * {@link formatCondensedSnapshotSubpath}, so nested `taskId` directories stay intact and `…-*.png` globs keep working.
19
+ */
20
+ declare function resolveLegacySnapshotFileRelativePath(params: {
21
+ projectRoot: string;
22
+ baselineDir: string;
23
+ legacySnapshotFilename: string;
24
+ shortenLongSnapshotPaths: boolean;
25
+ }): string;
26
+ //#endregion
27
+ export { resolveLegacySnapshotFileRelativePath, resolveSnapshotSubpathWithinLimits };
28
+ //# sourceMappingURL=snapshot_path_limits.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot_path_limits.d.mts","names":[],"sources":["../../src/server/snapshot_path_limits.ts"],"sourcesContent":[],"mappings":";;;;;;;;;iBAwFgB,kCAAA;;;;;;;;;;iBAoBA,qCAAA"}
@@ -0,0 +1,92 @@
1
+ import { basename, dirname, extname, join, resolve } from "pathe";
2
+ import { createHash } from "node:crypto";
3
+
4
+ //#region src/server/snapshot_path_limits.ts
5
+ /**
6
+ * Budget under {@link WIN_MAX_PATH} to leave margin for tooling and long `projectRoot` prefixes.
7
+ */
8
+ const WIN_SNAPSHOT_PATH_BUDGET = 250;
9
+ /** NTFS maximum length of a single path component (directory or file name). */
10
+ const NTFS_MAX_FILENAME = 255;
11
+ /** Max length of the readable prefix taken from the original basename (tersify-style). */
12
+ const SNAPSHOT_SUBPATH_PREFIX_MAX_LEN = 24;
13
+ /**
14
+ * Minimal probe file under the snapshot folder — detects when the directory prefix alone is too long.
15
+ */
16
+ const SNAPSHOT_SUBPATH_PROBE_MIN_BASENAME = "0.png";
17
+ function absoluteSnapshotFilePath(projectRoot, baselineDir, snapshotFilename) {
18
+ return resolve(projectRoot, join(baselineDir, snapshotFilename));
19
+ }
20
+ /**
21
+ * @returns true when the legacy absolute path should be shortened for Windows-safe tooling.
22
+ */
23
+ function legacySnapshotPathExceedsLimit(absolutePath) {
24
+ if (absolutePath.length >= WIN_SNAPSHOT_PATH_BUDGET) return true;
25
+ if (basename(absolutePath).length > NTFS_MAX_FILENAME) return true;
26
+ return false;
27
+ }
28
+ /**
29
+ * First readable token of a snapshot basename stem (before the first `.`), similar in spirit to
30
+ * [tersify](https://github.com/unional/tersify) terse labels — keeps a short human hint, not the whole name.
31
+ */
32
+ function snapshotBasenameTersifyPrefix(stem) {
33
+ if (!stem) return "snap";
34
+ const dot = stem.indexOf(".");
35
+ return ((dot === -1 ? stem : stem.slice(0, dot)).replace(/[^\w-]/g, "") || "snap").slice(0, SNAPSHOT_SUBPATH_PREFIX_MAX_LEN);
36
+ }
37
+ /**
38
+ * Keeps all directory segments of {@link rawSnapshotSubpath}; only the final filename is replaced with
39
+ * `{prefix}-{hash12}{ext}` where `prefix` comes from the first segment of the original stem (e.g. `Button.stories.ts` → `Button`).
40
+ */
41
+ function formatCondensedSnapshotSubpath(rawSnapshotSubpath) {
42
+ const norm = rawSnapshotSubpath.replace(/\\/g, "/");
43
+ const dir = dirname(norm);
44
+ const file = basename(norm);
45
+ const ext = extname(file);
46
+ const shortFile = `${snapshotBasenameTersifyPrefix(ext ? file.slice(0, -ext.length) : file)}-${createHash("sha256").update(norm).digest("hex").slice(0, 12)}${ext}`;
47
+ if (dir === "." || dir === "") return shortFile;
48
+ return join(dir, shortFile);
49
+ }
50
+ /**
51
+ * `snapshotBaselineRootAbs` is the absolute `__baselines__` root; with `rawSnapshotSubpath` this yields the
52
+ * test-module snapshot directory. Only checks whether that layout can host a tiny baseline file; long
53
+ * `taskId-key.png` paths are handled with {@link resolveLegacySnapshotFileRelativePath} using real names.
54
+ */
55
+ function rawSnapshotSubpathExceedsSafePathBudget(snapshotBaselineRootAbs, rawSnapshotSubpath) {
56
+ if (legacySnapshotPathExceedsLimit(resolve(resolve(snapshotBaselineRootAbs, rawSnapshotSubpath), SNAPSHOT_SUBPATH_PROBE_MIN_BASENAME))) return true;
57
+ for (const segment of rawSnapshotSubpath.split(/[/\\]/)) if (segment.length > NTFS_MAX_FILENAME) return true;
58
+ return false;
59
+ }
60
+ /**
61
+ * Returns {@link rawSnapshotSubpath} or a tersified final segment when `shortenLongSnapshotPaths` is on and
62
+ * `snapshotBaselineRootAbs` + subpath + a minimal `0.png` would exceed {@link legacySnapshotPathExceedsLimit},
63
+ * or a subpath segment exceeds NTFS length. Long snapshot **filenames** (`taskId-key.png`) are shortened
64
+ * separately via {@link resolveLegacySnapshotFileRelativePath}. Baseline, result, and diff trees share the
65
+ * same resolved subpath via {@link getTaskSubpath}.
66
+ */
67
+ function resolveSnapshotSubpathWithinLimits(params) {
68
+ const { snapshotBaselineRootAbs, rawSnapshotSubpath, shortenLongSnapshotPaths } = params;
69
+ if (!shortenLongSnapshotPaths) return rawSnapshotSubpath;
70
+ if (!rawSnapshotSubpathExceedsSafePathBudget(snapshotBaselineRootAbs, rawSnapshotSubpath)) return rawSnapshotSubpath;
71
+ return formatCondensedSnapshotSubpath(rawSnapshotSubpath);
72
+ }
73
+ /**
74
+ * When `shortenLongSnapshotPaths` is on and the legacy `{@link getLegacySnapshotFilename}` path exceeds
75
+ * {@link legacySnapshotPathExceedsLimit}, rewrites only the final path segment (after the last `/`) like
76
+ * {@link formatCondensedSnapshotSubpath}, so nested `taskId` directories stay intact and `…-*.png` globs keep working.
77
+ */
78
+ function resolveLegacySnapshotFileRelativePath(params) {
79
+ const { projectRoot, baselineDir, legacySnapshotFilename, shortenLongSnapshotPaths } = params;
80
+ if (!shortenLongSnapshotPaths) return legacySnapshotFilename;
81
+ const norm = legacySnapshotFilename.replace(/\\/g, "/");
82
+ if (!legacySnapshotPathExceedsLimit(absoluteSnapshotFilePath(projectRoot, baselineDir, norm))) return legacySnapshotFilename;
83
+ return formatCondensedSnapshotSubpath(norm);
84
+ }
85
+ function getLegacySnapshotFilename(params) {
86
+ const key = params.explicitSnapshotKey ?? (typeof params.defaultSnapshotKey === "string" ? params.defaultSnapshotKey : String(params.taskCount));
87
+ return `${params.taskId}-${key}.png`;
88
+ }
89
+
90
+ //#endregion
91
+ export { absoluteSnapshotFilePath, getLegacySnapshotFilename, resolveLegacySnapshotFileRelativePath, resolveSnapshotSubpathWithinLimits };
92
+ //# sourceMappingURL=snapshot_path_limits.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"snapshot_path_limits.mjs","names":[],"sources":["../../src/server/snapshot_path_limits.ts"],"sourcesContent":["import { createHash } from 'node:crypto'\nimport { basename, dirname, extname, join, resolve } from 'pathe'\n\n/** Classic Win32 `MAX_PATH` includes NUL; tools often treat ~259 as usable. */\nexport const WIN_MAX_PATH = 260\n\n/**\n * Budget under {@link WIN_MAX_PATH} to leave margin for tooling and long `projectRoot` prefixes.\n */\nexport const WIN_SNAPSHOT_PATH_BUDGET = 250\n\n/** NTFS maximum length of a single path component (directory or file name). */\nexport const NTFS_MAX_FILENAME = 255\n\n/** Max length of the readable prefix taken from the original basename (tersify-style). */\nconst SNAPSHOT_SUBPATH_PREFIX_MAX_LEN = 24\n\n/**\n * Minimal probe file under the snapshot folder — detects when the directory prefix alone is too long.\n */\nconst SNAPSHOT_SUBPATH_PROBE_MIN_BASENAME = '0.png'\n\nexport function absoluteSnapshotFilePath(projectRoot: string, baselineDir: string, snapshotFilename: string): string {\n\treturn resolve(projectRoot, join(baselineDir, snapshotFilename))\n}\n\n/**\n * @returns true when the legacy absolute path should be shortened for Windows-safe tooling.\n */\nexport function legacySnapshotPathExceedsLimit(absolutePath: string): boolean {\n\tif (absolutePath.length >= WIN_SNAPSHOT_PATH_BUDGET) return true\n\tif (basename(absolutePath).length > NTFS_MAX_FILENAME) return true\n\treturn false\n}\n\n/**\n * First readable token of a snapshot basename stem (before the first `.`), similar in spirit to\n * [tersify](https://github.com/unional/tersify) terse labels — keeps a short human hint, not the whole name.\n */\nfunction snapshotBasenameTersifyPrefix(stem: string): string {\n\tif (!stem) return 'snap'\n\tconst dot = stem.indexOf('.')\n\tconst first = dot === -1 ? stem : stem.slice(0, dot)\n\tconst cleaned = first.replace(/[^\\w-]/g, '')\n\treturn (cleaned || 'snap').slice(0, SNAPSHOT_SUBPATH_PREFIX_MAX_LEN)\n}\n\n/**\n * Keeps all directory segments of {@link rawSnapshotSubpath}; only the final filename is replaced with\n * `{prefix}-{hash12}{ext}` where `prefix` comes from the first segment of the original stem (e.g. `Button.stories.ts` → `Button`).\n */\nexport function formatCondensedSnapshotSubpath(rawSnapshotSubpath: string): string {\n\tconst norm = rawSnapshotSubpath.replace(/\\\\/g, '/')\n\tconst dir = dirname(norm)\n\tconst file = basename(norm)\n\tconst ext = extname(file)\n\tconst stem = ext ? file.slice(0, -ext.length) : file\n\tconst prefix = snapshotBasenameTersifyPrefix(stem)\n\tconst h = createHash('sha256').update(norm).digest('hex').slice(0, 12)\n\tconst shortFile = `${prefix}-${h}${ext}`\n\tif (dir === '.' || dir === '') return shortFile\n\treturn join(dir, shortFile)\n}\n\n/**\n * `snapshotBaselineRootAbs` is the absolute `__baselines__` root; with `rawSnapshotSubpath` this yields the\n * test-module snapshot directory. Only checks whether that layout can host a tiny baseline file; long\n * `taskId-key.png` paths are handled with {@link resolveLegacySnapshotFileRelativePath} using real names.\n */\nfunction rawSnapshotSubpathExceedsSafePathBudget(snapshotBaselineRootAbs: string, rawSnapshotSubpath: string): boolean {\n\tconst dirAbs = resolve(snapshotBaselineRootAbs, rawSnapshotSubpath)\n\n\tif (legacySnapshotPathExceedsLimit(resolve(dirAbs, SNAPSHOT_SUBPATH_PROBE_MIN_BASENAME))) return true\n\n\tfor (const segment of rawSnapshotSubpath.split(/[/\\\\]/)) {\n\t\tif (segment.length > NTFS_MAX_FILENAME) return true\n\t}\n\n\treturn false\n}\n\n/**\n * Returns {@link rawSnapshotSubpath} or a tersified final segment when `shortenLongSnapshotPaths` is on and\n * `snapshotBaselineRootAbs` + subpath + a minimal `0.png` would exceed {@link legacySnapshotPathExceedsLimit},\n * or a subpath segment exceeds NTFS length. Long snapshot **filenames** (`taskId-key.png`) are shortened\n * separately via {@link resolveLegacySnapshotFileRelativePath}. Baseline, result, and diff trees share the\n * same resolved subpath via {@link getTaskSubpath}.\n */\nexport function resolveSnapshotSubpathWithinLimits(params: {\n\tsnapshotBaselineRootAbs: string\n\trawSnapshotSubpath: string\n\tshortenLongSnapshotPaths: boolean\n}): string {\n\tconst { snapshotBaselineRootAbs, rawSnapshotSubpath, shortenLongSnapshotPaths } = params\n\tif (!shortenLongSnapshotPaths) return rawSnapshotSubpath\n\n\tif (!rawSnapshotSubpathExceedsSafePathBudget(snapshotBaselineRootAbs, rawSnapshotSubpath)) {\n\t\treturn rawSnapshotSubpath\n\t}\n\n\treturn formatCondensedSnapshotSubpath(rawSnapshotSubpath)\n}\n\n/**\n * When `shortenLongSnapshotPaths` is on and the legacy `{@link getLegacySnapshotFilename}` path exceeds\n * {@link legacySnapshotPathExceedsLimit}, rewrites only the final path segment (after the last `/`) like\n * {@link formatCondensedSnapshotSubpath}, so nested `taskId` directories stay intact and `…-*.png` globs keep working.\n */\nexport function resolveLegacySnapshotFileRelativePath(params: {\n\tprojectRoot: string\n\tbaselineDir: string\n\tlegacySnapshotFilename: string\n\tshortenLongSnapshotPaths: boolean\n}): string {\n\tconst { projectRoot, baselineDir, legacySnapshotFilename, shortenLongSnapshotPaths } = params\n\tif (!shortenLongSnapshotPaths) return legacySnapshotFilename\n\n\tconst norm = legacySnapshotFilename.replace(/\\\\/g, '/')\n\tconst abs = absoluteSnapshotFilePath(projectRoot, baselineDir, norm)\n\tif (!legacySnapshotPathExceedsLimit(abs)) return legacySnapshotFilename\n\n\treturn formatCondensedSnapshotSubpath(norm)\n}\n\nexport function getLegacySnapshotFilename(params: {\n\ttaskId: string\n\texplicitSnapshotKey: string | undefined\n\tdefaultSnapshotKey: string | undefined\n\ttaskCount: number\n}): string {\n\tconst key =\n\t\tparams.explicitSnapshotKey ??\n\t\t(typeof params.defaultSnapshotKey === 'string' ? params.defaultSnapshotKey : String(params.taskCount))\n\treturn `${params.taskId}-${key}.png`\n}\n"],"mappings":";;;;;;;AASA,MAAa,2BAA2B;;AAGxC,MAAa,oBAAoB;;AAGjC,MAAM,kCAAkC;;;;AAKxC,MAAM,sCAAsC;AAE5C,SAAgB,yBAAyB,aAAqB,aAAqB,kBAAkC;AACpH,QAAO,QAAQ,aAAa,KAAK,aAAa,iBAAiB,CAAC;;;;;AAMjE,SAAgB,+BAA+B,cAA+B;AAC7E,KAAI,aAAa,UAAU,yBAA0B,QAAO;AAC5D,KAAI,SAAS,aAAa,CAAC,SAAS,kBAAmB,QAAO;AAC9D,QAAO;;;;;;AAOR,SAAS,8BAA8B,MAAsB;AAC5D,KAAI,CAAC,KAAM,QAAO;CAClB,MAAM,MAAM,KAAK,QAAQ,IAAI;AAG7B,UAFc,QAAQ,KAAK,OAAO,KAAK,MAAM,GAAG,IAAI,EAC9B,QAAQ,WAAW,GAAG,IACzB,QAAQ,MAAM,GAAG,gCAAgC;;;;;;AAOrE,SAAgB,+BAA+B,oBAAoC;CAClF,MAAM,OAAO,mBAAmB,QAAQ,OAAO,IAAI;CACnD,MAAM,MAAM,QAAQ,KAAK;CACzB,MAAM,OAAO,SAAS,KAAK;CAC3B,MAAM,MAAM,QAAQ,KAAK;CAIzB,MAAM,YAAY,GAFH,8BADF,MAAM,KAAK,MAAM,GAAG,CAAC,IAAI,OAAO,GAAG,KACE,CAEtB,GADlB,WAAW,SAAS,CAAC,OAAO,KAAK,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG,GACnC;AACnC,KAAI,QAAQ,OAAO,QAAQ,GAAI,QAAO;AACtC,QAAO,KAAK,KAAK,UAAU;;;;;;;AAQ5B,SAAS,wCAAwC,yBAAiC,oBAAqC;AAGtH,KAAI,+BAA+B,QAFpB,QAAQ,yBAAyB,mBAAmB,EAEhB,oCAAoC,CAAC,CAAE,QAAO;AAEjG,MAAK,MAAM,WAAW,mBAAmB,MAAM,QAAQ,CACtD,KAAI,QAAQ,SAAS,kBAAmB,QAAO;AAGhD,QAAO;;;;;;;;;AAUR,SAAgB,mCAAmC,QAIxC;CACV,MAAM,EAAE,yBAAyB,oBAAoB,6BAA6B;AAClF,KAAI,CAAC,yBAA0B,QAAO;AAEtC,KAAI,CAAC,wCAAwC,yBAAyB,mBAAmB,CACxF,QAAO;AAGR,QAAO,+BAA+B,mBAAmB;;;;;;;AAQ1D,SAAgB,sCAAsC,QAK3C;CACV,MAAM,EAAE,aAAa,aAAa,wBAAwB,6BAA6B;AACvF,KAAI,CAAC,yBAA0B,QAAO;CAEtC,MAAM,OAAO,uBAAuB,QAAQ,OAAO,IAAI;AAEvD,KAAI,CAAC,+BADO,yBAAyB,aAAa,aAAa,KAAK,CAC5B,CAAE,QAAO;AAEjD,QAAO,+BAA+B,KAAK;;AAG5C,SAAgB,0BAA0B,QAK/B;CACV,MAAM,MACL,OAAO,wBACN,OAAO,OAAO,uBAAuB,WAAW,OAAO,qBAAqB,OAAO,OAAO,UAAU;AACtG,QAAO,GAAG,OAAO,OAAO,GAAG,IAAI"}
@@ -1,9 +1,10 @@
1
1
  import { BASELINE_DIR, DIFF_DIR, RESULT_DIR } from "../shared/constants.mjs";
2
+ import { resolveSnapshotSubpathWithinLimits } from "./snapshot_path_limits.mjs";
2
3
  import { getProjectName, getProjectRoot } from "./project.mjs";
3
4
  import { getSnapshotSubpath, resolveSnapshotRootDir } from "./snapshot_path.mjs";
4
5
  import { getVisOption } from "./vis_options.mjs";
5
6
  import { deps } from "./vis_server_context.deps.mjs";
6
- import { join, relative } from "pathe";
7
+ import { join, relative, resolve } from "pathe";
7
8
 
8
9
  //#region src/server/suite.ts
9
10
  const suites = {};
@@ -71,7 +72,12 @@ function createModule(state, testPath, options) {
71
72
  };
72
73
  }
73
74
  function getTaskSubpath(state, testPath, options) {
74
- return getSnapshotSubpath(relative(state.projectRoot, testPath), options);
75
+ const raw = getSnapshotSubpath(relative(state.projectRoot, testPath), options);
76
+ return resolveSnapshotSubpathWithinLimits({
77
+ snapshotBaselineRootAbs: resolve(state.projectRoot, state.snapshotBaselineDir),
78
+ rawSnapshotSubpath: raw,
79
+ shortenLongSnapshotPaths: options.shortenLongSnapshotPaths === true
80
+ });
75
81
  }
76
82
  function getSuite(context) {
77
83
  return suites[getSuiteId(context)];
@@ -1 +1 @@
1
- {"version":3,"file":"suite.mjs","names":[],"sources":["../../src/server/suite.ts"],"sourcesContent":["import { join, relative } from 'pathe'\nimport type { VisOptions } from '../config/types.ts'\nimport { BASELINE_DIR, DIFF_DIR, RESULT_DIR } from '../shared/constants.ts'\nimport { getProjectName, getProjectRoot } from './project.ts'\nimport { getSnapshotSubpath, resolveSnapshotRootDir } from './snapshot_path.ts'\nimport { getVisOption } from './vis_options.ts'\nimport { deps } from './vis_server_context.deps.ts'\nimport type { ExtendedBrowserCommandContext, VisSuite, VisSuites } from './vis_server_context.types.ts'\n\nconst suites: VisSuites = {}\n\n/**\n * Setup suite is called on each test file's beforeAll hook.\n * Test files include vitest test files and storybook story files.\n * It needs to make sure there is no race condition between the test files.\n */\nexport async function setupSuite(browserContext: ExtendedBrowserCommandContext) {\n\tconst suiteId = getSuiteId(browserContext)\n\tconst visOptions = getVisOption(browserContext)\n\n\tif (!suites[suiteId]) {\n\t\tsuites[suiteId] = createSuite(browserContext, visOptions)\n\t}\n\n\tconst suite = await suites[suiteId]\n\n\tconst { taskSubpath, baselineDir, diffDir, resultDir, tasks } = createModule(\n\t\tsuite,\n\t\tbrowserContext.testPath!,\n\t\tvisOptions,\n\t)\n\tsuite.modules[taskSubpath] = { baselineDir, diffDir, resultDir, tasks }\n\n\tawait Promise.allSettled([deps.rimraf(diffDir), deps.rimraf(resultDir)])\n\treturn {\n\t\tcomparisonMethod: visOptions.comparisonMethod,\n\t\tdiffOptions: visOptions.diffOptions,\n\t\tfailureThreshold: visOptions.failureThreshold,\n\t\tfailureThresholdType: visOptions.failureThresholdType,\n\t\tsnapshotKey: visOptions.snapshotKey,\n\t\tsubject: visOptions.subject,\n\t\ttimeout: visOptions.timeout,\n\t}\n}\n\n/**\n * Suite ID also contains the project name to make it unique\n * across different projects.\n */\nexport function getSuiteId(context: ExtendedBrowserCommandContext) {\n\treturn `${getProjectName(context)}/${context.project.config.name}`\n}\n\nasync function createSuite(\n\tbrowserContext: ExtendedBrowserCommandContext,\n\tvisOptions: Pick<VisOptions, 'snapshotRootDir' | 'subject'>,\n) {\n\t// console.log('visOptions', visOptions)\n\tconst snapshotRootDir = resolveSnapshotRootDir(browserContext, visOptions)\n\tconst projectRoot = getProjectRoot(browserContext)\n\n\tconst state = {\n\t\tprojectRoot,\n\t\ttestTimeout: browserContext.project.config.testTimeout,\n\t\thookTimeout: browserContext.project.config.hookTimeout,\n\t\tsnapshotRootDir,\n\t\tsnapshotBaselineDir: join(snapshotRootDir, BASELINE_DIR),\n\t\tsnapshotResultDir: join(snapshotRootDir, RESULT_DIR),\n\t\tsnapshotDiffDir: join(snapshotRootDir, DIFF_DIR),\n\t\tsnapshotRootPath: join(projectRoot, snapshotRootDir),\n\t\tsubject: visOptions.subject,\n\t\tmodules: {},\n\t}\n\tawait Promise.allSettled([deps.rimraf(join(state.snapshotDiffDir)), deps.rimraf(join(state.snapshotResultDir))])\n\treturn state\n}\n\nexport function createModule(state: VisSuite, testPath: string, options: Pick<VisOptions, 'snapshotSubpath'>) {\n\tconst taskSubpath = getTaskSubpath(state, testPath, options)\n\treturn {\n\t\ttaskSubpath,\n\t\tbaselineDir: join(state.snapshotBaselineDir, taskSubpath),\n\t\tresultDir: join(state.snapshotResultDir, taskSubpath),\n\t\tdiffDir: join(state.snapshotDiffDir, taskSubpath),\n\t\ttasks: {},\n\t}\n}\n\nexport function getTaskSubpath(state: VisSuite, testPath: string, options: Pick<VisOptions, 'snapshotSubpath'>) {\n\treturn getSnapshotSubpath(relative(state.projectRoot, testPath), options)\n}\n\nexport function getSuite(context: ExtendedBrowserCommandContext) {\n\treturn suites[getSuiteId(context)]!\n}\n"],"mappings":";;;;;;;;AASA,MAAM,SAAoB,EAAE;;;;;;AAO5B,eAAsB,WAAW,gBAA+C;CAC/E,MAAM,UAAU,WAAW,eAAe;CAC1C,MAAM,aAAa,aAAa,eAAe;AAE/C,KAAI,CAAC,OAAO,SACX,QAAO,WAAW,YAAY,gBAAgB,WAAW;CAG1D,MAAM,QAAQ,MAAM,OAAO;CAE3B,MAAM,EAAE,aAAa,aAAa,SAAS,WAAW,UAAU,aAC/D,OACA,eAAe,UACf,WACA;AACD,OAAM,QAAQ,eAAe;EAAE;EAAa;EAAS;EAAW;EAAO;AAEvE,OAAM,QAAQ,WAAW,CAAC,KAAK,OAAO,QAAQ,EAAE,KAAK,OAAO,UAAU,CAAC,CAAC;AACxE,QAAO;EACN,kBAAkB,WAAW;EAC7B,aAAa,WAAW;EACxB,kBAAkB,WAAW;EAC7B,sBAAsB,WAAW;EACjC,aAAa,WAAW;EACxB,SAAS,WAAW;EACpB,SAAS,WAAW;EACpB;;;;;;AAOF,SAAgB,WAAW,SAAwC;AAClE,QAAO,GAAG,eAAe,QAAQ,CAAC,GAAG,QAAQ,QAAQ,OAAO;;AAG7D,eAAe,YACd,gBACA,YACC;CAED,MAAM,kBAAkB,uBAAuB,gBAAgB,WAAW;CAC1E,MAAM,cAAc,eAAe,eAAe;CAElD,MAAM,QAAQ;EACb;EACA,aAAa,eAAe,QAAQ,OAAO;EAC3C,aAAa,eAAe,QAAQ,OAAO;EAC3C;EACA,qBAAqB,KAAK,iBAAiB,aAAa;EACxD,mBAAmB,KAAK,iBAAiB,WAAW;EACpD,iBAAiB,KAAK,iBAAiB,SAAS;EAChD,kBAAkB,KAAK,aAAa,gBAAgB;EACpD,SAAS,WAAW;EACpB,SAAS,EAAE;EACX;AACD,OAAM,QAAQ,WAAW,CAAC,KAAK,OAAO,KAAK,MAAM,gBAAgB,CAAC,EAAE,KAAK,OAAO,KAAK,MAAM,kBAAkB,CAAC,CAAC,CAAC;AAChH,QAAO;;AAGR,SAAgB,aAAa,OAAiB,UAAkB,SAA8C;CAC7G,MAAM,cAAc,eAAe,OAAO,UAAU,QAAQ;AAC5D,QAAO;EACN;EACA,aAAa,KAAK,MAAM,qBAAqB,YAAY;EACzD,WAAW,KAAK,MAAM,mBAAmB,YAAY;EACrD,SAAS,KAAK,MAAM,iBAAiB,YAAY;EACjD,OAAO,EAAE;EACT;;AAGF,SAAgB,eAAe,OAAiB,UAAkB,SAA8C;AAC/G,QAAO,mBAAmB,SAAS,MAAM,aAAa,SAAS,EAAE,QAAQ;;AAG1E,SAAgB,SAAS,SAAwC;AAChE,QAAO,OAAO,WAAW,QAAQ"}
1
+ {"version":3,"file":"suite.mjs","names":[],"sources":["../../src/server/suite.ts"],"sourcesContent":["import { join, relative, resolve } from 'pathe'\nimport type { VisOptions } from '../config/types.ts'\nimport { BASELINE_DIR, DIFF_DIR, RESULT_DIR } from '../shared/constants.ts'\nimport { getProjectName, getProjectRoot } from './project.ts'\nimport { getSnapshotSubpath, resolveSnapshotRootDir } from './snapshot_path.ts'\nimport { resolveSnapshotSubpathWithinLimits } from './snapshot_path_limits.ts'\nimport { getVisOption } from './vis_options.ts'\nimport { deps } from './vis_server_context.deps.ts'\nimport type { ExtendedBrowserCommandContext, VisSuite, VisSuites } from './vis_server_context.types.ts'\n\nconst suites: VisSuites = {}\n\n/**\n * Setup suite is called on each test file's beforeAll hook.\n * Test files include vitest test files and storybook story files.\n * It needs to make sure there is no race condition between the test files.\n */\nexport async function setupSuite(browserContext: ExtendedBrowserCommandContext) {\n\tconst suiteId = getSuiteId(browserContext)\n\tconst visOptions = getVisOption(browserContext)\n\n\tif (!suites[suiteId]) {\n\t\tsuites[suiteId] = createSuite(browserContext, visOptions)\n\t}\n\n\tconst suite = await suites[suiteId]\n\n\tconst { taskSubpath, baselineDir, diffDir, resultDir, tasks } = createModule(\n\t\tsuite,\n\t\tbrowserContext.testPath!,\n\t\tvisOptions,\n\t)\n\tsuite.modules[taskSubpath] = { baselineDir, diffDir, resultDir, tasks }\n\n\tawait Promise.allSettled([deps.rimraf(diffDir), deps.rimraf(resultDir)])\n\treturn {\n\t\tcomparisonMethod: visOptions.comparisonMethod,\n\t\tdiffOptions: visOptions.diffOptions,\n\t\tfailureThreshold: visOptions.failureThreshold,\n\t\tfailureThresholdType: visOptions.failureThresholdType,\n\t\tsnapshotKey: visOptions.snapshotKey,\n\t\tsubject: visOptions.subject,\n\t\ttimeout: visOptions.timeout,\n\t}\n}\n\n/**\n * Suite ID also contains the project name to make it unique\n * across different projects.\n */\nexport function getSuiteId(context: ExtendedBrowserCommandContext) {\n\treturn `${getProjectName(context)}/${context.project.config.name}`\n}\n\nasync function createSuite(\n\tbrowserContext: ExtendedBrowserCommandContext,\n\tvisOptions: Pick<VisOptions, 'snapshotRootDir' | 'subject'>,\n) {\n\t// console.log('visOptions', visOptions)\n\tconst snapshotRootDir = resolveSnapshotRootDir(browserContext, visOptions)\n\tconst projectRoot = getProjectRoot(browserContext)\n\n\tconst state = {\n\t\tprojectRoot,\n\t\ttestTimeout: browserContext.project.config.testTimeout,\n\t\thookTimeout: browserContext.project.config.hookTimeout,\n\t\tsnapshotRootDir,\n\t\tsnapshotBaselineDir: join(snapshotRootDir, BASELINE_DIR),\n\t\tsnapshotResultDir: join(snapshotRootDir, RESULT_DIR),\n\t\tsnapshotDiffDir: join(snapshotRootDir, DIFF_DIR),\n\t\tsnapshotRootPath: join(projectRoot, snapshotRootDir),\n\t\tsubject: visOptions.subject,\n\t\tmodules: {},\n\t}\n\tawait Promise.allSettled([deps.rimraf(join(state.snapshotDiffDir)), deps.rimraf(join(state.snapshotResultDir))])\n\treturn state\n}\n\nexport function createModule(\n\tstate: VisSuite,\n\ttestPath: string,\n\toptions: Pick<VisOptions, 'snapshotSubpath' | 'shortenLongSnapshotPaths'>,\n) {\n\tconst taskSubpath = getTaskSubpath(state, testPath, options)\n\treturn {\n\t\ttaskSubpath,\n\t\tbaselineDir: join(state.snapshotBaselineDir, taskSubpath),\n\t\tresultDir: join(state.snapshotResultDir, taskSubpath),\n\t\tdiffDir: join(state.snapshotDiffDir, taskSubpath),\n\t\ttasks: {},\n\t}\n}\n\nexport function getTaskSubpath(\n\tstate: VisSuite,\n\ttestPath: string,\n\toptions: Pick<VisOptions, 'snapshotSubpath' | 'shortenLongSnapshotPaths'>,\n) {\n\tconst raw = getSnapshotSubpath(relative(state.projectRoot, testPath), options)\n\tconst snapshotBaselineRootAbs = resolve(state.projectRoot, state.snapshotBaselineDir)\n\treturn resolveSnapshotSubpathWithinLimits({\n\t\tsnapshotBaselineRootAbs,\n\t\trawSnapshotSubpath: raw,\n\t\tshortenLongSnapshotPaths: options.shortenLongSnapshotPaths === true,\n\t})\n}\n\nexport function getSuite(context: ExtendedBrowserCommandContext) {\n\treturn suites[getSuiteId(context)]!\n}\n"],"mappings":";;;;;;;;;AAUA,MAAM,SAAoB,EAAE;;;;;;AAO5B,eAAsB,WAAW,gBAA+C;CAC/E,MAAM,UAAU,WAAW,eAAe;CAC1C,MAAM,aAAa,aAAa,eAAe;AAE/C,KAAI,CAAC,OAAO,SACX,QAAO,WAAW,YAAY,gBAAgB,WAAW;CAG1D,MAAM,QAAQ,MAAM,OAAO;CAE3B,MAAM,EAAE,aAAa,aAAa,SAAS,WAAW,UAAU,aAC/D,OACA,eAAe,UACf,WACA;AACD,OAAM,QAAQ,eAAe;EAAE;EAAa;EAAS;EAAW;EAAO;AAEvE,OAAM,QAAQ,WAAW,CAAC,KAAK,OAAO,QAAQ,EAAE,KAAK,OAAO,UAAU,CAAC,CAAC;AACxE,QAAO;EACN,kBAAkB,WAAW;EAC7B,aAAa,WAAW;EACxB,kBAAkB,WAAW;EAC7B,sBAAsB,WAAW;EACjC,aAAa,WAAW;EACxB,SAAS,WAAW;EACpB,SAAS,WAAW;EACpB;;;;;;AAOF,SAAgB,WAAW,SAAwC;AAClE,QAAO,GAAG,eAAe,QAAQ,CAAC,GAAG,QAAQ,QAAQ,OAAO;;AAG7D,eAAe,YACd,gBACA,YACC;CAED,MAAM,kBAAkB,uBAAuB,gBAAgB,WAAW;CAC1E,MAAM,cAAc,eAAe,eAAe;CAElD,MAAM,QAAQ;EACb;EACA,aAAa,eAAe,QAAQ,OAAO;EAC3C,aAAa,eAAe,QAAQ,OAAO;EAC3C;EACA,qBAAqB,KAAK,iBAAiB,aAAa;EACxD,mBAAmB,KAAK,iBAAiB,WAAW;EACpD,iBAAiB,KAAK,iBAAiB,SAAS;EAChD,kBAAkB,KAAK,aAAa,gBAAgB;EACpD,SAAS,WAAW;EACpB,SAAS,EAAE;EACX;AACD,OAAM,QAAQ,WAAW,CAAC,KAAK,OAAO,KAAK,MAAM,gBAAgB,CAAC,EAAE,KAAK,OAAO,KAAK,MAAM,kBAAkB,CAAC,CAAC,CAAC;AAChH,QAAO;;AAGR,SAAgB,aACf,OACA,UACA,SACC;CACD,MAAM,cAAc,eAAe,OAAO,UAAU,QAAQ;AAC5D,QAAO;EACN;EACA,aAAa,KAAK,MAAM,qBAAqB,YAAY;EACzD,WAAW,KAAK,MAAM,mBAAmB,YAAY;EACrD,SAAS,KAAK,MAAM,iBAAiB,YAAY;EACjD,OAAO,EAAE;EACT;;AAGF,SAAgB,eACf,OACA,UACA,SACC;CACD,MAAM,MAAM,mBAAmB,SAAS,MAAM,aAAa,SAAS,EAAE,QAAQ;AAE9E,QAAO,mCAAmC;EACzC,yBAF+B,QAAQ,MAAM,aAAa,MAAM,oBAAoB;EAGpF,oBAAoB;EACpB,0BAA0B,QAAQ,6BAA6B;EAC/D,CAAC;;AAGH,SAAgB,SAAS,SAAwC;AAChE,QAAO,OAAO,WAAW,QAAQ"}
@@ -0,0 +1,9 @@
1
+ import { stub } from "type-plus";
2
+ import "@vitest/browser-playwright";
3
+
4
+ //#region src/server/testing/stubBrowserCommandContext.ts
5
+ const stubBrowserCommandContext = stub.build({});
6
+
7
+ //#endregion
8
+ export { stubBrowserCommandContext };
9
+ //# sourceMappingURL=stubBrowserCommandContext.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stubBrowserCommandContext.mjs","names":[],"sources":["../../../src/server/testing/stubBrowserCommandContext.ts"],"sourcesContent":["import '@vitest/browser-playwright'\nimport { stub } from 'type-plus'\nimport type { ExtendedBrowserCommandContext } from '../vis_server_context.types.ts'\n\nexport const stubBrowserCommandContext = stub.build<ExtendedBrowserCommandContext>({})\n\nexport function createStubPartialBrowserCommandContext({ root, testPath }: { root: string; testPath: string }) {\n\treturn stub.build<ExtendedBrowserCommandContext>({\n\t\tproject: {\n\t\t\tconfig: { root },\n\t\t\trunner: { config: { name: 'subject' } },\n\t\t} as any,\n\t\tprovider: {},\n\t\ttestPath,\n\t})\n}\n"],"mappings":";;;;AAIA,MAAa,4BAA4B,KAAK,MAAqC,EAAE,CAAC"}