@crvy/rprtr 0.0.4 → 0.0.7
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 +81 -0
- package/README.md +41 -7
- package/dist/chunk-JGS2VWQP.js +500 -0
- package/dist/{chunk-BRFA7JCQ.js → chunk-OMDYTNWY.js} +266 -65
- package/dist/cli.js +2 -2
- package/dist/index.css +55 -0
- package/dist/index.js +691 -550
- package/dist/report-state.d.ts +6 -1
- package/dist/report-state.d.ts.map +1 -1
- package/dist/report-utils.d.ts +6 -1
- package/dist/report-utils.d.ts.map +1 -1
- package/dist/reporter-utils.d.ts +20 -1
- package/dist/reporter-utils.d.ts.map +1 -1
- package/dist/reporter.cjs +474 -156
- package/dist/reporter.d.ts +13 -1
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +224 -141
- package/dist/schemas.d.ts +217 -22
- package/dist/schemas.d.ts.map +1 -1
- package/dist/server/app.d.ts +6 -0
- package/dist/server/app.d.ts.map +1 -1
- package/dist/server/report-watch.d.ts +9 -0
- package/dist/server/report-watch.d.ts.map +1 -0
- package/dist/server/routes.d.ts +7 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server.cjs +520 -82
- 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 +9 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-IEYNAB6M.js +0 -263
package/dist/reporter.cjs
CHANGED
|
@@ -34,8 +34,9 @@ __export(reporter_exports, {
|
|
|
34
34
|
default: () => reporter_default
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(reporter_exports);
|
|
37
|
+
var import_fs = require("fs");
|
|
37
38
|
var import_promises2 = require("fs/promises");
|
|
38
|
-
var
|
|
39
|
+
var import_path3 = require("path");
|
|
39
40
|
var import_p_limit = __toESM(require("p-limit"), 1);
|
|
40
41
|
|
|
41
42
|
// src/report-artifact.ts
|
|
@@ -47,6 +48,36 @@ var import_url = require("url");
|
|
|
47
48
|
function normalizeScreenshotsBaseUrl(baseUrl) {
|
|
48
49
|
return baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
|
|
49
50
|
}
|
|
51
|
+
function classifyImage(image) {
|
|
52
|
+
if (image.actual !== void 0 || image.diff !== void 0) {
|
|
53
|
+
return "comparison";
|
|
54
|
+
}
|
|
55
|
+
if (image.expect !== void 0) {
|
|
56
|
+
return "baseline-only";
|
|
57
|
+
}
|
|
58
|
+
return "declared-only";
|
|
59
|
+
}
|
|
60
|
+
function withImageSource(image) {
|
|
61
|
+
return {
|
|
62
|
+
...image,
|
|
63
|
+
source: classifyImage(image)
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function getDeclaredVisualNames(visualNames, visualDeclarations) {
|
|
67
|
+
return visualDeclarations?.map(({ visualName }) => visualName) ?? visualNames;
|
|
68
|
+
}
|
|
69
|
+
function copyVisualDeclarations(visualDeclarations) {
|
|
70
|
+
return visualDeclarations?.map((visualDeclaration) => ({ ...visualDeclaration }));
|
|
71
|
+
}
|
|
72
|
+
function mergeDeclaredImages(images, visualNames) {
|
|
73
|
+
const names = /* @__PURE__ */ new Set([...Object.keys(images), ...visualNames]);
|
|
74
|
+
return Object.fromEntries(
|
|
75
|
+
Array.from(names, (name) => {
|
|
76
|
+
const current = images[name] ?? {};
|
|
77
|
+
return [name, withImageSource(current)];
|
|
78
|
+
})
|
|
79
|
+
);
|
|
80
|
+
}
|
|
50
81
|
function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/") {
|
|
51
82
|
const images = {};
|
|
52
83
|
const baseUrl = normalizeScreenshotsBaseUrl(screenshotsBaseUrl);
|
|
@@ -57,7 +88,7 @@ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/")
|
|
|
57
88
|
const baseName = match[1];
|
|
58
89
|
const role = match[2];
|
|
59
90
|
if (baseName === null || baseName === void 0 || role === null || role === void 0) continue;
|
|
60
|
-
images[baseName] ??= {
|
|
91
|
+
images[baseName] ??= {};
|
|
61
92
|
const url = `${baseUrl}${attachment.path}`;
|
|
62
93
|
const img = images[baseName];
|
|
63
94
|
if (img !== null && img !== void 0) {
|
|
@@ -68,10 +99,11 @@ function attachmentsToImages(attachments, screenshotsBaseUrl = "/screenshots/")
|
|
|
68
99
|
}
|
|
69
100
|
for (const key of Object.keys(images)) {
|
|
70
101
|
const img = images[key];
|
|
71
|
-
if (img?.actual !==
|
|
102
|
+
if (img?.actual !== void 0 && img?.expect !== void 0 && img?.diff === void 0) {
|
|
72
103
|
delete img.expect;
|
|
104
|
+
}
|
|
73
105
|
}
|
|
74
|
-
return images;
|
|
106
|
+
return mergeDeclaredImages(images, []);
|
|
75
107
|
}
|
|
76
108
|
function mapStatus(status) {
|
|
77
109
|
switch (status) {
|
|
@@ -87,17 +119,39 @@ function mapStatus(status) {
|
|
|
87
119
|
}
|
|
88
120
|
|
|
89
121
|
// src/report-state.ts
|
|
90
|
-
function
|
|
91
|
-
return
|
|
92
|
-
|
|
93
|
-
|
|
122
|
+
function isCurrentArtifact(image) {
|
|
123
|
+
return image?.actual !== void 0 || image?.expect !== void 0 || image?.diff !== void 0;
|
|
124
|
+
}
|
|
125
|
+
function isReusablePassingImage(image) {
|
|
126
|
+
const source = image.source ?? classifyImage(image);
|
|
127
|
+
if (source === "comparison") {
|
|
128
|
+
return image.actual !== void 0 && image.diff === void 0;
|
|
129
|
+
}
|
|
130
|
+
return source === "baseline-only";
|
|
131
|
+
}
|
|
132
|
+
function hasReusablePassingImages(images) {
|
|
133
|
+
return Object.values(images).some((img) => img !== null && img !== void 0 && isReusablePassingImage(img));
|
|
94
134
|
}
|
|
95
135
|
function preservePreviousPassingImages(test, status, images) {
|
|
96
|
-
if (status !== "passed"
|
|
136
|
+
if (status !== "passed") {
|
|
97
137
|
return images;
|
|
98
138
|
}
|
|
99
139
|
const previousImages = test.results?.[0]?.images ?? {};
|
|
100
|
-
|
|
140
|
+
if (!hasReusablePassingImages(previousImages)) {
|
|
141
|
+
return images;
|
|
142
|
+
}
|
|
143
|
+
return Object.entries(previousImages).reduce((currentImages, [name, previousImage]) => {
|
|
144
|
+
if (!Object.hasOwn(currentImages, name) || previousImage === void 0 || !isReusablePassingImage(previousImage) || isCurrentArtifact(currentImages[name])) {
|
|
145
|
+
return currentImages;
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
...currentImages,
|
|
149
|
+
[name]: {
|
|
150
|
+
...previousImage,
|
|
151
|
+
source: previousImage.source ?? classifyImage(previousImage)
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}, images);
|
|
101
155
|
}
|
|
102
156
|
function countDiffImages(images) {
|
|
103
157
|
return Object.values(images).filter((img) => img?.diff !== null && img?.diff !== void 0).length;
|
|
@@ -133,10 +187,15 @@ function applyTestEndEvent(state, data, options = {}) {
|
|
|
133
187
|
return null;
|
|
134
188
|
}
|
|
135
189
|
test.status = mapStatus(data.status);
|
|
190
|
+
const resultStatus = data.status === "passed" ? "success" : data.status === "failed" ? "failed" : "pending";
|
|
191
|
+
const visualDeclarations = copyVisualDeclarations(data.visualDeclarations);
|
|
136
192
|
const images = preservePreviousPassingImages(
|
|
137
193
|
test,
|
|
138
194
|
data.status,
|
|
139
|
-
|
|
195
|
+
mergeDeclaredImages(
|
|
196
|
+
attachmentsToImages(data.attachments, options.screenshotsBaseUrl),
|
|
197
|
+
getDeclaredVisualNames(data.visualNames, visualDeclarations)
|
|
198
|
+
)
|
|
140
199
|
);
|
|
141
200
|
const diffCount = countDiffImages(images);
|
|
142
201
|
const hasDiffs = diffCount > 0;
|
|
@@ -145,9 +204,10 @@ function applyTestEndEvent(state, data, options = {}) {
|
|
|
145
204
|
}
|
|
146
205
|
test.results = [
|
|
147
206
|
{
|
|
148
|
-
status:
|
|
207
|
+
status: resultStatus,
|
|
149
208
|
retries: 0,
|
|
150
209
|
images,
|
|
210
|
+
visualDeclarations,
|
|
151
211
|
error: data.error,
|
|
152
212
|
duration: data.duration
|
|
153
213
|
}
|
|
@@ -177,23 +237,40 @@ var LocationSchema = import_zod.z.object({
|
|
|
177
237
|
file: import_zod.z.string(),
|
|
178
238
|
line: import_zod.z.number()
|
|
179
239
|
});
|
|
240
|
+
var VisualSourceSchema = import_zod.z.enum(["comparison", "baseline-only", "declared-only"]);
|
|
180
241
|
var ImagesSchema = import_zod.z.object({
|
|
181
|
-
actual: import_zod.z.string(),
|
|
242
|
+
actual: import_zod.z.string().optional(),
|
|
182
243
|
expect: import_zod.z.string().optional(),
|
|
183
244
|
diff: import_zod.z.string().optional(),
|
|
184
|
-
error: import_zod.z.string().optional()
|
|
245
|
+
error: import_zod.z.string().optional(),
|
|
246
|
+
source: VisualSourceSchema.optional()
|
|
185
247
|
});
|
|
248
|
+
var ScreenshotDeclarationSchema = import_zod.z.discriminatedUnion("kind", [
|
|
249
|
+
import_zod.z.object({
|
|
250
|
+
visualName: import_zod.z.string(),
|
|
251
|
+
kind: import_zod.z.literal("named"),
|
|
252
|
+
declaredName: import_zod.z.string(),
|
|
253
|
+
snapshotBaseName: import_zod.z.string(),
|
|
254
|
+
occurrenceIndex: import_zod.z.number()
|
|
255
|
+
}),
|
|
256
|
+
import_zod.z.object({
|
|
257
|
+
visualName: import_zod.z.string(),
|
|
258
|
+
kind: import_zod.z.literal("unnamed"),
|
|
259
|
+
occurrenceIndex: import_zod.z.number()
|
|
260
|
+
})
|
|
261
|
+
]);
|
|
186
262
|
var AttachmentSchema = import_zod.z.object({
|
|
187
263
|
name: import_zod.z.string(),
|
|
188
264
|
path: import_zod.z.string(),
|
|
189
265
|
contentType: import_zod.z.string()
|
|
190
266
|
});
|
|
191
267
|
var TestStatusSchema = import_zod.z.enum(["unknown", "pending", "running", "failed", "approved", "success", "retrying"]);
|
|
268
|
+
var TestResultStatusSchema = import_zod.z.enum(["failed", "success", "pending"]);
|
|
192
269
|
var TestResultSchema = import_zod.z.object({
|
|
193
|
-
status:
|
|
270
|
+
status: TestResultStatusSchema,
|
|
194
271
|
retries: import_zod.z.number(),
|
|
195
|
-
// eslint-disable-next-line typescript/no-unsafe-type-assertion
|
|
196
272
|
images: import_zod.z.record(import_zod.z.string(), ImagesSchema).optional(),
|
|
273
|
+
visualDeclarations: import_zod.z.array(ScreenshotDeclarationSchema).optional(),
|
|
197
274
|
error: import_zod.z.string().optional(),
|
|
198
275
|
duration: import_zod.z.number().optional()
|
|
199
276
|
});
|
|
@@ -221,7 +298,6 @@ var CrvyRprtrSuiteSchema = import_zod.z.lazy(
|
|
|
221
298
|
opened: import_zod.z.boolean(),
|
|
222
299
|
checked: import_zod.z.boolean(),
|
|
223
300
|
indeterminate: import_zod.z.boolean(),
|
|
224
|
-
// eslint-disable-next-line typescript/no-unsafe-type-assertion
|
|
225
301
|
children: import_zod.z.record(import_zod.z.string(), import_zod.z.union([CrvyRprtrSuiteSchema, CrvyRprtrTestSchema])).optional()
|
|
226
302
|
})
|
|
227
303
|
);
|
|
@@ -240,6 +316,11 @@ var TestEndDataSchema = import_zod.z.object({
|
|
|
240
316
|
id: import_zod.z.string(),
|
|
241
317
|
status: import_zod.z.enum(["passed", "failed", "skipped"]),
|
|
242
318
|
attachments: import_zod.z.array(AttachmentSchema),
|
|
319
|
+
visualNames: import_zod.z.array(import_zod.z.string()).default([]),
|
|
320
|
+
visualDeclarations: import_zod.z.preprocess(
|
|
321
|
+
(value) => value === null ? void 0 : value,
|
|
322
|
+
import_zod.z.array(ScreenshotDeclarationSchema).optional()
|
|
323
|
+
),
|
|
243
324
|
error: import_zod.z.string().optional(),
|
|
244
325
|
duration: import_zod.z.number().optional()
|
|
245
326
|
});
|
|
@@ -412,18 +493,244 @@ async function writeReportArtifact(options) {
|
|
|
412
493
|
}
|
|
413
494
|
|
|
414
495
|
// src/reporter-utils.ts
|
|
415
|
-
|
|
416
|
-
|
|
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
|
+
};
|
|
417
558
|
for (const step of steps) {
|
|
418
|
-
|
|
419
|
-
if (match?.[1] !== void 0 && match[1] !== "") names.push(match[1]);
|
|
420
|
-
if (step.steps.length) names.push(...extractScreenshotNames(step.steps));
|
|
559
|
+
visitStep(step, state);
|
|
421
560
|
}
|
|
422
|
-
return
|
|
561
|
+
return state.declarations;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// src/snapshot-path-resolver.ts
|
|
565
|
+
var import_crypto = require("crypto");
|
|
566
|
+
var import_path2 = require("path");
|
|
567
|
+
var DEFAULT_SCREENSHOT_TEMPLATE = "{snapshotDir}/{testFileDir}/{testFileName}-snapshots/{arg}{-projectName}{-snapshotSuffix}{ext}";
|
|
568
|
+
var WINDOWS_FILESYSTEM_FRIENDLY_LENGTH = 60;
|
|
569
|
+
function isUnsafeFilePathCharacter(character) {
|
|
570
|
+
const codePoint = character.codePointAt(0);
|
|
571
|
+
return codePoint !== void 0 && (codePoint <= 44 || codePoint >= 46 && codePoint <= 47 || codePoint >= 58 && codePoint <= 64 || codePoint >= 91 && codePoint <= 96 || codePoint >= 123 && codePoint <= 127);
|
|
572
|
+
}
|
|
573
|
+
function sanitizeForFilePath(value) {
|
|
574
|
+
return Array.from(value).reduce(
|
|
575
|
+
(state, character) => {
|
|
576
|
+
const unsafeCharacter = isUnsafeFilePathCharacter(character);
|
|
577
|
+
return unsafeCharacter ? state.previousCharacterWasUnsafe ? state : {
|
|
578
|
+
value: `${state.value}-`,
|
|
579
|
+
previousCharacterWasUnsafe: true
|
|
580
|
+
} : {
|
|
581
|
+
value: `${state.value}${character}`,
|
|
582
|
+
previousCharacterWasUnsafe: false
|
|
583
|
+
};
|
|
584
|
+
},
|
|
585
|
+
{
|
|
586
|
+
value: "",
|
|
587
|
+
previousCharacterWasUnsafe: false
|
|
588
|
+
}
|
|
589
|
+
).value;
|
|
590
|
+
}
|
|
591
|
+
function trimLongString(value, length = WINDOWS_FILESYSTEM_FRIENDLY_LENGTH) {
|
|
592
|
+
if (value.length <= length) {
|
|
593
|
+
return value;
|
|
594
|
+
}
|
|
595
|
+
const hash = (0, import_crypto.createHash)("sha1").update(value).digest("hex");
|
|
596
|
+
const middle = `-${hash.slice(0, 5)}-`;
|
|
597
|
+
const start = Math.floor((length - middle.length) / 2);
|
|
598
|
+
const end = length - middle.length - start;
|
|
599
|
+
return value.slice(0, start) + middle + value.slice(-end);
|
|
600
|
+
}
|
|
601
|
+
function sanitizeFilePathBeforeExtension(filePath, extension = (0, import_path2.extname)(filePath)) {
|
|
602
|
+
const base = filePath.slice(0, filePath.length - extension.length);
|
|
603
|
+
return sanitizeForFilePath(base) + extension;
|
|
604
|
+
}
|
|
605
|
+
function addSuffixToFilePath(filePath, suffix) {
|
|
606
|
+
const extension = (0, import_path2.extname)(filePath);
|
|
607
|
+
return filePath.slice(0, filePath.length - extension.length) + suffix + extension;
|
|
608
|
+
}
|
|
609
|
+
function normalizedSnapshotDir(config) {
|
|
610
|
+
return (0, import_path2.resolve)(config.configDir, config.snapshotDir);
|
|
611
|
+
}
|
|
612
|
+
function templateValue(template, token, value) {
|
|
613
|
+
return template.replace(
|
|
614
|
+
new RegExp(`\\{(.)?${token}\\}`, "g"),
|
|
615
|
+
(_, prefix) => value === "" ? "" : `${prefix ?? ""}${value}`
|
|
616
|
+
);
|
|
617
|
+
}
|
|
618
|
+
function applyTemplate(input, nameArgument, extension) {
|
|
619
|
+
const template = input.config.toHaveScreenshotPathTemplate ?? input.config.snapshotPathTemplate ?? DEFAULT_SCREENSHOT_TEMPLATE;
|
|
620
|
+
const relativeTestFilePath = (0, import_path2.relative)(input.config.testDir, input.testFile);
|
|
621
|
+
const parsed = (0, import_path2.parse)(relativeTestFilePath);
|
|
622
|
+
const tokens = [
|
|
623
|
+
["testDir", input.config.testDir],
|
|
624
|
+
["snapshotDir", normalizedSnapshotDir(input.config)],
|
|
625
|
+
["snapshotSuffix", input.config.snapshotSuffix],
|
|
626
|
+
["testFileDir", parsed.dir],
|
|
627
|
+
["platform", process.platform],
|
|
628
|
+
["projectName", sanitizeForFilePath(input.config.projectName)],
|
|
629
|
+
["testName", ""],
|
|
630
|
+
["testFileName", parsed.base],
|
|
631
|
+
["testFilePath", relativeTestFilePath],
|
|
632
|
+
["arg", nameArgument],
|
|
633
|
+
["ext", extension]
|
|
634
|
+
];
|
|
635
|
+
const snapshotPath = tokens.reduce(
|
|
636
|
+
(currentTemplate, [token, value]) => templateValue(currentTemplate, token, value),
|
|
637
|
+
template
|
|
638
|
+
);
|
|
639
|
+
return (0, import_path2.resolve)(input.config.configDir, snapshotPath);
|
|
640
|
+
}
|
|
641
|
+
function removeExtension(filePath, extension = (0, import_path2.extname)(filePath)) {
|
|
642
|
+
return filePath.slice(0, filePath.length - extension.length);
|
|
643
|
+
}
|
|
644
|
+
function snapshotNameParts(declaredName) {
|
|
645
|
+
const extension = (0, import_path2.extname)(declaredName) || ".png";
|
|
646
|
+
return {
|
|
647
|
+
extension,
|
|
648
|
+
filePath: (0, import_path2.extname)(declaredName) === "" ? `${declaredName}${extension}` : declaredName
|
|
649
|
+
};
|
|
650
|
+
}
|
|
651
|
+
function filePathForOccurrence(filePath, occurrenceIndex) {
|
|
652
|
+
return occurrenceIndex === 1 ? filePath : addSuffixToFilePath(filePath, `-${occurrenceIndex - 1}`);
|
|
653
|
+
}
|
|
654
|
+
function createResolvedBaselineTarget(input, declaration, nameArgument, extension) {
|
|
655
|
+
return {
|
|
656
|
+
visualName: declaration.visualName,
|
|
657
|
+
attachmentBaseName: declaration.visualName,
|
|
658
|
+
artifactBaseName: sanitizeForFilePath(declaration.visualName),
|
|
659
|
+
snapshotPath: applyTemplate(input, nameArgument, extension)
|
|
660
|
+
};
|
|
661
|
+
}
|
|
662
|
+
function resolveStringCallTarget(input, declaration) {
|
|
663
|
+
const { extension, filePath } = snapshotNameParts(declaration.declaredName);
|
|
664
|
+
const occurrenceFilePath = filePathForOccurrence(filePath, declaration.occurrenceIndex);
|
|
665
|
+
const sanitizedNameWithExtension = sanitizeFilePathBeforeExtension(occurrenceFilePath, extension);
|
|
666
|
+
const nameArgument = removeExtension(sanitizedNameWithExtension, extension);
|
|
667
|
+
return createResolvedBaselineTarget(input, declaration, nameArgument, extension);
|
|
668
|
+
}
|
|
669
|
+
function resolveArrayCallTarget(input, declaration) {
|
|
670
|
+
const { extension, filePath } = snapshotNameParts(declaration.declaredName);
|
|
671
|
+
const occurrenceFilePath = filePathForOccurrence(filePath, declaration.occurrenceIndex);
|
|
672
|
+
const nameArgument = (0, import_path2.join)((0, import_path2.dirname)(occurrenceFilePath), (0, import_path2.basename)(occurrenceFilePath, extension));
|
|
673
|
+
return createResolvedBaselineTarget(input, declaration, nameArgument, extension);
|
|
674
|
+
}
|
|
675
|
+
function resolveNamedTarget(input, declaration) {
|
|
676
|
+
if (!declaration.declaredName.includes("/")) {
|
|
677
|
+
return resolveStringCallTarget(input, declaration);
|
|
678
|
+
}
|
|
679
|
+
const stringCallTarget = resolveStringCallTarget(input, declaration);
|
|
680
|
+
const arrayCallTarget = resolveArrayCallTarget(input, declaration);
|
|
681
|
+
if (stringCallTarget.snapshotPath === arrayCallTarget.snapshotPath) {
|
|
682
|
+
return stringCallTarget;
|
|
683
|
+
}
|
|
684
|
+
if (input.snapshotPathExists === void 0) {
|
|
685
|
+
return void 0;
|
|
686
|
+
}
|
|
687
|
+
const stringCallTargetExists = input.snapshotPathExists(stringCallTarget.snapshotPath);
|
|
688
|
+
const arrayCallTargetExists = input.snapshotPathExists(arrayCallTarget.snapshotPath);
|
|
689
|
+
if (stringCallTargetExists === arrayCallTargetExists) {
|
|
690
|
+
return void 0;
|
|
691
|
+
}
|
|
692
|
+
return stringCallTargetExists ? stringCallTarget : arrayCallTarget;
|
|
693
|
+
}
|
|
694
|
+
function reporterTitlesWithoutProjectAndFile(reporterTitlePath) {
|
|
695
|
+
return reporterTitlePath.slice(3).filter((part) => part !== "");
|
|
696
|
+
}
|
|
697
|
+
function anonymousName(reporterTitlePath, occurrenceIndex) {
|
|
698
|
+
const rawAnonymousName = `${reporterTitlesWithoutProjectAndFile(reporterTitlePath).join(" ")} ${occurrenceIndex}.png`;
|
|
699
|
+
return sanitizeFilePathBeforeExtension(trimLongString(rawAnonymousName), ".png");
|
|
700
|
+
}
|
|
701
|
+
function resolveTarget(input, declaration) {
|
|
702
|
+
switch (declaration.kind) {
|
|
703
|
+
case "named":
|
|
704
|
+
return typeof declaration.declaredName === "string" && declaration.declaredName !== "" ? resolveNamedTarget(input, declaration) : void 0;
|
|
705
|
+
case "unnamed": {
|
|
706
|
+
const anonymousFileName = anonymousName(input.reporterTitlePath, declaration.occurrenceIndex);
|
|
707
|
+
const extension = ".png";
|
|
708
|
+
const nameArgument = (0, import_path2.join)((0, import_path2.dirname)(anonymousFileName), (0, import_path2.basename)(anonymousFileName, extension));
|
|
709
|
+
return createResolvedBaselineTarget(input, declaration, nameArgument, extension);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
function resolveBaselineTargets(input) {
|
|
714
|
+
return input.declarations.flatMap((declaration) => {
|
|
715
|
+
const resolvedTarget = resolveTarget(input, declaration);
|
|
716
|
+
return resolvedTarget === void 0 ? [] : [resolvedTarget];
|
|
717
|
+
});
|
|
423
718
|
}
|
|
424
719
|
|
|
425
720
|
// src/reporter.ts
|
|
721
|
+
var CURRENT_DIRECTORY_ARTIFACT_SEGMENT = "+dot+";
|
|
722
|
+
var PARENT_DIRECTORY_ARTIFACT_SEGMENT = "+dotdot+";
|
|
426
723
|
var MAX_CONCURRENT_FILE_OPS = 5;
|
|
724
|
+
var SAFE_ARTIFACT_CHARACTER = /^[A-Za-z0-9._-]$/;
|
|
725
|
+
var encodeArtifactPathSegment = (segment) => {
|
|
726
|
+
if (segment === ".") return CURRENT_DIRECTORY_ARTIFACT_SEGMENT;
|
|
727
|
+
if (segment === "..") return PARENT_DIRECTORY_ARTIFACT_SEGMENT;
|
|
728
|
+
return Array.from(
|
|
729
|
+
segment,
|
|
730
|
+
(character) => SAFE_ARTIFACT_CHARACTER.test(character) ? character : encodeURIComponent(character)
|
|
731
|
+
).join("");
|
|
732
|
+
};
|
|
733
|
+
var safeArtifactPath = (name) => name.split("/").map(encodeArtifactPathSegment).join("/");
|
|
427
734
|
var CrvyRprtr = class {
|
|
428
735
|
ws = null;
|
|
429
736
|
serverUrl;
|
|
@@ -432,6 +739,11 @@ var CrvyRprtr = class {
|
|
|
432
739
|
workerIndex;
|
|
433
740
|
offlineReportPath;
|
|
434
741
|
reportHtmlPath;
|
|
742
|
+
configDir = process.cwd();
|
|
743
|
+
testMetadata = /* @__PURE__ */ new Map();
|
|
744
|
+
playwrightSnapshotDir;
|
|
745
|
+
playwrightSnapshotPathTemplate;
|
|
746
|
+
playwrightToHaveScreenshotPathTemplate;
|
|
435
747
|
isOfflineMode = false;
|
|
436
748
|
hadOfflineMode = false;
|
|
437
749
|
runEvents = [];
|
|
@@ -441,8 +753,12 @@ var CrvyRprtr = class {
|
|
|
441
753
|
this.workerIndex = parseInt(process.env.TEST_WORKER_INDEX ?? "0", 10) || 0;
|
|
442
754
|
this.offlineReportPath = options.offlineReportPath ?? `./crvy-rprtr-${this.workerIndex}.json`;
|
|
443
755
|
this.reportHtmlPath = options.reportHtmlPath ?? "./crvy-rprtr.html";
|
|
756
|
+
this.playwrightSnapshotDir = options.playwrightSnapshotDir;
|
|
757
|
+
this.playwrightSnapshotPathTemplate = options.playwrightSnapshotPathTemplate;
|
|
758
|
+
this.playwrightToHaveScreenshotPathTemplate = options.playwrightToHaveScreenshotPathTemplate;
|
|
444
759
|
}
|
|
445
760
|
async onBegin(config, suite) {
|
|
761
|
+
this.configDir = config.configFile === void 0 ? config.rootDir : (0, import_path3.dirname)(config.configFile);
|
|
446
762
|
console.log(`[CrvyRprtr] Starting run with ${suite.allTests().length} tests`);
|
|
447
763
|
await (0, import_promises2.mkdir)(this.screenshotDir, { recursive: true });
|
|
448
764
|
this.connect();
|
|
@@ -459,7 +775,7 @@ var CrvyRprtr = class {
|
|
|
459
775
|
this.ws.onopen = () => {
|
|
460
776
|
console.log("[CrvyRprtr] Connected to Crvy Rprtr server");
|
|
461
777
|
this.isOfflineMode = false;
|
|
462
|
-
for (const
|
|
778
|
+
for (const message of this.queue) this.ws.send(message);
|
|
463
779
|
this.queue = [];
|
|
464
780
|
};
|
|
465
781
|
this.ws.onerror = (error) => {
|
|
@@ -470,140 +786,158 @@ var CrvyRprtr = class {
|
|
|
470
786
|
console.log("[CrvyRprtr] Disconnected from Crvy Rprtr server");
|
|
471
787
|
this.enableOfflineMode();
|
|
472
788
|
};
|
|
473
|
-
} catch (
|
|
474
|
-
console.error("[CrvyRprtr] Failed to connect:",
|
|
789
|
+
} catch (error) {
|
|
790
|
+
console.error("[CrvyRprtr] Failed to connect:", error);
|
|
475
791
|
this.enableOfflineMode();
|
|
476
792
|
}
|
|
477
793
|
}
|
|
478
794
|
enableOfflineMode() {
|
|
479
|
-
if (
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
console.log("[CrvyRprtr] Offline mode enabled - events will be queued to file");
|
|
483
|
-
}
|
|
795
|
+
if (this.isOfflineMode) return;
|
|
796
|
+
this.isOfflineMode = this.hadOfflineMode = true;
|
|
797
|
+
console.log("[CrvyRprtr] Offline mode enabled - events will be queued to file");
|
|
484
798
|
}
|
|
485
|
-
|
|
799
|
+
describeTitlePath(test) {
|
|
486
800
|
const titlePath = [];
|
|
487
|
-
let suite = test.parent;
|
|
488
|
-
while (suite && suite.type === "describe") {
|
|
801
|
+
for (let suite = test.parent; suite?.type === "describe"; suite = suite.parent)
|
|
489
802
|
titlePath.unshift(suite.title);
|
|
490
|
-
|
|
491
|
-
|
|
803
|
+
return titlePath;
|
|
804
|
+
}
|
|
805
|
+
reporterTitlePath(test) {
|
|
806
|
+
return typeof test.titlePath === "function" ? test.titlePath() : ["", test.parent.project()?.name ?? "chromium", test.location.file, ...this.describeTitlePath(test), test.title];
|
|
807
|
+
}
|
|
808
|
+
onTestBegin(test) {
|
|
809
|
+
this.testMetadata.set(test.id, { reporterTitlePath: this.reporterTitlePath(test) });
|
|
492
810
|
this.send({
|
|
493
811
|
type: "test-begin",
|
|
494
812
|
data: {
|
|
495
813
|
id: test.id,
|
|
496
814
|
title: test.title,
|
|
497
|
-
titlePath,
|
|
815
|
+
titlePath: this.describeTitlePath(test),
|
|
498
816
|
browser: test.parent.project()?.name ?? "chromium",
|
|
499
|
-
location: {
|
|
500
|
-
file: test.location.file,
|
|
501
|
-
line: test.location.line
|
|
502
|
-
}
|
|
817
|
+
location: { file: test.location.file, line: test.location.line }
|
|
503
818
|
}
|
|
504
819
|
});
|
|
505
820
|
}
|
|
506
821
|
async onTestEnd(test, result) {
|
|
822
|
+
const screenshotDeclarations = extractScreenshotDeclarations(result.steps);
|
|
507
823
|
const savedAttachments = await this.saveAttachments(test.id, result);
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
824
|
+
try {
|
|
825
|
+
await this.copySnapshotBaselines(test, result.status, screenshotDeclarations, savedAttachments);
|
|
826
|
+
this.send({
|
|
827
|
+
type: "test-end",
|
|
828
|
+
data: {
|
|
829
|
+
id: test.id,
|
|
830
|
+
title: test.title,
|
|
831
|
+
status: result.status,
|
|
832
|
+
attachments: savedAttachments,
|
|
833
|
+
visualNames: screenshotDeclarations.map(({ visualName }) => visualName),
|
|
834
|
+
visualDeclarations: screenshotDeclarations,
|
|
835
|
+
error: result.errors.length > 0 ? result.errors[0]?.message : void 0,
|
|
836
|
+
duration: result.duration
|
|
837
|
+
}
|
|
838
|
+
});
|
|
839
|
+
} finally {
|
|
840
|
+
this.testMetadata.delete(test.id);
|
|
841
|
+
}
|
|
520
842
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
const
|
|
524
|
-
if (
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
843
|
+
baselineInput(test, shots) {
|
|
844
|
+
const project = test.parent.project();
|
|
845
|
+
const snapshotDir = this.playwrightSnapshotDir ?? project?.snapshotDir;
|
|
846
|
+
if (project === void 0 || typeof project.testDir !== "string" || typeof snapshotDir !== "string") return null;
|
|
847
|
+
return {
|
|
848
|
+
testFile: test.location.file,
|
|
849
|
+
reporterTitlePath: this.testMetadata.get(test.id)?.reporterTitlePath ?? this.reporterTitlePath(test),
|
|
850
|
+
declarations: shots,
|
|
851
|
+
config: {
|
|
852
|
+
configDir: this.configDir,
|
|
853
|
+
testDir: project.testDir,
|
|
854
|
+
snapshotDir,
|
|
855
|
+
projectName: project.name,
|
|
856
|
+
snapshotSuffix: process.platform,
|
|
857
|
+
snapshotPathTemplate: this.playwrightSnapshotPathTemplate,
|
|
858
|
+
toHaveScreenshotPathTemplate: this.playwrightToHaveScreenshotPathTemplate
|
|
859
|
+
},
|
|
860
|
+
snapshotPathExists: import_fs.existsSync
|
|
861
|
+
};
|
|
862
|
+
}
|
|
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
|
+
async copySnapshotBaselines(test, status, screenshotDeclarations, savedAttachments) {
|
|
876
|
+
if (status !== "passed" || screenshotDeclarations.length === 0) return;
|
|
877
|
+
const input = this.baselineInput(test, screenshotDeclarations);
|
|
878
|
+
if (input === null) return;
|
|
879
|
+
const targets = resolveBaselineTargets(input);
|
|
880
|
+
if (targets.length === 0) return;
|
|
881
|
+
const safeTestId = this.sanitizeId(test.id);
|
|
882
|
+
const testScreenshotDir = (0, import_path3.join)(this.screenshotDir, safeTestId);
|
|
528
883
|
const limit = (0, import_p_limit.default)(MAX_CONCURRENT_FILE_OPS);
|
|
529
|
-
|
|
530
|
-
(
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
const destName = `${baseName}-expected`;
|
|
534
|
-
const destPath = (0, import_path2.join)(testScreenshotDir, destName);
|
|
535
|
-
try {
|
|
536
|
-
await (0, import_promises2.mkdir)(testScreenshotDir, { recursive: true });
|
|
537
|
-
await (0, import_promises2.copyFile)(snapshotPath, destPath);
|
|
538
|
-
savedAttachments.push({
|
|
539
|
-
name: destName,
|
|
540
|
-
path: `${this.sanitizeId(test.id)}/${destName}`,
|
|
541
|
-
contentType: "image/png"
|
|
542
|
-
});
|
|
543
|
-
console.log(`[CrvyRprtr] Attached baseline: ${snapshotPath}`);
|
|
544
|
-
} catch {
|
|
545
|
-
}
|
|
546
|
-
})
|
|
884
|
+
await Promise.all(
|
|
885
|
+
targets.map(
|
|
886
|
+
(target) => limit(() => this.copyResolvedBaseline(safeTestId, testScreenshotDir, target, savedAttachments))
|
|
887
|
+
)
|
|
547
888
|
);
|
|
548
|
-
await Promise.all(copyPromises);
|
|
549
889
|
}
|
|
550
890
|
async saveAttachments(testId, result) {
|
|
551
891
|
const savedAttachments = [];
|
|
552
|
-
const
|
|
892
|
+
const safeTestId = this.sanitizeId(testId);
|
|
893
|
+
const testScreenshotDir = (0, import_path3.join)(this.screenshotDir, safeTestId);
|
|
553
894
|
const limit = (0, import_p_limit.default)(MAX_CONCURRENT_FILE_OPS);
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
}
|
|
579
|
-
|
|
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
|
+
)
|
|
580
921
|
);
|
|
581
|
-
await Promise.all(attachmentPromises);
|
|
582
922
|
return savedAttachments;
|
|
583
923
|
}
|
|
584
924
|
sanitizeId(id) {
|
|
585
925
|
return id.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
586
926
|
}
|
|
587
927
|
async writeOfflineReport() {
|
|
588
|
-
if (this.runEvents.length === 0)
|
|
589
|
-
|
|
590
|
-
return;
|
|
591
|
-
}
|
|
928
|
+
if (this.runEvents.length === 0) console.log("[CrvyRprtr] No offline events to write");
|
|
929
|
+
if (this.runEvents.length === 0) return;
|
|
592
930
|
try {
|
|
593
931
|
const report = {
|
|
594
932
|
version: 1,
|
|
595
933
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
596
934
|
workers: this.workerIndex + 1,
|
|
597
|
-
events: this.runEvents.map((
|
|
598
|
-
...e,
|
|
599
|
-
timestamp: Date.now(),
|
|
600
|
-
workerIndex: this.workerIndex
|
|
601
|
-
}))
|
|
935
|
+
events: this.runEvents.map((event) => ({ ...event, timestamp: Date.now(), workerIndex: this.workerIndex }))
|
|
602
936
|
};
|
|
603
937
|
await (0, import_promises2.writeFile)(this.offlineReportPath, JSON.stringify(report, null, 2));
|
|
604
938
|
console.log(`[CrvyRprtr] Wrote offline report: ${this.offlineReportPath}`);
|
|
605
|
-
} catch (
|
|
606
|
-
console.error("[CrvyRprtr] Failed to write offline report:",
|
|
939
|
+
} catch (error) {
|
|
940
|
+
console.error("[CrvyRprtr] Failed to write offline report:", error);
|
|
607
941
|
}
|
|
608
942
|
}
|
|
609
943
|
async writeStaticArtifact() {
|
|
@@ -614,51 +948,35 @@ var CrvyRprtr = class {
|
|
|
614
948
|
reportHtmlPath: this.reportHtmlPath
|
|
615
949
|
});
|
|
616
950
|
console.log(`[CrvyRprtr] Wrote report artifact: ${this.reportHtmlPath}`);
|
|
617
|
-
} catch (
|
|
618
|
-
console.error("[CrvyRprtr] Failed to write report artifact:",
|
|
951
|
+
} catch (error) {
|
|
952
|
+
console.error("[CrvyRprtr] Failed to write report artifact:", error);
|
|
619
953
|
}
|
|
620
954
|
}
|
|
621
955
|
async onEnd(result) {
|
|
622
|
-
this.send({
|
|
623
|
-
type: "run-end",
|
|
624
|
-
data: {
|
|
625
|
-
status: result.status
|
|
626
|
-
}
|
|
627
|
-
});
|
|
956
|
+
this.send({ type: "run-end", data: { status: result.status } });
|
|
628
957
|
await this.writeStaticArtifact();
|
|
629
|
-
if (this.hadOfflineMode)
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
this.ws?.close();
|
|
642
|
-
resolve2();
|
|
643
|
-
}, 1e3);
|
|
644
|
-
this.ws.close();
|
|
958
|
+
if (this.hadOfflineMode) await this.writeOfflineReport();
|
|
959
|
+
await new Promise((resolve3) => {
|
|
960
|
+
if (this.ws && this.ws.readyState !== WebSocket.CLOSED) {
|
|
961
|
+
this.ws.onclose = () => {
|
|
962
|
+
resolve3();
|
|
963
|
+
};
|
|
964
|
+
setTimeout(() => {
|
|
965
|
+
this.ws?.close();
|
|
966
|
+
resolve3();
|
|
967
|
+
}, 1e3);
|
|
968
|
+
this.ws.close();
|
|
969
|
+
} else resolve3();
|
|
645
970
|
});
|
|
646
971
|
}
|
|
647
|
-
send(
|
|
648
|
-
const
|
|
649
|
-
if (
|
|
650
|
-
this.runEvents.push({
|
|
651
|
-
|
|
652
|
-
data: msgObj.data
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
const payload = JSON.stringify(msg);
|
|
972
|
+
send(message) {
|
|
973
|
+
const event = message;
|
|
974
|
+
if (event.type === "test-begin" || event.type === "test-end" || event.type === "run-end")
|
|
975
|
+
this.runEvents.push({ type: event.type, data: event.data });
|
|
976
|
+
const payload = JSON.stringify(message);
|
|
656
977
|
if (!this.isOfflineMode) {
|
|
657
|
-
if (this.ws?.readyState === WebSocket.OPEN)
|
|
658
|
-
|
|
659
|
-
} else {
|
|
660
|
-
this.queue.push(payload);
|
|
661
|
-
}
|
|
978
|
+
if (this.ws?.readyState === WebSocket.OPEN) this.ws.send(payload);
|
|
979
|
+
else this.queue.push(payload);
|
|
662
980
|
}
|
|
663
981
|
}
|
|
664
982
|
};
|