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.
Files changed (21) hide show
  1. {test_reporting-3.7.0/test_reporting.egg-info → test_reporting-3.7.2}/PKG-INFO +1 -1
  2. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/classifier.py +47 -3
  3. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/publisher.py +10 -6
  4. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/templates/project.html +54 -10
  5. {test_reporting-3.7.0 → test_reporting-3.7.2}/setup.py +1 -1
  6. {test_reporting-3.7.0 → test_reporting-3.7.2/test_reporting.egg-info}/PKG-INFO +1 -1
  7. {test_reporting-3.7.0 → test_reporting-3.7.2}/LICENSE +0 -0
  8. {test_reporting-3.7.0 → test_reporting-3.7.2}/README.md +0 -0
  9. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/__init__.py +0 -0
  10. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/cli.py +0 -0
  11. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/config.py +0 -0
  12. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/plugin.py +0 -0
  13. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/storage.py +0 -0
  14. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/templates/index.html +0 -0
  15. {test_reporting-3.7.0 → test_reporting-3.7.2}/reporting/templates/run.html +0 -0
  16. {test_reporting-3.7.0 → test_reporting-3.7.2}/setup.cfg +0 -0
  17. {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/SOURCES.txt +0 -0
  18. {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/dependency_links.txt +0 -0
  19. {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/entry_points.txt +0 -0
  20. {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/requires.txt +0 -0
  21. {test_reporting-3.7.0 → test_reporting-3.7.2}/test_reporting.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: test-reporting
3
- Version: 3.7.0
3
+ Version: 3.7.2
4
4
  Summary: Multi-project test reporting dashboard — collect results locally or push to S3
5
5
  Home-page: https://github.com/amahdy77/test-reporting.git
6
6
  Author: Ashfaqur Mahdy
@@ -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 exception_message.lower():
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 exception_message.lower() for pattern in ReportingConfig.ENVIRONMENT_FAIL_PATTERNS):
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 exception_message.lower() and duration_ms > ReportingConfig.TIMEOUT_API_MAX:
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': 'Real bugs (assertion errors)',
408
- 'UI_TIMEOUT': 'UI timeouts',
409
- 'API_TIMEOUT': 'API timeouts',
410
- 'ENVIRONMENT': 'Infrastructure / connection issues',
411
- 'FLAKY': 'Passed on retry (unstable)',
412
- 'UNKNOWN': 'Uncategorised',
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
- if (!suiteGroups[s.suite]) {
561
- suiteGroups[s.suite] = [];
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[s.suite].push(s);
567
+ suiteGroups[baseName].push(s);
564
568
  });
565
569
 
566
- const fileRows = Object.keys(suiteGroups).map(function(suiteName) {
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 preview" onclick="toggleSectionExpand(this)">' +
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('2026-06-07');
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 clickAttr = f.last_run_id ? ' onclick="goToRun(\'' + f.last_run_id + '\')"' : '';
880
- 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 + '>' +
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
- return '<div class="treemap">' + cells + '</div>';
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.0',
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",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: test-reporting
3
- Version: 3.7.0
3
+ Version: 3.7.2
4
4
  Summary: Multi-project test reporting dashboard — collect results locally or push to S3
5
5
  Home-page: https://github.com/amahdy77/test-reporting.git
6
6
  Author: Ashfaqur Mahdy
File without changes
File without changes
File without changes