slik-report 1.0.3 → 1.0.4

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.
Files changed (2) hide show
  1. package/dist/index.js +268 -37
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -51,13 +51,12 @@ class AggregatorReporter {
51
51
  console.warn('AggregatorReporter: JSON report not found at', this.input);
52
52
  return;
53
53
  }
54
- console.log('AggregatorReporter: JSON file found. Reading file...');
54
+ console.log('AggregatorReporter: JSON data file found. Reading file and Processing...');
55
55
  const report = JSON.parse(fs.readFileSync(this.input, 'utf8'));
56
56
  // Check if external HTML exists
57
57
  const hasExternalHtml = fs.existsSync(this.anotherHTML);
58
58
  // Ensure we use a relative path for the HTML src attribute
59
59
  const externalHtmlPath = this.anotherHTML;
60
- console.log('AggregatorReporter: JSON data read. Processing...');
61
60
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
62
61
  const historyFile = path.join(this.historyDir, `run-${timestamp}.json`);
63
62
  writeFile(historyFile, safeJson(report));
@@ -70,7 +69,7 @@ class AggregatorReporter {
70
69
  };
71
70
  const htmlContent = this.generateReportHtml(processedReport);
72
71
  writeFile(this.outputHtml, htmlContent);
73
- console.log('AggregatorReporter: Report generation successful!');
72
+ console.log('Reporter: Report generation successful!', this.outputHtml);
74
73
  }
