test-reporting 3.7.0__tar.gz → 3.7.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.7.0/test_reporting.egg-info → test_reporting-3.7.2}/PKG-INFO +1 -1
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/classifier.py +47 -3
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/publisher.py +10 -6
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/templates/project.html +54 -10
- {test_reporting-3.7.0 → test_reporting-3.7.2}/setup.py +1 -1
- {test_reporting-3.7.0 → test_reporting-3.7.2/test_reporting.egg-info}/PKG-INFO +1 -1
- {test_reporting-3.7.0 → test_reporting-3.7.2}/LICENSE +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/README.md +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/__init__.py +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/cli.py +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/config.py +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/plugin.py +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/storage.py +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/templates/index.html +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/templates/run.html +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/setup.cfg +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/SOURCES.txt +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/dependency_links.txt +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/entry_points.txt +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/requires.txt +0 -0
- {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/top_level.txt +0 -0
|
@@ -16,6 +16,10 @@ class FailureClassifier:
|
|
|
16
16
|
'API_TIMEOUT': 'Soft Failure - API Timeout',
|
|
17
17
|
'ENVIRONMENT': 'Environment Issue',
|
|
18
18
|
'FLAKY': 'Flaky Test - Passed on Retry',
|
|
19
|
+
'ELEMENT_NOT_FOUND': 'Element Not Found',
|
|
20
|
+
'NAVIGATION_ERROR': 'Navigation/Page Load Error',
|
|
21
|
+
'SCRIPT_ERROR': 'JavaScript/Script Error',
|
|
22
|
+
'VALIDATION_ERROR': 'Data Validation Error',
|
|
19
23
|
'UNKNOWN': 'Unknown Error',
|
|
20
24
|
}
|
|
21
25
|
|
|
@@ -44,6 +48,9 @@ class FailureClassifier:
|
|
|
44
48
|
is_soft_fail = False
|
|
45
49
|
category = 'functional'
|
|
46
50
|
|
|
51
|
+
exception_lower = exception_message.lower()
|
|
52
|
+
exception_type_lower = exception_type.lower()
|
|
53
|
+
|
|
47
54
|
# Check if passed on retry (flaky)
|
|
48
55
|
if retry_count > 0:
|
|
49
56
|
failure_type = 'FLAKY'
|
|
@@ -51,11 +58,48 @@ class FailureClassifier:
|
|
|
51
58
|
category = 'stability'
|
|
52
59
|
|
|
53
60
|
# Check for assertion errors (hard failures)
|
|
54
|
-
elif 'AssertionError' in exception_type or 'assert' in
|
|
61
|
+
elif 'AssertionError' in exception_type or 'assert' in exception_lower:
|
|
55
62
|
failure_type = 'ASSERTION'
|
|
56
63
|
is_soft_fail = False
|
|
57
64
|
category = 'functional'
|
|
58
65
|
|
|
66
|
+
# Check for element not found errors
|
|
67
|
+
elif any(pattern in exception_lower for pattern in [
|
|
68
|
+
'element not found', 'no such element', 'locator not found',
|
|
69
|
+
'selector not found', 'element is not attached', 'element not visible',
|
|
70
|
+
'waiting for selector', 'strict mode violation'
|
|
71
|
+
]):
|
|
72
|
+
failure_type = 'ELEMENT_NOT_FOUND'
|
|
73
|
+
is_soft_fail = False
|
|
74
|
+
category = 'functional'
|
|
75
|
+
|
|
76
|
+
# Check for navigation/page load errors
|
|
77
|
+
elif any(pattern in exception_lower for pattern in [
|
|
78
|
+
'navigation', 'page.goto', 'net::err', 'failed to load',
|
|
79
|
+
'page crash', 'target closed', 'page did not load'
|
|
80
|
+
]):
|
|
81
|
+
failure_type = 'NAVIGATION_ERROR'
|
|
82
|
+
is_soft_fail = True
|
|
83
|
+
category = 'infrastructure'
|
|
84
|
+
|
|
85
|
+
# Check for JavaScript/script errors
|
|
86
|
+
elif any(pattern in exception_lower for pattern in [
|
|
87
|
+
'javascript error', 'script error', 'evaluation failed',
|
|
88
|
+
'uncaught exception', 'referenceerror', 'typeerror'
|
|
89
|
+
]) or 'Error' in exception_type and 'script' in exception_lower:
|
|
90
|
+
failure_type = 'SCRIPT_ERROR'
|
|
91
|
+
is_soft_fail = False
|
|
92
|
+
category = 'functional'
|
|
93
|
+
|
|
94
|
+
# Check for validation/data errors
|
|
95
|
+
elif any(pattern in exception_lower for pattern in [
|
|
96
|
+
'validation', 'invalid', 'expected', 'does not match',
|
|
97
|
+
'valueerror', 'keyerror', 'attributeerror'
|
|
98
|
+
]) or any(exc in exception_type for exc in ['ValueError', 'KeyError', 'AttributeError']):
|
|
99
|
+
failure_type = 'VALIDATION_ERROR'
|
|
100
|
+
is_soft_fail = False
|
|
101
|
+
category = 'functional'
|
|
102
|
+
|
|
59
103
|
# Check for UI timeouts (soft failures)
|
|
60
104
|
elif any(pattern in exception_message for pattern in ReportingConfig.SOFT_FAIL_PATTERNS):
|
|
61
105
|
failure_type = 'UI_TIMEOUT'
|
|
@@ -63,13 +107,13 @@ class FailureClassifier:
|
|
|
63
107
|
category = 'performance'
|
|
64
108
|
|
|
65
109
|
# Check for environment issues
|
|
66
|
-
elif any(pattern in
|
|
110
|
+
elif any(pattern in exception_lower for pattern in ReportingConfig.ENVIRONMENT_FAIL_PATTERNS):
|
|
67
111
|
failure_type = 'ENVIRONMENT'
|
|
68
112
|
is_soft_fail = True
|
|
69
113
|
category = 'infrastructure'
|
|
70
114
|
|
|
71
115
|
# Check for API timeouts
|
|
72
|
-
elif 'timeout' in
|
|
116
|
+
elif 'timeout' in exception_lower and duration_ms > ReportingConfig.TIMEOUT_API_MAX:
|
|
73
117
|
failure_type = 'API_TIMEOUT'
|
|
74
118
|
is_soft_fail = True
|
|
75
119
|
category = 'performance'
|
|
@@ -404,12 +404,16 @@ class Publisher:
|
|
|
404
404
|
# Real bugs (ASSERTION) need code fixes.
|
|
405
405
|
# Infrastructure issues (ENVIRONMENT, TIMEOUT) may not reflect quality.
|
|
406
406
|
type_labels = {
|
|
407
|
-
'ASSERTION':
|
|
408
|
-
'
|
|
409
|
-
'
|
|
410
|
-
'
|
|
411
|
-
'
|
|
412
|
-
'
|
|
407
|
+
'ASSERTION': 'Real bugs (assertion errors)',
|
|
408
|
+
'ELEMENT_NOT_FOUND': 'Element not found',
|
|
409
|
+
'VALIDATION_ERROR': 'Data validation errors',
|
|
410
|
+
'SCRIPT_ERROR': 'JavaScript/Script errors',
|
|
411
|
+
'UI_TIMEOUT': 'UI timeouts',
|
|
412
|
+
'API_TIMEOUT': 'API timeouts',
|
|
413
|
+
'NAVIGATION_ERROR': 'Navigation/Page load errors',
|
|
414
|
+
'ENVIRONMENT': 'Infrastructure / connection issues',
|
|
415
|
+
'FLAKY': 'Passed on retry (unstable)',
|
|
416
|
+
'UNKNOWN': 'Uncategorized',
|
|
413
417
|
}
|
|
414
418
|
failure_type_breakdown = [
|
|
415
419
|
{
|
|
@@ -280,6 +280,7 @@
|
|
|
280
280
|
border-radius: 6px; cursor: pointer; position: relative;
|
|
281
281
|
overflow: hidden; transition: transform .1s, box-shadow .1s; }
|
|
282
282
|
.treemap-cell:hover { transform: scale(1.05); z-index: 1; box-shadow: 0 4px 12px rgba(0,0,0,.3); }
|
|
283
|
+
.treemap-cell-hidden { display: none; }
|
|
283
284
|
.treemap-name { font-size: 11px; font-weight: 600; color: #fff; opacity: .9;
|
|
284
285
|
overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
285
286
|
.treemap-rate { font-size: 14px; font-weight: 800; color: #fff; }
|
|
@@ -555,15 +556,21 @@
|
|
|
555
556
|
const total = lastReg ? lastReg.total_tests || 1 : 1;
|
|
556
557
|
|
|
557
558
|
// Group suites by name to detect duplicates
|
|
559
|
+
// Strip [TAG] suffix to group reruns with original runs
|
|
558
560
|
const suiteGroups = {};
|
|
559
561
|
(reg.suites || []).forEach(function(s) {
|
|
560
|
-
|
|
561
|
-
|
|
562
|
+
// Remove [TAG] suffix like [RERUN], [RETRY], etc.
|
|
563
|
+
const baseName = s.suite.replace(/\s*\[.*?\]\s*$/, '');
|
|
564
|
+
if (!suiteGroups[baseName]) {
|
|
565
|
+
suiteGroups[baseName] = [];
|
|
562
566
|
}
|
|
563
|
-
suiteGroups[
|
|
567
|
+
suiteGroups[baseName].push(s);
|
|
564
568
|
});
|
|
565
569
|
|
|
566
|
-
|
|
570
|
+
// Sort suite names alphabetically
|
|
571
|
+
const sortedSuiteNames = Object.keys(suiteGroups).sort();
|
|
572
|
+
|
|
573
|
+
const fileRows = sortedSuiteNames.map(function(suiteName) {
|
|
567
574
|
const runs = suiteGroups[suiteName];
|
|
568
575
|
const runCount = runs.length;
|
|
569
576
|
|
|
@@ -678,6 +685,37 @@
|
|
|
678
685
|
if (runId) location.href = 'run.html?r=' + encodeURIComponent(runId) + '&p=' + encodeURIComponent(projectName);
|
|
679
686
|
}
|
|
680
687
|
|
|
688
|
+
function navigateToHistoryWithSearch(fileName) {
|
|
689
|
+
// Switch to Run History tab
|
|
690
|
+
switchTab('history');
|
|
691
|
+
// Wait for tab to render, then set search value and trigger filter
|
|
692
|
+
setTimeout(function() {
|
|
693
|
+
const searchInput = document.getElementById('historySearch');
|
|
694
|
+
if (searchInput) {
|
|
695
|
+
searchInput.value = fileName;
|
|
696
|
+
filterHistory();
|
|
697
|
+
// Scroll to the table
|
|
698
|
+
searchInput.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
699
|
+
}
|
|
700
|
+
}, 100);
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
function toggleTreemapCells(btn) {
|
|
704
|
+
const treemap = btn.previousElementSibling;
|
|
705
|
+
const hiddenCells = treemap.querySelectorAll('.treemap-cell-hidden');
|
|
706
|
+
const isExpanded = hiddenCells.length > 0 && hiddenCells[0].style.display === 'flex';
|
|
707
|
+
|
|
708
|
+
hiddenCells.forEach(function(cell) {
|
|
709
|
+
cell.style.display = isExpanded ? 'none' : 'flex';
|
|
710
|
+
});
|
|
711
|
+
|
|
712
|
+
if (isExpanded) {
|
|
713
|
+
btn.innerHTML = 'Show ' + hiddenCells.length + ' more ▼';
|
|
714
|
+
} else {
|
|
715
|
+
btn.innerHTML = 'Show less ▲';
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
681
719
|
/* ═══════════════════════════════════════════════════
|
|
682
720
|
ANALYSIS TAB
|
|
683
721
|
═══════════════════════════════════════════════════ */
|
|
@@ -725,7 +763,7 @@
|
|
|
725
763
|
'<h3>Test File Reliability (Treemap)</h3>' +
|
|
726
764
|
'<p class="section-explain">Each block = one file. Size ∝ test count. Color = pass rate. Worst files at top-left.</p>' +
|
|
727
765
|
'</div>' +
|
|
728
|
-
'<div class="section-body
|
|
766
|
+
'<div class="section-body">' +
|
|
729
767
|
renderTreemap(a.file_health || []) +
|
|
730
768
|
'</div>' +
|
|
731
769
|
'</div>' +
|
|
@@ -794,7 +832,7 @@
|
|
|
794
832
|
trend.forEach(function(d) { dataMap[d.date] = d.pass_rate; });
|
|
795
833
|
|
|
796
834
|
// Calculate 15 weeks back from today
|
|
797
|
-
const today = new Date(
|
|
835
|
+
const today = new Date();
|
|
798
836
|
const dayOfWeek = today.getDay(); // 0=Sun
|
|
799
837
|
const weeksBack = 15;
|
|
800
838
|
const totalDays = weeksBack * 7;
|
|
@@ -870,20 +908,26 @@
|
|
|
870
908
|
// Sort worst first
|
|
871
909
|
const sorted = files.slice().sort(function(a, b) { return a.pass_rate - b.pass_rate; });
|
|
872
910
|
const maxTests = Math.max.apply(null, sorted.map(function(f) { return f.total_runs || 1; }));
|
|
911
|
+
const limit = 12;
|
|
873
912
|
|
|
874
|
-
const cells = sorted.map(function(f) {
|
|
913
|
+
const cells = sorted.map(function(f, i) {
|
|
875
914
|
const ratio = (f.total_runs || 1) / maxTests;
|
|
876
915
|
const width = Math.round(80 + ratio * 160); // 80..240
|
|
877
916
|
var bg = f.pass_rate >= 95 ? '#276749' : f.pass_rate >= 80 ? '#975a16' : '#9b2c2c';
|
|
878
917
|
var bgLight = f.pass_rate >= 95 ? '#38a169' : f.pass_rate >= 80 ? '#d69e2e' : '#e53e3e';
|
|
879
|
-
const
|
|
880
|
-
|
|
918
|
+
const hiddenClass = i >= limit ? ' treemap-cell-hidden' : '';
|
|
919
|
+
const clickAttr = ' onclick="navigateToHistoryWithSearch(\'' + f.file_name + '\')" style="cursor:pointer;"';
|
|
920
|
+
return '<div class="treemap-cell' + hiddenClass + '" style="width:' + width + 'px;background:' + bgLight + ';" title="' + f.file_name + ': ' + f.pass_rate.toFixed(1) + '% pass rate (' + f.total_runs + ' runs) - Click to view all runs"' + clickAttr + '>' +
|
|
881
921
|
'<div class="treemap-rate">' + f.pass_rate.toFixed(0) + '%</div>' +
|
|
882
922
|
'<div class="treemap-name">' + f.file_name + '</div>' +
|
|
883
923
|
'</div>';
|
|
884
924
|
}).join('');
|
|
885
925
|
|
|
886
|
-
|
|
926
|
+
const expandBtn = sorted.length > limit
|
|
927
|
+
? '<div class="table-expand-btn" onclick="toggleTreemapCells(this)">Show ' + (sorted.length - limit) + ' more ▼</div>'
|
|
928
|
+
: '';
|
|
929
|
+
|
|
930
|
+
return '<div class="treemap">' + cells + '</div>' + expandBtn;
|
|
887
931
|
}
|
|
888
932
|
|
|
889
933
|
function renderFlakyTable(tests) {
|
|
@@ -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.7.
|
|
12
|
+
version='3.7.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
|
|
File without changes
|
|
File without changes
|