@crvy/rprtr 0.0.5 → 0.0.8
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 +65 -0
- package/README.md +34 -9
- package/dist/{chunk-MWLM3HWS.js → chunk-EJRLZZ22.js} +312 -125
- package/dist/{chunk-IH44LYNM.js → chunk-XU5VQ3FZ.js} +133 -53
- package/dist/cli.js +2 -2
- package/dist/debug-log.d.ts +3 -0
- package/dist/debug-log.d.ts.map +1 -0
- package/dist/index.css +3 -22
- package/dist/index.js +626 -600
- package/dist/report-state.d.ts +6 -1
- package/dist/report-state.d.ts.map +1 -1
- package/dist/report-utils.d.ts +3 -0
- package/dist/report-utils.d.ts.map +1 -1
- package/dist/reporter-artifact-ops.d.ts +22 -0
- package/dist/reporter-artifact-ops.d.ts.map +1 -0
- package/dist/reporter-utils.d.ts +11 -2
- package/dist/reporter-utils.d.ts.map +1 -1
- package/dist/reporter.cjs +417 -185
- package/dist/reporter.d.ts +12 -5
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +228 -181
- package/dist/schemas.d.ts +115 -15
- package/dist/schemas.d.ts.map +1 -1
- package/dist/server/app.d.ts +5 -0
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server/routes.d.ts +7 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server.cjs +331 -64
- package/dist/server.js +2 -2
- package/dist/snapshot-path-resolver.d.ts +30 -0
- package/dist/snapshot-path-resolver.d.ts.map +1 -0
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -9,11 +9,12 @@ import {
|
|
|
9
9
|
applyTestEndEvent,
|
|
10
10
|
createMutableReportState,
|
|
11
11
|
finalizeRunEvent,
|
|
12
|
+
resolveBaselineTargets,
|
|
12
13
|
safeParse
|
|
13
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-EJRLZZ22.js";
|
|
14
15
|
|
|
15
16
|
// src/server/app.ts
|
|
16
|
-
import { dirname as
|
|
17
|
+
import { dirname as dirname3, join as join4 } from "path";
|
|
17
18
|
import { fileURLToPath } from "url";
|
|
18
19
|
import pLimit from "p-limit";
|
|
19
20
|
|
|
@@ -177,7 +178,9 @@ function broadcastToBrowsers(wsClients, msg) {
|
|
|
177
178
|
// src/server/handlers.ts
|
|
178
179
|
function handleTestBegin(ctx, data) {
|
|
179
180
|
const test = applyTestBeginEvent(ctx, data);
|
|
181
|
+
ctx.reportData.isRunning = true;
|
|
180
182
|
console.log(` \u25B6 [${test.browser ?? "?"}] ${test.title}`);
|
|
183
|
+
broadcastToBrowsers(ctx.wsClients, { type: "test-begin", data });
|
|
181
184
|
}
|
|
182
185
|
function handleTestEnd(ctx, data) {
|
|
183
186
|
const result = applyTestEndEvent(ctx, data);
|
|
@@ -316,7 +319,8 @@ async function watchReportArtifacts(options) {
|
|
|
316
319
|
}
|
|
317
320
|
|
|
318
321
|
// src/server/routes.ts
|
|
319
|
-
import {
|
|
322
|
+
import { existsSync } from "fs";
|
|
323
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
320
324
|
var LIVE_UPDATES_WEBSOCKET_PATH = "/";
|
|
321
325
|
function isWebSocketUpgradeRequest(req) {
|
|
322
326
|
return new URL(req.url).pathname === LIVE_UPDATES_WEBSOCKET_PATH && req.headers.get("upgrade")?.toLowerCase() === "websocket";
|
|
@@ -339,6 +343,37 @@ async function handleSrcFiles(req) {
|
|
|
339
343
|
function handleApiReport(ctx) {
|
|
340
344
|
return Response.json(ctx.reportData);
|
|
341
345
|
}
|
|
346
|
+
var APPROVAL_TARGET_ERROR = "Could not resolve approval target";
|
|
347
|
+
function actualPathFromUrl(ctx, actualUrl) {
|
|
348
|
+
return actualUrl.startsWith("/screenshots/") ? join3(ctx.reportData.screenshotDir, actualUrl.slice("/screenshots/".length)) : actualUrl;
|
|
349
|
+
}
|
|
350
|
+
function reporterTitlePath(test) {
|
|
351
|
+
const testFile = test.location?.file;
|
|
352
|
+
return ["", test.browser, testFile ?? "", ...test.titlePath, test.title];
|
|
353
|
+
}
|
|
354
|
+
function resolveApprovalTarget(ctx, test, retry, imageName) {
|
|
355
|
+
const testFile = test.location?.file;
|
|
356
|
+
const declaration = test.results?.[retry]?.visualDeclarations?.find((candidate) => candidate.visualName === imageName);
|
|
357
|
+
if (ctx.approvalRouting === void 0 || testFile === void 0 || declaration === void 0) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
const targets = resolveBaselineTargets({
|
|
361
|
+
testFile,
|
|
362
|
+
reporterTitlePath: reporterTitlePath(test),
|
|
363
|
+
declarations: [declaration],
|
|
364
|
+
config: {
|
|
365
|
+
configDir: ctx.approvalRouting.configDir,
|
|
366
|
+
testDir: ctx.approvalRouting.playwrightTestDir ?? dirname2(testFile),
|
|
367
|
+
snapshotDir: ctx.approvalRouting.playwrightSnapshotDir ?? dirname2(testFile),
|
|
368
|
+
projectName: test.browser,
|
|
369
|
+
snapshotSuffix: process.platform,
|
|
370
|
+
snapshotPathTemplate: ctx.approvalRouting.playwrightSnapshotPathTemplate,
|
|
371
|
+
toHaveScreenshotPathTemplate: ctx.approvalRouting.playwrightToHaveScreenshotPathTemplate
|
|
372
|
+
},
|
|
373
|
+
snapshotPathExists: existsSync
|
|
374
|
+
});
|
|
375
|
+
return targets.length === 1 ? targets[0]?.snapshotPath ?? null : null;
|
|
376
|
+
}
|
|
342
377
|
async function handleApiApprove(ctx, req) {
|
|
343
378
|
try {
|
|
344
379
|
const rawBody = await req.json();
|
|
@@ -349,61 +384,96 @@ async function handleApiApprove(ctx, req) {
|
|
|
349
384
|
}
|
|
350
385
|
const { id, retry, image } = parsed;
|
|
351
386
|
const test = ctx.reportData.tests[id];
|
|
352
|
-
if (test
|
|
353
|
-
|
|
354
|
-
|
|
387
|
+
if (test === void 0) {
|
|
388
|
+
return Response.json({ success: false, error: "Test not found" }, { status: 404 });
|
|
389
|
+
}
|
|
390
|
+
const actualUrl = test.results?.[retry]?.images?.[image]?.actual;
|
|
391
|
+
if (actualUrl === void 0) {
|
|
392
|
+
return Response.json({ success: false, error: "Actual image not found" }, { status: 409 });
|
|
393
|
+
}
|
|
394
|
+
const snapshotPath = resolveApprovalTarget(ctx, test, retry, image);
|
|
395
|
+
if (snapshotPath === null) {
|
|
396
|
+
return Response.json({ success: false, error: APPROVAL_TARGET_ERROR }, { status: 409 });
|
|
397
|
+
}
|
|
398
|
+
try {
|
|
399
|
+
await copyFilePortable(actualPathFromUrl(ctx, actualUrl), snapshotPath);
|
|
400
|
+
test.approved = { ...test.approved ?? {}, [image]: retry };
|
|
355
401
|
await ctx.saveReport();
|
|
356
|
-
|
|
357
|
-
if (actualUrl !== null && actualUrl !== void 0 && test.location?.file !== null && test.location?.file !== void 0) {
|
|
358
|
-
const actualPath = actualUrl.replace("/screenshots/", `${ctx.reportData.screenshotDir}/`);
|
|
359
|
-
const snapshotPath = `${test.location.file}-snapshots/${image}-${test.browser}-${process.platform}.png`;
|
|
360
|
-
try {
|
|
361
|
-
await copyFilePortable(actualPath, snapshotPath);
|
|
362
|
-
console.log(` \u2714 Updated baseline: ${snapshotPath}`);
|
|
363
|
-
} catch (err) {
|
|
364
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
365
|
-
console.error(` \u2717 Failed to update baseline: ${errorMsg}`);
|
|
366
|
-
}
|
|
367
|
-
}
|
|
402
|
+
console.log(` \u2714 Updated baseline: ${snapshotPath}`);
|
|
368
403
|
console.log(` \u2714 Approved [${test.browser}] ${test.title} \u2014 ${image}`);
|
|
404
|
+
return Response.json({ success: true });
|
|
405
|
+
} catch (err) {
|
|
406
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
407
|
+
console.error(` \u2717 Failed to update baseline: ${errorMsg}`);
|
|
408
|
+
return Response.json({ success: false, error: "Failed to update baseline" }, { status: 500 });
|
|
369
409
|
}
|
|
370
|
-
return Response.json({ success: true });
|
|
371
410
|
} catch {
|
|
372
411
|
return Response.json({ success: false, error: "Invalid request" }, { status: 400 });
|
|
373
412
|
}
|
|
374
413
|
}
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const approved = {};
|
|
414
|
+
function createBulkApprovalUpdates(ctx) {
|
|
415
|
+
return Object.values(ctx.reportData.tests).flatMap((test) => {
|
|
416
|
+
if (!test.results || test.results.length === 0) {
|
|
417
|
+
return [];
|
|
418
|
+
}
|
|
381
419
|
const lastRetry = test.results.length - 1;
|
|
382
420
|
const lastResult = test.results[lastRetry];
|
|
383
|
-
if (!lastResult?.images)
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
421
|
+
if (!lastResult?.images) {
|
|
422
|
+
return [];
|
|
423
|
+
}
|
|
424
|
+
return Object.keys(lastResult.images).flatMap((imageName) => {
|
|
387
425
|
const actualUrl = lastResult.images?.[imageName]?.actual;
|
|
388
|
-
if (actualUrl
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}).catch((err) => {
|
|
395
|
-
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
396
|
-
console.error(` \u2717 Failed to update baseline: ${errorMsg}`);
|
|
397
|
-
})
|
|
398
|
-
);
|
|
426
|
+
if (actualUrl === void 0) {
|
|
427
|
+
return [Promise.resolve({ kind: "unresolved" })];
|
|
428
|
+
}
|
|
429
|
+
const snapshotPath = resolveApprovalTarget(ctx, test, lastRetry, imageName);
|
|
430
|
+
if (snapshotPath === null) {
|
|
431
|
+
return [Promise.resolve({ kind: "unresolved" })];
|
|
399
432
|
}
|
|
433
|
+
return [
|
|
434
|
+
copyFilePortable(actualPathFromUrl(ctx, actualUrl), snapshotPath).then(
|
|
435
|
+
() => ({
|
|
436
|
+
kind: "approved",
|
|
437
|
+
imageName,
|
|
438
|
+
retry: lastRetry,
|
|
439
|
+
snapshotPath,
|
|
440
|
+
test
|
|
441
|
+
})
|
|
442
|
+
).catch((err) => {
|
|
443
|
+
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
444
|
+
console.error(` \u2717 Failed to update baseline: ${errorMsg}`);
|
|
445
|
+
return { kind: "failed" };
|
|
446
|
+
})
|
|
447
|
+
];
|
|
400
448
|
});
|
|
401
|
-
test.approved = approved;
|
|
402
449
|
});
|
|
450
|
+
}
|
|
451
|
+
function summarizeBulkApprovalOutcomes(outcomes) {
|
|
452
|
+
return outcomes.reduce(
|
|
453
|
+
(summary, outcome) => {
|
|
454
|
+
switch (outcome.kind) {
|
|
455
|
+
case "approved": {
|
|
456
|
+
outcome.test.approved = { ...outcome.test.approved ?? {}, [outcome.imageName]: outcome.retry };
|
|
457
|
+
console.log(` \u2714 Updated baseline: ${outcome.snapshotPath}`);
|
|
458
|
+
return { ...summary, approved: summary.approved + 1 };
|
|
459
|
+
}
|
|
460
|
+
case "unresolved":
|
|
461
|
+
return { ...summary, unresolved: summary.unresolved + 1 };
|
|
462
|
+
case "failed":
|
|
463
|
+
return { ...summary, failed: summary.failed + 1 };
|
|
464
|
+
}
|
|
465
|
+
},
|
|
466
|
+
{ approved: 0, unresolved: 0, failed: 0 }
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
async function handleApiApproveAll(ctx) {
|
|
470
|
+
const outcomes = await Promise.all(createBulkApprovalUpdates(ctx));
|
|
471
|
+
const counts = summarizeBulkApprovalOutcomes(outcomes);
|
|
403
472
|
await ctx.saveReport();
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
473
|
+
console.log(
|
|
474
|
+
` \u2714 Approved all \u2014 approved: ${counts.approved}, unresolved: ${counts.unresolved}, failed: ${counts.failed}`
|
|
475
|
+
);
|
|
476
|
+
return Response.json({ success: counts.failed === 0, ...counts });
|
|
407
477
|
}
|
|
408
478
|
async function handleApiImages(req) {
|
|
409
479
|
const path = new URL(req.url).pathname.slice("/api/images/".length);
|
|
@@ -438,10 +508,10 @@ function handleHttpRequest(ctx, req) {
|
|
|
438
508
|
if (pathname === "/api/report") {
|
|
439
509
|
return Promise.resolve(handleApiReport(ctx));
|
|
440
510
|
}
|
|
441
|
-
if (pathname === "/api/approve") {
|
|
511
|
+
if (pathname === "/api/approve" && req.method === "POST") {
|
|
442
512
|
return handleApiApprove(ctx, req);
|
|
443
513
|
}
|
|
444
|
-
if (pathname === "/api/approve-all") {
|
|
514
|
+
if (pathname === "/api/approve-all" && req.method === "POST") {
|
|
445
515
|
return handleApiApproveAll(ctx);
|
|
446
516
|
}
|
|
447
517
|
if (pathname.startsWith("/api/images/")) {
|
|
@@ -574,7 +644,7 @@ function createWebSocketMessageHandler(getHandlerContext) {
|
|
|
574
644
|
};
|
|
575
645
|
}
|
|
576
646
|
async function resolveStaticDir(staticDir) {
|
|
577
|
-
const currentDir =
|
|
647
|
+
const currentDir = dirname3(fileURLToPath(import.meta.url));
|
|
578
648
|
const candidates = staticDir === void 0 ? [
|
|
579
649
|
currentDir,
|
|
580
650
|
join4(currentDir, "dist"),
|
|
@@ -599,7 +669,21 @@ async function resolveReportPath(reportPath) {
|
|
|
599
669
|
if (await isDirectory(reportPath)) {
|
|
600
670
|
return { reportFile: join4(reportPath, "report.json"), offlineReportDir: reportPath };
|
|
601
671
|
}
|
|
602
|
-
return { reportFile: reportPath, offlineReportDir:
|
|
672
|
+
return { reportFile: reportPath, offlineReportDir: dirname3(reportPath) };
|
|
673
|
+
}
|
|
674
|
+
function createRoutesContext(reportData, staticDir, saveReport, options) {
|
|
675
|
+
return {
|
|
676
|
+
reportData,
|
|
677
|
+
staticDir,
|
|
678
|
+
saveReport,
|
|
679
|
+
approvalRouting: {
|
|
680
|
+
configDir: options.configDir ?? process.cwd(),
|
|
681
|
+
playwrightTestDir: options.playwrightTestDir,
|
|
682
|
+
playwrightSnapshotDir: options.playwrightSnapshotDir,
|
|
683
|
+
playwrightSnapshotPathTemplate: options.playwrightSnapshotPathTemplate,
|
|
684
|
+
playwrightToHaveScreenshotPathTemplate: options.playwrightToHaveScreenshotPathTemplate
|
|
685
|
+
}
|
|
686
|
+
};
|
|
603
687
|
}
|
|
604
688
|
async function createServerApp(options = {}) {
|
|
605
689
|
const port = options.port ?? 3e3;
|
|
@@ -612,11 +696,7 @@ async function createServerApp(options = {}) {
|
|
|
612
696
|
async function saveReport() {
|
|
613
697
|
await writeJsonFile(reportFile, reportData);
|
|
614
698
|
}
|
|
615
|
-
const routesContext =
|
|
616
|
-
reportData,
|
|
617
|
-
staticDir,
|
|
618
|
-
saveReport
|
|
619
|
-
};
|
|
699
|
+
const routesContext = createRoutesContext(reportData, staticDir, saveReport, options);
|
|
620
700
|
const getHandlerContext = () => createHandlerContext(reportData, wsClients, currentRunIds, saveReport);
|
|
621
701
|
const handleRequest = (req) => handleHttpRequest(routesContext, req);
|
|
622
702
|
const handleWebSocketMessage = createWebSocketMessageHandler(getHandlerContext);
|
package/dist/cli.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-log.d.ts","sourceRoot":"","sources":["../src/debug-log.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,GAAG,GAAI,GAAG,MAAM,OAAO,EAAE,KAAG,IAExC,CAAA;AACD,eAAO,MAAM,QAAQ,GAAI,GAAG,MAAM,OAAO,EAAE,KAAG,IAE7C,CAAA"}
|
package/dist/index.css
CHANGED
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
--color-white: #fff;
|
|
25
25
|
--spacing: 0.25rem;
|
|
26
26
|
--container-xl: 36rem;
|
|
27
|
-
--container-2xl: 42rem;
|
|
28
27
|
--text-xs: 0.75rem;
|
|
29
28
|
--text-xs--line-height: calc(1 / 0.75);
|
|
30
29
|
--text-sm: 0.875rem;
|
|
@@ -324,6 +323,9 @@
|
|
|
324
323
|
.inline-block {
|
|
325
324
|
display: inline-block;
|
|
326
325
|
}
|
|
326
|
+
.table {
|
|
327
|
+
display: table;
|
|
328
|
+
}
|
|
327
329
|
.size-2 {
|
|
328
330
|
width: calc(var(--spacing) * 2);
|
|
329
331
|
height: calc(var(--spacing) * 2);
|
|
@@ -388,9 +390,6 @@
|
|
|
388
390
|
.w-full {
|
|
389
391
|
width: 100%;
|
|
390
392
|
}
|
|
391
|
-
.max-w-2xl {
|
|
392
|
-
max-width: var(--container-2xl);
|
|
393
|
-
}
|
|
394
393
|
.max-w-full {
|
|
395
394
|
max-width: 100%;
|
|
396
395
|
}
|
|
@@ -538,12 +537,6 @@
|
|
|
538
537
|
.border-edge {
|
|
539
538
|
border-color: var(--color-edge);
|
|
540
539
|
}
|
|
541
|
-
.border-edge\/70 {
|
|
542
|
-
border-color: var(--color-edge);
|
|
543
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
544
|
-
border-color: color-mix(in oklab, var(--color-edge) 70%, transparent);
|
|
545
|
-
}
|
|
546
|
-
}
|
|
547
540
|
.border-error {
|
|
548
541
|
border-color: var(--color-error);
|
|
549
542
|
}
|
|
@@ -556,12 +549,6 @@
|
|
|
556
549
|
border-color: color-mix(in oklab, var(--color-green-500) 60%, transparent);
|
|
557
550
|
}
|
|
558
551
|
}
|
|
559
|
-
.border-info\/40 {
|
|
560
|
-
border-color: var(--color-info);
|
|
561
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
562
|
-
border-color: color-mix(in oklab, var(--color-info) 40%, transparent);
|
|
563
|
-
}
|
|
564
|
-
}
|
|
565
552
|
.border-purple-500\/60 {
|
|
566
553
|
border-color: color-mix(in srgb, oklch(62.7% 0.265 303.9) 60%, transparent);
|
|
567
554
|
@supports (color: color-mix(in lab, red, red)) {
|
|
@@ -616,12 +603,6 @@
|
|
|
616
603
|
.bg-info {
|
|
617
604
|
background-color: var(--color-info);
|
|
618
605
|
}
|
|
619
|
-
.bg-info\/10 {
|
|
620
|
-
background-color: var(--color-info);
|
|
621
|
-
@supports (color: color-mix(in lab, red, red)) {
|
|
622
|
-
background-color: color-mix(in oklab, var(--color-info) 10%, transparent);
|
|
623
|
-
}
|
|
624
|
-
}
|
|
625
606
|
.bg-purple-500\/25 {
|
|
626
607
|
background-color: color-mix(in srgb, oklch(62.7% 0.265 303.9) 25%, transparent);
|
|
627
608
|
@supports (color: color-mix(in lab, red, red)) {
|