@arghajit/dummy 0.3.9 → 0.3.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/pulse.d.ts +12 -0
- package/dist/pulse.js +24 -0
- package/dist/reporter/index.d.ts +2 -0
- package/dist/reporter/index.js +5 -1
- package/dist/reporter/playwright-pulse-reporter.d.ts +1 -0
- package/dist/reporter/playwright-pulse-reporter.js +5 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +1 -1
- package/scripts/generate-email-report.mjs +60 -10
- package/scripts/generate-report.mjs +242 -50
- package/scripts/generate-static-report.mjs +194 -12
package/dist/pulse.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export type PulseSeverityLevel = "Minor" | "Low" | "Medium" | "High" | "Critical";
|
|
2
|
+
export declare const pulse: {
|
|
3
|
+
/**
|
|
4
|
+
* Sets the severity level for the current test.
|
|
5
|
+
* * @param level - The severity level ('Minor' | 'Low' | 'Medium' | 'High' | 'Critical')
|
|
6
|
+
* @example
|
|
7
|
+
* test('Login', async () => {
|
|
8
|
+
* pulse.severity('Critical');
|
|
9
|
+
* });
|
|
10
|
+
*/
|
|
11
|
+
severity: (level: PulseSeverityLevel) => void;
|
|
12
|
+
};
|
package/dist/pulse.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.pulse = void 0;
|
|
4
|
+
const test_1 = require("@playwright/test");
|
|
5
|
+
exports.pulse = {
|
|
6
|
+
/**
|
|
7
|
+
* Sets the severity level for the current test.
|
|
8
|
+
* * @param level - The severity level ('Minor' | 'Low' | 'Medium' | 'High' | 'Critical')
|
|
9
|
+
* @example
|
|
10
|
+
* test('Login', async () => {
|
|
11
|
+
* pulse.severity('Critical');
|
|
12
|
+
* });
|
|
13
|
+
*/
|
|
14
|
+
severity: (level) => {
|
|
15
|
+
const validLevels = ["Minor", "Low", "Medium", "High", "Critical"];
|
|
16
|
+
// Default to "Medium" if an invalid string is passed
|
|
17
|
+
const selectedLevel = validLevels.includes(level) ? level : "Medium";
|
|
18
|
+
// Add the annotation to Playwright's test info
|
|
19
|
+
test_1.test.info().annotations.push({
|
|
20
|
+
type: "pulse_severity",
|
|
21
|
+
description: selectedLevel,
|
|
22
|
+
});
|
|
23
|
+
},
|
|
24
|
+
};
|
package/dist/reporter/index.d.ts
CHANGED
|
@@ -3,3 +3,5 @@ export default PlaywrightPulseReporter;
|
|
|
3
3
|
export { PlaywrightPulseReporter };
|
|
4
4
|
export type { PlaywrightPulseReport } from "../lib/report-types";
|
|
5
5
|
export type { TestResult, TestRun, TestStep, TestStatus } from "../types";
|
|
6
|
+
export { pulse } from "../pulse";
|
|
7
|
+
export type { PulseSeverityLevel } from "../pulse";
|
package/dist/reporter/index.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.PlaywrightPulseReporter = void 0;
|
|
3
|
+
exports.pulse = exports.PlaywrightPulseReporter = void 0;
|
|
4
4
|
// src/reporter/index.ts
|
|
5
5
|
const playwright_pulse_reporter_1 = require("./playwright-pulse-reporter");
|
|
6
6
|
Object.defineProperty(exports, "PlaywrightPulseReporter", { enumerable: true, get: function () { return playwright_pulse_reporter_1.PlaywrightPulseReporter; } });
|
|
7
7
|
// Export the reporter class as the default export for CommonJS compatibility
|
|
8
8
|
// and also as a named export for potential ES module consumers.
|
|
9
9
|
exports.default = playwright_pulse_reporter_1.PlaywrightPulseReporter;
|
|
10
|
+
// --- NEW: Export the pulse helper ---
|
|
11
|
+
// This allows: import { pulse } from '@arghajit/playwright-pulse-report';
|
|
12
|
+
var pulse_1 = require("../pulse"); // Adjust path based on where you placed pulse.ts
|
|
13
|
+
Object.defineProperty(exports, "pulse", { enumerable: true, get: function () { return pulse_1.pulse; } });
|
|
@@ -16,6 +16,7 @@ export declare class PlaywrightPulseReporter implements Reporter {
|
|
|
16
16
|
printsToStdio(): boolean;
|
|
17
17
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
18
18
|
onTestBegin(test: TestCase): void;
|
|
19
|
+
private _getSeverity;
|
|
19
20
|
private getBrowserDetails;
|
|
20
21
|
private processStep;
|
|
21
22
|
onTestEnd(test: TestCase, result: PwTestResult): Promise<void>;
|
|
@@ -109,6 +109,10 @@ class PlaywrightPulseReporter {
|
|
|
109
109
|
onTestBegin(test) {
|
|
110
110
|
console.log(`Starting test: ${test.title}`);
|
|
111
111
|
}
|
|
112
|
+
_getSeverity(annotations) {
|
|
113
|
+
const severityAnnotation = annotations.find((a) => a.type === "pulse_severity");
|
|
114
|
+
return (severityAnnotation === null || severityAnnotation === void 0 ? void 0 : severityAnnotation.description) || "Medium";
|
|
115
|
+
}
|
|
112
116
|
getBrowserDetails(test) {
|
|
113
117
|
var _a, _b, _c, _d;
|
|
114
118
|
const project = (_a = test.parent) === null || _a === void 0 ? void 0 : _a.project();
|
|
@@ -267,6 +271,7 @@ class PlaywrightPulseReporter {
|
|
|
267
271
|
snippet: (_l = result.error) === null || _l === void 0 ? void 0 : _l.snippet,
|
|
268
272
|
codeSnippet: codeSnippet,
|
|
269
273
|
tags: test.tags.map((tag) => tag.startsWith("@") ? tag.substring(1) : tag),
|
|
274
|
+
severity: this._getSeverity(test.annotations),
|
|
270
275
|
screenshots: [],
|
|
271
276
|
videoPath: [],
|
|
272
277
|
tracePath: undefined,
|
package/dist/types/index.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arghajit/dummy",
|
|
3
3
|
"author": "Arghajit Singha",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.11",
|
|
5
5
|
"description": "A Playwright reporter and dashboard for visualizing test results.",
|
|
6
6
|
"homepage": "https://playwright-pulse-report.netlify.app/",
|
|
7
7
|
"keywords": [
|
|
@@ -217,6 +217,40 @@ function generateMinifiedHTML(reportData) {
|
|
|
217
217
|
const testFileParts = test.name.split(" > ");
|
|
218
218
|
const testTitle =
|
|
219
219
|
testFileParts[testFileParts.length - 1] || "Unnamed Test";
|
|
220
|
+
|
|
221
|
+
// --- NEW: Severity Logic ---
|
|
222
|
+
const severity = test.severity || "Medium";
|
|
223
|
+
const getSeverityColor = (level) => {
|
|
224
|
+
switch (level) {
|
|
225
|
+
case "Minor":
|
|
226
|
+
return "#006064";
|
|
227
|
+
case "Low":
|
|
228
|
+
return "#FFA07A";
|
|
229
|
+
case "Medium":
|
|
230
|
+
return "#577A11";
|
|
231
|
+
case "High":
|
|
232
|
+
return "#B71C1C";
|
|
233
|
+
case "Critical":
|
|
234
|
+
return "#64158A";
|
|
235
|
+
default:
|
|
236
|
+
return "#577A11";
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
// We use inline styles here to ensure they render correctly in emails
|
|
240
|
+
const severityBadge = `<span style="background-color: ${getSeverityColor(
|
|
241
|
+
severity
|
|
242
|
+
)}; font-size: 0.8em; font-weight: 600; padding: 3px 8px; border-radius: 4px; color: #fff; margin-left: 10px; white-space: nowrap;">${severity}</span>`;
|
|
243
|
+
|
|
244
|
+
// --- NEW: Tags Logic ---
|
|
245
|
+
const tagsBadges = (test.tags || [])
|
|
246
|
+
.map(
|
|
247
|
+
(tag) =>
|
|
248
|
+
`<span style="background-color: #7f8c8d; font-size: 0.8em; font-weight: 600; padding: 3px 8px; border-radius: 4px; color: #fff; margin-left: 5px; white-space: nowrap;">${sanitizeHTML(
|
|
249
|
+
tag
|
|
250
|
+
)}</span>`
|
|
251
|
+
)
|
|
252
|
+
.join("");
|
|
253
|
+
|
|
220
254
|
html += `
|
|
221
255
|
<li class="test-item ${getStatusClass(test.status)}"
|
|
222
256
|
data-test-name-min="${sanitizeHTML(testTitle.toLowerCase())}"
|
|
@@ -230,9 +264,9 @@ function generateMinifiedHTML(reportData) {
|
|
|
230
264
|
<span class="test-title-text" title="${sanitizeHTML(
|
|
231
265
|
test.name
|
|
232
266
|
)}">${sanitizeHTML(testTitle)}</span>
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
267
|
+
|
|
268
|
+
${severityBadge}
|
|
269
|
+
${tagsBadges}
|
|
236
270
|
</li>
|
|
237
271
|
`;
|
|
238
272
|
});
|
|
@@ -250,9 +284,9 @@ function generateMinifiedHTML(reportData) {
|
|
|
250
284
|
<head>
|
|
251
285
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
252
286
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
253
|
-
<link rel="icon" type="image/png" href="https://
|
|
254
|
-
<link rel="apple-touch-icon" href="https://
|
|
255
|
-
<title>
|
|
287
|
+
<link rel="icon" type="image/png" href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png">
|
|
288
|
+
<link rel="apple-touch-icon" href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png">
|
|
289
|
+
<title>Pulse Summary Report</title>
|
|
256
290
|
<style>
|
|
257
291
|
:root {
|
|
258
292
|
--primary-color: #2c3e50; /* Dark Blue/Grey */
|
|
@@ -492,8 +526,8 @@ function generateMinifiedHTML(reportData) {
|
|
|
492
526
|
<div class="container">
|
|
493
527
|
<header class="report-header">
|
|
494
528
|
<div class="report-header-title">
|
|
495
|
-
<img id="report-logo" src="https://
|
|
496
|
-
<h1>
|
|
529
|
+
<img id="report-logo" src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png" alt="Report Logo">
|
|
530
|
+
<h1>Pulse Summary Report</h1>
|
|
497
531
|
</div>
|
|
498
532
|
<div class="run-info">
|
|
499
533
|
<strong>Run Date:</strong> ${formatDate(
|
|
@@ -527,8 +561,24 @@ function generateMinifiedHTML(reportData) {
|
|
|
527
561
|
</section>
|
|
528
562
|
|
|
529
563
|
<section class="test-results-section">
|
|
530
|
-
<
|
|
531
|
-
|
|
564
|
+
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 15px; margin-top: 30px; margin-bottom: 15px; padding-bottom: 10px; border-bottom: 2px solid var(--secondary-color);">
|
|
565
|
+
<h1 style="margin: 0; font-size: 1.5em; color: var(--primary-color);">Test Case Summary</h1>
|
|
566
|
+
<div style="display: flex; flex-wrap: wrap; gap: 8px; align-items: center; font-size: 0.75em;">
|
|
567
|
+
<span style="font-weight: 600; color: var(--dark-gray-color);">Legend:</span>
|
|
568
|
+
|
|
569
|
+
<span style="margin-left: 4px; font-weight: 600; color: var(--text-color);">Severity:</span>
|
|
570
|
+
|
|
571
|
+
<span style="background-color: #006064; color: #fff; padding: 2px 6px; border-radius: 3px;">Minor</span>
|
|
572
|
+
<span style="background-color: #FFA07A; color: #fff; padding: 2px 6px; border-radius: 3px;">Low</span>
|
|
573
|
+
<span style="background-color: #577A11; color: #fff; padding: 2px 6px; border-radius: 3px;">Medium</span>
|
|
574
|
+
<span style="background-color: #B71C1C; color: #fff; padding: 2px 6px; border-radius: 3px;">High</span>
|
|
575
|
+
<span style="background-color: #64158A; color: #fff; padding: 2px 6px; border-radius: 3px;">Critical</span>
|
|
576
|
+
|
|
577
|
+
<span style="border-left: 1px solid #ccc; height: 14px; margin: 0 4px;"></span>
|
|
578
|
+
|
|
579
|
+
<span style="background-color: #7f8c8d; color: #fff; padding: 2px 6px; border-radius: 3px;">Tags</span>
|
|
580
|
+
</div>
|
|
581
|
+
</div>
|
|
532
582
|
<div class="filters-section">
|
|
533
583
|
<input type="text" id="filter-min-name" placeholder="Search by test name...">
|
|
534
584
|
<select id="filter-min-status">
|
|
@@ -702,6 +702,9 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
702
702
|
const cardHeight = Math.floor(dashboardHeight * 0.44);
|
|
703
703
|
const cardContentPadding = 16; // px
|
|
704
704
|
|
|
705
|
+
// Logic for Run Context
|
|
706
|
+
const runContext = process.env.CI ? "CI" : "Local Test";
|
|
707
|
+
|
|
705
708
|
return `
|
|
706
709
|
<div class="environment-dashboard-wrapper" id="${dashboardId}">
|
|
707
710
|
<style>
|
|
@@ -744,6 +747,20 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
744
747
|
gap: 20px;
|
|
745
748
|
font-size: 14px;
|
|
746
749
|
}
|
|
750
|
+
|
|
751
|
+
/* Mobile Responsiveness */
|
|
752
|
+
@media (max-width: 768px) {
|
|
753
|
+
.environment-dashboard-wrapper {
|
|
754
|
+
grid-template-columns: 1fr; /* Stack columns on mobile */
|
|
755
|
+
grid-template-rows: auto;
|
|
756
|
+
padding: 16px;
|
|
757
|
+
height: auto !important; /* Allow height to grow */
|
|
758
|
+
}
|
|
759
|
+
.env-card {
|
|
760
|
+
height: auto !important; /* Allow cards to grow based on content */
|
|
761
|
+
min-height: 200px;
|
|
762
|
+
}
|
|
763
|
+
}
|
|
747
764
|
|
|
748
765
|
.env-dashboard-header {
|
|
749
766
|
grid-column: 1 / -1;
|
|
@@ -753,6 +770,8 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
753
770
|
border-bottom: 1px solid var(--border-color);
|
|
754
771
|
padding-bottom: 16px;
|
|
755
772
|
margin-bottom: 8px;
|
|
773
|
+
flex-wrap: wrap; /* Allow wrapping header items */
|
|
774
|
+
gap: 10px;
|
|
756
775
|
}
|
|
757
776
|
|
|
758
777
|
.env-dashboard-title {
|
|
@@ -810,6 +829,8 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
810
829
|
padding: 10px 0;
|
|
811
830
|
border-bottom: 1px solid var(--border-light-color);
|
|
812
831
|
font-size: 0.875rem;
|
|
832
|
+
flex-wrap: wrap; /* Allow details to wrap on very small screens */
|
|
833
|
+
gap: 8px;
|
|
813
834
|
}
|
|
814
835
|
|
|
815
836
|
.env-detail-row:last-child {
|
|
@@ -827,6 +848,7 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
827
848
|
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace;
|
|
828
849
|
text-align: right;
|
|
829
850
|
word-break: break-all;
|
|
851
|
+
margin-left: auto; /* Push to right */
|
|
830
852
|
}
|
|
831
853
|
|
|
832
854
|
.env-chip {
|
|
@@ -1012,7 +1034,7 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
1012
1034
|
</div>
|
|
1013
1035
|
<div class="env-detail-row">
|
|
1014
1036
|
<span class="env-detail-label">Run Context</span>
|
|
1015
|
-
<span class="env-detail-value"
|
|
1037
|
+
<span class="env-detail-value">${runContext}</span>
|
|
1016
1038
|
</div>
|
|
1017
1039
|
</div>
|
|
1018
1040
|
</div>
|
|
@@ -1466,11 +1488,14 @@ function getSuitesData(results) {
|
|
|
1466
1488
|
}
|
|
1467
1489
|
function generateSuitesWidget(suitesData) {
|
|
1468
1490
|
if (!suitesData || suitesData.length === 0) {
|
|
1469
|
-
|
|
1491
|
+
// Maintain height consistency even if empty
|
|
1492
|
+
return `<div class="suites-widget" style="height: 450px;"><div class="suites-header"><h2>Test Suites</h2></div><div class="no-data">No suite data available.</div></div>`;
|
|
1470
1493
|
}
|
|
1494
|
+
|
|
1495
|
+
// Added inline styles for height consistency with Pie Chart (approx 450px) and scrolling
|
|
1471
1496
|
return `
|
|
1472
|
-
<div class="suites-widget">
|
|
1473
|
-
<div class="suites-header">
|
|
1497
|
+
<div class="suites-widget" style="height: 450px; display: flex; flex-direction: column;">
|
|
1498
|
+
<div class="suites-header" style="flex-shrink: 0;">
|
|
1474
1499
|
<h2>Test Suites</h2>
|
|
1475
1500
|
<span class="summary-badge">${
|
|
1476
1501
|
suitesData.length
|
|
@@ -1479,44 +1504,49 @@ function generateSuitesWidget(suitesData) {
|
|
|
1479
1504
|
0
|
|
1480
1505
|
)} tests</span>
|
|
1481
1506
|
</div>
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
<
|
|
1489
|
-
suite
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
${
|
|
1501
|
-
suite.
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1507
|
+
|
|
1508
|
+
<div class="suites-grid-container" style="flex-grow: 1; overflow-y: auto; padding-right: 5px;">
|
|
1509
|
+
<div class="suites-grid">
|
|
1510
|
+
${suitesData
|
|
1511
|
+
.map(
|
|
1512
|
+
(suite) => `
|
|
1513
|
+
<div class="suite-card status-${suite.statusOverall}">
|
|
1514
|
+
<div class="suite-card-header">
|
|
1515
|
+
<h3 class="suite-name" title="${sanitizeHTML(
|
|
1516
|
+
suite.name
|
|
1517
|
+
)} (${sanitizeHTML(suite.browser)})">${sanitizeHTML(
|
|
1518
|
+
suite.name
|
|
1519
|
+
)}</h3>
|
|
1520
|
+
</div>
|
|
1521
|
+
<div>🖥️ <span class="browser-tag">${sanitizeHTML(
|
|
1522
|
+
suite.browser
|
|
1523
|
+
)}</span></div>
|
|
1524
|
+
<div class="suite-card-body">
|
|
1525
|
+
<span class="test-count">${suite.count} test${
|
|
1526
|
+
suite.count !== 1 ? "s" : ""
|
|
1527
|
+
}</span>
|
|
1528
|
+
<div class="suite-stats">
|
|
1529
|
+
${
|
|
1530
|
+
suite.passed > 0
|
|
1531
|
+
? `<span class="stat-passed" title="Passed"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-check-circle-fill" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zm-3.97-3.03a.75.75 0 0 0-1.08.022L7.477 9.417 5.384 7.323a.75.75 0 0 0-1.06 1.06L6.97 11.03a.75.75 0 0 0 1.079-.02l3.992-4.99a.75.75 0 0 0-.01-1.05z"/></svg> ${suite.passed}</span>`
|
|
1532
|
+
: ""
|
|
1533
|
+
}
|
|
1534
|
+
${
|
|
1535
|
+
suite.failed > 0
|
|
1536
|
+
? `<span class="stat-failed" title="Failed"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-x-circle-fill" viewBox="0 0 16 16"><path d="M16 8A8 8 0 1 1 0 8a8 8 0 0 1 16 0zM5.354 4.646a.5.5 0 1 0-.708.708L7.293 8l-2.647 2.646a.5.5 0 0 0 .708.708L8 8.707l2.646 2.647a.5.5 0 0 0 .708-.708L8.707 8l2.647-2.646a.5.5 0 0 0-.708-.708L8 7.293 5.354 4.646z"/></svg> ${suite.failed}</span>`
|
|
1537
|
+
: ""
|
|
1538
|
+
}
|
|
1539
|
+
${
|
|
1540
|
+
suite.skipped > 0
|
|
1541
|
+
? `<span class="stat-skipped" title="Skipped"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" class="bi bi-exclamation-triangle-fill" viewBox="0 0 16 16"><path d="M8.982 1.566a1.13 1.13 0 0 0-1.96 0L.165 13.233c-.457.778.091 1.767.98 1.767h13.713c.889 0 1.438-.99.98-1.767L8.982 1.566zM8 5c.535 0 .954.462.9.995l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 5.995A.905.905 0 0 1 8 5zm.002 6a1 1 0 1 1 0 2 1 1 0 0 1 0-2z"/></svg> ${suite.skipped}</span>`
|
|
1542
|
+
: ""
|
|
1543
|
+
}
|
|
1544
|
+
</div>
|
|
1545
|
+
</div>
|
|
1546
|
+
</div>`
|
|
1547
|
+
)
|
|
1548
|
+
.join("")}
|
|
1516
1549
|
</div>
|
|
1517
|
-
</div>`
|
|
1518
|
-
)
|
|
1519
|
-
.join("")}
|
|
1520
1550
|
</div>
|
|
1521
1551
|
</div>`;
|
|
1522
1552
|
}
|
|
@@ -1748,7 +1778,7 @@ function generateSpecDurationChart(results) {
|
|
|
1748
1778
|
*/
|
|
1749
1779
|
function generateDescribeDurationChart(results) {
|
|
1750
1780
|
if (!results || results.length === 0)
|
|
1751
|
-
return '<div class="no-data">
|
|
1781
|
+
return '<div class="no-data">Seems like there is test describe block available in the executed test suite.</div>';
|
|
1752
1782
|
|
|
1753
1783
|
const describeMap = new Map();
|
|
1754
1784
|
let foundAnyDescribe = false;
|
|
@@ -1875,6 +1905,140 @@ function generateDescribeDurationChart(results) {
|
|
|
1875
1905
|
</script>
|
|
1876
1906
|
`;
|
|
1877
1907
|
}
|
|
1908
|
+
/**
|
|
1909
|
+
* Generates a stacked column chart showing test results distributed by severity.
|
|
1910
|
+
* Matches dimensions of the System Environment section (~600px).
|
|
1911
|
+
* Lazy-loaded for performance.
|
|
1912
|
+
*/
|
|
1913
|
+
function generateSeverityDistributionChart(results) {
|
|
1914
|
+
if (!results || results.length === 0) {
|
|
1915
|
+
return '<div class="trend-chart" style="height: 600px;"><div class="no-data">No results available for severity distribution.</div></div>';
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
const severityLevels = ["Critical", "High", "Medium", "Low", "Minor"];
|
|
1919
|
+
const data = {
|
|
1920
|
+
passed: [0, 0, 0, 0, 0],
|
|
1921
|
+
failed: [0, 0, 0, 0, 0],
|
|
1922
|
+
skipped: [0, 0, 0, 0, 0],
|
|
1923
|
+
};
|
|
1924
|
+
|
|
1925
|
+
results.forEach((test) => {
|
|
1926
|
+
const sev = test.severity || "Medium";
|
|
1927
|
+
const status = String(test.status).toLowerCase();
|
|
1928
|
+
|
|
1929
|
+
let index = severityLevels.indexOf(sev);
|
|
1930
|
+
if (index === -1) index = 2; // Default to Medium
|
|
1931
|
+
|
|
1932
|
+
if (status === "passed") {
|
|
1933
|
+
data.passed[index]++;
|
|
1934
|
+
} else if (
|
|
1935
|
+
status === "failed" ||
|
|
1936
|
+
status === "timedout" ||
|
|
1937
|
+
status === "interrupted"
|
|
1938
|
+
) {
|
|
1939
|
+
data.failed[index]++;
|
|
1940
|
+
} else {
|
|
1941
|
+
data.skipped[index]++;
|
|
1942
|
+
}
|
|
1943
|
+
});
|
|
1944
|
+
|
|
1945
|
+
const chartId = `sevDistChart-${Date.now()}-${Math.random()
|
|
1946
|
+
.toString(36)
|
|
1947
|
+
.substring(2, 7)}`;
|
|
1948
|
+
const renderFunctionName = `renderSevDistChart_${chartId.replace(/-/g, "_")}`;
|
|
1949
|
+
|
|
1950
|
+
const seriesData = [
|
|
1951
|
+
{ name: "Passed", data: data.passed, color: "var(--success-color)" },
|
|
1952
|
+
{ name: "Failed", data: data.failed, color: "var(--danger-color)" },
|
|
1953
|
+
{ name: "Skipped", data: data.skipped, color: "var(--warning-color)" },
|
|
1954
|
+
];
|
|
1955
|
+
|
|
1956
|
+
const seriesDataStr = JSON.stringify(seriesData);
|
|
1957
|
+
const categoriesStr = JSON.stringify(severityLevels);
|
|
1958
|
+
|
|
1959
|
+
return `
|
|
1960
|
+
<div class="trend-chart" style="height: 600px; padding: 28px; box-sizing: border-box;">
|
|
1961
|
+
<h3 class="chart-title-header">Severity Distribution</h3>
|
|
1962
|
+
<div id="${chartId}" class="lazy-load-chart" data-render-function-name="${renderFunctionName}" style="width: 100%; height: 100%;">
|
|
1963
|
+
<div class="no-data">Loading Severity Chart...</div>
|
|
1964
|
+
</div>
|
|
1965
|
+
<script>
|
|
1966
|
+
window.${renderFunctionName} = function() {
|
|
1967
|
+
const chartContainer = document.getElementById('${chartId}');
|
|
1968
|
+
if (!chartContainer) return;
|
|
1969
|
+
|
|
1970
|
+
if (typeof Highcharts !== 'undefined') {
|
|
1971
|
+
try {
|
|
1972
|
+
chartContainer.innerHTML = '';
|
|
1973
|
+
Highcharts.chart('${chartId}', {
|
|
1974
|
+
chart: { type: 'column', backgroundColor: 'transparent' },
|
|
1975
|
+
title: { text: null },
|
|
1976
|
+
xAxis: {
|
|
1977
|
+
categories: ${categoriesStr},
|
|
1978
|
+
crosshair: true,
|
|
1979
|
+
labels: { style: { color: 'var(--text-color-secondary)' } }
|
|
1980
|
+
},
|
|
1981
|
+
yAxis: {
|
|
1982
|
+
min: 0,
|
|
1983
|
+
title: { text: 'Test Count', style: { color: 'var(--text-color)' } },
|
|
1984
|
+
stackLabels: { enabled: true, style: { fontWeight: 'bold', color: 'var(--text-color)' } },
|
|
1985
|
+
labels: { style: { color: 'var(--text-color-secondary)' } }
|
|
1986
|
+
},
|
|
1987
|
+
legend: {
|
|
1988
|
+
itemStyle: { color: 'var(--text-color)' }
|
|
1989
|
+
},
|
|
1990
|
+
tooltip: {
|
|
1991
|
+
shared: true,
|
|
1992
|
+
useHTML: true,
|
|
1993
|
+
backgroundColor: 'rgba(10,10,10,0.92)',
|
|
1994
|
+
style: { color: '#f5f5f5' },
|
|
1995
|
+
formatter: function() {
|
|
1996
|
+
// Custom formatter to HIDE 0 values
|
|
1997
|
+
let tooltip = '<b>' + this.x + '</b><br/>';
|
|
1998
|
+
let hasItems = false;
|
|
1999
|
+
|
|
2000
|
+
this.points.forEach(point => {
|
|
2001
|
+
if (point.y > 0) { // ONLY show if count > 0
|
|
2002
|
+
tooltip += '<span style="color:' + point.series.color + '">●</span> ' +
|
|
2003
|
+
point.series.name + ': <b>' + point.y + '</b><br/>';
|
|
2004
|
+
hasItems = true;
|
|
2005
|
+
}
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
if (!hasItems) return false; // Hide tooltip entirely if no data
|
|
2009
|
+
|
|
2010
|
+
// Calculate total from visible points to ensure accuracy or use stackTotal
|
|
2011
|
+
tooltip += 'Total: ' + this.points[0].total;
|
|
2012
|
+
return tooltip;
|
|
2013
|
+
}
|
|
2014
|
+
},
|
|
2015
|
+
plotOptions: {
|
|
2016
|
+
column: {
|
|
2017
|
+
stacking: 'normal',
|
|
2018
|
+
dataLabels: {
|
|
2019
|
+
enabled: true,
|
|
2020
|
+
color: '#fff',
|
|
2021
|
+
style: { textOutline: 'none' },
|
|
2022
|
+
formatter: function() {
|
|
2023
|
+
return (this.y > 0) ? this.y : null; // Hide 0 labels on chart bars
|
|
2024
|
+
}
|
|
2025
|
+
},
|
|
2026
|
+
borderRadius: 3
|
|
2027
|
+
}
|
|
2028
|
+
},
|
|
2029
|
+
series: ${seriesDataStr},
|
|
2030
|
+
credits: { enabled: false }
|
|
2031
|
+
});
|
|
2032
|
+
} catch(e) {
|
|
2033
|
+
console.error("Error rendering severity chart:", e);
|
|
2034
|
+
chartContainer.innerHTML = '<div class="no-data">Error rendering chart.</div>';
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
};
|
|
2038
|
+
</script>
|
|
2039
|
+
</div>
|
|
2040
|
+
`;
|
|
2041
|
+
}
|
|
1878
2042
|
/**
|
|
1879
2043
|
* Generates the HTML content for the report.
|
|
1880
2044
|
* @param {object} reportData - The report data object containing run and results.
|
|
@@ -1918,6 +2082,28 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1918
2082
|
const testFileParts = test.name.split(" > ");
|
|
1919
2083
|
const testTitle =
|
|
1920
2084
|
testFileParts[testFileParts.length - 1] || "Unnamed Test";
|
|
2085
|
+
// --- NEW: Severity Logic ---
|
|
2086
|
+
const severity = test.severity || "Medium";
|
|
2087
|
+
const getSeverityColor = (level) => {
|
|
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
|
+
// ---------------------------
|
|
1921
2107
|
const generateStepsHTML = (steps, depth = 0) => {
|
|
1922
2108
|
if (!steps || steps.length === 0)
|
|
1923
2109
|
return "<div class='no-steps'>No steps recorded for this test.</div>";
|
|
@@ -2011,6 +2197,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2011
2197
|
<span class="test-case-browser">(${sanitizeHTML(browser)})</span>
|
|
2012
2198
|
</div>
|
|
2013
2199
|
<div class="test-case-meta">
|
|
2200
|
+
${severityBadge}
|
|
2014
2201
|
${
|
|
2015
2202
|
test.tags && test.tags.length > 0
|
|
2016
2203
|
? test.tags
|
|
@@ -2286,10 +2473,10 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2286
2473
|
<head>
|
|
2287
2474
|
<meta charset="UTF-8">
|
|
2288
2475
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2289
|
-
<link rel="icon" type="image/png" href="https://
|
|
2290
|
-
<link rel="apple-touch-icon" href="https://
|
|
2476
|
+
<link rel="icon" type="image/png" href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png">
|
|
2477
|
+
<link rel="apple-touch-icon" href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png">
|
|
2291
2478
|
<script src="https://code.highcharts.com/highcharts.js" defer></script>
|
|
2292
|
-
<title>
|
|
2479
|
+
<title>Pulse Report</title>
|
|
2293
2480
|
<style>
|
|
2294
2481
|
:root {
|
|
2295
2482
|
--primary-color: #3f51b5; --secondary-color: #ff4081; --accent-color: #673ab7; --accent-color-alt: #FF9800;
|
|
@@ -2330,7 +2517,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2330
2517
|
.status-passed .value, .stat-passed svg { color: var(--success-color); }
|
|
2331
2518
|
.status-failed .value, .stat-failed svg { color: var(--danger-color); }
|
|
2332
2519
|
.status-skipped .value, .stat-skipped svg { color: var(--warning-color); }
|
|
2333
|
-
.dashboard-bottom-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 28px; align-items:
|
|
2520
|
+
.dashboard-bottom-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 28px; align-items: start; }
|
|
2334
2521
|
.pie-chart-wrapper, .suites-widget, .trend-chart { background-color: var(--card-background-color); padding: 28px; border-radius: var(--border-radius); box-shadow: var(--box-shadow-light); display: flex; flex-direction: column; }
|
|
2335
2522
|
.pie-chart-wrapper h3, .suites-header h2, .trend-chart h3 { text-align: center; margin-top: 0; margin-bottom: 25px; font-size: 1.25em; font-weight: 600; color: var(--text-color); }
|
|
2336
2523
|
.trend-chart-container, .pie-chart-wrapper div[id^="pieChart-"] { flex-grow: 1; min-height: 250px; }
|
|
@@ -2433,6 +2620,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2433
2620
|
.status-badge-small.status-failed { background-color: var(--danger-color); }
|
|
2434
2621
|
.status-badge-small.status-skipped { background-color: var(--warning-color); }
|
|
2435
2622
|
.status-badge-small.status-unknown { background-color: var(--dark-gray-color); }
|
|
2623
|
+
.badge-severity { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 11px; font-weight: 700; color: white; text-transform: uppercase; margin-right: 8px; vertical-align: middle; }
|
|
2436
2624
|
.no-data, .no-tests, .no-steps, .no-data-chart { padding: 28px; text-align: center; color: var(--dark-gray-color); font-style: italic; font-size:1.1em; background-color: var(--light-gray-color); border-radius: var(--border-radius); margin: 18px 0; border: 1px dashed var(--medium-gray-color); }
|
|
2437
2625
|
.no-data-chart {font-size: 0.95em; padding: 18px;}
|
|
2438
2626
|
.ai-failure-cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); gap: 22px; }
|
|
@@ -2716,8 +2904,8 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2716
2904
|
<div class="container">
|
|
2717
2905
|
<header class="header">
|
|
2718
2906
|
<div class="header-title">
|
|
2719
|
-
<img id="report-logo" src="https://
|
|
2720
|
-
<h1>
|
|
2907
|
+
<img id="report-logo" src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png" alt="Report Logo">
|
|
2908
|
+
<h1>Pulse Report</h1>
|
|
2721
2909
|
</div>
|
|
2722
2910
|
<div class="run-info"><strong>Run Date:</strong> ${formatDate(
|
|
2723
2911
|
runSummary.timestamp
|
|
@@ -2751,7 +2939,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2751
2939
|
)}</div></div>
|
|
2752
2940
|
</div>
|
|
2753
2941
|
<div class="dashboard-bottom-row">
|
|
2754
|
-
<div style="display:
|
|
2942
|
+
<div style="display: flex; flex-direction: column; gap: 28px;">
|
|
2755
2943
|
${generatePieChart(
|
|
2756
2944
|
[
|
|
2757
2945
|
{ label: "Passed", value: runSummary.passed },
|
|
@@ -2768,9 +2956,13 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2768
2956
|
: '<div class="no-data">Environment data not available.</div>'
|
|
2769
2957
|
}
|
|
2770
2958
|
</div>
|
|
2959
|
+
|
|
2960
|
+
<div style="display: flex; flex-direction: column; gap: 28px;">
|
|
2771
2961
|
${generateSuitesWidget(suitesData)}
|
|
2962
|
+
${generateSeverityDistributionChart(results)}
|
|
2963
|
+
</div>
|
|
2772
2964
|
</div>
|
|
2773
|
-
|
|
2965
|
+
</div>
|
|
2774
2966
|
<div id="test-runs" class="tab-content">
|
|
2775
2967
|
<div class="filters">
|
|
2776
2968
|
<input type="text" id="filter-name" placeholder="Filter by test name/path..." style="border-color: black; border-style: outset;">
|
|
@@ -776,6 +776,9 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
776
776
|
const cardHeight = Math.floor(dashboardHeight * 0.44);
|
|
777
777
|
const cardContentPadding = 16; // px
|
|
778
778
|
|
|
779
|
+
// Logic for Run Context
|
|
780
|
+
const runContext = process.env.CI ? "CI" : "Local Test";
|
|
781
|
+
|
|
779
782
|
return `
|
|
780
783
|
<div class="environment-dashboard-wrapper" id="${dashboardId}">
|
|
781
784
|
<style>
|
|
@@ -819,6 +822,20 @@ gap: 20px;
|
|
|
819
822
|
font-size: 14px;
|
|
820
823
|
}
|
|
821
824
|
|
|
825
|
+
/* Mobile Responsiveness */
|
|
826
|
+
@media (max-width: 768px) {
|
|
827
|
+
.environment-dashboard-wrapper {
|
|
828
|
+
grid-template-columns: 1fr; /* Stack columns on mobile */
|
|
829
|
+
grid-template-rows: auto;
|
|
830
|
+
padding: 16px;
|
|
831
|
+
height: auto !important; /* Allow height to grow */
|
|
832
|
+
}
|
|
833
|
+
.env-card {
|
|
834
|
+
height: auto !important; /* Allow cards to grow based on content */
|
|
835
|
+
min-height: 200px;
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
822
839
|
.env-dashboard-header {
|
|
823
840
|
grid-column: 1 / -1;
|
|
824
841
|
display: flex;
|
|
@@ -827,6 +844,8 @@ align-items: center;
|
|
|
827
844
|
border-bottom: 1px solid var(--border-color);
|
|
828
845
|
padding-bottom: 16px;
|
|
829
846
|
margin-bottom: 8px;
|
|
847
|
+
flex-wrap: wrap; /* Allow wrapping header items */
|
|
848
|
+
gap: 10px;
|
|
830
849
|
}
|
|
831
850
|
|
|
832
851
|
.env-dashboard-title {
|
|
@@ -1086,7 +1105,7 @@ border-color: var(--border-color);
|
|
|
1086
1105
|
</div>
|
|
1087
1106
|
<div class="env-detail-row">
|
|
1088
1107
|
<span class="env-detail-label">Run Context</span>
|
|
1089
|
-
<span class="env-detail-value"
|
|
1108
|
+
<span class="env-detail-value">${runContext}</span>
|
|
1090
1109
|
</div>
|
|
1091
1110
|
</div>
|
|
1092
1111
|
</div>
|
|
@@ -1885,7 +1904,7 @@ function generateSpecDurationChart(results) {
|
|
|
1885
1904
|
*/
|
|
1886
1905
|
function generateDescribeDurationChart(results) {
|
|
1887
1906
|
if (!results || results.length === 0)
|
|
1888
|
-
return '<div class="no-data">
|
|
1907
|
+
return '<div class="no-data">Seems like there is test describe block available in the executed test suite.</div>';
|
|
1889
1908
|
|
|
1890
1909
|
const describeMap = new Map();
|
|
1891
1910
|
let foundAnyDescribe = false;
|
|
@@ -2012,6 +2031,140 @@ function generateDescribeDurationChart(results) {
|
|
|
2012
2031
|
</script>
|
|
2013
2032
|
`;
|
|
2014
2033
|
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Generates a stacked column chart showing test results distributed by severity.
|
|
2036
|
+
* Matches dimensions of the System Environment section (~600px).
|
|
2037
|
+
* Lazy-loaded for performance.
|
|
2038
|
+
*/
|
|
2039
|
+
function generateSeverityDistributionChart(results) {
|
|
2040
|
+
if (!results || results.length === 0) {
|
|
2041
|
+
return '<div class="trend-chart" style="height: 600px;"><div class="no-data">No results available for severity distribution.</div></div>';
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
const severityLevels = ["Critical", "High", "Medium", "Low", "Minor"];
|
|
2045
|
+
const data = {
|
|
2046
|
+
passed: [0, 0, 0, 0, 0],
|
|
2047
|
+
failed: [0, 0, 0, 0, 0],
|
|
2048
|
+
skipped: [0, 0, 0, 0, 0],
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
results.forEach((test) => {
|
|
2052
|
+
const sev = test.severity || "Medium";
|
|
2053
|
+
const status = String(test.status).toLowerCase();
|
|
2054
|
+
|
|
2055
|
+
let index = severityLevels.indexOf(sev);
|
|
2056
|
+
if (index === -1) index = 2; // Default to Medium
|
|
2057
|
+
|
|
2058
|
+
if (status === "passed") {
|
|
2059
|
+
data.passed[index]++;
|
|
2060
|
+
} else if (
|
|
2061
|
+
status === "failed" ||
|
|
2062
|
+
status === "timedout" ||
|
|
2063
|
+
status === "interrupted"
|
|
2064
|
+
) {
|
|
2065
|
+
data.failed[index]++;
|
|
2066
|
+
} else {
|
|
2067
|
+
data.skipped[index]++;
|
|
2068
|
+
}
|
|
2069
|
+
});
|
|
2070
|
+
|
|
2071
|
+
const chartId = `sevDistChart-${Date.now()}-${Math.random()
|
|
2072
|
+
.toString(36)
|
|
2073
|
+
.substring(2, 7)}`;
|
|
2074
|
+
const renderFunctionName = `renderSevDistChart_${chartId.replace(/-/g, "_")}`;
|
|
2075
|
+
|
|
2076
|
+
const seriesData = [
|
|
2077
|
+
{ name: "Passed", data: data.passed, color: "var(--success-color)" },
|
|
2078
|
+
{ name: "Failed", data: data.failed, color: "var(--danger-color)" },
|
|
2079
|
+
{ name: "Skipped", data: data.skipped, color: "var(--warning-color)" },
|
|
2080
|
+
];
|
|
2081
|
+
|
|
2082
|
+
const seriesDataStr = JSON.stringify(seriesData);
|
|
2083
|
+
const categoriesStr = JSON.stringify(severityLevels);
|
|
2084
|
+
|
|
2085
|
+
return `
|
|
2086
|
+
<div class="trend-chart" style="height: 600px; padding: 28px; box-sizing: border-box;">
|
|
2087
|
+
<h3 class="chart-title-header">Severity Distribution</h3>
|
|
2088
|
+
<div id="${chartId}" class="lazy-load-chart" data-render-function-name="${renderFunctionName}" style="width: 100%; height: 100%;">
|
|
2089
|
+
<div class="no-data">Loading Severity Chart...</div>
|
|
2090
|
+
</div>
|
|
2091
|
+
<script>
|
|
2092
|
+
window.${renderFunctionName} = function() {
|
|
2093
|
+
const chartContainer = document.getElementById('${chartId}');
|
|
2094
|
+
if (!chartContainer) return;
|
|
2095
|
+
|
|
2096
|
+
if (typeof Highcharts !== 'undefined') {
|
|
2097
|
+
try {
|
|
2098
|
+
chartContainer.innerHTML = '';
|
|
2099
|
+
Highcharts.chart('${chartId}', {
|
|
2100
|
+
chart: { type: 'column', backgroundColor: 'transparent' },
|
|
2101
|
+
title: { text: null },
|
|
2102
|
+
xAxis: {
|
|
2103
|
+
categories: ${categoriesStr},
|
|
2104
|
+
crosshair: true,
|
|
2105
|
+
labels: { style: { color: 'var(--text-color-secondary)' } }
|
|
2106
|
+
},
|
|
2107
|
+
yAxis: {
|
|
2108
|
+
min: 0,
|
|
2109
|
+
title: { text: 'Test Count', style: { color: 'var(--text-color)' } },
|
|
2110
|
+
stackLabels: { enabled: true, style: { fontWeight: 'bold', color: 'var(--text-color)' } },
|
|
2111
|
+
labels: { style: { color: 'var(--text-color-secondary)' } }
|
|
2112
|
+
},
|
|
2113
|
+
legend: {
|
|
2114
|
+
itemStyle: { color: 'var(--text-color)' }
|
|
2115
|
+
},
|
|
2116
|
+
tooltip: {
|
|
2117
|
+
shared: true,
|
|
2118
|
+
useHTML: true,
|
|
2119
|
+
backgroundColor: 'rgba(10,10,10,0.92)',
|
|
2120
|
+
style: { color: '#f5f5f5' },
|
|
2121
|
+
formatter: function() {
|
|
2122
|
+
// Custom formatter to HIDE 0 values
|
|
2123
|
+
let tooltip = '<b>' + this.x + '</b><br/>';
|
|
2124
|
+
let hasItems = false;
|
|
2125
|
+
|
|
2126
|
+
this.points.forEach(point => {
|
|
2127
|
+
if (point.y > 0) { // ONLY show if count > 0
|
|
2128
|
+
tooltip += '<span style="color:' + point.series.color + '">●</span> ' +
|
|
2129
|
+
point.series.name + ': <b>' + point.y + '</b><br/>';
|
|
2130
|
+
hasItems = true;
|
|
2131
|
+
}
|
|
2132
|
+
});
|
|
2133
|
+
|
|
2134
|
+
if (!hasItems) return false; // Hide tooltip entirely if no data
|
|
2135
|
+
|
|
2136
|
+
// Calculate total from visible points to ensure accuracy or use stackTotal
|
|
2137
|
+
tooltip += 'Total: ' + this.points[0].total;
|
|
2138
|
+
return tooltip;
|
|
2139
|
+
}
|
|
2140
|
+
},
|
|
2141
|
+
plotOptions: {
|
|
2142
|
+
column: {
|
|
2143
|
+
stacking: 'normal',
|
|
2144
|
+
dataLabels: {
|
|
2145
|
+
enabled: true,
|
|
2146
|
+
color: '#fff',
|
|
2147
|
+
style: { textOutline: 'none' },
|
|
2148
|
+
formatter: function() {
|
|
2149
|
+
return (this.y > 0) ? this.y : null; // Hide 0 labels on chart bars
|
|
2150
|
+
}
|
|
2151
|
+
},
|
|
2152
|
+
borderRadius: 3
|
|
2153
|
+
}
|
|
2154
|
+
},
|
|
2155
|
+
series: ${seriesDataStr},
|
|
2156
|
+
credits: { enabled: false }
|
|
2157
|
+
});
|
|
2158
|
+
} catch(e) {
|
|
2159
|
+
console.error("Error rendering severity chart:", e);
|
|
2160
|
+
chartContainer.innerHTML = '<div class="no-data">Error rendering chart.</div>';
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
};
|
|
2164
|
+
</script>
|
|
2165
|
+
</div>
|
|
2166
|
+
`;
|
|
2167
|
+
}
|
|
2015
2168
|
/**
|
|
2016
2169
|
* Generates the HTML report.
|
|
2017
2170
|
* @param {object} reportData - The data for the report.
|
|
@@ -2054,6 +2207,28 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2054
2207
|
const testFileParts = test.name.split(" > ");
|
|
2055
2208
|
const testTitle =
|
|
2056
2209
|
testFileParts[testFileParts.length - 1] || "Unnamed Test";
|
|
2210
|
+
// --- NEW: Severity Logic ---
|
|
2211
|
+
const severity = test.severity || "Medium";
|
|
2212
|
+
const getSeverityColor = (level) => {
|
|
2213
|
+
switch (level) {
|
|
2214
|
+
case "Minor":
|
|
2215
|
+
return "#006064";
|
|
2216
|
+
case "Low":
|
|
2217
|
+
return "#FFA07A";
|
|
2218
|
+
case "Medium":
|
|
2219
|
+
return "#577A11";
|
|
2220
|
+
case "High":
|
|
2221
|
+
return "#B71C1C";
|
|
2222
|
+
case "Critical":
|
|
2223
|
+
return "#64158A";
|
|
2224
|
+
default:
|
|
2225
|
+
return "#577A11";
|
|
2226
|
+
}
|
|
2227
|
+
};
|
|
2228
|
+
const severityColor = getSeverityColor(severity);
|
|
2229
|
+
// We reuse 'status-badge' class for size/font consistency, but override background color
|
|
2230
|
+
const severityBadge = `<span class="status-badge" style="background-color: ${severityColor}; margin-right: 8px;">${severity}</span>`;
|
|
2231
|
+
// ---------------------------
|
|
2057
2232
|
const generateStepsHTML = (steps, depth = 0) => {
|
|
2058
2233
|
if (!steps || steps.length === 0)
|
|
2059
2234
|
return "<div class='no-steps'>No steps recorded for this test.</div>";
|
|
@@ -2163,13 +2338,16 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2163
2338
|
testTitle
|
|
2164
2339
|
)}</span><span class="test-case-browser">(${sanitizeHTML(
|
|
2165
2340
|
browser
|
|
2166
|
-
)})</span></div><div class="test-case-meta"
|
|
2341
|
+
)})</span></div><div class="test-case-meta">
|
|
2342
|
+
${severityBadge}
|
|
2343
|
+
${
|
|
2167
2344
|
test.tags && test.tags.length > 0
|
|
2168
2345
|
? test.tags
|
|
2169
2346
|
.map((t) => `<span class="tag">${sanitizeHTML(t)}</span>`)
|
|
2170
2347
|
.join(" ")
|
|
2171
2348
|
: ""
|
|
2172
|
-
}
|
|
2349
|
+
}
|
|
2350
|
+
<span class="test-duration">${formatDuration(
|
|
2173
2351
|
test.duration
|
|
2174
2352
|
)}</span></div></div>
|
|
2175
2353
|
<div class="test-case-content" style="display: none;">
|
|
@@ -2453,10 +2631,10 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2453
2631
|
<head>
|
|
2454
2632
|
<meta charset="UTF-8">
|
|
2455
2633
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
2456
|
-
<link rel="icon" type="image/png" href="https://
|
|
2457
|
-
<link rel="apple-touch-icon" href="https://
|
|
2634
|
+
<link rel="icon" type="image/png" href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png">
|
|
2635
|
+
<link rel="apple-touch-icon" href="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png">
|
|
2458
2636
|
<script src="https://code.highcharts.com/highcharts.js" defer></script>
|
|
2459
|
-
<title>
|
|
2637
|
+
<title>Pulse Static Report</title>
|
|
2460
2638
|
|
|
2461
2639
|
<style>
|
|
2462
2640
|
:root {
|
|
@@ -2498,7 +2676,7 @@ body { font-family: var(--font-family); margin: 0; background-color: var(--backg
|
|
|
2498
2676
|
.status-passed .value, .stat-passed svg { color: var(--success-color); }
|
|
2499
2677
|
.status-failed .value, .stat-failed svg { color: var(--danger-color); }
|
|
2500
2678
|
.status-skipped .value, .stat-skipped svg { color: var(--warning-color); }
|
|
2501
|
-
.dashboard-bottom-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 28px; align-items:
|
|
2679
|
+
.dashboard-bottom-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); gap: 28px; align-items: start; }
|
|
2502
2680
|
.pie-chart-wrapper, .suites-widget, .trend-chart { background-color: var(--card-background-color); padding: 28px; border-radius: var(--border-radius); box-shadow: var(--box-shadow-light); display: flex; flex-direction: column; }
|
|
2503
2681
|
.pie-chart-wrapper h3, .suites-header h2, .trend-chart h3 { text-align: center; margin-top: 0; margin-bottom: 25px; font-size: 1.25em; font-weight: 600; color: var(--text-color); }
|
|
2504
2682
|
.trend-chart-container, .pie-chart-wrapper div[id^="pieChart-"] { flex-grow: 1; min-height: 250px; }
|
|
@@ -2620,6 +2798,7 @@ aspect-ratio: 16 / 9;
|
|
|
2620
2798
|
.status-badge-small.status-failed { background-color: var(--danger-color); }
|
|
2621
2799
|
.status-badge-small.status-skipped { background-color: var(--warning-color); }
|
|
2622
2800
|
.status-badge-small.status-unknown { background-color: var(--dark-gray-color); }
|
|
2801
|
+
.badge-severity { display: inline-block; padding: 4px 8px; border-radius: 4px; font-size: 11px; font-weight: 700; color: white; text-transform: uppercase; margin-right: 8px; vertical-align: middle; }
|
|
2623
2802
|
.no-data, .no-tests, .no-steps, .no-data-chart { padding: 28px; text-align: center; color: var(--dark-gray-color); font-style: italic; font-size:1.1em; background-color: var(--light-gray-color); border-radius: var(--border-radius); margin: 18px 0; border: 1px dashed var(--medium-gray-color); }
|
|
2624
2803
|
.no-data-chart {font-size: 0.95em; padding: 18px;}
|
|
2625
2804
|
.ai-failure-cards-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(400px, 1fr)); gap: 22px; }
|
|
@@ -2701,8 +2880,8 @@ aspect-ratio: 16 / 9;
|
|
|
2701
2880
|
<div class="container">
|
|
2702
2881
|
<header class="header">
|
|
2703
2882
|
<div class="header-title">
|
|
2704
|
-
<img id="report-logo" src="https://
|
|
2705
|
-
<h1>
|
|
2883
|
+
<img id="report-logo" src="https://ocpaxmghzmfbuhxzxzae.supabase.co/storage/v1/object/public/images/pulse-report/playwright_pulse_icon.png" alt="Report Logo">
|
|
2884
|
+
<h1>Pulse Static Report</h1>
|
|
2706
2885
|
</div>
|
|
2707
2886
|
<div class="run-info"><strong>Run Date:</strong> ${formatDate(
|
|
2708
2887
|
runSummary.timestamp
|
|
@@ -2753,7 +2932,10 @@ aspect-ratio: 16 / 9;
|
|
|
2753
2932
|
: '<div class="no-data">Environment data not available.</div>'
|
|
2754
2933
|
}
|
|
2755
2934
|
</div>
|
|
2756
|
-
|
|
2935
|
+
<div style="display: flex; flex-direction: column; gap: 28px;">
|
|
2936
|
+
${generateSuitesWidget(suitesData)}
|
|
2937
|
+
${generateSeverityDistributionChart(results)}
|
|
2938
|
+
</div>
|
|
2757
2939
|
</div>
|
|
2758
2940
|
</div>
|
|
2759
2941
|
<div id="test-runs" class="tab-content">
|
|
@@ -3634,4 +3816,4 @@ main().catch((err) => {
|
|
|
3634
3816
|
);
|
|
3635
3817
|
console.error(err.stack);
|
|
3636
3818
|
process.exit(1);
|
|
3637
|
-
});
|
|
3819
|
+
});
|