@crvy/rprtr 0.1.2 → 0.1.4

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/CHANGELOG.md CHANGED
@@ -5,6 +5,34 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.4] - 2026-06-19
9
+
10
+ ### Added
11
+
12
+ - **path-utils:** Add cross-platform absolute path helpers
13
+ - **server:** Log diagnostic when /file URL cannot resolve on host OS
14
+
15
+ ### Documentation
16
+
17
+ - **plans:** Cross-OS path handling and stale actual URL on passing rerun
18
+ - Document cross-OS artifact loading limitations
19
+
20
+ ### Fixed
21
+
22
+ - **report-utils:** Route Windows-style absolute paths to /file on any host
23
+ - **server:** Check pre-resolve path for foreign-OS diagnostic
24
+ - **report-state:** Drop stale comparison actual URL on passed reruns
25
+ - **report-state:** Strip stale actual/diff when preserving baseline-only images
26
+
27
+ ### Testing
28
+
29
+ - **path-utils:** Cover forward-slash UNC form
30
+ - **server-routes:** Cover baseline enrichment after failed→passed progression
31
+ ## [0.1.3] - 2026-06-18
32
+
33
+ ### Fixed
34
+
35
+ - **reporter:** Resolve browser label for Playwright's default project
8
36
  ## [0.1.2] - 2026-06-08
9
37
 
10
38
  ### Added
package/README.md CHANGED
@@ -113,6 +113,14 @@ Approval routing uses the same resolver, but it reads its resolver settings from
113
113
 
114
114
  When the server is running, Crvy Rprtr also refreshes the UI after report JSON or screenshot artifacts change on disk.
115
115
 
