reporting-labs 0.6.9 → 0.6.11
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 +39 -4
- package/dist/reporter.d.ts +15 -0
- package/dist/reporter.js +31 -0
- package/dist/template.html +249 -19
- package/dist/template.js +249 -19
- package/dist/types.d.ts +7 -0
- package/dist/wdio/env.d.ts +4 -0
- package/dist/wdio/env.js +62 -0
- package/dist/wdio/finalize.d.ts +38 -0
- package/dist/wdio/finalize.js +203 -0
- package/dist/wdio/index.d.ts +17 -0
- package/dist/wdio/index.js +22 -0
- package/dist/wdio/reporter.d.ts +67 -0
- package/dist/wdio/reporter.js +332 -0
- package/package.json +11 -2
package/dist/template.js
CHANGED
|
@@ -237,7 +237,7 @@ a{color:var(--accent)}
|
|
|
237
237
|
.slow li{display:flex;gap:10px;align-items:center;padding:6px 0;border-top:1px solid var(--line);font-size:13px}
|
|
238
238
|
.slow li:first-child{border-top:0}
|
|
239
239
|
.slow .bar{height:6px;border-radius:3px;background:var(--accent);opacity:.7}
|
|
240
|
-
.slow .t{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
240
|
+
.slow .t{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
241
241
|
.slow .d{font:12px var(--mono);color:var(--ink-3)}
|
|
242
242
|
.slow button{text-align:left;width:100%;display:flex;gap:10px;align-items:center}
|
|
243
243
|
.slow button:hover .t{color:var(--accent)}
|
|
@@ -273,7 +273,7 @@ a{color:var(--accent)}
|
|
|
273
273
|
/* ---------- detail ---------- */
|
|
274
274
|
.detail{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:22px;min-width:0}
|
|
275
275
|
.crumb{font-size:12px;color:var(--ink-3)} .crumb span+span::before{content:" / ";color:var(--line)}
|
|
276
|
-
.detail h3{font-size:20px;font-weight:600;margin:6px 0 10px;letter-spacing:-.01em}
|
|
276
|
+
.detail h3{font-size:20px;font-weight:600;margin:6px 0 10px;letter-spacing:-.01em;overflow-wrap:anywhere;min-width:0}
|
|
277
277
|
.badges{display:flex;gap:6px;flex-wrap:wrap;align-items:center;margin-bottom:18px}
|
|
278
278
|
.badge{font-size:12px;padding:3px 9px;border-radius:999px;font-weight:500}
|
|
279
279
|
.badge.passed{background:var(--pass-bg);color:var(--pass)} .badge.failed,.badge.timedOut,.badge.interrupted{background:var(--fail-bg);color:var(--fail)}
|
|
@@ -371,8 +371,8 @@ li.collapsed>ul{display:none}
|
|
|
371
371
|
.tags .bar i{display:block;height:100%}
|
|
372
372
|
/* meta chips in detail */
|
|
373
373
|
.metas{display:flex;flex-wrap:wrap;gap:6px;margin:-8px 0 18px}
|
|
374
|
-
.meta{display:inline-flex;align-items:center;gap:6px;font-size:12px;padding:4px 9px;border:1px solid var(--line);border-radius:4px;background:var(--surface-2)}
|
|
375
|
-
.meta .k{color:var(--ink-3)} .meta .v{font-weight:500}
|
|
374
|
+
.meta{display:inline-flex;align-items:center;gap:6px;font-size:12px;padding:4px 9px;border:1px solid var(--line);border-radius:4px;background:var(--surface-2);max-width:100%;min-width:0}
|
|
375
|
+
.meta .k{color:var(--ink-3);flex:none} .meta .v{font-weight:500;overflow-wrap:anywhere;min-width:0}
|
|
376
376
|
.meta.priority .v,.meta.severity .v{color:var(--fail)}
|
|
377
377
|
.meta.priority.low .v,.meta.severity.low .v{color:var(--ink)}
|
|
378
378
|
|
|
@@ -554,7 +554,7 @@ html,body{-webkit-font-smoothing:antialiased}
|
|
|
554
554
|
.dim .row[aria-pressed=true]{background:var(--accent-tint);outline:0}
|
|
555
555
|
.legend-inline{font-size:12px;gap:14px;margin-top:12px} .legend-inline span::before{width:9px;height:9px;border-radius:3px;margin-right:6px}
|
|
556
556
|
.slow li{padding:9px 0} .slow button{display:grid;grid-template-columns:1fr auto;gap:10px;align-items:start}
|
|
557
|
-
.slow .t{white-space:normal}
|
|
557
|
+
.slow .t{white-space:normal;overflow-wrap:anywhere;min-width:0}
|
|
558
558
|
.slow .bar{display:block;height:5px;border-radius:999px;background:var(--accent-soft);opacity:1;margin-top:6px;position:relative;overflow:hidden;width:100%!important}
|
|
559
559
|
.slow .bar i{position:absolute;inset:0;width:var(--w);background:var(--accent);border-radius:999px}
|
|
560
560
|
.slow button:hover .t{color:var(--accent-2)}
|
|
@@ -613,7 +613,7 @@ html,body{-webkit-font-smoothing:antialiased}
|
|
|
613
613
|
.attn2 td:first-child{border-radius:6px 0 0 6px} .attn2 td:last-child{border-radius:0 6px 6px 0}
|
|
614
614
|
.attn2 .pr{width:96px;white-space:nowrap;font-size:12.5px;line-height:1.35} .attn2 .pr .dot{display:inline-block;width:8px;height:8px;border-radius:50%;margin-right:7px}
|
|
615
615
|
.attn2 .pr span{font-weight:600;color:var(--ink)} .attn2 .pr small{display:block;color:var(--ink-3);font-size:11.5px;padding-left:15px}
|
|
616
|
-
.attn2 .tt .n{font-weight:500;color:var(--ink);line-height:1.35} .attn2 .tt .s{font-size:12px;color:var(--ink-3);margin-top:2px} .attn2 .tt .s b.new{color:var(--fail);font-weight:600} .attn2 .tt .s .rsn{color:var(--ink-2)}
|
|
616
|
+
.attn2 .tt{min-width:0} .attn2 .tt .n{font-weight:500;color:var(--ink);line-height:1.35;overflow-wrap:anywhere} .attn2 .tt .s{font-size:12px;color:var(--ink-3);margin-top:2px;overflow-wrap:anywhere} .attn2 .tt .s b.new{color:var(--fail);font-weight:600} .attn2 .tt .s .rsn{color:var(--ink-2)}
|
|
617
617
|
.attn2 .ow{text-align:right;color:var(--ink-3);font-size:12.5px;white-space:nowrap;width:1%;padding-top:10px}
|
|
618
618
|
.attn-foot{margin-top:auto;padding-top:12px;border-top:1px solid var(--line)} .attn-foot button{font-size:12.5px;font-weight:500;color:var(--accent-2)} .attn-foot button:hover{text-decoration:underline}
|
|
619
619
|
.clu2{list-style:none;margin:0;padding:0}
|
|
@@ -621,7 +621,7 @@ html,body{-webkit-font-smoothing:antialiased}
|
|
|
621
621
|
.clu2 .n{font-size:20px;font-weight:600;color:var(--fail);font-variant-numeric:tabular-nums;line-height:1.1} .clu2 .n small{display:block;font-size:10.5px;color:var(--ink-3);font-weight:500;letter-spacing:.04em;margin-top:1px}
|
|
622
622
|
.clu2 .b{min-width:0} .clu2 .msg{font:12.5px/1.45 var(--mono);color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
623
623
|
.clu2 .who{font-size:12.5px;color:var(--ink-2);margin-top:5px;line-height:1.45;display:flex;flex-direction:column;align-items:flex-start;gap:3px}
|
|
624
|
-
.clu2 .who button{color:var(--accent-2);font-weight:500;text-align:left;white-space:normal;max-width:100%;padding:0 0 0 14px;text-indent:-14px} .clu2 .who button .tri{display:inline-block;width:14px;text-indent:0;color:var(--ink-3);font-weight:400}
|
|
624
|
+
.clu2 .who button{color:var(--accent-2);font-weight:500;text-align:left;white-space:normal;overflow-wrap:anywhere;max-width:100%;padding:0 0 0 14px;text-indent:-14px} .clu2 .who button .tri{display:inline-block;width:14px;text-indent:0;color:var(--ink-3);font-weight:400}
|
|
625
625
|
.clu2 .who button:hover{text-decoration:underline} .clu2 .who button small{color:var(--ink-3);font-weight:400;font-size:11px}
|
|
626
626
|
.clu2 .who button.ex{color:var(--ink-3);font-weight:400;cursor:pointer;text-indent:0} .clu2 .who button.ex:hover{color:var(--accent-2);text-decoration:underline}
|
|
627
627
|
@media (max-width:640px){.attn2 .ow{display:none} .attn2 .pr{width:72px} .attn2 .pr small{display:none}}
|
|
@@ -632,9 +632,9 @@ html,body{-webkit-font-smoothing:antialiased}
|
|
|
632
632
|
.badge.xfail{background:var(--flaky-bg);color:var(--flaky-ink)}
|
|
633
633
|
.errwrap+.errwrap{margin-top:14px}
|
|
634
634
|
.why{border:1px solid var(--line);border-radius:var(--radius);padding:10px 14px;margin-bottom:10px;background:var(--surface)}
|
|
635
|
-
.why-c{font-size:12.5px;color:var(--ink);margin-bottom:2px;line-height:1.4} .why-c .why-k{margin-right:2px}
|
|
635
|
+
.why-c{font-size:12.5px;color:var(--ink);margin-bottom:2px;line-height:1.4;overflow-wrap:anywhere} .why-c .why-k{margin-right:2px}
|
|
636
636
|
.why-l{display:flex;align-items:center;gap:8px;margin-bottom:4px} .why-k{font-size:12px;font-weight:600;color:var(--fail)} .why-a{font:11.5px var(--mono);color:var(--ink-3)}
|
|
637
|
-
.why-s{font-size:var(--fs);color:var(--ink);line-height:1.45} .why-h{font-size:12.5px;color:var(--ink-2);margin-top:4px;line-height:1.45}
|
|
637
|
+
.why-s{font-size:var(--fs);color:var(--ink);line-height:1.45;overflow-wrap:anywhere} .why-h{font-size:12.5px;color:var(--ink-2);margin-top:4px;line-height:1.45;overflow-wrap:anywhere}
|
|
638
638
|
.why.assertion .why-k,.why.visual .why-k{color:#9A6A00}
|
|
639
639
|
.why.script .why-k,.why.thrown .why-k,.why.file .why-k{color:var(--ink-2)}
|
|
640
640
|
.errloc{font-family:var(--mono);color:var(--ink-3);margin-bottom:6px} .errloc a{color:var(--accent-2);text-decoration:none} .errloc a:hover{text-decoration:underline}
|
|
@@ -651,18 +651,18 @@ html,body{-webkit-font-smoothing:antialiased}
|
|
|
651
651
|
.badges .dots{margin-left:2px;margin-right:4px}
|
|
652
652
|
.flk{list-style:none;margin:0;padding:0} .flk li{border-top:1px solid var(--line)} .flk li:first-child{border-top:0}
|
|
653
653
|
.flk button{width:100%;text-align:left;padding:9px 4px;display:grid;grid-template-columns:1fr auto;gap:4px 12px;align-items:center}
|
|
654
|
-
.flk .t{font-size:13px;font-weight:500;color:var(--ink)} .flk .m{grid-column:1/-1;font-size:12px;color:var(--ink-3)}
|
|
654
|
+
.flk .t{font-size:13px;font-weight:500;color:var(--ink);overflow-wrap:anywhere;min-width:0} .flk .m{grid-column:1/-1;font-size:12px;color:var(--ink-3)}
|
|
655
655
|
.own{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:10px} .own li{border:1px solid var(--line);border-radius:10px;min-width:0}
|
|
656
656
|
.own button{width:100%;text-align:left;padding:10px 12px;display:grid;grid-template-columns:1fr auto;gap:2px 12px;align-items:center;border-radius:10px}
|
|
657
657
|
.own button:hover{background:var(--surface-2)}
|
|
658
|
-
.own .nm{font-weight:600} .own .cnts{font-size:12.5px;color:var(--ink-3);white-space:nowrap} .own .cnts b{font-weight:600}
|
|
658
|
+
.own .nm{font-weight:600;overflow-wrap:anywhere;min-width:0} .own .cnts{font-size:12.5px;color:var(--ink-3);white-space:nowrap} .own .cnts b{font-weight:600}
|
|
659
659
|
.own .cnts b.f{color:var(--fail)} .own .cnts b.k{color:var(--flaky-ink)}
|
|
660
660
|
.own .ts{grid-column:1/-1;font-size:12px;color:var(--ink-3);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
661
661
|
.fx-tools{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin-bottom:-4px} .fx-tools .fx-sum{font-size:13px;color:var(--ink-2);font-weight:500} .fx-tools .spacer{flex:1}
|
|
662
662
|
a.btn{text-decoration:none}
|
|
663
663
|
.skp{list-style:none;margin:0;padding:0} .skp li{border-top:1px solid var(--line)} .skp li:first-child{border-top:0}
|
|
664
664
|
.skp button{width:100%;text-align:left;padding:8px 4px;display:flex;flex-direction:column;gap:2px}
|
|
665
|
-
.skp .t{font-size:13px;font-weight:500;color:var(--ink)} .skp .r{font-size:12px;color:var(--ink-3)}
|
|
665
|
+
.skp .t{font-size:13px;font-weight:500;color:var(--ink);overflow-wrap:anywhere} .skp .r{font-size:12px;color:var(--ink-3)}
|
|
666
666
|
.gchart .gchart-head{display:flex;justify-content:space-between;align-items:flex-start;gap:12px}
|
|
667
667
|
.gchart .gchart-head h2{margin:0}
|
|
668
668
|
.gchart .gchart-sub{color:var(--ink-3);font-size:12px;margin-top:4px}
|
|
@@ -718,6 +718,104 @@ html,body{font-size:var(--fs)}
|
|
|
718
718
|
.nav .cnt,.chip,.step .cat,.folder,.item .d,.dim .k,.wk .n,.wk-sum,.attn2 .pr,.clu2 .n small,.flk .m,.own .cnts,.ediff .lbl,.ttip .tt-h span,.tags button,.hint-kbd,.trace-how,.api-col h5,.meta .k,.kv.env dt,.apitbl th,.hm thead th,.hm td button{font-family:var(--sans)}
|
|
719
719
|
.chip b,.loc,.file,.file-row,.kbd,.slow .d,.step .d,.hist .l,.wk .k,.tbl td,.kv2 dd,.kv.env dd,.logs,.txt,.err,.ediff .val,.ediff.lines,.clu2 .msg,.apitbl .u,.api-head .m,.api-head .sc,.api-head .d,.api-col pre,.badge.tag,.trend text,.tl text,.attn2 .tt .s .mono,.trace-how code{font-family:var(--mono)}
|
|
720
720
|
.chip{font-family:var(--sans)} .chip b{font-weight:500}
|
|
721
|
+
|
|
722
|
+
/* ---------- report footer: reportingLabs branding ---------- */
|
|
723
|
+
.rl-foot{max-width:1400px;margin:30px auto 0;padding:18px 28px 30px;border-top:1px solid var(--line);display:flex;align-items:center;gap:10px;color:var(--ink-3);font-size:12.5px}
|
|
724
|
+
.rl-foot-mark{width:22px;height:22px;display:inline-flex;flex:none}
|
|
725
|
+
.rl-foot-mark svg{width:22px;height:22px;display:block;border-radius:6px}
|
|
726
|
+
.rl-foot-txt b{color:var(--ink-2);font-weight:600}
|
|
727
|
+
@media print{ .rl-foot{display:none} }
|
|
728
|
+
|
|
729
|
+
/* ---------- print / PDF: a clean, client-shareable executive report ----------
|
|
730
|
+
Page 1 is a full executive summary (no wasted space); analytics, failure analysis, the
|
|
731
|
+
full test-case table and failure evidence each start on a fresh page. Per-test steps,
|
|
732
|
+
logs, API and console are left out so the PDF stays small and readable at 500+ tests. */
|
|
733
|
+
.printdoc{display:none}
|
|
734
|
+
@media print{
|
|
735
|
+
@page{ size:A4; margin:12mm 11mm 13mm; }
|
|
736
|
+
html,body{background:#fff!important}
|
|
737
|
+
#app{display:none!important}
|
|
738
|
+
.printdoc{display:block!important;max-width:none;margin:0;padding:0;color:var(--ink);font-size:12px}
|
|
739
|
+
.printdoc *{ -webkit-print-color-adjust:exact!important; print-color-adjust:exact!important; }
|
|
740
|
+
/* On paper nothing can be clicked or hovered, so never truncate text with an ellipsis or a
|
|
741
|
+
height clamp — wrap it and show it in full. Numbers, durations and status chips stay on one line. */
|
|
742
|
+
.printdoc .apitbl .u,.printdoc .apitbl .t,.printdoc .apitbl td,.printdoc .slow .t,.printdoc .proj .name,
|
|
743
|
+
.printdoc .file,.printdoc .item .tt .p,.printdoc .item .tt .n,.printdoc .item .d,.printdoc .step .t,
|
|
744
|
+
.printdoc .att figcaption,.printdoc .dim .k,.printdoc .attn .t,.printdoc .attn .t .tt,.printdoc .attn .sev,
|
|
745
|
+
.printdoc .attn2 .pr,.printdoc .attn2 .ow,.printdoc .attn2 .tt .n,.printdoc .attn2 .tt .s,
|
|
746
|
+
.printdoc .tbl td,.printdoc .tbl th,.printdoc .api-head .u,.printdoc .clu .msg,.printdoc .clu .n,
|
|
747
|
+
.printdoc .clu2 .msg,.printdoc .clu2 .who,.printdoc .clu2 .who button,.printdoc .own .ts,.printdoc .own .nm,
|
|
748
|
+
.printdoc .hist .b .l,.printdoc .loc,.printdoc .crumb span,.printdoc .pt-name,.printdoc .pt-sub,.printdoc .pt-spec{
|
|
749
|
+
white-space:normal!important;overflow:visible!important;text-overflow:clip!important;
|
|
750
|
+
max-width:none!important;max-height:none!important;overflow-wrap:break-word;-webkit-line-clamp:none!important;
|
|
751
|
+
}
|
|
752
|
+
/* durations / counts / status stay on one line so "9ms" never breaks to "9m s" */
|
|
753
|
+
.printdoc .apitbl td.t:last-child,.printdoc .num,.printdoc .pstatus,.printdoc .since-txt{white-space:nowrap!important;overflow-wrap:normal!important}
|
|
754
|
+
/* only genuinely unbreakable strings (urls, file paths, selectors) may break mid-token */
|
|
755
|
+
.printdoc .apitbl .u,.printdoc .api-head .u,.printdoc .pt-spec,.printdoc .loc,.printdoc .file,.printdoc .clu .msg,.printdoc .clu2 .msg{overflow-wrap:anywhere}
|
|
756
|
+
/* Content flows continuously — no forced page breaks between sections, which were leaving
|
|
757
|
+
whole pages nearly empty. Cards/rows just avoid being split across a page. */
|
|
758
|
+
.printdoc .psec{padding-top:0;margin-top:18px}
|
|
759
|
+
.printdoc .psec-h{font-size:16px;font-weight:600;letter-spacing:-.01em;margin:0 0 12px;padding-bottom:8px;border-bottom:2px solid var(--line);color:var(--ink);break-after:avoid}
|
|
760
|
+
/* Only small, atomic blocks avoid a page split. Big cards (clusters, needs-attention, the tables)
|
|
761
|
+
may flow across pages — keeping them whole is what left pages half-empty. */
|
|
762
|
+
.printdoc .gchart,.printdoc figure,.printdoc .kpi,.printdoc .hm,.printdoc .pev,.printdoc .pexec-card,.printdoc tr{break-inside:avoid}
|
|
763
|
+
.printdoc h4{break-after:avoid}
|
|
764
|
+
/* CSS grid does not fragment across print pages, so a tall two-column grid jumps to the next page
|
|
765
|
+
and leaves a gap. In print everything becomes a single full-width column that flows naturally. */
|
|
766
|
+
.printdoc .grid{display:block!important;margin:0}
|
|
767
|
+
.printdoc .grid>*{grid-column:auto!important;width:auto!important;max-width:none!important;margin:0 0 12px!important;break-inside:avoid}
|
|
768
|
+
.printdoc .grid>.card.w12,.printdoc .grid>.fail-rail,.printdoc .grid>.fx-tools{break-inside:auto}
|
|
769
|
+
.printdoc .graphs-grid>*{break-inside:avoid}
|
|
770
|
+
.printdoc .list,.printdoc .nav,.printdoc .band,.printdoc .stripe,.printdoc .fx-tools .btn,.printdoc .fx-tools .spacer{display:none!important}
|
|
771
|
+
/* drop interactive-only chrome and anything that cannot live on paper */
|
|
772
|
+
.printdoc .actions,.printdoc .gchart-head button,.printdoc .vids,.printdoc .cmp,.printdoc .hint-kbd,.printdoc .mask-note,.printdoc .clu2 .who .ex{display:none!important}
|
|
773
|
+
.printdoc a{color:var(--ink);text-decoration:none}
|
|
774
|
+
/* ---- cover / executive summary (page 1) ---- */
|
|
775
|
+
.printdoc .pcover{padding:2mm 0 0}
|
|
776
|
+
.printdoc .pcover-top{display:flex;align-items:center;gap:13px}
|
|
777
|
+
.printdoc .pcover .logo{width:44px;height:44px;flex:none}
|
|
778
|
+
.printdoc .pcover .logo svg{width:44px;height:44px;display:block;border-radius:11px}
|
|
779
|
+
.printdoc .pcover-title{font-size:25px;font-weight:600;letter-spacing:-.02em;line-height:1.1}
|
|
780
|
+
.printdoc .pcover-sub{color:var(--ink-3);font-size:12px;margin-top:3px}
|
|
781
|
+
.printdoc .pcover-meta{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px}
|
|
782
|
+
.printdoc .pverdict{font-size:17px;font-weight:600;margin:16px 0 2px}
|
|
783
|
+
.printdoc .pverdict.ok{color:var(--pass)} .printdoc .pverdict.bad{color:var(--fail)}
|
|
784
|
+
.printdoc .pcover-stats{display:flex;gap:10px;flex-wrap:wrap;margin:12px 0 4px}
|
|
785
|
+
.printdoc .pstat{border:1px solid var(--line);border-radius:10px;padding:9px 15px;min-width:84px;background:var(--surface)}
|
|
786
|
+
.printdoc .pstat .n{font-size:25px;font-weight:600;font-variant-numeric:tabular-nums;line-height:1.1}
|
|
787
|
+
.printdoc .pstat .l{font-size:10px;color:var(--ink-3);text-transform:uppercase;letter-spacing:.05em;margin-top:3px}
|
|
788
|
+
.printdoc .pexec{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-top:16px;align-items:start}
|
|
789
|
+
.printdoc .pexec-card{border:1px solid var(--line);border-radius:12px;padding:14px 16px;background:var(--surface)}
|
|
790
|
+
.printdoc .psub-h{font-size:12.5px;font-weight:600;color:var(--ink);margin:0 0 10px}
|
|
791
|
+
/* ---- the all-tests table ---- */
|
|
792
|
+
.printdoc .ptbl{width:100%;border-collapse:collapse;font-size:11px;table-layout:fixed}
|
|
793
|
+
.printdoc .ptbl thead{display:table-header-group}
|
|
794
|
+
.printdoc .ptbl th{text-align:left;font-size:10px;text-transform:uppercase;letter-spacing:.05em;color:var(--ink-3);font-weight:600;padding:7px 8px;border-bottom:2px solid var(--line)}
|
|
795
|
+
.printdoc .ptbl td{padding:7px 8px;border-bottom:1px solid var(--line);vertical-align:top;overflow-wrap:anywhere}
|
|
796
|
+
.printdoc .ptbl td.num{text-align:right;font-variant-numeric:tabular-nums;color:var(--ink-2);white-space:nowrap}
|
|
797
|
+
.printdoc .ptbl .pt-name{font-weight:500;color:var(--ink)}
|
|
798
|
+
.printdoc .ptbl .pt-sub{font-size:10px;color:var(--ink-3);margin-top:1px}
|
|
799
|
+
.printdoc .ptbl .pt-spec{font:10.5px var(--mono);color:var(--ink-2)}
|
|
800
|
+
.printdoc .pstatus{display:inline-flex;align-items:center;gap:6px;font-weight:500;white-space:nowrap}
|
|
801
|
+
.printdoc .pstatus .st{width:9px;height:9px;border-radius:50%;flex:none}
|
|
802
|
+
.printdoc .ptbl .c-n{width:30px} .printdoc .ptbl .c-pri{width:72px} .printdoc .ptbl .c-own{width:84px} .printdoc .ptbl .c-st{width:88px} .printdoc .ptbl .c-dur{width:62px}
|
|
803
|
+
/* ---- failure evidence ---- */
|
|
804
|
+
.printdoc .pev{padding:14px 0;border-top:1px solid var(--line)}
|
|
805
|
+
.printdoc .pev:first-child{border-top:0;padding-top:2px}
|
|
806
|
+
.printdoc .pev-head{display:flex;align-items:center;gap:9px}
|
|
807
|
+
.printdoc .pev-head .st{width:10px;height:10px;border-radius:50%;flex:none}
|
|
808
|
+
.printdoc .pev-title{font-size:14px;font-weight:600;letter-spacing:-.01em}
|
|
809
|
+
.printdoc .pev-loc{font:11px var(--mono);color:var(--ink-3);margin:3px 0 8px}
|
|
810
|
+
.printdoc .pev .why{margin-bottom:8px}
|
|
811
|
+
.printdoc .pev .att{display:flex;flex-wrap:wrap;gap:10px;margin-top:10px}
|
|
812
|
+
.printdoc .pev .att figure{flex:1 1 320px;min-width:0;margin:0}
|
|
813
|
+
.printdoc .pev .att img{width:100%;max-height:115mm;object-fit:contain;border:1px solid var(--line);border-radius:6px;background:#fff}
|
|
814
|
+
.printdoc .pev .att figcaption{font:11px var(--mono);color:var(--ink-3);padding:5px 7px;background:var(--surface-2);border-radius:0 0 6px 6px;margin-top:-6px}
|
|
815
|
+
.printdoc .pev .errfull,.printdoc .pev details{display:none!important}
|
|
816
|
+
.printdoc .pfoot{margin-top:5mm;padding-top:3mm;border-top:1px solid var(--line);font-size:10px;color:var(--ink-3)}
|
|
817
|
+
.printdoc .err,.printdoc pre{white-space:pre-wrap;word-break:break-word}
|
|
818
|
+
}
|
|
721
819
|
`;
|
|
722
820
|
const JS = String.raw `
|
|
723
821
|
(function(){
|
|
@@ -750,7 +848,8 @@ app.append(h('div',{class:'band'}, stripe(), header(), nav()),
|
|
|
750
848
|
h('div',{class:'view','data-view':'failures'}, h('div',{class:'view-pad'}, failuresView())),
|
|
751
849
|
h('div',{class:'view','data-view':'api'}, h('div',{class:'view-pad'}, apiView())),
|
|
752
850
|
h('div',{class:'view','data-view':'graphs'}, graphsView()),
|
|
753
|
-
hasTimelineView()? h('div',{class:'view','data-view':'timeline'}, h('div',{class:'view-pad'}, timelineView())) : null
|
|
851
|
+
hasTimelineView()? h('div',{class:'view','data-view':'timeline'}, h('div',{class:'view-pad'}, timelineView())) : null,
|
|
852
|
+
rlFooter());
|
|
754
853
|
select(state.selected || firstInteresting(), true);
|
|
755
854
|
showView(state.view);
|
|
756
855
|
window.addEventListener('keydown', e=>{
|
|
@@ -1045,6 +1144,7 @@ function header(){
|
|
|
1045
1144
|
const pal = h('select',{class:'pal','aria-label':'Palette',onchange:e=>{ html.setAttribute('data-palette',e.target.value); try{localStorage.setItem('rl-palette',e.target.value);}catch(e){} }},
|
|
1046
1145
|
[['lab','Blue'],['ocean','Ocean'],['ember','Ember'],['mono','Mono']].map(([v,l])=>h('option',{value:v,selected:html.getAttribute('data-palette')===v},l)));
|
|
1047
1146
|
const copy = h('button',{class:'btn',onclick:e=>copyText(summaryMarkdown(),e.currentTarget,'Copied for Slack')}, 'Copy summary');
|
|
1147
|
+
const pdf = h('button',{class:'btn',title:'Save the whole report as a PDF — every section, chart, cluster and screenshot, print-friendly',onclick:()=>exportPdf()}, 'Export PDF');
|
|
1048
1148
|
return h('header',{class:'hdr'},
|
|
1049
1149
|
data.options.logo ? h('img',{class:'logo brand',src:data.options.logo,alt:''}) : h('div',{class:'logo',title:'Generated by reportingLabs','aria-label':'reportingLabs'}, document.getElementById('rl-logo').content.cloneNode(true)),
|
|
1050
1150
|
data.options.project ? h('div',{class:'proj-blk'}, h('h1',{}, data.title),
|
|
@@ -1055,7 +1155,17 @@ function header(){
|
|
|
1055
1155
|
h('div',{class:'hmeta'}, Object.entries(data.metadata).map(([k,v])=>h('span',{class:'chip'}, k+' ', h('b',{},v)))),
|
|
1056
1156
|
h('div',{class:'spacer'}),
|
|
1057
1157
|
h('span',{class:'when'}, new Date(data.startTime).toLocaleString()+' · '+ms(data.duration)+' · '+data.workers+' worker'+(data.workers===1?'':'s')),
|
|
1058
|
-
h('div',{class:'ctl'}, copy, pal, toggle));
|
|
1158
|
+
h('div',{class:'ctl'}, copy, pdf, pal, toggle));
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
/* ---------- footer: reportingLabs branding ---------- */
|
|
1162
|
+
function rlFooter(){
|
|
1163
|
+
const src=document.getElementById('rl-logo').content.cloneNode(true).querySelector('svg');
|
|
1164
|
+
const svg=src.outerHTML.replace(/(id="|url\(#|href="#)(rl-[a-z]+)/g,'$1$2-f'); // unique ids vs the header's copy
|
|
1165
|
+
return h('footer',{class:'rl-foot'},
|
|
1166
|
+
h('span',{class:'rl-foot-mark',html:svg}),
|
|
1167
|
+
h('span',{class:'rl-foot-txt'}, 'Generated by ', h('b',{},'reportingLabs'),
|
|
1168
|
+
data.generatedAt? ' · '+new Date(data.generatedAt).toLocaleString() : null));
|
|
1059
1169
|
}
|
|
1060
1170
|
|
|
1061
1171
|
/* ---------- summary ---------- */
|
|
@@ -1580,8 +1690,9 @@ function select(id, quiet){
|
|
|
1580
1690
|
state.selected=id; state.retry=t.results.length-1; if(!quiet){ if(state.view!=='tests') showView('tests'); else setHash(); } refresh(); renderDetail(t);
|
|
1581
1691
|
const it=document.querySelector('.item[data-id="'+id+'"]'); if(it&&it.scrollIntoView) it.scrollIntoView({block:'nearest'});
|
|
1582
1692
|
}
|
|
1583
|
-
function renderDetail(t){
|
|
1584
|
-
|
|
1693
|
+
function renderDetail(t, d, retryIdx){
|
|
1694
|
+
d=d||$('#detail'); d.innerHTML='';
|
|
1695
|
+
const retry = retryIdx==null ? state.retry : retryIdx;
|
|
1585
1696
|
d.append(h('div',{class:'crumb'}, [t.file, ...t.path.map(p=>data.bdd&&!/^Feature:/i.test(p)?'Feature: '+p:p)].map(p=>h('span',{},p))),
|
|
1586
1697
|
h('div',{class:'titlebar'}, h('h3',{},t.title), h('div',{class:'actions'},
|
|
1587
1698
|
h('button',{class:'btn',onclick:e=>copyText(location.href.split('#')[0]+'#t='+t.id,e.currentTarget,'Link copied')},'Copy link'),
|
|
@@ -1594,11 +1705,11 @@ function renderDetail(t){
|
|
|
1594
1705
|
const otherAnn=t.annotations.filter(a=>!DIMS.includes(a.type.toLowerCase()) && !(a.type.toLowerCase() in t.meta));
|
|
1595
1706
|
if(otherAnn.length) d.append(h('h4',{},'Annotations'), h('dl',{class:'kv'}, otherAnn.map(a=>[h('dt',{},a.type),h('dd',{},a.description||'')])));
|
|
1596
1707
|
if(t.results.length>1){
|
|
1597
|
-
d.append(h('div',{class:'tabs'}, t.results.map((r,i)=>h('button',{class:'tab','aria-selected':
|
|
1708
|
+
d.append(h('div',{class:'tabs'}, t.results.map((r,i)=>h('button',{class:'tab','aria-selected':retry===i,onclick:()=>{state.retry=i;renderDetail(t);}}, (i===0?'Attempt 1':'Retry '+i)+' · '+(label[r.status]||r.status)))));
|
|
1598
1709
|
}
|
|
1599
|
-
const r=t.results[
|
|
1710
|
+
const r=t.results[retry]; if(!r){ d.append(h('p',{class:'empty'},'This test did not run.')); return; }
|
|
1600
1711
|
const body=h('div',{});
|
|
1601
|
-
if(t.note&&
|
|
1712
|
+
if(t.note&&retry===t.results.length-1) body.append(h('div',{class:'note '+(t.outcome==='passed'?'ok':'warn')}, t.note));
|
|
1602
1713
|
if(r.errors.length){ body.append(h('h4',{},r.errors.length>1?'Errors':'Error'), ...r.errors.map(errorView)); }
|
|
1603
1714
|
if(r.steps.length){ const stepTotal=Math.max(1, r.duration||0, r.steps.reduce((a,x)=>a+x.duration,0)); body.append(h('h4',{},data.bdd?'Scenario steps':'Steps', h('span',{class:'hint'},'bar = share of '+ms(stepTotal))), stepTree(r.steps, stepTotal)); }
|
|
1604
1715
|
if(r.logs.length){ const t0=r.startTime; body.append(h('h4',{},'Log'), h('div',{class:'logs'}, r.logs.map(l=>h('div',{class:'ln'+(/\b(error|fail|exception)\b/i.test(l.msg)?' err':/\bwarn/i.test(l.msg)?' warn':'')}, h('span',{class:'ts'},'+'+ms(Math.max(0,l.t-t0))), h('span',{class:'lm'},l.msg))))); }
|
|
@@ -1754,5 +1865,124 @@ function openBugReport(t){
|
|
|
1754
1865
|
}
|
|
1755
1866
|
function lightbox(src){ const lb=h('div',{class:'lb',onclick:()=>lb.remove()}, h('img',{src})); document.body.append(lb); }
|
|
1756
1867
|
function escape(s){ return s.replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c])); }
|
|
1868
|
+
|
|
1869
|
+
/* ---------- print / PDF ----------
|
|
1870
|
+
One builder serves both the "Export PDF" button (window.print) and headless page.pdf():
|
|
1871
|
+
Chromium fires 'beforeprint' for both. We assemble the whole report — cover, overview,
|
|
1872
|
+
failures, graphs, API and every test's detail expanded — into a .printdoc, force the light
|
|
1873
|
+
theme for paper, print, then tear it all down so the live app is untouched. Video is skipped. */
|
|
1874
|
+
function buildPrintDoc(){
|
|
1875
|
+
if(document.querySelector('.printdoc')) return;
|
|
1876
|
+
const html=document.documentElement;
|
|
1877
|
+
html.setAttribute('data-rl-theme-prev', html.getAttribute('data-theme')||'');
|
|
1878
|
+
html.setAttribute('data-theme','light'); // always light on paper, whatever the screen theme
|
|
1879
|
+
const pd=h('div',{class:'printdoc'});
|
|
1880
|
+
const s=data.stats, f=s.failed+s.timedOut+s.interrupted, ran=s.total-s.skipped, rate=ran?Math.round(s.passed/ran*100):0;
|
|
1881
|
+
const stat=(n,l,c)=>h('div',{class:'pstat'}, h('div',{class:'n',style:c?'color:'+c:''},String(n)), h('div',{class:'l'},l));
|
|
1882
|
+
let logo;
|
|
1883
|
+
if(data.options.logo){ logo = h('img',{src:data.options.logo,alt:'',style:'width:40px;height:40px;border-radius:8px'}); }
|
|
1884
|
+
else {
|
|
1885
|
+
// The live header already holds one copy of this SVG; in print that copy is display:none, and its
|
|
1886
|
+
// gradient/clip ids would win over an identical clone (first id in the document). Rewrite the ids so
|
|
1887
|
+
// the cover's logo paints its gradients and flask clip correctly.
|
|
1888
|
+
const src=document.getElementById('rl-logo').content.cloneNode(true).querySelector('svg');
|
|
1889
|
+
const html2=src.outerHTML.replace(/(id="|url\(#|href="#)(rl-[a-z]+)/g,'$1$2-p');
|
|
1890
|
+
const holder=h('span',{class:'logo',style:'display:inline-flex;width:40px;height:40px',html:html2});
|
|
1891
|
+
logo=holder;
|
|
1892
|
+
}
|
|
1893
|
+
const verdict = f ? (f+' of '+s.total+' test'+(s.total===1?'':'s')+' failed'+(s.flaky?', '+s.flaky+' flaky':''))
|
|
1894
|
+
: (ran===0 ? 'No tests ran' : 'All '+ran+' test'+(ran===1?'':'s')+' passed'+(s.flaky?', '+s.flaky+' flaky':'')+(s.skipped?' · '+s.skipped+' skipped':''));
|
|
1895
|
+
// ---- page 1: executive summary ----
|
|
1896
|
+
pd.append(h('div',{class:'pcover'},
|
|
1897
|
+
h('div',{class:'pcover-top'}, logo,
|
|
1898
|
+
h('div',{}, h('div',{class:'pcover-title'}, data.title),
|
|
1899
|
+
h('div',{class:'pcover-sub'}, (data.options.project?[data.options.project.name, data.options.project.version?'v'+data.options.project.version:null, data.options.project.team].filter(Boolean).join(' · ')+' · ':'')
|
|
1900
|
+
+ new Date(data.startTime).toLocaleString()+' · '+ms(data.duration)+' · '+data.workers+' worker'+(data.workers===1?'':'s')))),
|
|
1901
|
+
Object.keys(data.metadata).length? h('div',{class:'pcover-meta'}, Object.entries(data.metadata).map(([k,v])=>h('span',{class:'chip'}, k+' ', h('b',{},v)))) : null,
|
|
1902
|
+
h('div',{class:'pverdict '+(f?'bad':'ok')}, verdict),
|
|
1903
|
+
h('div',{class:'pcover-stats'},
|
|
1904
|
+
stat(rate+'%','Pass rate'), stat(s.passed,'Passed','var(--pass)'),
|
|
1905
|
+
f?stat(f,'Failed','var(--fail)'):null, s.flaky?stat(s.flaky,'Flaky','var(--flaky)'):null,
|
|
1906
|
+
s.skipped?stat(s.skipped,'Skipped','var(--ink-3)'):null, stat(s.total,'Total'))));
|
|
1907
|
+
const pexec=h('div',{class:'pexec'});
|
|
1908
|
+
pexec.append(h('div',{class:'pexec-card'}, h('div',{class:'psub-h'},'Pass rate'), donut()));
|
|
1909
|
+
const envc=printEnvCard(); if(envc) pexec.append(envc);
|
|
1910
|
+
pd.append(pexec);
|
|
1911
|
+
|
|
1912
|
+
// The first section flows right after the executive summary so page 1 is never half-empty;
|
|
1913
|
+
// the rest start on a fresh page.
|
|
1914
|
+
let first=true;
|
|
1915
|
+
const sec=(title,node)=>{ if(!node) return; pd.append(h('section',{class:'psec'+(first?'':' brk')}, h('h2',{class:'psec-h'},title), node)); first=false; };
|
|
1916
|
+
// ---- analytics, failure analysis, full inventory, evidence ----
|
|
1917
|
+
if(data.tests.length) sec('Test analytics', graphsView());
|
|
1918
|
+
if(f + s.flaky > 0){
|
|
1919
|
+
const fn=failuresView();
|
|
1920
|
+
// On paper there is no "+N more" to click, so expand every cluster to list all its tests.
|
|
1921
|
+
fn.querySelectorAll('.clu2 .who .ex').forEach(b=>{ try{ b.click(); }catch(e){} });
|
|
1922
|
+
sec('Failure analysis', fn);
|
|
1923
|
+
}
|
|
1924
|
+
sec('All test cases', allTestsTable());
|
|
1925
|
+
const bad=[...data.tests].filter(t=>isFail(t.outcome)||t.outcome==='flaky').sort((a,b)=>rank(a)-rank(b)||a.title.localeCompare(b.title));
|
|
1926
|
+
if(bad.length){ const ev=h('div',{}); for(const t of bad) ev.append(printEvidence(t)); sec('Failure evidence', ev); }
|
|
1927
|
+
pd.append(h('div',{class:'pfoot'}, 'Generated by reportingLabs · '+new Date().toLocaleString()));
|
|
1928
|
+
document.body.append(pd);
|
|
1929
|
+
}
|
|
1930
|
+
function printEnvCard(){
|
|
1931
|
+
if(!data.env||!data.env.length) return null;
|
|
1932
|
+
return h('div',{class:'pexec-card'}, h('div',{class:'psub-h'},'Environment'),
|
|
1933
|
+
h('dl',{class:'kv env'}, data.env.flatMap(r=>[h('dt',{},r.k), h('dd',{}, r.href? h('a',{href:r.href},r.v) : r.v)])));
|
|
1934
|
+
}
|
|
1935
|
+
function allTestsTable(){
|
|
1936
|
+
const hasPri=data.tests.some(t=>t.meta.priority||t.meta.severity), hasOwn=data.tests.some(t=>t.meta.owner), multiProj=data.projects.length>1;
|
|
1937
|
+
const cols=[['c-n','#'],['','Test'],['','Spec'], hasPri?['c-pri','Priority']:null, hasOwn?['c-own','Owner']:null, ['c-st','Status'], ['c-dur','Duration']].filter(Boolean);
|
|
1938
|
+
const order=[...data.tests].sort((a,b)=>((isFail(b.outcome)||b.outcome==='flaky')-(isFail(a.outcome)||a.outcome==='flaky'))||rank(a)-rank(b)||a.file.localeCompare(b.file)||a.title.localeCompare(b.title));
|
|
1939
|
+
let i=0;
|
|
1940
|
+
return h('table',{class:'ptbl'},
|
|
1941
|
+
h('colgroup',{}, cols.map(([c])=>h('col',{class:c||''}))),
|
|
1942
|
+
h('thead',{}, h('tr',{}, cols.map(([,l])=>h('th',{},l)))),
|
|
1943
|
+
h('tbody',{}, order.map(t=>{ i++; return h('tr',{}, [
|
|
1944
|
+
h('td',{class:'num'}, String(i)),
|
|
1945
|
+
h('td',{}, h('div',{class:'pt-name'}, t.title), t.path.length? h('div',{class:'pt-sub'}, t.path.join(' › ')) : null),
|
|
1946
|
+
h('td',{}, h('span',{class:'pt-spec'}, t.file.split('/').pop()+(multiProj?' · '+t.project:''))),
|
|
1947
|
+
hasPri? h('td',{}, [t.meta.priority,t.meta.severity].filter(Boolean).join(' · ')) : null,
|
|
1948
|
+
hasOwn? h('td',{}, t.meta.owner||'') : null,
|
|
1949
|
+
h('td',{}, h('span',{class:'pstatus'}, h('span',{class:'st '+t.outcome,style:'background:'+colorOf(bucket(t))}), label[t.outcome])),
|
|
1950
|
+
h('td',{class:'num'}, ms(t.duration)),
|
|
1951
|
+
].filter(x=>x!==null)); })));
|
|
1952
|
+
}
|
|
1953
|
+
function printEvidence(t){
|
|
1954
|
+
const r=t.results[t.results.length-1], e=r&&r.errors[0];
|
|
1955
|
+
const art=h('article',{class:'pev'});
|
|
1956
|
+
art.append(h('div',{class:'pev-head'}, h('span',{class:'st',style:'background:'+colorOf(bucket(t))}), h('span',{class:'pev-title'}, t.title), h('span',{class:'badge '+t.outcome}, label[t.outcome])));
|
|
1957
|
+
art.append(h('div',{class:'pev-loc'}, t.file+':'+t.line+(data.projects.length>1?' · '+t.project:'')+' · '+ms(t.duration)+(t.results.length>1?' · '+t.results.length+' attempts':'')+(t.meta.owner?' · '+t.meta.owner:'')+(t.meta.priority?' · '+t.meta.priority:'')));
|
|
1958
|
+
if(e){ if(e.explain) art.append(whyView(e.explain)); art.append(errorBody(e)); }
|
|
1959
|
+
else art.append(h('div',{style:'color:var(--ink-3)'}, t.outcome==='flaky'?'Passed on retry after an earlier failure.':'No error message was recorded.'));
|
|
1960
|
+
const imgs=(r?r.attachments:[]).filter(a=>a.src&&(a.contentType||'').startsWith('image/')&&!/-(expected|actual|diff)(\.\w+)?$/.test(a.name));
|
|
1961
|
+
if(imgs.length) art.append(h('div',{class:'att'}, imgs.slice(0,2).map(a=>h('figure',{}, h('img',{src:a.src,alt:a.name}), h('figcaption',{},a.name)))));
|
|
1962
|
+
return art;
|
|
1963
|
+
}
|
|
1964
|
+
function teardownPrintDoc(){
|
|
1965
|
+
const pd=document.querySelector('.printdoc'); if(pd) pd.remove();
|
|
1966
|
+
const html=document.documentElement, prev=html.getAttribute('data-rl-theme-prev');
|
|
1967
|
+
if(prev!=null){ if(prev) html.setAttribute('data-theme',prev); else html.removeAttribute('data-theme'); html.removeAttribute('data-rl-theme-prev'); }
|
|
1968
|
+
}
|
|
1969
|
+
// Build the print document and wait for every screenshot (a data: URI) to actually decode, so it
|
|
1970
|
+
// is painted — otherwise a print fired right after the DOM is built captures blank image boxes.
|
|
1971
|
+
async function preparePrint(){
|
|
1972
|
+
buildPrintDoc();
|
|
1973
|
+
try{ await Promise.all([...document.querySelectorAll('.printdoc img')].map(im=> im.decode? im.decode().catch(()=>{}) : Promise.resolve())); }catch(e){}
|
|
1974
|
+
return document.querySelectorAll('.printdoc img').length;
|
|
1975
|
+
}
|
|
1976
|
+
async function exportPdf(){
|
|
1977
|
+
await preparePrint();
|
|
1978
|
+
try{ window.print(); }catch(e){}
|
|
1979
|
+
}
|
|
1980
|
+
// A headless driver (the reporter's auto-generated report.pdf) awaits this, then calls page.pdf().
|
|
1981
|
+
window.reportingLabsPreparePrint = preparePrint;
|
|
1982
|
+
// A plain headless Chrome launched with --print-to-pdf on this file with ?rl-print (how the Java/CLI
|
|
1983
|
+
// generator renders report.pdf) has no way to call the hook, so build the print document on load instead.
|
|
1984
|
+
if(/[?&#]rl-print\b/.test(location.href)){ if(document.readyState!=='loading') preparePrint(); else window.addEventListener('DOMContentLoaded', preparePrint); }
|
|
1985
|
+
window.addEventListener('beforeprint', buildPrintDoc); // native Ctrl+P: build if not already built
|
|
1986
|
+
window.addEventListener('afterprint', teardownPrintDoc);
|
|
1757
1987
|
})();
|
|
1758
1988
|
`;
|
package/dist/types.d.ts
CHANGED
|
@@ -17,6 +17,13 @@ export interface ReportingLabsOptions {
|
|
|
17
17
|
emitJson?: boolean;
|
|
18
18
|
/** File name of the JSON blob inside outputFolder. Default: "report.json" */
|
|
19
19
|
jsonFile?: string;
|
|
20
|
+
/** Also write a print-ready `report.pdf` next to the HTML, rendered by headless Chromium from the
|
|
21
|
+
* report's own PDF layout (executive summary, charts, failure analysis, the full test-case table and
|
|
22
|
+
* screenshots of failures). Needs the Chromium that Playwright already provides. Default: true —
|
|
23
|
+
* set `false` to skip, or `{ file }` to rename. The report also has an "Export PDF" button. */
|
|
24
|
+
pdf?: boolean | {
|
|
25
|
+
file?: string;
|
|
26
|
+
};
|
|
20
27
|
/** Inline screenshots as base64 (single file, opens anywhere). Default: true */
|
|
21
28
|
embedAttachments?: boolean;
|
|
22
29
|
/** Max size (bytes) of a single attachment to embed. Larger ones are copied as files. Default: 2 MB */
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ciRunLabel } from '../reporter';
|
|
2
|
+
import type { EnvRow } from '../types';
|
|
3
|
+
export { ciRunLabel };
|
|
4
|
+
export declare function collectEnv(base: string, extra: Record<string, string> | undefined, browsers: string[], workers: number, metadataBranch?: string): EnvRow[];
|
package/dist/wdio/env.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ciRunLabel = void 0;
|
|
37
|
+
exports.collectEnv = collectEnv;
|
|
38
|
+
/** Environment-card rows for the WDIO report, reusing the Playwright reporter's CI/git helpers. */
|
|
39
|
+
const os = __importStar(require("os"));
|
|
40
|
+
const reporter_1 = require("../reporter");
|
|
41
|
+
Object.defineProperty(exports, "ciRunLabel", { enumerable: true, get: function () { return reporter_1.ciRunLabel; } });
|
|
42
|
+
function collectEnv(base, extra, browsers, workers, metadataBranch) {
|
|
43
|
+
const env = process.env;
|
|
44
|
+
const rows = [];
|
|
45
|
+
rows.push({ k: 'Node', v: process.version });
|
|
46
|
+
rows.push({ k: 'OS', v: `${os.type()} ${os.release()} (${os.arch()})` });
|
|
47
|
+
if (browsers.length)
|
|
48
|
+
rows.push({ k: 'Browsers', v: browsers.join(', ') });
|
|
49
|
+
if (workers > 1)
|
|
50
|
+
rows.push({ k: 'Workers', v: String(workers) });
|
|
51
|
+
const ci = (0, reporter_1.ciLink)(env);
|
|
52
|
+
if (ci)
|
|
53
|
+
rows.push({ k: 'CI', v: ci.name, href: ci.url });
|
|
54
|
+
const git = (0, reporter_1.gitInfo)(base, env);
|
|
55
|
+
if (git.sha)
|
|
56
|
+
rows.push({ k: 'Commit', v: `${git.sha.slice(0, 7)}${git.author ? ' · ' + git.author : ''}${git.subject ? ' · ' + git.subject : ''}`, href: git.url });
|
|
57
|
+
if (git.branch && !metadataBranch)
|
|
58
|
+
rows.push({ k: 'Branch', v: git.branch });
|
|
59
|
+
for (const [k, v] of Object.entries(extra || {}))
|
|
60
|
+
rows.push({ k, v: String(v), href: /^https?:\/\//.test(String(v)) ? String(v) : undefined });
|
|
61
|
+
return rows;
|
|
62
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface ReportingLabsCompleteOptions {
|
|
2
|
+
outputFolder?: string;
|
|
3
|
+
outputFile?: string;
|
|
4
|
+
jsonFile?: string;
|
|
5
|
+
emitJson?: boolean;
|
|
6
|
+
pdf?: boolean | {
|
|
7
|
+
file?: string;
|
|
8
|
+
};
|
|
9
|
+
title?: string;
|
|
10
|
+
logo?: string;
|
|
11
|
+
accent?: string;
|
|
12
|
+
theme?: 'auto' | 'light' | 'dark';
|
|
13
|
+
palette?: 'lab' | 'ocean' | 'ember' | 'mono';
|
|
14
|
+
metadata?: Record<string, string>;
|
|
15
|
+
env?: Record<string, string>;
|
|
16
|
+
sections?: {
|
|
17
|
+
title: string;
|
|
18
|
+
html: string;
|
|
19
|
+
}[];
|
|
20
|
+
customCss?: string;
|
|
21
|
+
dimensions?: string[];
|
|
22
|
+
dimensionOrder?: Record<string, string[]>;
|
|
23
|
+
project?: unknown;
|
|
24
|
+
links?: Record<string, unknown>;
|
|
25
|
+
bdd?: boolean;
|
|
26
|
+
announce?: boolean;
|
|
27
|
+
history?: {
|
|
28
|
+
enabled?: boolean;
|
|
29
|
+
file?: string;
|
|
30
|
+
keep?: number;
|
|
31
|
+
};
|
|
32
|
+
maskKeys?: string[];
|
|
33
|
+
maskValues?: string[];
|
|
34
|
+
maskFromEnv?: boolean;
|
|
35
|
+
embedFonts?: boolean;
|
|
36
|
+
editorLinks?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function reportingLabsComplete(options?: ReportingLabsCompleteOptions): Promise<string | undefined>;
|