test-reporting 3.6.0__tar.gz → 3.6.2__tar.gz
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.
- {test_reporting-3.6.0/test_reporting.egg-info → test_reporting-3.6.2}/PKG-INFO +1 -1
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/config.py +1 -1
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/publisher.py +15 -5
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/templates/index.html +2 -2
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/templates/project.html +117 -35
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/templates/run.html +4 -2
- {test_reporting-3.6.0 → test_reporting-3.6.2}/setup.py +1 -1
- {test_reporting-3.6.0 → test_reporting-3.6.2/test_reporting.egg-info}/PKG-INFO +1 -1
- {test_reporting-3.6.0 → test_reporting-3.6.2}/LICENSE +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/README.md +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/__init__.py +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/classifier.py +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/cli.py +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/plugin.py +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/reporting/storage.py +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/setup.cfg +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/test_reporting.egg-info/SOURCES.txt +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/test_reporting.egg-info/dependency_links.txt +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/test_reporting.egg-info/entry_points.txt +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/test_reporting.egg-info/requires.txt +0 -0
- {test_reporting-3.6.0 → test_reporting-3.6.2}/test_reporting.egg-info/top_level.txt +0 -0
|
@@ -102,7 +102,7 @@ class ReportingConfig:
|
|
|
102
102
|
|
|
103
103
|
@property
|
|
104
104
|
def DASHBOARD_TITLE(self):
|
|
105
|
-
return f"{self.PROJECT_NAME} - Test Dashboard"
|
|
105
|
+
return f"{self.PROJECT_NAME} - Test Result Dashboard"
|
|
106
106
|
|
|
107
107
|
def ensure_directories(self):
|
|
108
108
|
self.REPORTS_DIR.mkdir(exist_ok=True, parents=True)
|
|
@@ -493,8 +493,12 @@ class Publisher:
|
|
|
493
493
|
# Regression grouping
|
|
494
494
|
if tag not in regressions:
|
|
495
495
|
regressions[tag] = {'tag': tag, 'date': tag[:10], 'suites': [],
|
|
496
|
-
'total_tests': 0, 'passed': 0, 'failed': 0, 'pass_rate': 0
|
|
496
|
+
'total_tests': 0, 'passed': 0, 'failed': 0, 'pass_rate': 0,
|
|
497
|
+
'timestamp': ''}
|
|
497
498
|
reg = regressions[tag]
|
|
499
|
+
# Track the latest timestamp for this regression
|
|
500
|
+
if ts > reg.get('timestamp', ''):
|
|
501
|
+
reg['timestamp'] = ts
|
|
498
502
|
existing = next((s for s in reg['suites'] if s['suite'] == suite), None)
|
|
499
503
|
if existing:
|
|
500
504
|
if ts > existing.get('timestamp', ''):
|
|
@@ -525,8 +529,10 @@ class Publisher:
|
|
|
525
529
|
s['runs'] = sorted(s['runs'], key=lambda r: r['timestamp'], reverse=True)
|
|
526
530
|
s['runs'] = s['runs'][:self.config.DASHBOARD_MAX_RUNS_PER_SUITE]
|
|
527
531
|
|
|
528
|
-
# Sort regressions newest first
|
|
529
|
-
regressions = dict(sorted(regressions.items(),
|
|
532
|
+
# Sort regressions by timestamp (newest first), fallback to tag name
|
|
533
|
+
regressions = dict(sorted(regressions.items(),
|
|
534
|
+
key=lambda x: (x[1].get('timestamp', ''), x[0]),
|
|
535
|
+
reverse=True))
|
|
530
536
|
|
|
531
537
|
# Overall health from latest regression
|
|
532
538
|
last_reg = next(iter(regressions.values()), None) if regressions else None
|
|
@@ -745,7 +751,9 @@ class Publisher:
|
|
|
745
751
|
s3.put_object(Bucket=bucket, Key=dest_key, Body=f.read(),
|
|
746
752
|
ContentType='image/png')
|
|
747
753
|
print(f"[Reporting] ✓ Uploaded screenshot: {src.name}")
|
|
748
|
-
|
|
754
|
+
# Use full S3 URL so it works when dashboard is served from S3
|
|
755
|
+
s3_url = f"https://{bucket}.s3.{self.config.S3_REGION}.amazonaws.com/{dest_key}"
|
|
756
|
+
test['screenshot_path'] = s3_url
|
|
749
757
|
artifact_count += 1
|
|
750
758
|
else:
|
|
751
759
|
print(f"[Reporting] ✗ Screenshot not found: {src}")
|
|
@@ -761,7 +769,9 @@ class Publisher:
|
|
|
761
769
|
s3.put_object(Bucket=bucket, Key=dest_key, Body=f.read(),
|
|
762
770
|
ContentType='application/zip')
|
|
763
771
|
print(f"[Reporting] ✓ Uploaded trace: {src.name}")
|
|
764
|
-
|
|
772
|
+
# Use full S3 URL so it works when dashboard is served from S3
|
|
773
|
+
s3_url = f"https://{bucket}.s3.{self.config.S3_REGION}.amazonaws.com/{dest_key}"
|
|
774
|
+
test['trace_path'] = s3_url
|
|
765
775
|
artifact_count += 1
|
|
766
776
|
else:
|
|
767
777
|
print(f"[Reporting] ✗ Trace not found: {src}")
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
|
7
7
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
8
8
|
<meta http-equiv="Expires" content="0" />
|
|
9
|
-
<title>Test Dashboard</title>
|
|
9
|
+
<title>Test Result Dashboard</title>
|
|
10
10
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
11
11
|
<style>
|
|
12
12
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
@@ -197,7 +197,7 @@
|
|
|
197
197
|
<body>
|
|
198
198
|
|
|
199
199
|
<div class="topbar" id="topbar">
|
|
200
|
-
<div class="topbar-brand">Test <span>Dashboard</span></div>
|
|
200
|
+
<div class="topbar-brand">Test Result <span>Dashboard</span></div>
|
|
201
201
|
<div class="topbar-right">
|
|
202
202
|
<div class="topbar-meta" id="lastUpdated"></div>
|
|
203
203
|
<button class="theme-toggle" id="themeToggle" title="Toggle dark mode">🌙</button>
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
|
7
7
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
8
8
|
<meta http-equiv="Expires" content="0" />
|
|
9
|
-
<title>Project - Test Dashboard</title>
|
|
9
|
+
<title>Project - Test Result Dashboard</title>
|
|
10
10
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
|
|
11
11
|
<style>
|
|
12
12
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
@@ -273,9 +273,9 @@
|
|
|
273
273
|
.treemap { display: flex; flex-wrap: wrap; gap: 8px; }
|
|
274
274
|
.treemap-cell { height: 80px; display: flex; flex-direction: column;
|
|
275
275
|
justify-content: flex-end; padding: 8px;
|
|
276
|
-
border-radius: 6px; cursor:
|
|
277
|
-
overflow: hidden; transition: transform .1s; }
|
|
278
|
-
.treemap-cell:hover { transform: scale(1.
|
|
276
|
+
border-radius: 6px; cursor: pointer; position: relative;
|
|
277
|
+
overflow: hidden; transition: transform .1s, box-shadow .1s; }
|
|
278
|
+
.treemap-cell:hover { transform: scale(1.05); z-index: 1; box-shadow: 0 4px 12px rgba(0,0,0,.3); }
|
|
279
279
|
.treemap-name { font-size: 11px; font-weight: 600; color: #fff; opacity: .9;
|
|
280
280
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
281
281
|
.treemap-rate { font-size: 14px; font-weight: 800; color: #fff; }
|
|
@@ -286,6 +286,13 @@
|
|
|
286
286
|
/* ── History table ── */
|
|
287
287
|
.hist-table a { color: var(--blue); text-decoration: none; font-weight: 500; }
|
|
288
288
|
.hist-table a:hover { text-decoration: underline; }
|
|
289
|
+
.hist-table th.sortable { cursor: pointer; user-select: none; position: relative; }
|
|
290
|
+
.hist-table th.sortable:hover { background: var(--bg); }
|
|
291
|
+
.hist-table th.sortable::after { content: '⇅'; margin-left: 6px; opacity: 0.3; font-size: 10px; }
|
|
292
|
+
.hist-table th.sortable.asc::after { content: '↑'; opacity: 1; }
|
|
293
|
+
.hist-table th.sortable.desc::after { content: '↓'; opacity: 1; }
|
|
294
|
+
.hist-table tbody tr { cursor: pointer; }
|
|
295
|
+
.hist-table tbody tr:hover { background: var(--blue-bg); }
|
|
289
296
|
|
|
290
297
|
/* ── Sparkline cell ── */
|
|
291
298
|
.sparkline-cell canvas { display: block; }
|
|
@@ -385,6 +392,14 @@
|
|
|
385
392
|
return new Date(iso).toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
386
393
|
}
|
|
387
394
|
|
|
395
|
+
function fmtDateTime(iso) {
|
|
396
|
+
if (!iso) return '-';
|
|
397
|
+
const date = new Date(iso);
|
|
398
|
+
const dateStr = date.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' });
|
|
399
|
+
const timeStr = date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', hour12: false });
|
|
400
|
+
return dateStr + ' ' + timeStr;
|
|
401
|
+
}
|
|
402
|
+
|
|
388
403
|
function fmtDur(s) {
|
|
389
404
|
if (!s || s === 0) return '-';
|
|
390
405
|
if (s >= 60) return Math.floor(s / 60) + 'm ' + Math.floor(s % 60) + 's';
|
|
@@ -546,10 +561,12 @@
|
|
|
546
561
|
}).join('');
|
|
547
562
|
|
|
548
563
|
const isOpen = i === 0;
|
|
564
|
+
const regDateTime = reg.timestamp ? fmtDateTime(reg.timestamp) : '';
|
|
565
|
+
const regTitle = regDateTime ? reg.tag + ' · ' + regDateTime : reg.tag;
|
|
549
566
|
return '<div class="regression">' +
|
|
550
567
|
'<div class="reg-head" onclick="toggleReg(this)">' +
|
|
551
568
|
'<div>' +
|
|
552
|
-
'<div class="reg-title">' +
|
|
569
|
+
'<div class="reg-title">' + regTitle + '</div>' +
|
|
553
570
|
'<div class="reg-sub">' + reg.suites.length + ' test ' + plural(reg.suites.length, 'file') + ' · ' + reg.total_tests + ' tests</div>' +
|
|
554
571
|
'</div>' +
|
|
555
572
|
'<div class="reg-right">' +
|
|
@@ -805,7 +822,8 @@
|
|
|
805
822
|
const width = Math.round(80 + ratio * 160); // 80..240
|
|
806
823
|
var bg = f.pass_rate >= 95 ? '#276749' : f.pass_rate >= 80 ? '#975a16' : '#9b2c2c';
|
|
807
824
|
var bgLight = f.pass_rate >= 95 ? '#38a169' : f.pass_rate >= 80 ? '#d69e2e' : '#e53e3e';
|
|
808
|
-
|
|
825
|
+
const clickAttr = f.last_run_id ? ' onclick="goToRun(\'' + f.last_run_id + '\')"' : '';
|
|
826
|
+
return '<div class="treemap-cell" style="width:' + width + 'px;background:' + bgLight + ';" title="' + f.file_name + ': ' + f.pass_rate.toFixed(1) + '% pass rate (' + f.total_runs + ' runs)' + (f.last_run_id ? ' - Click to view latest run' : '') + '"' + clickAttr + '>' +
|
|
809
827
|
'<div class="treemap-rate">' + f.pass_rate.toFixed(0) + '%</div>' +
|
|
810
828
|
'<div class="treemap-name">' + f.file_name + '</div>' +
|
|
811
829
|
'</div>';
|
|
@@ -895,6 +913,9 @@
|
|
|
895
913
|
HISTORY TAB
|
|
896
914
|
═══════════════════════════════════════════════════ */
|
|
897
915
|
|
|
916
|
+
// Global state for history table sorting
|
|
917
|
+
var historyData = { runs: [], fileTrends: {}, sortColumn: 'timestamp', sortDir: 'desc' };
|
|
918
|
+
|
|
898
919
|
function renderHistory(suites) {
|
|
899
920
|
if (!suites || typeof suites !== 'object') return '<p class="empty-section">No run history yet.</p>';
|
|
900
921
|
const names = Object.keys(suites);
|
|
@@ -917,12 +938,88 @@
|
|
|
917
938
|
|
|
918
939
|
if (recentRuns.length === 0) return '<p class="empty-section">No runs yet.</p>';
|
|
919
940
|
|
|
941
|
+
// Store data globally for sorting
|
|
942
|
+
historyData.runs = recentRuns;
|
|
943
|
+
historyData.fileTrends = fileTrends;
|
|
944
|
+
historyData.sortColumn = 'timestamp';
|
|
945
|
+
historyData.sortDir = 'desc';
|
|
946
|
+
|
|
947
|
+
return '<div class="section">' +
|
|
948
|
+
'<div class="section-head">' +
|
|
949
|
+
'<h3>All Test File Runs</h3>' +
|
|
950
|
+
'<span style="font-size:13px;color:var(--muted)">' + recentRuns.length + ' recent runs across ' + names.length + ' test ' + plural(names.length, 'file') + '</span>' +
|
|
951
|
+
'</div>' +
|
|
952
|
+
'<div class="section-body-flush">' +
|
|
953
|
+
'<table class="hist-table" id="historyTable">' +
|
|
954
|
+
'<thead><tr>' +
|
|
955
|
+
'<th class="sortable desc" style="width:90px" onclick="sortHistory(\'timestamp\')">Date</th>' +
|
|
956
|
+
'<th style="width:60px">Time</th>' +
|
|
957
|
+
'<th class="sortable" onclick="sortHistory(\'file_name\')">Test File</th>' +
|
|
958
|
+
'<th class="sortable" style="width:100px" onclick="sortHistory(\'regression_tag\')">Tag</th>' +
|
|
959
|
+
'<th class="sortable" style="width:80px" onclick="sortHistory(\'pass_rate\')">Pass Rate</th>' +
|
|
960
|
+
'<th class="sortable" style="width:60px" onclick="sortHistory(\'total_tests\')">Tests</th>' +
|
|
961
|
+
'<th class="sortable" style="width:60px" onclick="sortHistory(\'failed\')">Failed</th>' +
|
|
962
|
+
'<th class="sortable" style="width:80px" onclick="sortHistory(\'duration_seconds\')">Duration</th>' +
|
|
963
|
+
'<th style="width:90px">Trend</th>' +
|
|
964
|
+
'</tr></thead>' +
|
|
965
|
+
'<tbody id="historyTableBody"></tbody>' +
|
|
966
|
+
'</table>' +
|
|
967
|
+
'</div>' +
|
|
968
|
+
'</div>';
|
|
969
|
+
}
|
|
970
|
+
|
|
971
|
+
function sortHistory(column) {
|
|
972
|
+
// Toggle direction if same column, otherwise default to desc
|
|
973
|
+
if (historyData.sortColumn === column) {
|
|
974
|
+
historyData.sortDir = historyData.sortDir === 'asc' ? 'desc' : 'asc';
|
|
975
|
+
} else {
|
|
976
|
+
historyData.sortColumn = column;
|
|
977
|
+
historyData.sortDir = column === 'timestamp' ? 'desc' : 'asc';
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// Sort the data
|
|
981
|
+
historyData.runs.sort(function(a, b) {
|
|
982
|
+
var aVal = a[column];
|
|
983
|
+
var bVal = b[column];
|
|
984
|
+
|
|
985
|
+
// Handle null/undefined
|
|
986
|
+
if (aVal == null) aVal = '';
|
|
987
|
+
if (bVal == null) bVal = '';
|
|
988
|
+
|
|
989
|
+
var cmp = 0;
|
|
990
|
+
if (typeof aVal === 'number' && typeof bVal === 'number') {
|
|
991
|
+
cmp = aVal - bVal;
|
|
992
|
+
} else {
|
|
993
|
+
cmp = String(aVal).localeCompare(String(bVal));
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
return historyData.sortDir === 'asc' ? cmp : -cmp;
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
// Update header classes
|
|
1000
|
+
var headers = document.querySelectorAll('#historyTable th.sortable');
|
|
1001
|
+
headers.forEach(function(th) {
|
|
1002
|
+
th.classList.remove('asc', 'desc');
|
|
1003
|
+
});
|
|
1004
|
+
var activeHeader = Array.from(headers).find(function(th) {
|
|
1005
|
+
return th.textContent.toLowerCase().includes(column.replace('_', ' '));
|
|
1006
|
+
});
|
|
1007
|
+
if (activeHeader) {
|
|
1008
|
+
activeHeader.classList.add(historyData.sortDir);
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// Re-render table body
|
|
1012
|
+
renderHistoryTableBody();
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
function renderHistoryTableBody() {
|
|
920
1016
|
const sparklineIds = {};
|
|
921
|
-
const rows =
|
|
1017
|
+
const rows = historyData.runs.map(function(run, i) {
|
|
922
1018
|
const pc = pillClass(run.pass_rate);
|
|
923
1019
|
const sparkId = 'hist-spark-' + i;
|
|
924
|
-
|
|
925
|
-
|
|
1020
|
+
const runUrl = 'run.html?r=' + encodeURIComponent(run.run_id) + '&p=' + encodeURIComponent(projectName);
|
|
1021
|
+
sparklineIds[sparkId] = historyData.fileTrends[run.file_name] || [];
|
|
1022
|
+
return '<tr onclick="window.location.href=\'' + runUrl + '\'">' +
|
|
926
1023
|
'<td style="font-size:12px">' + fmtDate(run.timestamp) + '</td>' +
|
|
927
1024
|
'<td style="font-size:12px;color:var(--muted)">' + fmtTime(run.timestamp) + '</td>' +
|
|
928
1025
|
'<td><div style="font-weight:600;font-size:13px;max-width:300px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap" title="' + run.file_name + '">' + run.file_name + '</div></td>' +
|
|
@@ -932,10 +1029,12 @@
|
|
|
932
1029
|
'<td style="color:var(--red);text-align:center">' + run.failed + '</td>' +
|
|
933
1030
|
'<td style="color:var(--muted);font-size:12px">' + fmtDur(run.duration_seconds) + '</td>' +
|
|
934
1031
|
'<td class="sparkline-cell"><canvas id="' + sparkId + '" width="80" height="30"></canvas></td>' +
|
|
935
|
-
'<td style="text-align:center"><a href="run.html?r=' + encodeURIComponent(run.run_id) + '&p=' + encodeURIComponent(projectName) + '" style="white-space:nowrap">View →</a></td>' +
|
|
936
1032
|
'</tr>';
|
|
937
1033
|
}).join('');
|
|
938
1034
|
|
|
1035
|
+
document.getElementById('historyTableBody').innerHTML = rows;
|
|
1036
|
+
|
|
1037
|
+
// Draw sparklines after DOM update
|
|
939
1038
|
setTimeout(function() {
|
|
940
1039
|
for (var sid in sparklineIds) {
|
|
941
1040
|
var canvas = document.getElementById(sid);
|
|
@@ -956,30 +1055,6 @@
|
|
|
956
1055
|
});
|
|
957
1056
|
}
|
|
958
1057
|
}, 60);
|
|
959
|
-
|
|
960
|
-
return '<div class="section">' +
|
|
961
|
-
'<div class="section-head">' +
|
|
962
|
-
'<h3>All Test File Runs</h3>' +
|
|
963
|
-
'<span style="font-size:13px;color:var(--muted)">' + recentRuns.length + ' recent runs across ' + names.length + ' test ' + plural(names.length, 'file') + '</span>' +
|
|
964
|
-
'</div>' +
|
|
965
|
-
'<div class="section-body-flush">' +
|
|
966
|
-
'<table class="hist-table">' +
|
|
967
|
-
'<thead><tr>' +
|
|
968
|
-
'<th style="width:90px">Date</th>' +
|
|
969
|
-
'<th style="width:60px">Time</th>' +
|
|
970
|
-
'<th>Test File</th>' +
|
|
971
|
-
'<th style="width:100px">Tag</th>' +
|
|
972
|
-
'<th style="width:80px">Pass Rate</th>' +
|
|
973
|
-
'<th style="width:60px">Tests</th>' +
|
|
974
|
-
'<th style="width:60px">Failed</th>' +
|
|
975
|
-
'<th style="width:80px">Duration</th>' +
|
|
976
|
-
'<th style="width:90px">Trend</th>' +
|
|
977
|
-
'<th style="width:70px"></th>' +
|
|
978
|
-
'</tr></thead>' +
|
|
979
|
-
'<tbody>' + rows + '</tbody>' +
|
|
980
|
-
'</table>' +
|
|
981
|
-
'</div>' +
|
|
982
|
-
'</div>';
|
|
983
1058
|
}
|
|
984
1059
|
|
|
985
1060
|
/* ═══════════════════════════════════════════════════
|
|
@@ -1142,7 +1217,9 @@
|
|
|
1142
1217
|
}
|
|
1143
1218
|
|
|
1144
1219
|
document.getElementById('pageTitle').textContent = proj.name;
|
|
1145
|
-
document.getElementById('lastRun')
|
|
1220
|
+
const lastRunEl = document.getElementById('lastRun');
|
|
1221
|
+
lastRunEl.textContent = 'Last run ' + relTime(proj.last_run);
|
|
1222
|
+
lastRunEl.title = 'All times shown in your local timezone';
|
|
1146
1223
|
document.title = proj.name + ' - Test Dashboard';
|
|
1147
1224
|
|
|
1148
1225
|
const analytics = proj.analytics || {};
|
|
@@ -1166,6 +1243,11 @@
|
|
|
1166
1243
|
if (totalEl) animateCounter(totalEl, lastReg ? lastReg.total_tests : 0, 0);
|
|
1167
1244
|
if (failEl) animateCounter(failEl, lastReg ? lastReg.failed : 0, 0);
|
|
1168
1245
|
|
|
1246
|
+
// Render history table body if data exists
|
|
1247
|
+
if (historyData.runs.length > 0) {
|
|
1248
|
+
renderHistoryTableBody();
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1169
1251
|
// Draw charts after DOM settles
|
|
1170
1252
|
setTimeout(function() { drawCharts(proj); }, 60);
|
|
1171
1253
|
});
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate" />
|
|
7
7
|
<meta http-equiv="Pragma" content="no-cache" />
|
|
8
8
|
<meta http-equiv="Expires" content="0" />
|
|
9
|
-
<title>Run Details - Test Dashboard</title>
|
|
9
|
+
<title>Run Details - Test Result Dashboard</title>
|
|
10
10
|
<style>
|
|
11
11
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
12
12
|
|
|
@@ -413,7 +413,9 @@
|
|
|
413
413
|
document.title = (run.suite_name || 'Run') + ' - Test Dashboard';
|
|
414
414
|
document.getElementById('pageTitle').textContent =
|
|
415
415
|
(run.suite_name || 'Run') + ' · ' + (run.project_name || '');
|
|
416
|
-
document.getElementById('runTime')
|
|
416
|
+
var runTimeEl = document.getElementById('runTime');
|
|
417
|
+
runTimeEl.textContent = fmtDate(run.timestamp);
|
|
418
|
+
runTimeEl.title = 'Time shown in your local timezone';
|
|
417
419
|
|
|
418
420
|
var backHref = projectName
|
|
419
421
|
? 'project.html?p=' + encodeURIComponent(projectName)
|
|
@@ -9,7 +9,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name='test-reporting',
|
|
12
|
-
version='3.6.
|
|
12
|
+
version='3.6.2',
|
|
13
13
|
description='Multi-project test reporting dashboard — collect results locally or push to S3',
|
|
14
14
|
long_description=long_description,
|
|
15
15
|
long_description_content_type="text/markdown",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|