116
+ ## Cross-OS Artifact Loading
117
+
118
+ Crvy Rprtr stores image URLs exactly as the reporter that produced them saw them. In live mode (server running during the test run), failure artifacts are referenced by absolute path under `/file/<encoded>`; in CI/offline mode, attachments are copied into `screenshots/` and referenced by relative path under `/screenshots/`.
119
+
120
+ If you generate a report on one operating system and then open it on another (for example, downloading a Windows CI runner's `report.json` onto a macOS laptop), absolute-path `/file/...` URLs cannot resolve: the file is not on your filesystem. Crvy Rprtr logs a single diagnostic line per such request and returns 404. The `/screenshots/...` and `/baseline/...` URLs remain portable because they resolve through the server's `screenshotDir` or snapshot resolver.
121
+
122
+ For fully portable artifact loading across operating systems, run the reporter in CI mode (`ci: true`) and ship the `screenshots/` directory alongside the report JSON.
123
+
116
124
  ## Programmatic API
117
125
 
118
126
  ```ts
@@ -11,9 +11,10 @@ import {
11
11
  applyTestEndEvent,
12
12
  createMutableReportState,
13
13
  finalizeRunEvent,
14
+ isForeignAbsolutePath,
14
15
  resolveBaselineTargets,
15
16
  safeParse
16
- } from "./chunk-KFXC7QDV.js";
17
+ } from "./chunk-WRMHUY5A.js";
17
18
 
18
19
  // src/server/app.ts
19
20
  import { dirname as dirname3, join as join3 } from "path";
@@ -210,14 +211,21 @@ async function realpathOrNull(path) {
210
211
  }
211
212
  async function handleFile(ctx, req) {
212
213
  const notFound = () => new Response("Not Found", { status: 404 });
214
+ let rawDecoded;
213
215
  let decodedPath;
214
216
  try {
215
- decodedPath = resolve2(decodeURIComponent(new URL(req.url).pathname.slice("/file/".length)));
217
+ rawDecoded = decodeURIComponent(new URL(req.url).pathname.slice("/file/".length));
218
+ decodedPath = resolve2(rawDecoded);
216
219
  } catch {
217
220
  return notFound();
218
221
  }
219
222
  const realTarget = await realpathOrNull(decodedPath);
220
223
  if (realTarget === null) {
224
+ if (isForeignAbsolutePath(rawDecoded, process.platform)) {
225
+ console.warn(
226
+ `[Crvy Rprtr] /file request resolved to a foreign-OS absolute path that cannot be served on ${process.platform}: ${rawDecoded}`
227
+ );
228
+ }
221
229
  return notFound();
222
230
  }
223
231
  const realRoots = (await Promise.all((ctx.artifactRoots ?? []).map((root) => realpathOrNull(resolve2(root))))).filter(
@@ -235,7 +243,8 @@ async function handleFile(ctx, req) {
235
243
  }
236
244
  function reporterTitlePath(test) {
237
245
  const testFile = test.location?.file;
238
- return ["", test.browser, testFile ?? "", ...test.titlePath, test.title];
246
+ const projectName = test.projectName ?? test.browser;
247
+ return ["", projectName, testFile ?? "", ...test.titlePath, test.title];
239
248
  }
240
249
  function resolveBaselineSnapshotPath(routing, test, retry, imageName) {
241
250
  const testFile = test.location?.file;
@@ -251,7 +260,7 @@ function resolveBaselineSnapshotPath(routing, test, retry, imageName) {
251
260
  configDir: routing.configDir,
252
261
  testDir: routing.playwrightTestDir ?? dirname2(testFile),
253
262
  snapshotDir: routing.playwrightSnapshotDir ?? dirname2(testFile),
254
- projectName: test.browser,
263
+ projectName: test.projectName ?? test.browser,
255
264
  snapshotSuffix: process.platform,
256
265
  snapshotPathTemplate: routing.playwrightSnapshotPathTemplate,
257
266
  toHaveScreenshotPathTemplate: routing.playwrightToHaveScreenshotPathTemplate
@@ -216,6 +216,7 @@ var TestDataSchema = z.object({
216
216
  id: z.string(),
217
217
  titlePath: z.array(z.string()),
218
218
  browser: z.string(),
219
+ projectName: z.string().optional(),
219
220
  title: z.string(),
220
221
  skip: z.union([z.boolean(), z.string()]).optional(),
221
222
  retries: z.number().optional(),
@@ -267,6 +268,7 @@ var TestBeginDataSchema = z.object({
267
268
  title: z.string(),
268
269
  titlePath: z.array(z.string()),
269
270
  browser: z.string(),
271
+ projectName: z.string().optional(),
270
272
  location: LocationSchema
271
273
  });
272
274
  var TestEndDataSchema = z.object({
@@ -339,8 +341,26 @@ function safeParse(schema, data) {
339
341
  return null;
340
342
  }
341
343
 
344
+ // src/path-utils.ts
345
+ var posixAbsolutePattern = /^\//u;
346
+ var windowsDrivePattern = /^[A-Za-z]:[\\/]/u;
347
+ var windowsUncPattern = /^[\\/][\\/]/u;
348
+ function isPosixAbsolutePath(p) {
349
+ return posixAbsolutePattern.test(p);
350
+ }
351
+ function isWindowsAbsolutePath(p) {
352
+ return windowsDrivePattern.test(p) || windowsUncPattern.test(p);
353
+ }
354
+ function isAnyAbsolutePath(p) {
355
+ return isPosixAbsolutePath(p) || isWindowsAbsolutePath(p);
356
+ }
357
+ function isForeignAbsolutePath(p, hostPlatform) {
358
+ if (!isAnyAbsolutePath(p)) return false;
359
+ if (hostPlatform === "win32") return !isWindowsAbsolutePath(p);
360
+ return !isPosixAbsolutePath(p);
361
+ }
362
+
342
363
  // src/report-utils.ts
343
- import { isAbsolute } from "path";
344
364
  function normalizeScreenshotsBaseUrl(baseUrl) {
345
365
  return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
346
366
  }
@@ -385,7 +405,7 @@ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/")
385
405
  const role = match[2];
386
406
  if (baseName === null || baseName === void 0 || role === null || role === void 0) continue;
387
407
  images[baseName] ??= {};
388
- const url = isAbsolute(attachment.path) ? `/file/${encodeURIComponent(attachment.path)}` : `${baseUrl}${attachment.path}`;
408
+ const url = isAnyAbsolutePath(attachment.path) ? `/file/${encodeURIComponent(attachment.path)}` : `${baseUrl}${attachment.path}`;
389
409
  const img = images[baseName];
390
410
  if (img !== null && img !== void 0) {
391
411
  if (role === "actual") img.actual = url;
@@ -420,9 +440,6 @@ function isCurrentArtifact(image) {
420
440
  }
421
441
  function isReusablePassingImage(image) {
422
442
  const source = image.source ?? classifyImage(image);
423
- if (source === "comparison") {
424
- return image.actual !== void 0 && image.diff === void 0;
425
- }
426
443
  return source === "baseline-only";
427
444
  }
428
445
  function hasReusablePassingImages(images) {
@@ -443,8 +460,8 @@ function preservePreviousPassingImages(test, status, images) {
443
460
  return {
444
461
  ...currentImages,
445
462
  [name]: {
446
- ...previousImage,
447
- source: previousImage.source ?? classifyImage(previousImage)
463
+ expect: previousImage.expect,
464
+ source: "baseline-only"
448
465
  }
449
466
  };
450
467
  }, images);
@@ -465,12 +482,16 @@ function createMutableReportState(screenshotDir = "./screenshots") {
465
482
  };
466
483
  }
467
484
  function applyTestBeginEvent(state, data) {
468
- const { id, title, titlePath, browser, location } = data;
485
+ const { id, title, titlePath, browser, projectName, location } = data;
469
486
  state.currentRunIds.add(id);
470
487
  state.reportData.tests[id] ??= {
471
488
  id,
472
489
  titlePath: titlePath ?? [],
473
490
  browser: browser ?? "",
491
+ // Older reporters sent the raw project name in `browser` and had no
492
+ // `projectName` field. Preserve that value for snapshot path resolution
493
+ // when loading data produced by such reporters.
494
+ projectName: projectName ?? browser ?? "",
474
495
  title: title ?? "",
475
496
  location,
476
497
  status: "running"
@@ -528,6 +549,7 @@ function finalizeRunEvent(state) {
528
549
  }
529
550
 
530
551
  export {
552
+ isForeignAbsolutePath,
531
553
  createMutableReportState,
532
554
  applyTestBeginEvent,
533
555
  applyTestEndEvent,
package/dist/cli.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  startServer
4
- } from "./chunk-RJEAJDMQ.js";
5
- import "./chunk-KFXC7QDV.js";
4
+ } from "./chunk-KLGJSEKV.js";
5
+ import "./chunk-WRMHUY5A.js";
6
6
 
7
7
  // src/cli.ts
8
8
  import { join } from "path";
package/dist/index.css CHANGED
@@ -221,6 +221,9 @@
221
221
  .absolute {
222
222
  position: absolute;
223
223
  }
224
+ .fixed {
225
+ position: fixed;
226
+ }
224
227
  .relative {
225
228
  position: relative;
226
229
  }
package/dist/index.js CHANGED
@@ -19368,6 +19368,7 @@ var TestDataSchema = external_exports.object({
19368
19368
  id: external_exports.string(),
19369
19369
  titlePath: external_exports.array(external_exports.string()),
19370
19370
  browser: external_exports.string(),
19371
+ projectName: external_exports.string().optional(),
19371
19372
  title: external_exports.string(),
19372
19373
  skip: external_exports.union([external_exports.boolean(), external_exports.string()]).optional(),
19373
19374
  retries: external_exports.number().optional(),
@@ -19419,6 +19420,7 @@ var TestBeginDataSchema = external_exports.object({
19419
19420
  title: external_exports.string(),
19420
19421
  titlePath: external_exports.array(external_exports.string()),
19421
19422
  browser: external_exports.string(),
19423
+ projectName: external_exports.string().optional(),
19422
19424
  location: LocationSchema
19423
19425
  });
19424
19426
  var TestEndDataSchema = external_exports.object({
@@ -19618,6 +19620,10 @@ function hasScreenshots(item) {
19618
19620
  }
19619
19621
 
19620
19622
  // src/client/helpers/path.ts
19623
+ var DEFAULT_BROWSER_KEY = "default";
19624
+ function browserKeyFor(browser) {
19625
+ return browser === "" ? DEFAULT_BROWSER_KEY : browser;
19626
+ }
19621
19627
  function getTestPath(test) {
19622
19628
  return [...test.titlePath, test.title, test.browser].filter(isDefined);
19623
19629
  }
@@ -19676,7 +19682,7 @@ function treeifyTests(testsById) {
19676
19682
  Object.values(testsById).forEach((test) => {
19677
19683
  if (test === void 0) return;
19678
19684
  const titlePath = test.titlePath ?? [];
19679
- const browser = test.browser ?? "";
19685
+ const browser = browserKeyFor(test.browser ?? "");
19680
19686
  const title = test.title;
19681
19687
  const pathParts = [...titlePath, title, browser].filter((p) => p !== void 0 && p !== "");
19682
19688
  const [browserName, ...testPathParts] = pathParts.reverse();
@@ -19863,8 +19869,8 @@ function collectTestsById(suite) {
19863
19869
  function pathTokensFor(test) {
19864
19870
  const titlePath = test.titlePath ?? [];
19865
19871
  const title = test.title;
19866
- const browser = test.browser ?? "";
19867
- if (title === void 0 || title === "" || browser === "") return null;
19872
+ if (title === void 0 || title === "") return null;
19873
+ const browser = browserKeyFor(test.browser ?? "");
19868
19874
  const pathParts = [...titlePath, title, browser].filter((p) => p !== void 0 && p !== "");
19869
19875
  const reversed = pathParts.reverse();
19870
19876
  const browserKey = reversed[0];
@@ -0,0 +1,3 @@
1
+ export declare function isAnyAbsolutePath(p: string): boolean;
2
+ export declare function isForeignAbsolutePath(p: string, hostPlatform: NodeJS.Platform): boolean;
3
+ //# sourceMappingURL=path-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-utils.d.ts","sourceRoot":"","sources":["../src/path-utils.ts"],"names":[],"mappings":"AAYA,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEpD;AAED,wBAAgB,qBAAqB,CAAC,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,CAAC,QAAQ,GAAG,OAAO,CAIvF"}
@@ -1 +1 @@
1
- {"version":3,"file":"report-state.d.ts","sourceRoot":"","sources":["../src/report-state.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC9D,OAAO,KAAK,EAAU,QAAQ,EAAc,MAAM,YAAY,CAAA;AAE9D,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,iBAAiB,CAAA;IAC7B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,KAAK,sBAAsB,GAAG,WAAW,GAAG;IAC1C,kBAAkB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAA;CACtD,CAAA;AA0DD,wBAAgB,wBAAwB,CAAC,aAAa,SAAkB,GAAG,kBAAkB,CAW5F;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,aAAa,GAAG,QAAQ,CAa5F;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,sBAAsB,EAC5B,OAAO,GAAE;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5C,kBAAkB,GAAG,IAAI,CAyC3B;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAa/G"}
1
+ {"version":3,"file":"report-state.d.ts","sourceRoot":"","sources":["../src/report-state.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC9D,OAAO,KAAK,EAAU,QAAQ,EAAc,MAAM,YAAY,CAAA;AAE9D,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC/B,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;IACrB,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,iBAAiB,CAAA;IAC7B,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,KAAK,sBAAsB,GAAG,WAAW,GAAG;IAC1C,kBAAkB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAA;CACtD,CAAA;AAqDD,wBAAgB,wBAAwB,CAAC,aAAa,SAAkB,GAAG,kBAAkB,CAW5F;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,aAAa,GAAG,QAAQ,CAiB5F;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,kBAAkB,EACzB,IAAI,EAAE,sBAAsB,EAC5B,OAAO,GAAE;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAO,GAC5C,kBAAkB,GAAG,IAAI,CAyC3B;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,kBAAkB,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAa/G"}
@@ -1 +1 @@
1
- {"version":3,"file":"report-utils.d.ts","sourceRoot":"","sources":["../src/report-utils.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAMhE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAUzD;AASD,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,kBAAkB,EAAE,SAAS,qBAAqB,EAAE,GAAG,SAAS,GAC/D,SAAS,MAAM,EAAE,CAEnB;AAED,wBAAgB,sBAAsB,CACpC,kBAAkB,EAAE,SAAS,qBAAqB,EAAE,GAAG,SAAS,GAC/D,SAAS,qBAAqB,EAAE,GAAG,SAAS,CAE9C;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACvC,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CASjC;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,UAAU,EAAE,EACzB,kBAAkB,SAAkB,GACnC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkCjC;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAWrF"}
1
+ {"version":3,"file":"report-utils.d.ts","sourceRoot":"","sources":["../src/report-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAMhE,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAUzD;AASD,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,SAAS,MAAM,EAAE,EAC9B,kBAAkB,EAAE,SAAS,qBAAqB,EAAE,GAAG,SAAS,GAC/D,SAAS,MAAM,EAAE,CAEnB;AAED,wBAAgB,sBAAsB,CACpC,kBAAkB,EAAE,SAAS,qBAAqB,EAAE,GAAG,SAAS,GAC/D,SAAS,qBAAqB,EAAE,GAAG,SAAS,CAE9C;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EACvC,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CASjC;AAED,wBAAgB,mBAAmB,CACjC,WAAW,EAAE,UAAU,EAAE,EACzB,kBAAkB,SAAkB,GACnC,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkCjC;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAWrF"}
package/dist/reporter.cjs CHANGED
@@ -36,7 +36,7 @@ __export(reporter_exports, {
36
36
  module.exports = __toCommonJS(reporter_exports);
37
37
  var import_fs = require("fs");
38
38
  var import_promises3 = require("fs/promises");
39
- var import_path5 = require("path");
39
+ var import_path4 = require("path");
40
40
  var import_p_limit2 = __toESM(require("p-limit"), 1);
41
41
 
42
42
  // src/ci.ts
@@ -56,16 +56,29 @@ var logError = (...args) => {
56
56
 
57
57
  // src/reporter-artifact-ops.ts
58
58
  var import_promises2 = require("fs/promises");
59
- var import_path3 = require("path");
59
+ var import_path2 = require("path");
60
60
  var import_p_limit = __toESM(require("p-limit"), 1);
61
61
 
62
62
  // src/report-artifact.ts
63
63
  var import_promises = require("fs/promises");
64
- var import_path2 = require("path");
64
+ var import_path = require("path");
65
65
  var import_url = require("url");
66
66
 
67
+ // src/path-utils.ts
68
+ var posixAbsolutePattern = /^\//u;
69
+ var windowsDrivePattern = /^[A-Za-z]:[\\/]/u;
70
+ var windowsUncPattern = /^[\\/][\\/]/u;
71
+ function isPosixAbsolutePath(p) {
72
+ return posixAbsolutePattern.test(p);
73
+ }
74
+ function isWindowsAbsolutePath(p) {
75
+ return windowsDrivePattern.test(p) || windowsUncPattern.test(p);
76
+ }
77
+ function isAnyAbsolutePath(p) {
78
+ return isPosixAbsolutePath(p) || isWindowsAbsolutePath(p);
79
+ }
80
+
67
81
  // src/report-utils.ts
68
- var import_path = require("path");
69
82
  function normalizeScreenshotsBaseUrl(baseUrl) {
70
83
  return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
71
84
  }
@@ -110,7 +123,7 @@ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/")
110
123
  const role = match[2];
111
124
  if (baseName === null || baseName === void 0 || role === null || role === void 0) continue;
112
125
  images[baseName] ??= {};
113
- const url = (0, import_path.isAbsolute)(attachment.path) ? `/file/${encodeURIComponent(attachment.path)}` : `${baseUrl}${attachment.path}`;
126
+ const url = isAnyAbsolutePath(attachment.path) ? `/file/${encodeURIComponent(attachment.path)}` : `${baseUrl}${attachment.path}`;
114
127
  const img = images[baseName];
115
128
  if (img !== null && img !== void 0) {
116
129
  if (role === "actual") img.actual = url;
@@ -145,9 +158,6 @@ function isCurrentArtifact(image) {
145
158
  }
146
159
  function isReusablePassingImage(image) {
147
160
  const source = image.source ?? classifyImage(image);
148
- if (source === "comparison") {
149
- return image.actual !== void 0 && image.diff === void 0;
150
- }
151
161
  return source === "baseline-only";
152
162
  }
153
163
  function hasReusablePassingImages(images) {
@@ -168,8 +178,8 @@ function preservePreviousPassingImages(test, status, images) {
168
178
  return {
169
179
  ...currentImages,
170
180
  [name]: {
171
- ...previousImage,
172
- source: previousImage.source ?? classifyImage(previousImage)
181
+ expect: previousImage.expect,
182
+ source: "baseline-only"
173
183
  }
174
184
  };
175
185
  }, images);
@@ -190,12 +200,16 @@ function createMutableReportState(screenshotDir = "./screenshots") {
190
200
  };
191
201
  }
192
202
  function applyTestBeginEvent(state, data) {
193
- const { id, title, titlePath, browser, location } = data;
203
+ const { id, title, titlePath, browser, projectName, location } = data;
194
204
  state.currentRunIds.add(id);
195
205
  state.reportData.tests[id] ??= {
196
206
  id,
197
207
  titlePath: titlePath ?? [],
198
208
  browser: browser ?? "",
209
+ // Older reporters sent the raw project name in `browser` and had no
210
+ // `projectName` field. Preserve that value for snapshot path resolution
211
+ // when loading data produced by such reporters.
212
+ projectName: projectName ?? browser ?? "",
199
213
  title: title ?? "",
200
214
  location,
201
215
  status: "running"
@@ -299,6 +313,7 @@ var TestDataSchema = import_zod.z.object({
299
313
  id: import_zod.z.string(),
300
314
  titlePath: import_zod.z.array(import_zod.z.string()),
301
315
  browser: import_zod.z.string(),
316
+ projectName: import_zod.z.string().optional(),
302
317
  title: import_zod.z.string(),
303
318
  skip: import_zod.z.union([import_zod.z.boolean(), import_zod.z.string()]).optional(),
304
319
  retries: import_zod.z.number().optional(),
@@ -350,6 +365,7 @@ var TestBeginDataSchema = import_zod.z.object({
350
365
  title: import_zod.z.string(),
351
366
  titlePath: import_zod.z.array(import_zod.z.string()),
352
367
  browser: import_zod.z.string(),
368
+ projectName: import_zod.z.string().optional(),
353
369
  location: LocationSchema
354
370
  });
355
371
  var TestEndDataSchema = import_zod.z.object({
@@ -427,7 +443,7 @@ var import_meta = { url: require("url").pathToFileURL(__filename).href };
427
443
  var DEFAULT_REPORT_HTML_PATH = "./crvy-rprtr.html";
428
444
  var STATIC_ARTIFACT_APPROVAL_MESSAGE = "This artifact is read-only. Open it with the Crvy Rprtr server to approve screenshots.";
429
445
  function toBrowserPath(pathValue) {
430
- const normalized = pathValue.split(import_path2.sep).join("/");
446
+ const normalized = pathValue.split(import_path.sep).join("/");
431
447
  if (normalized === "") return ".";
432
448
  if (normalized.startsWith(".")) return normalized;
433
449
  return `./${normalized}`;
@@ -463,11 +479,11 @@ function renderArtifactHtml(template, bootstrapData, stylesheet, script) {
463
479
  );
464
480
  }
465
481
  async function getPackagedAssetPath(fileName) {
466
- const currentDir = (0, import_path2.dirname)((0, import_url.fileURLToPath)(import_meta.url));
467
- const candidates = [currentDir, (0, import_path2.resolve)(currentDir, "../dist")];
482
+ const currentDir = (0, import_path.dirname)((0, import_url.fileURLToPath)(import_meta.url));
483
+ const candidates = [currentDir, (0, import_path.resolve)(currentDir, "../dist")];
468
484
  const resolvedCandidates = await Promise.all(
469
485
  candidates.map(async (candidate) => {
470
- const assetPath = (0, import_path2.resolve)(candidate, fileName);
486
+ const assetPath = (0, import_path.resolve)(candidate, fileName);
471
487
  try {
472
488
  await (0, import_promises.access)(assetPath);
473
489
  return assetPath;
@@ -485,7 +501,7 @@ async function getPackagedAssetPath(fileName) {
485
501
  );
486
502
  }
487
503
  function buildStaticBootstrapData(events, screenshotDir, htmlPath) {
488
- const screenshotBaseUrl = toBrowserDirPath((0, import_path2.relative)((0, import_path2.dirname)(htmlPath), (0, import_path2.resolve)(screenshotDir)));
504
+ const screenshotBaseUrl = toBrowserDirPath((0, import_path.relative)((0, import_path.dirname)(htmlPath), (0, import_path.resolve)(screenshotDir)));
489
505
  const state = createMutableReportState(screenshotDir);
490
506
  for (const event of events) {
491
507
  switch (event.type) {
@@ -524,13 +540,13 @@ function buildStaticBootstrapData(events, screenshotDir, htmlPath) {
524
540
  return parsedBootstrapData;
525
541
  }
526
542
  async function writeReportArtifact(options) {
527
- const reportHtmlPath = (0, import_path2.resolve)(options.reportHtmlPath ?? DEFAULT_REPORT_HTML_PATH);
543
+ const reportHtmlPath = (0, import_path.resolve)(options.reportHtmlPath ?? DEFAULT_REPORT_HTML_PATH);
528
544
  const [indexHtmlPath, indexJsPath, indexCssPath] = await Promise.all([
529
545
  getPackagedAssetPath("index.html"),
530
546
  getPackagedAssetPath("index.js"),
531
547
  getPackagedAssetPath("index.css")
532
548
  ]);
533
- await (0, import_promises.mkdir)((0, import_path2.dirname)(reportHtmlPath), { recursive: true });
549
+ await (0, import_promises.mkdir)((0, import_path.dirname)(reportHtmlPath), { recursive: true });
534
550
  const [template, script, stylesheet] = await Promise.all([
535
551
  (0, import_promises.readFile)(indexHtmlPath, "utf8"),
536
552
  (0, import_promises.readFile)(indexJsPath, "utf8"),
@@ -563,9 +579,9 @@ function sanitizeId(id) {
563
579
  async function copyResolvedBaseline(safeTestId, testScreenshotDir, target, savedAttachments) {
564
580
  const attachmentName = `${target.attachmentBaseName}-expected.png`;
565
581
  const artifactPath = safeArtifactPath(attachmentName);
566
- const destPath = (0, import_path3.join)(testScreenshotDir, artifactPath);
582
+ const destPath = (0, import_path2.join)(testScreenshotDir, artifactPath);
567
583
  try {
568
- await (0, import_promises2.mkdir)((0, import_path3.dirname)(destPath), { recursive: true });
584
+ await (0, import_promises2.mkdir)((0, import_path2.dirname)(destPath), { recursive: true });
569
585
  await (0, import_promises2.copyFile)(target.snapshotPath, destPath);
570
586
  savedAttachments.push({ name: attachmentName, path: `${safeTestId}/${artifactPath}`, contentType: "image/png" });
571
587
  log(`[CrvyRprtr] Attached baseline: ${target.snapshotPath}`);
@@ -575,7 +591,7 @@ async function copyResolvedBaseline(safeTestId, testScreenshotDir, target, saved
575
591
  async function saveAttachments(screenshotDir, testId, result) {
576
592
  const savedAttachments = [];
577
593
  const safeTestId = sanitizeId(testId);
578
- const testScreenshotDir = (0, import_path3.join)(screenshotDir, safeTestId);
594
+ const testScreenshotDir = (0, import_path2.join)(screenshotDir, safeTestId);
579
595
  const limit = (0, import_p_limit.default)(MAX_CONCURRENT_FILE_OPS);
580
596
  await Promise.all(
581
597
  result.attachments.filter(
@@ -584,8 +600,8 @@ async function saveAttachments(screenshotDir, testId, result) {
584
600
  (attachment) => limit(async () => {
585
601
  try {
586
602
  const artifactPath = safeArtifactPath(attachment.name);
587
- const destPath = (0, import_path3.join)(testScreenshotDir, artifactPath);
588
- await (0, import_promises2.mkdir)((0, import_path3.dirname)(destPath), { recursive: true });
603
+ const destPath = (0, import_path2.join)(testScreenshotDir, artifactPath);
604
+ await (0, import_promises2.mkdir)((0, import_path2.dirname)(destPath), { recursive: true });
589
605
  await (0, import_promises2.copyFile)(attachment.path, destPath);
590
606
  savedAttachments.push({
591
607
  name: attachment.name,
@@ -715,7 +731,7 @@ function extractScreenshotDeclarations(steps) {
715
731
 
716
732
  // src/snapshot-path-resolver.ts
717
733
  var import_crypto = require("crypto");
718
- var import_path4 = require("path");
734
+ var import_path3 = require("path");
719
735
  var DEFAULT_SCREENSHOT_TEMPLATE = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}";
720
736
  var WINDOWS_FILESYSTEM_FRIENDLY_LENGTH = 60;
721
737
  function isUnsafeFilePathCharacter(character) {
@@ -750,16 +766,16 @@ function trimLongString(value, length = WINDOWS_FILESYSTEM_FRIENDLY_LENGTH) {
750
766
  const end = length - middle.length - start;
751
767
  return value.slice(0, start) + middle + value.slice(-end);
752
768
  }
753
- function sanitizeFilePathBeforeExtension(filePath, extension = (0, import_path4.extname)(filePath)) {
769
+ function sanitizeFilePathBeforeExtension(filePath, extension = (0, import_path3.extname)(filePath)) {
754
770
  const base = filePath.slice(0, filePath.length - extension.length);
755
771
  return sanitizeForFilePath(base) + extension;
756
772
  }
757
773
  function addSuffixToFilePath(filePath, suffix) {
758
- const extension = (0, import_path4.extname)(filePath);
774
+ const extension = (0, import_path3.extname)(filePath);
759
775
  return filePath.slice(0, filePath.length - extension.length) + suffix + extension;
760
776
  }
761
777
  function normalizedSnapshotDir(config) {
762
- return (0, import_path4.resolve)(config.configDir, config.snapshotDir);
778
+ return (0, import_path3.resolve)(config.configDir, config.snapshotDir);
763
779
  }
764
780
  function templateValue(template, token, value) {
765
781
  return template.replace(
@@ -769,8 +785,8 @@ function templateValue(template, token, value) {
769
785
  }
770
786
  function applyTemplate(input, nameArgument, extension) {
771
787
  const template = input.config.toHaveScreenshotPathTemplate ?? input.config.snapshotPathTemplate ?? DEFAULT_SCREENSHOT_TEMPLATE;
772
- const relativeTestFilePath = (0, import_path4.relative)(input.config.testDir, input.testFile);
773
- const parsed = (0, import_path4.parse)(relativeTestFilePath);
788
+ const relativeTestFilePath = (0, import_path3.relative)(input.config.testDir, input.testFile);
789
+ const parsed = (0, import_path3.parse)(relativeTestFilePath);
774
790
  const tokens = [
775
791
  ["testDir", input.config.testDir],
776
792
  ["snapshotDir", normalizedSnapshotDir(input.config)],
@@ -788,16 +804,16 @@ function applyTemplate(input, nameArgument, extension) {
788
804
  (currentTemplate, [token, value]) => templateValue(currentTemplate, token, value),
789
805
  template
790
806
  );
791
- return (0, import_path4.resolve)(input.config.configDir, snapshotPath);
807
+ return (0, import_path3.resolve)(input.config.configDir, snapshotPath);
792
808
  }
793
- function removeExtension(filePath, extension = (0, import_path4.extname)(filePath)) {
809
+ function removeExtension(filePath, extension = (0, import_path3.extname)(filePath)) {
794
810
  return filePath.slice(0, filePath.length - extension.length);
795
811
  }
796
812
  function snapshotNameParts(declaredName) {
797
- const extension = (0, import_path4.extname)(declaredName) || ".png";
813
+ const extension = (0, import_path3.extname)(declaredName) || ".png";
798
814
  return {
799
815
  extension,
800
- filePath: (0, import_path4.extname)(declaredName) === "" ? `${declaredName}${extension}` : declaredName
816
+ filePath: (0, import_path3.extname)(declaredName) === "" ? `${declaredName}${extension}` : declaredName
801
817
  };
802
818
  }
803
819
  function filePathForOccurrence(filePath, occurrenceIndex) {
@@ -821,7 +837,7 @@ function resolveStringCallTarget(input, declaration) {
821
837
  function resolveArrayCallTarget(input, declaration) {
822
838
  const { extension, filePath } = snapshotNameParts(declaration.declaredName);
823
839
  const occurrenceFilePath = filePathForOccurrence(filePath, declaration.occurrenceIndex);
824
- const nameArgument = (0, import_path4.join)((0, import_path4.dirname)(occurrenceFilePath), (0, import_path4.basename)(occurrenceFilePath, extension));
840
+ const nameArgument = (0, import_path3.join)((0, import_path3.dirname)(occurrenceFilePath), (0, import_path3.basename)(occurrenceFilePath, extension));
825
841
  return createResolvedBaselineTarget(input, declaration, nameArgument, extension);
826
842
  }
827
843
  function resolveNamedTarget(input, declaration) {
@@ -863,7 +879,7 @@ function resolveTarget(input, declaration) {
863
879
  case "unnamed": {
864
880
  const anonymousFileName = anonymousName(input.reporterTitlePath, declaration.occurrenceIndex);
865
881
  const extension = ".png";
866
- const nameArgument = (0, import_path4.join)((0, import_path4.dirname)(anonymousFileName), (0, import_path4.basename)(anonymousFileName, extension));
882
+ const nameArgument = (0, import_path3.join)((0, import_path3.dirname)(anonymousFileName), (0, import_path3.basename)(anonymousFileName, extension));
867
883
  return createResolvedBaselineTarget(input, declaration, nameArgument, extension);
868
884
  }
869
885
  }
@@ -915,7 +931,7 @@ var CrvyRprtr = class {
915
931
  if (this.ci) this.isOfflineMode = true;
916
932
  }
917
933
  async onBegin(config, suite) {
918
- this.configDir = config.configFile === void 0 ? config.rootDir : (0, import_path5.dirname)(config.configFile);
934
+ this.configDir = config.configFile === void 0 ? config.rootDir : (0, import_path4.dirname)(config.configFile);
919
935
  log(`[CrvyRprtr] Starting run with ${suite.allTests().length} tests`);
920
936
  await (0, import_promises3.mkdir)(this.screenshotDir, { recursive: true });
921
937
  if (!this.ci) {
@@ -975,10 +991,23 @@ var CrvyRprtr = class {
975
991
  titlePath.unshift(suite.title);
976
992
  return titlePath;
977
993
  }
994
+ /** Resolves a non-empty browser label for UI display, falling back to the
995
+ * configured browser engine since the raw project `name` is `""` for the
996
+ * default project and devices spread `defaultBrowserType` into `use`. */
997
+ resolveBrowserLabel(project) {
998
+ const name = project?.name;
999
+ if (name !== void 0 && name !== "") return name;
1000
+ const browserName = project?.use?.browserName;
1001
+ if (browserName !== void 0) return browserName;
1002
+ const defaultBrowserType = project?.use?.defaultBrowserType;
1003
+ if (defaultBrowserType !== void 0) return defaultBrowserType;
1004
+ return "chromium";
1005
+ }
978
1006
  reporterTitlePath(test) {
979
- return typeof test.titlePath === "function" ? test.titlePath() : ["", test.parent.project()?.name ?? "chromium", test.location.file, ...this.describeTitlePath(test), test.title];
1007
+ return typeof test.titlePath === "function" ? test.titlePath() : ["", test.parent.project()?.name ?? "", test.location.file, ...this.describeTitlePath(test), test.title];
980
1008
  }
981
1009
  onTestBegin(test) {
1010
+ const project = test.parent.project();
982
1011
  this.testMetadata.set(test.id, {
983
1012
  reporterTitlePath: this.reporterTitlePath(test)
984
1013
  });
@@ -988,7 +1017,8 @@ var CrvyRprtr = class {
988
1017
  id: test.id,
989
1018
  title: test.title,
990
1019
  titlePath: this.describeTitlePath(test),
991
- browser: test.parent.project()?.name ?? "chromium",
1020
+ browser: this.resolveBrowserLabel(project),
1021
+ projectName: project?.name ?? "",
992
1022
  location: { file: test.location.file, line: test.location.line }
993
1023
  }
994
1024
  });
@@ -1050,7 +1080,7 @@ var CrvyRprtr = class {
1050
1080
  async copyBaselinesForTargets(testId, status, resolvedTargets, savedAttachments) {
1051
1081
  if (status !== "passed" || resolvedTargets.length === 0) return;
1052
1082
  const safeTestId = sanitizeId(testId);
1053
- const testScreenshotDir = (0, import_path5.join)(this.screenshotDir, safeTestId);
1083
+ const testScreenshotDir = (0, import_path4.join)(this.screenshotDir, safeTestId);
1054
1084
  const limit = (0, import_p_limit2.default)(5);
1055
1085
  await Promise.all(
1056
1086
  resolvedTargets.map(
@@ -24,6 +24,10 @@ export declare class CrvyRprtr implements Reporter {
24
24
  private enableOfflineMode;
25
25
  private sendRegister;
26
26
  private describeTitlePath;
27
+ /** Resolves a non-empty browser label for UI display, falling back to the
28
+ * configured browser engine since the raw project `name` is `""` for the
29
+ * default project and devices spread `defaultBrowserType` into `use`. */
30
+ private resolveBrowserLabel;
27
31
  private reporterTitlePath;
28
32
  onTestBegin(test: TestCase): void;
29
33
  onTestEnd(test: TestCase, result: TestResult): void;
@@ -1 +1 @@
1
- {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAa9G,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,uBAAuB,CAAA;AAS9B,YAAY,EAAE,gBAAgB,EAAE,CAAA;AAEhC,qBAAa,SAAU,YAAW,QAAQ;IACxC,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,YAAY,CAAqD;IACzE,OAAO,CAAC,qBAAqB,CAAC,CAAQ;IACtC,OAAO,CAAC,8BAA8B,CAAC,CAAQ;IAC/C,OAAO,CAAC,sCAAsC,CAAC,CAAQ;IACvD,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,gBAAgB,CAAgC;gBAE5C,OAAO,GAAE,gBAAqB;IAapC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9D,OAAO,CAAC,OAAO;IA6Bf,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,iBAAiB;IAMzB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAgBjC,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAmCnD,OAAO,CAAC,aAAa;YAqBP,uBAAuB;IAgB/B,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC9C,OAAO,CAAC,IAAI;CAUb;AAED,eAAe,SAAS,CAAA"}
1
+ {"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,UAAU,EAEV,UAAU,EACV,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAA;AAalC,OAAO,EAEL,KAAK,gBAAgB,EAEtB,MAAM,uBAAuB,CAAA;AAS9B,YAAY,EAAE,gBAAgB,EAAE,CAAA;AAEhC,qBAAa,SAAU,YAAW,QAAQ;IACxC,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,cAAc,CAAQ;IAC9B,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,YAAY,CAAqD;IACzE,OAAO,CAAC,qBAAqB,CAAC,CAAQ;IACtC,OAAO,CAAC,8BAA8B,CAAC,CAAQ;IAC/C,OAAO,CAAC,sCAAsC,CAAC,CAAQ;IACvD,OAAO,CAAC,aAAa,CAAQ;IAC7B,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAS;IAC5B,OAAO,CAAC,gBAAgB,CAAgC;gBAE5C,OAAO,GAAE,gBAAqB;IAapC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAU9D,OAAO,CAAC,OAAO;IA6Bf,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,iBAAiB;IAOzB;;6EAEyE;IACzE,OAAO,CAAC,mBAAmB;IAU3B,OAAO,CAAC,iBAAiB;IAOzB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAkBjC,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAmCnD,OAAO,CAAC,aAAa;YAqBP,uBAAuB;IAgB/B,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAwC9C,OAAO,CAAC,IAAI;CAUb;AAED,eAAe,SAAS,CAAA"}
package/dist/reporter.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveBaselineTargets,
10
10
  safeParse,
11
11
  withResolvedVisualNames
12
- } from "./chunk-KFXC7QDV.js";
12
+ } from "./chunk-WRMHUY5A.js";
13
13
 
14
14
  // src/reporter.ts
15
15
  import { existsSync } from "fs";
@@ -421,10 +421,23 @@ var CrvyRprtr = class {
421
421
  titlePath.unshift(suite.title);
422
422
  return titlePath;
423
423
  }
424
+ /** Resolves a non-empty browser label for UI display, falling back to the
425
+ * configured browser engine since the raw project `name` is `""` for the
426
+ * default project and devices spread `defaultBrowserType` into `use`. */
427
+ resolveBrowserLabel(project) {
428
+ const name = project?.name;
429
+ if (name !== void 0 && name !== "") return name;
430
+ const browserName = project?.use?.browserName;
431
+ if (browserName !== void 0) return browserName;
432
+ const defaultBrowserType = project?.use?.defaultBrowserType;
433
+ if (defaultBrowserType !== void 0) return defaultBrowserType;
434
+ return "chromium";
435
+ }
424
436
  reporterTitlePath(test) {
425
- return typeof test.titlePath === "function" ? test.titlePath() : ["", test.parent.project()?.name ?? "chromium", test.location.file, ...this.describeTitlePath(test), test.title];
437
+ return typeof test.titlePath === "function" ? test.titlePath() : ["", test.parent.project()?.name ?? "", test.location.file, ...this.describeTitlePath(test), test.title];
426
438
  }
427
439
  onTestBegin(test) {
440
+ const project = test.parent.project();
428
441
  this.testMetadata.set(test.id, {
429
442
  reporterTitlePath: this.reporterTitlePath(test)
430
443
  });
@@ -434,7 +447,8 @@ var CrvyRprtr = class {
434
447
  id: test.id,
435
448
  title: test.title,
436
449
  titlePath: this.describeTitlePath(test),
437
- browser: test.parent.project()?.name ?? "chromium",
450
+ browser: this.resolveBrowserLabel(project),
451
+ projectName: project?.name ?? "",
438
452
  location: { file: test.location.file, line: test.location.line }
439
453
  }
440
454
  });
package/dist/schemas.d.ts CHANGED
@@ -93,6 +93,7 @@ export declare const TestDataSchema: z.ZodObject<{
93
93
  id: z.ZodString;
94
94
  titlePath: z.ZodArray<z.ZodString>;
95
95
  browser: z.ZodString;
96
+ projectName: z.ZodOptional<z.ZodString>;
96
97
  title: z.ZodString;
97
98
  skip: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
98
99
  retries: z.ZodOptional<z.ZodNumber>;
@@ -153,6 +154,7 @@ export declare const CrvyRprtrTestSchema: z.ZodObject<{
153
154
  id: z.ZodString;
154
155
  titlePath: z.ZodArray<z.ZodString>;
155
156
  browser: z.ZodString;
157
+ projectName: z.ZodOptional<z.ZodString>;
156
158
  title: z.ZodString;
157
159
  skip: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
158
160
  retries: z.ZodOptional<z.ZodNumber>;
@@ -238,6 +240,7 @@ export declare const WebSocketMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
238
240
  id: z.ZodString;
239
241
  titlePath: z.ZodArray<z.ZodString>;
240
242
  browser: z.ZodString;
243
+ projectName: z.ZodOptional<z.ZodString>;
241
244
  title: z.ZodString;
242
245
  skip: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
243
246
  retries: z.ZodOptional<z.ZodNumber>;
@@ -299,6 +302,7 @@ export declare const WebSocketMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
299
302
  id: z.ZodString;
300
303
  titlePath: z.ZodArray<z.ZodString>;
301
304
  browser: z.ZodString;
305
+ projectName: z.ZodOptional<z.ZodString>;
302
306
  title: z.ZodString;
303
307
  skip: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
304
308
  retries: z.ZodOptional<z.ZodNumber>;
@@ -371,6 +375,7 @@ export declare const WebSocketMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObjec
371
375
  id: z.ZodString;
372
376
  titlePath: z.ZodArray<z.ZodString>;
373
377
  browser: z.ZodString;
378
+ projectName: z.ZodOptional<z.ZodString>;
374
379
  title: z.ZodString;
375
380
  skip: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
376
381
  retries: z.ZodOptional<z.ZodNumber>;
@@ -438,6 +443,7 @@ export declare const TestBeginDataSchema: z.ZodObject<{
438
443
  title: z.ZodString;
439
444
  titlePath: z.ZodArray<z.ZodString>;
440
445
  browser: z.ZodString;
446
+ projectName: z.ZodOptional<z.ZodString>;
441
447
  location: z.ZodObject<{
442
448
  file: z.ZodString;
443
449
  line: z.ZodNumber;
@@ -493,6 +499,7 @@ export declare const ReportDataSchema: z.ZodObject<{
493
499
  id: z.ZodString;
494
500
  titlePath: z.ZodArray<z.ZodString>;
495
501
  browser: z.ZodString;
502
+ projectName: z.ZodOptional<z.ZodString>;
496
503
  title: z.ZodString;
497
504
  skip: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
498
505
  retries: z.ZodOptional<z.ZodNumber>;
@@ -558,6 +565,7 @@ export declare const LoadedReportDataSchema: z.ZodObject<{
558
565
  id: z.ZodString;
559
566
  titlePath: z.ZodArray<z.ZodString>;
560
567
  browser: z.ZodString;
568
+ projectName: z.ZodOptional<z.ZodString>;
561
569
  title: z.ZodString;
562
570
  skip: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
563
571
  retries: z.ZodOptional<z.ZodNumber>;
@@ -654,6 +662,7 @@ export declare const ReportApiResponseSchema: z.ZodObject<{
654
662
  id: z.ZodString;
655
663
  titlePath: z.ZodArray<z.ZodString>;
656
664
  browser: z.ZodString;
665
+ projectName: z.ZodOptional<z.ZodString>;
657
666
  title: z.ZodString;
658
667
  skip: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
659
668
  retries: z.ZodOptional<z.ZodNumber>;
@@ -718,6 +727,7 @@ export declare const ClientBootstrapDataSchema: z.ZodObject<{
718
727
  id: z.ZodString;
719
728
  titlePath: z.ZodArray<z.ZodString>;
720
729
  browser: z.ZodString;
730
+ projectName: z.ZodOptional<z.ZodString>;
721
731
  title: z.ZodString;
722
732
  skip: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodString]>>;
723
733
  retries: z.ZodOptional<z.ZodNumber>;
@@ -1 +1 @@
1
- {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"artifact-routes.d.ts","sourceRoot":"","sources":["../../src/server/artifact-routes.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAWhD,wBAAsB,UAAU,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAgCpF;AAOD,MAAM,MAAM,eAAe,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAA;AAE9D,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAyBf;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmCxF;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAQhH"}
1
+ {"version":3,"file":"artifact-routes.d.ts","sourceRoot":"","sources":["../../src/server/artifact-routes.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAWhD,wBAAsB,UAAU,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyCpF;AAYD,MAAM,MAAM,eAAe,GAAG,aAAa,CAAC,iBAAiB,CAAC,CAAA;AAE9D,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,eAAe,EACxB,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,GAChB,MAAM,GAAG,IAAI,CAyBf;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,CAmCxF;AAED,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,IAAI,CAQhH"}
package/dist/server.cjs CHANGED
@@ -35,16 +35,34 @@ __export(server_exports, {
35
35
  module.exports = __toCommonJS(server_exports);
36
36
 
37
37
  // src/server/app.ts
38
- var import_path8 = require("path");
38
+ var import_path7 = require("path");
39
39
  var import_url = require("url");
40
40
  var import_p_limit = __toESM(require("p-limit"), 1);
41
41
 
42
42
  // src/offline-reports.ts
43
43
  var import_promises = require("fs/promises");
44
- var import_path2 = require("path");
44
+ var import_path = require("path");
45
+
46
+ // src/path-utils.ts
47
+ var posixAbsolutePattern = /^\//u;
48
+ var windowsDrivePattern = /^[A-Za-z]:[\\/]/u;
49
+ var windowsUncPattern = /^[\\/][\\/]/u;
50
+ function isPosixAbsolutePath(p) {
51
+ return posixAbsolutePattern.test(p);
52
+ }
53
+ function isWindowsAbsolutePath(p) {
54
+ return windowsDrivePattern.test(p) || windowsUncPattern.test(p);
55
+ }
56
+ function isAnyAbsolutePath(p) {
57
+ return isPosixAbsolutePath(p) || isWindowsAbsolutePath(p);
58
+ }
59
+ function isForeignAbsolutePath(p, hostPlatform) {
60
+ if (!isAnyAbsolutePath(p)) return false;
61
+ if (hostPlatform === "win32") return !isWindowsAbsolutePath(p);
62
+ return !isPosixAbsolutePath(p);
63
+ }
45
64
 
46
65
  // src/report-utils.ts
47
- var import_path = require("path");
48
66
  function normalizeScreenshotsBaseUrl(baseUrl) {
49
67
  return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
50
68
  }
@@ -89,7 +107,7 @@ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/")
89
107
  const role = match[2];
90
108
  if (baseName === null || baseName === void 0 || role === null || role === void 0) continue;
91
109
  images[baseName] ??= {};
92
- const url = (0, import_path.isAbsolute)(attachment.path) ? `/file/${encodeURIComponent(attachment.path)}` : `${baseUrl}${attachment.path}`;
110
+ const url = isAnyAbsolutePath(attachment.path) ? `/file/${encodeURIComponent(attachment.path)}` : `${baseUrl}${attachment.path}`;
93
111
  const img = images[baseName];
94
112
  if (img !== null && img !== void 0) {
95
113
  if (role === "actual") img.actual = url;
@@ -124,9 +142,6 @@ function isCurrentArtifact(image) {
124
142
  }
125
143
  function isReusablePassingImage(image) {
126
144
  const source = image.source ?? classifyImage(image);
127
- if (source === "comparison") {
128
- return image.actual !== void 0 && image.diff === void 0;
129
- }
130
145
  return source === "baseline-only";
131
146
  }
132
147
  function hasReusablePassingImages(images) {
@@ -147,8 +162,8 @@ function preservePreviousPassingImages(test, status, images) {
147
162
  return {
148
163
  ...currentImages,
149
164
  [name]: {
150
- ...previousImage,
151
- source: previousImage.source ?? classifyImage(previousImage)
165
+ expect: previousImage.expect,
166
+ source: "baseline-only"
152
167
  }
153
168
  };
154
169
  }, images);
@@ -169,12 +184,16 @@ function createMutableReportState(screenshotDir = "./screenshots") {
169
184
  };
170
185
  }
171
186
  function applyTestBeginEvent(state, data) {
172
- const { id, title, titlePath, browser, location } = data;
187
+ const { id, title, titlePath, browser, projectName, location } = data;
173
188
  state.currentRunIds.add(id);
174
189
  state.reportData.tests[id] ??= {
175
190
  id,
176
191
  titlePath: titlePath ?? [],
177
192
  browser: browser ?? "",
193
+ // Older reporters sent the raw project name in `browser` and had no
194
+ // `projectName` field. Preserve that value for snapshot path resolution
195
+ // when loading data produced by such reporters.
196
+ projectName: projectName ?? browser ?? "",
178
197
  title: title ?? "",
179
198
  location,
180
199
  status: "running"
@@ -278,6 +297,7 @@ var TestDataSchema = import_zod.z.object({
278
297
  id: import_zod.z.string(),
279
298
  titlePath: import_zod.z.array(import_zod.z.string()),
280
299
  browser: import_zod.z.string(),
300
+ projectName: import_zod.z.string().optional(),
281
301
  title: import_zod.z.string(),
282
302
  skip: import_zod.z.union([import_zod.z.boolean(), import_zod.z.string()]).optional(),
283
303
  retries: import_zod.z.number().optional(),
@@ -329,6 +349,7 @@ var TestBeginDataSchema = import_zod.z.object({
329
349
  title: import_zod.z.string(),
330
350
  titlePath: import_zod.z.array(import_zod.z.string()),
331
351
  browser: import_zod.z.string(),
352
+ projectName: import_zod.z.string().optional(),
332
353
  location: LocationSchema
333
354
  });
334
355
  var TestEndDataSchema = import_zod.z.object({
@@ -406,7 +427,7 @@ var OFFLINE_REPORT_FILE_PATTERN = /^crvy-rprtr(?:-\d+)?\.json$/;
406
427
  async function findOfflineReportPaths(searchDir) {
407
428
  try {
408
429
  const entries = await (0, import_promises.readdir)(searchDir);
409
- return entries.filter((entry) => OFFLINE_REPORT_FILE_PATTERN.test(entry)).sort((left, right) => left.localeCompare(right)).map((entry) => (0, import_path2.join)(searchDir, entry));
430
+ return entries.filter((entry) => OFFLINE_REPORT_FILE_PATTERN.test(entry)).sort((left, right) => left.localeCompare(right)).map((entry) => (0, import_path.join)(searchDir, entry));
410
431
  } catch (error) {
411
432
  if (error instanceof Error && "code" in error && error.code === "ENOENT") {
412
433
  return [];
@@ -458,7 +479,7 @@ function mergeOfflineReportsIntoTests(existingTests, offlineReports, options = {
458
479
 
459
480
  // src/server/file-utils.ts
460
481
  var import_promises2 = require("fs/promises");
461
- var import_path3 = require("path");
482
+ var import_path2 = require("path");
462
483
  function isFileNotFound(error) {
463
484
  return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
464
485
  }
@@ -499,16 +520,16 @@ async function readJsonFile(filePath) {
499
520
  }
500
521
  }
501
522
  async function writeJsonFile(filePath, value) {
502
- await (0, import_promises2.mkdir)((0, import_path3.dirname)(filePath), { recursive: true });
523
+ await (0, import_promises2.mkdir)((0, import_path2.dirname)(filePath), { recursive: true });
503
524
  await (0, import_promises2.writeFile)(filePath, `${JSON.stringify(value, null, 2)}
504
525
  `);
505
526
  }
506
527
  async function copyFilePortable(sourcePath, destinationPath) {
507
- await (0, import_promises2.mkdir)((0, import_path3.dirname)(destinationPath), { recursive: true });
528
+ await (0, import_promises2.mkdir)((0, import_path2.dirname)(destinationPath), { recursive: true });
508
529
  await (0, import_promises2.copyFile)(sourcePath, destinationPath);
509
530
  }
510
531
  function inferContentType(filePath) {
511
- switch ((0, import_path3.extname)(filePath).toLowerCase()) {
532
+ switch ((0, import_path2.extname)(filePath).toLowerCase()) {
512
533
  case ".css":
513
534
  return "text/css";
514
535
  case ".gif":
@@ -557,11 +578,11 @@ var import_fs2 = require("fs");
557
578
  // src/server/artifact-routes.ts
558
579
  var import_fs = require("fs");
559
580
  var import_promises3 = require("fs/promises");
560
- var import_path6 = require("path");
581
+ var import_path5 = require("path");
561
582
 
562
583
  // src/snapshot-path-resolver.ts
563
584
  var import_crypto = require("crypto");
564
- var import_path4 = require("path");
585
+ var import_path3 = require("path");
565
586
  var DEFAULT_SCREENSHOT_TEMPLATE = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}";
566
587
  var WINDOWS_FILESYSTEM_FRIENDLY_LENGTH = 60;
567
588
  function isUnsafeFilePathCharacter(character) {
@@ -596,16 +617,16 @@ function trimLongString(value, length = WINDOWS_FILESYSTEM_FRIENDLY_LENGTH) {
596
617
  const end = length - middle.length - start;
597
618
  return value.slice(0, start) + middle + value.slice(-end);
598
619
  }
599
- function sanitizeFilePathBeforeExtension(filePath, extension = (0, import_path4.extname)(filePath)) {
620
+ function sanitizeFilePathBeforeExtension(filePath, extension = (0, import_path3.extname)(filePath)) {
600
621
  const base = filePath.slice(0, filePath.length - extension.length);
601
622
  return sanitizeForFilePath(base) + extension;
602
623
  }
603
624
  function addSuffixToFilePath(filePath, suffix) {
604
- const extension = (0, import_path4.extname)(filePath);
625
+ const extension = (0, import_path3.extname)(filePath);
605
626
  return filePath.slice(0, filePath.length - extension.length) + suffix + extension;
606
627
  }
607
628
  function normalizedSnapshotDir(config) {
608
- return (0, import_path4.resolve)(config.configDir, config.snapshotDir);
629
+ return (0, import_path3.resolve)(config.configDir, config.snapshotDir);
609
630
  }
610
631
  function templateValue(template, token, value) {
611
632
  return template.replace(
@@ -615,8 +636,8 @@ function templateValue(template, token, value) {
615
636
  }
616
637
  function applyTemplate(input, nameArgument, extension) {
617
638
  const template = input.config.toHaveScreenshotPathTemplate ?? input.config.snapshotPathTemplate ?? DEFAULT_SCREENSHOT_TEMPLATE;
618
- const relativeTestFilePath = (0, import_path4.relative)(input.config.testDir, input.testFile);
619
- const parsed = (0, import_path4.parse)(relativeTestFilePath);
639
+ const relativeTestFilePath = (0, import_path3.relative)(input.config.testDir, input.testFile);
640
+ const parsed = (0, import_path3.parse)(relativeTestFilePath);
620
641
  const tokens = [
621
642
  ["testDir", input.config.testDir],
622
643
  ["snapshotDir", normalizedSnapshotDir(input.config)],
@@ -634,16 +655,16 @@ function applyTemplate(input, nameArgument, extension) {
634
655
  (currentTemplate, [token, value]) => templateValue(currentTemplate, token, value),
635
656
  template
636
657
  );
637
- return (0, import_path4.resolve)(input.config.configDir, snapshotPath);
658
+ return (0, import_path3.resolve)(input.config.configDir, snapshotPath);
638
659
  }
639
- function removeExtension(filePath, extension = (0, import_path4.extname)(filePath)) {
660
+ function removeExtension(filePath, extension = (0, import_path3.extname)(filePath)) {
640
661
  return filePath.slice(0, filePath.length - extension.length);
641
662
  }
642
663
  function snapshotNameParts(declaredName) {
643
- const extension = (0, import_path4.extname)(declaredName) || ".png";
664
+ const extension = (0, import_path3.extname)(declaredName) || ".png";
644
665
  return {
645
666
  extension,
646
- filePath: (0, import_path4.extname)(declaredName) === "" ? `${declaredName}${extension}` : declaredName
667
+ filePath: (0, import_path3.extname)(declaredName) === "" ? `${declaredName}${extension}` : declaredName
647
668
  };
648
669
  }
649
670
  function filePathForOccurrence(filePath, occurrenceIndex) {
@@ -667,7 +688,7 @@ function resolveStringCallTarget(input, declaration) {
667
688
  function resolveArrayCallTarget(input, declaration) {
668
689
  const { extension, filePath } = snapshotNameParts(declaration.declaredName);
669
690
  const occurrenceFilePath = filePathForOccurrence(filePath, declaration.occurrenceIndex);
670
- const nameArgument = (0, import_path4.join)((0, import_path4.dirname)(occurrenceFilePath), (0, import_path4.basename)(occurrenceFilePath, extension));
691
+ const nameArgument = (0, import_path3.join)((0, import_path3.dirname)(occurrenceFilePath), (0, import_path3.basename)(occurrenceFilePath, extension));
671
692
  return createResolvedBaselineTarget(input, declaration, nameArgument, extension);
672
693
  }
673
694
  function resolveNamedTarget(input, declaration) {
@@ -705,7 +726,7 @@ function resolveTarget(input, declaration) {
705
726
  case "unnamed": {
706
727
  const anonymousFileName = anonymousName(input.reporterTitlePath, declaration.occurrenceIndex);
707
728
  const extension = ".png";
708
- const nameArgument = (0, import_path4.join)((0, import_path4.dirname)(anonymousFileName), (0, import_path4.basename)(anonymousFileName, extension));
729
+ const nameArgument = (0, import_path3.join)((0, import_path3.dirname)(anonymousFileName), (0, import_path3.basename)(anonymousFileName, extension));
709
730
  return createResolvedBaselineTarget(input, declaration, nameArgument, extension);
710
731
  }
711
732
  }
@@ -718,7 +739,7 @@ function resolveBaselineTargets(input) {
718
739
  }
719
740
 
720
741
  // src/server/utils.ts
721
- var import_path5 = require("path");
742
+ var import_path4 = require("path");
722
743
  var LIVE_UPDATES_WEBSOCKET_PATH = "/";
723
744
  function broadcastToBrowsers(wsClients, msg) {
724
745
  const payload = JSON.stringify(msg);
@@ -730,10 +751,10 @@ function isWebSocketUpgradeRequest(req) {
730
751
  return new URL(req.url).pathname === LIVE_UPDATES_WEBSOCKET_PATH && req.headers.get("upgrade")?.toLowerCase() === "websocket";
731
752
  }
732
753
  function isPathWithinRoots(target, roots) {
733
- const resolvedTarget = (0, import_path5.resolve)(target);
754
+ const resolvedTarget = (0, import_path4.resolve)(target);
734
755
  return roots.some((root) => {
735
- const rel = (0, import_path5.relative)((0, import_path5.resolve)(root), resolvedTarget);
736
- return rel === "" || !rel.startsWith(`..${import_path5.sep}`) && rel !== ".." && !(0, import_path5.isAbsolute)(rel);
756
+ const rel = (0, import_path4.relative)((0, import_path4.resolve)(root), resolvedTarget);
757
+ return rel === "" || !rel.startsWith(`..${import_path4.sep}`) && rel !== ".." && !(0, import_path4.isAbsolute)(rel);
737
758
  });
738
759
  }
739
760
 
@@ -747,17 +768,24 @@ async function realpathOrNull(path) {
747
768
  }
748
769
  async function handleFile(ctx, req) {
749
770
  const notFound = () => new Response("Not Found", { status: 404 });
771
+ let rawDecoded;
750
772
  let decodedPath;
751
773
  try {
752
- decodedPath = (0, import_path6.resolve)(decodeURIComponent(new URL(req.url).pathname.slice("/file/".length)));
774
+ rawDecoded = decodeURIComponent(new URL(req.url).pathname.slice("/file/".length));
775
+ decodedPath = (0, import_path5.resolve)(rawDecoded);
753
776
  } catch {
754
777
  return notFound();
755
778
  }
756
779
  const realTarget = await realpathOrNull(decodedPath);
757
780
  if (realTarget === null) {
781
+ if (isForeignAbsolutePath(rawDecoded, process.platform)) {
782
+ console.warn(
783
+ `[Crvy Rprtr] /file request resolved to a foreign-OS absolute path that cannot be served on ${process.platform}: ${rawDecoded}`
784
+ );
785
+ }
758
786
  return notFound();
759
787
  }
760
- const realRoots = (await Promise.all((ctx.artifactRoots ?? []).map((root) => realpathOrNull((0, import_path6.resolve)(root))))).filter(
788
+ const realRoots = (await Promise.all((ctx.artifactRoots ?? []).map((root) => realpathOrNull((0, import_path5.resolve)(root))))).filter(
761
789
  (root) => root !== null
762
790
  );
763
791
  if (!isPathWithinRoots(realTarget, realRoots)) {
@@ -772,7 +800,8 @@ async function handleFile(ctx, req) {
772
800
  }
773
801
  function reporterTitlePath(test) {
774
802
  const testFile = test.location?.file;
775
- return ["", test.browser, testFile ?? "", ...test.titlePath, test.title];
803
+ const projectName = test.projectName ?? test.browser;
804
+ return ["", projectName, testFile ?? "", ...test.titlePath, test.title];
776
805
  }
777
806
  function resolveBaselineSnapshotPath(routing, test, retry, imageName) {
778
807
  const testFile = test.location?.file;
@@ -786,9 +815,9 @@ function resolveBaselineSnapshotPath(routing, test, retry, imageName) {
786
815
  declarations: [declaration],
787
816
  config: {
788
817
  configDir: routing.configDir,
789
- testDir: routing.playwrightTestDir ?? (0, import_path6.dirname)(testFile),
790
- snapshotDir: routing.playwrightSnapshotDir ?? (0, import_path6.dirname)(testFile),
791
- projectName: test.browser,
818
+ testDir: routing.playwrightTestDir ?? (0, import_path5.dirname)(testFile),
819
+ snapshotDir: routing.playwrightSnapshotDir ?? (0, import_path5.dirname)(testFile),
820
+ projectName: test.projectName ?? test.browser,
792
821
  snapshotSuffix: process.platform,
793
822
  snapshotPathTemplate: routing.playwrightSnapshotPathTemplate,
794
823
  toHaveScreenshotPathTemplate: routing.playwrightToHaveScreenshotPathTemplate
@@ -946,9 +975,9 @@ function handleRegister(ctx, data) {
946
975
  }
947
976
 
948
977
  // src/server/routes.ts
949
- var import_path7 = require("path");
978
+ var import_path6 = require("path");
950
979
  async function handleRoot(ctx) {
951
- const html = await respondWithFile((0, import_path7.join)(ctx.staticDir, "index.html"), "text/html");
980
+ const html = await respondWithFile((0, import_path6.join)(ctx.staticDir, "index.html"), "text/html");
952
981
  return html ?? new Response("Not Found", { status: 404 });
953
982
  }
954
983
  async function handleAppCss() {
@@ -968,7 +997,7 @@ function handleApiReport(ctx) {
968
997
  var APPROVAL_TARGET_ERROR = "Could not resolve approval target";
969
998
  function actualPathFromUrl(ctx, actualUrl) {
970
999
  if (actualUrl.startsWith("/screenshots/")) {
971
- return (0, import_path7.join)(ctx.reportData.screenshotDir, actualUrl.slice("/screenshots/".length));
1000
+ return (0, import_path6.join)(ctx.reportData.screenshotDir, actualUrl.slice("/screenshots/".length));
972
1001
  }
973
1002
  if (actualUrl.startsWith("/file/")) {
974
1003
  return decodeURIComponent(actualUrl.slice("/file/".length));
@@ -1090,7 +1119,7 @@ async function handleScreenshots(ctx, req) {
1090
1119
  }
1091
1120
  async function handleDist(ctx, req) {
1092
1121
  const path = new URL(req.url).pathname.slice("/dist/".length);
1093
- const filePath = (0, import_path7.join)(ctx.staticDir, path);
1122
+ const filePath = (0, import_path6.join)(ctx.staticDir, path);
1094
1123
  const contentType = filePath.endsWith(".css") ? "text/css" : filePath.endsWith(".js") ? "application/javascript" : filePath.endsWith(".svelte") ? "text/plain" : "application/octet-stream";
1095
1124
  const file = await respondWithFile(filePath, contentType);
1096
1125
  return file ?? new Response("Not Found", { status: 404 });
@@ -1253,19 +1282,19 @@ function createWebSocketMessageHandler(getHandlerContext) {
1253
1282
  };
1254
1283
  }
1255
1284
  async function resolveStaticDir(staticDir) {
1256
- const currentDir = (0, import_path8.dirname)((0, import_url.fileURLToPath)(import_meta.url));
1285
+ const currentDir = (0, import_path7.dirname)((0, import_url.fileURLToPath)(import_meta.url));
1257
1286
  const candidates = staticDir === void 0 ? [
1258
1287
  currentDir,
1259
- (0, import_path8.join)(currentDir, "dist"),
1260
- (0, import_path8.join)(currentDir, "..", "dist"),
1261
- (0, import_path8.join)(currentDir, "..", "..", "dist"),
1262
- (0, import_path8.join)(currentDir, ".."),
1263
- (0, import_path8.join)(currentDir, "..", "..")
1264
- ] : [staticDir, (0, import_path8.join)(staticDir, "dist")];
1288
+ (0, import_path7.join)(currentDir, "dist"),
1289
+ (0, import_path7.join)(currentDir, "..", "dist"),
1290
+ (0, import_path7.join)(currentDir, "..", "..", "dist"),
1291
+ (0, import_path7.join)(currentDir, ".."),
1292
+ (0, import_path7.join)(currentDir, "..", "..")
1293
+ ] : [staticDir, (0, import_path7.join)(staticDir, "dist")];
1265
1294
  const resolvedCandidates = await Promise.all(
1266
1295
  candidates.map(async (candidate) => ({
1267
1296
  candidate,
1268
- exists: await fileExists((0, import_path8.join)(candidate, "index.html"))
1297
+ exists: await fileExists((0, import_path7.join)(candidate, "index.html"))
1269
1298
  }))
1270
1299
  );
1271
1300
  const resolved = resolvedCandidates.find(({ exists }) => exists);
@@ -1276,9 +1305,9 @@ async function resolveStaticDir(staticDir) {
1276
1305
  }
1277
1306
  async function resolveReportPath(reportPath) {
1278
1307
  if (await isDirectory(reportPath)) {
1279
- return { reportFile: (0, import_path8.join)(reportPath, "report.json"), offlineReportDir: reportPath };
1308
+ return { reportFile: (0, import_path7.join)(reportPath, "report.json"), offlineReportDir: reportPath };
1280
1309
  }
1281
- return { reportFile: reportPath, offlineReportDir: (0, import_path8.dirname)(reportPath) };
1310
+ return { reportFile: reportPath, offlineReportDir: (0, import_path7.dirname)(reportPath) };
1282
1311
  }
1283
1312
  function createRoutesContext(reportData, staticDir, saveReport, options) {
1284
1313
  const artifactRoots = [reportData.screenshotDir, options.outputDir, options.playwrightSnapshotDir].filter(
package/dist/server.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  startServer
3
- } from "./chunk-RJEAJDMQ.js";
4
- import "./chunk-KFXC7QDV.js";
3
+ } from "./chunk-KLGJSEKV.js";
4
+ import "./chunk-WRMHUY5A.js";
5
5
  export {
6
6
  startServer
7
7
  };
package/dist/types.d.ts CHANGED
@@ -30,6 +30,13 @@ export interface TestData {
30
30
  id: string;
31
31
  titlePath: string[];
32
32
  browser: string;
33
+ /**
34
+ * Raw Playwright project name. Used for snapshot path resolution so the
35
+ * resolver matches what Playwright itself used when storing baselines
36
+ * (empty string for the implicit default project). Undefined for data
37
+ * produced by older reporters; consumers fall back to `browser`.
38
+ */
39
+ projectName?: string;
33
40
  title: string;
34
41
  skip?: boolean | string;
35
42
  retries?: number;
@@ -87,6 +94,7 @@ export interface TestBeginMessage {
87
94
  title: string;
88
95
  titlePath: string[];
89
96
  browser: string;
97
+ projectName?: string;
90
98
  location: Location;
91
99
  };
92
100
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAA;AAEhE,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,eAAe,GAAG,eAAe,CAAA;AAE3E,MAAM,WAAW,MAAM;IACrB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,YAAY,CAAA;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,UAAU,CAAA;AAE3G,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAA;AAE/D,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,gBAAgB,CAAA;IACxB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IACxC,kBAAkB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAA;IACrD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,EAAE,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf;;;;;OAKG;IACH,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IACvB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,OAAO,CAAC,EAAE,UAAU,EAAE,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,IAAI,CAAA;IACjD,WAAW,CAAC,EAAE,UAAU,EAAE,CAAA;IAC1B,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB;AAED,MAAM,WAAW,aAAc,SAAQ,QAAQ;IAC7C,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,IAAI,EAAE,OAAO,CAAA;IACb,MAAM,CAAC,EAAE,UAAU,CAAA;IACnB,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,EAAE,OAAO,CAAA;IAChB,aAAa,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,aAAa,CAAC,CAAC,CAAA;CACnE;AAED,MAAM,MAAM,cAAc,GAAG,cAAc,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAA;AAExE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,CAAA;IAChE,IAAI,EAAE,OAAO,CAAA;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;IACvC,cAAc,EAAE,MAAM,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;IAC/B,YAAY,CAAC,EAAE,OAAO,CAAA;CACvB;AAED,MAAM,MAAM,sBAAsB,GAC9B;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,iBAAiB,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAEtC,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,YAAY,CAAA;IAClB,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAA;QACV,KAAK,EAAE,MAAM,CAAA;QACb,SAAS,EAAE,MAAM,EAAE,CAAA;QACnB,OAAO,EAAE,MAAM,CAAA;QACf,WAAW,CAAC,EAAE,MAAM,CAAA;QACpB,QAAQ,EAAE,QAAQ,CAAA;KACnB,CAAA;CACF;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAA;IAChB,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAA;QACV,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;QACvC,WAAW,EAAE,UAAU,EAAE,CAAA;QACzB,WAAW,EAAE,MAAM,EAAE,CAAA;QACrB,kBAAkB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAA;QACrD,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAA;CACF;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,SAAS,CAAA;IACf,IAAI,EAAE;QAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAA;KAAE,CAAA;CAClD;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAA;IACxC,QAAQ,EAAE,MAAM,EAAE,CAAA;IAClB,YAAY,EAAE,OAAO,CAAA;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,UAAU,GAAG,IAAI,CAAA;IACzB,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,YAAY,GAAG,UAAU,GAAG,SAAS,CAAA;IAC3C,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,GAAG,cAAc,CAAC,MAAM,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAA;IAC/E,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,YAAY,EAAE,CAAA;CACvB;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE;QACN,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;QAC/B,YAAY,EAAE,OAAO,CAAA;KACtB,CAAA;IACD,WAAW,EAAE,OAAO,CAAA;IACpB,eAAe,EAAE,OAAO,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,KAAK,IAAI,CAAC,CAEpE;AAED,wBAAgB,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,aAAa,CAKrD;AAGD,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,CAAC,EAAE,CAGzF;AAED,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAGrG;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,GAAG,MAAM,EAAE,CAG7F"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crvy/rprtr",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Playwright reporter with visual regression UI for comparing and approving screenshot tests",
5
5
  "keywords": [
6
6
  "crvy",