@arghajit/dummy 0.1.0-beta-14 → 0.1.0-beta-15
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/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arghajit/dummy",
|
|
3
3
|
"author": "Arghajit Singha",
|
|
4
|
-
"version": "0.1.0-beta-
|
|
4
|
+
"version": "0.1.0-beta-15",
|
|
5
5
|
"description": "A Playwright reporter and dashboard for visualizing test results.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"playwright",
|
|
@@ -67,7 +67,6 @@
|
|
|
67
67
|
"@types/node": "^20",
|
|
68
68
|
"@types/ua-parser-js": "^0.7.39",
|
|
69
69
|
"eslint": "9.25.1",
|
|
70
|
-
"pretty-ansi": "^3.0.0",
|
|
71
70
|
"typescript": "^5"
|
|
72
71
|
},
|
|
73
72
|
"engines": {
|
|
@@ -177,9 +177,11 @@ function generateMinifiedHTML(reportData) {
|
|
|
177
177
|
};
|
|
178
178
|
|
|
179
179
|
const testsByBrowser = new Map();
|
|
180
|
+
const allBrowsers = new Set();
|
|
180
181
|
if (results && results.length > 0) {
|
|
181
182
|
results.forEach((test) => {
|
|
182
183
|
const browser = test.browser || "unknown";
|
|
184
|
+
allBrowsers.add(browser);
|
|
183
185
|
if (!testsByBrowser.has(browser)) {
|
|
184
186
|
testsByBrowser.set(browser, []);
|
|
185
187
|
}
|
|
@@ -195,7 +197,9 @@ function generateMinifiedHTML(reportData) {
|
|
|
195
197
|
let html = "";
|
|
196
198
|
testsByBrowser.forEach((tests, browser) => {
|
|
197
199
|
html += `
|
|
198
|
-
<div class="browser-section"
|
|
200
|
+
<div class="browser-section" data-browser-group="${sanitizeHTML(
|
|
201
|
+
browser.toLowerCase()
|
|
202
|
+
)}">
|
|
199
203
|
<h2 class="browser-title">${sanitizeHTML(capitalize(browser))}</h2>
|
|
200
204
|
<ul class="test-list">
|
|
201
205
|
`;
|
|
@@ -204,7 +208,12 @@ function generateMinifiedHTML(reportData) {
|
|
|
204
208
|
const testTitle =
|
|
205
209
|
testFileParts[testFileParts.length - 1] || "Unnamed Test";
|
|
206
210
|
html += `
|
|
207
|
-
<li class="test-item ${getStatusClass(test.status)}"
|
|
211
|
+
<li class="test-item ${getStatusClass(test.status)}"
|
|
212
|
+
data-test-name-min="${sanitizeHTML(testTitle.toLowerCase())}"
|
|
213
|
+
data-status-min="${sanitizeHTML(
|
|
214
|
+
String(test.status).toLowerCase()
|
|
215
|
+
)}"
|
|
216
|
+
data-browser-min="${sanitizeHTML(browser.toLowerCase())}">
|
|
208
217
|
<span class="test-status-icon">${getStatusIcon(
|
|
209
218
|
test.status
|
|
210
219
|
)}</span>
|
|
@@ -343,6 +352,43 @@ function generateMinifiedHTML(reportData) {
|
|
|
343
352
|
padding-bottom: 10px;
|
|
344
353
|
border-bottom: 2px solid var(--secondary-color);
|
|
345
354
|
}
|
|
355
|
+
|
|
356
|
+
/* Filters Section */
|
|
357
|
+
.filters-section {
|
|
358
|
+
display: flex;
|
|
359
|
+
flex-wrap: wrap;
|
|
360
|
+
gap: 15px;
|
|
361
|
+
margin-bottom: 20px;
|
|
362
|
+
padding: 15px;
|
|
363
|
+
background-color: var(--light-gray-color);
|
|
364
|
+
border-radius: var(--border-radius);
|
|
365
|
+
border: 1px solid var(--border-color);
|
|
366
|
+
}
|
|
367
|
+
.filters-section input[type="text"],
|
|
368
|
+
.filters-section select {
|
|
369
|
+
padding: 8px 12px;
|
|
370
|
+
border: 1px solid var(--medium-gray-color);
|
|
371
|
+
border-radius: 4px;
|
|
372
|
+
font-size: 0.95em;
|
|
373
|
+
flex-grow: 1;
|
|
374
|
+
}
|
|
375
|
+
.filters-section select {
|
|
376
|
+
min-width: 150px;
|
|
377
|
+
}
|
|
378
|
+
.filters-section button {
|
|
379
|
+
padding: 8px 15px;
|
|
380
|
+
font-size: 0.95em;
|
|
381
|
+
background-color: var(--secondary-color);
|
|
382
|
+
color: white;
|
|
383
|
+
border: none;
|
|
384
|
+
border-radius: 4px;
|
|
385
|
+
cursor: pointer;
|
|
386
|
+
transition: background-color 0.2s ease;
|
|
387
|
+
}
|
|
388
|
+
.filters-section button:hover {
|
|
389
|
+
background-color: var(--primary-color);
|
|
390
|
+
}
|
|
391
|
+
|
|
346
392
|
.browser-section {
|
|
347
393
|
margin-bottom: 25px;
|
|
348
394
|
}
|
|
@@ -365,7 +411,7 @@ function generateMinifiedHTML(reportData) {
|
|
|
365
411
|
border: 1px solid var(--border-color);
|
|
366
412
|
border-radius: var(--border-radius);
|
|
367
413
|
background-color: #fff;
|
|
368
|
-
transition: background-color 0.2s ease;
|
|
414
|
+
transition: background-color 0.2s ease, display 0.3s ease-out;
|
|
369
415
|
}
|
|
370
416
|
.test-item:hover {
|
|
371
417
|
background-color: var(--light-gray-color);
|
|
@@ -424,10 +470,11 @@ function generateMinifiedHTML(reportData) {
|
|
|
424
470
|
.report-header { flex-direction: column; align-items: flex-start; gap: 10px; }
|
|
425
471
|
.report-header h1 { font-size: 1.5em; }
|
|
426
472
|
.run-info { text-align: left; }
|
|
427
|
-
.summary-stats { grid-template-columns: 1fr 1fr; }
|
|
473
|
+
.summary-stats { grid-template-columns: 1fr 1fr; }
|
|
474
|
+
.filters-section { flex-direction: column; }
|
|
428
475
|
}
|
|
429
476
|
@media (max-width: 480px) {
|
|
430
|
-
.summary-stats { grid-template-columns: 1fr; }
|
|
477
|
+
.summary-stats { grid-template-columns: 1fr; }
|
|
431
478
|
}
|
|
432
479
|
</style>
|
|
433
480
|
</head>
|
|
@@ -471,6 +518,30 @@ function generateMinifiedHTML(reportData) {
|
|
|
471
518
|
|
|
472
519
|
<section class="test-results-section">
|
|
473
520
|
<h1 class="section-title">Test Case Summary</h1>
|
|
521
|
+
|
|
522
|
+
<div class="filters-section">
|
|
523
|
+
<input type="text" id="filter-min-name" placeholder="Search by test name...">
|
|
524
|
+
<select id="filter-min-status">
|
|
525
|
+
<option value="">All Statuses</option>
|
|
526
|
+
<option value="passed">Passed</option>
|
|
527
|
+
<option value="failed">Failed</option>
|
|
528
|
+
<option value="skipped">Skipped</option>
|
|
529
|
+
<option value="unknown">Unknown</option>
|
|
530
|
+
</select>
|
|
531
|
+
<select id="filter-min-browser">
|
|
532
|
+
<option value="">All Browsers</option>
|
|
533
|
+
${Array.from(allBrowsers)
|
|
534
|
+
.map(
|
|
535
|
+
(browser) =>
|
|
536
|
+
`<option value="${sanitizeHTML(
|
|
537
|
+
browser.toLowerCase()
|
|
538
|
+
)}">${sanitizeHTML(capitalize(browser))}</option>`
|
|
539
|
+
)
|
|
540
|
+
.join("")}
|
|
541
|
+
</select>
|
|
542
|
+
<button id="clear-min-filters">Clear Filters</button>
|
|
543
|
+
</div>
|
|
544
|
+
|
|
474
545
|
${generateTestListHTML()}
|
|
475
546
|
</section>
|
|
476
547
|
|
|
@@ -485,16 +556,83 @@ function generateMinifiedHTML(reportData) {
|
|
|
485
556
|
</footer>
|
|
486
557
|
</div>
|
|
487
558
|
<script>
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
559
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
560
|
+
const nameFilterMin = document.getElementById('filter-min-name');
|
|
561
|
+
const statusFilterMin = document.getElementById('filter-min-status');
|
|
562
|
+
const browserFilterMin = document.getElementById('filter-min-browser');
|
|
563
|
+
const clearMinFiltersBtn = document.getElementById('clear-min-filters');
|
|
564
|
+
const testItemsMin = document.querySelectorAll('.test-results-section .test-item');
|
|
565
|
+
const browserSections = document.querySelectorAll('.test-results-section .browser-section');
|
|
566
|
+
|
|
567
|
+
function filterMinifiedTests() {
|
|
568
|
+
const nameValue = nameFilterMin.value.toLowerCase();
|
|
569
|
+
const statusValue = statusFilterMin.value;
|
|
570
|
+
const browserValue = browserFilterMin.value;
|
|
571
|
+
let anyBrowserSectionVisible = false;
|
|
572
|
+
|
|
573
|
+
browserSections.forEach(section => {
|
|
574
|
+
let sectionHasVisibleTests = false;
|
|
575
|
+
const testsInThisSection = section.querySelectorAll('.test-item');
|
|
576
|
+
|
|
577
|
+
testsInThisSection.forEach(testItem => {
|
|
578
|
+
const testName = testItem.getAttribute('data-test-name-min');
|
|
579
|
+
const testStatus = testItem.getAttribute('data-status-min');
|
|
580
|
+
const testBrowser = testItem.getAttribute('data-browser-min');
|
|
581
|
+
|
|
582
|
+
const nameMatch = testName.includes(nameValue);
|
|
583
|
+
const statusMatch = !statusValue || testStatus === statusValue;
|
|
584
|
+
const browserMatch = !browserValue || testBrowser === browserValue;
|
|
585
|
+
|
|
586
|
+
if (nameMatch && statusMatch && browserMatch) {
|
|
587
|
+
testItem.style.display = 'flex';
|
|
588
|
+
sectionHasVisibleTests = true;
|
|
589
|
+
anyBrowserSectionVisible = true;
|
|
590
|
+
} else {
|
|
591
|
+
testItem.style.display = 'none';
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
// Hide browser section if no tests match OR if a specific browser is selected and it's not this one
|
|
595
|
+
if (!sectionHasVisibleTests || (browserValue && section.getAttribute('data-browser-group') !== browserValue)) {
|
|
596
|
+
section.style.display = 'none';
|
|
597
|
+
} else {
|
|
598
|
+
section.style.display = '';
|
|
599
|
+
}
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
// Show "no tests" message if all sections are hidden
|
|
603
|
+
const noTestsMessage = document.querySelector('.test-results-section .no-tests');
|
|
604
|
+
if (noTestsMessage) {
|
|
605
|
+
noTestsMessage.style.display = anyBrowserSectionVisible ? 'none' : 'block';
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (nameFilterMin) nameFilterMin.addEventListener('input', filterMinifiedTests);
|
|
611
|
+
if (statusFilterMin) statusFilterMin.addEventListener('change', filterMinifiedTests);
|
|
612
|
+
if (browserFilterMin) browserFilterMin.addEventListener('change', filterMinifiedTests);
|
|
613
|
+
|
|
614
|
+
if (clearMinFiltersBtn) {
|
|
615
|
+
clearMinFiltersBtn.addEventListener('click', () => {
|
|
616
|
+
nameFilterMin.value = '';
|
|
617
|
+
statusFilterMin.value = '';
|
|
618
|
+
browserFilterMin.value = '';
|
|
619
|
+
filterMinifiedTests();
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
// Initial filter call in case of pre-filled values (though unlikely here)
|
|
623
|
+
if (testItemsMin.length > 0) { // Only filter if there are items
|
|
624
|
+
filterMinifiedTests();
|
|
625
|
+
}
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
// Fallback helper functions (though ideally not needed client-side for this minified report)
|
|
491
629
|
if (typeof formatDuration === 'undefined') {
|
|
492
|
-
function formatDuration(ms) {
|
|
630
|
+
function formatDuration(ms) {
|
|
493
631
|
if (ms === undefined || ms === null || ms < 0) return "0.0s";
|
|
494
632
|
return (ms / 1000).toFixed(1) + "s";
|
|
495
633
|
}
|
|
496
634
|
}
|
|
497
|
-
if (typeof formatDate === 'undefined') {
|
|
635
|
+
if (typeof formatDate === 'undefined') {
|
|
498
636
|
function formatDate(dateStrOrDate) {
|
|
499
637
|
if (!dateStrOrDate) return "N/A";
|
|
500
638
|
try {
|
|
@@ -5,7 +5,6 @@ import { readFileSync, existsSync as fsExistsSync } from "fs"; // ADD THIS LINE
|
|
|
5
5
|
import path from "path";
|
|
6
6
|
import { fork } from "child_process"; // Add this
|
|
7
7
|
import { fileURLToPath } from "url"; // Add this for resolving path in ESM
|
|
8
|
-
import prettyAnsi from "pretty-ansi";
|
|
9
8
|
|
|
10
9
|
// Use dynamic import for chalk as it's ESM only
|
|
11
10
|
let chalk;
|
|
@@ -27,6 +26,129 @@ const DEFAULT_OUTPUT_DIR = "pulse-report";
|
|
|
27
26
|
const DEFAULT_JSON_FILE = "playwright-pulse-report.json";
|
|
28
27
|
const DEFAULT_HTML_FILE = "playwright-pulse-static-report.html";
|
|
29
28
|
// Helper functions
|
|
29
|
+
export function ansiToHtml(text) {
|
|
30
|
+
if (!text) {
|
|
31
|
+
return "";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const codes = {
|
|
35
|
+
0: "color:inherit;font-weight:normal;font-style:normal;text-decoration:none;opacity:1;background-color:inherit;",
|
|
36
|
+
1: "font-weight:bold",
|
|
37
|
+
2: "opacity:0.6",
|
|
38
|
+
3: "font-style:italic",
|
|
39
|
+
4: "text-decoration:underline",
|
|
40
|
+
30: "color:#000", // black
|
|
41
|
+
31: "color:#d00", // red
|
|
42
|
+
32: "color:#0a0", // green
|
|
43
|
+
33: "color:#aa0", // yellow
|
|
44
|
+
34: "color:#00d", // blue
|
|
45
|
+
35: "color:#a0a", // magenta
|
|
46
|
+
36: "color:#0aa", // cyan
|
|
47
|
+
37: "color:#aaa", // light grey
|
|
48
|
+
39: "color:inherit", // default foreground color
|
|
49
|
+
40: "background-color:#000", // black background
|
|
50
|
+
41: "background-color:#d00", // red background
|
|
51
|
+
42: "background-color:#0a0", // green background
|
|
52
|
+
43: "background-color:#aa0", // yellow background
|
|
53
|
+
44: "background-color:#00d", // blue background
|
|
54
|
+
45: "background-color:#a0a", // magenta background
|
|
55
|
+
46: "background-color:#0aa", // cyan background
|
|
56
|
+
47: "background-color:#aaa", // light grey background
|
|
57
|
+
49: "background-color:inherit", // default background color
|
|
58
|
+
90: "color:#555", // dark grey
|
|
59
|
+
91: "color:#f55", // light red
|
|
60
|
+
92: "color:#5f5", // light green
|
|
61
|
+
93: "color:#ff5", // light yellow
|
|
62
|
+
94: "color:#55f", // light blue
|
|
63
|
+
95: "color:#f5f", // light magenta
|
|
64
|
+
96: "color:#5ff", // light cyan
|
|
65
|
+
97: "color:#fff", // white
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
let currentStylesArray = [];
|
|
69
|
+
let html = "";
|
|
70
|
+
let openSpan = false;
|
|
71
|
+
|
|
72
|
+
const applyStyles = () => {
|
|
73
|
+
if (openSpan) {
|
|
74
|
+
html += "</span>";
|
|
75
|
+
openSpan = false;
|
|
76
|
+
}
|
|
77
|
+
if (currentStylesArray.length > 0) {
|
|
78
|
+
const styleString = currentStylesArray.filter((s) => s).join(";");
|
|
79
|
+
if (styleString) {
|
|
80
|
+
html += `<span style="${styleString}">`;
|
|
81
|
+
openSpan = true;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const resetAndApplyNewCodes = (newCodesStr) => {
|
|
87
|
+
const newCodes = newCodesStr.split(";");
|
|
88
|
+
|
|
89
|
+
if (newCodes.includes("0")) {
|
|
90
|
+
currentStylesArray = [];
|
|
91
|
+
if (codes["0"]) currentStylesArray.push(codes["0"]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const code of newCodes) {
|
|
95
|
+
if (code === "0") continue;
|
|
96
|
+
|
|
97
|
+
if (codes[code]) {
|
|
98
|
+
if (code === "39") {
|
|
99
|
+
currentStylesArray = currentStylesArray.filter(
|
|
100
|
+
(s) => !s.startsWith("color:")
|
|
101
|
+
);
|
|
102
|
+
currentStylesArray.push("color:inherit");
|
|
103
|
+
} else if (code === "49") {
|
|
104
|
+
currentStylesArray = currentStylesArray.filter(
|
|
105
|
+
(s) => !s.startsWith("background-color:")
|
|
106
|
+
);
|
|
107
|
+
currentStylesArray.push("background-color:inherit");
|
|
108
|
+
} else {
|
|
109
|
+
currentStylesArray.push(codes[code]);
|
|
110
|
+
}
|
|
111
|
+
} else if (code.startsWith("38;2;") || code.startsWith("48;2;")) {
|
|
112
|
+
const parts = code.split(";");
|
|
113
|
+
const type = parts[0] === "38" ? "color" : "background-color";
|
|
114
|
+
if (parts.length === 5) {
|
|
115
|
+
currentStylesArray = currentStylesArray.filter(
|
|
116
|
+
(s) => !s.startsWith(type + ":")
|
|
117
|
+
);
|
|
118
|
+
currentStylesArray.push(
|
|
119
|
+
`${type}:rgb(${parts[2]},${parts[3]},${parts[4]})`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
applyStyles();
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const segments = text.split(/(\x1b\[[0-9;]*m)/g);
|
|
128
|
+
|
|
129
|
+
for (const segment of segments) {
|
|
130
|
+
if (!segment) continue;
|
|
131
|
+
|
|
132
|
+
if (segment.startsWith("\x1b[") && segment.endsWith("m")) {
|
|
133
|
+
const command = segment.slice(2, -1);
|
|
134
|
+
resetAndApplyNewCodes(command);
|
|
135
|
+
} else {
|
|
136
|
+
const escapedContent = segment
|
|
137
|
+
.replace(/&/g, "&")
|
|
138
|
+
.replace(/</g, "<")
|
|
139
|
+
.replace(/>/g, ">")
|
|
140
|
+
.replace(/"/g, """)
|
|
141
|
+
.replace(/'/g, "'");
|
|
142
|
+
html += escapedContent;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (openSpan) {
|
|
147
|
+
html += "</span>";
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return html;
|
|
151
|
+
}
|
|
30
152
|
function sanitizeHTML(str) {
|
|
31
153
|
if (str === null || str === undefined) return "";
|
|
32
154
|
return String(str).replace(/[&<>"']/g, (match) => {
|
|
@@ -45,7 +167,7 @@ function capitalize(str) {
|
|
|
45
167
|
return str[0].toUpperCase() + str.slice(1).toLowerCase();
|
|
46
168
|
}
|
|
47
169
|
function formatPlaywrightError(error) {
|
|
48
|
-
const commandOutput =
|
|
170
|
+
const commandOutput = ansiToHtml(error || error.message);
|
|
49
171
|
return convertPlaywrightErrorToHTML(commandOutput);
|
|
50
172
|
}
|
|
51
173
|
function convertPlaywrightErrorToHTML(str) {
|
|
@@ -585,6 +707,335 @@ function generatePieChart(data, chartWidth = 300, chartHeight = 300) {
|
|
|
585
707
|
</div>
|
|
586
708
|
`;
|
|
587
709
|
}
|
|
710
|
+
function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
711
|
+
// Format memory for display
|
|
712
|
+
const formattedMemory = environment.memory.replace(/(\d+\.\d{2})GB/, "$1 GB");
|
|
713
|
+
|
|
714
|
+
// Generate a unique ID for the dashboard
|
|
715
|
+
const dashboardId = `envDashboard-${Date.now()}-${Math.random()
|
|
716
|
+
.toString(36)
|
|
717
|
+
.substring(2, 7)}`;
|
|
718
|
+
|
|
719
|
+
const cardHeight = Math.floor(dashboardHeight * 0.44);
|
|
720
|
+
const cardContentPadding = 16; // px
|
|
721
|
+
|
|
722
|
+
return `
|
|
723
|
+
<div class="environment-dashboard-wrapper" id="${dashboardId}">
|
|
724
|
+
<style>
|
|
725
|
+
.environment-dashboard-wrapper *,
|
|
726
|
+
.environment-dashboard-wrapper *::before,
|
|
727
|
+
.environment-dashboard-wrapper *::after {
|
|
728
|
+
box-sizing: border-box;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
.environment-dashboard-wrapper {
|
|
732
|
+
--primary-color: #007bff;
|
|
733
|
+
--primary-light-color: #e6f2ff;
|
|
734
|
+
--secondary-color: #6c757d;
|
|
735
|
+
--success-color: #28a745;
|
|
736
|
+
--success-light-color: #eaf6ec;
|
|
737
|
+
--warning-color: #ffc107;
|
|
738
|
+
--warning-light-color: #fff9e6;
|
|
739
|
+
--danger-color: #dc3545;
|
|
740
|
+
|
|
741
|
+
--background-color: #ffffff;
|
|
742
|
+
--card-background-color: #ffffff;
|
|
743
|
+
--text-color: #212529;
|
|
744
|
+
--text-color-secondary: #6c757d;
|
|
745
|
+
--border-color: #dee2e6;
|
|
746
|
+
--border-light-color: #f1f3f5;
|
|
747
|
+
--icon-color: #495057;
|
|
748
|
+
--chip-background: #e9ecef;
|
|
749
|
+
--chip-text: #495057;
|
|
750
|
+
--shadow-color: rgba(0, 0, 0, 0.075);
|
|
751
|
+
|
|
752
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji';
|
|
753
|
+
background-color: var(--background-color);
|
|
754
|
+
border-radius: 12px;
|
|
755
|
+
box-shadow: 0 6px 12px var(--shadow-color);
|
|
756
|
+
padding: 24px;
|
|
757
|
+
color: var(--text-color);
|
|
758
|
+
display: grid;
|
|
759
|
+
grid-template-columns: 1fr 1fr;
|
|
760
|
+
grid-template-rows: auto 1fr;
|
|
761
|
+
gap: 20px;
|
|
762
|
+
font-size: 14px;
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
.env-dashboard-header {
|
|
766
|
+
grid-column: 1 / -1;
|
|
767
|
+
display: flex;
|
|
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
|
+
}
|
|
774
|
+
|
|
775
|
+
.env-dashboard-title {
|
|
776
|
+
font-size: 1.5rem;
|
|
777
|
+
font-weight: 600;
|
|
778
|
+
color: var(--text-color);
|
|
779
|
+
margin: 0;
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
.env-dashboard-subtitle {
|
|
783
|
+
font-size: 0.875rem;
|
|
784
|
+
color: var(--text-color-secondary);
|
|
785
|
+
margin-top: 4px;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
.env-card {
|
|
789
|
+
background-color: var(--card-background-color);
|
|
790
|
+
border-radius: 8px;
|
|
791
|
+
padding: ${cardContentPadding}px;
|
|
792
|
+
box-shadow: 0 3px 6px var(--shadow-color);
|
|
793
|
+
height: ${cardHeight}px;
|
|
794
|
+
display: flex;
|
|
795
|
+
flex-direction: column;
|
|
796
|
+
overflow: hidden;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
.env-card-header {
|
|
800
|
+
font-weight: 600;
|
|
801
|
+
font-size: 1rem;
|
|
802
|
+
margin-bottom: 12px;
|
|
803
|
+
color: var(--text-color);
|
|
804
|
+
display: flex;
|
|
805
|
+
align-items: center;
|
|
806
|
+
padding-bottom: 8px;
|
|
807
|
+
border-bottom: 1px solid var(--border-light-color);
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
.env-card-header svg {
|
|
811
|
+
margin-right: 10px;
|
|
812
|
+
width: 18px;
|
|
813
|
+
height: 18px;
|
|
814
|
+
fill: var(--icon-color);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
.env-card-content {
|
|
818
|
+
flex-grow: 1;
|
|
819
|
+
overflow-y: auto;
|
|
820
|
+
padding-right: 5px;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.env-detail-row {
|
|
824
|
+
display: flex;
|
|
825
|
+
justify-content: space-between;
|
|
826
|
+
align-items: center;
|
|
827
|
+
padding: 10px 0;
|
|
828
|
+
border-bottom: 1px solid var(--border-light-color);
|
|
829
|
+
font-size: 0.875rem;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
.env-detail-row:last-child {
|
|
833
|
+
border-bottom: none;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
.env-detail-label {
|
|
837
|
+
color: var(--text-color-secondary);
|
|
838
|
+
font-weight: 500;
|
|
839
|
+
margin-right: 10px;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
.env-detail-value {
|
|
843
|
+
color: var(--text-color);
|
|
844
|
+
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
845
|
+
text-align: right;
|
|
846
|
+
word-break: break-all;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
.env-chip {
|
|
850
|
+
display: inline-block;
|
|
851
|
+
padding: 4px 10px;
|
|
852
|
+
border-radius: 16px;
|
|
853
|
+
font-size: 0.75rem;
|
|
854
|
+
font-weight: 500;
|
|
855
|
+
line-height: 1.2;
|
|
856
|
+
background-color: var(--chip-background);
|
|
857
|
+
color: var(--chip-text);
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
.env-chip-primary {
|
|
861
|
+
background-color: var(--primary-light-color);
|
|
862
|
+
color: var(--primary-color);
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
.env-chip-success {
|
|
866
|
+
background-color: var(--success-light-color);
|
|
867
|
+
color: var(--success-color);
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
.env-chip-warning {
|
|
871
|
+
background-color: var(--warning-light-color);
|
|
872
|
+
color: var(--warning-color);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.env-cpu-cores {
|
|
876
|
+
display: flex;
|
|
877
|
+
align-items: center;
|
|
878
|
+
gap: 6px;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
.env-core-indicator {
|
|
882
|
+
width: 12px;
|
|
883
|
+
height: 12px;
|
|
884
|
+
border-radius: 50%;
|
|
885
|
+
background-color: var(--success-color);
|
|
886
|
+
border: 1px solid rgba(0,0,0,0.1);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
.env-core-indicator.inactive {
|
|
890
|
+
background-color: var(--border-light-color);
|
|
891
|
+
opacity: 0.7;
|
|
892
|
+
border-color: var(--border-color);
|
|
893
|
+
}
|
|
894
|
+
</style>
|
|
895
|
+
|
|
896
|
+
<div class="env-dashboard-header">
|
|
897
|
+
<div>
|
|
898
|
+
<h3 class="env-dashboard-title">System Environment</h3>
|
|
899
|
+
<p class="env-dashboard-subtitle">Snapshot of the execution environment</p>
|
|
900
|
+
</div>
|
|
901
|
+
<span class="env-chip env-chip-primary">${environment.host}</span>
|
|
902
|
+
</div>
|
|
903
|
+
|
|
904
|
+
<div class="env-card">
|
|
905
|
+
<div class="env-card-header">
|
|
906
|
+
<svg viewBox="0 0 24 24"><path d="M4 6h16V4H4a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8h-2v10H4V6zm18-2h-4a2 2 0 0 0-2-2h-4a2 2 0 0 0-2 2H6a2 2 0 0 0-2 2v2h20V6a2 2 0 0 0-2-2zM8 12h8v2H8v-2zm0 4h8v2H8v-2z"/></svg>
|
|
907
|
+
Hardware
|
|
908
|
+
</div>
|
|
909
|
+
<div class="env-card-content">
|
|
910
|
+
<div class="env-detail-row">
|
|
911
|
+
<span class="env-detail-label">CPU Model</span>
|
|
912
|
+
<span class="env-detail-value">${environment.cpu.model}</span>
|
|
913
|
+
</div>
|
|
914
|
+
<div class="env-detail-row">
|
|
915
|
+
<span class="env-detail-label">CPU Cores</span>
|
|
916
|
+
<span class="env-detail-value">
|
|
917
|
+
<div class="env-cpu-cores">
|
|
918
|
+
${Array.from(
|
|
919
|
+
{ length: Math.max(0, environment.cpu.cores || 0) },
|
|
920
|
+
(_, i) =>
|
|
921
|
+
`<div class="env-core-indicator ${
|
|
922
|
+
i >=
|
|
923
|
+
(environment.cpu.cores >= 8 ? 8 : environment.cpu.cores)
|
|
924
|
+
? "inactive"
|
|
925
|
+
: ""
|
|
926
|
+
}" title="Core ${i + 1}"></div>`
|
|
927
|
+
).join("")}
|
|
928
|
+
<span>${environment.cpu.cores || "N/A"} cores</span>
|
|
929
|
+
</div>
|
|
930
|
+
</span>
|
|
931
|
+
</div>
|
|
932
|
+
<div class="env-detail-row">
|
|
933
|
+
<span class="env-detail-label">Memory</span>
|
|
934
|
+
<span class="env-detail-value">${formattedMemory}</span>
|
|
935
|
+
</div>
|
|
936
|
+
</div>
|
|
937
|
+
</div>
|
|
938
|
+
|
|
939
|
+
<div class="env-card">
|
|
940
|
+
<div class="env-card-header">
|
|
941
|
+
<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-0.01 18c-2.76 0-5.26-1.12-7.07-2.93A7.973 7.973 0 0 1 4 12c0-2.21.9-4.21 2.36-5.64A7.994 7.994 0 0 1 11.99 4c4.41 0 8 3.59 8 8 0 2.76-1.12 5.26-2.93 7.07A7.973 7.973 0 0 1 11.99 20zM12 8c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/></svg>
|
|
942
|
+
Operating System
|
|
943
|
+
</div>
|
|
944
|
+
<div class="env-card-content">
|
|
945
|
+
<div class="env-detail-row">
|
|
946
|
+
<span class="env-detail-label">OS Type</span>
|
|
947
|
+
<span class="env-detail-value">${
|
|
948
|
+
environment.os.split(" ")[0] === "darwin"
|
|
949
|
+
? "macOS"
|
|
950
|
+
: environment.os.split(" ")[0] || "Unknown"
|
|
951
|
+
}</span>
|
|
952
|
+
</div>
|
|
953
|
+
<div class="env-detail-row">
|
|
954
|
+
<span class="env-detail-label">OS Version</span>
|
|
955
|
+
<span class="env-detail-value">${
|
|
956
|
+
environment.os.split(" ")[1] || "N/A"
|
|
957
|
+
}</span>
|
|
958
|
+
</div>
|
|
959
|
+
<div class="env-detail-row">
|
|
960
|
+
<span class="env-detail-label">Hostname</span>
|
|
961
|
+
<span class="env-detail-value" title="${environment.host}">${
|
|
962
|
+
environment.host
|
|
963
|
+
}</span>
|
|
964
|
+
</div>
|
|
965
|
+
</div>
|
|
966
|
+
</div>
|
|
967
|
+
|
|
968
|
+
<div class="env-card">
|
|
969
|
+
<div class="env-card-header">
|
|
970
|
+
<svg viewBox="0 0 24 24"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>
|
|
971
|
+
Node.js Runtime
|
|
972
|
+
</div>
|
|
973
|
+
<div class="env-card-content">
|
|
974
|
+
<div class="env-detail-row">
|
|
975
|
+
<span class="env-detail-label">Node Version</span>
|
|
976
|
+
<span class="env-detail-value">${environment.node}</span>
|
|
977
|
+
</div>
|
|
978
|
+
<div class="env-detail-row">
|
|
979
|
+
<span class="env-detail-label">V8 Engine</span>
|
|
980
|
+
<span class="env-detail-value">${environment.v8}</span>
|
|
981
|
+
</div>
|
|
982
|
+
<div class="env-detail-row">
|
|
983
|
+
<span class="env-detail-label">Working Dir</span>
|
|
984
|
+
<span class="env-detail-value" title="${environment.cwd}">${
|
|
985
|
+
environment.cwd.length > 25
|
|
986
|
+
? "..." + environment.cwd.slice(-22)
|
|
987
|
+
: environment.cwd
|
|
988
|
+
}</span>
|
|
989
|
+
</div>
|
|
990
|
+
</div>
|
|
991
|
+
</div>
|
|
992
|
+
|
|
993
|
+
<div class="env-card">
|
|
994
|
+
<div class="env-card-header">
|
|
995
|
+
<svg viewBox="0 0 24 24"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4s1.79-4 4-4h.71C7.37 8.69 9.48 7 12 7c2.76 0 5 2.24 5 5v1h2c1.66 0 3 1.34 3 3s-1.34 3-3 3z"/></svg>
|
|
996
|
+
System Summary
|
|
997
|
+
</div>
|
|
998
|
+
<div class="env-card-content">
|
|
999
|
+
<div class="env-detail-row">
|
|
1000
|
+
<span class="env-detail-label">Platform Arch</span>
|
|
1001
|
+
<span class="env-detail-value">
|
|
1002
|
+
<span class="env-chip ${
|
|
1003
|
+
environment.os.includes("darwin") &&
|
|
1004
|
+
environment.cpu.model.toLowerCase().includes("apple")
|
|
1005
|
+
? "env-chip-success"
|
|
1006
|
+
: "env-chip-warning"
|
|
1007
|
+
}">
|
|
1008
|
+
${
|
|
1009
|
+
environment.os.includes("darwin") &&
|
|
1010
|
+
environment.cpu.model.toLowerCase().includes("apple")
|
|
1011
|
+
? "Apple Silicon"
|
|
1012
|
+
: environment.cpu.model.toLowerCase().includes("arm") ||
|
|
1013
|
+
environment.cpu.model.toLowerCase().includes("aarch64")
|
|
1014
|
+
? "ARM-based"
|
|
1015
|
+
: "x86/Other"
|
|
1016
|
+
}
|
|
1017
|
+
</span>
|
|
1018
|
+
</span>
|
|
1019
|
+
</div>
|
|
1020
|
+
<div class="env-detail-row">
|
|
1021
|
+
<span class="env-detail-label">Memory per Core</span>
|
|
1022
|
+
<span class="env-detail-value">${
|
|
1023
|
+
environment.cpu.cores > 0
|
|
1024
|
+
? (
|
|
1025
|
+
parseFloat(environment.memory) / environment.cpu.cores
|
|
1026
|
+
).toFixed(2) + " GB"
|
|
1027
|
+
: "N/A"
|
|
1028
|
+
}</span>
|
|
1029
|
+
</div>
|
|
1030
|
+
<div class="env-detail-row">
|
|
1031
|
+
<span class="env-detail-label">Run Context</span>
|
|
1032
|
+
<span class="env-detail-value">CI/Local Test</span>
|
|
1033
|
+
</div>
|
|
1034
|
+
</div>
|
|
1035
|
+
</div>
|
|
1036
|
+
</div>
|
|
1037
|
+
`;
|
|
1038
|
+
}
|
|
588
1039
|
function generateTestHistoryContent(trendData) {
|
|
589
1040
|
if (
|
|
590
1041
|
!trendData ||
|
|
@@ -1197,7 +1648,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1197
1648
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1198
1649
|
<link rel="icon" type="image/png" href="https://i.postimg.cc/XqVn1NhF/pulse.png">
|
|
1199
1650
|
<link rel="apple-touch-icon" href="https://i.postimg.cc/XqVn1NhF/pulse.png">
|
|
1200
|
-
<script src="https://code.highcharts.com/highcharts.js"></script>
|
|
1651
|
+
<script src="https://code.highcharts.com/highcharts.js" defer></script>
|
|
1201
1652
|
<title>Playwright Pulse Report</title>
|
|
1202
1653
|
<style>
|
|
1203
1654
|
:root {
|
|
@@ -1397,6 +1848,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1397
1848
|
)}</div></div>
|
|
1398
1849
|
</div>
|
|
1399
1850
|
<div class="dashboard-bottom-row">
|
|
1851
|
+
<div style="display: grid; gap: 20px">
|
|
1400
1852
|
${generatePieChart(
|
|
1401
1853
|
[
|
|
1402
1854
|
{ label: "Passed", value: runSummary.passed },
|
|
@@ -1406,6 +1858,13 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1406
1858
|
400,
|
|
1407
1859
|
390
|
|
1408
1860
|
)}
|
|
1861
|
+
${
|
|
1862
|
+
runSummary.environment &&
|
|
1863
|
+
Object.keys(runSummary.environment).length > 0
|
|
1864
|
+
? generateEnvironmentDashboard(runSummary.environment)
|
|
1865
|
+
: '<div class="no-data">Environment data not available.</div>'
|
|
1866
|
+
}
|
|
1867
|
+
</div>
|
|
1409
1868
|
${generateSuitesWidget(suitesData)}
|
|
1410
1869
|
</div>
|
|
1411
1870
|
</div>
|
|
@@ -39,6 +39,7 @@ function mergeReports(files) {
|
|
|
39
39
|
combinedRun.failed += run.failed || 0;
|
|
40
40
|
combinedRun.skipped += run.skipped || 0;
|
|
41
41
|
combinedRun.duration += run.duration || 0;
|
|
42
|
+
combinedRun.environment = run.environment;
|
|
42
43
|
|
|
43
44
|
if (json.results) {
|
|
44
45
|
combinedResults.push(...json.results);
|