@checkly/playwright-reporter 0.1.10 → 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 +343 -267
- package/dist/index.js +1597 -789
- 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";
|
|
@@ -269,6 +282,19 @@ function toConsoleMessage(event) {
|
|
|
269
282
|
};
|
|
270
283
|
}
|
|
271
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
|
+
|
|
272
298
|
// ../utils/src/network-adapter.ts
|
|
273
299
|
import { createHash as createHash2 } from "crypto";
|
|
274
300
|
function generateId2(url, method, startedAt) {
|
|
@@ -491,11 +517,729 @@ var TraceReader = class {
|
|
|
491
517
|
import * as fs3 from "fs";
|
|
492
518
|
import * as os from "os";
|
|
493
519
|
import * as path2 from "path";
|
|
494
|
-
|
|
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
|
|
495
1235
|
var Zipper = class {
|
|
496
1236
|
outputPath;
|
|
1237
|
+
compressionLevel;
|
|
1238
|
+
onProgress;
|
|
497
1239
|
constructor(options) {
|
|
498
1240
|
this.outputPath = options.outputPath;
|
|
1241
|
+
this.compressionLevel = options.compressionLevel ?? 0;
|
|
1242
|
+
this.onProgress = options.onProgress;
|
|
499
1243
|
}
|
|
500
1244
|
/**
|
|
501
1245
|
* Creates a ZIP archive containing the JSON report and assets
|
|
@@ -504,63 +1248,94 @@ var Zipper = class {
|
|
|
504
1248
|
* @returns ZIP creation result with metadata
|
|
505
1249
|
*/
|
|
506
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) {
|
|
507
1291
|
const entries = [];
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
size: zipSize,
|
|
529
|
-
entryCount: entries.length,
|
|
530
|
-
entries
|
|
531
|
-
});
|
|
532
|
-
});
|
|
533
|
-
archive.on("error", (err) => {
|
|
534
|
-
reject(err);
|
|
535
|
-
});
|
|
536
|
-
output.on("error", (err) => {
|
|
537
|
-
reject(err);
|
|
538
|
-
});
|
|
539
|
-
archive.pipe(output);
|
|
540
|
-
if (!fs3.existsSync(reportPath)) {
|
|
541
|
-
reject(new Error(`Report file not found: ${reportPath}`));
|
|
542
|
-
return;
|
|
543
|
-
}
|
|
544
|
-
const transformedReportPath = this.transformJsonReport(reportPath);
|
|
545
|
-
archive.file(transformedReportPath, { name: "output/playwright-test-report.json" });
|
|
546
|
-
for (const asset of assets) {
|
|
547
|
-
if (!fs3.existsSync(asset.sourcePath)) {
|
|
548
|
-
console.warn(`[Checkly Reporter] Skipping missing asset: ${asset.sourcePath}`);
|
|
549
|
-
continue;
|
|
550
|
-
}
|
|
551
|
-
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));
|
|
552
1312
|
}
|
|
553
|
-
archive.finalize();
|
|
554
|
-
} catch (error) {
|
|
555
|
-
reject(error);
|
|
556
1313
|
}
|
|
557
|
-
}
|
|
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;
|
|
558
1336
|
}
|
|
559
1337
|
/**
|
|
560
1338
|
* Transforms the JSON report to use relative paths for attachments
|
|
561
|
-
* This ensures the UI can map attachment paths to ZIP entries
|
|
562
|
-
* @param reportPath - Path to the original JSON report
|
|
563
|
-
* @returns Path to the transformed JSON report (in temp directory)
|
|
564
1339
|
*/
|
|
565
1340
|
transformJsonReport(reportPath) {
|
|
566
1341
|
const reportContent = fs3.readFileSync(reportPath, "utf-8");
|
|
@@ -572,8 +1347,6 @@ var Zipper = class {
|
|
|
572
1347
|
}
|
|
573
1348
|
/**
|
|
574
1349
|
* Recursively transforms attachment paths in the report structure
|
|
575
|
-
* Converts absolute paths to relative paths matching ZIP structure
|
|
576
|
-
* @param obj - Object to transform (mutated in place)
|
|
577
1350
|
*/
|
|
578
1351
|
transformAttachmentPaths(obj) {
|
|
579
1352
|
if (typeof obj !== "object" || obj === null) {
|
|
@@ -593,21 +1366,7 @@ var Zipper = class {
|
|
|
593
1366
|
Object.values(obj).forEach((value) => this.transformAttachmentPaths(value));
|
|
594
1367
|
}
|
|
595
1368
|
/**
|
|
596
|
-
* Normalizes attachment paths by extracting the relevant snapshot directory portion
|
|
597
|
-
* Supports Playwright's default and common custom snapshot directory patterns,
|
|
598
|
-
* as well as blob merge resource paths.
|
|
599
|
-
*
|
|
600
|
-
* Priority order (first match wins):
|
|
601
|
-
* 1. test-results/ (highest priority, existing behavior)
|
|
602
|
-
* 2. blob-reports/resources/ (blob merge extraction paths)
|
|
603
|
-
* 3. snapshots directories (Playwright default pattern)
|
|
604
|
-
* 4. __screenshots__/ (common custom pattern)
|
|
605
|
-
* 5. __snapshots__/ (common custom pattern)
|
|
606
|
-
* 6. screenshots/ (simple custom pattern)
|
|
607
|
-
* 7. snapshots/ (simple custom pattern)
|
|
608
|
-
*
|
|
609
|
-
* @param attachmentPath - Absolute or relative path to attachment
|
|
610
|
-
* @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
|
|
611
1370
|
*/
|
|
612
1371
|
normalizeAttachmentPath(attachmentPath) {
|
|
613
1372
|
const normalizedPath = attachmentPath.replace(/\\/g, "/");
|
|
@@ -644,17 +1403,11 @@ var Zipper = class {
|
|
|
644
1403
|
}
|
|
645
1404
|
};
|
|
646
1405
|
|
|
647
|
-
// src/
|
|
648
|
-
import * as fs4 from "fs";
|
|
649
|
-
import { readFileSync as readFileSync4 } from "fs";
|
|
650
|
-
import * as path3 from "path";
|
|
651
|
-
import { dirname, join as join3 } from "path";
|
|
652
|
-
import { fileURLToPath } from "url";
|
|
653
|
-
|
|
654
|
-
// ../clients/src/checkly-client.ts
|
|
1406
|
+
// src/api/client.ts
|
|
655
1407
|
import axios from "axios";
|
|
1408
|
+
import FormData from "form-data";
|
|
656
1409
|
|
|
657
|
-
//
|
|
1410
|
+
// src/api/errors.ts
|
|
658
1411
|
var ApiError = class extends Error {
|
|
659
1412
|
data;
|
|
660
1413
|
constructor(data, options) {
|
|
@@ -664,44 +1417,20 @@ var ApiError = class extends Error {
|
|
|
664
1417
|
}
|
|
665
1418
|
};
|
|
666
1419
|
var ValidationError = class extends ApiError {
|
|
667
|
-
constructor(data, options) {
|
|
668
|
-
super(data, options);
|
|
669
|
-
}
|
|
670
1420
|
};
|
|
671
1421
|
var UnauthorizedError = class extends ApiError {
|
|
672
|
-
constructor(data, options) {
|
|
673
|
-
super(data, options);
|
|
674
|
-
}
|
|
675
1422
|
};
|
|
676
1423
|
var ForbiddenError = class extends ApiError {
|
|
677
|
-
constructor(data, options) {
|
|
678
|
-
super(data, options);
|
|
679
|
-
}
|
|
680
1424
|
};
|
|
681
1425
|
var NotFoundError = class extends ApiError {
|
|
682
|
-
constructor(data, options) {
|
|
683
|
-
super(data, options);
|
|
684
|
-
}
|
|
685
1426
|
};
|
|
686
1427
|
var RequestTimeoutError = class extends ApiError {
|
|
687
|
-
constructor(data, options) {
|
|
688
|
-
super(data, options);
|
|
689
|
-
}
|
|
690
1428
|
};
|
|
691
1429
|
var ConflictError = class extends ApiError {
|
|
692
|
-
constructor(data, options) {
|
|
693
|
-
super(data, options);
|
|
694
|
-
}
|
|
695
1430
|
};
|
|
696
1431
|
var ServerError = class extends ApiError {
|
|
697
|
-
constructor(data, options) {
|
|
698
|
-
super(data, options);
|
|
699
|
-
}
|
|
700
1432
|
};
|
|
701
1433
|
var MiscellaneousError = class extends ApiError {
|
|
702
|
-
constructor(data, options) {
|
|
703
|
-
super(data, options);
|
|
704
|
-
}
|
|
705
1434
|
};
|
|
706
1435
|
var MissingResponseError = class extends Error {
|
|
707
1436
|
constructor(message, options) {
|
|
@@ -709,63 +1438,58 @@ var MissingResponseError = class extends Error {
|
|
|
709
1438
|
this.name = "MissingResponseError";
|
|
710
1439
|
}
|
|
711
1440
|
};
|
|
712
|
-
function parseErrorData(data,
|
|
713
|
-
if (!data)
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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
|
+
}
|
|
746
1476
|
}
|
|
747
1477
|
if (typeof data === "string") {
|
|
748
|
-
return {
|
|
749
|
-
statusCode: options.statusCode,
|
|
750
|
-
error: data,
|
|
751
|
-
message: data
|
|
752
|
-
};
|
|
1478
|
+
return { statusCode, error: data, message: data };
|
|
753
1479
|
}
|
|
754
1480
|
return void 0;
|
|
755
1481
|
}
|
|
756
|
-
function handleErrorResponse(
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
});
|
|
768
|
-
}
|
|
1482
|
+
function handleErrorResponse(err2) {
|
|
1483
|
+
const axiosError = err2;
|
|
1484
|
+
if (!axiosError.response) {
|
|
1485
|
+
throw new MissingResponseError(axiosError.message || "Network error");
|
|
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
|
+
};
|
|
769
1493
|
switch (status) {
|
|
770
1494
|
case 400:
|
|
771
1495
|
throw new ValidationError(errorData);
|
|
@@ -787,132 +1511,326 @@ function handleErrorResponse(err) {
|
|
|
787
1511
|
}
|
|
788
1512
|
}
|
|
789
1513
|
|
|
790
|
-
//
|
|
791
|
-
function
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
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) => {
|
|
796
1523
|
if (config.headers) {
|
|
797
|
-
config.headers.Authorization = `Bearer ${apiKey}`;
|
|
798
|
-
config.headers["x-checkly-account"] = accountId;
|
|
799
|
-
config.headers["User-Agent"] = `@checkly/playwright-reporter/${getVersion()}`;
|
|
1524
|
+
config.headers.Authorization = `Bearer ${options.apiKey}`;
|
|
1525
|
+
config.headers["x-checkly-account"] = options.accountId;
|
|
800
1526
|
}
|
|
801
1527
|
return config;
|
|
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
|
+
}
|
|
802
1557
|
};
|
|
803
1558
|
}
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
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"
|
|
807
1567
|
};
|
|
1568
|
+
return urls[environment];
|
|
808
1569
|
}
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
this.accountId = options.accountId;
|
|
816
|
-
this.apiKey = options.apiKey;
|
|
817
|
-
this.baseUrl = options.baseUrl;
|
|
818
|
-
this.api = axios.create({
|
|
819
|
-
baseURL: this.baseUrl,
|
|
820
|
-
timeout: 12e4,
|
|
821
|
-
// 120 second timeout for large uploads
|
|
822
|
-
maxContentLength: Number.POSITIVE_INFINITY,
|
|
823
|
-
// Allow large payloads
|
|
824
|
-
maxBodyLength: Number.POSITIVE_INFINITY
|
|
825
|
-
// Allow large request bodies
|
|
826
|
-
});
|
|
827
|
-
this.api.interceptors.request.use(createRequestInterceptor(this.apiKey, this.accountId));
|
|
828
|
-
this.api.interceptors.response.use((response) => response, createResponseErrorInterceptor());
|
|
829
|
-
}
|
|
830
|
-
/**
|
|
831
|
-
* Gets the underlying axios instance
|
|
832
|
-
* Useful for creating resource-specific clients (e.g., TestResults)
|
|
833
|
-
*/
|
|
834
|
-
getAxiosInstance() {
|
|
835
|
-
return this.api;
|
|
836
|
-
}
|
|
837
|
-
};
|
|
838
|
-
|
|
839
|
-
// ../clients/src/test-results.ts
|
|
840
|
-
import FormData from "form-data";
|
|
841
|
-
var TestResults = class {
|
|
842
|
-
constructor(api) {
|
|
843
|
-
this.api = api;
|
|
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";
|
|
844
1576
|
}
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
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");
|
|
1595
|
+
}
|
|
1596
|
+
if (options.outputPath) {
|
|
1597
|
+
console.warn("[Checkly] Warning: outputPath is deprecated, ZIP is now written to {outputDir}/checkly-report.zip");
|
|
1598
|
+
}
|
|
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 });
|
|
1612
|
+
}
|
|
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
|
+
};
|
|
857
1663
|
}
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
* @throws {ServerError} If S3 upload fails
|
|
871
|
-
*/
|
|
872
|
-
async uploadTestResultAsset(testSessionId, testResultId, assets) {
|
|
873
|
-
const form = new FormData();
|
|
874
|
-
form.append("assets", assets, {
|
|
875
|
-
filename: "assets.zip",
|
|
876
|
-
contentType: "application/zip"
|
|
877
|
-
});
|
|
878
|
-
const response = await this.api.post(
|
|
879
|
-
`/next/test-sessions/${testSessionId}/results/${testResultId}/assets`,
|
|
880
|
-
form,
|
|
881
|
-
{
|
|
882
|
-
headers: {
|
|
883
|
-
...form.getHeaders()
|
|
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
|
+
}
|
|
884
1676
|
}
|
|
885
1677
|
}
|
|
886
|
-
|
|
887
|
-
|
|
1678
|
+
suite.suites?.forEach(processSuite);
|
|
1679
|
+
}
|
|
1680
|
+
report.suites.forEach(processSuite);
|
|
888
1681
|
}
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
);
|
|
907
|
-
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
|
+
};
|
|
908
1699
|
}
|
|
909
|
-
|
|
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
|
+
}
|
|
910
1819
|
|
|
911
1820
|
// src/reporter.ts
|
|
1821
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
1822
|
+
import { dirname, join as join4 } from "path";
|
|
1823
|
+
import { fileURLToPath } from "url";
|
|
912
1824
|
var __filename = fileURLToPath(import.meta.url);
|
|
913
1825
|
var __dirname = dirname(__filename);
|
|
914
|
-
|
|
915
|
-
|
|
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
|
+
}
|
|
916
1834
|
var pluralRules = new Intl.PluralRules("en-US");
|
|
917
1835
|
var projectForms = {
|
|
918
1836
|
zero: "Project",
|
|
@@ -922,580 +1840,470 @@ var projectForms = {
|
|
|
922
1840
|
many: "Projects",
|
|
923
1841
|
other: "Projects"
|
|
924
1842
|
};
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
return env;
|
|
944
|
-
}
|
|
945
|
-
function convertStepToJSON(step) {
|
|
946
|
-
return {
|
|
947
|
-
title: step.title,
|
|
948
|
-
duration: step.duration,
|
|
949
|
-
error: step.error,
|
|
950
|
-
steps: step.steps.length > 0 ? step.steps.map(convertStepToJSON) : void 0
|
|
951
|
-
};
|
|
952
|
-
}
|
|
953
|
-
function getDirectoryName() {
|
|
954
|
-
const cwd = process.cwd();
|
|
955
|
-
let dirName = path3.basename(cwd);
|
|
956
|
-
if (!dirName || dirName === "/" || dirName === ".") {
|
|
957
|
-
dirName = "playwright-tests";
|
|
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";
|
|
958
1861
|
}
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
1862
|
+
use(extension) {
|
|
1863
|
+
this.extensions.push(extension);
|
|
1864
|
+
return this;
|
|
962
1865
|
}
|
|
963
|
-
return dirName;
|
|
964
|
-
}
|
|
965
|
-
var ChecklyReporter = class {
|
|
966
|
-
options;
|
|
967
|
-
assetCollector;
|
|
968
|
-
zipper;
|
|
969
|
-
testResults;
|
|
970
|
-
testSession;
|
|
971
|
-
startTime;
|
|
972
|
-
testCounts = {
|
|
973
|
-
passed: 0,
|
|
974
|
-
failed: 0,
|
|
975
|
-
flaky: 0
|
|
976
|
-
};
|
|
977
|
-
// Store steps per test result, keyed by "testId:retry"
|
|
978
|
-
stepsMap = /* @__PURE__ */ new Map();
|
|
979
|
-
// Store warnings per test result, keyed by "testId:retry"
|
|
980
|
-
warningsMap = /* @__PURE__ */ new Map();
|
|
981
|
-
// Store trace file paths per test result, keyed by "testId:retry"
|
|
982
|
-
tracePathsMap = /* @__PURE__ */ new Map();
|
|
983
|
-
// Store console messages per test result, keyed by "testId:retry"
|
|
984
|
-
consoleMessagesMap = /* @__PURE__ */ new Map();
|
|
985
|
-
// Store network requests per test result, keyed by "testId:retry"
|
|
986
|
-
networkRequestsMap = /* @__PURE__ */ new Map();
|
|
987
|
-
/**
|
|
988
|
-
* Log a message if verbose mode is enabled
|
|
989
|
-
*/
|
|
990
1866
|
log(message, data) {
|
|
991
|
-
if (!this.
|
|
992
|
-
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
993
|
-
|
|
994
|
-
|
|
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);
|
|
995
1872
|
} else {
|
|
996
|
-
console.log(
|
|
1873
|
+
console.log(`${prefix} ${message}`);
|
|
997
1874
|
}
|
|
998
1875
|
}
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
const apiKey = process.env.CHECKLY_API_KEY || options.apiKey;
|
|
1003
|
-
const accountId = process.env.CHECKLY_ACCOUNT_ID || options.accountId;
|
|
1004
|
-
const verbose = options.verbose ?? process.env.CHECKLY_REPORTER_VERBOSE === "true";
|
|
1005
|
-
this.options = {
|
|
1006
|
-
accountId,
|
|
1007
|
-
apiKey,
|
|
1008
|
-
outputPath: options.outputPath ?? "checkly-report.zip",
|
|
1009
|
-
jsonReportPath: options.jsonReportPath ?? "test-results/playwright-test-report.json",
|
|
1010
|
-
testResultsDir: options.testResultsDir ?? "test-results",
|
|
1011
|
-
dryRun: options.dryRun ?? false,
|
|
1012
|
-
sessionName: options.sessionName,
|
|
1013
|
-
verbose
|
|
1876
|
+
createExtensionLogger(extensionName) {
|
|
1877
|
+
return (message, data) => {
|
|
1878
|
+
this.log(`[${extensionName}] ${message}`, data);
|
|
1014
1879
|
};
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
hasAccountId: !!accountId,
|
|
1033
|
-
outputPath: this.options.outputPath,
|
|
1034
|
-
jsonReportPath: this.options.jsonReportPath,
|
|
1035
|
-
testResultsDir: this.options.testResultsDir,
|
|
1036
|
-
dryRun: this.options.dryRun,
|
|
1037
|
-
verbose: this.options.verbose,
|
|
1038
|
-
hasTestResults: !!this.testResults
|
|
1039
|
-
});
|
|
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) });
|
|
1040
1897
|
}
|
|
1041
1898
|
}
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
*/
|
|
1046
|
-
resolveSessionName(context) {
|
|
1047
|
-
const { sessionName } = this.options;
|
|
1048
|
-
if (typeof sessionName === "function") {
|
|
1049
|
-
return sessionName(context);
|
|
1899
|
+
onTestBegin(test, result) {
|
|
1900
|
+
for (const ext of this.extensions) {
|
|
1901
|
+
ext.onTestBegin?.({ test, result, log: this.createExtensionLogger(ext.name) });
|
|
1050
1902
|
}
|
|
1051
|
-
|
|
1052
|
-
|
|
1903
|
+
}
|
|
1904
|
+
onStepBegin(test, result, step) {
|
|
1905
|
+
for (const ext of this.extensions) {
|
|
1906
|
+
ext.onStepBegin?.({ test, result, step, log: this.createExtensionLogger(ext.name) });
|
|
1053
1907
|
}
|
|
1054
|
-
return `Playwright Test Session: ${context.directoryName}`;
|
|
1055
1908
|
}
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
* The warning type depends on the trace configuration and test result state
|
|
1060
|
-
*/
|
|
1061
|
-
checkTraceAttachment(test, result) {
|
|
1062
|
-
const key = `${test.id}:${result.retry}`;
|
|
1063
|
-
const traceAttachment = result.attachments?.find(
|
|
1064
|
-
(attachment) => attachment.name === "trace" || attachment.contentType === "application/zip"
|
|
1065
|
-
);
|
|
1066
|
-
if (traceAttachment?.path) {
|
|
1067
|
-
this.tracePathsMap.set(key, traceAttachment.path);
|
|
1068
|
-
return;
|
|
1909
|
+
onStepEnd(test, result, step) {
|
|
1910
|
+
for (const ext of this.extensions) {
|
|
1911
|
+
ext.onStepEnd?.({ test, result, step, log: this.createExtensionLogger(ext.name) });
|
|
1069
1912
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
const
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
message = 'Traces are disabled. Set trace: "on" in playwright.config.ts to capture traces.';
|
|
1082
|
-
break;
|
|
1083
|
-
case "retain-on-failure":
|
|
1084
|
-
if (testPassed) {
|
|
1085
|
-
warningType = "trace-retained-on-failure";
|
|
1086
|
-
message = 'No trace retained because test passed. Trace mode is "retain-on-failure" which discards traces for passing tests.';
|
|
1087
|
-
} else {
|
|
1088
|
-
warningType = "trace-missing";
|
|
1089
|
-
message = 'Trace should exist but was not found. The test failed with trace: "retain-on-failure".';
|
|
1090
|
-
}
|
|
1091
|
-
break;
|
|
1092
|
-
case "on-first-retry":
|
|
1093
|
-
if (!isRetry) {
|
|
1094
|
-
warningType = "trace-first-retry-only";
|
|
1095
|
-
message = 'No trace for initial attempt. Trace mode is "on-first-retry" which only records traces on the first retry.';
|
|
1096
|
-
} else if (result.retry === 1) {
|
|
1097
|
-
warningType = "trace-missing";
|
|
1098
|
-
message = 'Trace should exist but was not found. This is the first retry with trace: "on-first-retry".';
|
|
1099
|
-
} else {
|
|
1100
|
-
warningType = "trace-first-retry-only";
|
|
1101
|
-
message = `No trace for retry #${result.retry}. Trace mode is "on-first-retry" which only records the first retry.`;
|
|
1102
|
-
}
|
|
1103
|
-
break;
|
|
1104
|
-
case "on-all-retries":
|
|
1105
|
-
if (!isRetry) {
|
|
1106
|
-
warningType = "trace-retries-only";
|
|
1107
|
-
message = 'No trace for initial attempt. Trace mode is "on-all-retries" which only records traces on retries.';
|
|
1108
|
-
} else {
|
|
1109
|
-
warningType = "trace-missing";
|
|
1110
|
-
message = `Trace should exist but was not found. This is retry #${result.retry} with trace: "on-all-retries".`;
|
|
1111
|
-
}
|
|
1112
|
-
break;
|
|
1113
|
-
case "retain-on-first-failure":
|
|
1114
|
-
if (testPassed) {
|
|
1115
|
-
warningType = "trace-retained-on-first-failure";
|
|
1116
|
-
message = 'No trace retained because test passed. Trace mode is "retain-on-first-failure" which discards traces for passing tests.';
|
|
1117
|
-
} else if (isRetry) {
|
|
1118
|
-
warningType = "trace-retained-on-first-failure";
|
|
1119
|
-
message = 'No trace for retries. Trace mode is "retain-on-first-failure" which only records the first run.';
|
|
1120
|
-
} else {
|
|
1121
|
-
warningType = "trace-missing";
|
|
1122
|
-
message = 'Trace should exist but was not found. The test failed on first run with trace: "retain-on-first-failure".';
|
|
1123
|
-
}
|
|
1124
|
-
break;
|
|
1125
|
-
case "on":
|
|
1126
|
-
warningType = "trace-missing";
|
|
1127
|
-
message = 'Trace should exist but was not found. Trace mode is "on" which should always record traces.';
|
|
1128
|
-
break;
|
|
1129
|
-
default:
|
|
1130
|
-
warningType = "trace-missing";
|
|
1131
|
-
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) });
|
|
1132
1924
|
}
|
|
1133
|
-
const warnings = this.warningsMap.get(key) || [];
|
|
1134
|
-
warnings.push({ type: warningType, message });
|
|
1135
|
-
this.warningsMap.set(key, warnings);
|
|
1136
1925
|
}
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
*/
|
|
1141
|
-
onBegin(config, suite) {
|
|
1142
|
-
this.startTime = /* @__PURE__ */ new Date();
|
|
1143
|
-
if (!this.testResults) {
|
|
1144
|
-
return;
|
|
1926
|
+
onStdOut(chunk, test, result) {
|
|
1927
|
+
for (const ext of this.extensions) {
|
|
1928
|
+
ext.onStdOut?.({ chunk, test, result, log: this.createExtensionLogger(ext.name) });
|
|
1145
1929
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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)
|
|
1170
1989
|
});
|
|
1171
|
-
} catch (error) {
|
|
1172
|
-
console.error("[Checkly Reporter] Error in onBegin:", error);
|
|
1173
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
|
+
};
|
|
1174
2066
|
}
|
|
1175
2067
|
/**
|
|
1176
|
-
*
|
|
1177
|
-
*
|
|
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.
|
|
1178
2070
|
*/
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
const
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
this.testCounts.flaky++;
|
|
1194
|
-
this.testCounts.passed++;
|
|
1195
|
-
} else {
|
|
1196
|
-
if (result.status === "passed") {
|
|
1197
|
-
this.testCounts.passed++;
|
|
1198
|
-
} else if (result.status === "failed" || result.status === "timedOut") {
|
|
1199
|
-
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);
|
|
1200
2085
|
}
|
|
1201
2086
|
}
|
|
1202
|
-
} catch (error) {
|
|
1203
|
-
console.error("[Checkly Reporter] Error in onTestEnd:", error);
|
|
1204
2087
|
}
|
|
2088
|
+
return Array.from(fileSuites.values());
|
|
1205
2089
|
}
|
|
1206
2090
|
/**
|
|
1207
|
-
*
|
|
1208
|
-
*
|
|
2091
|
+
* Recursively serialize a suite and its children.
|
|
2092
|
+
* Returns null if the suite has no tests.
|
|
1209
2093
|
*/
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
console.error(
|
|
1224
|
-
" reporter: [\n ['json', { outputFile: 'test-results/playwright-test-report.json' }],\n ['@checkly/playwright-reporter']\n ]"
|
|
1225
|
-
);
|
|
1226
|
-
return;
|
|
1227
|
-
}
|
|
1228
|
-
this.log("Reading JSON report", { path: jsonReportPath });
|
|
1229
|
-
const reportContent = fs4.readFileSync(jsonReportPath, "utf-8");
|
|
1230
|
-
const report = JSON.parse(reportContent);
|
|
1231
|
-
this.log("JSON report parsed", {
|
|
1232
|
-
configVersion: report.config?.version,
|
|
1233
|
-
projectsCount: report.config?.projects?.length ?? 0,
|
|
1234
|
-
suitesCount: report.suites?.length ?? 0,
|
|
1235
|
-
rootDir: report.config?.rootDir
|
|
1236
|
-
});
|
|
1237
|
-
await this.extractDataFromTraces();
|
|
1238
|
-
this.injectDataIntoReport(report);
|
|
1239
|
-
this.log("Data injected into report", {
|
|
1240
|
-
projectsCountAfterReconstruction: report.config?.projects?.length ?? 0
|
|
1241
|
-
});
|
|
1242
|
-
fs4.writeFileSync(jsonReportPath, JSON.stringify(report, null, 2), "utf-8");
|
|
1243
|
-
this.log("Enriched report written to disk");
|
|
1244
|
-
this.log("Collecting assets", { testResultsDir: this.options.testResultsDir });
|
|
1245
|
-
const assets = await this.assetCollector.collectAssets(report);
|
|
1246
|
-
this.log("Assets collected", {
|
|
1247
|
-
count: assets.length,
|
|
1248
|
-
assets: assets.map((a) => ({ source: a.sourcePath, archive: a.archivePath, type: a.type }))
|
|
1249
|
-
});
|
|
1250
|
-
this.log("Creating ZIP archive", { outputPath: this.options.outputPath });
|
|
1251
|
-
const result = await this.zipper.createZip(jsonReportPath, assets);
|
|
1252
|
-
this.log("ZIP created", {
|
|
1253
|
-
zipPath: result.zipPath,
|
|
1254
|
-
zipSize: result.size,
|
|
1255
|
-
entriesCount: result.entries.length,
|
|
1256
|
-
entries: result.entries.map((e) => ({ name: e.name, start: e.start, end: e.end }))
|
|
1257
|
-
});
|
|
1258
|
-
if (this.testResults && this.testSession) {
|
|
1259
|
-
this.log("Uploading results", { testSessionId: this.testSession.testSessionId });
|
|
1260
|
-
await this.uploadResults(report, result.zipPath, result.entries);
|
|
1261
|
-
if (!this.options.dryRun) {
|
|
1262
|
-
try {
|
|
1263
|
-
fs4.unlinkSync(result.zipPath);
|
|
1264
|
-
} catch (cleanupError) {
|
|
1265
|
-
console.warn(`[Checkly Reporter] Warning: Could not delete ZIP file: ${cleanupError}`);
|
|
1266
|
-
}
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
if (this.testResults && this.testSession?.link) {
|
|
1270
|
-
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));
|
|
1271
2107
|
}
|
|
1272
|
-
} catch (error) {
|
|
1273
|
-
console.error("[Checkly Reporter] ERROR creating report:", error);
|
|
1274
2108
|
}
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
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
|
+
};
|
|
1284
2117
|
}
|
|
1285
2118
|
/**
|
|
1286
|
-
*
|
|
1287
|
-
*
|
|
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.
|
|
1288
2122
|
*/
|
|
1289
|
-
|
|
1290
|
-
const
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
(async () => {
|
|
1294
|
-
const reader = new TraceReader(tracePath);
|
|
1295
|
-
if (!await reader.open()) return;
|
|
1296
|
-
const messages = await reader.extractEvents('"type":"console"', toConsoleMessage);
|
|
1297
|
-
if (messages.length > 0) {
|
|
1298
|
-
this.consoleMessagesMap.set(key, messages);
|
|
1299
|
-
}
|
|
1300
|
-
const networkRequests = await reader.extractEvents('"type":"resource-snapshot"', toNetworkRequest);
|
|
1301
|
-
if (networkRequests.length > 0) {
|
|
1302
|
-
this.networkRequestsMap.set(key, networkRequests);
|
|
1303
|
-
}
|
|
1304
|
-
})().catch((error) => {
|
|
1305
|
-
console.error(`[Checkly Reporter] Failed to extract data from trace: ${error}`);
|
|
1306
|
-
})
|
|
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
|
|
1307
2127
|
);
|
|
2128
|
+
if (toSuite) {
|
|
2129
|
+
this.mergeSuites(toSuite, fromSuite);
|
|
2130
|
+
} else {
|
|
2131
|
+
to.suites = to.suites ?? [];
|
|
2132
|
+
to.suites.push(fromSuite);
|
|
2133
|
+
}
|
|
1308
2134
|
}
|
|
1309
|
-
|
|
2135
|
+
to.specs.push(...from.specs);
|
|
1310
2136
|
}
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
result.steps = steps;
|
|
1324
|
-
}
|
|
1325
|
-
const warnings = this.warningsMap.get(key);
|
|
1326
|
-
const consoleMessages = this.consoleMessagesMap.get(key);
|
|
1327
|
-
const networkRequests = this.networkRequestsMap.get(key);
|
|
1328
|
-
const hasData = warnings && warnings.length > 0 || consoleMessages && consoleMessages.length > 0 || networkRequests && networkRequests.length > 0;
|
|
1329
|
-
if (hasData) {
|
|
1330
|
-
result._checkly = {
|
|
1331
|
-
...warnings && warnings.length > 0 ? { warnings } : {},
|
|
1332
|
-
...consoleMessages && consoleMessages.length > 0 ? { console: consoleMessages } : {},
|
|
1333
|
-
...networkRequests && networkRequests.length > 0 ? { network: networkRequests } : {}
|
|
1334
|
-
};
|
|
1335
|
-
}
|
|
1336
|
-
}
|
|
1337
|
-
}
|
|
1338
|
-
}
|
|
1339
|
-
if (suite.suites) {
|
|
1340
|
-
for (const nestedSuite of suite.suites) {
|
|
1341
|
-
processSuite(nestedSuite);
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
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)]
|
|
1344
2149
|
};
|
|
1345
|
-
for (const suite of report.suites) {
|
|
1346
|
-
processSuite(suite);
|
|
1347
|
-
}
|
|
1348
|
-
this.reconstructProjectsFromTests(report);
|
|
1349
2150
|
}
|
|
1350
2151
|
/**
|
|
1351
|
-
*
|
|
1352
|
-
*
|
|
1353
|
-
* 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
|
|
1354
2154
|
*/
|
|
1355
|
-
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
const
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
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))
|
|
1382
2210
|
};
|
|
1383
|
-
|
|
1384
|
-
|
|
2211
|
+
}
|
|
2212
|
+
serializeError(error, includeContext) {
|
|
2213
|
+
if (error.value !== void 0) {
|
|
2214
|
+
return includeContext ? { message: error.value } : { value: error.value };
|
|
1385
2215
|
}
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
}
|
|
1390
|
-
if (
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
}
|
|
1399
|
-
} else {
|
|
1400
|
-
this.log("No project reconstruction needed", {
|
|
1401
|
-
reason: configAny.projects?.length > 0 ? "projects already present" : "no project names found in tests"
|
|
1402
|
-
});
|
|
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
|
+
}
|
|
1403
2229
|
}
|
|
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;
|
|
2233
|
+
}
|
|
2234
|
+
return result;
|
|
1404
2235
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
hasTestResults: !!this.testResults,
|
|
1413
|
-
hasTestSession: !!this.testSession
|
|
1414
|
-
});
|
|
1415
|
-
if (!this.testResults || !this.testSession) {
|
|
1416
|
-
this.log("uploadResults skipped - missing testResults or testSession");
|
|
1417
|
-
return;
|
|
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);
|
|
1418
2243
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
testCounts: this.testCounts,
|
|
1428
|
-
overallStatus,
|
|
1429
|
-
isDegraded,
|
|
1430
|
-
responseTime,
|
|
1431
|
-
zipSizeBytes,
|
|
1432
|
-
testSessionId: this.testSession.testSessionId,
|
|
1433
|
-
testResultsCount: this.testSession.testResults.length
|
|
1434
|
-
});
|
|
1435
|
-
if (this.testSession.testResults.length > 0) {
|
|
1436
|
-
const firstResult = this.testSession.testResults[0];
|
|
1437
|
-
this.log("Using first test result for upload", {
|
|
1438
|
-
testResultId: firstResult.testResultId
|
|
1439
|
-
});
|
|
1440
|
-
let assetId;
|
|
1441
|
-
if (zipSizeBytes > 0) {
|
|
1442
|
-
this.log("Starting S3 asset upload", { zipSizeBytes });
|
|
1443
|
-
try {
|
|
1444
|
-
const assets = fs4.createReadStream(zipPath);
|
|
1445
|
-
const uploadResponse = await this.testResults.uploadTestResultAsset(
|
|
1446
|
-
this.testSession.testSessionId,
|
|
1447
|
-
firstResult.testResultId,
|
|
1448
|
-
assets
|
|
1449
|
-
);
|
|
1450
|
-
assetId = uploadResponse.assetId;
|
|
1451
|
-
this.log("S3 asset upload completed", { assetId });
|
|
1452
|
-
} catch (error) {
|
|
1453
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1454
|
-
console.error("[Checkly Reporter] Asset upload failed:", errorMessage);
|
|
1455
|
-
this.log("S3 asset upload failed", { error: errorMessage });
|
|
1456
|
-
}
|
|
1457
|
-
} else {
|
|
1458
|
-
this.log("Skipping S3 upload - ZIP is empty");
|
|
1459
|
-
}
|
|
1460
|
-
const updatePayload = {
|
|
1461
|
-
status: overallStatus,
|
|
1462
|
-
assetEntries: assetId ? entries : void 0,
|
|
1463
|
-
isDegraded,
|
|
1464
|
-
startedAt: this.startTime?.toISOString(),
|
|
1465
|
-
stoppedAt: endTime.toISOString(),
|
|
1466
|
-
responseTime,
|
|
1467
|
-
metadata: {
|
|
1468
|
-
usageData: {
|
|
1469
|
-
s3PostTotalBytes: zipSizeBytes
|
|
1470
|
-
}
|
|
1471
|
-
}
|
|
1472
|
-
};
|
|
1473
|
-
this.log("Updating test result", {
|
|
1474
|
-
testResultId: firstResult.testResultId,
|
|
1475
|
-
payload: updatePayload,
|
|
1476
|
-
assetEntriesCount: assetId ? entries.length : 0
|
|
1477
|
-
});
|
|
1478
|
-
await this.testResults.updateTestResult(this.testSession.testSessionId, firstResult.testResultId, updatePayload);
|
|
1479
|
-
this.log("Test result updated successfully");
|
|
1480
|
-
} else {
|
|
1481
|
-
this.log("No test results in session to update");
|
|
1482
|
-
}
|
|
1483
|
-
} catch (error) {
|
|
1484
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1485
|
-
console.error("[Checkly Reporter] Failed to upload results:", errorMessage);
|
|
1486
|
-
this.log("uploadResults failed", { error: 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;
|
|
1487
2252
|
}
|
|
2253
|
+
return "unexpected";
|
|
2254
|
+
}
|
|
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======================================================");
|
|
1488
2269
|
}
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
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));
|
|
1494
2294
|
}
|
|
1495
2295
|
};
|
|
2296
|
+
var index_default = ChecklyReporter;
|
|
2297
|
+
function createChecklyReporter(options = {}) {
|
|
2298
|
+
return ["@checkly/playwright-reporter", options];
|
|
2299
|
+
}
|
|
1496
2300
|
export {
|
|
1497
|
-
|
|
2301
|
+
BaseReporter,
|
|
1498
2302
|
ChecklyReporter,
|
|
1499
|
-
|
|
1500
|
-
|
|
2303
|
+
checklyUpload,
|
|
2304
|
+
createChecklyReporter,
|
|
2305
|
+
index_default as default,
|
|
2306
|
+
isValidJSONReport,
|
|
2307
|
+
isValidStats,
|
|
2308
|
+
isValidTestResult
|
|
1501
2309
|
};
|