@arghajit/dummy 0.1.0-beta-25 → 0.1.0-beta-27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -53
- package/dist/reporter/playwright-pulse-reporter.js +5 -4
- package/dist/reporter/tsconfig.reporter.tsbuildinfo +1 -0
- package/dist/types/index.d.ts +2 -1
- package/package.json +1 -1
- package/scripts/generate-email-report.mjs +6 -6
- package/scripts/generate-report.mjs +322 -24
- package/scripts/generate-static-report.mjs +379 -33
|
@@ -1019,6 +1019,260 @@ function generateEnvironmentDashboard(environment, dashboardHeight = 600) {
|
|
|
1019
1019
|
</div>
|
|
1020
1020
|
`;
|
|
1021
1021
|
}
|
|
1022
|
+
function generateWorkerDistributionChart(results) {
|
|
1023
|
+
if (!results || results.length === 0) {
|
|
1024
|
+
return '<div class="no-data">No test results data available to display worker distribution.</div>';
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
// 1. Sort results by startTime to ensure chronological order
|
|
1028
|
+
const sortedResults = [...results].sort((a, b) => {
|
|
1029
|
+
const timeA = a.startTime ? new Date(a.startTime).getTime() : 0;
|
|
1030
|
+
const timeB = b.startTime ? new Date(b.startTime).getTime() : 0;
|
|
1031
|
+
return timeA - timeB;
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
const workerData = sortedResults.reduce((acc, test) => {
|
|
1035
|
+
const workerId =
|
|
1036
|
+
typeof test.workerId !== "undefined" ? test.workerId : "N/A";
|
|
1037
|
+
if (!acc[workerId]) {
|
|
1038
|
+
acc[workerId] = { passed: 0, failed: 0, skipped: 0, tests: [] };
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const status = String(test.status).toLowerCase();
|
|
1042
|
+
if (status === "passed" || status === "failed" || status === "skipped") {
|
|
1043
|
+
acc[workerId][status]++;
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
const testTitleParts = test.name.split(" > ");
|
|
1047
|
+
const testTitle =
|
|
1048
|
+
testTitleParts[testTitleParts.length - 1] || "Unnamed Test";
|
|
1049
|
+
// Store both name and status for each test
|
|
1050
|
+
acc[workerId].tests.push({ name: testTitle, status: status });
|
|
1051
|
+
|
|
1052
|
+
return acc;
|
|
1053
|
+
}, {});
|
|
1054
|
+
|
|
1055
|
+
const workerIds = Object.keys(workerData).sort((a, b) => {
|
|
1056
|
+
if (a === "N/A") return 1;
|
|
1057
|
+
if (b === "N/A") return -1;
|
|
1058
|
+
return parseInt(a, 10) - parseInt(b, 10);
|
|
1059
|
+
});
|
|
1060
|
+
|
|
1061
|
+
if (workerIds.length === 0) {
|
|
1062
|
+
return '<div class="no-data">Could not determine worker distribution from test data.</div>';
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
const chartId = `workerDistChart-${Date.now()}-${Math.random()
|
|
1066
|
+
.toString(36)
|
|
1067
|
+
.substring(2, 7)}`;
|
|
1068
|
+
const renderFunctionName = `renderWorkerDistChart_${chartId.replace(
|
|
1069
|
+
/-/g,
|
|
1070
|
+
"_"
|
|
1071
|
+
)}`;
|
|
1072
|
+
const modalJsNamespace = `modal_funcs_${chartId.replace(/-/g, "_")}`;
|
|
1073
|
+
|
|
1074
|
+
// The categories now just need the name for the axis labels
|
|
1075
|
+
const categories = workerIds.map((id) => `Worker ${id}`);
|
|
1076
|
+
|
|
1077
|
+
// We pass the full data separately to the script
|
|
1078
|
+
const fullWorkerData = workerIds.map((id) => ({
|
|
1079
|
+
id: id,
|
|
1080
|
+
name: `Worker ${id}`,
|
|
1081
|
+
tests: workerData[id].tests,
|
|
1082
|
+
}));
|
|
1083
|
+
|
|
1084
|
+
const passedData = workerIds.map((id) => workerData[id].passed);
|
|
1085
|
+
const failedData = workerIds.map((id) => workerData[id].failed);
|
|
1086
|
+
const skippedData = workerIds.map((id) => workerData[id].skipped);
|
|
1087
|
+
|
|
1088
|
+
const categoriesString = JSON.stringify(categories);
|
|
1089
|
+
const fullDataString = JSON.stringify(fullWorkerData);
|
|
1090
|
+
const seriesString = JSON.stringify([
|
|
1091
|
+
{ name: "Passed", data: passedData, color: "var(--success-color)" },
|
|
1092
|
+
{ name: "Failed", data: failedData, color: "var(--danger-color)" },
|
|
1093
|
+
{ name: "Skipped", data: skippedData, color: "var(--warning-color)" },
|
|
1094
|
+
]);
|
|
1095
|
+
|
|
1096
|
+
// The HTML now includes the chart container, the modal, and styles for the modal
|
|
1097
|
+
return `
|
|
1098
|
+
<style>
|
|
1099
|
+
.worker-modal-overlay {
|
|
1100
|
+
position: fixed; z-index: 1050; left: 0; top: 0; width: 100%; height: 100%;
|
|
1101
|
+
overflow: auto; background-color: rgba(0,0,0,0.6);
|
|
1102
|
+
display: none; align-items: center; justify-content: center;
|
|
1103
|
+
}
|
|
1104
|
+
.worker-modal-content {
|
|
1105
|
+
background-color: #3d4043;
|
|
1106
|
+
color: var(--card-background-color);
|
|
1107
|
+
margin: auto; padding: 20px; border: 1px solid var(--border-color, #888);
|
|
1108
|
+
width: 80%; max-width: 700px; border-radius: 8px;
|
|
1109
|
+
position: relative; box-shadow: 0 5px 15px rgba(0,0,0,0.5);
|
|
1110
|
+
}
|
|
1111
|
+
.worker-modal-close {
|
|
1112
|
+
position: absolute; top: 10px; right: 20px;
|
|
1113
|
+
font-size: 28px; font-weight: bold; cursor: pointer;
|
|
1114
|
+
line-height: 1;
|
|
1115
|
+
}
|
|
1116
|
+
.worker-modal-close:hover, .worker-modal-close:focus {
|
|
1117
|
+
color: var(--text-color, #000);
|
|
1118
|
+
}
|
|
1119
|
+
#worker-modal-body-${chartId} ul {
|
|
1120
|
+
list-style-type: none; padding-left: 0; margin-top: 15px; max-height: 45vh; overflow-y: auto;
|
|
1121
|
+
}
|
|
1122
|
+
#worker-modal-body-${chartId} li {
|
|
1123
|
+
padding: 8px 5px; border-bottom: 1px solid var(--border-color, #eee);
|
|
1124
|
+
font-size: 0.9em;
|
|
1125
|
+
}
|
|
1126
|
+
#worker-modal-body-${chartId} li:last-child {
|
|
1127
|
+
border-bottom: none;
|
|
1128
|
+
}
|
|
1129
|
+
#worker-modal-body-${chartId} li > span {
|
|
1130
|
+
display: inline-block;
|
|
1131
|
+
width: 70px;
|
|
1132
|
+
font-weight: bold;
|
|
1133
|
+
text-align: right;
|
|
1134
|
+
margin-right: 10px;
|
|
1135
|
+
}
|
|
1136
|
+
</style>
|
|
1137
|
+
|
|
1138
|
+
<div id="${chartId}" class="trend-chart-container lazy-load-chart" data-render-function-name="${renderFunctionName}" style="min-height: 350px;">
|
|
1139
|
+
<div class="no-data">Loading Worker Distribution Chart...</div>
|
|
1140
|
+
</div>
|
|
1141
|
+
|
|
1142
|
+
<div id="worker-modal-${chartId}" class="worker-modal-overlay">
|
|
1143
|
+
<div class="worker-modal-content">
|
|
1144
|
+
<span class="worker-modal-close">×</span>
|
|
1145
|
+
<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>
|
|
1146
|
+
<div id="worker-modal-body-${chartId}"></div>
|
|
1147
|
+
</div>
|
|
1148
|
+
</div>
|
|
1149
|
+
|
|
1150
|
+
<script>
|
|
1151
|
+
// Namespace for modal functions to avoid global scope pollution
|
|
1152
|
+
window.${modalJsNamespace} = {};
|
|
1153
|
+
|
|
1154
|
+
window.${renderFunctionName} = function() {
|
|
1155
|
+
const chartContainer = document.getElementById('${chartId}');
|
|
1156
|
+
if (!chartContainer) { console.error("Chart container ${chartId} not found."); return; }
|
|
1157
|
+
|
|
1158
|
+
// --- Modal Setup ---
|
|
1159
|
+
const modal = document.getElementById('worker-modal-${chartId}');
|
|
1160
|
+
const modalTitle = document.getElementById('worker-modal-title-${chartId}');
|
|
1161
|
+
const modalBody = document.getElementById('worker-modal-body-${chartId}');
|
|
1162
|
+
const closeModalBtn = modal.querySelector('.worker-modal-close');
|
|
1163
|
+
|
|
1164
|
+
window.${modalJsNamespace}.open = function(worker) {
|
|
1165
|
+
if (!worker) return;
|
|
1166
|
+
modalTitle.textContent = 'Test Details for ' + worker.name;
|
|
1167
|
+
|
|
1168
|
+
let testListHtml = '<ul>';
|
|
1169
|
+
if (worker.tests && worker.tests.length > 0) {
|
|
1170
|
+
worker.tests.forEach(test => {
|
|
1171
|
+
let color = 'inherit';
|
|
1172
|
+
if (test.status === 'passed') color = 'var(--success-color)';
|
|
1173
|
+
else if (test.status === 'failed') color = 'var(--danger-color)';
|
|
1174
|
+
else if (test.status === 'skipped') color = 'var(--warning-color)';
|
|
1175
|
+
|
|
1176
|
+
const escapedName = test.name.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
1177
|
+
testListHtml += \`<li style="color: \${color};"><span style="color: \${color}">[\${test.status.toUpperCase()}]</span> \${escapedName}</li>\`;
|
|
1178
|
+
});
|
|
1179
|
+
} else {
|
|
1180
|
+
testListHtml += '<li>No detailed test data available for this worker.</li>';
|
|
1181
|
+
}
|
|
1182
|
+
testListHtml += '</ul>';
|
|
1183
|
+
|
|
1184
|
+
modalBody.innerHTML = testListHtml;
|
|
1185
|
+
modal.style.display = 'flex';
|
|
1186
|
+
};
|
|
1187
|
+
|
|
1188
|
+
const closeModal = function() {
|
|
1189
|
+
modal.style.display = 'none';
|
|
1190
|
+
};
|
|
1191
|
+
|
|
1192
|
+
closeModalBtn.onclick = closeModal;
|
|
1193
|
+
modal.onclick = function(event) {
|
|
1194
|
+
// Close if clicked on the dark overlay background
|
|
1195
|
+
if (event.target == modal) {
|
|
1196
|
+
closeModal();
|
|
1197
|
+
}
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
// --- Highcharts Setup ---
|
|
1202
|
+
if (typeof Highcharts !== 'undefined') {
|
|
1203
|
+
try {
|
|
1204
|
+
chartContainer.innerHTML = '';
|
|
1205
|
+
const fullData = ${fullDataString};
|
|
1206
|
+
|
|
1207
|
+
const chartOptions = {
|
|
1208
|
+
chart: { type: 'bar', height: 350, backgroundColor: 'transparent' },
|
|
1209
|
+
title: { text: null },
|
|
1210
|
+
xAxis: {
|
|
1211
|
+
categories: ${categoriesString},
|
|
1212
|
+
title: { text: 'Worker ID' },
|
|
1213
|
+
labels: { style: { color: 'var(--text-color-secondary)' }}
|
|
1214
|
+
},
|
|
1215
|
+
yAxis: {
|
|
1216
|
+
min: 0,
|
|
1217
|
+
title: { text: 'Number of Tests' },
|
|
1218
|
+
labels: { style: { color: 'var(--text-color-secondary)' }},
|
|
1219
|
+
stackLabels: { enabled: true, style: { fontWeight: 'bold', color: 'var(--text-color)' } }
|
|
1220
|
+
},
|
|
1221
|
+
legend: { reversed: true, itemStyle: { fontSize: "12px", color: 'var(--text-color)' } },
|
|
1222
|
+
plotOptions: {
|
|
1223
|
+
series: {
|
|
1224
|
+
stacking: 'normal',
|
|
1225
|
+
cursor: 'pointer',
|
|
1226
|
+
point: {
|
|
1227
|
+
events: {
|
|
1228
|
+
click: function () {
|
|
1229
|
+
// 'this.x' is the index of the category
|
|
1230
|
+
const workerData = fullData[this.x];
|
|
1231
|
+
window.${modalJsNamespace}.open(workerData);
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
},
|
|
1237
|
+
tooltip: {
|
|
1238
|
+
shared: true,
|
|
1239
|
+
headerFormat: '<b>{point.key}</b> (Click for details)<br/>',
|
|
1240
|
+
pointFormat: '<span style="color:{series.color}">●</span> {series.name}: <b>{point.y}</b><br/>',
|
|
1241
|
+
footerFormat: 'Total: <b>{point.total}</b>'
|
|
1242
|
+
},
|
|
1243
|
+
series: ${seriesString},
|
|
1244
|
+
credits: { enabled: false }
|
|
1245
|
+
};
|
|
1246
|
+
Highcharts.chart('${chartId}', chartOptions);
|
|
1247
|
+
} catch (e) {
|
|
1248
|
+
console.error("Error rendering chart ${chartId}:", e);
|
|
1249
|
+
chartContainer.innerHTML = '<div class="no-data">Error rendering worker distribution chart.</div>';
|
|
1250
|
+
}
|
|
1251
|
+
} else {
|
|
1252
|
+
chartContainer.innerHTML = '<div class="no-data">Charting library not available for worker distribution.</div>';
|
|
1253
|
+
}
|
|
1254
|
+
};
|
|
1255
|
+
</script>
|
|
1256
|
+
`;
|
|
1257
|
+
}
|
|
1258
|
+
const infoTooltip = `
|
|
1259
|
+
<span class="info-tooltip" style="display: inline-block; margin-left: 8px;">
|
|
1260
|
+
<span class="info-icon"
|
|
1261
|
+
style="cursor: pointer; font-size: 1.25rem;"
|
|
1262
|
+
onclick="window.workerInfoPrompt()">ℹ️</span>
|
|
1263
|
+
</span>
|
|
1264
|
+
<script>
|
|
1265
|
+
window.workerInfoPrompt = function() {
|
|
1266
|
+
const message = 'Why is worker -1 special?\\n\\n' +
|
|
1267
|
+
'Playwright assigns skipped tests to worker -1 because:\\n' +
|
|
1268
|
+
'1. They don\\'t require browser execution\\n' +
|
|
1269
|
+
'2. This keeps real workers focused on actual tests\\n' +
|
|
1270
|
+
'3. Maintains clean reporting\\n\\n' +
|
|
1271
|
+
'This is an intentional optimization by Playwright.';
|
|
1272
|
+
alert(message);
|
|
1273
|
+
}
|
|
1274
|
+
</script>
|
|
1275
|
+
`;
|
|
1022
1276
|
function generateTestHistoryContent(trendData) {
|
|
1023
1277
|
if (
|
|
1024
1278
|
!trendData ||
|
|
@@ -1267,11 +1521,16 @@ function generateSuitesWidget(suitesData) {
|
|
|
1267
1521
|
</div>`;
|
|
1268
1522
|
}
|
|
1269
1523
|
function getAttachmentIcon(contentType) {
|
|
1270
|
-
if (contentType
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
if (
|
|
1524
|
+
if (!contentType) return "📎"; // Handle undefined/null
|
|
1525
|
+
|
|
1526
|
+
const normalizedType = contentType.toLowerCase();
|
|
1527
|
+
|
|
1528
|
+
if (normalizedType.includes("pdf")) return "📄";
|
|
1529
|
+
if (normalizedType.includes("json")) return "{ }";
|
|
1530
|
+
if (/html/.test(normalizedType)) return "🌐"; // Fixed: regex for any HTML type
|
|
1531
|
+
if (normalizedType.includes("xml")) return "<>";
|
|
1532
|
+
if (normalizedType.includes("csv")) return "📊";
|
|
1533
|
+
if (normalizedType.startsWith("text/")) return "📝";
|
|
1275
1534
|
return "📎";
|
|
1276
1535
|
}
|
|
1277
1536
|
function generateHTML(reportData, trendData = null) {
|
|
@@ -1329,14 +1588,14 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1329
1588
|
<div class="step-details" style="display: none;">
|
|
1330
1589
|
${
|
|
1331
1590
|
step.codeLocation
|
|
1332
|
-
? `<div class="step-info"><strong>Location:</strong> ${sanitizeHTML(
|
|
1591
|
+
? `<div class="step-info code-section"><strong>Location:</strong> ${sanitizeHTML(
|
|
1333
1592
|
step.codeLocation
|
|
1334
1593
|
)}</div>`
|
|
1335
1594
|
: ""
|
|
1336
1595
|
}
|
|
1337
1596
|
${
|
|
1338
1597
|
step.errorMessage
|
|
1339
|
-
? `<div class="
|
|
1598
|
+
? `<div class="test-error-summary">
|
|
1340
1599
|
${
|
|
1341
1600
|
step.stackTrace
|
|
1342
1601
|
? `<div class="stack-trace">${formatPlaywrightError(
|
|
@@ -1441,15 +1700,30 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1441
1700
|
</div>`
|
|
1442
1701
|
: ""
|
|
1443
1702
|
}
|
|
1444
|
-
<h4>Steps</h4>
|
|
1445
|
-
<div class="steps-list">${generateStepsHTML(test.steps)}</div>
|
|
1446
1703
|
${
|
|
1447
|
-
test.
|
|
1448
|
-
? `<div class="
|
|
1449
|
-
test.
|
|
1450
|
-
)}</pre></div>`
|
|
1704
|
+
test.snippet
|
|
1705
|
+
? `<div class="code-section"><h4>Error Snippet</h4><pre><code>${formatPlaywrightError(
|
|
1706
|
+
test.snippet
|
|
1707
|
+
)}</code></pre></div>`
|
|
1451
1708
|
: ""
|
|
1452
1709
|
}
|
|
1710
|
+
<h4>Steps</h4>
|
|
1711
|
+
<div class="steps-list">${generateStepsHTML(test.steps)}</div>
|
|
1712
|
+
${(() => {
|
|
1713
|
+
if (!test.stdout || test.stdout.length === 0) return "";
|
|
1714
|
+
// Create a unique ID for the <pre> element to target it for copying
|
|
1715
|
+
const logId = `stdout-log-${test.id || testIndex}`;
|
|
1716
|
+
return `<div class="console-output-section">
|
|
1717
|
+
<h4>Console Output (stdout)
|
|
1718
|
+
<button class="copy-btn" onclick="copyLogContent('${logId}', this)">Copy Console</button>
|
|
1719
|
+
</h4>
|
|
1720
|
+
<div class="log-wrapper">
|
|
1721
|
+
<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(
|
|
1722
|
+
test.stdout.map((line) => sanitizeHTML(line)).join("\n")
|
|
1723
|
+
)}</pre>
|
|
1724
|
+
</div>
|
|
1725
|
+
</div>`;
|
|
1726
|
+
})()}
|
|
1453
1727
|
${
|
|
1454
1728
|
test.stderr && test.stderr.length > 0
|
|
1455
1729
|
? `<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(
|
|
@@ -1571,6 +1845,9 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1571
1845
|
</div>
|
|
1572
1846
|
<div class="attachment-info">
|
|
1573
1847
|
<div class="trace-actions">
|
|
1848
|
+
<a href="${sanitizeHTML(
|
|
1849
|
+
attachment.path
|
|
1850
|
+
)}" target="_blank" class="view-full">View</a>
|
|
1574
1851
|
<a href="${sanitizeHTML(
|
|
1575
1852
|
attachment.path
|
|
1576
1853
|
)}" target="_blank" download="${sanitizeHTML(
|
|
@@ -1605,8 +1882,8 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1605
1882
|
<head>
|
|
1606
1883
|
<meta charset="UTF-8">
|
|
1607
1884
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
1608
|
-
<link rel="icon" type="image/png" href="https://i.postimg.cc/
|
|
1609
|
-
<link rel="apple-touch-icon" href="https://i.postimg.cc/
|
|
1885
|
+
<link rel="icon" type="image/png" href="https://i.postimg.cc/v817w4sg/logo.png">
|
|
1886
|
+
<link rel="apple-touch-icon" href="https://i.postimg.cc/v817w4sg/logo.png">
|
|
1610
1887
|
<script src="https://code.highcharts.com/highcharts.js" defer></script>
|
|
1611
1888
|
<title>Playwright Pulse Report</title>
|
|
1612
1889
|
<style>
|
|
@@ -1630,7 +1907,7 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1630
1907
|
.header { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; padding-bottom: 25px; border-bottom: 1px solid var(--border-color); margin-bottom: 25px; }
|
|
1631
1908
|
.header-title { display: flex; align-items: center; gap: 15px; }
|
|
1632
1909
|
.header h1 { margin: 0; font-size: 1.85em; font-weight: 600; color: var(--primary-color); }
|
|
1633
|
-
#report-logo { height: 40px; width:
|
|
1910
|
+
#report-logo { height: 40px; width: 55px; }
|
|
1634
1911
|
.run-info { font-size: 0.9em; text-align: right; color: var(--text-color-secondary); line-height:1.5;}
|
|
1635
1912
|
.run-info strong { color: var(--text-color); }
|
|
1636
1913
|
.tabs { display: flex; border-bottom: 2px solid var(--border-color); margin-bottom: 30px; overflow-x: auto; }
|
|
@@ -1709,8 +1986,8 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1709
1986
|
.step-duration { color: var(--dark-gray-color); font-size: 0.9em; }
|
|
1710
1987
|
.step-details { display: none; padding: 14px; margin-top: 8px; background: #fdfdfd; border-radius: 6px; font-size: 0.95em; border: 1px solid var(--light-gray-color); }
|
|
1711
1988
|
.step-info { margin-bottom: 8px; }
|
|
1712
|
-
.
|
|
1713
|
-
.
|
|
1989
|
+
.test-error-summary { color: var(--danger-color); margin-top: 12px; padding: 14px; background: rgba(244,67,54,0.05); border-radius: 4px; font-size: 0.95em; border-left: 3px solid var(--danger-color); }
|
|
1990
|
+
.test-error-summary pre.stack-trace { margin-top: 10px; padding: 12px; background-color: rgba(0,0,0,0.03); border-radius: 4px; font-size:0.9em; max-height: 280px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; }
|
|
1714
1991
|
.step-hook { background-color: rgba(33,150,243,0.04); border-left: 3px solid var(--info-color) !important; }
|
|
1715
1992
|
.step-hook .step-title { font-style: italic; color: var(--info-color)}
|
|
1716
1993
|
.nested-steps { margin-top: 12px; }
|
|
@@ -1767,17 +2044,18 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1767
2044
|
.download-trace:hover { background: #cbd5e0; }
|
|
1768
2045
|
.filters button.clear-filters-btn { background-color: var(--medium-gray-color); color: var(--text-color); }
|
|
1769
2046
|
.filters button.clear-filters-btn:hover { background-color: var(--dark-gray-color); color: #fff; }
|
|
2047
|
+
.copy-btn {color: var(--primary-color); background: #fefefe; border-radius: 8px; cursor: pointer; border-color: var(--primary-color); font-size: 1em; margin-left: 93%; font-weight: 600;}
|
|
1770
2048
|
@media (max-width: 1200px) { .trend-charts-row { grid-template-columns: 1fr; } }
|
|
1771
2049
|
@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; } }
|
|
1772
2050
|
@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;} }
|
|
1773
|
-
@media (max-width: 480px) { body {font-size: 14px;} .container {padding: 15px;} .header h1 {font-size: 1.4em;} #report-logo { height: 35px; width:
|
|
2051
|
+
@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;} }
|
|
1774
2052
|
</style>
|
|
1775
2053
|
</head>
|
|
1776
2054
|
<body>
|
|
1777
2055
|
<div class="container">
|
|
1778
2056
|
<header class="header">
|
|
1779
2057
|
<div class="header-title">
|
|
1780
|
-
<img id="report-logo" src="
|
|
2058
|
+
<img id="report-logo" src="https://i.postimg.cc/v817w4sg/logo.png" alt="Report Logo">
|
|
1781
2059
|
<h1>Playwright Pulse Report</h1>
|
|
1782
2060
|
</div>
|
|
1783
2061
|
<div class="run-info"><strong>Run Date:</strong> ${formatDate(
|
|
@@ -1870,6 +2148,12 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1870
2148
|
}
|
|
1871
2149
|
</div>
|
|
1872
2150
|
</div>
|
|
2151
|
+
<h2 class="tab-main-title">Test Distribution by Worker ${infoTooltip}</h2>
|
|
2152
|
+
<div class="trend-charts-row">
|
|
2153
|
+
<div class="trend-chart">
|
|
2154
|
+
${generateWorkerDistributionChart(results)}
|
|
2155
|
+
</div>
|
|
2156
|
+
</div>
|
|
1873
2157
|
<h2 class="tab-main-title">Individual Test History</h2>
|
|
1874
2158
|
${
|
|
1875
2159
|
trendData &&
|
|
@@ -1884,7 +2168,6 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1884
2168
|
</div>
|
|
1885
2169
|
<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;">
|
|
1886
2170
|
<div style="display: inline-flex; align-items: center; gap: 0.5rem; color: #333; font-size: 0.9rem; font-weight: 600; letter-spacing: 0.5px;">
|
|
1887
|
-
<img width="48" height="48" src="https://img.icons8.com/emoji/48/index-pointing-at-the-viewer-light-skin-tone-emoji.png" alt="index-pointing-at-the-viewer-light-skin-tone-emoji"/>
|
|
1888
2171
|
<span>Created by</span>
|
|
1889
2172
|
<a href="https://github.com/Arghajit47" 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'">Arghajit Singha</a>
|
|
1890
2173
|
</div>
|
|
@@ -1899,6 +2182,21 @@ function generateHTML(reportData, trendData = null) {
|
|
|
1899
2182
|
return (ms / 1000).toFixed(1) + "s";
|
|
1900
2183
|
}
|
|
1901
2184
|
}
|
|
2185
|
+
function copyLogContent(elementId, button) {
|
|
2186
|
+
const logElement = document.getElementById(elementId);
|
|
2187
|
+
if (!logElement) {
|
|
2188
|
+
console.error('Could not find log element with ID:', elementId);
|
|
2189
|
+
return;
|
|
2190
|
+
}
|
|
2191
|
+
navigator.clipboard.writeText(logElement.innerText).then(() => {
|
|
2192
|
+
button.textContent = 'Copied!';
|
|
2193
|
+
setTimeout(() => { button.textContent = 'Copy'; }, 2000);
|
|
2194
|
+
}).catch(err => {
|
|
2195
|
+
console.error('Failed to copy log content:', err);
|
|
2196
|
+
button.textContent = 'Failed';
|
|
2197
|
+
setTimeout(() => { button.textContent = 'Copy'; }, 2000);
|
|
2198
|
+
});
|
|
2199
|
+
}
|
|
1902
2200
|
function initializeReportInteractivity() {
|
|
1903
2201
|
const tabButtons = document.querySelectorAll('.tab-button');
|
|
1904
2202
|
const tabContents = document.querySelectorAll('.tab-content');
|
|
@@ -2061,9 +2359,9 @@ function generateHTML(reportData, trendData = null) {
|
|
|
2061
2359
|
|
|
2062
2360
|
function copyErrorToClipboard(button) {
|
|
2063
2361
|
// 1. Find the main error container, which should always be present.
|
|
2064
|
-
const errorContainer = button.closest('.
|
|
2362
|
+
const errorContainer = button.closest('.test-error-summary');
|
|
2065
2363
|
if (!errorContainer) {
|
|
2066
|
-
console.error("Could not find '.
|
|
2364
|
+
console.error("Could not find '.test-error-summary' container. The report's HTML structure might have changed.");
|
|
2067
2365
|
return;
|
|
2068
2366
|
}
|
|
2069
2367
|
|
|
@@ -2338,4 +2636,4 @@ main().catch((err) => {
|
|
|
2338
2636
|
);
|
|
2339
2637
|
console.error(err.stack);
|
|
2340
2638
|
process.exit(1);
|
|
2341
|
-
});
|
|
2639
|
+
});
|