@arghajit/playwright-pulse-report 0.3.2 → 0.3.3
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/LICENSE +21 -0
- package/README.md +3 -3
- package/package.json +3 -4
- package/scripts/generate-email-report.mjs +1 -1
- package/scripts/generate-report.mjs +1540 -385
- package/scripts/generate-static-report.mjs +3701 -844
- package/scripts/generate-trend.mjs +0 -0
- package/scripts/merge-pulse-report.js +152 -37
- package/scripts/sendReport.mjs +150 -65
- package/dist/reporter/tsconfig.reporter.tsbuildinfo +0 -1
|
@@ -98,12 +98,12 @@ export function ansiToHtml(text) {
|
|
|
98
98
|
if (codes[code]) {
|
|
99
99
|
if (code === "39") {
|
|
100
100
|
currentStylesArray = currentStylesArray.filter(
|
|
101
|
-
(s) => !s.startsWith("color:")
|
|
101
|
+
(s) => !s.startsWith("color:"),
|
|
102
102
|
);
|
|
103
103
|
currentStylesArray.push("color:inherit");
|
|
104
104
|
} else if (code === "49") {
|
|
105
105
|
currentStylesArray = currentStylesArray.filter(
|
|
106
|
-
(s) => !s.startsWith("background-color:")
|
|
106
|
+
(s) => !s.startsWith("background-color:"),
|
|
107
107
|
);
|
|
108
108
|
currentStylesArray.push("background-color:inherit");
|
|
109
109
|
} else {
|
|
@@ -114,10 +114,10 @@ export function ansiToHtml(text) {
|
|
|
114
114
|
const type = parts[0] === "38" ? "color" : "background-color";
|
|
115
115
|
if (parts.length === 5) {
|
|
116
116
|
currentStylesArray = currentStylesArray.filter(
|
|
117
|
-
(s) => !s.startsWith(type + ":")
|
|
117
|
+
(s) => !s.startsWith(type + ":"),
|
|
118
118
|
);
|
|
119
119
|
currentStylesArray.push(
|
|
120
|
-
`${type}:rgb(${parts[2]},${parts[3]},${parts[4]})
|
|
120
|
+
`${type}:rgb(${parts[2]},${parts[3]},${parts[4]})`,
|
|
121
121
|
);
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -175,7 +175,7 @@ function convertPlaywrightErrorToHTML(str) {
|
|
|
175
175
|
if (!str) return "";
|
|
176
176
|
return str
|
|
177
177
|
.replace(/^(\s+)/gm, (match) =>
|
|
178
|
-
match.replace(/ /g, " ").replace(/\t/g, " ")
|
|
178
|
+
match.replace(/ /g, " ").replace(/\t/g, " "),
|
|
179
179
|
)
|
|
180
180
|
.replace(/<red>/g, '<span style="color: red;">')
|
|
181
181
|
.replace(/<green>/g, '<span style="color: green;">')
|
|
@@ -265,7 +265,7 @@ function generateTestTrendsChart(trendData) {
|
|
|
265
265
|
.substring(2, 7)}`;
|
|
266
266
|
const renderFunctionName = `renderTestTrendsChart_${chartId.replace(
|
|
267
267
|
/-/g,
|
|
268
|
-
"_"
|
|
268
|
+
"_",
|
|
269
269
|
)}`;
|
|
270
270
|
const runs = trendData.overall;
|
|
271
271
|
|
|
@@ -360,7 +360,7 @@ function generateDurationTrendChart(trendData) {
|
|
|
360
360
|
.substring(2, 7)}`;
|
|
361
361
|
const renderFunctionName = `renderDurationTrendChart_${chartId.replace(
|
|
362
362
|
/-/g,
|
|
363
|
-
"_"
|
|
363
|
+
"_",
|
|
364
364
|
)}`;
|
|
365
365
|
const runs = trendData.overall;
|
|
366
366
|
|
|
@@ -455,7 +455,7 @@ function generateTestHistoryChart(history) {
|
|
|
455
455
|
if (!history || history.length === 0)
|
|
456
456
|
return '<div class="no-data-chart">No data for chart</div>';
|
|
457
457
|
const validHistory = history.filter(
|
|
458
|
-
(h) => h && typeof h.duration === "number" && h.duration >= 0
|
|
458
|
+
(h) => h && typeof h.duration === "number" && h.duration >= 0,
|
|
459
459
|
);
|
|
460
460
|
if (validHistory.length === 0)
|
|
461
461
|
return '<div class="no-data-chart">No valid data for chart</div>';
|
|
@@ -465,7 +465,7 @@ function generateTestHistoryChart(history) {
|
|
|
465
465
|
.substring(2, 7)}`;
|
|
466
466
|
const renderFunctionName = `renderTestHistoryChart_${chartId.replace(
|
|
467
467
|
/-/g,
|
|
468
|
-
"_"
|
|
468
|
+
"_",
|
|
469
469
|
)}`;
|
|
470
470
|
|
|
471
471
|
const seriesDataPoints = validHistory.map((run) => {
|
|
@@ -499,12 +499,12 @@ function generateTestHistoryChart(history) {
|
|
|
499
499
|
const accentColorRGB = "103, 58, 183"; // Assuming var(--accent-color) is Deep Purple #673ab7
|
|
500
500
|
|
|
501
501
|
const categoriesString = JSON.stringify(
|
|
502
|
-
validHistory.map((_, i) => `R${i + 1}`)
|
|
502
|
+
validHistory.map((_, i) => `R${i + 1}`),
|
|
503
503
|
);
|
|
504
504
|
const seriesDataPointsString = JSON.stringify(seriesDataPoints);
|
|
505
505
|
|
|
506
506
|
return `
|
|
507
|
-
<div id="${chartId}" style="width: 320px; height: 100px;" class="lazy-load-chart" data-render-function-name="${renderFunctionName}">
|
|
507
|
+
<div id="${chartId}" style="width: 100%; max-width: 320px; height: 100px;" class="lazy-load-chart" data-render-function-name="${renderFunctionName}">
|
|
508
508
|
<div class="no-data-chart">Loading History...</div>
|
|
509
509
|
</div>
|
|
510
510
|
<script>
|
|
@@ -568,7 +568,7 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
|
|
|
568
568
|
}
|
|
569
569
|
const passedEntry = data.find((d) => d.label === "Passed");
|
|
570
570
|
const passedPercentage = Math.round(
|
|
571
|
-
((passedEntry ? passedEntry.value : 0) / total) * 100
|
|
571
|
+
((passedEntry ? passedEntry.value : 0) / total) * 100,
|
|
572
572
|
);
|
|
573
573
|
|
|
574
574
|
const chartId = `pieChart-${Date.now()}-${Math.random()
|
|
@@ -614,13 +614,11 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
|
|
|
614
614
|
{
|
|
615
615
|
chart: {
|
|
616
616
|
type: 'pie',
|
|
617
|
-
width:
|
|
618
|
-
height: ${
|
|
619
|
-
chartHeight - 40
|
|
620
|
-
}, // Adjusted height to make space for legend if chartHeight is for the whole wrapper
|
|
617
|
+
width: null,
|
|
618
|
+
height: ${chartHeight - 40},
|
|
621
619
|
backgroundColor: 'transparent',
|
|
622
620
|
plotShadow: false,
|
|
623
|
-
spacingBottom: 40
|
|
621
|
+
spacingBottom: 40
|
|
624
622
|
},
|
|
625
623
|
title: {
|
|
626
624
|
text: '${passedPercentage}%',
|
|
@@ -670,8 +668,8 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
|
|
|
670
668
|
<div class="pie-chart-wrapper" style="align-items: center; max-height: 450px">
|
|
671
669
|
<div style="display: flex; align-items: start; width: 100%;"><h3>Test Distribution</h3></div>
|
|
672
670
|
<div id="${chartId}" style="width: ${chartWidth}px; height: ${
|
|
673
|
-
|
|
674
|
-
|
|
671
|
+
chartHeight - 40
|
|
672
|
+
}px;"></div>
|
|
675
673
|
<script>
|
|
676
674
|
document.addEventListener('DOMContentLoaded', function() {
|
|
677
675
|
if (typeof Highcharts !== 'undefined') {
|
|
@@ -690,7 +688,7 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
|
|
|
690
688
|
</div>
|
|
691
689
|
`;
|
|
692
690
|
}
|
|
693
|
-
function generateEnvironmentDashboard(environment
|
|
691
|
+
function generateEnvironmentDashboard(environment) {
|
|
694
692
|
// Format memory for display
|
|
695
693
|
const formattedMemory = environment.memory.replace(/(\d+\.\d{2})GB/, "$1 GB");
|
|
696
694
|
|
|
@@ -699,9 +697,6 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
699
697
|
.toString(36)
|
|
700
698
|
.substring(2, 7)}`;
|
|
701
699
|
|
|
702
|
-
const cardHeight = Math.floor(dashboardHeight * 0.44);
|
|
703
|
-
const cardContentPadding = 16; // px
|
|
704
|
-
|
|
705
700
|
// Logic for Run Context
|
|
706
701
|
const runContext = process.env.CI ? "CI" : "Local Test";
|
|
707
702
|
|
|
@@ -715,166 +710,145 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
715
710
|
}
|
|
716
711
|
|
|
717
712
|
.environment-dashboard-wrapper {
|
|
718
|
-
--primary-color: #
|
|
719
|
-
--
|
|
720
|
-
--
|
|
721
|
-
--success-color: #28a745;
|
|
722
|
-
--success-light-color: #eaf6ec;
|
|
723
|
-
--warning-color: #ffc107;
|
|
724
|
-
--warning-light-color: #fff9e6;
|
|
725
|
-
--danger-color: #dc3545;
|
|
713
|
+
--primary-color: #6366f1;
|
|
714
|
+
--success-color: #10b981;
|
|
715
|
+
--warning-color: #f59e0b;
|
|
726
716
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
--border-light-color: #f1f3f5;
|
|
733
|
-
--icon-color: #495057;
|
|
734
|
-
--chip-background: #e9ecef;
|
|
735
|
-
--chip-text: #495057;
|
|
736
|
-
--shadow-color: rgba(0, 0, 0, 0.075);
|
|
737
|
-
|
|
738
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
|
|
739
|
-
background-color: var(--background-color);
|
|
740
|
-
border-radius: 12px;
|
|
741
|
-
box-shadow: 0 6px 12px var(--shadow-color);
|
|
742
|
-
padding: 24px;
|
|
743
|
-
color: var(--text-color);
|
|
717
|
+
background-color: white;
|
|
718
|
+
padding: 48px;
|
|
719
|
+
border-bottom: 1px solid #e2e8f0;
|
|
720
|
+
font-family: var(--font-family);
|
|
721
|
+
color: #0f172a;
|
|
744
722
|
display: grid;
|
|
745
|
-
grid-template-columns:
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
723
|
+
grid-template-columns: repeat(2, 1fr);
|
|
724
|
+
gap: 32px;
|
|
725
|
+
font-size: 15px;
|
|
726
|
+
transform: translateZ(0);
|
|
749
727
|
}
|
|
750
728
|
|
|
751
|
-
/* Mobile Responsiveness */
|
|
752
729
|
@media (max-width: 768px) {
|
|
753
730
|
.environment-dashboard-wrapper {
|
|
754
|
-
grid-template-columns: 1fr;
|
|
755
|
-
|
|
756
|
-
padding: 16px;
|
|
757
|
-
height: auto !important; /* Allow height to grow */
|
|
731
|
+
grid-template-columns: 1fr;
|
|
732
|
+
padding: 32px 24px;
|
|
758
733
|
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
734
|
+
}
|
|
735
|
+
@media (max-width: 480px) {
|
|
736
|
+
.environment-dashboard-wrapper {
|
|
737
|
+
padding: 24px;
|
|
762
738
|
}
|
|
763
739
|
}
|
|
764
740
|
|
|
765
741
|
.env-dashboard-header {
|
|
766
742
|
grid-column: 1 / -1;
|
|
767
|
-
|
|
768
|
-
justify-content: space-between;
|
|
769
|
-
align-items: center;
|
|
770
|
-
border-bottom: 1px solid var(--border-color);
|
|
771
|
-
padding-bottom: 16px;
|
|
772
|
-
margin-bottom: 8px;
|
|
773
|
-
flex-wrap: wrap; /* Allow wrapping header items */
|
|
774
|
-
gap: 10px;
|
|
743
|
+
margin-bottom: 24px;
|
|
775
744
|
}
|
|
776
745
|
|
|
777
746
|
.env-dashboard-title {
|
|
778
|
-
font-size:
|
|
779
|
-
font-weight:
|
|
780
|
-
color:
|
|
781
|
-
|
|
747
|
+
font-size: 2em;
|
|
748
|
+
font-weight: 900;
|
|
749
|
+
color: #0f172a;
|
|
750
|
+
letter-spacing: -0.02em;
|
|
751
|
+
margin: 0 0 8px 0;
|
|
782
752
|
}
|
|
783
753
|
|
|
784
754
|
.env-dashboard-subtitle {
|
|
785
|
-
font-size:
|
|
786
|
-
color:
|
|
787
|
-
margin
|
|
755
|
+
font-size: 1.05em;
|
|
756
|
+
color: #64748b;
|
|
757
|
+
margin: 0;
|
|
758
|
+
font-weight: 400;
|
|
788
759
|
}
|
|
789
760
|
|
|
790
761
|
.env-card {
|
|
791
|
-
background
|
|
792
|
-
border
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
height: ${cardHeight}px;
|
|
762
|
+
background: white;
|
|
763
|
+
border: none;
|
|
764
|
+
border-left: 4px solid #e2e8f0;
|
|
765
|
+
padding: 28px;
|
|
796
766
|
display: flex;
|
|
797
767
|
flex-direction: column;
|
|
798
|
-
|
|
768
|
+
gap: 20px;
|
|
769
|
+
transition: all 0.12s ease;
|
|
770
|
+
transform: translateZ(0);
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
.env-card:hover {
|
|
774
|
+
border-left-color: var(--primary-color);
|
|
775
|
+
background: #fafbfc;
|
|
799
776
|
}
|
|
800
777
|
|
|
801
778
|
.env-card-header {
|
|
802
|
-
font-weight:
|
|
803
|
-
font-size:
|
|
804
|
-
|
|
805
|
-
color: var(--text-color);
|
|
779
|
+
font-weight: 700;
|
|
780
|
+
font-size: 1.05em;
|
|
781
|
+
color: #0f172a;
|
|
806
782
|
display: flex;
|
|
807
783
|
align-items: center;
|
|
808
|
-
|
|
809
|
-
|
|
784
|
+
gap: 10px;
|
|
785
|
+
text-transform: uppercase;
|
|
786
|
+
letter-spacing: 0.5px;
|
|
810
787
|
}
|
|
811
788
|
|
|
812
789
|
.env-card-header svg {
|
|
813
|
-
|
|
814
|
-
width: 18px;
|
|
790
|
+
width: 18px;
|
|
815
791
|
height: 18px;
|
|
816
|
-
fill:
|
|
792
|
+
fill: #6366f1;
|
|
817
793
|
}
|
|
818
794
|
|
|
819
795
|
.env-card-content {
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
796
|
+
display: flex;
|
|
797
|
+
flex-direction: column;
|
|
798
|
+
gap: 16px;
|
|
823
799
|
}
|
|
824
800
|
|
|
825
801
|
.env-detail-row {
|
|
826
802
|
display: flex;
|
|
827
803
|
justify-content: space-between;
|
|
828
|
-
align-items:
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
flex-wrap: wrap; /* Allow details to wrap on very small screens */
|
|
833
|
-
gap: 8px;
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
.env-detail-row:last-child {
|
|
837
|
-
border-bottom: none;
|
|
804
|
+
align-items: flex-start;
|
|
805
|
+
gap: 16px;
|
|
806
|
+
font-size: 1em;
|
|
807
|
+
padding: 8px 0;
|
|
838
808
|
}
|
|
839
809
|
|
|
840
810
|
.env-detail-label {
|
|
841
|
-
color:
|
|
842
|
-
font-weight:
|
|
843
|
-
|
|
811
|
+
color: #64748b;
|
|
812
|
+
font-weight: 600;
|
|
813
|
+
font-size: 0.9em;
|
|
814
|
+
text-transform: uppercase;
|
|
815
|
+
letter-spacing: 0.3px;
|
|
816
|
+
flex-shrink: 0;
|
|
844
817
|
}
|
|
845
818
|
|
|
846
819
|
.env-detail-value {
|
|
847
|
-
color:
|
|
820
|
+
color: #0f172a;
|
|
848
821
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
822
|
+
font-size: 0.95em;
|
|
849
823
|
text-align: right;
|
|
850
|
-
word-break: break-
|
|
851
|
-
margin-left: auto;
|
|
824
|
+
word-break: break-word;
|
|
825
|
+
margin-left: auto;
|
|
852
826
|
}
|
|
853
827
|
|
|
854
828
|
.env-chip {
|
|
855
|
-
display: inline-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
font-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
829
|
+
display: inline-flex;
|
|
830
|
+
align-items: center;
|
|
831
|
+
padding: 6px 14px;
|
|
832
|
+
border-radius: 6px;
|
|
833
|
+
font-size: 0.85em;
|
|
834
|
+
font-weight: 700;
|
|
835
|
+
text-transform: uppercase;
|
|
836
|
+
letter-spacing: 0.5px;
|
|
863
837
|
}
|
|
864
838
|
|
|
865
839
|
.env-chip-primary {
|
|
866
|
-
background-color:
|
|
867
|
-
color:
|
|
840
|
+
background-color: #ede9fe;
|
|
841
|
+
color: #6366f1;
|
|
868
842
|
}
|
|
869
843
|
|
|
870
844
|
.env-chip-success {
|
|
871
|
-
background-color:
|
|
872
|
-
color:
|
|
845
|
+
background-color: #d1fae5;
|
|
846
|
+
color: #10b981;
|
|
873
847
|
}
|
|
874
848
|
|
|
875
849
|
.env-chip-warning {
|
|
876
|
-
background-color:
|
|
877
|
-
color:
|
|
850
|
+
background-color: #fef3c7;
|
|
851
|
+
color: #f59e0b;
|
|
878
852
|
}
|
|
879
853
|
|
|
880
854
|
.env-cpu-cores {
|
|
@@ -920,17 +894,7 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
920
894
|
<span class="env-detail-label">CPU Cores</span>
|
|
921
895
|
<span class="env-detail-value">
|
|
922
896
|
<div class="env-cpu-cores">
|
|
923
|
-
${
|
|
924
|
-
{ length: Math.max(0, environment.cpu.cores || 0) },
|
|
925
|
-
(_, i) =>
|
|
926
|
-
`<div class="env-core-indicator ${
|
|
927
|
-
i >=
|
|
928
|
-
(environment.cpu.cores >= 8 ? 8 : environment.cpu.cores)
|
|
929
|
-
? "inactive"
|
|
930
|
-
: ""
|
|
931
|
-
}" title="Core ${i + 1}"></div>`
|
|
932
|
-
).join("")}
|
|
933
|
-
<span>${environment.cpu.cores || "N/A"} cores</span>
|
|
897
|
+
<span>${environment.cpu.cores || "N/A"} core${environment.cpu.cores !== 1 ? "s" : ""}</span>
|
|
934
898
|
</div>
|
|
935
899
|
</span>
|
|
936
900
|
</div>
|
|
@@ -964,8 +928,8 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
964
928
|
<div class="env-detail-row">
|
|
965
929
|
<span class="env-detail-label">Hostname</span>
|
|
966
930
|
<span class="env-detail-value" title="${environment.host}">${
|
|
967
|
-
|
|
968
|
-
|
|
931
|
+
environment.host
|
|
932
|
+
}</span>
|
|
969
933
|
</div>
|
|
970
934
|
</div>
|
|
971
935
|
</div>
|
|
@@ -987,10 +951,10 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
987
951
|
<div class="env-detail-row">
|
|
988
952
|
<span class="env-detail-label">Working Dir</span>
|
|
989
953
|
<span class="env-detail-value" title="${environment.cwd}">${
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
954
|
+
environment.cwd.length > 25
|
|
955
|
+
? "..." + environment.cwd.slice(-22)
|
|
956
|
+
: environment.cwd
|
|
957
|
+
}</span>
|
|
994
958
|
</div>
|
|
995
959
|
</div>
|
|
996
960
|
</div>
|
|
@@ -1015,9 +979,9 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
1015
979
|
environment.cpu.model.toLowerCase().includes("apple")
|
|
1016
980
|
? "Apple Silicon"
|
|
1017
981
|
: environment.cpu.model.toLowerCase().includes("arm") ||
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
982
|
+
environment.cpu.model.toLowerCase().includes("aarch64")
|
|
983
|
+
? "ARM-based"
|
|
984
|
+
: "x86/Other"
|
|
1021
985
|
}
|
|
1022
986
|
</span>
|
|
1023
987
|
</span>
|
|
@@ -1089,7 +1053,7 @@ function generateWorkerDistributionChart(results) {
|
|
|
1089
1053
|
.substring(2, 7)}`;
|
|
1090
1054
|
const renderFunctionName = `renderWorkerDistChart_${chartId.replace(
|
|
1091
1055
|
/-/g,
|
|
1092
|
-
"_"
|
|
1056
|
+
"_",
|
|
1093
1057
|
)}`;
|
|
1094
1058
|
const modalJsNamespace = `modal_funcs_${chartId.replace(/-/g, "_")}`;
|
|
1095
1059
|
|
|
@@ -1131,12 +1095,22 @@ function generateWorkerDistributionChart(results) {
|
|
|
1131
1095
|
position: relative; box-shadow: 0 5px 15px rgba(0,0,0,0.5);
|
|
1132
1096
|
}
|
|
1133
1097
|
.worker-modal-close {
|
|
1134
|
-
position: absolute;
|
|
1135
|
-
|
|
1098
|
+
position: absolute;
|
|
1099
|
+
top: 15px;
|
|
1100
|
+
right: 25px;
|
|
1101
|
+
font-size: 32px;
|
|
1102
|
+
font-weight: bold;
|
|
1103
|
+
cursor: pointer;
|
|
1136
1104
|
line-height: 1;
|
|
1105
|
+
z-index: 10;
|
|
1106
|
+
color: #fff;
|
|
1107
|
+
transition: color 0.2s ease;
|
|
1108
|
+
user-select: none;
|
|
1109
|
+
-webkit-user-select: none;
|
|
1137
1110
|
}
|
|
1138
1111
|
.worker-modal-close:hover, .worker-modal-close:focus {
|
|
1139
|
-
color:
|
|
1112
|
+
color: #ef4444;
|
|
1113
|
+
transform: scale(1.1);
|
|
1140
1114
|
}
|
|
1141
1115
|
#worker-modal-body-${chartId} ul {
|
|
1142
1116
|
list-style-type: none; padding-left: 0; margin-top: 15px; max-height: 45vh; overflow-y: auto;
|
|
@@ -1163,7 +1137,7 @@ function generateWorkerDistributionChart(results) {
|
|
|
1163
1137
|
|
|
1164
1138
|
<div id="worker-modal-${chartId}" class="worker-modal-overlay">
|
|
1165
1139
|
<div class="worker-modal-content">
|
|
1166
|
-
<span class="worker-modal-close">×</span>
|
|
1140
|
+
<span class="worker-modal-close" onclick="window.${modalJsNamespace}.close?.()">×</span>
|
|
1167
1141
|
<h3 id="worker-modal-title-${chartId}" style="text-align: center; margin-top: 0; margin-bottom: 25px; font-size: 1.25em; font-weight: 600; color: #fff"></h3>
|
|
1168
1142
|
<div id="worker-modal-body-${chartId}"></div>
|
|
1169
1143
|
</div>
|
|
@@ -1210,10 +1184,14 @@ function generateWorkerDistributionChart(results) {
|
|
|
1210
1184
|
const closeModal = function() {
|
|
1211
1185
|
modal.style.display = 'none';
|
|
1212
1186
|
};
|
|
1187
|
+
|
|
1188
|
+
window.${modalJsNamespace}.close = closeModal;
|
|
1213
1189
|
|
|
1214
|
-
closeModalBtn
|
|
1190
|
+
if (closeModalBtn) {
|
|
1191
|
+
closeModalBtn.onclick = closeModal;
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1215
1194
|
modal.onclick = function(event) {
|
|
1216
|
-
// Close if clicked on the dark overlay background
|
|
1217
1195
|
if (event.target == modal) {
|
|
1218
1196
|
closeModal();
|
|
1219
1197
|
}
|
|
@@ -1329,7 +1307,7 @@ function generateTestHistoryContent(trendData) {
|
|
|
1329
1307
|
? `test run ${overallRun.runId}`
|
|
1330
1308
|
: `test run ${index + 1}`;
|
|
1331
1309
|
const testRunForThisOverallRun = trendData.testRuns[runKey]?.find(
|
|
1332
|
-
(t) => t && t.testName === fullTestName
|
|
1310
|
+
(t) => t && t.testName === fullTestName,
|
|
1333
1311
|
);
|
|
1334
1312
|
if (testRunForThisOverallRun) {
|
|
1335
1313
|
history.push({
|
|
@@ -1369,12 +1347,12 @@ function generateTestHistoryContent(trendData) {
|
|
|
1369
1347
|
: { status: "unknown" };
|
|
1370
1348
|
return `
|
|
1371
1349
|
<div class="test-history-card" data-test-name="${sanitizeHTML(
|
|
1372
|
-
test.testTitle.toLowerCase()
|
|
1350
|
+
test.testTitle.toLowerCase(),
|
|
1373
1351
|
)}" data-latest-status="${latestRun.status}">
|
|
1374
1352
|
<div class="test-history-header">
|
|
1375
1353
|
<p title="${sanitizeHTML(test.testTitle)}">${capitalize(
|
|
1376
|
-
|
|
1377
|
-
|
|
1354
|
+
sanitizeHTML(test.testTitle),
|
|
1355
|
+
)}</p>
|
|
1378
1356
|
<span class="status-badge ${getStatusClass(latestRun.status)}">
|
|
1379
1357
|
${String(latestRun.status).toUpperCase()}
|
|
1380
1358
|
</span>
|
|
@@ -1396,11 +1374,11 @@ function generateTestHistoryContent(trendData) {
|
|
|
1396
1374
|
<tr>
|
|
1397
1375
|
<td>${run.runId}</td>
|
|
1398
1376
|
<td><span class="status-badge-small ${getStatusClass(
|
|
1399
|
-
run.status
|
|
1377
|
+
run.status,
|
|
1400
1378
|
)}">${String(run.status).toUpperCase()}</span></td>
|
|
1401
1379
|
<td>${formatDuration(run.duration)}</td>
|
|
1402
1380
|
<td>${formatDate(run.timestamp)}</td>
|
|
1403
|
-
</tr
|
|
1381
|
+
</tr>`,
|
|
1404
1382
|
)
|
|
1405
1383
|
.join("")}
|
|
1406
1384
|
</tbody>
|
|
@@ -1500,9 +1478,9 @@ function generateSuitesWidget(suitesData) {
|
|
|
1500
1478
|
<span class="summary-badge">${
|
|
1501
1479
|
suitesData.length
|
|
1502
1480
|
} suites • ${suitesData.reduce(
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1481
|
+
(sum, suite) => sum + suite.count,
|
|
1482
|
+
0,
|
|
1483
|
+
)} tests</span>
|
|
1506
1484
|
</div>
|
|
1507
1485
|
|
|
1508
1486
|
<div class="suites-grid-container" style="flex-grow: 1; overflow-y: auto; padding-right: 5px;">
|
|
@@ -1512,16 +1490,14 @@ function generateSuitesWidget(suitesData) {
|
|
|
1512
1490
|
(suite) => `
|
|
1513
1491
|
<div class="suite-card status-${suite.statusOverall}">
|
|
1514
1492
|
<div class="suite-card-header">
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
)}</span></div>
|
|
1524
|
-
<div class="suite-card-body">
|
|
1493
|
+
<h3 class="suite-name" title="${sanitizeHTML(
|
|
1494
|
+
suite.name,
|
|
1495
|
+
)} (${sanitizeHTML(suite.browser)})">${sanitizeHTML(suite.name)}</h3>
|
|
1496
|
+
</div>
|
|
1497
|
+
<div style="margin-bottom: 12px;"><span class="browser-tag" title="🌐 ${sanitizeHTML(suite.browser)}">🌐 ${sanitizeHTML(
|
|
1498
|
+
suite.browser,
|
|
1499
|
+
)}</span></div>
|
|
1500
|
+
<div class="suite-card-body">
|
|
1525
1501
|
<span class="test-count">${suite.count} test${
|
|
1526
1502
|
suite.count !== 1 ? "s" : ""
|
|
1527
1503
|
}</span>
|
|
@@ -1543,7 +1519,7 @@ function generateSuitesWidget(suitesData) {
|
|
|
1543
1519
|
}
|
|
1544
1520
|
</div>
|
|
1545
1521
|
</div>
|
|
1546
|
-
</div
|
|
1522
|
+
</div>`,
|
|
1547
1523
|
)
|
|
1548
1524
|
.join("")}
|
|
1549
1525
|
</div>
|
|
@@ -1565,12 +1541,11 @@ function getAttachmentIcon(contentType) {
|
|
|
1565
1541
|
}
|
|
1566
1542
|
function generateAIFailureAnalyzerTab(results) {
|
|
1567
1543
|
const failedTests = (results || []).filter(
|
|
1568
|
-
(test) => test.status === "failed"
|
|
1544
|
+
(test) => test.status === "failed",
|
|
1569
1545
|
);
|
|
1570
1546
|
|
|
1571
1547
|
if (failedTests.length === 0) {
|
|
1572
1548
|
return `
|
|
1573
|
-
<h2 class="tab-main-title">AI Failure Analysis</h2>
|
|
1574
1549
|
<div class="no-data">Congratulations! No failed tests in this run.</div>
|
|
1575
1550
|
`;
|
|
1576
1551
|
}
|
|
@@ -1579,7 +1554,6 @@ function generateAIFailureAnalyzerTab(results) {
|
|
|
1579
1554
|
const btoa = (str) => Buffer.from(str).toString("base64");
|
|
1580
1555
|
|
|
1581
1556
|
return `
|
|
1582
|
-
<h2 class="tab-main-title">AI Failure Analysis</h2>
|
|
1583
1557
|
<div class="ai-analyzer-stats">
|
|
1584
1558
|
<div class="stat-item">
|
|
1585
1559
|
<span class="stat-number">${failedTests.length}</span>
|
|
@@ -1594,7 +1568,7 @@ function generateAIFailureAnalyzerTab(results) {
|
|
|
1594
1568
|
<div class="stat-item">
|
|
1595
1569
|
<span class="stat-number">${Math.round(
|
|
1596
1570
|
failedTests.reduce((sum, test) => sum + (test.duration || 0), 0) /
|
|
1597
|
-
1000
|
|
1571
|
+
1000,
|
|
1598
1572
|
)}s</span>
|
|
1599
1573
|
<span class="stat-label">Total Duration</span>
|
|
1600
1574
|
</div>
|
|
@@ -1617,14 +1591,14 @@ function generateAIFailureAnalyzerTab(results) {
|
|
|
1617
1591
|
<div class="failure-header">
|
|
1618
1592
|
<div class="failure-main-info">
|
|
1619
1593
|
<h3 class="failure-title" title="${sanitizeHTML(
|
|
1620
|
-
test.name
|
|
1594
|
+
test.name,
|
|
1621
1595
|
)}">${sanitizeHTML(testTitle)}</h3>
|
|
1622
1596
|
<div class="failure-meta">
|
|
1623
1597
|
<span class="browser-indicator">${sanitizeHTML(
|
|
1624
|
-
test.browser || "unknown"
|
|
1598
|
+
test.browser || "unknown",
|
|
1625
1599
|
)}</span>
|
|
1626
1600
|
<span class="duration-indicator">${formatDuration(
|
|
1627
|
-
test.duration
|
|
1601
|
+
test.duration,
|
|
1628
1602
|
)}</span>
|
|
1629
1603
|
</div>
|
|
1630
1604
|
</div>
|
|
@@ -1639,7 +1613,7 @@ function generateAIFailureAnalyzerTab(results) {
|
|
|
1639
1613
|
</div>
|
|
1640
1614
|
<div class="failure-error-preview">
|
|
1641
1615
|
<div class="error-snippet">${formatPlaywrightError(
|
|
1642
|
-
truncatedError
|
|
1616
|
+
truncatedError,
|
|
1643
1617
|
)}</div>
|
|
1644
1618
|
<button class="expand-error-btn" onclick="toggleErrorDetails(this)">
|
|
1645
1619
|
<span class="expand-text">Show Full Error</span>
|
|
@@ -1649,10 +1623,16 @@ function generateAIFailureAnalyzerTab(results) {
|
|
|
1649
1623
|
<div class="full-error-details" style="display: none;">
|
|
1650
1624
|
<div class="full-error-content">
|
|
1651
1625
|
${formatPlaywrightError(
|
|
1652
|
-
test.errorMessage ||
|
|
1626
|
+
test.errorMessage ||
|
|
1627
|
+
"No detailed error message available",
|
|
1653
1628
|
)}
|
|
1654
1629
|
</div>
|
|
1655
1630
|
</div>
|
|
1631
|
+
<div class="ai-suggestion-container" style="display: none;">
|
|
1632
|
+
<div class="ai-suggestion-content">
|
|
1633
|
+
<!-- AI suggestion will be injected here -->
|
|
1634
|
+
</div>
|
|
1635
|
+
</div>
|
|
1656
1636
|
</div>
|
|
1657
1637
|
`;
|
|
1658
1638
|
})
|
|
@@ -1895,7 +1875,25 @@ function generateDescribeDurationChart(results) {
|
|
|
1895
1875
|
series: [{
|
|
1896
1876
|
name: 'Duration',
|
|
1897
1877
|
data: ${dataStr},
|
|
1898
|
-
|
|
1878
|
+
colorByPoint: true,
|
|
1879
|
+
colors: [
|
|
1880
|
+
'#9333ea',
|
|
1881
|
+
'#6366f1',
|
|
1882
|
+
'#0ea5e9',
|
|
1883
|
+
'#10b981',
|
|
1884
|
+
'#84cc16',
|
|
1885
|
+
'#eab308',
|
|
1886
|
+
'#f97316',
|
|
1887
|
+
'#ef4444',
|
|
1888
|
+
'#ec4899',
|
|
1889
|
+
'#8b5cf6',
|
|
1890
|
+
'#06b6d4',
|
|
1891
|
+
'#14b8a6',
|
|
1892
|
+
'#a3e635',
|
|
1893
|
+
'#fbbf24',
|
|
1894
|
+
'#fb923c',
|
|
1895
|
+
'#f87171'
|
|
1896
|
+
],
|
|
1899
1897
|
}],
|
|
1900
1898
|
credits: { enabled: false }
|
|
1901
1899
|
});
|
|
@@ -2067,12 +2065,41 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2067
2065
|
const passPercentage = Math.round((runSummary.passed / totalTestsOr1) * 100);
|
|
2068
2066
|
const failPercentage = Math.round((runSummary.failed / totalTestsOr1) * 100);
|
|
2069
2067
|
const skipPercentage = Math.round(
|
|
2070
|
-
((runSummary.skipped || 0) / totalTestsOr1) * 100
|
|
2068
|
+
((runSummary.skipped || 0) / totalTestsOr1) * 100,
|
|
2071
2069
|
);
|
|
2072
2070
|
const avgTestDuration =
|
|
2073
2071
|
runSummary.totalTests > 0
|
|
2074
2072
|
? formatDuration(runSummary.duration / runSummary.totalTests)
|
|
2075
2073
|
: "0.0s";
|
|
2074
|
+
|
|
2075
|
+
// Calculate retry statistics
|
|
2076
|
+
const totalRetried = (results || []).reduce((acc, test) => {
|
|
2077
|
+
if (test.retries && test.retries > 0) {
|
|
2078
|
+
return acc + 1;
|
|
2079
|
+
}
|
|
2080
|
+
return acc;
|
|
2081
|
+
}, 0);
|
|
2082
|
+
|
|
2083
|
+
// Calculate browser distribution
|
|
2084
|
+
const browserStats = (results || []).reduce((acc, test) => {
|
|
2085
|
+
let browserName = "unknown";
|
|
2086
|
+
if (test.browser) {
|
|
2087
|
+
// Extract browser name from strings like "Chrome v143 on Windows 10"
|
|
2088
|
+
const match = test.browser.match(/^(\w+)/);
|
|
2089
|
+
browserName = match ? match[1] : test.browser;
|
|
2090
|
+
}
|
|
2091
|
+
acc[browserName] = (acc[browserName] || 0) + 1;
|
|
2092
|
+
return acc;
|
|
2093
|
+
}, {});
|
|
2094
|
+
|
|
2095
|
+
const totalTests = runSummary.totalTests || 1;
|
|
2096
|
+
const browserBreakdown = Object.entries(browserStats)
|
|
2097
|
+
.map(([browser, count]) => ({
|
|
2098
|
+
browser,
|
|
2099
|
+
count,
|
|
2100
|
+
percentage: Math.round((count / totalTests) * 100),
|
|
2101
|
+
}))
|
|
2102
|
+
.sort((a, b) => b.count - a.count);
|
|
2076
2103
|
function generateTestCasesHTML() {
|
|
2077
2104
|
if (!results || results.length === 0)
|
|
2078
2105
|
return '<div class="no-tests">No test results found in this run.</div>';
|
|
@@ -2082,28 +2109,9 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2082
2109
|
const testFileParts = test.name.split(" > ");
|
|
2083
2110
|
const testTitle =
|
|
2084
2111
|
testFileParts[testFileParts.length - 1] || "Unnamed Test";
|
|
2085
|
-
// ---
|
|
2112
|
+
// --- Simplified Severity Badge ---
|
|
2086
2113
|
const severity = test.severity || "Medium";
|
|
2087
|
-
const
|
|
2088
|
-
switch (level) {
|
|
2089
|
-
case "Minor":
|
|
2090
|
-
return "#006064";
|
|
2091
|
-
case "Low":
|
|
2092
|
-
return "#FFA07A";
|
|
2093
|
-
case "Medium":
|
|
2094
|
-
return "#577A11";
|
|
2095
|
-
case "High":
|
|
2096
|
-
return "#B71C1C";
|
|
2097
|
-
case "Critical":
|
|
2098
|
-
return "#64158A";
|
|
2099
|
-
default:
|
|
2100
|
-
return "#577A11";
|
|
2101
|
-
}
|
|
2102
|
-
};
|
|
2103
|
-
const severityColor = getSeverityColor(severity);
|
|
2104
|
-
// We reuse 'status-badge' class for size/font consistency, but override background color
|
|
2105
|
-
const severityBadge = `<span class="status-badge" style="background-color: ${severityColor}; margin-right: 8px;">${severity}</span>`;
|
|
2106
|
-
// ---------------------------
|
|
2114
|
+
const severityBadge = `<span class="severity-badge" data-severity="${severity.toLowerCase()}">${severity}</span>`;
|
|
2107
2115
|
const generateStepsHTML = (steps, depth = 0) => {
|
|
2108
2116
|
if (!steps || steps.length === 0)
|
|
2109
2117
|
return "<div class='no-steps'>No steps recorded for this test.</div>";
|
|
@@ -2120,17 +2128,17 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2120
2128
|
<div class="step-header ${stepClass}" role="button" aria-expanded="false">
|
|
2121
2129
|
<span class="step-icon">${getStatusIcon(step.status)}</span>
|
|
2122
2130
|
<span class="step-title">${sanitizeHTML(
|
|
2123
|
-
step.title
|
|
2131
|
+
step.title,
|
|
2124
2132
|
)}${hookIndicator}</span>
|
|
2125
2133
|
<span class="step-duration">${formatDuration(
|
|
2126
|
-
step.duration
|
|
2134
|
+
step.duration,
|
|
2127
2135
|
)}</span>
|
|
2128
2136
|
</div>
|
|
2129
2137
|
<div class="step-details" style="display: none;">
|
|
2130
2138
|
${
|
|
2131
2139
|
step.codeLocation
|
|
2132
2140
|
? `<div class="step-info code-section"><strong>Location:</strong> ${sanitizeHTML(
|
|
2133
|
-
step.codeLocation
|
|
2141
|
+
step.codeLocation,
|
|
2134
2142
|
)}</div>`
|
|
2135
2143
|
: ""
|
|
2136
2144
|
}
|
|
@@ -2140,7 +2148,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2140
2148
|
${
|
|
2141
2149
|
step.stackTrace
|
|
2142
2150
|
? `<div class="stack-trace">${formatPlaywrightError(
|
|
2143
|
-
step.stackTrace
|
|
2151
|
+
step.stackTrace,
|
|
2144
2152
|
)}</div>`
|
|
2145
2153
|
: ""
|
|
2146
2154
|
}
|
|
@@ -2170,7 +2178,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2170
2178
|
hasNestedSteps
|
|
2171
2179
|
? `<div class="nested-steps">${generateStepsHTML(
|
|
2172
2180
|
step.steps,
|
|
2173
|
-
depth + 1
|
|
2181
|
+
depth + 1,
|
|
2174
2182
|
)}</div>`
|
|
2175
2183
|
: ""
|
|
2176
2184
|
}
|
|
@@ -2184,15 +2192,12 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2184
2192
|
<div class="test-case" data-status="${
|
|
2185
2193
|
test.status
|
|
2186
2194
|
}" data-browser="${sanitizeHTML(browser)}" data-tags="${(test.tags || [])
|
|
2187
|
-
|
|
2188
|
-
|
|
2195
|
+
.join(",")
|
|
2196
|
+
.toLowerCase()}">
|
|
2189
2197
|
<div class="test-case-header" role="button" aria-expanded="false">
|
|
2190
2198
|
<div class="test-case-summary">
|
|
2191
|
-
<span class="status-badge ${getStatusClass(test.status)}">${String(
|
|
2192
|
-
test.status
|
|
2193
|
-
).toUpperCase()}</span>
|
|
2194
2199
|
<span class="test-case-title" title="${sanitizeHTML(
|
|
2195
|
-
test.name
|
|
2200
|
+
test.name,
|
|
2196
2201
|
)}">${sanitizeHTML(testTitle)}</span>
|
|
2197
2202
|
<span class="test-case-browser">(${sanitizeHTML(browser)})</span>
|
|
2198
2203
|
</div>
|
|
@@ -2205,6 +2210,11 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2205
2210
|
.join(" ")
|
|
2206
2211
|
: ""
|
|
2207
2212
|
}
|
|
2213
|
+
</div>
|
|
2214
|
+
<div class="test-case-status-duration">
|
|
2215
|
+
<span class="status-badge ${getStatusClass(test.status)}">${String(
|
|
2216
|
+
test.status,
|
|
2217
|
+
).toUpperCase()}</span>
|
|
2208
2218
|
<span class="test-duration">${formatDuration(test.duration)}</span>
|
|
2209
2219
|
</div>
|
|
2210
2220
|
</div>
|
|
@@ -2224,14 +2234,14 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2224
2234
|
const descriptionHtml =
|
|
2225
2235
|
isIssueOrBug && descriptionText.match(/^[A-Z]+-\d+$/)
|
|
2226
2236
|
? `<a href="#" class="annotation-link" data-annotation="${sanitizeHTML(
|
|
2227
|
-
descriptionText
|
|
2237
|
+
descriptionText,
|
|
2228
2238
|
)}" style="color: #3b82f6; text-decoration: underline; cursor: pointer;">${sanitizeHTML(
|
|
2229
|
-
descriptionText
|
|
2239
|
+
descriptionText,
|
|
2230
2240
|
)}</a>`
|
|
2231
2241
|
: sanitizeHTML(descriptionText);
|
|
2232
2242
|
const locationText = annotation.location
|
|
2233
2243
|
? `<div style="font-size: 0.85em; color: #6b7280; margin-top: 4px;">Location: ${sanitizeHTML(
|
|
2234
|
-
annotation.location.file
|
|
2244
|
+
annotation.location.file,
|
|
2235
2245
|
)}:${annotation.location.line}:${
|
|
2236
2246
|
annotation.location.column
|
|
2237
2247
|
}</div>`
|
|
@@ -2253,14 +2263,14 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2253
2263
|
: ""
|
|
2254
2264
|
}
|
|
2255
2265
|
<p><strong>Test run Worker ID:</strong> ${sanitizeHTML(
|
|
2256
|
-
test.workerId
|
|
2266
|
+
test.workerId,
|
|
2257
2267
|
)} [<strong>Total No. of Workers:</strong> ${sanitizeHTML(
|
|
2258
|
-
|
|
2259
|
-
|
|
2268
|
+
test.totalWorkers,
|
|
2269
|
+
)}]</p>
|
|
2260
2270
|
${
|
|
2261
2271
|
test.errorMessage
|
|
2262
2272
|
? `<div class="test-error-summary">${formatPlaywrightError(
|
|
2263
|
-
test.errorMessage
|
|
2273
|
+
test.errorMessage,
|
|
2264
2274
|
)}
|
|
2265
2275
|
<button
|
|
2266
2276
|
class="copy-error-btn"
|
|
@@ -2287,7 +2297,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2287
2297
|
${
|
|
2288
2298
|
test.snippet
|
|
2289
2299
|
? `<div class="code-section"><h4>Error Snippet</h4><pre><code>${formatPlaywrightError(
|
|
2290
|
-
test.snippet
|
|
2300
|
+
test.snippet,
|
|
2291
2301
|
)}</code></pre></div>`
|
|
2292
2302
|
: ""
|
|
2293
2303
|
}
|
|
@@ -2299,19 +2309,21 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2299
2309
|
const logId = `stdout-log-${test.id || index}`;
|
|
2300
2310
|
return `<div class="console-output-section">
|
|
2301
2311
|
<h4>Console Output (stdout)
|
|
2302
|
-
<button class="copy-btn" onclick="copyLogContent('${logId}', this)">Copy
|
|
2312
|
+
<button class="copy-btn" onclick="copyLogContent('${logId}', this)">Copy</button>
|
|
2303
2313
|
</h4>
|
|
2304
2314
|
<div class="log-wrapper">
|
|
2305
2315
|
<pre id="${logId}" class="console-log stdout-log" style="background-color: #2d2d2d; color: wheat; padding: 1.25em; border-radius: 0.85em; line-height: 1.2;">${formatPlaywrightError(
|
|
2306
|
-
|
|
2307
|
-
|
|
2316
|
+
test.stdout
|
|
2317
|
+
.map((line) => sanitizeHTML(line))
|
|
2318
|
+
.join("\n"),
|
|
2319
|
+
)}</pre>
|
|
2308
2320
|
</div>
|
|
2309
2321
|
</div>`;
|
|
2310
2322
|
})()}
|
|
2311
2323
|
${
|
|
2312
2324
|
test.stderr && test.stderr.length > 0
|
|
2313
2325
|
? `<div class="console-output-section"><h4>Console Output (stderr)</h4><pre class="console-log stderr-log" style="background-color: #2d2d2d; color: indianred; padding: 1.25em; border-radius: 0.85em; line-height: 1.2;">${formatPlaywrightError(
|
|
2314
|
-
test.stderr.map((line) => sanitizeHTML(line)).join("\n")
|
|
2326
|
+
test.stderr.map((line) => sanitizeHTML(line)).join("\n"),
|
|
2315
2327
|
)}</pre></div>`
|
|
2316
2328
|
: ""
|
|
2317
2329
|
}
|
|
@@ -2331,15 +2343,15 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2331
2343
|
<div class="attachment-info">
|
|
2332
2344
|
<div class="trace-actions">
|
|
2333
2345
|
<a href="${fixPath(
|
|
2334
|
-
screenshot
|
|
2346
|
+
screenshot,
|
|
2335
2347
|
)}" target="_blank" class="view-full">View Full Image</a>
|
|
2336
2348
|
<a href="${fixPath(
|
|
2337
|
-
screenshot
|
|
2349
|
+
screenshot,
|
|
2338
2350
|
)}" target="_blank" download="screenshot-${Date.now()}-${index}.png">Download</a>
|
|
2339
2351
|
</div>
|
|
2340
2352
|
</div>
|
|
2341
2353
|
</div>
|
|
2342
|
-
|
|
2354
|
+
`,
|
|
2343
2355
|
)
|
|
2344
2356
|
.join("")}
|
|
2345
2357
|
</div>
|
|
@@ -2369,14 +2381,14 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2369
2381
|
index + 1
|
|
2370
2382
|
}">
|
|
2371
2383
|
<source src="${sanitizeHTML(
|
|
2372
|
-
fixedVideoUrl
|
|
2384
|
+
fixedVideoUrl,
|
|
2373
2385
|
)}" type="${mimeType}">
|
|
2374
2386
|
Your browser does not support the video tag.
|
|
2375
2387
|
</video>
|
|
2376
2388
|
<div class="attachment-info">
|
|
2377
2389
|
<div class="trace-actions">
|
|
2378
2390
|
<a href="${sanitizeHTML(
|
|
2379
|
-
fixedVideoUrl
|
|
2391
|
+
fixedVideoUrl,
|
|
2380
2392
|
)}" target="_blank" download="video-${Date.now()}-${index}.${fileExtension}">Download</a>
|
|
2381
2393
|
</div>
|
|
2382
2394
|
</div>
|
|
@@ -2395,16 +2407,16 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2395
2407
|
<div class="trace-preview">
|
|
2396
2408
|
<span class="trace-icon">📄</span>
|
|
2397
2409
|
<span class="trace-name">${sanitizeHTML(
|
|
2398
|
-
path.basename(test.tracePath)
|
|
2410
|
+
path.basename(test.tracePath),
|
|
2399
2411
|
)}</span>
|
|
2400
2412
|
</div>
|
|
2401
2413
|
<div class="attachment-info">
|
|
2402
2414
|
<div class="trace-actions">
|
|
2403
2415
|
<a href="${sanitizeHTML(
|
|
2404
|
-
fixPath(test.tracePath)
|
|
2416
|
+
fixPath(test.tracePath),
|
|
2405
2417
|
)}" target="_blank" download="${sanitizeHTML(
|
|
2406
|
-
|
|
2407
|
-
|
|
2418
|
+
path.basename(test.tracePath),
|
|
2419
|
+
)}" class="download-trace">Download Trace</a>
|
|
2408
2420
|
</div>
|
|
2409
2421
|
</div>
|
|
2410
2422
|
</div>
|
|
@@ -2424,30 +2436,30 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2424
2436
|
(attachment) => `
|
|
2425
2437
|
<div class="attachment-item generic-attachment">
|
|
2426
2438
|
<div class="attachment-icon">${getAttachmentIcon(
|
|
2427
|
-
attachment.contentType
|
|
2439
|
+
attachment.contentType,
|
|
2428
2440
|
)}</div>
|
|
2429
2441
|
<div class="attachment-caption">
|
|
2430
2442
|
<span class="attachment-name" title="${sanitizeHTML(
|
|
2431
|
-
attachment.name
|
|
2443
|
+
attachment.name,
|
|
2432
2444
|
)}">${sanitizeHTML(attachment.name)}</span>
|
|
2433
2445
|
<span class="attachment-type">${sanitizeHTML(
|
|
2434
|
-
attachment.contentType
|
|
2446
|
+
attachment.contentType,
|
|
2435
2447
|
)}</span>
|
|
2436
2448
|
</div>
|
|
2437
2449
|
<div class="attachment-info">
|
|
2438
2450
|
<div class="trace-actions">
|
|
2439
2451
|
<a href="${sanitizeHTML(
|
|
2440
|
-
fixPath(attachment.path)
|
|
2452
|
+
fixPath(attachment.path),
|
|
2441
2453
|
)}" target="_blank" class="view-full">View</a>
|
|
2442
2454
|
<a href="${sanitizeHTML(
|
|
2443
|
-
fixPath(attachment.path)
|
|
2455
|
+
fixPath(attachment.path),
|
|
2444
2456
|
)}" target="_blank" download="${sanitizeHTML(
|
|
2445
|
-
|
|
2446
|
-
|
|
2457
|
+
attachment.name,
|
|
2458
|
+
)}" class="download-trace">Download</a>
|
|
2447
2459
|
</div>
|
|
2448
2460
|
</div>
|
|
2449
2461
|
</div>
|
|
2450
|
-
|
|
2462
|
+
`,
|
|
2451
2463
|
)
|
|
2452
2464
|
.join("")}
|
|
2453
2465
|
</div>
|
|
@@ -2458,7 +2470,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2458
2470
|
${
|
|
2459
2471
|
test.codeSnippet
|
|
2460
2472
|
? `<div class="code-section"><h4>Code Snippet</h4><pre><code>${formatPlaywrightError(
|
|
2461
|
-
sanitizeHTML(test.codeSnippet)
|
|
2473
|
+
sanitizeHTML(test.codeSnippet),
|
|
2462
2474
|
)}</code></pre></div>`
|
|
2463
2475
|
: ""
|
|
2464
2476
|
}
|
|
@@ -2475,17 +2487,51 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2475
2487
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2476
2488
|
<link rel="icon" type="image/png" href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png">
|
|
2477
2489
|
<link rel="apple-touch-icon" href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png">
|
|
2490
|
+
<!-- Preconnect to external domains -->
|
|
2491
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
|
|
2492
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
2493
|
+
<link rel="preconnect" href="https://code.highcharts.com">
|
|
2494
|
+
|
|
2495
|
+
<!-- Preload critical font -->
|
|
2496
|
+
<link rel="preload" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700&display=swap" as="style" onload="this.onload=null;this.rel='stylesheet'">
|
|
2497
|
+
<noscript><link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;600;700&display=swap"></noscript>
|
|
2498
|
+
|
|
2478
2499
|
<script src="https://code.highcharts.com/highcharts.js" defer></script>
|
|
2479
2500
|
<title>Pulse Report</title>
|
|
2480
2501
|
<style>
|
|
2481
2502
|
:root {
|
|
2482
|
-
--primary-color: #
|
|
2483
|
-
--
|
|
2484
|
-
--
|
|
2485
|
-
--
|
|
2486
|
-
--
|
|
2487
|
-
--
|
|
2503
|
+
--primary-color: #6366f1; --primary-dark: #4f46e5; --primary-light: #818cf8;
|
|
2504
|
+
--secondary-color: #8b5cf6; --secondary-dark: #7c3aed; --secondary-light: #a78bfa;
|
|
2505
|
+
--accent-color: #ec4899; --accent-alt: #06b6d4;
|
|
2506
|
+
--success-color: #10b981; --success-dark: #059669; --success-light: #34d399;
|
|
2507
|
+
--danger-color: #ef4444; --danger-dark: #dc2626; --danger-light: #f87171;
|
|
2508
|
+
--warning-color: #f59e0b; --warning-dark: #d97706; --warning-light: #fbbf24;
|
|
2509
|
+
--info-color: #3b82f6;
|
|
2510
|
+
--neutral-50: #fafafa; --neutral-100: #f5f5f5; --neutral-200: #e5e5e5; --neutral-300: #d4d4d4;
|
|
2511
|
+
--neutral-400: #a3a3a3; --neutral-500: #737373; --neutral-600: #525252; --neutral-700: #404040;
|
|
2512
|
+
--neutral-800: #262626; --neutral-900: #171717;
|
|
2513
|
+
--text-primary: #0f172a; --text-secondary: #475569; --text-tertiary: #94a3b8;
|
|
2514
|
+
--bg-primary: #ffffff; --bg-secondary: #f8fafc; --bg-tertiary: #f1f5f9;
|
|
2515
|
+
--border-light: #e2e8f0; --border-medium: #cbd5e1; --border-dark: #94a3b8;
|
|
2516
|
+
--font-family: 'Plus Jakarta Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
2517
|
+
--radius-sm: 8px; --radius-md: 12px; --radius-lg: 16px; --radius-xl: 20px; --radius-2xl: 24px;
|
|
2518
|
+
--shadow-xs: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
|
2519
|
+
--shadow-sm: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
|
|
2520
|
+
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
|
|
2521
|
+
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -4px rgba(0, 0, 0, 0.1);
|
|
2522
|
+
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 8px 10px -6px rgba(0, 0, 0, 0.1);
|
|
2523
|
+
--shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25);
|
|
2524
|
+
--glow-primary: 0 0 20px rgba(99, 102, 241, 0.4), 0 0 40px rgba(99, 102, 241, 0.2);
|
|
2525
|
+
--glow-success: 0 0 20px rgba(16, 185, 129, 0.4), 0 0 40px rgba(16, 185, 129, 0.2);
|
|
2526
|
+
--glow-danger: 0 0 20px rgba(239, 68, 68, 0.4), 0 0 40px rgba(239, 68, 68, 0.2);
|
|
2488
2527
|
}
|
|
2528
|
+
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
2529
|
+
::selection { background: var(--primary-color); color: white; }
|
|
2530
|
+
::-webkit-scrollbar { width: 0; height: 0; display: none; }
|
|
2531
|
+
::-webkit-scrollbar-track { display: none; }
|
|
2532
|
+
::-webkit-scrollbar-thumb { display: none; }
|
|
2533
|
+
::-webkit-scrollbar-thumb:hover { display: none; }
|
|
2534
|
+
* { scrollbar-width: none; -ms-overflow-style: none; }
|
|
2489
2535
|
.trend-chart-container, .test-history-trend div[id^="testHistoryChart-"] { min-height: 100px; }
|
|
2490
2536
|
.lazy-load-chart .no-data, .lazy-load-chart .no-data-chart { display: flex; align-items: center; justify-content: center; height: 100%; font-style: italic; color: var(--dark-gray-color); }
|
|
2491
2537
|
.highcharts-background { fill: transparent; }
|
|
@@ -2493,34 +2539,659 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2493
2539
|
.highcharts-axis-labels text, .highcharts-legend-item text { fill: var(--text-color-secondary) !important; font-size: 12px !important; }
|
|
2494
2540
|
.highcharts-axis-title { fill: var(--text-color) !important; }
|
|
2495
2541
|
.highcharts-tooltip > span { background-color: rgba(10,10,10,0.92) !important; border-color: rgba(10,10,10,0.92) !important; color: #f5f5f5 !important; padding: 10px !important; border-radius: 6px !important; }
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2542
|
+
html {
|
|
2543
|
+
overflow-x: hidden;
|
|
2544
|
+
}
|
|
2545
|
+
body {
|
|
2546
|
+
font-family: var(--font-family);
|
|
2547
|
+
margin: 0;
|
|
2548
|
+
background: #fafbfc;
|
|
2549
|
+
color: var(--text-primary);
|
|
2550
|
+
line-height: 1.6;
|
|
2551
|
+
font-size: 15px;
|
|
2552
|
+
min-height: 100vh;
|
|
2553
|
+
overflow-x: hidden;
|
|
2554
|
+
-webkit-font-smoothing: antialiased;
|
|
2555
|
+
-moz-osx-font-smoothing: grayscale;
|
|
2556
|
+
text-rendering: optimizeLegibility;
|
|
2557
|
+
}
|
|
2558
|
+
* {
|
|
2559
|
+
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
2560
|
+
will-change: transform, opacity;
|
|
2561
|
+
}
|
|
2562
|
+
*:not(input):not(select):not(textarea):not(button) {
|
|
2563
|
+
transition-duration: 0.15s;
|
|
2564
|
+
}
|
|
2565
|
+
.container {
|
|
2566
|
+
padding: 0;
|
|
2567
|
+
margin: 0;
|
|
2568
|
+
max-width: 100%;
|
|
2569
|
+
overflow-x: hidden;
|
|
2570
|
+
}
|
|
2571
|
+
.header {
|
|
2572
|
+
display: flex;
|
|
2573
|
+
justify-content: space-between;
|
|
2574
|
+
align-items: center;
|
|
2575
|
+
padding: 32px 40px 28px 40px;
|
|
2576
|
+
border-bottom: 1px solid #e2e8f0;
|
|
2577
|
+
background: rgba(255, 255, 255, 0.95);
|
|
2578
|
+
}
|
|
2579
|
+
.header-title {
|
|
2580
|
+
display: flex;
|
|
2581
|
+
align-items: center;
|
|
2582
|
+
gap: 20px;
|
|
2583
|
+
}
|
|
2584
|
+
.header h1 {
|
|
2585
|
+
margin: 0;
|
|
2586
|
+
font-size: 2.5em;
|
|
2587
|
+
font-weight: 900;
|
|
2588
|
+
color: #0f172a;
|
|
2589
|
+
line-height: 1;
|
|
2590
|
+
letter-spacing: -0.03em;
|
|
2591
|
+
}
|
|
2592
|
+
#report-logo {
|
|
2593
|
+
height: 60px;
|
|
2594
|
+
}
|
|
2595
|
+
.run-info {
|
|
2596
|
+
display: flex;
|
|
2597
|
+
gap: 16px;
|
|
2598
|
+
align-items: stretch;
|
|
2599
|
+
background: #ffffff;
|
|
2600
|
+
border-radius: 12px;
|
|
2601
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
|
2602
|
+
border: 1px solid #e2e8f0;
|
|
2603
|
+
overflow: hidden;
|
|
2604
|
+
}
|
|
2605
|
+
.run-info-item {
|
|
2606
|
+
display: flex;
|
|
2607
|
+
flex-direction: column;
|
|
2608
|
+
gap: 8px;
|
|
2609
|
+
padding: 16px 28px;
|
|
2610
|
+
position: relative;
|
|
2611
|
+
flex: 1;
|
|
2612
|
+
min-width: 0;
|
|
2613
|
+
max-width: 100%;
|
|
2614
|
+
overflow-wrap: break-word;
|
|
2615
|
+
word-break: break-word;
|
|
2616
|
+
}
|
|
2617
|
+
.run-info-item:not(:last-child)::after {
|
|
2618
|
+
content: '';
|
|
2619
|
+
position: absolute;
|
|
2620
|
+
right: 0;
|
|
2621
|
+
top: 20%;
|
|
2622
|
+
bottom: 20%;
|
|
2623
|
+
width: 1px;
|
|
2624
|
+
background: linear-gradient(to bottom, transparent, #e2e8f0 20%, #e2e8f0 80%, transparent);
|
|
2625
|
+
}
|
|
2626
|
+
.run-info-item:first-child {
|
|
2627
|
+
background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%);
|
|
2628
|
+
}
|
|
2629
|
+
.run-info-item:last-child {
|
|
2630
|
+
background: linear-gradient(135deg, #ddd6fe 0%, #c4b5fd 100%);
|
|
2631
|
+
}
|
|
2632
|
+
.run-info strong {
|
|
2633
|
+
display: flex;
|
|
2634
|
+
align-items: center;
|
|
2635
|
+
gap: 6px;
|
|
2636
|
+
font-size: 0.7em;
|
|
2637
|
+
text-transform: uppercase;
|
|
2638
|
+
letter-spacing: 1px;
|
|
2639
|
+
color: #64748b;
|
|
2640
|
+
margin: 0;
|
|
2641
|
+
font-weight: 700;
|
|
2642
|
+
}
|
|
2643
|
+
.run-info strong::before {
|
|
2644
|
+
content: '';
|
|
2645
|
+
width: 8px;
|
|
2646
|
+
height: 8px;
|
|
2647
|
+
border-radius: 50%;
|
|
2648
|
+
background: currentColor;
|
|
2649
|
+
opacity: 0.6;
|
|
2650
|
+
}
|
|
2651
|
+
.run-info-item:first-child strong {
|
|
2652
|
+
color: #92400e;
|
|
2653
|
+
}
|
|
2654
|
+
.run-info-item:last-child strong {
|
|
2655
|
+
color: #5b21b6;
|
|
2656
|
+
}
|
|
2657
|
+
.run-info span {
|
|
2658
|
+
font-weight: 800;
|
|
2659
|
+
color: #0f172a;
|
|
2660
|
+
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
2661
|
+
letter-spacing: -0.02em;
|
|
2662
|
+
line-height: 1.2;
|
|
2663
|
+
white-space: nowrap;
|
|
2664
|
+
}
|
|
2665
|
+
.tabs {
|
|
2666
|
+
display: flex;
|
|
2667
|
+
background: #0f172a;
|
|
2668
|
+
padding: 0;
|
|
2669
|
+
margin: 0;
|
|
2670
|
+
position: sticky;
|
|
2671
|
+
top: 0;
|
|
2672
|
+
z-index: 100;
|
|
2673
|
+
overflow-x: auto;
|
|
2674
|
+
-webkit-overflow-scrolling: touch;
|
|
2675
|
+
max-width: 100vw;
|
|
2676
|
+
width: 100%;
|
|
2677
|
+
}
|
|
2678
|
+
.tab-button {
|
|
2679
|
+
flex: 1 1 auto;
|
|
2680
|
+
padding: 24px 20px;
|
|
2681
|
+
background: transparent;
|
|
2682
|
+
border: none;
|
|
2683
|
+
cursor: pointer;
|
|
2684
|
+
font-size: 0.85em;
|
|
2685
|
+
font-weight: 700;
|
|
2686
|
+
color: #64748b;
|
|
2687
|
+
transition: all 0.2s ease;
|
|
2688
|
+
white-space: nowrap;
|
|
2689
|
+
text-transform: uppercase;
|
|
2690
|
+
letter-spacing: 1.2px;
|
|
2691
|
+
border-right: 1px solid #1e293b;
|
|
2692
|
+
min-width: 0;
|
|
2693
|
+
}
|
|
2694
|
+
.tab-button:last-child { border-right: none; }
|
|
2695
|
+
.tab-button:hover {
|
|
2696
|
+
background: #1e293b;
|
|
2697
|
+
color: #ffffff;
|
|
2698
|
+
}
|
|
2699
|
+
.tab-button.active {
|
|
2700
|
+
background: #6366f1;
|
|
2701
|
+
color: #ffffff;
|
|
2702
|
+
}
|
|
2703
|
+
.tab-content {
|
|
2704
|
+
display: none;
|
|
2705
|
+
animation: fadeIn 0.4s ease-out;
|
|
2706
|
+
overflow-x: hidden;
|
|
2707
|
+
max-width: 100%;
|
|
2708
|
+
}
|
|
2709
|
+
.tab-content.active {
|
|
2710
|
+
display: block;
|
|
2711
|
+
}
|
|
2510
2712
|
@keyframes fadeIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2713
|
+
|
|
2714
|
+
@media (max-width: 1200px) {
|
|
2715
|
+
.trend-charts-row {
|
|
2716
|
+
grid-template-columns: 1fr;
|
|
2717
|
+
}
|
|
2718
|
+
.dashboard-bottom-row {
|
|
2719
|
+
grid-template-columns: 1fr;
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
.dashboard-grid {
|
|
2724
|
+
display: grid;
|
|
2725
|
+
grid-template-columns: repeat(4, 1fr);
|
|
2726
|
+
gap: 0;
|
|
2727
|
+
margin: 0 0 40px 0;
|
|
2728
|
+
}
|
|
2729
|
+
.browser-breakdown {
|
|
2730
|
+
display: flex;
|
|
2731
|
+
flex-direction: column;
|
|
2732
|
+
gap: 4px;
|
|
2733
|
+
margin-top: 6px;
|
|
2734
|
+
max-height: 150px;
|
|
2735
|
+
overflow-y: auto;
|
|
2736
|
+
padding-right: 4px;
|
|
2737
|
+
scrollbar-width: thin;
|
|
2738
|
+
scrollbar-color: #cbd5e1 #f1f5f9;
|
|
2739
|
+
}
|
|
2740
|
+
.browser-breakdown::-webkit-scrollbar {
|
|
2741
|
+
width: 6px;
|
|
2742
|
+
display: block;
|
|
2743
|
+
}
|
|
2744
|
+
.browser-breakdown::-webkit-scrollbar-track {
|
|
2745
|
+
background: #f1f5f9;
|
|
2746
|
+
border-radius: 3px;
|
|
2747
|
+
display: block;
|
|
2748
|
+
}
|
|
2749
|
+
.browser-breakdown::-webkit-scrollbar-thumb {
|
|
2750
|
+
background: #cbd5e1;
|
|
2751
|
+
border-radius: 3px;
|
|
2752
|
+
display: block;
|
|
2753
|
+
}
|
|
2754
|
+
.browser-breakdown::-webkit-scrollbar-thumb:hover {
|
|
2755
|
+
background: #94a3b8;
|
|
2756
|
+
display: block;
|
|
2757
|
+
}
|
|
2758
|
+
.browser-item {
|
|
2759
|
+
display: flex;
|
|
2760
|
+
justify-content: space-between;
|
|
2761
|
+
align-items: center;
|
|
2762
|
+
font-size: 0.95em;
|
|
2763
|
+
}
|
|
2764
|
+
.browser-name {
|
|
2765
|
+
font-weight: 700;
|
|
2766
|
+
color: #0f172a;
|
|
2767
|
+
text-transform: capitalize;
|
|
2768
|
+
font-size: 1.05em;
|
|
2769
|
+
}
|
|
2770
|
+
.browser-stats {
|
|
2771
|
+
color: #64748b;
|
|
2772
|
+
font-weight: 700;
|
|
2773
|
+
font-size: 0.95em;
|
|
2774
|
+
}
|
|
2775
|
+
.summary-card {
|
|
2776
|
+
padding: 36px 32px;
|
|
2777
|
+
text-align: left;
|
|
2778
|
+
background: rgba(255, 255, 255, 0.95);
|
|
2779
|
+
border: 1px solid #e2e8f0;
|
|
2780
|
+
transition: background 0.2s ease;
|
|
2781
|
+
border-right: 1px solid #e2e8f0;
|
|
2782
|
+
border-bottom: 1px solid #e2e8f0;
|
|
2783
|
+
}
|
|
2784
|
+
.summary-card:nth-child(4n) { border-right: none; }
|
|
2785
|
+
.summary-card h3 {
|
|
2786
|
+
margin: 0 0 12px;
|
|
2787
|
+
font-size: 0.7em;
|
|
2788
|
+
font-weight: 700;
|
|
2789
|
+
color: #94a3b8;
|
|
2790
|
+
text-transform: uppercase;
|
|
2791
|
+
letter-spacing: 1.2px;
|
|
2792
|
+
}
|
|
2793
|
+
.summary-card .value {
|
|
2794
|
+
font-size: 2.8em;
|
|
2795
|
+
font-weight: 900;
|
|
2796
|
+
margin: 0;
|
|
2797
|
+
line-height: 1;
|
|
2798
|
+
letter-spacing: -0.03em;
|
|
2799
|
+
}
|
|
2800
|
+
.summary-card .trend-percentage {
|
|
2801
|
+
font-size: 0.9em;
|
|
2802
|
+
color: #64748b;
|
|
2803
|
+
margin-top: 8px;
|
|
2804
|
+
font-weight: 600;
|
|
2805
|
+
}
|
|
2806
|
+
|
|
2807
|
+
@media (max-width: 1024px) {
|
|
2808
|
+
.header {
|
|
2809
|
+
padding: 32px 24px;
|
|
2810
|
+
flex-direction: column;
|
|
2811
|
+
gap: 24px;
|
|
2812
|
+
align-items: flex-start;
|
|
2813
|
+
}
|
|
2814
|
+
.run-info {
|
|
2815
|
+
width: 100%;
|
|
2816
|
+
justify-content: flex-start;
|
|
2817
|
+
gap: 24px;
|
|
2818
|
+
}
|
|
2819
|
+
.dashboard-grid {
|
|
2820
|
+
grid-template-columns: repeat(2, 1fr);
|
|
2821
|
+
}
|
|
2822
|
+
.summary-card:nth-child(2n) { border-right: none; }
|
|
2823
|
+
.summary-card:nth-child(n+7) { border-bottom: none; }
|
|
2824
|
+
.filters {
|
|
2825
|
+
padding: 24px;
|
|
2826
|
+
flex-direction: column;
|
|
2827
|
+
}
|
|
2828
|
+
.filters input { min-width: 100%; }
|
|
2829
|
+
.filters select { min-width: 100%; }
|
|
2830
|
+
.filters button { width: 100%; }
|
|
2831
|
+
.copy-btn {
|
|
2832
|
+
font-size: 0.75em;
|
|
2833
|
+
padding: 8px 16px;
|
|
2834
|
+
margin-left: 0;
|
|
2835
|
+
}
|
|
2836
|
+
.console-output-section h4 {
|
|
2837
|
+
flex-direction: column;
|
|
2838
|
+
align-items: flex-start;
|
|
2839
|
+
gap: 8px;
|
|
2840
|
+
}
|
|
2841
|
+
.log-wrapper {
|
|
2842
|
+
max-height: 300px;
|
|
2843
|
+
}
|
|
2844
|
+
.tabs {
|
|
2845
|
+
overflow-x: auto;
|
|
2846
|
+
}
|
|
2847
|
+
.tab-button {
|
|
2848
|
+
padding: 20px 24px;
|
|
2849
|
+
font-size: 0.75em;
|
|
2850
|
+
white-space: nowrap;
|
|
2851
|
+
}
|
|
2852
|
+
.tag {
|
|
2853
|
+
font-size: 0.65em;
|
|
2854
|
+
padding: 4px 10px;
|
|
2855
|
+
margin-right: 4px;
|
|
2856
|
+
margin-bottom: 4px;
|
|
2857
|
+
letter-spacing: 0.3px;
|
|
2858
|
+
}
|
|
2859
|
+
.test-case-header {
|
|
2860
|
+
grid-template-columns: 1fr;
|
|
2861
|
+
grid-template-rows: auto auto auto;
|
|
2862
|
+
gap: 12px;
|
|
2863
|
+
padding: 16px 20px;
|
|
2864
|
+
}
|
|
2865
|
+
.test-case-summary {
|
|
2866
|
+
grid-column: 1;
|
|
2867
|
+
grid-row: 1;
|
|
2868
|
+
flex-direction: column;
|
|
2869
|
+
align-items: flex-start;
|
|
2870
|
+
gap: 8px;
|
|
2871
|
+
width: 100%;
|
|
2872
|
+
max-width: 100%;
|
|
2873
|
+
overflow: hidden;
|
|
2874
|
+
}
|
|
2875
|
+
.test-case-title {
|
|
2876
|
+
width: 100%;
|
|
2877
|
+
max-width: 100%;
|
|
2878
|
+
}
|
|
2879
|
+
.test-case-browser {
|
|
2880
|
+
width: 100%;
|
|
2881
|
+
max-width: 100%;
|
|
2882
|
+
white-space: normal;
|
|
2883
|
+
}
|
|
2884
|
+
.test-case-meta {
|
|
2885
|
+
grid-column: 1;
|
|
2886
|
+
grid-row: 2;
|
|
2887
|
+
width: 100%;
|
|
2888
|
+
gap: 6px;
|
|
2889
|
+
}
|
|
2890
|
+
.test-case-status-duration {
|
|
2891
|
+
grid-column: 1;
|
|
2892
|
+
grid-row: 3;
|
|
2893
|
+
align-items: flex-start;
|
|
2894
|
+
}
|
|
2895
|
+
.test-case {
|
|
2896
|
+
margin: 0 0 12px 0;
|
|
2897
|
+
border-radius: 8px;
|
|
2898
|
+
}
|
|
2899
|
+
.test-case-content {
|
|
2900
|
+
padding: 20px;
|
|
2901
|
+
}
|
|
2902
|
+
.pie-chart-wrapper, .suites-widget, .trend-chart {
|
|
2903
|
+
padding: 32px 24px;
|
|
2904
|
+
}
|
|
2905
|
+
.test-history-grid {
|
|
2906
|
+
grid-template-columns: 1fr;
|
|
2907
|
+
}
|
|
2908
|
+
.ai-failure-cards-grid {
|
|
2909
|
+
grid-template-columns: 1fr;
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
|
|
2913
|
+
@media (max-width: 768px) {
|
|
2914
|
+
.header h1 { font-size: 1.8em; }
|
|
2915
|
+
#report-logo { height: 48px; }
|
|
2916
|
+
.tabs {
|
|
2917
|
+
flex-wrap: nowrap;
|
|
2918
|
+
gap: 0;
|
|
2919
|
+
overflow-x: auto;
|
|
2920
|
+
}
|
|
2921
|
+
.tab-button {
|
|
2922
|
+
padding: 16px 20px;
|
|
2923
|
+
font-size: 0.7em;
|
|
2924
|
+
flex: 1 1 auto;
|
|
2925
|
+
min-width: 100px;
|
|
2926
|
+
}
|
|
2927
|
+
.dashboard-grid {
|
|
2928
|
+
grid-template-columns: 1fr;
|
|
2929
|
+
}
|
|
2930
|
+
.summary-card {
|
|
2931
|
+
padding: 32px 24px !important;
|
|
2932
|
+
border-right: none !important;
|
|
2933
|
+
}
|
|
2934
|
+
.summary-card .value { font-size: 2.5em !important; }
|
|
2935
|
+
.dashboard-bottom-row {
|
|
2936
|
+
grid-template-columns: 1fr;
|
|
2937
|
+
gap: 0;
|
|
2938
|
+
}
|
|
2939
|
+
.dashboard-column {
|
|
2940
|
+
gap: 0;
|
|
2941
|
+
}
|
|
2942
|
+
.pie-chart-wrapper, .suites-widget, .trend-chart {
|
|
2943
|
+
padding: 28px 20px;
|
|
2944
|
+
}
|
|
2945
|
+
.pie-chart-wrapper h3, .suites-header h2, .trend-chart h3, .chart-title-header {
|
|
2946
|
+
font-size: 1.2em;
|
|
2947
|
+
margin-bottom: 20px;
|
|
2948
|
+
}
|
|
2949
|
+
.pie-chart-wrapper div[id^="pieChart-"] {
|
|
2950
|
+
width: 100% !important;
|
|
2951
|
+
max-width: 100% !important;
|
|
2952
|
+
min-height: 280px;
|
|
2953
|
+
overflow: visible !important;
|
|
2954
|
+
}
|
|
2955
|
+
.pie-chart-wrapper {
|
|
2956
|
+
overflow: visible !important;
|
|
2957
|
+
}
|
|
2958
|
+
.trend-chart-container {
|
|
2959
|
+
min-height: 280px;
|
|
2960
|
+
}
|
|
2961
|
+
.suites-grid {
|
|
2962
|
+
grid-template-columns: 1fr;
|
|
2963
|
+
}
|
|
2964
|
+
.test-case-summary {
|
|
2965
|
+
flex-direction: column;
|
|
2966
|
+
align-items: flex-start;
|
|
2967
|
+
gap: 8px;
|
|
2968
|
+
}
|
|
2969
|
+
.test-case-title {
|
|
2970
|
+
width: 100%;
|
|
2971
|
+
}
|
|
2972
|
+
.test-case-browser {
|
|
2973
|
+
width: 100%;
|
|
2974
|
+
}
|
|
2975
|
+
.test-case-meta {
|
|
2976
|
+
flex-wrap: wrap;
|
|
2977
|
+
gap: 6px;
|
|
2978
|
+
width: 100%;
|
|
2979
|
+
}
|
|
2980
|
+
.test-history-trend-section {
|
|
2981
|
+
padding: 0px 20px !important;
|
|
2982
|
+
}
|
|
2983
|
+
.ai-failure-cards-grid {
|
|
2984
|
+
grid-template-columns: 1fr;
|
|
2985
|
+
}
|
|
2986
|
+
.ai-analyzer-stats {
|
|
2987
|
+
flex-direction: column;
|
|
2988
|
+
gap: 15px;
|
|
2989
|
+
text-align: center;
|
|
2990
|
+
}
|
|
2991
|
+
.failure-header {
|
|
2992
|
+
flex-direction: column;
|
|
2993
|
+
align-items: stretch;
|
|
2994
|
+
gap: 15px;
|
|
2995
|
+
}
|
|
2996
|
+
.failure-main-info {
|
|
2997
|
+
text-align: center;
|
|
2998
|
+
}
|
|
2999
|
+
.failure-meta {
|
|
3000
|
+
justify-content: center;
|
|
3001
|
+
}
|
|
3002
|
+
.ai-buttons-group {
|
|
3003
|
+
flex-direction: column;
|
|
3004
|
+
width: 100%;
|
|
3005
|
+
}
|
|
3006
|
+
.compact-ai-btn, .copy-prompt-btn {
|
|
3007
|
+
justify-content: center;
|
|
3008
|
+
padding: 12px 20px;
|
|
3009
|
+
width: 100%;
|
|
3010
|
+
}
|
|
3011
|
+
}
|
|
3012
|
+
|
|
3013
|
+
@media (max-width: 480px) {
|
|
3014
|
+
.header { padding: 24px 16px; }
|
|
3015
|
+
.header h1 { font-size: 1.5em; }
|
|
3016
|
+
#report-logo { height: 42px; }
|
|
3017
|
+
.run-info {
|
|
3018
|
+
flex-direction: column;
|
|
3019
|
+
gap: 12px;
|
|
3020
|
+
width: 100%;
|
|
3021
|
+
}
|
|
3022
|
+
.run-info-item {
|
|
3023
|
+
padding: 14px 20px;
|
|
3024
|
+
}
|
|
3025
|
+
.run-info-item:not(:last-child)::after {
|
|
3026
|
+
display: none;
|
|
3027
|
+
}
|
|
3028
|
+
.run-info-item:not(:last-child) {
|
|
3029
|
+
border-bottom: 1px solid var(--light-gray-color);
|
|
3030
|
+
}
|
|
3031
|
+
.run-info strong {
|
|
3032
|
+
font-size: 0.65em;
|
|
3033
|
+
}
|
|
3034
|
+
.run-info span {
|
|
3035
|
+
font-size: 1.1em;
|
|
3036
|
+
white-space: normal;
|
|
3037
|
+
word-break: break-word;
|
|
3038
|
+
overflow-wrap: break-word;
|
|
3039
|
+
}
|
|
3040
|
+
.tabs {
|
|
3041
|
+
flex-wrap: wrap;
|
|
3042
|
+
gap: 4px;
|
|
3043
|
+
padding: 8px;
|
|
3044
|
+
}
|
|
3045
|
+
.tab-button {
|
|
3046
|
+
padding: 14px 10px;
|
|
3047
|
+
font-size: 0.6em;
|
|
3048
|
+
letter-spacing: 0.3px;
|
|
3049
|
+
flex: 1 1 calc(50% - 4px);
|
|
3050
|
+
min-width: 0;
|
|
3051
|
+
text-align: center;
|
|
3052
|
+
}
|
|
3053
|
+
.dashboard-grid { gap: 0; }
|
|
3054
|
+
.summary-card { padding: 28px 16px !important; }
|
|
3055
|
+
.summary-card h3 { font-size: 0.65em; }
|
|
3056
|
+
.summary-card .value { font-size: 2em !important; }
|
|
3057
|
+
.dashboard-bottom-row { gap: 0; }
|
|
3058
|
+
.dashboard-column {
|
|
3059
|
+
gap: 0;
|
|
3060
|
+
}
|
|
3061
|
+
.pie-chart-wrapper, .suites-widget, .trend-chart {
|
|
3062
|
+
padding: 20px 16px;
|
|
3063
|
+
}
|
|
3064
|
+
.pie-chart-wrapper h3, .suites-header h2, .trend-chart h3, .chart-title-header {
|
|
3065
|
+
font-size: 1em;
|
|
3066
|
+
margin-bottom: 16px;
|
|
3067
|
+
font-weight: 800;
|
|
3068
|
+
}
|
|
3069
|
+
.env-dashboard-title {
|
|
3070
|
+
font-size: 1em;
|
|
3071
|
+
margin-bottom: 6px;
|
|
3072
|
+
}
|
|
3073
|
+
.env-dashboard-subtitle {
|
|
3074
|
+
font-size: 0.85em;
|
|
3075
|
+
}
|
|
3076
|
+
.env-card-header {
|
|
3077
|
+
font-size: 0.85em;
|
|
3078
|
+
}
|
|
3079
|
+
.pie-chart-wrapper div[id^="pieChart-"] {
|
|
3080
|
+
width: 100% !important;
|
|
3081
|
+
max-width: 100% !important;
|
|
3082
|
+
min-height: 250px;
|
|
3083
|
+
overflow: visible !important;
|
|
3084
|
+
}
|
|
3085
|
+
.pie-chart-wrapper {
|
|
3086
|
+
overflow: visible !important;
|
|
3087
|
+
padding: 20px 12px;
|
|
3088
|
+
}
|
|
3089
|
+
.trend-chart-container {
|
|
3090
|
+
min-height: 250px;
|
|
3091
|
+
}
|
|
3092
|
+
.suites-grid {
|
|
3093
|
+
grid-template-columns: 1fr;
|
|
3094
|
+
gap: 16px;
|
|
3095
|
+
}
|
|
3096
|
+
.suite-card {
|
|
3097
|
+
padding: 16px;
|
|
3098
|
+
}
|
|
3099
|
+
.filters {
|
|
3100
|
+
padding: 16px;
|
|
3101
|
+
gap: 8px;
|
|
3102
|
+
}
|
|
3103
|
+
.test-history-trend-section {
|
|
3104
|
+
padding: 0px 16px !important;
|
|
3105
|
+
}
|
|
3106
|
+
.test-case {
|
|
3107
|
+
margin: 0 0 10px 0;
|
|
3108
|
+
border-radius: 6px;
|
|
3109
|
+
}
|
|
3110
|
+
.test-case-header {
|
|
3111
|
+
padding: 14px 16px;
|
|
3112
|
+
}
|
|
3113
|
+
.test-case-content {
|
|
3114
|
+
padding: 16px;
|
|
3115
|
+
}
|
|
3116
|
+
.stat-item .stat-number {
|
|
3117
|
+
font-size: 1.5em;
|
|
3118
|
+
}
|
|
3119
|
+
.failure-header {
|
|
3120
|
+
padding: 15px;
|
|
3121
|
+
}
|
|
3122
|
+
.failure-error-preview, .full-error-details {
|
|
3123
|
+
padding-left: 15px;
|
|
3124
|
+
padding-right: 15px;
|
|
3125
|
+
}
|
|
3126
|
+
.header h1 {
|
|
3127
|
+
word-break: break-word;
|
|
3128
|
+
overflow-wrap: break-word;
|
|
3129
|
+
}
|
|
3130
|
+
h2, h3, h4 {
|
|
3131
|
+
word-break: break-word;
|
|
3132
|
+
overflow-wrap: break-word;
|
|
3133
|
+
}
|
|
3134
|
+
.environment-dashboard-wrapper {
|
|
3135
|
+
padding: 24px 16px;
|
|
3136
|
+
gap: 24px;
|
|
3137
|
+
}
|
|
3138
|
+
.env-card {
|
|
3139
|
+
padding: 20px;
|
|
3140
|
+
}
|
|
3141
|
+
}
|
|
3142
|
+
.summary-card.status-passed { background: rgba(16, 185, 129, 0.02); }
|
|
3143
|
+
.summary-card.status-passed:hover {
|
|
3144
|
+
background: rgba(16, 185, 129, 0.15);
|
|
3145
|
+
box-shadow: 0 4px 12px rgba(16, 185, 129, 0.2);
|
|
3146
|
+
}
|
|
3147
|
+
.summary-card.status-passed .value { color: #10b981; }
|
|
3148
|
+
.summary-card.status-failed { background: rgba(239, 68, 68, 0.02); }
|
|
3149
|
+
.summary-card.status-failed:hover {
|
|
3150
|
+
background: rgba(239, 68, 68, 0.15);
|
|
3151
|
+
box-shadow: 0 4px 12px rgba(239, 68, 68, 0.2);
|
|
3152
|
+
}
|
|
3153
|
+
.summary-card.status-failed .value { color: #ef4444; }
|
|
3154
|
+
.summary-card.status-skipped { background: rgba(245, 158, 11, 0.02); }
|
|
3155
|
+
.summary-card.status-skipped:hover {
|
|
3156
|
+
background: rgba(245, 158, 11, 0.15);
|
|
3157
|
+
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.2);
|
|
3158
|
+
}
|
|
3159
|
+
.summary-card.status-skipped .value { color: #f59e0b; }
|
|
3160
|
+
.summary-card:not([class*='status-']) .value { color: #0f172a; }
|
|
2520
3161
|
.dashboard-bottom-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 28px; align-items: start; }
|
|
2521
|
-
.
|
|
2522
|
-
|
|
2523
|
-
|
|
3162
|
+
.dashboard-column {
|
|
3163
|
+
display: flex;
|
|
3164
|
+
flex-direction: column;
|
|
3165
|
+
gap: 28px;
|
|
3166
|
+
}
|
|
3167
|
+
.pie-chart-wrapper, .suites-widget, .trend-chart {
|
|
3168
|
+
background: rgba(255, 255, 255, 0.95);
|
|
3169
|
+
padding: 48px;
|
|
3170
|
+
border: 1px solid #e2e8f0;
|
|
3171
|
+
border-radius: 16px;
|
|
3172
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
|
3173
|
+
display: flex;
|
|
3174
|
+
flex-direction: column;
|
|
3175
|
+
overflow: visible;
|
|
3176
|
+
margin-bottom: 24px;
|
|
3177
|
+
}
|
|
3178
|
+
.pie-chart-wrapper {
|
|
3179
|
+
position: relative;
|
|
3180
|
+
}
|
|
3181
|
+
.pie-chart-wrapper h3, .suites-header h2, .trend-chart h3, .chart-title-header {
|
|
3182
|
+
text-align: left;
|
|
3183
|
+
margin: 0 0 40px 0;
|
|
3184
|
+
font-size: 1.8em;
|
|
3185
|
+
font-weight: 900;
|
|
3186
|
+
color: #0f172a;
|
|
3187
|
+
letter-spacing: -0.02em;
|
|
3188
|
+
}
|
|
3189
|
+
.trend-chart-container, .pie-chart-wrapper div[id^="pieChart-"] {
|
|
3190
|
+
flex-grow: 1;
|
|
3191
|
+
min-height: 250px;
|
|
3192
|
+
width: 100%;
|
|
3193
|
+
overflow: visible;
|
|
3194
|
+
}
|
|
2524
3195
|
.status-badge-small-tooltip { padding: 2px 5px; border-radius: 3px; font-size: 0.9em; font-weight: 600; color: white; text-transform: uppercase; }
|
|
2525
3196
|
.status-badge-small-tooltip.status-passed { background-color: var(--success-color); }
|
|
2526
3197
|
.status-badge-small-tooltip.status-failed { background-color: var(--danger-color); }
|
|
@@ -2529,40 +3200,307 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2529
3200
|
.suites-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px; }
|
|
2530
3201
|
.summary-badge { background-color: var(--light-gray-color); color: var(--text-color-secondary); padding: 7px 14px; border-radius: 16px; font-size: 0.9em; }
|
|
2531
3202
|
.suites-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px; }
|
|
2532
|
-
.suite-card {
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
3203
|
+
.suite-card {
|
|
3204
|
+
border: none;
|
|
3205
|
+
border-left: 4px solid #e2e8f0;
|
|
3206
|
+
padding: 24px;
|
|
3207
|
+
background: white;
|
|
3208
|
+
transition: all 0.15s ease;
|
|
3209
|
+
}
|
|
3210
|
+
.suite-card:hover {
|
|
3211
|
+
background: #fafbfc;
|
|
3212
|
+
border-left-color: #6366f1;
|
|
3213
|
+
}
|
|
3214
|
+
.suite-card.status-passed { border-left-color: #10b981; }
|
|
3215
|
+
.suite-card.status-passed:hover { background: rgba(16, 185, 129, 0.02); }
|
|
3216
|
+
.suite-card.status-failed { border-left-color: #ef4444; }
|
|
3217
|
+
.suite-card.status-failed:hover { background: rgba(239, 68, 68, 0.02); }
|
|
3218
|
+
.suite-card.status-skipped { border-left-color: #f59e0b; }
|
|
3219
|
+
.suite-card.status-skipped:hover { background: rgba(245, 158, 11, 0.02); }
|
|
2537
3220
|
.suite-card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 12px; }
|
|
2538
3221
|
.suite-name { font-weight: 600; font-size: 1.05em; color: var(--text-color); margin-right: 10px; word-break: break-word;}
|
|
2539
|
-
.browser-tag {
|
|
3222
|
+
.browser-tag {
|
|
3223
|
+
font-size: 0.85em;
|
|
3224
|
+
font-weight: 600;
|
|
3225
|
+
background: linear-gradient(135deg, rgba(96, 165, 250, 0.2) 0%, rgba(59, 130, 246, 0.15) 100%);
|
|
3226
|
+
padding: 6px 12px;
|
|
3227
|
+
border-radius: var(--radius-sm);
|
|
3228
|
+
border: 1px solid rgba(96, 165, 250, 0.3);
|
|
3229
|
+
display: inline-block;
|
|
3230
|
+
box-shadow: 0 2px 8px rgba(96, 165, 250, 0.15), inset 0 1px 0 rgba(96, 165, 250, 0.2);
|
|
3231
|
+
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
|
3232
|
+
letter-spacing: 0.3px;
|
|
3233
|
+
max-width: 200px;
|
|
3234
|
+
overflow: hidden;
|
|
3235
|
+
text-overflow: ellipsis;
|
|
3236
|
+
white-space: nowrap;
|
|
3237
|
+
vertical-align: middle;
|
|
3238
|
+
cursor: help;
|
|
3239
|
+
transition: all 0.2s ease;
|
|
3240
|
+
}
|
|
3241
|
+
.browser-tag:hover {
|
|
3242
|
+
background: linear-gradient(135deg, rgba(96, 165, 250, 0.3) 0%, rgba(59, 130, 246, 0.25) 100%);
|
|
3243
|
+
border-color: rgba(96, 165, 250, 0.5);
|
|
3244
|
+
}
|
|
2540
3245
|
.suite-card-body .test-count { font-size: 0.95em; color: var(--text-color-secondary); display: block; margin-bottom: 10px; }
|
|
2541
3246
|
.suite-stats { display: flex; gap: 14px; font-size: 0.95em; align-items: center; }
|
|
2542
3247
|
.suite-stats span { display: flex; align-items: center; gap: 6px; }
|
|
2543
3248
|
.suite-stats svg { vertical-align: middle; font-size: 1.15em; }
|
|
2544
|
-
.
|
|
2545
|
-
.
|
|
2546
|
-
.
|
|
2547
|
-
.filters
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
.
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
.
|
|
2565
|
-
|
|
3249
|
+
.suite-stats .stat-passed { color: #10b981; }
|
|
3250
|
+
.suite-stats .stat-failed { color: #ef4444; }
|
|
3251
|
+
.suite-stats .stat-skipped { color: #f59e0b; }
|
|
3252
|
+
.filters {
|
|
3253
|
+
display: flex;
|
|
3254
|
+
flex-wrap: wrap;
|
|
3255
|
+
gap: 12px;
|
|
3256
|
+
margin: 0;
|
|
3257
|
+
padding: 24px 32px;
|
|
3258
|
+
background: #f8fafc;
|
|
3259
|
+
border-bottom: 1px solid #e2e8f0;
|
|
3260
|
+
}
|
|
3261
|
+
.filters input, .filters select, .filters button {
|
|
3262
|
+
padding: 14px 18px;
|
|
3263
|
+
border: 2px solid #e2e8f0;
|
|
3264
|
+
font-size: 0.9em;
|
|
3265
|
+
font-family: var(--font-family);
|
|
3266
|
+
font-weight: 600;
|
|
3267
|
+
transition: all 0.15s ease;
|
|
3268
|
+
}
|
|
3269
|
+
.filters input {
|
|
3270
|
+
flex: 1 1 300px;
|
|
3271
|
+
min-width: 0;
|
|
3272
|
+
background: white;
|
|
3273
|
+
}
|
|
3274
|
+
.filters input:focus {
|
|
3275
|
+
outline: none;
|
|
3276
|
+
border-color: #6366f1;
|
|
3277
|
+
}
|
|
3278
|
+
.filters select {
|
|
3279
|
+
flex: 0 0 auto;
|
|
3280
|
+
min-width: 180px;
|
|
3281
|
+
background: white;
|
|
3282
|
+
cursor: pointer;
|
|
3283
|
+
}
|
|
3284
|
+
.filters select:focus {
|
|
3285
|
+
outline: none;
|
|
3286
|
+
border-color: #6366f1;
|
|
3287
|
+
}
|
|
3288
|
+
.filters button {
|
|
3289
|
+
background: #0f172a;
|
|
3290
|
+
color: white;
|
|
3291
|
+
cursor: pointer;
|
|
3292
|
+
border: none;
|
|
3293
|
+
font-weight: 700;
|
|
3294
|
+
padding: 14px 32px;
|
|
3295
|
+
text-transform: uppercase;
|
|
3296
|
+
letter-spacing: 0.5px;
|
|
3297
|
+
font-size: 0.8em;
|
|
3298
|
+
flex: 0 0 auto;
|
|
3299
|
+
}
|
|
3300
|
+
.filters button:hover {
|
|
3301
|
+
background: #1e293b;
|
|
3302
|
+
color: white;
|
|
3303
|
+
}
|
|
3304
|
+
.test-case {
|
|
3305
|
+
margin: 0 0 16px 0;
|
|
3306
|
+
padding: 0;
|
|
3307
|
+
border: 1px solid #e2e8f0;
|
|
3308
|
+
border-radius: 12px;
|
|
3309
|
+
background: rgba(255, 255, 255, 0.95);
|
|
3310
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
|
|
3311
|
+
transition: transform 0.2s ease;
|
|
3312
|
+
overflow: hidden;
|
|
3313
|
+
}
|
|
3314
|
+
.test-case:hover {
|
|
3315
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
3316
|
+
transform: translateY(-2px);
|
|
3317
|
+
border-color: #cbd5e1;
|
|
3318
|
+
}
|
|
3319
|
+
.test-case:last-child {
|
|
3320
|
+
margin-bottom: 0;
|
|
3321
|
+
}
|
|
3322
|
+
.test-case-header {
|
|
3323
|
+
padding: 20px 24px;
|
|
3324
|
+
background: linear-gradient(to right, #ffffff 0%, #f8fafc 100%);
|
|
3325
|
+
cursor: pointer;
|
|
3326
|
+
display: grid;
|
|
3327
|
+
grid-template-columns: 1fr auto;
|
|
3328
|
+
grid-template-rows: auto auto;
|
|
3329
|
+
gap: 12px 20px;
|
|
3330
|
+
transition: all 0.2s ease;
|
|
3331
|
+
border-bottom: 2px solid #f1f5f9;
|
|
3332
|
+
position: relative;
|
|
3333
|
+
}
|
|
3334
|
+
.test-case-header::before {
|
|
3335
|
+
content: '';
|
|
3336
|
+
position: absolute;
|
|
3337
|
+
left: 0;
|
|
3338
|
+
top: 0;
|
|
3339
|
+
bottom: 0;
|
|
3340
|
+
width: 4px;
|
|
3341
|
+
background: transparent;
|
|
3342
|
+
transition: background 0.2s ease;
|
|
3343
|
+
}
|
|
3344
|
+
.test-case-header:hover::before {
|
|
3345
|
+
background: linear-gradient(to bottom, #6366f1 0%, #8b5cf6 100%);
|
|
3346
|
+
}
|
|
3347
|
+
.test-case-header[aria-expanded="true"] {
|
|
3348
|
+
background: linear-gradient(to right, #f8fafc 0%, #f1f5f9 100%);
|
|
3349
|
+
border-bottom-color: #e2e8f0;
|
|
3350
|
+
}
|
|
3351
|
+
.test-case-header[aria-expanded="true"]::before {
|
|
3352
|
+
background: linear-gradient(to bottom, #6366f1 0%, #8b5cf6 100%);
|
|
3353
|
+
}
|
|
3354
|
+
.test-case-summary {
|
|
3355
|
+
display: flex;
|
|
3356
|
+
align-items: center;
|
|
3357
|
+
gap: 14px;
|
|
3358
|
+
flex-wrap: wrap;
|
|
3359
|
+
min-width: 0;
|
|
3360
|
+
grid-column: 1;
|
|
3361
|
+
grid-row: 1;
|
|
3362
|
+
}
|
|
3363
|
+
.test-case-title {
|
|
3364
|
+
font-weight: 600;
|
|
3365
|
+
color: var(--text-color);
|
|
3366
|
+
font-size: 1em;
|
|
3367
|
+
word-break: break-word;
|
|
3368
|
+
overflow-wrap: break-word;
|
|
3369
|
+
flex: 1 1 auto;
|
|
3370
|
+
min-width: 0;
|
|
3371
|
+
}
|
|
3372
|
+
.test-case-browser {
|
|
3373
|
+
font-size: 0.9em;
|
|
3374
|
+
color: var(--text-color-secondary);
|
|
3375
|
+
word-break: break-word;
|
|
3376
|
+
overflow-wrap: break-word;
|
|
3377
|
+
max-width: 100%;
|
|
3378
|
+
}
|
|
3379
|
+
.test-case-meta {
|
|
3380
|
+
display: flex;
|
|
3381
|
+
align-items: center;
|
|
3382
|
+
gap: 8px;
|
|
3383
|
+
font-size: 0.9em;
|
|
3384
|
+
color: var(--text-color-secondary);
|
|
3385
|
+
flex-wrap: wrap;
|
|
3386
|
+
min-width: 0;
|
|
3387
|
+
grid-column: 1;
|
|
3388
|
+
grid-row: 2;
|
|
3389
|
+
}
|
|
3390
|
+
.test-case-status-duration {
|
|
3391
|
+
display: flex;
|
|
3392
|
+
flex-direction: column;
|
|
3393
|
+
align-items: flex-end;
|
|
3394
|
+
gap: 8px;
|
|
3395
|
+
grid-column: 2;
|
|
3396
|
+
grid-row: 1 / 3;
|
|
3397
|
+
align-self: center;
|
|
3398
|
+
}
|
|
3399
|
+
.test-duration {
|
|
3400
|
+
background-color: var(--light-gray-color);
|
|
3401
|
+
padding: 6px 12px;
|
|
3402
|
+
border-radius: 8px;
|
|
3403
|
+
font-size: 0.9em;
|
|
3404
|
+
white-space: nowrap;
|
|
3405
|
+
flex-shrink: 0;
|
|
3406
|
+
font-weight: 700;
|
|
3407
|
+
color: #0f172a;
|
|
3408
|
+
}
|
|
3409
|
+
.status-badge {
|
|
3410
|
+
padding: 8px 20px;
|
|
3411
|
+
border-radius: 0;
|
|
3412
|
+
font-size: 0.7em;
|
|
3413
|
+
font-weight: 800;
|
|
3414
|
+
color: white;
|
|
3415
|
+
text-transform: uppercase;
|
|
3416
|
+
min-width: 100px;
|
|
3417
|
+
text-align: center;
|
|
3418
|
+
letter-spacing: 1px;
|
|
3419
|
+
}
|
|
3420
|
+
.status-badge.status-passed { background: #10b981; }
|
|
3421
|
+
.status-badge.status-failed { background: #ef4444; }
|
|
3422
|
+
.status-badge.status-skipped { background: #f59e0b; }
|
|
3423
|
+
.status-badge.status-unknown { background: #64748b; }
|
|
3424
|
+
|
|
3425
|
+
/* --- NEON GLASS SEVERITY BADGES --- */
|
|
3426
|
+
.severity-badge {
|
|
3427
|
+
display: inline-flex;
|
|
3428
|
+
align-items: center;
|
|
3429
|
+
gap: 6px;
|
|
3430
|
+
padding: 5px 12px;
|
|
3431
|
+
border-radius: 99px;
|
|
3432
|
+
font-size: 0.75em;
|
|
3433
|
+
font-weight: 700;
|
|
3434
|
+
text-transform: uppercase;
|
|
3435
|
+
letter-spacing: 0.05em;
|
|
3436
|
+
border: 1px solid;
|
|
3437
|
+
backdrop-filter: blur(4px);
|
|
3438
|
+
-webkit-backdrop-filter: blur(4px);
|
|
3439
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
3440
|
+
}
|
|
3441
|
+
.severity-badge::before {
|
|
3442
|
+
content: '';
|
|
3443
|
+
width: 6px;
|
|
3444
|
+
height: 6px;
|
|
3445
|
+
border-radius: 50%;
|
|
3446
|
+
background-color: currentColor;
|
|
3447
|
+
box-shadow: 0 0 6px currentColor;
|
|
3448
|
+
}
|
|
3449
|
+
/* Auto-map colors based on data-severity attribute */
|
|
3450
|
+
.severity-badge[data-severity="critical"] {
|
|
3451
|
+
color: #ff4d4d;
|
|
3452
|
+
background-color: rgba(255, 77, 77, 0.1);
|
|
3453
|
+
border-color: rgba(255, 77, 77, 0.25);
|
|
3454
|
+
}
|
|
3455
|
+
.severity-badge[data-severity="high"] {
|
|
3456
|
+
color: #fb923c;
|
|
3457
|
+
background-color: rgba(251, 146, 60, 0.1);
|
|
3458
|
+
border-color: rgba(251, 146, 60, 0.25);
|
|
3459
|
+
}
|
|
3460
|
+
.severity-badge[data-severity="medium"] {
|
|
3461
|
+
color: #facc15;
|
|
3462
|
+
background-color: rgba(250, 204, 21, 0.1);
|
|
3463
|
+
border-color: rgba(250, 204, 21, 0.25);
|
|
3464
|
+
}
|
|
3465
|
+
.severity-badge[data-severity="low"] {
|
|
3466
|
+
color: #4ade80;
|
|
3467
|
+
background-color: rgba(74, 222, 128, 0.1);
|
|
3468
|
+
border-color: rgba(74, 222, 128, 0.25);
|
|
3469
|
+
}
|
|
3470
|
+
.severity-badge[data-severity="minor"] {
|
|
3471
|
+
color: #94a3b8;
|
|
3472
|
+
background-color: rgba(148, 163, 184, 0.1);
|
|
3473
|
+
border-color: rgba(148, 163, 184, 0.25);
|
|
3474
|
+
}
|
|
3475
|
+
|
|
3476
|
+
.tag {
|
|
3477
|
+
display: inline-flex;
|
|
3478
|
+
align-items: center;
|
|
3479
|
+
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
|
3480
|
+
color: #ffffff;
|
|
3481
|
+
padding: 6px 14px;
|
|
3482
|
+
border-radius: 6px;
|
|
3483
|
+
font-size: 0.8em;
|
|
3484
|
+
margin-right: 8px;
|
|
3485
|
+
margin-bottom: 4px;
|
|
3486
|
+
font-weight: 600;
|
|
3487
|
+
text-transform: uppercase;
|
|
3488
|
+
letter-spacing: 0.5px;
|
|
3489
|
+
box-shadow: 0 2px 6px rgba(99, 102, 241, 0.25);
|
|
3490
|
+
transition: all 0.2s ease;
|
|
3491
|
+
flex-shrink: 0;
|
|
3492
|
+
white-space: nowrap;
|
|
3493
|
+
}
|
|
3494
|
+
.tag:hover {
|
|
3495
|
+
box-shadow: 0 4px 10px rgba(99, 102, 241, 0.35);
|
|
3496
|
+
transform: translateY(-1px);
|
|
3497
|
+
}
|
|
3498
|
+
.test-case-content {
|
|
3499
|
+
display: none;
|
|
3500
|
+
padding: 24px;
|
|
3501
|
+
background: linear-gradient(to bottom, #ffffff 0%, #f9fafb 100%);
|
|
3502
|
+
border-top: 1px solid #e2e8f0;
|
|
3503
|
+
}
|
|
2566
3504
|
.test-case-content h4 { margin-top: 22px; margin-bottom: 14px; font-size: 1.15em; color: var(--primary-color); }
|
|
2567
3505
|
.test-case-content p { margin-bottom: 10px; font-size: 1em; }
|
|
2568
3506
|
.test-error-summary { margin-bottom: 20px; padding: 14px; background-color: rgba(244,67,54,0.05); border: 1px solid rgba(244,67,54,0.2); border-left: 4px solid var(--danger-color); border-radius: 4px; }
|
|
@@ -2609,10 +3547,24 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2609
3547
|
.test-history-header h3 { margin: 0; font-size: 1.15em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } /* This was h3, changed to p for consistency with user file */
|
|
2610
3548
|
.test-history-header p { font-weight: 500 } /* Added this */
|
|
2611
3549
|
.test-history-trend { margin-bottom: 20px; min-height: 110px; }
|
|
3550
|
+
.test-history-trend-section {
|
|
3551
|
+
padding: 0px 48px !important;
|
|
3552
|
+
}
|
|
3553
|
+
.test-history-trend-section .chart-title-header {
|
|
3554
|
+
margin: 0 0 20px 0 !important;
|
|
3555
|
+
}
|
|
2612
3556
|
.test-history-trend div[id^="testHistoryChart-"] { display: block; margin: 0 auto; max-width:100%; height: 100px; width: 320px; }
|
|
2613
3557
|
.test-history-details-collapsible summary { cursor: pointer; font-size: 1em; color: var(--primary-color); margin-bottom: 10px; font-weight:500; }
|
|
2614
3558
|
.test-history-details-collapsible summary:hover {text-decoration: underline;}
|
|
2615
|
-
.test-history-details
|
|
3559
|
+
.test-history-details {
|
|
3560
|
+
overflow-x: auto;
|
|
3561
|
+
max-width: 100%;
|
|
3562
|
+
}
|
|
3563
|
+
.test-history-details table {
|
|
3564
|
+
width: 100%;
|
|
3565
|
+
border-collapse: collapse;
|
|
3566
|
+
font-size: 0.95em;
|
|
3567
|
+
}
|
|
2616
3568
|
.test-history-details th, .test-history-details td { padding: 9px 12px; text-align: left; border-bottom: 1px solid var(--light-gray-color); }
|
|
2617
3569
|
.test-history-details th { background-color: var(--light-gray-color); font-weight: 600; }
|
|
2618
3570
|
.status-badge-small { padding: 3px 7px; border-radius: 4px; font-size: 0.8em; font-weight: 600; color: white; text-transform: uppercase; display: inline-block; }
|
|
@@ -2630,13 +3582,57 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2630
3582
|
.ai-failure-card-body { padding: 20px; }
|
|
2631
3583
|
.ai-fix-btn { background-color: var(--primary-color); color: white; border: none; padding: 10px 18px; font-size: 1em; font-weight: 600; border-radius: 6px; cursor: pointer; transition: background-color 0.2s ease, transform 0.2s ease; display: inline-flex; align-items: center; gap: 8px; }
|
|
2632
3584
|
.ai-fix-btn:hover { background-color: var(--accent-color); transform: translateY(-2px); }
|
|
2633
|
-
.ai-modal-overlay {
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
3585
|
+
.ai-modal-overlay {
|
|
3586
|
+
position: fixed;
|
|
3587
|
+
top: 0;
|
|
3588
|
+
left: 0;
|
|
3589
|
+
width: 100%;
|
|
3590
|
+
height: 100%;
|
|
3591
|
+
background-color: rgba(0,0,0,0.8);
|
|
3592
|
+
display: none;
|
|
3593
|
+
align-items: center;
|
|
3594
|
+
justify-content: center;
|
|
3595
|
+
z-index: 1050;
|
|
3596
|
+
animation: fadeIn 0.3s;
|
|
3597
|
+
}
|
|
3598
|
+
.ai-modal-content {
|
|
3599
|
+
background-color: var(--card-background-color);
|
|
3600
|
+
color: var(--text-color);
|
|
3601
|
+
border-radius: var(--border-radius);
|
|
3602
|
+
width: 90%;
|
|
3603
|
+
max-width: 800px;
|
|
3604
|
+
max-height: 90vh;
|
|
3605
|
+
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
|
|
3606
|
+
display: flex;
|
|
3607
|
+
flex-direction: column;
|
|
3608
|
+
overflow: hidden;
|
|
3609
|
+
}
|
|
3610
|
+
.ai-modal-header {
|
|
3611
|
+
padding: 18px 25px;
|
|
3612
|
+
border-bottom: 1px solid var(--border-color);
|
|
3613
|
+
display: flex;
|
|
3614
|
+
justify-content: space-between;
|
|
3615
|
+
align-items: center;
|
|
3616
|
+
}
|
|
3617
|
+
.ai-modal-header h3 {
|
|
3618
|
+
margin: 0;
|
|
3619
|
+
font-size: 1.25em;
|
|
3620
|
+
}
|
|
3621
|
+
.ai-modal-close {
|
|
3622
|
+
font-size: 2rem;
|
|
3623
|
+
font-weight: 300;
|
|
3624
|
+
cursor: pointer;
|
|
3625
|
+
color: var(--dark-gray-color);
|
|
3626
|
+
line-height: 1;
|
|
3627
|
+
transition: color 0.2s;
|
|
3628
|
+
}
|
|
3629
|
+
.ai-modal-close:hover {
|
|
3630
|
+
color: var(--danger-color);
|
|
3631
|
+
}
|
|
3632
|
+
.ai-modal-body {
|
|
3633
|
+
padding: 25px;
|
|
3634
|
+
overflow-y: auto;
|
|
3635
|
+
}
|
|
2640
3636
|
.ai-modal-body h4 { margin-top: 18px; margin-bottom: 10px; font-size: 1.1em; color: var(--primary-color); }
|
|
2641
3637
|
.ai-modal-body p { margin-bottom: 15px; }
|
|
2642
3638
|
.ai-loader { margin: 40px auto; border: 5px solid #f3f3f3; border-top: 5px solid var(--primary-color); border-radius: 50%; width: 50px; height: 50px; animation: spin 1s linear infinite; }
|
|
@@ -2650,9 +3646,68 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2650
3646
|
.view-trace:hover { background: #2c5282; }
|
|
2651
3647
|
.download-trace { background: #e2e8f0; color: #2d3748; }
|
|
2652
3648
|
.download-trace:hover { background: #cbd5e0; }
|
|
2653
|
-
.filters button.clear-filters-btn {
|
|
2654
|
-
|
|
2655
|
-
|
|
3649
|
+
.filters button.clear-filters-btn {
|
|
3650
|
+
background-color: var(--medium-gray-color);
|
|
3651
|
+
color: var(--text-color);
|
|
3652
|
+
pointer-events: auto;
|
|
3653
|
+
cursor: pointer;
|
|
3654
|
+
}
|
|
3655
|
+
.filters button.clear-filters-btn:active,
|
|
3656
|
+
.filters button.clear-filters-btn:focus {
|
|
3657
|
+
background-color: var(--medium-gray-color);
|
|
3658
|
+
color: var(--text-color);
|
|
3659
|
+
transform: none;
|
|
3660
|
+
box-shadow: none;
|
|
3661
|
+
outline: none;
|
|
3662
|
+
}
|
|
3663
|
+
.copy-btn {
|
|
3664
|
+
color: #ffffff;
|
|
3665
|
+
background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
|
|
3666
|
+
border: none;
|
|
3667
|
+
border-radius: 8px;
|
|
3668
|
+
cursor: pointer;
|
|
3669
|
+
font-size: 0.85em;
|
|
3670
|
+
font-weight: 600;
|
|
3671
|
+
padding: 10px 20px;
|
|
3672
|
+
transition: all 0.2s ease;
|
|
3673
|
+
box-shadow: 0 2px 8px rgba(99, 102, 241, 0.2);
|
|
3674
|
+
text-transform: uppercase;
|
|
3675
|
+
letter-spacing: 0.5px;
|
|
3676
|
+
margin-left: auto;
|
|
3677
|
+
display: inline-flex;
|
|
3678
|
+
align-items: center;
|
|
3679
|
+
gap: 6px;
|
|
3680
|
+
}
|
|
3681
|
+
.copy-btn:hover {
|
|
3682
|
+
background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
|
|
3683
|
+
box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
|
|
3684
|
+
transform: translateY(-1px);
|
|
3685
|
+
}
|
|
3686
|
+
.copy-btn:active {
|
|
3687
|
+
transform: translateY(0);
|
|
3688
|
+
box-shadow: 0 2px 6px rgba(99, 102, 241, 0.2);
|
|
3689
|
+
}
|
|
3690
|
+
.log-wrapper {
|
|
3691
|
+
max-width: 100%;
|
|
3692
|
+
overflow-x: auto;
|
|
3693
|
+
overflow-y: auto;
|
|
3694
|
+
max-height: 400px;
|
|
3695
|
+
border-radius: 8px;
|
|
3696
|
+
background: #2d2d2d;
|
|
3697
|
+
}
|
|
3698
|
+
.log-wrapper pre {
|
|
3699
|
+
margin: 0;
|
|
3700
|
+
white-space: pre;
|
|
3701
|
+
word-wrap: normal;
|
|
3702
|
+
overflow-wrap: normal;
|
|
3703
|
+
}
|
|
3704
|
+
.console-output-section h4 {
|
|
3705
|
+
display: flex;
|
|
3706
|
+
align-items: center;
|
|
3707
|
+
justify-content: space-between;
|
|
3708
|
+
gap: 16px;
|
|
3709
|
+
margin-bottom: 12px;
|
|
3710
|
+
}
|
|
2656
3711
|
/* Compact AI Failure Analyzer Styles */
|
|
2657
3712
|
.ai-analyzer-stats {
|
|
2658
3713
|
display: flex;
|
|
@@ -2852,6 +3907,62 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2852
3907
|
max-height: 300px;
|
|
2853
3908
|
overflow-y: auto;
|
|
2854
3909
|
}
|
|
3910
|
+
.ai-suggestion-container {
|
|
3911
|
+
margin-top: 15px;
|
|
3912
|
+
border-top: 2px solid #e2e8f0;
|
|
3913
|
+
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
|
3914
|
+
animation: slideDown 0.3s ease-out;
|
|
3915
|
+
}
|
|
3916
|
+
@keyframes slideDown {
|
|
3917
|
+
from {
|
|
3918
|
+
opacity: 0;
|
|
3919
|
+
max-height: 0;
|
|
3920
|
+
transform: translateY(-10px);
|
|
3921
|
+
}
|
|
3922
|
+
to {
|
|
3923
|
+
opacity: 1;
|
|
3924
|
+
max-height: 1000px;
|
|
3925
|
+
transform: translateY(0);
|
|
3926
|
+
}
|
|
3927
|
+
}
|
|
3928
|
+
.ai-suggestion-content {
|
|
3929
|
+
padding: 20px;
|
|
3930
|
+
}
|
|
3931
|
+
.ai-suggestion-header {
|
|
3932
|
+
display: flex;
|
|
3933
|
+
align-items: center;
|
|
3934
|
+
gap: 10px;
|
|
3935
|
+
margin-bottom: 15px;
|
|
3936
|
+
padding-bottom: 10px;
|
|
3937
|
+
border-bottom: 2px solid #6366f1;
|
|
3938
|
+
}
|
|
3939
|
+
.ai-suggestion-header h4 {
|
|
3940
|
+
margin: 0;
|
|
3941
|
+
color: #6366f1;
|
|
3942
|
+
font-size: 1.1em;
|
|
3943
|
+
font-weight: 700;
|
|
3944
|
+
}
|
|
3945
|
+
.ai-suggestion-body {
|
|
3946
|
+
color: #0f172a;
|
|
3947
|
+
line-height: 1.6;
|
|
3948
|
+
}
|
|
3949
|
+
.ai-suggestion-body h4 {
|
|
3950
|
+
color: #6366f1;
|
|
3951
|
+
margin-top: 15px;
|
|
3952
|
+
margin-bottom: 8px;
|
|
3953
|
+
font-size: 1em;
|
|
3954
|
+
}
|
|
3955
|
+
.ai-suggestion-body p {
|
|
3956
|
+
margin-bottom: 10px;
|
|
3957
|
+
}
|
|
3958
|
+
.ai-suggestion-body pre {
|
|
3959
|
+
background: #1e293b;
|
|
3960
|
+
color: #e2e8f0;
|
|
3961
|
+
padding: 12px;
|
|
3962
|
+
border-radius: 6px;
|
|
3963
|
+
overflow-x: auto;
|
|
3964
|
+
font-size: 0.9em;
|
|
3965
|
+
}
|
|
2855
3966
|
|
|
2856
3967
|
/* Responsive adjustments for compact design */
|
|
2857
3968
|
@media (max-width: 768px) {
|
|
@@ -2894,10 +4005,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2894
4005
|
}
|
|
2895
4006
|
}
|
|
2896
4007
|
|
|
2897
|
-
|
|
2898
|
-
@media (max-width: 992px) { .dashboard-bottom-row { grid-template-columns: 1fr; } .pie-chart-wrapper div[id^="pieChart-"] { max-width: 350px; margin: 0 auto; } .filters input { min-width: 180px; } .filters select { min-width: 150px; } }
|
|
2899
|
-
@media (max-width: 768px) { body { font-size: 15px; } .container { margin: 10px; padding: 20px; } .header { flex-direction: column; align-items: flex-start; gap: 15px; } .header h1 { font-size: 1.6em; } .run-info { text-align: left; font-size:0.9em; } .tabs { margin-bottom: 25px;} .tab-button { padding: 12px 20px; font-size: 1.05em;} .dashboard-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 18px;} .summary-card .value {font-size: 2em;} .summary-card h3 {font-size: 0.95em;} .filters { flex-direction: column; padding: 18px; gap: 12px;} .filters input, .filters select, .filters button {width: 100%; box-sizing: border-box;} .test-case-header { flex-direction: column; align-items: flex-start; gap: 10px; padding: 14px; } .test-case-summary {gap: 10px;} .test-case-title {font-size: 1.05em;} .test-case-meta { flex-direction: row; flex-wrap: wrap; gap: 8px; margin-top: 8px;} .attachments-grid {grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 18px;} .test-history-grid {grid-template-columns: 1fr;} .pie-chart-wrapper {min-height: auto;} .ai-failure-cards-grid { grid-template-columns: 1fr; } }
|
|
2900
|
-
@media (max-width: 480px) { body {font-size: 14px;} .container {padding: 15px;} .header h1 {font-size: 1.4em;} #report-logo { height: 35px; width: 50px; } .tab-button {padding: 10px 15px; font-size: 1em;} .summary-card .value {font-size: 1.8em;} .attachments-grid {grid-template-columns: 1fr;} .step-item {padding-left: calc(var(--depth, 0) * 18px);} .test-case-content, .step-details {padding: 15px;} .trend-charts-row {gap: 20px;} .trend-chart {padding: 20px;} }
|
|
4008
|
+
|
|
2901
4009
|
</style>
|
|
2902
4010
|
</head>
|
|
2903
4011
|
<body>
|
|
@@ -2907,11 +4015,16 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2907
4015
|
<img id="report-logo" src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png" alt="Report Logo">
|
|
2908
4016
|
<h1>Pulse Report</h1>
|
|
2909
4017
|
</div>
|
|
2910
|
-
<div class="run-info"
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
2914
|
-
|
|
4018
|
+
<div class="run-info">
|
|
4019
|
+
<div class="run-info-item">
|
|
4020
|
+
<strong>Run Date</strong>
|
|
4021
|
+
<span>${formatDate(runSummary.timestamp)}</span>
|
|
4022
|
+
</div>
|
|
4023
|
+
<div class="run-info-item">
|
|
4024
|
+
<strong>Total Duration</strong>
|
|
4025
|
+
<span>${formatDuration(runSummary.duration)}</span>
|
|
4026
|
+
</div>
|
|
4027
|
+
</div>
|
|
2915
4028
|
</header>
|
|
2916
4029
|
<div class="tabs">
|
|
2917
4030
|
<button class="tab-button active" data-tab="dashboard">Dashboard</button>
|
|
@@ -2935,11 +4048,37 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2935
4048
|
}</div><div class="trend-percentage">${skipPercentage}%</div></div>
|
|
2936
4049
|
<div class="summary-card"><h3>Avg. Test Time</h3><div class="value">${avgTestDuration}</div></div>
|
|
2937
4050
|
<div class="summary-card"><h3>Run Duration</h3><div class="value">${formatDuration(
|
|
2938
|
-
runSummary.duration
|
|
4051
|
+
runSummary.duration,
|
|
2939
4052
|
)}</div></div>
|
|
4053
|
+
<div class="summary-card">
|
|
4054
|
+
<h3>🔄 Retry Count</h3>
|
|
4055
|
+
<div class="value">${totalRetried}</div>
|
|
4056
|
+
</div>
|
|
4057
|
+
<div class="summary-card">
|
|
4058
|
+
<h3>🌐 Browser Distribution <span style="font-size: 0.7em; color: var(--text-color-secondary); font-weight: 400;">(${browserBreakdown.length} total)</span></h3>
|
|
4059
|
+
<div class="browser-breakdown" style="max-height: 200px; overflow-y: auto; padding-right: 4px;">
|
|
4060
|
+
${browserBreakdown
|
|
4061
|
+
.slice(0, 5)
|
|
4062
|
+
.map(
|
|
4063
|
+
(b) =>
|
|
4064
|
+
`<div class="browser-item">
|
|
4065
|
+
<span class="browser-name">${sanitizeHTML(b.browser)}</span>
|
|
4066
|
+
<span class="browser-stats">${b.percentage}% (${b.count})</span>
|
|
4067
|
+
</div>`,
|
|
4068
|
+
)
|
|
4069
|
+
.join("")}
|
|
4070
|
+
${
|
|
4071
|
+
browserBreakdown.length > 5
|
|
4072
|
+
? `<div class="browser-item" style="opacity: 0.6; font-style: italic; justify-content: center; border-top: 1px solid #e2e8f0; margin-top: 8px; padding-top: 8px;">
|
|
4073
|
+
<span>+${browserBreakdown.length - 5} more browsers</span>
|
|
4074
|
+
</div>`
|
|
4075
|
+
: ""
|
|
4076
|
+
}
|
|
4077
|
+
</div>
|
|
4078
|
+
</div>
|
|
2940
4079
|
</div>
|
|
2941
4080
|
<div class="dashboard-bottom-row">
|
|
2942
|
-
<div
|
|
4081
|
+
<div class="dashboard-column">
|
|
2943
4082
|
${generatePieChart(
|
|
2944
4083
|
[
|
|
2945
4084
|
{ label: "Passed", value: runSummary.passed },
|
|
@@ -2947,7 +4086,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2947
4086
|
{ label: "Skipped", value: runSummary.skipped || 0 },
|
|
2948
4087
|
],
|
|
2949
4088
|
400,
|
|
2950
|
-
390
|
|
4089
|
+
390,
|
|
2951
4090
|
)}
|
|
2952
4091
|
${
|
|
2953
4092
|
runSummary.environment &&
|
|
@@ -2957,7 +4096,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2957
4096
|
}
|
|
2958
4097
|
</div>
|
|
2959
4098
|
|
|
2960
|
-
<div
|
|
4099
|
+
<div class="dashboard-column">
|
|
2961
4100
|
${generateSuitesWidget(suitesData)}
|
|
2962
4101
|
${generateSeverityDistributionChart(results)}
|
|
2963
4102
|
</div>
|
|
@@ -2969,22 +4108,21 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2969
4108
|
<select id="filter-status"><option value="">All Statuses</option><option value="passed">Passed</option><option value="failed">Failed</option><option value="skipped">Skipped</option></select>
|
|
2970
4109
|
<select id="filter-browser"><option value="">All Browsers</option>${Array.from(
|
|
2971
4110
|
new Set(
|
|
2972
|
-
(results || []).map((test) => test.browser || "unknown")
|
|
2973
|
-
)
|
|
4111
|
+
(results || []).map((test) => test.browser || "unknown"),
|
|
4112
|
+
),
|
|
2974
4113
|
)
|
|
2975
4114
|
.map(
|
|
2976
4115
|
(browser) =>
|
|
2977
4116
|
`<option value="${sanitizeHTML(browser)}">${sanitizeHTML(
|
|
2978
|
-
browser
|
|
2979
|
-
)}</option
|
|
4117
|
+
browser,
|
|
4118
|
+
)}</option>`,
|
|
2980
4119
|
)
|
|
2981
4120
|
.join("")}</select>
|
|
2982
|
-
<button id="
|
|
4121
|
+
<button id="clear-run-summary-filters" class="clear-filters-btn">Clear Filters</button>
|
|
2983
4122
|
</div>
|
|
2984
4123
|
<div class="test-cases-list">${generateTestCasesHTML()}</div>
|
|
2985
4124
|
</div>
|
|
2986
4125
|
<div id="test-history" class="tab-content">
|
|
2987
|
-
<h2 class="tab-main-title">Execution Trends</h2>
|
|
2988
4126
|
<div class="trend-charts-row">
|
|
2989
4127
|
<div class="trend-chart"><h3 class="chart-title-header">Test Volume & Outcome Trends</h3>
|
|
2990
4128
|
${
|
|
@@ -3011,13 +4149,15 @@ function generateHTML(reportData, trendData = null) {
|
|
|
3011
4149
|
${generateDescribeDurationChart(results)}
|
|
3012
4150
|
</div>
|
|
3013
4151
|
</div>
|
|
3014
|
-
<h2 class="tab-main-title">Test Distribution by Worker ${infoTooltip}</h2>
|
|
3015
4152
|
<div class="trend-charts-row">
|
|
3016
4153
|
<div class="trend-chart">
|
|
4154
|
+
<h3 class="chart-title-header">Test Distribution by Worker ${infoTooltip}</h3>
|
|
3017
4155
|
${generateWorkerDistributionChart(results)}
|
|
3018
4156
|
</div>
|
|
3019
4157
|
</div>
|
|
3020
|
-
<
|
|
4158
|
+
<div class="trend-chart test-history-trend-section" style="border-bottom: none;">
|
|
4159
|
+
<h3 class="chart-title-header">Individual Test History</h3>
|
|
4160
|
+
</div>
|
|
3021
4161
|
${
|
|
3022
4162
|
trendData &&
|
|
3023
4163
|
trendData.testRuns &&
|
|
@@ -3032,7 +4172,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
3032
4172
|
<footer style="padding: 0.5rem; box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05); text-align: center; font-family: 'Segoe UI', system-ui, sans-serif;">
|
|
3033
4173
|
<div style="display: inline-flex; align-items: center; gap: 0.5rem; color: #333; font-size: 0.9rem; font-weight: 600; letter-spacing: 0.5px;">
|
|
3034
4174
|
<span>Created by</span>
|
|
3035
|
-
<a href="https://
|
|
4175
|
+
<a href="https://www.npmjs.com/package/@arghajit/playwright-pulse-report" target="_blank" rel="noopener noreferrer" style="color: #7737BF; font-weight: 700; font-style: italic; text-decoration: none; transition: all 0.2s ease;" onmouseover="this.style.color='#BF5C37'" onmouseout="this.style.color='#7737BF'">Pulse Report</a>
|
|
3036
4176
|
</div>
|
|
3037
4177
|
<div style="margin-top: 0.5rem; font-size: 0.75rem; color: #666;">Crafted with precision</div>
|
|
3038
4178
|
</footer>
|
|
@@ -3051,25 +4191,35 @@ function generateHTML(reportData, trendData = null) {
|
|
|
3051
4191
|
console.error('Could not find log element with ID:', elementId);
|
|
3052
4192
|
return;
|
|
3053
4193
|
}
|
|
4194
|
+
const originalText = button.textContent;
|
|
3054
4195
|
navigator.clipboard.writeText(logElement.innerText).then(() => {
|
|
3055
4196
|
button.textContent = 'Copied!';
|
|
3056
|
-
setTimeout(() => { button.textContent =
|
|
4197
|
+
setTimeout(() => { button.textContent = originalText; }, 2000);
|
|
3057
4198
|
}).catch(err => {
|
|
3058
4199
|
console.error('Failed to copy log content:', err);
|
|
3059
4200
|
button.textContent = 'Failed';
|
|
3060
|
-
setTimeout(() => { button.textContent =
|
|
4201
|
+
setTimeout(() => { button.textContent = originalText; }, 2000);
|
|
3061
4202
|
});
|
|
3062
4203
|
}
|
|
3063
4204
|
|
|
3064
4205
|
// --- AI Failure Analyzer Functions ---
|
|
3065
4206
|
function getAIFix(button) {
|
|
3066
|
-
const
|
|
3067
|
-
const
|
|
3068
|
-
const
|
|
4207
|
+
const failureItem = button.closest('.compact-failure-item');
|
|
4208
|
+
const aiContainer = failureItem.querySelector('.ai-suggestion-container');
|
|
4209
|
+
const aiContent = failureItem.querySelector('.ai-suggestion-content');
|
|
3069
4210
|
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
4211
|
+
// Toggle if already visible
|
|
4212
|
+
if (aiContainer.style.display === 'block') {
|
|
4213
|
+
aiContainer.style.display = 'none';
|
|
4214
|
+
button.querySelector('.ai-text').textContent = 'AI Fix';
|
|
4215
|
+
return;
|
|
4216
|
+
}
|
|
4217
|
+
|
|
4218
|
+
// Show loading state
|
|
4219
|
+
aiContainer.style.display = 'block';
|
|
4220
|
+
aiContent.innerHTML = '<div class="ai-loader" style="margin: 40px auto;"></div>';
|
|
4221
|
+
button.querySelector('.ai-text').textContent = 'Loading...';
|
|
4222
|
+
button.disabled = true;
|
|
3073
4223
|
|
|
3074
4224
|
try {
|
|
3075
4225
|
const testJson = button.dataset.testJson;
|
|
@@ -3087,7 +4237,6 @@ function getAIFix(button) {
|
|
|
3087
4237
|
const codeSnippet = test.snippet || '';
|
|
3088
4238
|
|
|
3089
4239
|
const shortTestName = testName.split(' > ').pop();
|
|
3090
|
-
modalTitle.textContent = \`Analysis for: \${shortTestName}\`;
|
|
3091
4240
|
|
|
3092
4241
|
const apiUrl = 'https://ai-test-analyser.netlify.app/api/analyze';
|
|
3093
4242
|
fetch(apiUrl, {
|
|
@@ -3151,18 +4300,31 @@ function getAIFix(button) {
|
|
|
3151
4300
|
suggestionsHtml += \`<div class="code-section"><pre><code>No suggestion provided.</code></pre></div>\`;
|
|
3152
4301
|
}
|
|
3153
4302
|
|
|
3154
|
-
// Combine both parts and
|
|
3155
|
-
|
|
4303
|
+
// Combine both parts and display inline
|
|
4304
|
+
button.querySelector('.ai-text').textContent = 'Hide AI Fix';
|
|
4305
|
+
button.disabled = false;
|
|
4306
|
+
aiContent.innerHTML = \`
|
|
4307
|
+
<div class="ai-suggestion-header">
|
|
4308
|
+
<h4>🤖 AI Analysis Result</h4>
|
|
4309
|
+
</div>
|
|
4310
|
+
<div class="ai-suggestion-body">
|
|
4311
|
+
\${analysisHtml}
|
|
4312
|
+
\${suggestionsHtml}
|
|
4313
|
+
</div>
|
|
4314
|
+
\`;
|
|
3156
4315
|
})
|
|
3157
4316
|
.catch(err => {
|
|
3158
4317
|
console.error('AI Fix Error:', err);
|
|
3159
|
-
|
|
4318
|
+
button.disabled = false;
|
|
4319
|
+
button.querySelector('.ai-text').textContent = 'AI Fix';
|
|
4320
|
+
aiContent.innerHTML = \`<div class="test-error-summary"><strong>Error:</strong> Failed to get AI analysis. Please check the console for details. <br><br> \${err.message}</div>\`;
|
|
3160
4321
|
});
|
|
3161
4322
|
|
|
3162
4323
|
} catch (e) {
|
|
3163
4324
|
console.error('Error processing test data for AI Fix:', e);
|
|
3164
|
-
|
|
3165
|
-
|
|
4325
|
+
button.disabled = false;
|
|
4326
|
+
button.querySelector('.ai-text').textContent = 'AI Fix';
|
|
4327
|
+
aiContent.innerHTML = \`<div class="test-error-summary">Could not process test data. Is it formatted correctly?</div>\`;
|
|
3166
4328
|
}
|
|
3167
4329
|
}
|
|
3168
4330
|
|
|
@@ -3245,6 +4407,7 @@ Code Snippet:
|
|
|
3245
4407
|
function closeAiModal() {
|
|
3246
4408
|
const modal = document.getElementById('ai-fix-modal');
|
|
3247
4409
|
if(modal) modal.style.display = 'none';
|
|
4410
|
+
document.body.style.setProperty('overflow', '', 'important');
|
|
3248
4411
|
}
|
|
3249
4412
|
|
|
3250
4413
|
function toggleErrorDetails(button) {
|
|
@@ -3352,16 +4515,7 @@ Code Snippet:
|
|
|
3352
4515
|
document.querySelectorAll('#test-runs .step-header').forEach(header => {
|
|
3353
4516
|
header.addEventListener('click', () => toggleElementDetails(header, '.step-details'));
|
|
3354
4517
|
});
|
|
3355
|
-
|
|
3356
|
-
const collapseAllBtn = document.getElementById('collapse-all-tests');
|
|
3357
|
-
function setAllTestRunDetailsVisibility(displayMode, ariaState) {
|
|
3358
|
-
document.querySelectorAll('#test-runs .test-case-content').forEach(el => el.style.display = displayMode);
|
|
3359
|
-
document.querySelectorAll('#test-runs .step-details').forEach(el => el.style.display = displayMode);
|
|
3360
|
-
document.querySelectorAll('#test-runs .test-case-header[aria-expanded]').forEach(el => el.setAttribute('aria-expanded', ariaState));
|
|
3361
|
-
document.querySelectorAll('#test-runs .step-header[aria-expanded]').forEach(el => el.setAttribute('aria-expanded', ariaState));
|
|
3362
|
-
}
|
|
3363
|
-
if (expandAllBtn) expandAllBtn.addEventListener('click', () => setAllTestRunDetailsVisibility('block', 'true'));
|
|
3364
|
-
if (collapseAllBtn) collapseAllBtn.addEventListener('click', () => setAllTestRunDetailsVisibility('none', 'false'));
|
|
4518
|
+
|
|
3365
4519
|
// --- Annotation Link Handler ---
|
|
3366
4520
|
document.querySelectorAll('a.annotation-link').forEach(link => {
|
|
3367
4521
|
link.addEventListener('click', (e) => {
|
|
@@ -3530,7 +4684,7 @@ async function main() {
|
|
|
3530
4684
|
// Script to archive current run to JSON history (this is your modified "generate-trend.mjs")
|
|
3531
4685
|
const archiveRunScriptPath = path.resolve(
|
|
3532
4686
|
__dirname,
|
|
3533
|
-
"generate-trend.mjs" // Keeping the filename as per your request
|
|
4687
|
+
"generate-trend.mjs", // Keeping the filename as per your request
|
|
3534
4688
|
);
|
|
3535
4689
|
|
|
3536
4690
|
const outputDir = await getOutputDir(customOutputDir);
|
|
@@ -3547,7 +4701,7 @@ async function main() {
|
|
|
3547
4701
|
console.log(chalk.gray(` (from CLI argument)`));
|
|
3548
4702
|
} else {
|
|
3549
4703
|
console.log(
|
|
3550
|
-
chalk.gray(` (auto-detected from playwright.config or using default)`)
|
|
4704
|
+
chalk.gray(` (auto-detected from playwright.config or using default)`),
|
|
3551
4705
|
);
|
|
3552
4706
|
}
|
|
3553
4707
|
|
|
@@ -3556,14 +4710,14 @@ async function main() {
|
|
|
3556
4710
|
const archiveArgs = customOutputDir ? ["--outputDir", customOutputDir] : [];
|
|
3557
4711
|
await runScript(archiveRunScriptPath, archiveArgs);
|
|
3558
4712
|
console.log(
|
|
3559
|
-
chalk.green("Current run data archiving to history completed.")
|
|
4713
|
+
chalk.green("Current run data archiving to history completed."),
|
|
3560
4714
|
);
|
|
3561
4715
|
} catch (error) {
|
|
3562
4716
|
console.error(
|
|
3563
4717
|
chalk.red(
|
|
3564
|
-
"Failed to archive current run data. Report might use stale or incomplete historical trends."
|
|
4718
|
+
"Failed to archive current run data. Report might use stale or incomplete historical trends.",
|
|
3565
4719
|
),
|
|
3566
|
-
error
|
|
4720
|
+
error,
|
|
3567
4721
|
);
|
|
3568
4722
|
}
|
|
3569
4723
|
|
|
@@ -3578,22 +4732,22 @@ async function main() {
|
|
|
3578
4732
|
!currentRunReportData.results
|
|
3579
4733
|
) {
|
|
3580
4734
|
throw new Error(
|
|
3581
|
-
"Invalid report JSON structure. 'results' field is missing or invalid."
|
|
4735
|
+
"Invalid report JSON structure. 'results' field is missing or invalid.",
|
|
3582
4736
|
);
|
|
3583
4737
|
}
|
|
3584
4738
|
if (!Array.isArray(currentRunReportData.results)) {
|
|
3585
4739
|
currentRunReportData.results = [];
|
|
3586
4740
|
console.warn(
|
|
3587
4741
|
chalk.yellow(
|
|
3588
|
-
"Warning: 'results' field in current run JSON was not an array. Treated as empty."
|
|
3589
|
-
)
|
|
4742
|
+
"Warning: 'results' field in current run JSON was not an array. Treated as empty.",
|
|
4743
|
+
),
|
|
3590
4744
|
);
|
|
3591
4745
|
}
|
|
3592
4746
|
} catch (error) {
|
|
3593
4747
|
console.error(
|
|
3594
4748
|
chalk.red(
|
|
3595
|
-
`Critical Error: Could not read or parse main report JSON at ${reportJsonPath}: ${error.message}
|
|
3596
|
-
)
|
|
4749
|
+
`Critical Error: Could not read or parse main report JSON at ${reportJsonPath}: ${error.message}`,
|
|
4750
|
+
),
|
|
3597
4751
|
);
|
|
3598
4752
|
process.exit(1);
|
|
3599
4753
|
}
|
|
@@ -3606,7 +4760,8 @@ async function main() {
|
|
|
3606
4760
|
|
|
3607
4761
|
const jsonHistoryFiles = allHistoryFiles
|
|
3608
4762
|
.filter(
|
|
3609
|
-
(file) =>
|
|
4763
|
+
(file) =>
|
|
4764
|
+
file.startsWith(HISTORY_FILE_PREFIX) && file.endsWith(".json"),
|
|
3610
4765
|
)
|
|
3611
4766
|
.map((file) => {
|
|
3612
4767
|
const timestampPart = file
|
|
@@ -3623,7 +4778,7 @@ async function main() {
|
|
|
3623
4778
|
|
|
3624
4779
|
const filesToLoadForTrend = jsonHistoryFiles.slice(
|
|
3625
4780
|
0,
|
|
3626
|
-
MAX_HISTORY_FILES_TO_LOAD_FOR_REPORT
|
|
4781
|
+
MAX_HISTORY_FILES_TO_LOAD_FOR_REPORT,
|
|
3627
4782
|
);
|
|
3628
4783
|
|
|
3629
4784
|
for (const fileMeta of filesToLoadForTrend) {
|
|
@@ -3634,29 +4789,29 @@ async function main() {
|
|
|
3634
4789
|
} catch (fileReadError) {
|
|
3635
4790
|
console.warn(
|
|
3636
4791
|
chalk.yellow(
|
|
3637
|
-
`Could not read/parse history file ${fileMeta.name}: ${fileReadError.message}
|
|
3638
|
-
)
|
|
4792
|
+
`Could not read/parse history file ${fileMeta.name}: ${fileReadError.message}`,
|
|
4793
|
+
),
|
|
3639
4794
|
);
|
|
3640
4795
|
}
|
|
3641
4796
|
}
|
|
3642
4797
|
historicalRuns.reverse(); // Oldest first for charts
|
|
3643
4798
|
console.log(
|
|
3644
4799
|
chalk.green(
|
|
3645
|
-
`Loaded ${historicalRuns.length} historical run(s) for trend analysis
|
|
3646
|
-
)
|
|
4800
|
+
`Loaded ${historicalRuns.length} historical run(s) for trend analysis.`,
|
|
4801
|
+
),
|
|
3647
4802
|
);
|
|
3648
4803
|
} catch (error) {
|
|
3649
4804
|
if (error.code === "ENOENT") {
|
|
3650
4805
|
console.warn(
|
|
3651
4806
|
chalk.yellow(
|
|
3652
|
-
`History directory '${historyDir}' not found. No historical trends will be displayed
|
|
3653
|
-
)
|
|
4807
|
+
`History directory '${historyDir}' not found. No historical trends will be displayed.`,
|
|
4808
|
+
),
|
|
3654
4809
|
);
|
|
3655
4810
|
} else {
|
|
3656
4811
|
console.warn(
|
|
3657
4812
|
chalk.yellow(
|
|
3658
|
-
`Error loading historical data from '${historyDir}': ${error.message}
|
|
3659
|
-
)
|
|
4813
|
+
`Error loading historical data from '${historyDir}': ${error.message}`,
|
|
4814
|
+
),
|
|
3660
4815
|
);
|
|
3661
4816
|
}
|
|
3662
4817
|
}
|
|
@@ -3689,13 +4844,13 @@ async function main() {
|
|
|
3689
4844
|
duration: test.duration,
|
|
3690
4845
|
status: test.status,
|
|
3691
4846
|
timestamp: new Date(test.startTime),
|
|
3692
|
-
})
|
|
4847
|
+
}),
|
|
3693
4848
|
);
|
|
3694
4849
|
}
|
|
3695
4850
|
}
|
|
3696
4851
|
});
|
|
3697
4852
|
trendData.overall.sort(
|
|
3698
|
-
(a, b) => a.timestamp.getTime() - b.timestamp.getTime()
|
|
4853
|
+
(a, b) => a.timestamp.getTime() - b.timestamp.getTime(),
|
|
3699
4854
|
);
|
|
3700
4855
|
}
|
|
3701
4856
|
|
|
@@ -3705,8 +4860,8 @@ async function main() {
|
|
|
3705
4860
|
await fs.writeFile(reportHtmlPath, htmlContent, "utf-8");
|
|
3706
4861
|
console.log(
|
|
3707
4862
|
chalk.green.bold(
|
|
3708
|
-
`🎉 Pulse report generated successfully at: ${reportHtmlPath}
|
|
3709
|
-
)
|
|
4863
|
+
`🎉 Pulse report generated successfully at: ${reportHtmlPath}`,
|
|
4864
|
+
),
|
|
3710
4865
|
);
|
|
3711
4866
|
console.log(chalk.gray(`(You can open this file in your browser)`));
|
|
3712
4867
|
} catch (error) {
|
|
@@ -3717,7 +4872,7 @@ async function main() {
|
|
|
3717
4872
|
}
|
|
3718
4873
|
main().catch((err) => {
|
|
3719
4874
|
console.error(
|
|
3720
|
-
chalk.red.bold(`Unhandled error during script execution: ${err.message}`)
|
|
4875
|
+
chalk.red.bold(`Unhandled error during script execution: ${err.message}`),
|
|
3721
4876
|
);
|
|
3722
4877
|
console.error(err.stack);
|
|
3723
4878
|
process.exit(1);
|