@checkly/playwright-reporter 0.1.9 → 1.0.0
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/README.md +266 -0
- package/dist/index.d.ts +340 -175
- package/dist/index.js +1856 -641
- package/package.json +16 -14
package/dist/index.js
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
// src/extensions/checkly-upload.ts
|
|
2
|
+
import * as fs4 from "fs";
|
|
3
|
+
import * as path3 from "path";
|
|
4
|
+
|
|
5
|
+
// ../utils/src/ansi.ts
|
|
6
|
+
var ansiRegex = new RegExp(
|
|
7
|
+
[
|
|
8
|
+
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
|
9
|
+
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"
|
|
10
|
+
].join("|"),
|
|
11
|
+
"g"
|
|
12
|
+
);
|
|
13
|
+
|
|
1
14
|
// ../utils/src/asset-collector.ts
|
|
2
15
|
import * as fs from "fs";
|
|
3
16
|
import * as path from "path";
|
|
@@ -234,15 +247,999 @@ var AssetCollector = class {
|
|
|
234
247
|
}
|
|
235
248
|
};
|
|
236
249
|
|
|
237
|
-
// ../utils/src/
|
|
250
|
+
// ../utils/src/console-adapter.ts
|
|
251
|
+
import { createHash } from "crypto";
|
|
252
|
+
function normalizeType(messageType) {
|
|
253
|
+
switch (messageType.toLowerCase()) {
|
|
254
|
+
case "debug":
|
|
255
|
+
return "debug";
|
|
256
|
+
case "error":
|
|
257
|
+
return "error";
|
|
258
|
+
case "info":
|
|
259
|
+
return "info";
|
|
260
|
+
case "warning":
|
|
261
|
+
case "warn":
|
|
262
|
+
return "warning";
|
|
263
|
+
default:
|
|
264
|
+
return "log";
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function generateId(time, messageType, text, url) {
|
|
268
|
+
return createHash("sha256").update(`${time}-${messageType}-${text}-${url}`).digest("hex").substring(0, 16);
|
|
269
|
+
}
|
|
270
|
+
function toConsoleMessage(event) {
|
|
271
|
+
const url = event.location?.url || "";
|
|
272
|
+
return {
|
|
273
|
+
id: generateId(event.time, event.messageType, event.text, url),
|
|
274
|
+
location: {
|
|
275
|
+
url,
|
|
276
|
+
columnNumber: event.location?.columnNumber || 0,
|
|
277
|
+
lineNumber: event.location?.lineNumber || 0
|
|
278
|
+
},
|
|
279
|
+
text: event.text || "",
|
|
280
|
+
timestamp: event.time,
|
|
281
|
+
type: normalizeType(event.messageType)
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// ../utils/src/git-info.ts
|
|
286
|
+
function getGitHubRepoInfo() {
|
|
287
|
+
const repository = process.env.GITHUB_REPOSITORY;
|
|
288
|
+
if (!repository) return void 0;
|
|
289
|
+
return {
|
|
290
|
+
repoUrl: `https://github.com/${repository}`,
|
|
291
|
+
commitId: process.env.GITHUB_SHA,
|
|
292
|
+
branchName: process.env.GITHUB_REF_NAME,
|
|
293
|
+
commitOwner: process.env.GITHUB_ACTOR,
|
|
294
|
+
commitMessage: process.env.GITHUB_EVENT_NAME
|
|
295
|
+
};
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ../utils/src/network-adapter.ts
|
|
299
|
+
import { createHash as createHash2 } from "crypto";
|
|
300
|
+
function generateId2(url, method, startedAt) {
|
|
301
|
+
return createHash2("sha256").update(`${url}-${method}-${startedAt}`).digest("hex").substring(0, 16);
|
|
302
|
+
}
|
|
303
|
+
function extractDomain(url) {
|
|
304
|
+
try {
|
|
305
|
+
return new URL(url).hostname;
|
|
306
|
+
} catch {
|
|
307
|
+
return "";
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
function headersArrayToRecord(headers) {
|
|
311
|
+
const record = {};
|
|
312
|
+
for (const { name, value } of headers) {
|
|
313
|
+
record[name.toLowerCase()] = value;
|
|
314
|
+
}
|
|
315
|
+
return record;
|
|
316
|
+
}
|
|
317
|
+
function isSuccessStatus(status) {
|
|
318
|
+
return status >= 200 && status < 400;
|
|
319
|
+
}
|
|
320
|
+
function determineResourceType(snapshot) {
|
|
321
|
+
if (snapshot._resourceType) {
|
|
322
|
+
return snapshot._resourceType;
|
|
323
|
+
}
|
|
324
|
+
if (snapshot._apiRequest) {
|
|
325
|
+
return "fetch";
|
|
326
|
+
}
|
|
327
|
+
return "other";
|
|
328
|
+
}
|
|
329
|
+
function toNetworkRequest(event) {
|
|
330
|
+
const { snapshot } = event;
|
|
331
|
+
const startedAt = new Date(snapshot.startedDateTime).getTime();
|
|
332
|
+
const time = Math.round(snapshot.time);
|
|
333
|
+
const finishedAt = startedAt + time;
|
|
334
|
+
const statusCode = snapshot.response.status;
|
|
335
|
+
const url = snapshot.request.url;
|
|
336
|
+
const method = snapshot.request.method;
|
|
337
|
+
return {
|
|
338
|
+
id: generateId2(url, method, startedAt),
|
|
339
|
+
url,
|
|
340
|
+
domain: extractDomain(url),
|
|
341
|
+
method,
|
|
342
|
+
resourceType: determineResourceType(snapshot),
|
|
343
|
+
statusCode,
|
|
344
|
+
statusText: snapshot.response.statusText || "",
|
|
345
|
+
start: startedAt,
|
|
346
|
+
startedAt,
|
|
347
|
+
finishedAt,
|
|
348
|
+
time,
|
|
349
|
+
hasFinished: true,
|
|
350
|
+
hasSucceeded: isSuccessStatus(statusCode),
|
|
351
|
+
requestHeaders: headersArrayToRecord(snapshot.request.headers || []),
|
|
352
|
+
responseHeaders: headersArrayToRecord(snapshot.response.headers || []),
|
|
353
|
+
transferBytes: snapshot.response._transferSize,
|
|
354
|
+
resourceBytes: snapshot.response.content?.size
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// ../utils/src/trace-reader.ts
|
|
238
359
|
import * as fs2 from "fs";
|
|
360
|
+
|
|
361
|
+
// ../utils/src/zip-reader.ts
|
|
362
|
+
import { promisify } from "util";
|
|
363
|
+
import * as zlib from "zlib";
|
|
364
|
+
var gunzip2 = promisify(zlib.gunzip);
|
|
365
|
+
var inflateRaw2 = promisify(zlib.inflateRaw);
|
|
366
|
+
function parseZipEntries(zipBuffer) {
|
|
367
|
+
const EOCD_SIG = 101010256;
|
|
368
|
+
let eocdOffset = -1;
|
|
369
|
+
for (let i = zipBuffer.length - 22; i >= 0; i--) {
|
|
370
|
+
if (zipBuffer.readUInt32LE(i) === EOCD_SIG) {
|
|
371
|
+
eocdOffset = i;
|
|
372
|
+
break;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
if (eocdOffset === -1) {
|
|
376
|
+
return [];
|
|
377
|
+
}
|
|
378
|
+
const cdOffset = zipBuffer.readUInt32LE(eocdOffset + 16);
|
|
379
|
+
const cdEntries = zipBuffer.readUInt16LE(eocdOffset + 10);
|
|
380
|
+
const entries = [];
|
|
381
|
+
const CD_SIG = 33639248;
|
|
382
|
+
let offset = cdOffset;
|
|
383
|
+
for (let i = 0; i < cdEntries; i++) {
|
|
384
|
+
if (zipBuffer.readUInt32LE(offset) !== CD_SIG) {
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
const compressionMethod = zipBuffer.readUInt16LE(offset + 10);
|
|
388
|
+
const compressedSize = zipBuffer.readUInt32LE(offset + 20);
|
|
389
|
+
const fileNameLength = zipBuffer.readUInt16LE(offset + 28);
|
|
390
|
+
const extraFieldLength = zipBuffer.readUInt16LE(offset + 30);
|
|
391
|
+
const commentLength = zipBuffer.readUInt16LE(offset + 32);
|
|
392
|
+
const localHeaderOffset = zipBuffer.readUInt32LE(offset + 42);
|
|
393
|
+
const fileName = zipBuffer.subarray(offset + 46, offset + 46 + fileNameLength).toString("utf-8");
|
|
394
|
+
entries.push({
|
|
395
|
+
fileName,
|
|
396
|
+
compressionMethod,
|
|
397
|
+
compressedSize,
|
|
398
|
+
localHeaderOffset
|
|
399
|
+
});
|
|
400
|
+
offset += 46 + fileNameLength + extraFieldLength + commentLength;
|
|
401
|
+
}
|
|
402
|
+
return entries;
|
|
403
|
+
}
|
|
404
|
+
async function readZipEntryContent(zipBuffer, entry) {
|
|
405
|
+
const LOCAL_SIG = 67324752;
|
|
406
|
+
if (zipBuffer.readUInt32LE(entry.localHeaderOffset) !== LOCAL_SIG) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const localFileNameLength = zipBuffer.readUInt16LE(entry.localHeaderOffset + 26);
|
|
410
|
+
const localExtraLength = zipBuffer.readUInt16LE(entry.localHeaderOffset + 28);
|
|
411
|
+
const dataOffset = entry.localHeaderOffset + 30 + localFileNameLength + localExtraLength;
|
|
412
|
+
const compressedData = zipBuffer.subarray(dataOffset, dataOffset + entry.compressedSize);
|
|
413
|
+
let buffer;
|
|
414
|
+
if (entry.compressionMethod === 0) {
|
|
415
|
+
buffer = compressedData;
|
|
416
|
+
} else if (entry.compressionMethod === 8) {
|
|
417
|
+
buffer = await inflateRaw2(compressedData);
|
|
418
|
+
} else {
|
|
419
|
+
return null;
|
|
420
|
+
}
|
|
421
|
+
if (buffer.length >= 2 && buffer[0] === 31 && buffer[1] === 139) {
|
|
422
|
+
const decompressed = await gunzip2(buffer);
|
|
423
|
+
return decompressed.toString("utf-8");
|
|
424
|
+
}
|
|
425
|
+
return buffer.toString("utf-8");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
// ../utils/src/trace-reader.ts
|
|
429
|
+
var TraceReader = class {
|
|
430
|
+
constructor(tracePath) {
|
|
431
|
+
this.tracePath = tracePath;
|
|
432
|
+
}
|
|
433
|
+
zipBuffer = null;
|
|
434
|
+
traceEntries = [];
|
|
435
|
+
async open() {
|
|
436
|
+
if (!fs2.existsSync(this.tracePath)) {
|
|
437
|
+
return false;
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
this.zipBuffer = fs2.readFileSync(this.tracePath);
|
|
441
|
+
const entries = parseZipEntries(this.zipBuffer);
|
|
442
|
+
this.traceEntries = entries.filter(
|
|
443
|
+
(e) => (/^\d+-trace\.trace$/.test(e.fileName) || /^\d+-trace\.network$/.test(e.fileName)) && !e.fileName.includes("/")
|
|
444
|
+
);
|
|
445
|
+
return this.traceEntries.length > 0;
|
|
446
|
+
} catch {
|
|
447
|
+
return false;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Extracts events matching a text filter.
|
|
452
|
+
*
|
|
453
|
+
* @param textFilter - Substring to match (e.g., '"type":"console"')
|
|
454
|
+
* @param adapter - Optional adapter to transform events
|
|
455
|
+
*/
|
|
456
|
+
async extractEvents(textFilter, adapter) {
|
|
457
|
+
if (!this.zipBuffer) {
|
|
458
|
+
throw new Error("TraceReader not opened. Call open() first.");
|
|
459
|
+
}
|
|
460
|
+
const results = [];
|
|
461
|
+
for (const traceEntry of this.traceEntries) {
|
|
462
|
+
const content = await readZipEntryContent(this.zipBuffer, traceEntry);
|
|
463
|
+
if (!content) continue;
|
|
464
|
+
for (const line of content.split("\n")) {
|
|
465
|
+
if (line.indexOf(textFilter) !== -1) {
|
|
466
|
+
try {
|
|
467
|
+
const event = JSON.parse(line);
|
|
468
|
+
results.push(adapter ? adapter(event) : event);
|
|
469
|
+
} catch {
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
if (results.length > 0 && typeof results[0].time === "number") {
|
|
475
|
+
return results.sort((a, b) => a.time - b.time);
|
|
476
|
+
}
|
|
477
|
+
return results;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Extracts all events from the trace.
|
|
481
|
+
*/
|
|
482
|
+
async extractAllEvents(adapter) {
|
|
483
|
+
if (!this.zipBuffer) {
|
|
484
|
+
throw new Error("TraceReader not opened. Call open() first.");
|
|
485
|
+
}
|
|
486
|
+
const results = [];
|
|
487
|
+
for (const traceEntry of this.traceEntries) {
|
|
488
|
+
const content = await readZipEntryContent(this.zipBuffer, traceEntry);
|
|
489
|
+
if (!content) continue;
|
|
490
|
+
for (const line of content.split("\n")) {
|
|
491
|
+
if (line.trim()) {
|
|
492
|
+
try {
|
|
493
|
+
const event = JSON.parse(line);
|
|
494
|
+
results.push(adapter ? adapter(event) : event);
|
|
495
|
+
} catch {
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if (results.length > 0 && typeof results[0].time === "number") {
|
|
501
|
+
return results.sort((a, b) => a.time - b.time);
|
|
502
|
+
}
|
|
503
|
+
return results;
|
|
504
|
+
}
|
|
505
|
+
listFiles() {
|
|
506
|
+
if (!this.zipBuffer) {
|
|
507
|
+
return [];
|
|
508
|
+
}
|
|
509
|
+
return parseZipEntries(this.zipBuffer).map((e) => e.fileName);
|
|
510
|
+
}
|
|
511
|
+
isOpen() {
|
|
512
|
+
return this.zipBuffer !== null;
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
|
|
516
|
+
// ../utils/src/zipper.ts
|
|
517
|
+
import * as fs3 from "fs";
|
|
239
518
|
import * as os from "os";
|
|
240
519
|
import * as path2 from "path";
|
|
241
|
-
|
|
520
|
+
|
|
521
|
+
// ../../node_modules/.pnpm/fflate@0.8.2/node_modules/fflate/esm/index.mjs
|
|
522
|
+
import { createRequire } from "module";
|
|
523
|
+
var require2 = createRequire("/");
|
|
524
|
+
var Worker;
|
|
525
|
+
try {
|
|
526
|
+
Worker = require2("worker_threads").Worker;
|
|
527
|
+
} catch (e) {
|
|
528
|
+
}
|
|
529
|
+
var u8 = Uint8Array;
|
|
530
|
+
var u16 = Uint16Array;
|
|
531
|
+
var i32 = Int32Array;
|
|
532
|
+
var fleb = new u8([
|
|
533
|
+
0,
|
|
534
|
+
0,
|
|
535
|
+
0,
|
|
536
|
+
0,
|
|
537
|
+
0,
|
|
538
|
+
0,
|
|
539
|
+
0,
|
|
540
|
+
0,
|
|
541
|
+
1,
|
|
542
|
+
1,
|
|
543
|
+
1,
|
|
544
|
+
1,
|
|
545
|
+
2,
|
|
546
|
+
2,
|
|
547
|
+
2,
|
|
548
|
+
2,
|
|
549
|
+
3,
|
|
550
|
+
3,
|
|
551
|
+
3,
|
|
552
|
+
3,
|
|
553
|
+
4,
|
|
554
|
+
4,
|
|
555
|
+
4,
|
|
556
|
+
4,
|
|
557
|
+
5,
|
|
558
|
+
5,
|
|
559
|
+
5,
|
|
560
|
+
5,
|
|
561
|
+
0,
|
|
562
|
+
/* unused */
|
|
563
|
+
0,
|
|
564
|
+
0,
|
|
565
|
+
/* impossible */
|
|
566
|
+
0
|
|
567
|
+
]);
|
|
568
|
+
var fdeb = new u8([
|
|
569
|
+
0,
|
|
570
|
+
0,
|
|
571
|
+
0,
|
|
572
|
+
0,
|
|
573
|
+
1,
|
|
574
|
+
1,
|
|
575
|
+
2,
|
|
576
|
+
2,
|
|
577
|
+
3,
|
|
578
|
+
3,
|
|
579
|
+
4,
|
|
580
|
+
4,
|
|
581
|
+
5,
|
|
582
|
+
5,
|
|
583
|
+
6,
|
|
584
|
+
6,
|
|
585
|
+
7,
|
|
586
|
+
7,
|
|
587
|
+
8,
|
|
588
|
+
8,
|
|
589
|
+
9,
|
|
590
|
+
9,
|
|
591
|
+
10,
|
|
592
|
+
10,
|
|
593
|
+
11,
|
|
594
|
+
11,
|
|
595
|
+
12,
|
|
596
|
+
12,
|
|
597
|
+
13,
|
|
598
|
+
13,
|
|
599
|
+
/* unused */
|
|
600
|
+
0,
|
|
601
|
+
0
|
|
602
|
+
]);
|
|
603
|
+
var clim = new u8([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
|
|
604
|
+
var freb = function(eb, start) {
|
|
605
|
+
var b = new u16(31);
|
|
606
|
+
for (var i = 0; i < 31; ++i) {
|
|
607
|
+
b[i] = start += 1 << eb[i - 1];
|
|
608
|
+
}
|
|
609
|
+
var r = new i32(b[30]);
|
|
610
|
+
for (var i = 1; i < 30; ++i) {
|
|
611
|
+
for (var j = b[i]; j < b[i + 1]; ++j) {
|
|
612
|
+
r[j] = j - b[i] << 5 | i;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return { b, r };
|
|
616
|
+
};
|
|
617
|
+
var _a = freb(fleb, 2);
|
|
618
|
+
var fl = _a.b;
|
|
619
|
+
var revfl = _a.r;
|
|
620
|
+
fl[28] = 258, revfl[258] = 28;
|
|
621
|
+
var _b = freb(fdeb, 0);
|
|
622
|
+
var fd = _b.b;
|
|
623
|
+
var revfd = _b.r;
|
|
624
|
+
var rev = new u16(32768);
|
|
625
|
+
for (i = 0; i < 32768; ++i) {
|
|
626
|
+
x = (i & 43690) >> 1 | (i & 21845) << 1;
|
|
627
|
+
x = (x & 52428) >> 2 | (x & 13107) << 2;
|
|
628
|
+
x = (x & 61680) >> 4 | (x & 3855) << 4;
|
|
629
|
+
rev[i] = ((x & 65280) >> 8 | (x & 255) << 8) >> 1;
|
|
630
|
+
}
|
|
631
|
+
var x;
|
|
632
|
+
var i;
|
|
633
|
+
var hMap = (function(cd, mb, r) {
|
|
634
|
+
var s = cd.length;
|
|
635
|
+
var i = 0;
|
|
636
|
+
var l = new u16(mb);
|
|
637
|
+
for (; i < s; ++i) {
|
|
638
|
+
if (cd[i])
|
|
639
|
+
++l[cd[i] - 1];
|
|
640
|
+
}
|
|
641
|
+
var le = new u16(mb);
|
|
642
|
+
for (i = 1; i < mb; ++i) {
|
|
643
|
+
le[i] = le[i - 1] + l[i - 1] << 1;
|
|
644
|
+
}
|
|
645
|
+
var co;
|
|
646
|
+
if (r) {
|
|
647
|
+
co = new u16(1 << mb);
|
|
648
|
+
var rvb = 15 - mb;
|
|
649
|
+
for (i = 0; i < s; ++i) {
|
|
650
|
+
if (cd[i]) {
|
|
651
|
+
var sv = i << 4 | cd[i];
|
|
652
|
+
var r_1 = mb - cd[i];
|
|
653
|
+
var v = le[cd[i] - 1]++ << r_1;
|
|
654
|
+
for (var m = v | (1 << r_1) - 1; v <= m; ++v) {
|
|
655
|
+
co[rev[v] >> rvb] = sv;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
} else {
|
|
660
|
+
co = new u16(s);
|
|
661
|
+
for (i = 0; i < s; ++i) {
|
|
662
|
+
if (cd[i]) {
|
|
663
|
+
co[i] = rev[le[cd[i] - 1]++] >> 15 - cd[i];
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return co;
|
|
668
|
+
});
|
|
669
|
+
var flt = new u8(288);
|
|
670
|
+
for (i = 0; i < 144; ++i)
|
|
671
|
+
flt[i] = 8;
|
|
672
|
+
var i;
|
|
673
|
+
for (i = 144; i < 256; ++i)
|
|
674
|
+
flt[i] = 9;
|
|
675
|
+
var i;
|
|
676
|
+
for (i = 256; i < 280; ++i)
|
|
677
|
+
flt[i] = 7;
|
|
678
|
+
var i;
|
|
679
|
+
for (i = 280; i < 288; ++i)
|
|
680
|
+
flt[i] = 8;
|
|
681
|
+
var i;
|
|
682
|
+
var fdt = new u8(32);
|
|
683
|
+
for (i = 0; i < 32; ++i)
|
|
684
|
+
fdt[i] = 5;
|
|
685
|
+
var i;
|
|
686
|
+
var flm = /* @__PURE__ */ hMap(flt, 9, 0);
|
|
687
|
+
var fdm = /* @__PURE__ */ hMap(fdt, 5, 0);
|
|
688
|
+
var shft = function(p) {
|
|
689
|
+
return (p + 7) / 8 | 0;
|
|
690
|
+
};
|
|
691
|
+
var slc = function(v, s, e) {
|
|
692
|
+
if (s == null || s < 0)
|
|
693
|
+
s = 0;
|
|
694
|
+
if (e == null || e > v.length)
|
|
695
|
+
e = v.length;
|
|
696
|
+
return new u8(v.subarray(s, e));
|
|
697
|
+
};
|
|
698
|
+
var ec = [
|
|
699
|
+
"unexpected EOF",
|
|
700
|
+
"invalid block type",
|
|
701
|
+
"invalid length/literal",
|
|
702
|
+
"invalid distance",
|
|
703
|
+
"stream finished",
|
|
704
|
+
"no stream handler",
|
|
705
|
+
,
|
|
706
|
+
"no callback",
|
|
707
|
+
"invalid UTF-8 data",
|
|
708
|
+
"extra field too long",
|
|
709
|
+
"date not in range 1980-2099",
|
|
710
|
+
"filename too long",
|
|
711
|
+
"stream finishing",
|
|
712
|
+
"invalid zip data"
|
|
713
|
+
// determined by unknown compression method
|
|
714
|
+
];
|
|
715
|
+
var err = function(ind, msg, nt) {
|
|
716
|
+
var e = new Error(msg || ec[ind]);
|
|
717
|
+
e.code = ind;
|
|
718
|
+
if (Error.captureStackTrace)
|
|
719
|
+
Error.captureStackTrace(e, err);
|
|
720
|
+
if (!nt)
|
|
721
|
+
throw e;
|
|
722
|
+
return e;
|
|
723
|
+
};
|
|
724
|
+
var wbits = function(d, p, v) {
|
|
725
|
+
v <<= p & 7;
|
|
726
|
+
var o = p / 8 | 0;
|
|
727
|
+
d[o] |= v;
|
|
728
|
+
d[o + 1] |= v >> 8;
|
|
729
|
+
};
|
|
730
|
+
var wbits16 = function(d, p, v) {
|
|
731
|
+
v <<= p & 7;
|
|
732
|
+
var o = p / 8 | 0;
|
|
733
|
+
d[o] |= v;
|
|
734
|
+
d[o + 1] |= v >> 8;
|
|
735
|
+
d[o + 2] |= v >> 16;
|
|
736
|
+
};
|
|
737
|
+
var hTree = function(d, mb) {
|
|
738
|
+
var t = [];
|
|
739
|
+
for (var i = 0; i < d.length; ++i) {
|
|
740
|
+
if (d[i])
|
|
741
|
+
t.push({ s: i, f: d[i] });
|
|
742
|
+
}
|
|
743
|
+
var s = t.length;
|
|
744
|
+
var t2 = t.slice();
|
|
745
|
+
if (!s)
|
|
746
|
+
return { t: et, l: 0 };
|
|
747
|
+
if (s == 1) {
|
|
748
|
+
var v = new u8(t[0].s + 1);
|
|
749
|
+
v[t[0].s] = 1;
|
|
750
|
+
return { t: v, l: 1 };
|
|
751
|
+
}
|
|
752
|
+
t.sort(function(a, b) {
|
|
753
|
+
return a.f - b.f;
|
|
754
|
+
});
|
|
755
|
+
t.push({ s: -1, f: 25001 });
|
|
756
|
+
var l = t[0], r = t[1], i0 = 0, i1 = 1, i2 = 2;
|
|
757
|
+
t[0] = { s: -1, f: l.f + r.f, l, r };
|
|
758
|
+
while (i1 != s - 1) {
|
|
759
|
+
l = t[t[i0].f < t[i2].f ? i0++ : i2++];
|
|
760
|
+
r = t[i0 != i1 && t[i0].f < t[i2].f ? i0++ : i2++];
|
|
761
|
+
t[i1++] = { s: -1, f: l.f + r.f, l, r };
|
|
762
|
+
}
|
|
763
|
+
var maxSym = t2[0].s;
|
|
764
|
+
for (var i = 1; i < s; ++i) {
|
|
765
|
+
if (t2[i].s > maxSym)
|
|
766
|
+
maxSym = t2[i].s;
|
|
767
|
+
}
|
|
768
|
+
var tr = new u16(maxSym + 1);
|
|
769
|
+
var mbt = ln(t[i1 - 1], tr, 0);
|
|
770
|
+
if (mbt > mb) {
|
|
771
|
+
var i = 0, dt = 0;
|
|
772
|
+
var lft = mbt - mb, cst = 1 << lft;
|
|
773
|
+
t2.sort(function(a, b) {
|
|
774
|
+
return tr[b.s] - tr[a.s] || a.f - b.f;
|
|
775
|
+
});
|
|
776
|
+
for (; i < s; ++i) {
|
|
777
|
+
var i2_1 = t2[i].s;
|
|
778
|
+
if (tr[i2_1] > mb) {
|
|
779
|
+
dt += cst - (1 << mbt - tr[i2_1]);
|
|
780
|
+
tr[i2_1] = mb;
|
|
781
|
+
} else
|
|
782
|
+
break;
|
|
783
|
+
}
|
|
784
|
+
dt >>= lft;
|
|
785
|
+
while (dt > 0) {
|
|
786
|
+
var i2_2 = t2[i].s;
|
|
787
|
+
if (tr[i2_2] < mb)
|
|
788
|
+
dt -= 1 << mb - tr[i2_2]++ - 1;
|
|
789
|
+
else
|
|
790
|
+
++i;
|
|
791
|
+
}
|
|
792
|
+
for (; i >= 0 && dt; --i) {
|
|
793
|
+
var i2_3 = t2[i].s;
|
|
794
|
+
if (tr[i2_3] == mb) {
|
|
795
|
+
--tr[i2_3];
|
|
796
|
+
++dt;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
mbt = mb;
|
|
800
|
+
}
|
|
801
|
+
return { t: new u8(tr), l: mbt };
|
|
802
|
+
};
|
|
803
|
+
var ln = function(n, l, d) {
|
|
804
|
+
return n.s == -1 ? Math.max(ln(n.l, l, d + 1), ln(n.r, l, d + 1)) : l[n.s] = d;
|
|
805
|
+
};
|
|
806
|
+
var lc = function(c) {
|
|
807
|
+
var s = c.length;
|
|
808
|
+
while (s && !c[--s])
|
|
809
|
+
;
|
|
810
|
+
var cl = new u16(++s);
|
|
811
|
+
var cli = 0, cln = c[0], cls = 1;
|
|
812
|
+
var w = function(v) {
|
|
813
|
+
cl[cli++] = v;
|
|
814
|
+
};
|
|
815
|
+
for (var i = 1; i <= s; ++i) {
|
|
816
|
+
if (c[i] == cln && i != s)
|
|
817
|
+
++cls;
|
|
818
|
+
else {
|
|
819
|
+
if (!cln && cls > 2) {
|
|
820
|
+
for (; cls > 138; cls -= 138)
|
|
821
|
+
w(32754);
|
|
822
|
+
if (cls > 2) {
|
|
823
|
+
w(cls > 10 ? cls - 11 << 5 | 28690 : cls - 3 << 5 | 12305);
|
|
824
|
+
cls = 0;
|
|
825
|
+
}
|
|
826
|
+
} else if (cls > 3) {
|
|
827
|
+
w(cln), --cls;
|
|
828
|
+
for (; cls > 6; cls -= 6)
|
|
829
|
+
w(8304);
|
|
830
|
+
if (cls > 2)
|
|
831
|
+
w(cls - 3 << 5 | 8208), cls = 0;
|
|
832
|
+
}
|
|
833
|
+
while (cls--)
|
|
834
|
+
w(cln);
|
|
835
|
+
cls = 1;
|
|
836
|
+
cln = c[i];
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
return { c: cl.subarray(0, cli), n: s };
|
|
840
|
+
};
|
|
841
|
+
var clen = function(cf, cl) {
|
|
842
|
+
var l = 0;
|
|
843
|
+
for (var i = 0; i < cl.length; ++i)
|
|
844
|
+
l += cf[i] * cl[i];
|
|
845
|
+
return l;
|
|
846
|
+
};
|
|
847
|
+
var wfblk = function(out, pos, dat) {
|
|
848
|
+
var s = dat.length;
|
|
849
|
+
var o = shft(pos + 2);
|
|
850
|
+
out[o] = s & 255;
|
|
851
|
+
out[o + 1] = s >> 8;
|
|
852
|
+
out[o + 2] = out[o] ^ 255;
|
|
853
|
+
out[o + 3] = out[o + 1] ^ 255;
|
|
854
|
+
for (var i = 0; i < s; ++i)
|
|
855
|
+
out[o + i + 4] = dat[i];
|
|
856
|
+
return (o + 4 + s) * 8;
|
|
857
|
+
};
|
|
858
|
+
var wblk = function(dat, out, final, syms, lf, df, eb, li, bs, bl, p) {
|
|
859
|
+
wbits(out, p++, final);
|
|
860
|
+
++lf[256];
|
|
861
|
+
var _a2 = hTree(lf, 15), dlt = _a2.t, mlb = _a2.l;
|
|
862
|
+
var _b2 = hTree(df, 15), ddt = _b2.t, mdb = _b2.l;
|
|
863
|
+
var _c = lc(dlt), lclt = _c.c, nlc = _c.n;
|
|
864
|
+
var _d = lc(ddt), lcdt = _d.c, ndc = _d.n;
|
|
865
|
+
var lcfreq = new u16(19);
|
|
866
|
+
for (var i = 0; i < lclt.length; ++i)
|
|
867
|
+
++lcfreq[lclt[i] & 31];
|
|
868
|
+
for (var i = 0; i < lcdt.length; ++i)
|
|
869
|
+
++lcfreq[lcdt[i] & 31];
|
|
870
|
+
var _e = hTree(lcfreq, 7), lct = _e.t, mlcb = _e.l;
|
|
871
|
+
var nlcc = 19;
|
|
872
|
+
for (; nlcc > 4 && !lct[clim[nlcc - 1]]; --nlcc)
|
|
873
|
+
;
|
|
874
|
+
var flen = bl + 5 << 3;
|
|
875
|
+
var ftlen = clen(lf, flt) + clen(df, fdt) + eb;
|
|
876
|
+
var dtlen = clen(lf, dlt) + clen(df, ddt) + eb + 14 + 3 * nlcc + clen(lcfreq, lct) + 2 * lcfreq[16] + 3 * lcfreq[17] + 7 * lcfreq[18];
|
|
877
|
+
if (bs >= 0 && flen <= ftlen && flen <= dtlen)
|
|
878
|
+
return wfblk(out, p, dat.subarray(bs, bs + bl));
|
|
879
|
+
var lm, ll, dm, dl;
|
|
880
|
+
wbits(out, p, 1 + (dtlen < ftlen)), p += 2;
|
|
881
|
+
if (dtlen < ftlen) {
|
|
882
|
+
lm = hMap(dlt, mlb, 0), ll = dlt, dm = hMap(ddt, mdb, 0), dl = ddt;
|
|
883
|
+
var llm = hMap(lct, mlcb, 0);
|
|
884
|
+
wbits(out, p, nlc - 257);
|
|
885
|
+
wbits(out, p + 5, ndc - 1);
|
|
886
|
+
wbits(out, p + 10, nlcc - 4);
|
|
887
|
+
p += 14;
|
|
888
|
+
for (var i = 0; i < nlcc; ++i)
|
|
889
|
+
wbits(out, p + 3 * i, lct[clim[i]]);
|
|
890
|
+
p += 3 * nlcc;
|
|
891
|
+
var lcts = [lclt, lcdt];
|
|
892
|
+
for (var it = 0; it < 2; ++it) {
|
|
893
|
+
var clct = lcts[it];
|
|
894
|
+
for (var i = 0; i < clct.length; ++i) {
|
|
895
|
+
var len = clct[i] & 31;
|
|
896
|
+
wbits(out, p, llm[len]), p += lct[len];
|
|
897
|
+
if (len > 15)
|
|
898
|
+
wbits(out, p, clct[i] >> 5 & 127), p += clct[i] >> 12;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
} else {
|
|
902
|
+
lm = flm, ll = flt, dm = fdm, dl = fdt;
|
|
903
|
+
}
|
|
904
|
+
for (var i = 0; i < li; ++i) {
|
|
905
|
+
var sym = syms[i];
|
|
906
|
+
if (sym > 255) {
|
|
907
|
+
var len = sym >> 18 & 31;
|
|
908
|
+
wbits16(out, p, lm[len + 257]), p += ll[len + 257];
|
|
909
|
+
if (len > 7)
|
|
910
|
+
wbits(out, p, sym >> 23 & 31), p += fleb[len];
|
|
911
|
+
var dst = sym & 31;
|
|
912
|
+
wbits16(out, p, dm[dst]), p += dl[dst];
|
|
913
|
+
if (dst > 3)
|
|
914
|
+
wbits16(out, p, sym >> 5 & 8191), p += fdeb[dst];
|
|
915
|
+
} else {
|
|
916
|
+
wbits16(out, p, lm[sym]), p += ll[sym];
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
wbits16(out, p, lm[256]);
|
|
920
|
+
return p + ll[256];
|
|
921
|
+
};
|
|
922
|
+
var deo = /* @__PURE__ */ new i32([65540, 131080, 131088, 131104, 262176, 1048704, 1048832, 2114560, 2117632]);
|
|
923
|
+
var et = /* @__PURE__ */ new u8(0);
|
|
924
|
+
var dflt = function(dat, lvl, plvl, pre, post, st) {
|
|
925
|
+
var s = st.z || dat.length;
|
|
926
|
+
var o = new u8(pre + s + 5 * (1 + Math.ceil(s / 7e3)) + post);
|
|
927
|
+
var w = o.subarray(pre, o.length - post);
|
|
928
|
+
var lst = st.l;
|
|
929
|
+
var pos = (st.r || 0) & 7;
|
|
930
|
+
if (lvl) {
|
|
931
|
+
if (pos)
|
|
932
|
+
w[0] = st.r >> 3;
|
|
933
|
+
var opt = deo[lvl - 1];
|
|
934
|
+
var n = opt >> 13, c = opt & 8191;
|
|
935
|
+
var msk_1 = (1 << plvl) - 1;
|
|
936
|
+
var prev = st.p || new u16(32768), head = st.h || new u16(msk_1 + 1);
|
|
937
|
+
var bs1_1 = Math.ceil(plvl / 3), bs2_1 = 2 * bs1_1;
|
|
938
|
+
var hsh = function(i2) {
|
|
939
|
+
return (dat[i2] ^ dat[i2 + 1] << bs1_1 ^ dat[i2 + 2] << bs2_1) & msk_1;
|
|
940
|
+
};
|
|
941
|
+
var syms = new i32(25e3);
|
|
942
|
+
var lf = new u16(288), df = new u16(32);
|
|
943
|
+
var lc_1 = 0, eb = 0, i = st.i || 0, li = 0, wi = st.w || 0, bs = 0;
|
|
944
|
+
for (; i + 2 < s; ++i) {
|
|
945
|
+
var hv = hsh(i);
|
|
946
|
+
var imod = i & 32767, pimod = head[hv];
|
|
947
|
+
prev[imod] = pimod;
|
|
948
|
+
head[hv] = imod;
|
|
949
|
+
if (wi <= i) {
|
|
950
|
+
var rem = s - i;
|
|
951
|
+
if ((lc_1 > 7e3 || li > 24576) && (rem > 423 || !lst)) {
|
|
952
|
+
pos = wblk(dat, w, 0, syms, lf, df, eb, li, bs, i - bs, pos);
|
|
953
|
+
li = lc_1 = eb = 0, bs = i;
|
|
954
|
+
for (var j = 0; j < 286; ++j)
|
|
955
|
+
lf[j] = 0;
|
|
956
|
+
for (var j = 0; j < 30; ++j)
|
|
957
|
+
df[j] = 0;
|
|
958
|
+
}
|
|
959
|
+
var l = 2, d = 0, ch_1 = c, dif = imod - pimod & 32767;
|
|
960
|
+
if (rem > 2 && hv == hsh(i - dif)) {
|
|
961
|
+
var maxn = Math.min(n, rem) - 1;
|
|
962
|
+
var maxd = Math.min(32767, i);
|
|
963
|
+
var ml = Math.min(258, rem);
|
|
964
|
+
while (dif <= maxd && --ch_1 && imod != pimod) {
|
|
965
|
+
if (dat[i + l] == dat[i + l - dif]) {
|
|
966
|
+
var nl = 0;
|
|
967
|
+
for (; nl < ml && dat[i + nl] == dat[i + nl - dif]; ++nl)
|
|
968
|
+
;
|
|
969
|
+
if (nl > l) {
|
|
970
|
+
l = nl, d = dif;
|
|
971
|
+
if (nl > maxn)
|
|
972
|
+
break;
|
|
973
|
+
var mmd = Math.min(dif, nl - 2);
|
|
974
|
+
var md = 0;
|
|
975
|
+
for (var j = 0; j < mmd; ++j) {
|
|
976
|
+
var ti = i - dif + j & 32767;
|
|
977
|
+
var pti = prev[ti];
|
|
978
|
+
var cd = ti - pti & 32767;
|
|
979
|
+
if (cd > md)
|
|
980
|
+
md = cd, pimod = ti;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
imod = pimod, pimod = prev[imod];
|
|
985
|
+
dif += imod - pimod & 32767;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
if (d) {
|
|
989
|
+
syms[li++] = 268435456 | revfl[l] << 18 | revfd[d];
|
|
990
|
+
var lin = revfl[l] & 31, din = revfd[d] & 31;
|
|
991
|
+
eb += fleb[lin] + fdeb[din];
|
|
992
|
+
++lf[257 + lin];
|
|
993
|
+
++df[din];
|
|
994
|
+
wi = i + l;
|
|
995
|
+
++lc_1;
|
|
996
|
+
} else {
|
|
997
|
+
syms[li++] = dat[i];
|
|
998
|
+
++lf[dat[i]];
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
for (i = Math.max(i, wi); i < s; ++i) {
|
|
1003
|
+
syms[li++] = dat[i];
|
|
1004
|
+
++lf[dat[i]];
|
|
1005
|
+
}
|
|
1006
|
+
pos = wblk(dat, w, lst, syms, lf, df, eb, li, bs, i - bs, pos);
|
|
1007
|
+
if (!lst) {
|
|
1008
|
+
st.r = pos & 7 | w[pos / 8 | 0] << 3;
|
|
1009
|
+
pos -= 7;
|
|
1010
|
+
st.h = head, st.p = prev, st.i = i, st.w = wi;
|
|
1011
|
+
}
|
|
1012
|
+
} else {
|
|
1013
|
+
for (var i = st.w || 0; i < s + lst; i += 65535) {
|
|
1014
|
+
var e = i + 65535;
|
|
1015
|
+
if (e >= s) {
|
|
1016
|
+
w[pos / 8 | 0] = lst;
|
|
1017
|
+
e = s;
|
|
1018
|
+
}
|
|
1019
|
+
pos = wfblk(w, pos + 1, dat.subarray(i, e));
|
|
1020
|
+
}
|
|
1021
|
+
st.i = s;
|
|
1022
|
+
}
|
|
1023
|
+
return slc(o, 0, pre + shft(pos) + post);
|
|
1024
|
+
};
|
|
1025
|
+
var crct = /* @__PURE__ */ (function() {
|
|
1026
|
+
var t = new Int32Array(256);
|
|
1027
|
+
for (var i = 0; i < 256; ++i) {
|
|
1028
|
+
var c = i, k = 9;
|
|
1029
|
+
while (--k)
|
|
1030
|
+
c = (c & 1 && -306674912) ^ c >>> 1;
|
|
1031
|
+
t[i] = c;
|
|
1032
|
+
}
|
|
1033
|
+
return t;
|
|
1034
|
+
})();
|
|
1035
|
+
var crc = function() {
|
|
1036
|
+
var c = -1;
|
|
1037
|
+
return {
|
|
1038
|
+
p: function(d) {
|
|
1039
|
+
var cr = c;
|
|
1040
|
+
for (var i = 0; i < d.length; ++i)
|
|
1041
|
+
cr = crct[cr & 255 ^ d[i]] ^ cr >>> 8;
|
|
1042
|
+
c = cr;
|
|
1043
|
+
},
|
|
1044
|
+
d: function() {
|
|
1045
|
+
return ~c;
|
|
1046
|
+
}
|
|
1047
|
+
};
|
|
1048
|
+
};
|
|
1049
|
+
var dopt = function(dat, opt, pre, post, st) {
|
|
1050
|
+
if (!st) {
|
|
1051
|
+
st = { l: 1 };
|
|
1052
|
+
if (opt.dictionary) {
|
|
1053
|
+
var dict = opt.dictionary.subarray(-32768);
|
|
1054
|
+
var newDat = new u8(dict.length + dat.length);
|
|
1055
|
+
newDat.set(dict);
|
|
1056
|
+
newDat.set(dat, dict.length);
|
|
1057
|
+
dat = newDat;
|
|
1058
|
+
st.w = dict.length;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
return dflt(dat, opt.level == null ? 6 : opt.level, opt.mem == null ? st.l ? Math.ceil(Math.max(8, Math.min(13, Math.log(dat.length))) * 1.5) : 20 : 12 + opt.mem, pre, post, st);
|
|
1062
|
+
};
|
|
1063
|
+
var mrg = function(a, b) {
|
|
1064
|
+
var o = {};
|
|
1065
|
+
for (var k in a)
|
|
1066
|
+
o[k] = a[k];
|
|
1067
|
+
for (var k in b)
|
|
1068
|
+
o[k] = b[k];
|
|
1069
|
+
return o;
|
|
1070
|
+
};
|
|
1071
|
+
var wbytes = function(d, b, v) {
|
|
1072
|
+
for (; v; ++b)
|
|
1073
|
+
d[b] = v, v >>>= 8;
|
|
1074
|
+
};
|
|
1075
|
+
function deflateSync(data, opts) {
|
|
1076
|
+
return dopt(data, opts || {}, 0, 0);
|
|
1077
|
+
}
|
|
1078
|
+
var fltn = function(d, p, t, o) {
|
|
1079
|
+
for (var k in d) {
|
|
1080
|
+
var val = d[k], n = p + k, op = o;
|
|
1081
|
+
if (Array.isArray(val))
|
|
1082
|
+
op = mrg(o, val[1]), val = val[0];
|
|
1083
|
+
if (val instanceof u8)
|
|
1084
|
+
t[n] = [val, op];
|
|
1085
|
+
else {
|
|
1086
|
+
t[n += "/"] = [new u8(0), op];
|
|
1087
|
+
fltn(val, n, t, o);
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
var te = typeof TextEncoder != "undefined" && /* @__PURE__ */ new TextEncoder();
|
|
1092
|
+
var td = typeof TextDecoder != "undefined" && /* @__PURE__ */ new TextDecoder();
|
|
1093
|
+
var tds = 0;
|
|
1094
|
+
try {
|
|
1095
|
+
td.decode(et, { stream: true });
|
|
1096
|
+
tds = 1;
|
|
1097
|
+
} catch (e) {
|
|
1098
|
+
}
|
|
1099
|
+
function strToU8(str, latin1) {
|
|
1100
|
+
if (latin1) {
|
|
1101
|
+
var ar_1 = new u8(str.length);
|
|
1102
|
+
for (var i = 0; i < str.length; ++i)
|
|
1103
|
+
ar_1[i] = str.charCodeAt(i);
|
|
1104
|
+
return ar_1;
|
|
1105
|
+
}
|
|
1106
|
+
if (te)
|
|
1107
|
+
return te.encode(str);
|
|
1108
|
+
var l = str.length;
|
|
1109
|
+
var ar = new u8(str.length + (str.length >> 1));
|
|
1110
|
+
var ai = 0;
|
|
1111
|
+
var w = function(v) {
|
|
1112
|
+
ar[ai++] = v;
|
|
1113
|
+
};
|
|
1114
|
+
for (var i = 0; i < l; ++i) {
|
|
1115
|
+
if (ai + 5 > ar.length) {
|
|
1116
|
+
var n = new u8(ai + 8 + (l - i << 1));
|
|
1117
|
+
n.set(ar);
|
|
1118
|
+
ar = n;
|
|
1119
|
+
}
|
|
1120
|
+
var c = str.charCodeAt(i);
|
|
1121
|
+
if (c < 128 || latin1)
|
|
1122
|
+
w(c);
|
|
1123
|
+
else if (c < 2048)
|
|
1124
|
+
w(192 | c >> 6), w(128 | c & 63);
|
|
1125
|
+
else if (c > 55295 && c < 57344)
|
|
1126
|
+
c = 65536 + (c & 1023 << 10) | str.charCodeAt(++i) & 1023, w(240 | c >> 18), w(128 | c >> 12 & 63), w(128 | c >> 6 & 63), w(128 | c & 63);
|
|
1127
|
+
else
|
|
1128
|
+
w(224 | c >> 12), w(128 | c >> 6 & 63), w(128 | c & 63);
|
|
1129
|
+
}
|
|
1130
|
+
return slc(ar, 0, ai);
|
|
1131
|
+
}
|
|
1132
|
+
var exfl = function(ex) {
|
|
1133
|
+
var le = 0;
|
|
1134
|
+
if (ex) {
|
|
1135
|
+
for (var k in ex) {
|
|
1136
|
+
var l = ex[k].length;
|
|
1137
|
+
if (l > 65535)
|
|
1138
|
+
err(9);
|
|
1139
|
+
le += l + 4;
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
return le;
|
|
1143
|
+
};
|
|
1144
|
+
var wzh = function(d, b, f, fn, u, c, ce, co) {
|
|
1145
|
+
var fl2 = fn.length, ex = f.extra, col = co && co.length;
|
|
1146
|
+
var exl = exfl(ex);
|
|
1147
|
+
wbytes(d, b, ce != null ? 33639248 : 67324752), b += 4;
|
|
1148
|
+
if (ce != null)
|
|
1149
|
+
d[b++] = 20, d[b++] = f.os;
|
|
1150
|
+
d[b] = 20, b += 2;
|
|
1151
|
+
d[b++] = f.flag << 1 | (c < 0 && 8), d[b++] = u && 8;
|
|
1152
|
+
d[b++] = f.compression & 255, d[b++] = f.compression >> 8;
|
|
1153
|
+
var dt = new Date(f.mtime == null ? Date.now() : f.mtime), y = dt.getFullYear() - 1980;
|
|
1154
|
+
if (y < 0 || y > 119)
|
|
1155
|
+
err(10);
|
|
1156
|
+
wbytes(d, b, y << 25 | dt.getMonth() + 1 << 21 | dt.getDate() << 16 | dt.getHours() << 11 | dt.getMinutes() << 5 | dt.getSeconds() >> 1), b += 4;
|
|
1157
|
+
if (c != -1) {
|
|
1158
|
+
wbytes(d, b, f.crc);
|
|
1159
|
+
wbytes(d, b + 4, c < 0 ? -c - 2 : c);
|
|
1160
|
+
wbytes(d, b + 8, f.size);
|
|
1161
|
+
}
|
|
1162
|
+
wbytes(d, b + 12, fl2);
|
|
1163
|
+
wbytes(d, b + 14, exl), b += 16;
|
|
1164
|
+
if (ce != null) {
|
|
1165
|
+
wbytes(d, b, col);
|
|
1166
|
+
wbytes(d, b + 6, f.attrs);
|
|
1167
|
+
wbytes(d, b + 10, ce), b += 14;
|
|
1168
|
+
}
|
|
1169
|
+
d.set(fn, b);
|
|
1170
|
+
b += fl2;
|
|
1171
|
+
if (exl) {
|
|
1172
|
+
for (var k in ex) {
|
|
1173
|
+
var exf = ex[k], l = exf.length;
|
|
1174
|
+
wbytes(d, b, +k);
|
|
1175
|
+
wbytes(d, b + 2, l);
|
|
1176
|
+
d.set(exf, b + 4), b += 4 + l;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
if (col)
|
|
1180
|
+
d.set(co, b), b += col;
|
|
1181
|
+
return b;
|
|
1182
|
+
};
|
|
1183
|
+
var wzf = function(o, b, c, d, e) {
|
|
1184
|
+
wbytes(o, b, 101010256);
|
|
1185
|
+
wbytes(o, b + 8, c);
|
|
1186
|
+
wbytes(o, b + 10, c);
|
|
1187
|
+
wbytes(o, b + 12, d);
|
|
1188
|
+
wbytes(o, b + 16, e);
|
|
1189
|
+
};
|
|
1190
|
+
function zipSync(data, opts) {
|
|
1191
|
+
if (!opts)
|
|
1192
|
+
opts = {};
|
|
1193
|
+
var r = {};
|
|
1194
|
+
var files = [];
|
|
1195
|
+
fltn(data, "", r, opts);
|
|
1196
|
+
var o = 0;
|
|
1197
|
+
var tot = 0;
|
|
1198
|
+
for (var fn in r) {
|
|
1199
|
+
var _a2 = r[fn], file = _a2[0], p = _a2[1];
|
|
1200
|
+
var compression = p.level == 0 ? 0 : 8;
|
|
1201
|
+
var f = strToU8(fn), s = f.length;
|
|
1202
|
+
var com = p.comment, m = com && strToU8(com), ms = m && m.length;
|
|
1203
|
+
var exl = exfl(p.extra);
|
|
1204
|
+
if (s > 65535)
|
|
1205
|
+
err(11);
|
|
1206
|
+
var d = compression ? deflateSync(file, p) : file, l = d.length;
|
|
1207
|
+
var c = crc();
|
|
1208
|
+
c.p(file);
|
|
1209
|
+
files.push(mrg(p, {
|
|
1210
|
+
size: file.length,
|
|
1211
|
+
crc: c.d(),
|
|
1212
|
+
c: d,
|
|
1213
|
+
f,
|
|
1214
|
+
m,
|
|
1215
|
+
u: s != fn.length || m && com.length != ms,
|
|
1216
|
+
o,
|
|
1217
|
+
compression
|
|
1218
|
+
}));
|
|
1219
|
+
o += 30 + s + exl + l;
|
|
1220
|
+
tot += 76 + 2 * (s + exl) + (ms || 0) + l;
|
|
1221
|
+
}
|
|
1222
|
+
var out = new u8(tot + 22), oe = o, cdl = tot - o;
|
|
1223
|
+
for (var i = 0; i < files.length; ++i) {
|
|
1224
|
+
var f = files[i];
|
|
1225
|
+
wzh(out, f.o, f, f.f, f.u, f.c.length);
|
|
1226
|
+
var badd = 30 + f.f.length + exfl(f.extra);
|
|
1227
|
+
out.set(f.c, f.o + badd);
|
|
1228
|
+
wzh(out, o, f, f.f, f.u, f.c.length, f.o, f.m), o += 16 + badd + (f.m ? f.m.length : 0);
|
|
1229
|
+
}
|
|
1230
|
+
wzf(out, o, files.length, cdl, oe);
|
|
1231
|
+
return out;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
// ../utils/src/zipper.ts
|
|
242
1235
|
var Zipper = class {
|
|
243
1236
|
outputPath;
|
|
1237
|
+
compressionLevel;
|
|
1238
|
+
onProgress;
|
|
244
1239
|
constructor(options) {
|
|
245
1240
|
this.outputPath = options.outputPath;
|
|
1241
|
+
this.compressionLevel = options.compressionLevel ?? 0;
|
|
1242
|
+
this.onProgress = options.onProgress;
|
|
246
1243
|
}
|
|
247
1244
|
/**
|
|
248
1245
|
* Creates a ZIP archive containing the JSON report and assets
|
|
@@ -251,76 +1248,105 @@ var Zipper = class {
|
|
|
251
1248
|
* @returns ZIP creation result with metadata
|
|
252
1249
|
*/
|
|
253
1250
|
async createZip(reportPath, assets) {
|
|
1251
|
+
if (!fs3.existsSync(reportPath)) {
|
|
1252
|
+
throw new Error(`Report file not found: ${reportPath}`);
|
|
1253
|
+
}
|
|
1254
|
+
const transformedReportPath = this.transformJsonReport(reportPath);
|
|
1255
|
+
const files = {};
|
|
1256
|
+
const totalEntries = assets.length + 1;
|
|
1257
|
+
let processedEntries = 0;
|
|
1258
|
+
const reportContent = fs3.readFileSync(transformedReportPath);
|
|
1259
|
+
files["output/playwright-test-report.json"] = new Uint8Array(reportContent);
|
|
1260
|
+
processedEntries++;
|
|
1261
|
+
this.onProgress?.({ processedEntries, totalEntries, currentFile: "output/playwright-test-report.json" });
|
|
1262
|
+
for (const asset of assets) {
|
|
1263
|
+
if (!fs3.existsSync(asset.sourcePath)) {
|
|
1264
|
+
console.warn(`[Checkly Reporter] Skipping missing asset: ${asset.sourcePath}`);
|
|
1265
|
+
continue;
|
|
1266
|
+
}
|
|
1267
|
+
const content = fs3.readFileSync(asset.sourcePath);
|
|
1268
|
+
files[asset.archivePath] = new Uint8Array(content);
|
|
1269
|
+
processedEntries++;
|
|
1270
|
+
this.onProgress?.({ processedEntries, totalEntries, currentFile: asset.archivePath });
|
|
1271
|
+
}
|
|
1272
|
+
const zipData = zipSync(files, { level: this.compressionLevel });
|
|
1273
|
+
fs3.writeFileSync(this.outputPath, zipData);
|
|
1274
|
+
try {
|
|
1275
|
+
fs3.unlinkSync(transformedReportPath);
|
|
1276
|
+
} catch {
|
|
1277
|
+
}
|
|
1278
|
+
const entries = this.extractEntryOffsets(zipData);
|
|
1279
|
+
return {
|
|
1280
|
+
zipPath: this.outputPath,
|
|
1281
|
+
size: zipData.length,
|
|
1282
|
+
entryCount: entries.length,
|
|
1283
|
+
entries
|
|
1284
|
+
};
|
|
1285
|
+
}
|
|
1286
|
+
/**
|
|
1287
|
+
* Extracts byte offsets for each entry from the ZIP data
|
|
1288
|
+
* Parses the central directory to get accurate offset information
|
|
1289
|
+
*/
|
|
1290
|
+
extractEntryOffsets(zipData) {
|
|
254
1291
|
const entries = [];
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
size: zipSize,
|
|
276
|
-
entryCount: entries.length,
|
|
277
|
-
entries
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
archive.on("error", (err) => {
|
|
281
|
-
reject(err);
|
|
282
|
-
});
|
|
283
|
-
output.on("error", (err) => {
|
|
284
|
-
reject(err);
|
|
285
|
-
});
|
|
286
|
-
archive.pipe(output);
|
|
287
|
-
if (!fs2.existsSync(reportPath)) {
|
|
288
|
-
reject(new Error(`Report file not found: ${reportPath}`));
|
|
289
|
-
return;
|
|
290
|
-
}
|
|
291
|
-
const transformedReportPath = this.transformJsonReport(reportPath);
|
|
292
|
-
archive.file(transformedReportPath, { name: "output/playwright-test-report.json" });
|
|
293
|
-
for (const asset of assets) {
|
|
294
|
-
if (!fs2.existsSync(asset.sourcePath)) {
|
|
295
|
-
console.warn(`[Checkly Reporter] Skipping missing asset: ${asset.sourcePath}`);
|
|
296
|
-
continue;
|
|
297
|
-
}
|
|
298
|
-
archive.file(asset.sourcePath, { name: asset.archivePath });
|
|
1292
|
+
const view = new DataView(zipData.buffer, zipData.byteOffset, zipData.byteLength);
|
|
1293
|
+
let eocdOffset = -1;
|
|
1294
|
+
for (let i = zipData.length - 22; i >= 0; i--) {
|
|
1295
|
+
if (view.getUint32(i, true) === 101010256) {
|
|
1296
|
+
eocdOffset = i;
|
|
1297
|
+
break;
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
if (eocdOffset === -1) {
|
|
1301
|
+
console.warn("[Checkly Reporter] Could not find ZIP end of central directory");
|
|
1302
|
+
return entries;
|
|
1303
|
+
}
|
|
1304
|
+
let cdOffset = view.getUint32(eocdOffset + 16, true);
|
|
1305
|
+
const cdSize = view.getUint32(eocdOffset + 12, true);
|
|
1306
|
+
if (cdOffset === 4294967295 && eocdOffset >= 20) {
|
|
1307
|
+
const zip64LocatorOffset = eocdOffset - 20;
|
|
1308
|
+
if (view.getUint32(zip64LocatorOffset, true) === 117853008) {
|
|
1309
|
+
const zip64EocdOffset = Number(view.getBigUint64(zip64LocatorOffset + 8, true));
|
|
1310
|
+
if (view.getUint32(zip64EocdOffset, true) === 101075792) {
|
|
1311
|
+
cdOffset = Number(view.getBigUint64(zip64EocdOffset + 48, true));
|
|
299
1312
|
}
|
|
300
|
-
archive.finalize();
|
|
301
|
-
} catch (error) {
|
|
302
|
-
reject(error);
|
|
303
1313
|
}
|
|
304
|
-
}
|
|
1314
|
+
}
|
|
1315
|
+
let offset = cdOffset;
|
|
1316
|
+
const cdEnd = cdOffset + cdSize;
|
|
1317
|
+
while (offset < cdEnd && view.getUint32(offset, true) === 33639248) {
|
|
1318
|
+
const compressedSize = view.getUint32(offset + 20, true);
|
|
1319
|
+
const filenameLength = view.getUint16(offset + 28, true);
|
|
1320
|
+
const extraLength = view.getUint16(offset + 30, true);
|
|
1321
|
+
const commentLength = view.getUint16(offset + 32, true);
|
|
1322
|
+
const localHeaderOffset = view.getUint32(offset + 42, true);
|
|
1323
|
+
const filenameBytes = zipData.slice(offset + 46, offset + 46 + filenameLength);
|
|
1324
|
+
const filename = new TextDecoder().decode(filenameBytes);
|
|
1325
|
+
const localExtraLength = view.getUint16(localHeaderOffset + 28, true);
|
|
1326
|
+
const dataStart = localHeaderOffset + 30 + filenameLength + localExtraLength;
|
|
1327
|
+
const dataEnd = dataStart + compressedSize - 1;
|
|
1328
|
+
entries.push({
|
|
1329
|
+
name: filename.replace(/\\/g, "/"),
|
|
1330
|
+
start: dataStart,
|
|
1331
|
+
end: dataEnd >= dataStart ? dataEnd : dataStart
|
|
1332
|
+
});
|
|
1333
|
+
offset += 46 + filenameLength + extraLength + commentLength;
|
|
1334
|
+
}
|
|
1335
|
+
return entries;
|
|
305
1336
|
}
|
|
306
1337
|
/**
|
|
307
1338
|
* Transforms the JSON report to use relative paths for attachments
|
|
308
|
-
* This ensures the UI can map attachment paths to ZIP entries
|
|
309
|
-
* @param reportPath - Path to the original JSON report
|
|
310
|
-
* @returns Path to the transformed JSON report (in temp directory)
|
|
311
1339
|
*/
|
|
312
1340
|
transformJsonReport(reportPath) {
|
|
313
|
-
const reportContent =
|
|
1341
|
+
const reportContent = fs3.readFileSync(reportPath, "utf-8");
|
|
314
1342
|
const report = JSON.parse(reportContent);
|
|
315
1343
|
this.transformAttachmentPaths(report);
|
|
316
1344
|
const tempReportPath = path2.join(os.tmpdir(), `playwright-test-report-${Date.now()}.json`);
|
|
317
|
-
|
|
1345
|
+
fs3.writeFileSync(tempReportPath, JSON.stringify(report, null, 2));
|
|
318
1346
|
return tempReportPath;
|
|
319
1347
|
}
|
|
320
1348
|
/**
|
|
321
1349
|
* Recursively transforms attachment paths in the report structure
|
|
322
|
-
* Converts absolute paths to relative paths matching ZIP structure
|
|
323
|
-
* @param obj - Object to transform (mutated in place)
|
|
324
1350
|
*/
|
|
325
1351
|
transformAttachmentPaths(obj) {
|
|
326
1352
|
if (typeof obj !== "object" || obj === null) {
|
|
@@ -340,21 +1366,7 @@ var Zipper = class {
|
|
|
340
1366
|
Object.values(obj).forEach((value) => this.transformAttachmentPaths(value));
|
|
341
1367
|
}
|
|
342
1368
|
/**
|
|
343
|
-
* Normalizes attachment paths by extracting the relevant snapshot directory portion
|
|
344
|
-
* Supports Playwright's default and common custom snapshot directory patterns,
|
|
345
|
-
* as well as blob merge resource paths.
|
|
346
|
-
*
|
|
347
|
-
* Priority order (first match wins):
|
|
348
|
-
* 1. test-results/ (highest priority, existing behavior)
|
|
349
|
-
* 2. blob-reports/resources/ (blob merge extraction paths)
|
|
350
|
-
* 3. snapshots directories (Playwright default pattern)
|
|
351
|
-
* 4. __screenshots__/ (common custom pattern)
|
|
352
|
-
* 5. __snapshots__/ (common custom pattern)
|
|
353
|
-
* 6. screenshots/ (simple custom pattern)
|
|
354
|
-
* 7. snapshots/ (simple custom pattern)
|
|
355
|
-
*
|
|
356
|
-
* @param attachmentPath - Absolute or relative path to attachment
|
|
357
|
-
* @returns Normalized path starting from the matched directory, or original path if no match
|
|
1369
|
+
* Normalizes attachment paths by extracting the relevant snapshot directory portion
|
|
358
1370
|
*/
|
|
359
1371
|
normalizeAttachmentPath(attachmentPath) {
|
|
360
1372
|
const normalizedPath = attachmentPath.replace(/\\/g, "/");
|
|
@@ -391,17 +1403,11 @@ var Zipper = class {
|
|
|
391
1403
|
}
|
|
392
1404
|
};
|
|
393
1405
|
|
|
394
|
-
// src/
|
|
395
|
-
import * as fs3 from "fs";
|
|
396
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
397
|
-
import * as path3 from "path";
|
|
398
|
-
import { dirname, join as join3 } from "path";
|
|
399
|
-
import { fileURLToPath } from "url";
|
|
400
|
-
|
|
401
|
-
// ../clients/src/checkly-client.ts
|
|
1406
|
+
// src/api/client.ts
|
|
402
1407
|
import axios from "axios";
|
|
1408
|
+
import FormData from "form-data";
|
|
403
1409
|
|
|
404
|
-
//
|
|
1410
|
+
// src/api/errors.ts
|
|
405
1411
|
var ApiError = class extends Error {
|
|
406
1412
|
data;
|
|
407
1413
|
constructor(data, options) {
|
|
@@ -411,44 +1417,20 @@ var ApiError = class extends Error {
|
|
|
411
1417
|
}
|
|
412
1418
|
};
|
|
413
1419
|
var ValidationError = class extends ApiError {
|
|
414
|
-
constructor(data, options) {
|
|
415
|
-
super(data, options);
|
|
416
|
-
}
|
|
417
1420
|
};
|
|
418
1421
|
var UnauthorizedError = class extends ApiError {
|
|
419
|
-
constructor(data, options) {
|
|
420
|
-
super(data, options);
|
|
421
|
-
}
|
|
422
1422
|
};
|
|
423
1423
|
var ForbiddenError = class extends ApiError {
|
|
424
|
-
constructor(data, options) {
|
|
425
|
-
super(data, options);
|
|
426
|
-
}
|
|
427
1424
|
};
|
|
428
1425
|
var NotFoundError = class extends ApiError {
|
|
429
|
-
constructor(data, options) {
|
|
430
|
-
super(data, options);
|
|
431
|
-
}
|
|
432
1426
|
};
|
|
433
1427
|
var RequestTimeoutError = class extends ApiError {
|
|
434
|
-
constructor(data, options) {
|
|
435
|
-
super(data, options);
|
|
436
|
-
}
|
|
437
1428
|
};
|
|
438
1429
|
var ConflictError = class extends ApiError {
|
|
439
|
-
constructor(data, options) {
|
|
440
|
-
super(data, options);
|
|
441
|
-
}
|
|
442
1430
|
};
|
|
443
1431
|
var ServerError = class extends ApiError {
|
|
444
|
-
constructor(data, options) {
|
|
445
|
-
super(data, options);
|
|
446
|
-
}
|
|
447
1432
|
};
|
|
448
1433
|
var MiscellaneousError = class extends ApiError {
|
|
449
|
-
constructor(data, options) {
|
|
450
|
-
super(data, options);
|
|
451
|
-
}
|
|
452
1434
|
};
|
|
453
1435
|
var MissingResponseError = class extends Error {
|
|
454
1436
|
constructor(message, options) {
|
|
@@ -456,63 +1438,58 @@ var MissingResponseError = class extends Error {
|
|
|
456
1438
|
this.name = "MissingResponseError";
|
|
457
1439
|
}
|
|
458
1440
|
};
|
|
459
|
-
function parseErrorData(data,
|
|
460
|
-
if (!data)
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
1441
|
+
function parseErrorData(data, statusCode) {
|
|
1442
|
+
if (!data) return void 0;
|
|
1443
|
+
if (typeof data === "object" && data !== null) {
|
|
1444
|
+
const obj = data;
|
|
1445
|
+
if (obj.statusCode && obj.error && obj.message) {
|
|
1446
|
+
return {
|
|
1447
|
+
statusCode: obj.statusCode,
|
|
1448
|
+
error: obj.error,
|
|
1449
|
+
message: obj.message,
|
|
1450
|
+
errorCode: obj.errorCode
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
|
+
if (obj.error && obj.message) {
|
|
1454
|
+
return {
|
|
1455
|
+
statusCode,
|
|
1456
|
+
error: obj.error,
|
|
1457
|
+
message: obj.message,
|
|
1458
|
+
errorCode: obj.errorCode
|
|
1459
|
+
};
|
|
1460
|
+
}
|
|
1461
|
+
if (obj.error) {
|
|
1462
|
+
return {
|
|
1463
|
+
statusCode,
|
|
1464
|
+
error: obj.error,
|
|
1465
|
+
message: obj.error
|
|
1466
|
+
};
|
|
1467
|
+
}
|
|
1468
|
+
if (obj.message) {
|
|
1469
|
+
return {
|
|
1470
|
+
statusCode,
|
|
1471
|
+
error: obj.message,
|
|
1472
|
+
message: obj.message,
|
|
1473
|
+
errorCode: obj.errorCode
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
493
1476
|
}
|
|
494
1477
|
if (typeof data === "string") {
|
|
495
|
-
return {
|
|
496
|
-
statusCode: options.statusCode,
|
|
497
|
-
error: data,
|
|
498
|
-
message: data
|
|
499
|
-
};
|
|
1478
|
+
return { statusCode, error: data, message: data };
|
|
500
1479
|
}
|
|
501
1480
|
return void 0;
|
|
502
1481
|
}
|
|
503
|
-
function handleErrorResponse(
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
const { status, data } = err.response;
|
|
508
|
-
const errorData = parseErrorData(data, { statusCode: status });
|
|
509
|
-
if (!errorData) {
|
|
510
|
-
throw new MiscellaneousError({
|
|
511
|
-
statusCode: status,
|
|
512
|
-
error: "Unknown error",
|
|
513
|
-
message: err.message || "An error occurred"
|
|
514
|
-
});
|
|
1482
|
+
function handleErrorResponse(err2) {
|
|
1483
|
+
const axiosError = err2;
|
|
1484
|
+
if (!axiosError.response) {
|
|
1485
|
+
throw new MissingResponseError(axiosError.message || "Network error");
|
|
515
1486
|
}
|
|
1487
|
+
const { status, data } = axiosError.response;
|
|
1488
|
+
const errorData = parseErrorData(data, status) ?? {
|
|
1489
|
+
statusCode: status,
|
|
1490
|
+
error: "Unknown error",
|
|
1491
|
+
message: axiosError.message || "An error occurred"
|
|
1492
|
+
};
|
|
516
1493
|
switch (status) {
|
|
517
1494
|
case 400:
|
|
518
1495
|
throw new ValidationError(errorData);
|
|
@@ -534,132 +1511,326 @@ function handleErrorResponse(err) {
|
|
|
534
1511
|
}
|
|
535
1512
|
}
|
|
536
1513
|
|
|
537
|
-
//
|
|
538
|
-
function
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
1514
|
+
// src/api/client.ts
|
|
1515
|
+
function createClient(options) {
|
|
1516
|
+
const http = axios.create({
|
|
1517
|
+
baseURL: options.baseUrl,
|
|
1518
|
+
timeout: 12e4,
|
|
1519
|
+
maxContentLength: Number.POSITIVE_INFINITY,
|
|
1520
|
+
maxBodyLength: Number.POSITIVE_INFINITY
|
|
1521
|
+
});
|
|
1522
|
+
http.interceptors.request.use((config) => {
|
|
543
1523
|
if (config.headers) {
|
|
544
|
-
config.headers.Authorization = `Bearer ${apiKey}`;
|
|
545
|
-
config.headers["x-checkly-account"] = accountId;
|
|
546
|
-
config.headers["User-Agent"] = `@checkly/playwright-reporter/${getVersion()}`;
|
|
1524
|
+
config.headers.Authorization = `Bearer ${options.apiKey}`;
|
|
1525
|
+
config.headers["x-checkly-account"] = options.accountId;
|
|
547
1526
|
}
|
|
548
1527
|
return config;
|
|
549
|
-
};
|
|
1528
|
+
});
|
|
1529
|
+
http.interceptors.response.use(
|
|
1530
|
+
(response) => response,
|
|
1531
|
+
(error) => handleErrorResponse(error)
|
|
1532
|
+
);
|
|
1533
|
+
return {
|
|
1534
|
+
testSessions: {
|
|
1535
|
+
async create(request) {
|
|
1536
|
+
const response = await http.post("/next/test-sessions/create", request);
|
|
1537
|
+
return response.data;
|
|
1538
|
+
},
|
|
1539
|
+
async uploadAsset(testSessionId, testResultId, assets) {
|
|
1540
|
+
const form = new FormData();
|
|
1541
|
+
form.append("assets", assets, { filename: "assets.zip", contentType: "application/zip" });
|
|
1542
|
+
const response = await http.post(
|
|
1543
|
+
`/next/test-sessions/${testSessionId}/results/${testResultId}/assets`,
|
|
1544
|
+
form,
|
|
1545
|
+
{ headers: form.getHeaders() }
|
|
1546
|
+
);
|
|
1547
|
+
return response.data;
|
|
1548
|
+
},
|
|
1549
|
+
async updateResult(testSessionId, testResultId, request) {
|
|
1550
|
+
const response = await http.post(
|
|
1551
|
+
`/next/test-sessions/${testSessionId}/results/${testResultId}`,
|
|
1552
|
+
request
|
|
1553
|
+
);
|
|
1554
|
+
return response.data;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
};
|
|
550
1558
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
1559
|
+
|
|
1560
|
+
// src/extensions/checkly-upload.ts
|
|
1561
|
+
function getApiUrl(environment) {
|
|
1562
|
+
const urls = {
|
|
1563
|
+
local: "http://127.0.0.1:3000",
|
|
1564
|
+
development: "https://api-dev.checklyhq.com",
|
|
1565
|
+
staging: "https://api-test.checklyhq.com",
|
|
1566
|
+
production: "https://api.checklyhq.com"
|
|
554
1567
|
};
|
|
1568
|
+
return urls[environment];
|
|
555
1569
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
this.accountId = options.accountId;
|
|
563
|
-
this.apiKey = options.apiKey;
|
|
564
|
-
this.baseUrl = options.baseUrl;
|
|
565
|
-
this.api = axios.create({
|
|
566
|
-
baseURL: this.baseUrl,
|
|
567
|
-
timeout: 12e4,
|
|
568
|
-
// 120 second timeout for large uploads
|
|
569
|
-
maxContentLength: Number.POSITIVE_INFINITY,
|
|
570
|
-
// Allow large payloads
|
|
571
|
-
maxBodyLength: Number.POSITIVE_INFINITY
|
|
572
|
-
// Allow large request bodies
|
|
573
|
-
});
|
|
574
|
-
this.api.interceptors.request.use(createRequestInterceptor(this.apiKey, this.accountId));
|
|
575
|
-
this.api.interceptors.response.use((response) => response, createResponseErrorInterceptor());
|
|
1570
|
+
function getEnvironment(options) {
|
|
1571
|
+
const env = options.environment || process.env.CHECKLY_ENV || "production";
|
|
1572
|
+
const valid = ["local", "development", "staging", "production"];
|
|
1573
|
+
if (!valid.includes(env)) {
|
|
1574
|
+
console.warn(`[Checkly] Invalid environment "${env}", using "production"`);
|
|
1575
|
+
return "production";
|
|
576
1576
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
1577
|
+
return env;
|
|
1578
|
+
}
|
|
1579
|
+
function getDirectoryName() {
|
|
1580
|
+
const cwd = process.cwd();
|
|
1581
|
+
let name = path3.basename(cwd);
|
|
1582
|
+
if (!name || name === "/" || name === ".") name = "playwright-tests";
|
|
1583
|
+
name = name.replace(/[<>:"|?*]/g, "-");
|
|
1584
|
+
if (name.length > 255) name = name.substring(0, 255);
|
|
1585
|
+
return name;
|
|
1586
|
+
}
|
|
1587
|
+
function checklyUpload(options = {}) {
|
|
1588
|
+
const apiKey = process.env.CHECKLY_API_KEY || options.apiKey;
|
|
1589
|
+
const accountId = process.env.CHECKLY_ACCOUNT_ID || options.accountId;
|
|
1590
|
+
const environment = getEnvironment(options);
|
|
1591
|
+
const baseUrl = getApiUrl(environment);
|
|
1592
|
+
const dryRun = options.dryRun ?? false;
|
|
1593
|
+
if (options.testResultsDir) {
|
|
1594
|
+
console.warn("[Checkly] Warning: testResultsDir is deprecated, use outputDir instead");
|
|
583
1595
|
}
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
// ../clients/src/test-results.ts
|
|
587
|
-
import FormData from "form-data";
|
|
588
|
-
var TestResults = class {
|
|
589
|
-
constructor(api) {
|
|
590
|
-
this.api = api;
|
|
1596
|
+
if (options.outputPath) {
|
|
1597
|
+
console.warn("[Checkly] Warning: outputPath is deprecated, ZIP is now written to {outputDir}/checkly-report.zip");
|
|
591
1598
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
1599
|
+
let api;
|
|
1600
|
+
let testSession;
|
|
1601
|
+
let startTime;
|
|
1602
|
+
let resolvedOutputDir;
|
|
1603
|
+
let assetCollector;
|
|
1604
|
+
let zipper;
|
|
1605
|
+
const testCounts = { passed: 0, failed: 0, flaky: 0 };
|
|
1606
|
+
const tracePathsMap = /* @__PURE__ */ new Map();
|
|
1607
|
+
const warningsMap = /* @__PURE__ */ new Map();
|
|
1608
|
+
const consoleMessagesMap = /* @__PURE__ */ new Map();
|
|
1609
|
+
const networkRequestsMap = /* @__PURE__ */ new Map();
|
|
1610
|
+
if (!dryRun && apiKey && accountId) {
|
|
1611
|
+
api = createClient({ apiKey, accountId, baseUrl });
|
|
604
1612
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
1613
|
+
function resolveSessionName(ctx) {
|
|
1614
|
+
const { sessionName } = options;
|
|
1615
|
+
if (typeof sessionName === "function") return sessionName(ctx);
|
|
1616
|
+
if (typeof sessionName === "string") return sessionName;
|
|
1617
|
+
return `Playwright Test Session: ${ctx.directoryName}`;
|
|
1618
|
+
}
|
|
1619
|
+
function checkTraceAttachment(test, result) {
|
|
1620
|
+
const key = `${test.id}:${result.retry}`;
|
|
1621
|
+
const traceAttachment = result.attachments?.find((a) => a.name === "trace" || a.contentType === "application/zip");
|
|
1622
|
+
if (traceAttachment?.path) {
|
|
1623
|
+
tracePathsMap.set(key, traceAttachment.path);
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
const traceConfig = test.parent?.project()?.use?.trace;
|
|
1627
|
+
const traceMode = typeof traceConfig === "object" ? traceConfig.mode : traceConfig;
|
|
1628
|
+
if (!traceMode || traceMode === "off") return;
|
|
1629
|
+
const warnings = warningsMap.get(key) || [];
|
|
1630
|
+
warnings.push({ type: "trace-missing", message: `No trace found for trace mode "${traceMode}"` });
|
|
1631
|
+
warningsMap.set(key, warnings);
|
|
1632
|
+
}
|
|
1633
|
+
async function extractTraceData(key, tracePath) {
|
|
1634
|
+
const reader = new TraceReader(tracePath);
|
|
1635
|
+
if (!await reader.open()) return;
|
|
1636
|
+
const messages = await reader.extractEvents('"type":"console"', toConsoleMessage);
|
|
1637
|
+
if (messages.length > 0) consoleMessagesMap.set(key, messages);
|
|
1638
|
+
const requests = await reader.extractEvents('"type":"resource-snapshot"', toNetworkRequest);
|
|
1639
|
+
if (requests.length > 0) networkRequestsMap.set(key, requests);
|
|
1640
|
+
}
|
|
1641
|
+
async function extractDataFromTraces(log) {
|
|
1642
|
+
if (tracePathsMap.size === 0) return;
|
|
1643
|
+
log("\u{1F4E6} Extracting trace data", { traces: tracePathsMap.size });
|
|
1644
|
+
const extractions = Array.from(tracePathsMap.entries()).map(
|
|
1645
|
+
([key, tracePath]) => extractTraceData(key, tracePath).catch((err2) => {
|
|
1646
|
+
console.error(`[Checkly] Failed to extract trace data: ${err2}`);
|
|
1647
|
+
})
|
|
1648
|
+
);
|
|
1649
|
+
await Promise.all(extractions);
|
|
1650
|
+
}
|
|
1651
|
+
function buildChecklyExtensionData(key) {
|
|
1652
|
+
const warnings = warningsMap.get(key);
|
|
1653
|
+
const consoleMessages = consoleMessagesMap.get(key);
|
|
1654
|
+
const network = networkRequestsMap.get(key);
|
|
1655
|
+
if (!warnings?.length && !consoleMessages?.length && !network?.length) {
|
|
1656
|
+
return null;
|
|
1657
|
+
}
|
|
1658
|
+
return {
|
|
1659
|
+
...warnings?.length ? { warnings } : {},
|
|
1660
|
+
...consoleMessages?.length ? { console: consoleMessages } : {},
|
|
1661
|
+
...network?.length ? { network } : {}
|
|
1662
|
+
};
|
|
1663
|
+
}
|
|
1664
|
+
function injectDataIntoReport(report) {
|
|
1665
|
+
function processSuite(suite) {
|
|
1666
|
+
for (const spec of suite.specs) {
|
|
1667
|
+
for (const test of spec.tests) {
|
|
1668
|
+
for (const result of test.results) {
|
|
1669
|
+
const key = `${spec.id}:${result.retry}`;
|
|
1670
|
+
const checklyData = buildChecklyExtensionData(key);
|
|
1671
|
+
if (checklyData) {
|
|
1672
|
+
;
|
|
1673
|
+
result._checkly = checklyData;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
631
1676
|
}
|
|
632
1677
|
}
|
|
633
|
-
|
|
634
|
-
|
|
1678
|
+
suite.suites?.forEach(processSuite);
|
|
1679
|
+
}
|
|
1680
|
+
report.suites.forEach(processSuite);
|
|
635
1681
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
);
|
|
654
|
-
return response.data;
|
|
1682
|
+
function calculateTiming(report) {
|
|
1683
|
+
const stats = report.stats;
|
|
1684
|
+
if (stats?.startTime && stats?.duration !== void 0) {
|
|
1685
|
+
const start = new Date(stats.startTime);
|
|
1686
|
+
const responseTime = Math.round(stats.duration);
|
|
1687
|
+
return {
|
|
1688
|
+
startedAt: start.toISOString(),
|
|
1689
|
+
stoppedAt: new Date(start.getTime() + responseTime).toISOString(),
|
|
1690
|
+
responseTime
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
const now = Date.now();
|
|
1694
|
+
return {
|
|
1695
|
+
startedAt: startTime?.toISOString() ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
1696
|
+
stoppedAt: new Date(now).toISOString(),
|
|
1697
|
+
responseTime: startTime ? Math.round(now - startTime.getTime()) : 0
|
|
1698
|
+
};
|
|
655
1699
|
}
|
|
656
|
-
|
|
1700
|
+
async function uploadAssets(log, zipPath, zipSizeBytes) {
|
|
1701
|
+
if (!api || !testSession || zipSizeBytes === 0) return void 0;
|
|
1702
|
+
const firstResult = testSession.testResults[0];
|
|
1703
|
+
if (!firstResult) return void 0;
|
|
1704
|
+
log("\u{1F4E4} Uploading assets", { size: `${(zipSizeBytes / 1024).toFixed(1)}KB` });
|
|
1705
|
+
try {
|
|
1706
|
+
const assets = fs4.createReadStream(zipPath);
|
|
1707
|
+
const uploadResponse = await api.testSessions.uploadAsset(
|
|
1708
|
+
testSession.testSessionId,
|
|
1709
|
+
firstResult.testResultId,
|
|
1710
|
+
assets
|
|
1711
|
+
);
|
|
1712
|
+
log("\u2705 Assets uploaded", { assetId: uploadResponse.assetId });
|
|
1713
|
+
return uploadResponse.assetId;
|
|
1714
|
+
} catch (err2) {
|
|
1715
|
+
console.error("[Checkly] Asset upload failed:", err2);
|
|
1716
|
+
return void 0;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
async function uploadResults(log, report, zipPath, entries) {
|
|
1720
|
+
if (!api || !testSession || testSession.testResults.length === 0) return;
|
|
1721
|
+
try {
|
|
1722
|
+
const { failed, flaky } = testCounts;
|
|
1723
|
+
const overallStatus = failed > 0 ? "FAILED" : "PASSED";
|
|
1724
|
+
const isDegraded = failed === 0 && flaky > 0;
|
|
1725
|
+
const timing = calculateTiming(report);
|
|
1726
|
+
const zipSizeBytes = (await fs4.promises.stat(zipPath)).size;
|
|
1727
|
+
const assetId = await uploadAssets(log, zipPath, zipSizeBytes);
|
|
1728
|
+
const firstResult = testSession.testResults[0];
|
|
1729
|
+
log("\u{1F4DD} Updating test result", { status: overallStatus, isDegraded });
|
|
1730
|
+
await api.testSessions.updateResult(testSession.testSessionId, firstResult.testResultId, {
|
|
1731
|
+
status: overallStatus,
|
|
1732
|
+
assetEntries: assetId ? entries : void 0,
|
|
1733
|
+
isDegraded,
|
|
1734
|
+
...timing,
|
|
1735
|
+
metadata: { usageData: { s3PostTotalBytes: zipSizeBytes } }
|
|
1736
|
+
});
|
|
1737
|
+
} catch (err2) {
|
|
1738
|
+
console.error("[Checkly] Failed to upload results:", err2);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
return {
|
|
1742
|
+
name: "checkly-upload",
|
|
1743
|
+
onBegin: async ({ config, suite, log }) => {
|
|
1744
|
+
startTime = /* @__PURE__ */ new Date();
|
|
1745
|
+
resolvedOutputDir = options.outputDir ?? options.testResultsDir ?? config.projects[0]?.outputDir ?? "test-results";
|
|
1746
|
+
const zipPath = options.outputPath ?? path3.join(resolvedOutputDir, "checkly-report.zip");
|
|
1747
|
+
assetCollector = new AssetCollector(resolvedOutputDir);
|
|
1748
|
+
zipper = new Zipper({ outputPath: zipPath });
|
|
1749
|
+
if (!api) {
|
|
1750
|
+
if (!dryRun && (!apiKey || !accountId)) {
|
|
1751
|
+
log("\u26A0\uFE0F Skipping upload (missing API credentials)");
|
|
1752
|
+
}
|
|
1753
|
+
return;
|
|
1754
|
+
}
|
|
1755
|
+
try {
|
|
1756
|
+
const directoryName = getDirectoryName();
|
|
1757
|
+
const sessionName = resolveSessionName({ directoryName, config, suite });
|
|
1758
|
+
const repoInfo = getGitHubRepoInfo();
|
|
1759
|
+
log("\u{1F517} Creating test session", { name: sessionName, environment });
|
|
1760
|
+
testSession = await api.testSessions.create({
|
|
1761
|
+
name: sessionName,
|
|
1762
|
+
environment: process.env.NODE_ENV || "test",
|
|
1763
|
+
repoInfo,
|
|
1764
|
+
startedAt: startTime.getTime(),
|
|
1765
|
+
testResults: [{ name: directoryName }],
|
|
1766
|
+
provider: "PW_REPORTER"
|
|
1767
|
+
});
|
|
1768
|
+
log("\u2705 Session created", { id: testSession.testSessionId });
|
|
1769
|
+
} catch (err2) {
|
|
1770
|
+
console.error("[Checkly] Failed to create test session:", err2);
|
|
1771
|
+
}
|
|
1772
|
+
},
|
|
1773
|
+
onTestEnd: ({ test, result }) => {
|
|
1774
|
+
checkTraceAttachment(test, result);
|
|
1775
|
+
const outcome = test.outcome();
|
|
1776
|
+
const testIsComplete = result.retry === test.retries || outcome !== "unexpected";
|
|
1777
|
+
if (!testIsComplete) return;
|
|
1778
|
+
if (outcome === "flaky") {
|
|
1779
|
+
testCounts.flaky++;
|
|
1780
|
+
testCounts.passed++;
|
|
1781
|
+
} else if (result.status === "passed") {
|
|
1782
|
+
testCounts.passed++;
|
|
1783
|
+
} else if (result.status === "failed" || result.status === "timedOut") {
|
|
1784
|
+
testCounts.failed++;
|
|
1785
|
+
}
|
|
1786
|
+
},
|
|
1787
|
+
onEnd: async ({ report, log, addSummaryLine }) => {
|
|
1788
|
+
try {
|
|
1789
|
+
await extractDataFromTraces(log);
|
|
1790
|
+
injectDataIntoReport(report);
|
|
1791
|
+
fs4.mkdirSync(resolvedOutputDir, { recursive: true });
|
|
1792
|
+
const tempReportPath = path3.join(resolvedOutputDir, `.checkly-report-${Date.now()}.json`);
|
|
1793
|
+
fs4.writeFileSync(tempReportPath, JSON.stringify(report, null, 2));
|
|
1794
|
+
const assets = await assetCollector.collectAssets(report);
|
|
1795
|
+
const zipResult = await zipper.createZip(tempReportPath, assets);
|
|
1796
|
+
log("\u{1F4E6} ZIP created", { path: zipResult.zipPath, size: `${(zipResult.size / 1024).toFixed(1)}KB` });
|
|
1797
|
+
if (api && testSession) {
|
|
1798
|
+
await uploadResults(log, report, zipResult.zipPath, zipResult.entries);
|
|
1799
|
+
if (!dryRun) {
|
|
1800
|
+
try {
|
|
1801
|
+
fs4.unlinkSync(zipResult.zipPath);
|
|
1802
|
+
} catch {
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
if (testSession.link) {
|
|
1806
|
+
addSummaryLine(`\u{1F517} Checkly Test Session: ${testSession.link}`);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
try {
|
|
1810
|
+
fs4.unlinkSync(tempReportPath);
|
|
1811
|
+
} catch {
|
|
1812
|
+
}
|
|
1813
|
+
} catch (err2) {
|
|
1814
|
+
console.error("[Checkly] Error in onEnd:", err2);
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
};
|
|
1818
|
+
}
|
|
657
1819
|
|
|
658
1820
|
// src/reporter.ts
|
|
1821
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1822
|
+
import { dirname, join as join4 } from "path";
|
|
1823
|
+
import { fileURLToPath } from "url";
|
|
659
1824
|
var __filename = fileURLToPath(import.meta.url);
|
|
660
1825
|
var __dirname = dirname(__filename);
|
|
661
|
-
|
|
662
|
-
|
|
1826
|
+
function getPackageVersion() {
|
|
1827
|
+
try {
|
|
1828
|
+
const packageJson = JSON.parse(readFileSync3(join4(__dirname, "..", "package.json"), "utf-8"));
|
|
1829
|
+
return packageJson.version;
|
|
1830
|
+
} catch {
|
|
1831
|
+
return "unknown";
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
663
1834
|
var pluralRules = new Intl.PluralRules("en-US");
|
|
664
1835
|
var projectForms = {
|
|
665
1836
|
zero: "Project",
|
|
@@ -669,426 +1840,470 @@ var projectForms = {
|
|
|
669
1840
|
many: "Projects",
|
|
670
1841
|
other: "Projects"
|
|
671
1842
|
};
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
1843
|
+
var BaseReporter = class {
|
|
1844
|
+
config;
|
|
1845
|
+
suite;
|
|
1846
|
+
startTime;
|
|
1847
|
+
options;
|
|
1848
|
+
verbose;
|
|
1849
|
+
globalErrors = [];
|
|
1850
|
+
tests = /* @__PURE__ */ new Map();
|
|
1851
|
+
expectedCount = 0;
|
|
1852
|
+
unexpectedCount = 0;
|
|
1853
|
+
flakyCount = 0;
|
|
1854
|
+
skippedCount = 0;
|
|
1855
|
+
_report = null;
|
|
1856
|
+
extensions = [];
|
|
1857
|
+
summaryLines = [];
|
|
1858
|
+
constructor(options = {}) {
|
|
1859
|
+
this.options = options;
|
|
1860
|
+
this.verbose = options.verbose ?? process.env.CHECKLY_REPORTER_VERBOSE === "true";
|
|
689
1861
|
}
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
return {
|
|
694
|
-
title: step.title,
|
|
695
|
-
duration: step.duration,
|
|
696
|
-
error: step.error,
|
|
697
|
-
steps: step.steps.length > 0 ? step.steps.map(convertStepToJSON) : void 0
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
function getDirectoryName() {
|
|
701
|
-
const cwd = process.cwd();
|
|
702
|
-
let dirName = path3.basename(cwd);
|
|
703
|
-
if (!dirName || dirName === "/" || dirName === ".") {
|
|
704
|
-
dirName = "playwright-tests";
|
|
1862
|
+
use(extension) {
|
|
1863
|
+
this.extensions.push(extension);
|
|
1864
|
+
return this;
|
|
705
1865
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
1866
|
+
log(message, data) {
|
|
1867
|
+
if (!this.verbose) return;
|
|
1868
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().split("T")[1].slice(0, 12);
|
|
1869
|
+
const prefix = `[Checkly ${timestamp}]`;
|
|
1870
|
+
if (data && Object.keys(data).length > 0) {
|
|
1871
|
+
console.log(`${prefix} ${message}`, data);
|
|
1872
|
+
} else {
|
|
1873
|
+
console.log(`${prefix} ${message}`);
|
|
1874
|
+
}
|
|
709
1875
|
}
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
options;
|
|
714
|
-
assetCollector;
|
|
715
|
-
zipper;
|
|
716
|
-
testResults;
|
|
717
|
-
testSession;
|
|
718
|
-
startTime;
|
|
719
|
-
testCounts = {
|
|
720
|
-
passed: 0,
|
|
721
|
-
failed: 0,
|
|
722
|
-
flaky: 0
|
|
723
|
-
};
|
|
724
|
-
// Store steps per test result, keyed by "testId:retry"
|
|
725
|
-
stepsMap = /* @__PURE__ */ new Map();
|
|
726
|
-
// Store warnings per test result, keyed by "testId:retry"
|
|
727
|
-
warningsMap = /* @__PURE__ */ new Map();
|
|
728
|
-
constructor(options = {}) {
|
|
729
|
-
const environment = getEnvironment(options);
|
|
730
|
-
const baseUrl = getApiUrl(environment);
|
|
731
|
-
const apiKey = process.env.CHECKLY_API_KEY || options.apiKey;
|
|
732
|
-
const accountId = process.env.CHECKLY_ACCOUNT_ID || options.accountId;
|
|
733
|
-
this.options = {
|
|
734
|
-
accountId,
|
|
735
|
-
apiKey,
|
|
736
|
-
outputPath: options.outputPath ?? "checkly-report.zip",
|
|
737
|
-
jsonReportPath: options.jsonReportPath ?? "test-results/playwright-test-report.json",
|
|
738
|
-
testResultsDir: options.testResultsDir ?? "test-results",
|
|
739
|
-
dryRun: options.dryRun ?? false,
|
|
740
|
-
sessionName: options.sessionName
|
|
1876
|
+
createExtensionLogger(extensionName) {
|
|
1877
|
+
return (message, data) => {
|
|
1878
|
+
this.log(`[${extensionName}] ${message}`, data);
|
|
741
1879
|
};
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
1880
|
+
}
|
|
1881
|
+
onBegin(config, suite) {
|
|
1882
|
+
this.config = config;
|
|
1883
|
+
this.suite = suite;
|
|
1884
|
+
this.startTime = /* @__PURE__ */ new Date();
|
|
1885
|
+
this.tests.clear();
|
|
1886
|
+
this.globalErrors = [];
|
|
1887
|
+
this.expectedCount = 0;
|
|
1888
|
+
this.unexpectedCount = 0;
|
|
1889
|
+
this.flakyCount = 0;
|
|
1890
|
+
this.skippedCount = 0;
|
|
1891
|
+
this._report = null;
|
|
1892
|
+
const testCount = this.countTests(suite);
|
|
1893
|
+
const projectNames = config.projects.map((p) => p.name).join(", ");
|
|
1894
|
+
this.log(`\u{1F3AC} Starting test run`, { tests: testCount, projects: projectNames, workers: config.workers });
|
|
1895
|
+
for (const ext of this.extensions) {
|
|
1896
|
+
ext.onBegin?.({ config, suite, log: this.createExtensionLogger(ext.name) });
|
|
753
1897
|
}
|
|
754
1898
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
*/
|
|
759
|
-
resolveSessionName(context) {
|
|
760
|
-
const { sessionName } = this.options;
|
|
761
|
-
if (typeof sessionName === "function") {
|
|
762
|
-
return sessionName(context);
|
|
1899
|
+
onTestBegin(test, result) {
|
|
1900
|
+
for (const ext of this.extensions) {
|
|
1901
|
+
ext.onTestBegin?.({ test, result, log: this.createExtensionLogger(ext.name) });
|
|
763
1902
|
}
|
|
764
|
-
|
|
765
|
-
|
|
1903
|
+
}
|
|
1904
|
+
onStepBegin(test, result, step) {
|
|
1905
|
+
for (const ext of this.extensions) {
|
|
1906
|
+
ext.onStepBegin?.({ test, result, step, log: this.createExtensionLogger(ext.name) });
|
|
766
1907
|
}
|
|
767
|
-
return `Playwright Test Session: ${context.directoryName}`;
|
|
768
1908
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
*/
|
|
773
|
-
checkTraceAttachment(test, result) {
|
|
774
|
-
const warningsKey = `${test.id}:${result.retry}`;
|
|
775
|
-
const hasTrace = result.attachments?.some(
|
|
776
|
-
(attachment) => attachment.name === "trace" || attachment.contentType === "application/zip"
|
|
777
|
-
);
|
|
778
|
-
if (hasTrace) {
|
|
779
|
-
return;
|
|
1909
|
+
onStepEnd(test, result, step) {
|
|
1910
|
+
for (const ext of this.extensions) {
|
|
1911
|
+
ext.onStepEnd?.({ test, result, step, log: this.createExtensionLogger(ext.name) });
|
|
780
1912
|
}
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
message = 'Traces are disabled. Set trace: "on" in playwright.config.ts to capture traces.';
|
|
793
|
-
break;
|
|
794
|
-
case "retain-on-failure":
|
|
795
|
-
if (testPassed) {
|
|
796
|
-
warningType = "trace-retained-on-failure";
|
|
797
|
-
message = 'No trace retained because test passed. Trace mode is "retain-on-failure" which discards traces for passing tests.';
|
|
798
|
-
} else {
|
|
799
|
-
warningType = "trace-missing";
|
|
800
|
-
message = 'Trace should exist but was not found. The test failed with trace: "retain-on-failure".';
|
|
801
|
-
}
|
|
802
|
-
break;
|
|
803
|
-
case "on-first-retry":
|
|
804
|
-
if (!isRetry) {
|
|
805
|
-
warningType = "trace-first-retry-only";
|
|
806
|
-
message = 'No trace for initial attempt. Trace mode is "on-first-retry" which only records traces on the first retry.';
|
|
807
|
-
} else if (result.retry === 1) {
|
|
808
|
-
warningType = "trace-missing";
|
|
809
|
-
message = 'Trace should exist but was not found. This is the first retry with trace: "on-first-retry".';
|
|
810
|
-
} else {
|
|
811
|
-
warningType = "trace-first-retry-only";
|
|
812
|
-
message = `No trace for retry #${result.retry}. Trace mode is "on-first-retry" which only records the first retry.`;
|
|
813
|
-
}
|
|
814
|
-
break;
|
|
815
|
-
case "on-all-retries":
|
|
816
|
-
if (!isRetry) {
|
|
817
|
-
warningType = "trace-retries-only";
|
|
818
|
-
message = 'No trace for initial attempt. Trace mode is "on-all-retries" which only records traces on retries.';
|
|
819
|
-
} else {
|
|
820
|
-
warningType = "trace-missing";
|
|
821
|
-
message = `Trace should exist but was not found. This is retry #${result.retry} with trace: "on-all-retries".`;
|
|
822
|
-
}
|
|
823
|
-
break;
|
|
824
|
-
case "retain-on-first-failure":
|
|
825
|
-
if (testPassed) {
|
|
826
|
-
warningType = "trace-retained-on-first-failure";
|
|
827
|
-
message = 'No trace retained because test passed. Trace mode is "retain-on-first-failure" which discards traces for passing tests.';
|
|
828
|
-
} else if (isRetry) {
|
|
829
|
-
warningType = "trace-retained-on-first-failure";
|
|
830
|
-
message = 'No trace for retries. Trace mode is "retain-on-first-failure" which only records the first run.';
|
|
831
|
-
} else {
|
|
832
|
-
warningType = "trace-missing";
|
|
833
|
-
message = 'Trace should exist but was not found. The test failed on first run with trace: "retain-on-first-failure".';
|
|
834
|
-
}
|
|
835
|
-
break;
|
|
836
|
-
case "on":
|
|
837
|
-
warningType = "trace-missing";
|
|
838
|
-
message = 'Trace should exist but was not found. Trace mode is "on" which should always record traces.';
|
|
839
|
-
break;
|
|
840
|
-
default:
|
|
841
|
-
warningType = "trace-missing";
|
|
842
|
-
message = `No trace found. Trace mode "${traceMode}" may not be generating traces for this result.`;
|
|
1913
|
+
}
|
|
1914
|
+
onTestEnd(test, result) {
|
|
1915
|
+
const testId = test.id;
|
|
1916
|
+
let testData = this.tests.get(testId);
|
|
1917
|
+
if (!testData) {
|
|
1918
|
+
testData = { testCase: test, results: [] };
|
|
1919
|
+
this.tests.set(testId, testData);
|
|
1920
|
+
}
|
|
1921
|
+
testData.results.push(result);
|
|
1922
|
+
for (const ext of this.extensions) {
|
|
1923
|
+
ext.onTestEnd?.({ test, result, log: this.createExtensionLogger(ext.name) });
|
|
843
1924
|
}
|
|
844
|
-
const warnings = this.warningsMap.get(warningsKey) || [];
|
|
845
|
-
warnings.push({ type: warningType, message });
|
|
846
|
-
this.warningsMap.set(warningsKey, warnings);
|
|
847
1925
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
*/
|
|
852
|
-
onBegin(config, suite) {
|
|
853
|
-
this.startTime = /* @__PURE__ */ new Date();
|
|
854
|
-
if (!this.testResults) {
|
|
855
|
-
return;
|
|
1926
|
+
onStdOut(chunk, test, result) {
|
|
1927
|
+
for (const ext of this.extensions) {
|
|
1928
|
+
ext.onStdOut?.({ chunk, test, result, log: this.createExtensionLogger(ext.name) });
|
|
856
1929
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
1930
|
+
}
|
|
1931
|
+
onStdErr(chunk, test, result) {
|
|
1932
|
+
for (const ext of this.extensions) {
|
|
1933
|
+
ext.onStdErr?.({ chunk, test, result, log: this.createExtensionLogger(ext.name) });
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
onError(error) {
|
|
1937
|
+
this.globalErrors.push(error);
|
|
1938
|
+
this.log(`\u{1F4A5} Global error`, { message: error.message?.slice(0, 100) });
|
|
1939
|
+
for (const ext of this.extensions) {
|
|
1940
|
+
ext.onError?.({ error, log: this.createExtensionLogger(ext.name) });
|
|
1941
|
+
}
|
|
1942
|
+
}
|
|
1943
|
+
async onEnd(result) {
|
|
1944
|
+
for (const testData of this.tests.values()) {
|
|
1945
|
+
switch (testData.testCase.outcome()) {
|
|
1946
|
+
case "expected":
|
|
1947
|
+
this.expectedCount++;
|
|
1948
|
+
break;
|
|
1949
|
+
case "unexpected":
|
|
1950
|
+
this.unexpectedCount++;
|
|
1951
|
+
break;
|
|
1952
|
+
case "flaky":
|
|
1953
|
+
this.flakyCount++;
|
|
1954
|
+
break;
|
|
1955
|
+
case "skipped":
|
|
1956
|
+
this.skippedCount++;
|
|
1957
|
+
break;
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
this._report = this.buildReport(result);
|
|
1961
|
+
this.log(`\u{1F3C1} Test run finished`, {
|
|
1962
|
+
status: result.status,
|
|
1963
|
+
duration: `${result.duration}ms`,
|
|
1964
|
+
passed: this.expectedCount,
|
|
1965
|
+
failed: this.unexpectedCount,
|
|
1966
|
+
flaky: this.flakyCount,
|
|
1967
|
+
skipped: this.skippedCount
|
|
1968
|
+
});
|
|
1969
|
+
const outputDir = this.options.outputDir;
|
|
1970
|
+
const outputFile = this.options.outputFile ?? process.env.PLAYWRIGHT_JSON_OUTPUT_NAME;
|
|
1971
|
+
if (outputFile) {
|
|
1972
|
+
console.warn("[Checkly] Warning: outputFile is deprecated, use outputDir instead");
|
|
1973
|
+
}
|
|
1974
|
+
const outputPath = outputDir ? `${outputDir}/checkly-report.json` : outputFile;
|
|
1975
|
+
if (outputPath) {
|
|
1976
|
+
const fs5 = await import("fs");
|
|
1977
|
+
const path4 = await import("path");
|
|
1978
|
+
const dir = path4.dirname(outputPath);
|
|
1979
|
+
if (dir && dir !== ".") fs5.mkdirSync(dir, { recursive: true });
|
|
1980
|
+
fs5.writeFileSync(outputPath, JSON.stringify(this._report, null, 2));
|
|
1981
|
+
this.log(`\u{1F4C4} Report saved`, { path: outputPath });
|
|
1982
|
+
}
|
|
1983
|
+
for (const ext of this.extensions) {
|
|
1984
|
+
await ext.onEnd?.({
|
|
1985
|
+
result,
|
|
1986
|
+
report: this._report,
|
|
1987
|
+
log: this.createExtensionLogger(ext.name),
|
|
1988
|
+
addSummaryLine: (line) => this.summaryLines.push(line)
|
|
881
1989
|
});
|
|
882
|
-
} catch (error) {
|
|
883
|
-
console.error("[Checkly Reporter] Error in onBegin:", error);
|
|
884
1990
|
}
|
|
1991
|
+
this.printSummary();
|
|
1992
|
+
}
|
|
1993
|
+
async onExit() {
|
|
1994
|
+
this.log(`\u{1F44B} Reporter exiting`);
|
|
1995
|
+
for (const ext of this.extensions) {
|
|
1996
|
+
await ext.onExit?.({ log: this.createExtensionLogger(ext.name) });
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
printsToStdio() {
|
|
2000
|
+
return false;
|
|
2001
|
+
}
|
|
2002
|
+
getReport() {
|
|
2003
|
+
if (this._report) return this._report;
|
|
2004
|
+
return this.buildReport({
|
|
2005
|
+
status: "passed",
|
|
2006
|
+
startTime: this.startTime,
|
|
2007
|
+
duration: Date.now() - this.startTime.getTime()
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
buildReport(fullResult) {
|
|
2011
|
+
return {
|
|
2012
|
+
config: this.serializeConfig(),
|
|
2013
|
+
suites: this.serializeSuites(),
|
|
2014
|
+
errors: this.globalErrors.map((e) => this.serializeError(e, true)),
|
|
2015
|
+
stats: {
|
|
2016
|
+
startTime: this.startTime.toISOString(),
|
|
2017
|
+
duration: fullResult.duration,
|
|
2018
|
+
expected: this.expectedCount,
|
|
2019
|
+
unexpected: this.unexpectedCount,
|
|
2020
|
+
flaky: this.flakyCount,
|
|
2021
|
+
skipped: this.skippedCount
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
}
|
|
2025
|
+
serializeConfig() {
|
|
2026
|
+
const c = this.config;
|
|
2027
|
+
return {
|
|
2028
|
+
rootDir: c.rootDir,
|
|
2029
|
+
configFile: c.configFile ?? void 0,
|
|
2030
|
+
version: c.version,
|
|
2031
|
+
workers: c.workers,
|
|
2032
|
+
fullyParallel: c.fullyParallel,
|
|
2033
|
+
forbidOnly: c.forbidOnly,
|
|
2034
|
+
globalTimeout: c.globalTimeout,
|
|
2035
|
+
maxFailures: c.maxFailures,
|
|
2036
|
+
metadata: c.metadata ?? {},
|
|
2037
|
+
projects: c.projects.map((p) => this.serializeProject(p)),
|
|
2038
|
+
shard: c.shard ?? null,
|
|
2039
|
+
tags: c.tags ?? [],
|
|
2040
|
+
updateSourceMethod: c.updateSourceMethod,
|
|
2041
|
+
preserveOutput: c.preserveOutput,
|
|
2042
|
+
quiet: c.quiet,
|
|
2043
|
+
reportSlowTests: c.reportSlowTests ?? null,
|
|
2044
|
+
webServer: c.webServer ?? null,
|
|
2045
|
+
globalSetup: c.globalSetup ?? null,
|
|
2046
|
+
globalTeardown: c.globalTeardown ?? null,
|
|
2047
|
+
grep: c.grep,
|
|
2048
|
+
grepInvert: c.grepInvert,
|
|
2049
|
+
reporter: c.reporter,
|
|
2050
|
+
updateSnapshots: c.updateSnapshots
|
|
2051
|
+
};
|
|
2052
|
+
}
|
|
2053
|
+
serializeProject(p) {
|
|
2054
|
+
return {
|
|
2055
|
+
id: p.name,
|
|
2056
|
+
name: p.name,
|
|
2057
|
+
testDir: p.testDir,
|
|
2058
|
+
outputDir: p.outputDir,
|
|
2059
|
+
timeout: p.timeout,
|
|
2060
|
+
retries: p.retries,
|
|
2061
|
+
repeatEach: p.repeatEach,
|
|
2062
|
+
metadata: p.metadata ?? {},
|
|
2063
|
+
testMatch: Array.isArray(p.testMatch) ? p.testMatch.map(String) : [String(p.testMatch)],
|
|
2064
|
+
testIgnore: Array.isArray(p.testIgnore) ? p.testIgnore.map(String) : [String(p.testIgnore)]
|
|
2065
|
+
};
|
|
885
2066
|
}
|
|
886
2067
|
/**
|
|
887
|
-
*
|
|
888
|
-
*
|
|
2068
|
+
* Serialize suites following the same order as native Playwright JSON reporter.
|
|
2069
|
+
* Iterates through suite tree structure (discovery order) rather than test completion order.
|
|
889
2070
|
*/
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
this.testCounts.flaky++;
|
|
905
|
-
this.testCounts.passed++;
|
|
906
|
-
} else {
|
|
907
|
-
if (result.status === "passed") {
|
|
908
|
-
this.testCounts.passed++;
|
|
909
|
-
} else if (result.status === "failed" || result.status === "timedOut") {
|
|
910
|
-
this.testCounts.failed++;
|
|
2071
|
+
serializeSuites() {
|
|
2072
|
+
const fileSuites = /* @__PURE__ */ new Map();
|
|
2073
|
+
for (const projectSuite of this.suite.suites) {
|
|
2074
|
+
for (const fileSuite of projectSuite.suites) {
|
|
2075
|
+
const file = fileSuite.location?.file;
|
|
2076
|
+
if (!file) continue;
|
|
2077
|
+
const fileName = this.getFileName(file);
|
|
2078
|
+
const serialized = this.serializeSuite(fileSuite, fileName);
|
|
2079
|
+
if (!serialized) continue;
|
|
2080
|
+
const existing = fileSuites.get(file);
|
|
2081
|
+
if (existing) {
|
|
2082
|
+
this.mergeSuites(existing, serialized);
|
|
2083
|
+
} else {
|
|
2084
|
+
fileSuites.set(file, serialized);
|
|
911
2085
|
}
|
|
912
2086
|
}
|
|
913
|
-
} catch (error) {
|
|
914
|
-
console.error("[Checkly Reporter] Error in onTestEnd:", error);
|
|
915
2087
|
}
|
|
2088
|
+
return Array.from(fileSuites.values());
|
|
916
2089
|
}
|
|
917
2090
|
/**
|
|
918
|
-
*
|
|
919
|
-
*
|
|
2091
|
+
* Recursively serialize a suite and its children.
|
|
2092
|
+
* Returns null if the suite has no tests.
|
|
920
2093
|
*/
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
this.injectDataIntoReport(report);
|
|
935
|
-
fs3.writeFileSync(jsonReportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
936
|
-
const assets = await this.assetCollector.collectAssets(report);
|
|
937
|
-
const result = await this.zipper.createZip(jsonReportPath, assets);
|
|
938
|
-
if (this.testResults && this.testSession) {
|
|
939
|
-
await this.uploadResults(report, result.zipPath, result.entries);
|
|
940
|
-
if (!this.options.dryRun) {
|
|
941
|
-
try {
|
|
942
|
-
fs3.unlinkSync(result.zipPath);
|
|
943
|
-
} catch (cleanupError) {
|
|
944
|
-
console.warn(`[Checkly Reporter] Warning: Could not delete ZIP file: ${cleanupError}`);
|
|
945
|
-
}
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
if (this.testResults && this.testSession?.link) {
|
|
949
|
-
this.printSummary(report, this.testSession);
|
|
2094
|
+
serializeSuite(suite, fileName) {
|
|
2095
|
+
const allTests = suite.allTests();
|
|
2096
|
+
if (allTests.length === 0) return null;
|
|
2097
|
+
const childSuites = [];
|
|
2098
|
+
for (const child of suite.suites) {
|
|
2099
|
+
const serialized = this.serializeSuite(child, fileName);
|
|
2100
|
+
if (serialized) childSuites.push(serialized);
|
|
2101
|
+
}
|
|
2102
|
+
const specs = [];
|
|
2103
|
+
for (const test of suite.tests) {
|
|
2104
|
+
const testData = this.tests.get(test.id);
|
|
2105
|
+
if (testData) {
|
|
2106
|
+
specs.push(this.serializeSpec(testData));
|
|
950
2107
|
}
|
|
951
|
-
} catch (error) {
|
|
952
|
-
console.error("[Checkly Reporter] ERROR creating report:", error);
|
|
953
2108
|
}
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
console.log("\n======================================================");
|
|
2109
|
+
return {
|
|
2110
|
+
title: suite.title || fileName,
|
|
2111
|
+
file: fileName,
|
|
2112
|
+
line: suite.location?.line ?? 0,
|
|
2113
|
+
column: suite.location?.column ?? 0,
|
|
2114
|
+
specs,
|
|
2115
|
+
suites: childSuites.length ? childSuites : void 0
|
|
2116
|
+
};
|
|
963
2117
|
}
|
|
964
2118
|
/**
|
|
965
|
-
*
|
|
966
|
-
*
|
|
2119
|
+
* Merge tests from 'from' suite into 'to' suite (for multi-project scenarios).
|
|
2120
|
+
* Matches native Playwright JSON reporter behavior - specs are NOT merged,
|
|
2121
|
+
* each project gets its own spec even for the same test.
|
|
967
2122
|
*/
|
|
968
|
-
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
const warnings = this.warningsMap.get(key);
|
|
979
|
-
if (warnings && warnings.length > 0) {
|
|
980
|
-
result._checkly = { warnings };
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
if (suite.suites) {
|
|
986
|
-
for (const nestedSuite of suite.suites) {
|
|
987
|
-
processSuite(nestedSuite);
|
|
988
|
-
}
|
|
2123
|
+
mergeSuites(to, from) {
|
|
2124
|
+
for (const fromSuite of from.suites || []) {
|
|
2125
|
+
const toSuite = to.suites?.find(
|
|
2126
|
+
(s) => s.title === fromSuite.title && s.line === fromSuite.line && s.column === fromSuite.column
|
|
2127
|
+
);
|
|
2128
|
+
if (toSuite) {
|
|
2129
|
+
this.mergeSuites(toSuite, fromSuite);
|
|
2130
|
+
} else {
|
|
2131
|
+
to.suites = to.suites ?? [];
|
|
2132
|
+
to.suites.push(fromSuite);
|
|
989
2133
|
}
|
|
990
|
-
};
|
|
991
|
-
for (const suite of report.suites) {
|
|
992
|
-
processSuite(suite);
|
|
993
2134
|
}
|
|
994
|
-
|
|
2135
|
+
to.specs.push(...from.specs);
|
|
2136
|
+
}
|
|
2137
|
+
serializeSpec(data) {
|
|
2138
|
+
const { testCase } = data;
|
|
2139
|
+
const outcome = testCase.outcome();
|
|
2140
|
+
return {
|
|
2141
|
+
id: testCase.id,
|
|
2142
|
+
title: testCase.title,
|
|
2143
|
+
file: this.getFileName(testCase.location.file),
|
|
2144
|
+
line: testCase.location.line,
|
|
2145
|
+
column: testCase.location.column,
|
|
2146
|
+
tags: this.extractTags(testCase),
|
|
2147
|
+
ok: outcome === "expected" || outcome === "flaky" || outcome === "skipped",
|
|
2148
|
+
tests: [this.serializeTest(data)]
|
|
2149
|
+
};
|
|
995
2150
|
}
|
|
996
2151
|
/**
|
|
997
|
-
*
|
|
998
|
-
*
|
|
999
|
-
* doesn't populate projects array or projectId fields
|
|
2152
|
+
* Extract tags from test case - uses testCase.tags (1.42+) or extracts @tags from title
|
|
2153
|
+
* Tags are returned without @ prefix to match native Playwright JSON reporter
|
|
1000
2154
|
*/
|
|
1001
|
-
|
|
1002
|
-
const
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
2155
|
+
extractTags(testCase) {
|
|
2156
|
+
const runtimeTags = testCase.tags;
|
|
2157
|
+
if (runtimeTags && runtimeTags.length > 0) {
|
|
2158
|
+
return runtimeTags.map((t) => t.replace(/^@/, ""));
|
|
2159
|
+
}
|
|
2160
|
+
const titleTags = testCase.title.match(/@[\w-]+/g);
|
|
2161
|
+
return titleTags?.map((t) => t.replace(/^@/, "")) ?? [];
|
|
2162
|
+
}
|
|
2163
|
+
serializeTest(data) {
|
|
2164
|
+
const { testCase, results } = data;
|
|
2165
|
+
return {
|
|
2166
|
+
projectId: testCase.parent.project()?.name ?? "default",
|
|
2167
|
+
projectName: testCase.parent.project()?.name ?? "default",
|
|
2168
|
+
timeout: testCase.timeout,
|
|
2169
|
+
expectedStatus: testCase.expectedStatus,
|
|
2170
|
+
annotations: this.serializeAnnotations(testCase.annotations),
|
|
2171
|
+
results: results.map((r) => this.serializeTestResult(r)),
|
|
2172
|
+
status: this.mapOutcome(testCase.outcome())
|
|
2173
|
+
};
|
|
2174
|
+
}
|
|
2175
|
+
serializeTestResult(r) {
|
|
2176
|
+
return {
|
|
2177
|
+
workerIndex: r.workerIndex,
|
|
2178
|
+
parallelIndex: r.parallelIndex ?? 0,
|
|
2179
|
+
status: r.status,
|
|
2180
|
+
duration: r.duration,
|
|
2181
|
+
startTime: r.startTime.toISOString(),
|
|
2182
|
+
retry: r.retry,
|
|
2183
|
+
errors: r.errors.map((e) => this.serializeError(e, true)),
|
|
2184
|
+
error: r.error ? this.serializeError(r.error, false) : void 0,
|
|
2185
|
+
errorLocation: r.error?.location,
|
|
2186
|
+
stdout: r.stdout.map(
|
|
2187
|
+
(s) => typeof s === "string" ? { text: s } : Buffer.isBuffer(s) ? { buffer: s.toString("base64") } : s
|
|
2188
|
+
),
|
|
2189
|
+
stderr: r.stderr.map(
|
|
2190
|
+
(s) => typeof s === "string" ? { text: s } : Buffer.isBuffer(s) ? { buffer: s.toString("base64") } : s
|
|
2191
|
+
),
|
|
2192
|
+
attachments: r.attachments.map((a) => ({
|
|
2193
|
+
name: a.name,
|
|
2194
|
+
contentType: a.contentType,
|
|
2195
|
+
path: a.path,
|
|
2196
|
+
body: a.body ? a.body.toString("base64") : void 0
|
|
2197
|
+
})),
|
|
2198
|
+
steps: r.steps.map((s) => this.serializeStep(s)),
|
|
2199
|
+
annotations: this.serializeAnnotations(
|
|
2200
|
+
r.annotations ?? []
|
|
2201
|
+
)
|
|
2202
|
+
};
|
|
2203
|
+
}
|
|
2204
|
+
serializeStep(step) {
|
|
2205
|
+
return {
|
|
2206
|
+
title: step.title,
|
|
2207
|
+
duration: step.duration,
|
|
2208
|
+
error: step.error ? this.serializeError(step.error, false) : void 0,
|
|
2209
|
+
steps: step.steps?.map((s) => this.serializeStep(s))
|
|
1020
2210
|
};
|
|
1021
|
-
|
|
1022
|
-
|
|
2211
|
+
}
|
|
2212
|
+
serializeError(error, includeContext) {
|
|
2213
|
+
if (error.value !== void 0) {
|
|
2214
|
+
return includeContext ? { message: error.value } : { value: error.value };
|
|
2215
|
+
}
|
|
2216
|
+
let msg = error.message ?? "Unknown error";
|
|
2217
|
+
if (includeContext && !/^(\w+Error|Error):/.test(msg)) {
|
|
2218
|
+
msg = "Error: " + msg;
|
|
2219
|
+
}
|
|
2220
|
+
if (includeContext) {
|
|
2221
|
+
if (error.snippet) msg += "\n\n" + error.snippet;
|
|
2222
|
+
if (error.stack) {
|
|
2223
|
+
const frames = error.stack.split("\n").filter((l) => l.trim().startsWith("at "));
|
|
2224
|
+
if (frames.length) msg += "\n" + frames.join("\n");
|
|
2225
|
+
} else if (error.location) {
|
|
2226
|
+
msg += `
|
|
2227
|
+
at ${error.location.file}:${error.location.line}:${error.location.column}`;
|
|
2228
|
+
}
|
|
1023
2229
|
}
|
|
1024
|
-
const
|
|
1025
|
-
if (
|
|
1026
|
-
|
|
1027
|
-
id: name,
|
|
1028
|
-
name
|
|
1029
|
-
}));
|
|
2230
|
+
const result = { message: msg, stack: error.stack, location: error.location, snippet: error.snippet };
|
|
2231
|
+
if (error.matcherResult !== void 0) {
|
|
2232
|
+
result.matcherResult = error.matcherResult;
|
|
1030
2233
|
}
|
|
2234
|
+
return result;
|
|
1031
2235
|
}
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
2236
|
+
serializeAnnotations(annotations) {
|
|
2237
|
+
return annotations.map((a) => ({ type: a.type, description: a.description, location: a.location }));
|
|
2238
|
+
}
|
|
2239
|
+
countTests(suite) {
|
|
2240
|
+
let count = suite.tests?.length ?? 0;
|
|
2241
|
+
for (const child of suite.suites ?? []) {
|
|
2242
|
+
count += this.countTests(child);
|
|
1038
2243
|
}
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
const firstResult = this.testSession.testResults[0];
|
|
1048
|
-
let assetId;
|
|
1049
|
-
if (zipSizeBytes > 0) {
|
|
1050
|
-
try {
|
|
1051
|
-
const assets = fs3.createReadStream(zipPath);
|
|
1052
|
-
const uploadResponse = await this.testResults.uploadTestResultAsset(
|
|
1053
|
-
this.testSession.testSessionId,
|
|
1054
|
-
firstResult.testResultId,
|
|
1055
|
-
assets
|
|
1056
|
-
);
|
|
1057
|
-
assetId = uploadResponse.assetId;
|
|
1058
|
-
} catch (error) {
|
|
1059
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1060
|
-
console.error("[Checkly Reporter] Asset upload failed:", errorMessage);
|
|
1061
|
-
}
|
|
1062
|
-
}
|
|
1063
|
-
await this.testResults.updateTestResult(this.testSession.testSessionId, firstResult.testResultId, {
|
|
1064
|
-
status: overallStatus,
|
|
1065
|
-
assetEntries: assetId ? entries : void 0,
|
|
1066
|
-
isDegraded,
|
|
1067
|
-
startedAt: this.startTime?.toISOString(),
|
|
1068
|
-
stoppedAt: endTime.toISOString(),
|
|
1069
|
-
responseTime,
|
|
1070
|
-
metadata: {
|
|
1071
|
-
usageData: {
|
|
1072
|
-
s3PostTotalBytes: zipSizeBytes
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
});
|
|
1076
|
-
}
|
|
1077
|
-
} catch (error) {
|
|
1078
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1079
|
-
console.error("[Checkly Reporter] Failed to upload results:", errorMessage);
|
|
2244
|
+
return count;
|
|
2245
|
+
}
|
|
2246
|
+
getFileName(filePath) {
|
|
2247
|
+
return filePath.split("/").pop() ?? filePath;
|
|
2248
|
+
}
|
|
2249
|
+
mapOutcome(outcome) {
|
|
2250
|
+
if (outcome === "expected" || outcome === "unexpected" || outcome === "flaky" || outcome === "skipped") {
|
|
2251
|
+
return outcome;
|
|
1080
2252
|
}
|
|
2253
|
+
return "unexpected";
|
|
1081
2254
|
}
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
2255
|
+
printSummary() {
|
|
2256
|
+
const pkgVersion = getPackageVersion();
|
|
2257
|
+
const playwrightVersion = this.config.version;
|
|
2258
|
+
const projects = this.config.projects;
|
|
2259
|
+
const projectNames = projects.map((p) => p.name).join(", ");
|
|
2260
|
+
const rule = pluralRules.select(projects.length);
|
|
2261
|
+
console.log("\n======================================================\n");
|
|
2262
|
+
console.log(`\u{1F99D} Checkly reporter: ${pkgVersion}`);
|
|
2263
|
+
console.log(`\u{1F3AD} Playwright: ${playwrightVersion}`);
|
|
2264
|
+
console.log(`\u{1F4D4} ${projectForms[rule]}: ${projectNames}`);
|
|
2265
|
+
for (const line of this.summaryLines) {
|
|
2266
|
+
console.log(line);
|
|
2267
|
+
}
|
|
2268
|
+
console.log("\n======================================================");
|
|
1087
2269
|
}
|
|
1088
2270
|
};
|
|
2271
|
+
|
|
2272
|
+
// src/types/output-contract.ts
|
|
2273
|
+
function isValidJSONReport(obj) {
|
|
2274
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
2275
|
+
const report = obj;
|
|
2276
|
+
return typeof report.config === "object" && report.config !== null && Array.isArray(report.suites) && Array.isArray(report.errors) && typeof report.stats === "object" && report.stats !== null;
|
|
2277
|
+
}
|
|
2278
|
+
function isValidStats(obj) {
|
|
2279
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
2280
|
+
const stats = obj;
|
|
2281
|
+
return typeof stats.startTime === "string" && typeof stats.duration === "number" && typeof stats.expected === "number" && typeof stats.unexpected === "number" && typeof stats.flaky === "number" && typeof stats.skipped === "number";
|
|
2282
|
+
}
|
|
2283
|
+
function isValidTestResult(obj) {
|
|
2284
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
2285
|
+
const result = obj;
|
|
2286
|
+
return typeof result.workerIndex === "number" && typeof result.parallelIndex === "number" && typeof result.status === "string" && typeof result.duration === "number" && typeof result.startTime === "string" && typeof result.retry === "number" && Array.isArray(result.errors) && Array.isArray(result.stdout) && Array.isArray(result.stderr) && Array.isArray(result.attachments) && Array.isArray(result.annotations);
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
// src/index.ts
|
|
2290
|
+
var ChecklyReporter = class extends BaseReporter {
|
|
2291
|
+
constructor(options = {}) {
|
|
2292
|
+
super(options);
|
|
2293
|
+
this.use(checklyUpload(options));
|
|
2294
|
+
}
|
|
2295
|
+
};
|
|
2296
|
+
var index_default = ChecklyReporter;
|
|
2297
|
+
function createChecklyReporter(options = {}) {
|
|
2298
|
+
return ["@checkly/playwright-reporter", options];
|
|
2299
|
+
}
|
|
1089
2300
|
export {
|
|
1090
|
-
|
|
2301
|
+
BaseReporter,
|
|
1091
2302
|
ChecklyReporter,
|
|
1092
|
-
|
|
1093
|
-
|
|
2303
|
+
checklyUpload,
|
|
2304
|
+
createChecklyReporter,
|
|
2305
|
+
index_default as default,
|
|
2306
|
+
isValidJSONReport,
|
|
2307
|
+
isValidStats,
|
|
2308
|
+
isValidTestResult
|
|
1094
2309
|
};
|