@crvy/rprtr 0.0.7 → 0.0.9
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 +10 -0
- package/dist/{chunk-OMDYTNWY.js → chunk-TS64TROX.js} +20 -139
- 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 +0 -22
- package/dist/index.js +83 -127
- package/dist/reporter-artifact-ops.d.ts +22 -0
- package/dist/reporter-artifact-ops.d.ts.map +1 -0
- package/dist/reporter.cjs +194 -176
- package/dist/reporter.d.ts +0 -5
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +135 -117
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/handlers.d.ts.map +1 -1
- package/dist/server.cjs +35 -154
- package/dist/server.js +2 -2
- package/package.json +1 -1
- package/dist/server/report-watch.d.ts +0 -9
- package/dist/server/report-watch.d.ts.map +0 -1
- package/dist/{chunk-JGS2VWQP.js → chunk-EJRLZZ22.js} +142 -142
package/dist/reporter.cjs
CHANGED
|
@@ -35,6 +35,20 @@ __export(reporter_exports, {
|
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(reporter_exports);
|
|
37
37
|
var import_fs = require("fs");
|
|
38
|
+
var import_promises3 = require("fs/promises");
|
|
39
|
+
var import_path4 = require("path");
|
|
40
|
+
var import_p_limit2 = __toESM(require("p-limit"), 1);
|
|
41
|
+
|
|
42
|
+
// src/debug-log.ts
|
|
43
|
+
var isDebug = () => process.env.DEBUG !== void 0 && process.env.DEBUG !== "";
|
|
44
|
+
var log = (...args) => {
|
|
45
|
+
if (isDebug()) console.log(...args);
|
|
46
|
+
};
|
|
47
|
+
var logError = (...args) => {
|
|
48
|
+
if (isDebug()) console.error(...args);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// src/reporter-artifact-ops.ts
|
|
38
52
|
var import_promises2 = require("fs/promises");
|
|
39
53
|
var import_path3 = require("path");
|
|
40
54
|
var import_p_limit = __toESM(require("p-limit"), 1);
|
|
@@ -492,75 +506,6 @@ async function writeReportArtifact(options) {
|
|
|
492
506
|
await (0, import_promises.writeFile)(reportHtmlPath, html);
|
|
493
507
|
}
|
|
494
508
|
|
|
495
|
-
// src/reporter-utils.ts
|
|
496
|
-
var NAMED_SCREENSHOT_STEP_TITLE = /toHaveScreenshot\((.+?)\)/;
|
|
497
|
-
var UNNAMED_SCREENSHOT_STEP_TITLE = /^Expect "toHaveScreenshot"(?:\s|$)/;
|
|
498
|
-
var SYNTHETIC_SCREENSHOT_PREFIX = "__unnamed-screenshot-";
|
|
499
|
-
function addVisualSuffix(visualName, occurrenceIndex) {
|
|
500
|
-
if (occurrenceIndex === 1) {
|
|
501
|
-
return visualName;
|
|
502
|
-
}
|
|
503
|
-
const slashIndex = visualName.lastIndexOf("/");
|
|
504
|
-
return slashIndex === -1 ? `${visualName}-${occurrenceIndex - 1}` : `${visualName.slice(0, slashIndex + 1)}${visualName.slice(slashIndex + 1)}-${occurrenceIndex - 1}`;
|
|
505
|
-
}
|
|
506
|
-
function normalizeNamedScreenshot(titleMatch, state) {
|
|
507
|
-
const unquotedName = titleMatch.trim().replace(/^['"`]|['"`]$/g, "");
|
|
508
|
-
if (unquotedName === "") {
|
|
509
|
-
return null;
|
|
510
|
-
}
|
|
511
|
-
const normalizedPath = unquotedName.replace(/\\/g, "/");
|
|
512
|
-
const declaredName = normalizedPath.replace(/\.png$/, "");
|
|
513
|
-
if (declaredName === "") {
|
|
514
|
-
return null;
|
|
515
|
-
}
|
|
516
|
-
const occurrenceIndex = (state.namedOccurrences.get(declaredName) ?? 0) + 1;
|
|
517
|
-
state.namedOccurrences.set(declaredName, occurrenceIndex);
|
|
518
|
-
return {
|
|
519
|
-
visualName: addVisualSuffix(declaredName, occurrenceIndex),
|
|
520
|
-
kind: "named",
|
|
521
|
-
declaredName,
|
|
522
|
-
snapshotBaseName: declaredName,
|
|
523
|
-
occurrenceIndex
|
|
524
|
-
};
|
|
525
|
-
}
|
|
526
|
-
function visitStep(step, state) {
|
|
527
|
-
const declarationsBeforeChildren = state.declarations.length;
|
|
528
|
-
for (const nestedStep of step.steps) {
|
|
529
|
-
visitStep(nestedStep, state);
|
|
530
|
-
}
|
|
531
|
-
const namedMatch = step.title.match(NAMED_SCREENSHOT_STEP_TITLE);
|
|
532
|
-
if (namedMatch?.[1] !== void 0) {
|
|
533
|
-
const declaration = normalizeNamedScreenshot(namedMatch[1], state);
|
|
534
|
-
if (declaration !== null) {
|
|
535
|
-
state.declarations.push(declaration);
|
|
536
|
-
return true;
|
|
537
|
-
}
|
|
538
|
-
}
|
|
539
|
-
const hasNestedScreenshotDeclaration = state.declarations.length > declarationsBeforeChildren;
|
|
540
|
-
if (UNNAMED_SCREENSHOT_STEP_TITLE.test(step.title) && !hasNestedScreenshotDeclaration) {
|
|
541
|
-
const occurrenceIndex = state.nextUnnamedIndex;
|
|
542
|
-
state.declarations.push({
|
|
543
|
-
visualName: `${SYNTHETIC_SCREENSHOT_PREFIX}${occurrenceIndex}`,
|
|
544
|
-
kind: "unnamed",
|
|
545
|
-
occurrenceIndex
|
|
546
|
-
});
|
|
547
|
-
state.nextUnnamedIndex += 1;
|
|
548
|
-
return true;
|
|
549
|
-
}
|
|
550
|
-
return hasNestedScreenshotDeclaration;
|
|
551
|
-
}
|
|
552
|
-
function extractScreenshotDeclarations(steps) {
|
|
553
|
-
const state = {
|
|
554
|
-
declarations: [],
|
|
555
|
-
namedOccurrences: /* @__PURE__ */ new Map(),
|
|
556
|
-
nextUnnamedIndex: 1
|
|
557
|
-
};
|
|
558
|
-
for (const step of steps) {
|
|
559
|
-
visitStep(step, state);
|
|
560
|
-
}
|
|
561
|
-
return state.declarations;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
509
|
// src/snapshot-path-resolver.ts
|
|
565
510
|
var import_crypto = require("crypto");
|
|
566
511
|
var import_path2 = require("path");
|
|
@@ -717,20 +662,168 @@ function resolveBaselineTargets(input) {
|
|
|
717
662
|
});
|
|
718
663
|
}
|
|
719
664
|
|
|
720
|
-
// src/reporter.ts
|
|
665
|
+
// src/reporter-artifact-ops.ts
|
|
721
666
|
var CURRENT_DIRECTORY_ARTIFACT_SEGMENT = "+dot+";
|
|
722
667
|
var PARENT_DIRECTORY_ARTIFACT_SEGMENT = "+dotdot+";
|
|
723
668
|
var MAX_CONCURRENT_FILE_OPS = 5;
|
|
724
669
|
var SAFE_ARTIFACT_CHARACTER = /^[A-Za-z0-9._-]$/;
|
|
725
|
-
|
|
670
|
+
function encodeArtifactPathSegment(segment) {
|
|
726
671
|
if (segment === ".") return CURRENT_DIRECTORY_ARTIFACT_SEGMENT;
|
|
727
672
|
if (segment === "..") return PARENT_DIRECTORY_ARTIFACT_SEGMENT;
|
|
728
673
|
return Array.from(
|
|
729
674
|
segment,
|
|
730
675
|
(character) => SAFE_ARTIFACT_CHARACTER.test(character) ? character : encodeURIComponent(character)
|
|
731
676
|
).join("");
|
|
732
|
-
}
|
|
733
|
-
|
|
677
|
+
}
|
|
678
|
+
function safeArtifactPath(name) {
|
|
679
|
+
return name.split("/").map(encodeArtifactPathSegment).join("/");
|
|
680
|
+
}
|
|
681
|
+
function sanitizeId(id) {
|
|
682
|
+
return id.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
683
|
+
}
|
|
684
|
+
async function copyResolvedBaseline(safeTestId, testScreenshotDir, target, savedAttachments) {
|
|
685
|
+
const attachmentName = `${target.attachmentBaseName}-expected.png`;
|
|
686
|
+
const artifactPath = safeArtifactPath(attachmentName);
|
|
687
|
+
const destPath = (0, import_path3.join)(testScreenshotDir, artifactPath);
|
|
688
|
+
try {
|
|
689
|
+
await (0, import_promises2.mkdir)((0, import_path3.dirname)(destPath), { recursive: true });
|
|
690
|
+
await (0, import_promises2.copyFile)(target.snapshotPath, destPath);
|
|
691
|
+
savedAttachments.push({ name: attachmentName, path: `${safeTestId}/${artifactPath}`, contentType: "image/png" });
|
|
692
|
+
log(`[CrvyRprtr] Attached baseline: ${target.snapshotPath}`);
|
|
693
|
+
} catch {
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
async function saveAttachments(screenshotDir, testId, result) {
|
|
697
|
+
const savedAttachments = [];
|
|
698
|
+
const safeTestId = sanitizeId(testId);
|
|
699
|
+
const testScreenshotDir = (0, import_path3.join)(screenshotDir, safeTestId);
|
|
700
|
+
const limit = (0, import_p_limit.default)(MAX_CONCURRENT_FILE_OPS);
|
|
701
|
+
await Promise.all(
|
|
702
|
+
result.attachments.filter(
|
|
703
|
+
(attachment) => attachment.contentType === "image/png" && attachment.path !== void 0
|
|
704
|
+
).map(
|
|
705
|
+
(attachment) => limit(async () => {
|
|
706
|
+
try {
|
|
707
|
+
const artifactPath = safeArtifactPath(attachment.name);
|
|
708
|
+
const destPath = (0, import_path3.join)(testScreenshotDir, artifactPath);
|
|
709
|
+
await (0, import_promises2.mkdir)((0, import_path3.dirname)(destPath), { recursive: true });
|
|
710
|
+
await (0, import_promises2.copyFile)(attachment.path, destPath);
|
|
711
|
+
savedAttachments.push({
|
|
712
|
+
name: attachment.name,
|
|
713
|
+
path: `${safeTestId}/${artifactPath}`,
|
|
714
|
+
contentType: attachment.contentType ?? "image/png"
|
|
715
|
+
});
|
|
716
|
+
log(`[CrvyRprtr] Saved screenshot: ${destPath}`);
|
|
717
|
+
} catch (error) {
|
|
718
|
+
logError(`[CrvyRprtr] Failed to save screenshot: ${attachment.path}`, error);
|
|
719
|
+
savedAttachments.push({
|
|
720
|
+
name: attachment.name,
|
|
721
|
+
path: attachment.path,
|
|
722
|
+
contentType: attachment.contentType ?? "image/png"
|
|
723
|
+
});
|
|
724
|
+
}
|
|
725
|
+
})
|
|
726
|
+
)
|
|
727
|
+
);
|
|
728
|
+
return savedAttachments;
|
|
729
|
+
}
|
|
730
|
+
async function writeOfflineReport(runEvents, offlineReportPath, workerIndex) {
|
|
731
|
+
if (runEvents.length === 0) {
|
|
732
|
+
log("[CrvyRprtr] No offline events to write");
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
try {
|
|
736
|
+
const report = {
|
|
737
|
+
version: 1,
|
|
738
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
739
|
+
workers: workerIndex + 1,
|
|
740
|
+
events: runEvents.map((event) => ({ ...event, timestamp: Date.now(), workerIndex }))
|
|
741
|
+
};
|
|
742
|
+
await (0, import_promises2.writeFile)(offlineReportPath, JSON.stringify(report, null, 2));
|
|
743
|
+
log(`[CrvyRprtr] Wrote offline report: ${offlineReportPath}`);
|
|
744
|
+
} catch (error) {
|
|
745
|
+
logError("[CrvyRprtr] Failed to write offline report:", error);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
async function writeStaticArtifact(runEvents, screenshotDir, reportHtmlPath) {
|
|
749
|
+
try {
|
|
750
|
+
await writeReportArtifact({ events: runEvents, screenshotDir, reportHtmlPath });
|
|
751
|
+
log(`[CrvyRprtr] Wrote report artifact: ${reportHtmlPath}`);
|
|
752
|
+
} catch (error) {
|
|
753
|
+
logError("[CrvyRprtr] Failed to write report artifact:", error);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/reporter-utils.ts
|
|
758
|
+
var NAMED_SCREENSHOT_STEP_TITLE = /toHaveScreenshot\((.+?)\)/;
|
|
759
|
+
var UNNAMED_SCREENSHOT_STEP_TITLE = /^Expect "toHaveScreenshot"(?:\s|$)/;
|
|
760
|
+
var SYNTHETIC_SCREENSHOT_PREFIX = "__unnamed-screenshot-";
|
|
761
|
+
function addVisualSuffix(visualName, occurrenceIndex) {
|
|
762
|
+
if (occurrenceIndex === 1) {
|
|
763
|
+
return visualName;
|
|
764
|
+
}
|
|
765
|
+
const slashIndex = visualName.lastIndexOf("/");
|
|
766
|
+
return slashIndex === -1 ? `${visualName}-${occurrenceIndex - 1}` : `${visualName.slice(0, slashIndex + 1)}${visualName.slice(slashIndex + 1)}-${occurrenceIndex - 1}`;
|
|
767
|
+
}
|
|
768
|
+
function normalizeNamedScreenshot(titleMatch, state) {
|
|
769
|
+
const unquotedName = titleMatch.trim().replace(/^['"`]|['"`]$/g, "");
|
|
770
|
+
if (unquotedName === "") {
|
|
771
|
+
return null;
|
|
772
|
+
}
|
|
773
|
+
const normalizedPath = unquotedName.replace(/\\/g, "/");
|
|
774
|
+
const declaredName = normalizedPath.replace(/\.png$/, "");
|
|
775
|
+
if (declaredName === "") {
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
const occurrenceIndex = (state.namedOccurrences.get(declaredName) ?? 0) + 1;
|
|
779
|
+
state.namedOccurrences.set(declaredName, occurrenceIndex);
|
|
780
|
+
return {
|
|
781
|
+
visualName: addVisualSuffix(declaredName, occurrenceIndex),
|
|
782
|
+
kind: "named",
|
|
783
|
+
declaredName,
|
|
784
|
+
snapshotBaseName: declaredName,
|
|
785
|
+
occurrenceIndex
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
function visitStep(step, state) {
|
|
789
|
+
const declarationsBeforeChildren = state.declarations.length;
|
|
790
|
+
for (const nestedStep of step.steps) {
|
|
791
|
+
visitStep(nestedStep, state);
|
|
792
|
+
}
|
|
793
|
+
const namedMatch = step.title.match(NAMED_SCREENSHOT_STEP_TITLE);
|
|
794
|
+
if (namedMatch?.[1] !== void 0) {
|
|
795
|
+
const declaration = normalizeNamedScreenshot(namedMatch[1], state);
|
|
796
|
+
if (declaration !== null) {
|
|
797
|
+
state.declarations.push(declaration);
|
|
798
|
+
return true;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
const hasNestedScreenshotDeclaration = state.declarations.length > declarationsBeforeChildren;
|
|
802
|
+
if (UNNAMED_SCREENSHOT_STEP_TITLE.test(step.title) && !hasNestedScreenshotDeclaration) {
|
|
803
|
+
const occurrenceIndex = state.nextUnnamedIndex;
|
|
804
|
+
state.declarations.push({
|
|
805
|
+
visualName: `${SYNTHETIC_SCREENSHOT_PREFIX}${occurrenceIndex}`,
|
|
806
|
+
kind: "unnamed",
|
|
807
|
+
occurrenceIndex
|
|
808
|
+
});
|
|
809
|
+
state.nextUnnamedIndex += 1;
|
|
810
|
+
return true;
|
|
811
|
+
}
|
|
812
|
+
return hasNestedScreenshotDeclaration;
|
|
813
|
+
}
|
|
814
|
+
function extractScreenshotDeclarations(steps) {
|
|
815
|
+
const state = {
|
|
816
|
+
declarations: [],
|
|
817
|
+
namedOccurrences: /* @__PURE__ */ new Map(),
|
|
818
|
+
nextUnnamedIndex: 1
|
|
819
|
+
};
|
|
820
|
+
for (const step of steps) {
|
|
821
|
+
visitStep(step, state);
|
|
822
|
+
}
|
|
823
|
+
return state.declarations;
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
// src/reporter.ts
|
|
734
827
|
var CrvyRprtr = class {
|
|
735
828
|
ws = null;
|
|
736
829
|
serverUrl;
|
|
@@ -758,43 +851,43 @@ var CrvyRprtr = class {
|
|
|
758
851
|
this.playwrightToHaveScreenshotPathTemplate = options.playwrightToHaveScreenshotPathTemplate;
|
|
759
852
|
}
|
|
760
853
|
async onBegin(config, suite) {
|
|
761
|
-
this.configDir = config.configFile === void 0 ? config.rootDir : (0,
|
|
762
|
-
|
|
763
|
-
await (0,
|
|
854
|
+
this.configDir = config.configFile === void 0 ? config.rootDir : (0, import_path4.dirname)(config.configFile);
|
|
855
|
+
log(`[CrvyRprtr] Starting run with ${suite.allTests().length} tests`);
|
|
856
|
+
await (0, import_promises3.mkdir)(this.screenshotDir, { recursive: true });
|
|
764
857
|
this.connect();
|
|
765
858
|
}
|
|
766
859
|
connect() {
|
|
767
860
|
const WebSocketConstructor = globalThis.WebSocket;
|
|
768
861
|
if (typeof WebSocketConstructor !== "function") {
|
|
769
|
-
|
|
862
|
+
log("[CrvyRprtr] WebSocket unavailable in current runtime; offline mode enabled");
|
|
770
863
|
this.enableOfflineMode();
|
|
771
864
|
return;
|
|
772
865
|
}
|
|
773
866
|
try {
|
|
774
867
|
this.ws = new WebSocketConstructor(this.serverUrl);
|
|
775
868
|
this.ws.onopen = () => {
|
|
776
|
-
|
|
869
|
+
log("[CrvyRprtr] Connected to Crvy Rprtr server");
|
|
777
870
|
this.isOfflineMode = false;
|
|
778
871
|
for (const message of this.queue) this.ws.send(message);
|
|
779
872
|
this.queue = [];
|
|
780
873
|
};
|
|
781
874
|
this.ws.onerror = (error) => {
|
|
782
|
-
|
|
875
|
+
logError("[CrvyRprtr] WebSocket error:", error);
|
|
783
876
|
this.enableOfflineMode();
|
|
784
877
|
};
|
|
785
878
|
this.ws.onclose = () => {
|
|
786
|
-
|
|
879
|
+
log("[CrvyRprtr] Disconnected from Crvy Rprtr server");
|
|
787
880
|
this.enableOfflineMode();
|
|
788
881
|
};
|
|
789
882
|
} catch (error) {
|
|
790
|
-
|
|
883
|
+
logError("[CrvyRprtr] Failed to connect:", error);
|
|
791
884
|
this.enableOfflineMode();
|
|
792
885
|
}
|
|
793
886
|
}
|
|
794
887
|
enableOfflineMode() {
|
|
795
888
|
if (this.isOfflineMode) return;
|
|
796
889
|
this.isOfflineMode = this.hadOfflineMode = true;
|
|
797
|
-
|
|
890
|
+
log("[CrvyRprtr] Offline mode enabled - events will be queued to file");
|
|
798
891
|
}
|
|
799
892
|
describeTitlePath(test) {
|
|
800
893
|
const titlePath = [];
|
|
@@ -820,7 +913,7 @@ var CrvyRprtr = class {
|
|
|
820
913
|
}
|
|
821
914
|
async onTestEnd(test, result) {
|
|
822
915
|
const screenshotDeclarations = extractScreenshotDeclarations(result.steps);
|
|
823
|
-
const savedAttachments = await this.
|
|
916
|
+
const savedAttachments = await saveAttachments(this.screenshotDir, test.id, result);
|
|
824
917
|
try {
|
|
825
918
|
await this.copySnapshotBaselines(test, result.status, screenshotDeclarations, savedAttachments);
|
|
826
919
|
this.send({
|
|
@@ -860,113 +953,38 @@ var CrvyRprtr = class {
|
|
|
860
953
|
snapshotPathExists: import_fs.existsSync
|
|
861
954
|
};
|
|
862
955
|
}
|
|
863
|
-
async copyResolvedBaseline(safeTestId, testScreenshotDir, target, savedAttachments) {
|
|
864
|
-
const attachmentName = `${target.attachmentBaseName}-expected.png`;
|
|
865
|
-
const artifactPath = safeArtifactPath(attachmentName);
|
|
866
|
-
const destPath = (0, import_path3.join)(testScreenshotDir, artifactPath);
|
|
867
|
-
try {
|
|
868
|
-
await (0, import_promises2.mkdir)((0, import_path3.dirname)(destPath), { recursive: true });
|
|
869
|
-
await (0, import_promises2.copyFile)(target.snapshotPath, destPath);
|
|
870
|
-
savedAttachments.push({ name: attachmentName, path: `${safeTestId}/${artifactPath}`, contentType: "image/png" });
|
|
871
|
-
console.log(`[CrvyRprtr] Attached baseline: ${target.snapshotPath}`);
|
|
872
|
-
} catch {
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
956
|
async copySnapshotBaselines(test, status, screenshotDeclarations, savedAttachments) {
|
|
876
957
|
if (status !== "passed" || screenshotDeclarations.length === 0) return;
|
|
877
958
|
const input = this.baselineInput(test, screenshotDeclarations);
|
|
878
959
|
if (input === null) return;
|
|
879
960
|
const targets = resolveBaselineTargets(input);
|
|
880
961
|
if (targets.length === 0) return;
|
|
881
|
-
const safeTestId =
|
|
882
|
-
const testScreenshotDir = (0,
|
|
883
|
-
const limit = (0,
|
|
962
|
+
const safeTestId = sanitizeId(test.id);
|
|
963
|
+
const testScreenshotDir = (0, import_path4.join)(this.screenshotDir, safeTestId);
|
|
964
|
+
const limit = (0, import_p_limit2.default)(5);
|
|
884
965
|
await Promise.all(
|
|
885
966
|
targets.map(
|
|
886
|
-
(target) => limit(() =>
|
|
967
|
+
(target) => limit(() => copyResolvedBaseline(safeTestId, testScreenshotDir, target, savedAttachments))
|
|
887
968
|
)
|
|
888
969
|
);
|
|
889
970
|
}
|
|
890
|
-
async saveAttachments(testId, result) {
|
|
891
|
-
const savedAttachments = [];
|
|
892
|
-
const safeTestId = this.sanitizeId(testId);
|
|
893
|
-
const testScreenshotDir = (0, import_path3.join)(this.screenshotDir, safeTestId);
|
|
894
|
-
const limit = (0, import_p_limit.default)(MAX_CONCURRENT_FILE_OPS);
|
|
895
|
-
await Promise.all(
|
|
896
|
-
result.attachments.filter(
|
|
897
|
-
(attachment) => attachment.contentType === "image/png" && attachment.path !== void 0
|
|
898
|
-
).map(
|
|
899
|
-
(attachment) => limit(async () => {
|
|
900
|
-
try {
|
|
901
|
-
const artifactPath = safeArtifactPath(attachment.name);
|
|
902
|
-
const destPath = (0, import_path3.join)(testScreenshotDir, artifactPath);
|
|
903
|
-
await (0, import_promises2.mkdir)((0, import_path3.dirname)(destPath), { recursive: true });
|
|
904
|
-
await (0, import_promises2.copyFile)(attachment.path, destPath);
|
|
905
|
-
savedAttachments.push({
|
|
906
|
-
name: attachment.name,
|
|
907
|
-
path: `${safeTestId}/${artifactPath}`,
|
|
908
|
-
contentType: attachment.contentType
|
|
909
|
-
});
|
|
910
|
-
console.log(`[CrvyRprtr] Saved screenshot: ${destPath}`);
|
|
911
|
-
} catch (error) {
|
|
912
|
-
console.error(`[CrvyRprtr] Failed to save screenshot: ${attachment.path}`, error);
|
|
913
|
-
savedAttachments.push({
|
|
914
|
-
name: attachment.name,
|
|
915
|
-
path: attachment.path,
|
|
916
|
-
contentType: attachment.contentType
|
|
917
|
-
});
|
|
918
|
-
}
|
|
919
|
-
})
|
|
920
|
-
)
|
|
921
|
-
);
|
|
922
|
-
return savedAttachments;
|
|
923
|
-
}
|
|
924
|
-
sanitizeId(id) {
|
|
925
|
-
return id.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
926
|
-
}
|
|
927
|
-
async writeOfflineReport() {
|
|
928
|
-
if (this.runEvents.length === 0) console.log("[CrvyRprtr] No offline events to write");
|
|
929
|
-
if (this.runEvents.length === 0) return;
|
|
930
|
-
try {
|
|
931
|
-
const report = {
|
|
932
|
-
version: 1,
|
|
933
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
934
|
-
workers: this.workerIndex + 1,
|
|
935
|
-
events: this.runEvents.map((event) => ({ ...event, timestamp: Date.now(), workerIndex: this.workerIndex }))
|
|
936
|
-
};
|
|
937
|
-
await (0, import_promises2.writeFile)(this.offlineReportPath, JSON.stringify(report, null, 2));
|
|
938
|
-
console.log(`[CrvyRprtr] Wrote offline report: ${this.offlineReportPath}`);
|
|
939
|
-
} catch (error) {
|
|
940
|
-
console.error("[CrvyRprtr] Failed to write offline report:", error);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
async writeStaticArtifact() {
|
|
944
|
-
try {
|
|
945
|
-
await writeReportArtifact({
|
|
946
|
-
events: this.runEvents,
|
|
947
|
-
screenshotDir: this.screenshotDir,
|
|
948
|
-
reportHtmlPath: this.reportHtmlPath
|
|
949
|
-
});
|
|
950
|
-
console.log(`[CrvyRprtr] Wrote report artifact: ${this.reportHtmlPath}`);
|
|
951
|
-
} catch (error) {
|
|
952
|
-
console.error("[CrvyRprtr] Failed to write report artifact:", error);
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
971
|
async onEnd(result) {
|
|
956
972
|
this.send({ type: "run-end", data: { status: result.status } });
|
|
957
|
-
await this.
|
|
958
|
-
if (this.hadOfflineMode) await this.
|
|
973
|
+
await writeStaticArtifact(this.runEvents, this.screenshotDir, this.reportHtmlPath);
|
|
974
|
+
if (this.hadOfflineMode) await writeOfflineReport(this.runEvents, this.offlineReportPath, this.workerIndex);
|
|
959
975
|
await new Promise((resolve3) => {
|
|
960
|
-
if (this.ws
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
this.ws
|
|
969
|
-
|
|
976
|
+
if (!this.ws || this.ws.readyState === WebSocket.CLOSED) {
|
|
977
|
+
resolve3();
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
this.ws.onclose = () => {
|
|
981
|
+
resolve3();
|
|
982
|
+
};
|
|
983
|
+
setTimeout(() => {
|
|
984
|
+
this.ws?.close();
|
|
985
|
+
resolve3();
|
|
986
|
+
}, 1e3);
|
|
987
|
+
this.ws.close();
|
|
970
988
|
});
|
|
971
989
|
}
|
|
972
990
|
send(message) {
|
package/dist/reporter.d.ts
CHANGED
|
@@ -33,12 +33,7 @@ export declare class CrvyRprtr implements Reporter {
|
|
|
33
33
|
onTestBegin(test: TestCase): void;
|
|
34
34
|
onTestEnd(test: TestCase, result: TestResult): Promise<void>;
|
|
35
35
|
private baselineInput;
|
|
36
|
-
private copyResolvedBaseline;
|
|
37
36
|
private copySnapshotBaselines;
|
|
38
|
-
private saveAttachments;
|
|
39
|
-
private sanitizeId;
|
|
40
|
-
private writeOfflineReport;
|
|
41
|
-
private writeStaticArtifact;
|
|
42
37
|
onEnd(result: FullResult): Promise<void>;
|
|
43
38
|
private send;
|
|
44
39
|
}
|
package/dist/reporter.d.ts.map
CHANGED
|
@@ -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;
|
|
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;AAgB9G,MAAM,WAAW,gBAAgB;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAC9B,8BAA8B,CAAC,EAAE,MAAM,CAAA;IACvC,sCAAsC,CAAC,EAAE,MAAM,CAAA;CAChD;AAED,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,cAAc,CAAQ;IAC9B,OAAO,CAAC,SAAS,CAAiB;gBAEtB,OAAO,GAAE,gBAAqB;IAWpC,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9D,OAAO,CAAC,OAAO;IA6Bf,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,iBAAiB;IAOzB,OAAO,CAAC,iBAAiB;IAMzB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAc3B,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAuBlE,OAAO,CAAC,aAAa;YAqBP,qBAAqB;IAqB7B,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAoB9C,OAAO,CAAC,IAAI;CAUb;AAED,eAAe,SAAS,CAAA"}
|