75
74
  catch (e) {
76
75
  console.error('AggregatorReporter failed:', e);
@@ -458,6 +457,7 @@ iframe { width: 100%; height: 80vh; border: none; border-radius: 8px; box-shadow
458
457
  <li class="sidebar-menu-item"><a href="#" class="active" data-tab="overview">Overview</a></li>
459
458
  <li class="sidebar-menu-item"><a href="#" data-tab="categories">Categories</a></li>
460
459
  <li class="sidebar-menu-item"><a href="#" data-tab="suites">Suites</a></li>
460
+ <li class="sidebar-menu-item"><a href="#" data-tab="execution-view">Execution View</a></li>
461
461
  <li class="sidebar-menu-item"><a href="#" data-tab="graphs">Graphs</a></li>
462
462
  <li class="sidebar-menu-item"><a href="#" data-tab="timeline">Timeline</a></li>
463
463
  <li class="sidebar-menu-item"><a href="#" data-tab="history">Historical Runs</a></li>
@@ -509,6 +509,13 @@ iframe { width: 100%; height: 80vh; border: none; border-radius: 8px; box-shadow
509
509
  </div>
510
510
  </div>
511
511
 
512
+ <div id="execution-view" class="tab-content">
513
+ <div class="card">
514
+ <h2>Execution Flow</h2>
515
+ <div id="cy" style="width: 100%; height: 650px; background-color: var(--table-header-bg); border-radius: 8px; border: 1px solid var(--table-border);"></div>
516
+ </div>
517
+ </div>
518
+
512
519
  <div id="graphs" class="tab-content">
513
520
  <div class="card">
514
521
  <h2>Test Graphs & Trends</h2>
@@ -600,6 +607,7 @@ iframe { width: 100%; height: 80vh; border: none; border-radius: 8px; box-shadow
600
607
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
601
608
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
602
609
  <script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
610
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.26.0/cytoscape.min.js"></script>
603
611
 
604
612
  <script>
605
613
  const reportData = ${safeReportJson};
@@ -663,6 +671,7 @@ let allTests = [];
663
671
  return;
664
672
  }
665
673
 
674
+ if (targetId === 'execution-view') { renderExecutionView(); }
666
675
  if (targetId === 'graphs') { renderCharts(); }
667
676
  if (targetId === 'timeline') { renderTimeline(); }
668
677
  if (targetId === 'history') { renderHistoryGraph(); }
@@ -945,6 +954,131 @@ let allTests = [];
945
954
  }
946
955
  }
947
956
 
957
+ function renderExecutionView() {
958
+ const cyContainer = document.getElementById('cy');
959
+ if (!cyContainer || cyContainer.initialized) return;
960
+
961
+ const elements = [];
962
+ elements.push({ data: { id: 'start', label: 'START', type: 'root' } });
963
+
964
+ reportData.suites.forEach((suite, sIdx) => {
965
+ const suiteId = 'suite_' + sIdx;
966
+ elements.push({
967
+ data: {
968
+ id: suiteId,
969
+ label: suite.title,
970
+ type: 'suite'
971
+ }
972
+ });
973
+ elements.push({ data: { source: 'start', target: suiteId } });
974
+
975
+ suite.specs.forEach((spec, spIdx) => {
976
+ const test = spec.tests[0];
977
+ const testId = suiteId + '_test_' + spIdx;
978
+ const startTimeStr = test.results?.[0]?.startTime ? new Date(test.results[0].startTime).toLocaleTimeString() : 'N/A';
979
+ const endTimeStr = (test.results?.[0]?.startTime && test.duration)
980
+ ? new Date(new Date(test.results[0].startTime).getTime() + test.duration).toLocaleTimeString()
981
+ : 'N/A';
982
+
983
+ elements.push({
984
+ data: {
985
+ id: testId,
986
+ label: spec.title,
987
+ type: 'test',
988
+ status: test.status,
989
+ duration: (test.duration / 1000).toFixed(2) + 's',
990
+ start: startTimeStr,
991
+ end: endTimeStr
992
+ }
993
+ });
994
+ elements.push({ data: { source: suiteId, target: testId } });
995
+ });
996
+ });
997
+
998
+ const cy = cytoscape({
999
+ container: cyContainer,
1000
+ elements: elements,
1001
+ style: [
1002
+ {
1003
+ selector: 'node',
1004
+ style: {
1005
+ 'label': 'data(label)',
1006
+ 'background-color': '#2563EB',
1007
+ 'color': '#fff',
1008
+ 'text-valign': 'center',
1009
+ 'text-halign': 'center',
1010
+ 'font-size': '10px',
1011
+ 'width': '100px',
1012
+ 'height': '40px',
1013
+ 'shape': 'round-rectangle',
1014
+ 'text-wrap': 'wrap',
1015
+ 'text-max-width': '90px'
1016
+ }
1017
+ },
1018
+ {
1019
+ selector: 'node[type="root"]',
1020
+ style: { 'background-color': '#172B4D', 'width': '60px', 'height': '60px', 'shape': 'ellipse', 'font-weight': 'bold' }
1021
+ },
1022
+ {
1023
+ selector: 'node[type="suite"]',
1024
+ style: { 'background-color': '#6B7280', 'width': '140px', 'font-style': 'italic' }
1025
+ },
1026
+ {
1027
+ selector: 'node[status="passed"]',
1028
+ style: { 'background-color': '#4CAF50' }
1029
+ },
1030
+ {
1031
+ selector: 'node[status="failed"]',
1032
+ style: { 'background-color': '#F44336' }
1033
+ },
1034
+ {
1035
+ selector: 'node[status="skipped"]',
1036
+ style: { 'background-color': '#9E9E9E' }
1037
+ },
1038
+ {
1039
+ selector: 'edge',
1040
+ style: {
1041
+ 'width': 2,
1042
+ 'line-color': '#cbd5e1',
1043
+ 'target-arrow-color': '#cbd5e1',
1044
+ 'target-arrow-shape': 'triangle',
1045
+ 'curve-style': 'bezier'
1046
+ }
1047
+ }
1048
+ ],
1049
+ layout: {
1050
+ name: 'breadthfirst',
1051
+ directed: true,
1052
+ padding: 30,
1053
+ spacingFactor: 1.2
1054
+ }
1055
+ });
1056
+
1057
+ cy.on('tap', 'node', function(evt){
1058
+ const node = evt.target;
1059
+ const data = node.data();
1060
+ if (data.id === 'start') return;
1061
+
1062
+ document.getElementById('modalTitle').textContent = (data.type === 'suite' ? 'Suite/File: ' : 'Test Case: ') + data.label;
1063
+ modalHeader.innerHTML = '<tr><th>Property</th><th>Value</th></tr>';
1064
+
1065
+ let detailsHtml = '<tr><td>Name</td><td>' + data.label + '</td></tr>';
1066
+ if (data.type === 'test') {
1067
+ detailsHtml += '<tr><td>Status</td><td><span class="status-badge status-' + data.status + '">' + data.status + '</span></td></tr>' +
1068
+ '<tr><td>Duration</td><td>' + data.duration + '</td></tr>' +
1069
+ '<tr><td>Start Time</td><td>' + data.start + '</td></tr>' +
1070
+ '<tr><td>End Time</td><td>' + data.end + '</td></tr>';
1071
+ } else {
1072
+ detailsHtml += '<tr><td>Type</td><td>TestSuite / Source File</td></tr>';
1073
+ }
1074
+
1075
+ modalBody.innerHTML = detailsHtml;
1076
+ modal.style.display = 'block';
1077
+ });
1078
+
1079
+ cyContainer.initialized = true;
1080
+ }
1081
+
948
1082
  function renderTags() {
949
1083
  const stats = reportData.tagStats;
950
1084
  if (!stats || stats.length === 0) return;
@@ -1122,42 +1256,139 @@ let allTests = [];
1122
1256
  }
1123
1257
 
1124
1258
  function renderHistoryGraph() {
1125
- const history = reportData.history;
1126
- document.getElementById('historyRunsTitle').textContent = 'Historical Runs (Count: ' + history.length + ')';
1127
- const historyCtx = document.getElementById('historicalRunsChart');
1128
- if (history.length > 0) {
1129
- historicalRunsChart = new Chart(historyCtx, {
1130
- type: 'bar',
1131
- data: {
1132
- labels: history.map(h => h.date),
1133
- datasets: [
1134
- {
1135
- type: 'line', label: 'Pass Rate', data: history.map(h => h.passRate),
1136
- borderColor: 'rgb(255, 159, 64)', yAxisID: 'y1', tension: 0.1, pointRadius: 5, pointBackgroundColor: 'rgb(255, 159, 64)', order: 0,
1137
- },
1138
- { type: 'bar', label: 'Passed', data: history.map(h => h.passed), backgroundColor: '#4CAF50', order: 1 },
1139
- { type: 'bar', label: 'Failed', data: history.map(h => h.failed), backgroundColor: 'rgba(255, 99, 132, 0.8)', order: 1 },
1140
- { type: 'bar', label: 'Skipped', data: history.map(h => h.skipped), backgroundColor: 'rgba(158, 158, 158, 0.8)', order: 1 }
1141
- ]
1142
- },
1143
- options: {
1144
- responsive: true, maintainAspectRatio: false,
1145
- animation: {
1146
- duration: 1200,
1147
- easing: 'easeOutQuart'
1148
- },
1149
- scales: {
1150
- x: { stacked: true, },
1151
- y: { stacked: true, beginAtZero: true, title: { display: true, text: 'Test Count' } },
1152
- y1: { position: 'right', beginAtZero: true, max: 100, title: { display: true, text: 'Pass Rate (%)' }, grid: { drawOnChartArea: false } }
1153
- }
1154
- }
1155
- });
1156
- } else {
1157
- historyCtx.parentElement.innerHTML = '<div>No historical runs data available.</div>';
1158
- }
1259
+ const history = reportData.history;
1260
+ document.getElementById('historyRunsTitle').textContent =
1261
+ 'Historical Runs (Count: ' + history.length + ')';
1262
+
1263
+ const historyCtx = document.getElementById('historicalRunsChart');
1264
+
1265
+ if (!history || history.length === 0) {
1266
+ historyCtx.parentElement.innerHTML =
1267
+ '<div>No historical runs data available.</div>';
1268
+ return;
1269
+ }
1270
+
1271
+ // Destroy previous chart to avoid leaks / overlays
1272
+ if (historicalRunsChart) {
1273
+ historicalRunsChart.destroy();
1159
1274
  }
1160
1275
 
1276
+ historicalRunsChart = new Chart(historyCtx, {
1277
+ type: 'bar',
1278
+ data: {
1279
+ labels: history.map(h => h.date),
1280
+ datasets: [
1281
+ {
1282
+ type: 'line',
1283
+ label: 'Pass Rate',
1284
+ data: history.map(h => h.passRate), // assume 0–100
1285
+ yAxisID: 'y1',
1286
+ borderColor: 'rgb(255, 159, 64)',
1287
+ pointBackgroundColor: 'rgb(255, 159, 64)',
1288
+ pointRadius: 5,
1289
+ tension: 0.1,
1290
+ order: 0,
1291
+ stack: 'passrate',
1292
+ animations: {
1293
+ opacity: {
1294
+ from: 0,
1295
+ to: 1,
1296
+ duration: 800,
1297
+ easing: 'linear'
1298
+ },
1299
+ tension: {
1300
+ duration: 1200,
1301
+ easing: 'easeOutCubic',
1302
+ from: 0.4,
1303
+ to: 0.1
1304
+ }
1305
+ }
1306
+ },
1307
+ {
1308
+ label: 'Passed',
1309
+ data: history.map(h => h.passed),
1310
+ backgroundColor: '#4CAF50',
1311
+ order: 1
1312
+ },
1313
+ {
1314
+ label: 'Failed',
1315
+ data: history.map(h => h.failed),
1316
+ backgroundColor: 'rgba(255, 99, 132, 0.8)',
1317
+ order: 1
1318
+ },
1319
+ {
1320
+ label: 'Skipped',
1321
+ data: history.map(h => h.skipped),
1322
+ backgroundColor: 'rgba(158, 158, 158, 0.8)',
1323
+ order: 1
1324
+ }
1325
+ ]
1326
+ },
1327
+ options: {
1328
+ responsive: true,
1329
+ maintainAspectRatio: false,
1330
+ animation: {
1331
+ duration: 1800,
1332
+ easing: 'easeOutQuart',
1333
+ delay: (ctx) => {
1334
+ if (ctx.type === 'data') {
1335
+ return ctx.dataIndex * 120;
1336
+ }
1337
+ return 0;
1338
+ }
1339
+ },
1340
+ animations: {
1341
+ y: {
1342
+ from: 0,
1343
+ duration: 1400,
1344
+ easing: 'easeOutCubic'
1345
+ },
1346
+ opacity: {
1347
+ duration: 800,
1348
+ easing: 'linear',
1349
+ from: 0,
1350
+ to: 1
1351
+ }
1352
+ },
1353
+ plugins: {
1354
+ legend: {
1355
+ position: 'top'
1356
+ },
1357
+ tooltip: {
1358
+ mode: 'index',
1359
+ intersect: false
1360
+ }
1361
+ },
1362
+ scales: {
1363
+ x: {
1364
+ stacked: true
1365
+ },
1366
+ y: {
1367
+ stacked: true,
1368
+ beginAtZero: true,
1369
+ title: {
1370
+ display: true,
1371
+ text: 'Test Count'
1372
+ }
1373
+ },
1374
+ y1: {
1375
+ type: 'linear',
1376
+ position: 'right',
1377
+ beginAtZero: true,
1378
+ max: 100,
1379
+ title: {
1380
+ display: true,
1381
+ text: 'Pass Rate (%)'
1382
+ },
1383
+ grid: {
1384
+ drawOnChartArea: false
1385
+ }
1386
+ }
1387
+ }
1388
+ }
1389
+ });
1390
+ }
1391
+
1161
1392
  const filterPassed = document.getElementById('filterPassed');
1162
1393
  const filterFailed = document.getElementById('filterFailed');
1163
1394
  const filterSkipped = document.getElementById('filterSkipped');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slik-report",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "A powerful Playwright reporter for aggregating test results, flakiness analysis, and historical trend tracking.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",