test-report-vibes 0.2.0__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.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oscar Barrios Torrero
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,167 @@
1
+ Metadata-Version: 2.4
2
+ Name: test-report-vibes
3
+ Version: 0.2.0
4
+ Summary: Deterministic HTML summaries of Cucumber test reports
5
+ Author-email: Oscar Barrios Torrero <srbarrios@gmail.com>
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/srbarrios/test-report-vibes
8
+ Project-URL: Issues, https://github.com/srbarrios/test-report-vibes/issues
9
+ Keywords: cucumber,testing,report,html
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Topic :: Software Development :: Testing
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: click>=8.1.0
22
+ Requires-Dist: pydantic>=2.0.0
23
+ Requires-Dist: jinja2>=3.1.0
24
+ Requires-Dist: rich>=13.0.0
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
27
+ Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
28
+ Requires-Dist: black>=24.0.0; extra == "dev"
29
+ Requires-Dist: ruff>=0.3.0; extra == "dev"
30
+ Dynamic: license-file
31
+
32
+ # Test Report Vibes
33
+
34
+ <img width="150" src="https://github.com/user-attachments/assets/2d347d22-b88e-472f-8416-595caaed4524" />
35
+
36
+
37
+ **Deterministic HTML summaries of Cucumber test reports.**
38
+
39
+ `test-report-vibes` turns Cucumber JSON output into a focused, self-contained HTML report so you can quickly review what failed and why.
40
+
41
+ ## What it does
42
+
43
+ - Filters report data to failing, undefined, and pending steps
44
+ - Keeps scenario context (all steps), plus optional screenshots from embeddings/hooks
45
+ - Groups issues by feature and includes pass/fail stats across the full run
46
+ - Builds a deterministic executive summary (no external AI/LLM calls)
47
+ - Optionally classifies failing scenarios by tags such as `@new_issue` and `@flaky`
48
+
49
+ ## Installation
50
+
51
+ From source:
52
+
53
+ ```bash
54
+ git clone https://github.com/srbarrios/test-report-vibes.git
55
+ cd test-report-vibes
56
+ pip install -e .
57
+ ```
58
+
59
+ ## Quick start
60
+
61
+ ```bash
62
+ test-report-vibes examples/sample_report_with_classifiers.json
63
+ ```
64
+
65
+ This creates `examples/sample_report_with_classifiers.html`.
66
+
67
+ ## CLI usage
68
+
69
+ ```text
70
+ test-report-vibes [OPTIONS] INPUT_FILE
71
+
72
+ Arguments:
73
+ INPUT_FILE Path to Cucumber JSON report [required]
74
+
75
+ Options:
76
+ -o, --output PATH Output HTML file path (default: INPUT_FILE.html)
77
+ -v, --verbose Verbose output with detailed exception trace on errors
78
+ --no-classify Skip the tag-based classification section
79
+ --help Show this message and exit
80
+ ```
81
+
82
+ Examples:
83
+
84
+ ```bash
85
+ # Default output path: INPUT_FILE.html
86
+ test-report-vibes cucumber-report.json
87
+
88
+ # Custom output file
89
+ test-report-vibes cucumber-report.json -o report-summary.html
90
+
91
+ # Disable tag-based classification section
92
+ test-report-vibes cucumber-report.json --no-classify
93
+
94
+ # Run as module
95
+ python -m test_report_vibes cucumber-report.json
96
+ ```
97
+
98
+ ## Input format
99
+
100
+ The tool expects standard Cucumber JSON (root array of features). At minimum, each feature should include:
101
+
102
+ - `uri`, `id`, `name`, `keyword`, `elements`
103
+
104
+ Steps support these statuses:
105
+
106
+ - `passed`, `failed`, `skipped`, `pending`, `undefined`
107
+
108
+ Screenshots are supported through base64 embeddings with image mime types (for example `image/png`) on steps and hooks.
109
+
110
+ ## Output report highlights
111
+
112
+ Generated HTML includes:
113
+
114
+ - Overall run dashboard (passed/failed/skipped feature percentages)
115
+ - Executive summary cards such as:
116
+ - Most impacted features
117
+ - Recurring normalized error patterns
118
+ - Top error types
119
+ - Framework gaps (undefined/pending steps)
120
+ - Slowest failing scenarios and steps
121
+ - Failing features with collapsible scenario details
122
+ - Full step context including status, duration, location, and error text
123
+ - Embedded screenshots when present
124
+ - Optional "Classified features" section from tag-based mapping
125
+
126
+ ## Tag-based classification
127
+
128
+ When classification is enabled (default), statuses are derived from tags in this priority order:
129
+
130
+ - `@new_issue` -> `New and reported`
131
+ - `@under_debugging` -> `Debugging`
132
+ - `@bug_reported` -> `Bug reported`
133
+ - `@test_issue` -> `Test Framework issue`
134
+ - `@flaky` -> `Flaky Test`
135
+ - no match -> `Not reported`
136
+
137
+ ## Sample Report
138
+
139
+ Check out a live example of the generated report:
140
+
141
+ [View Sample Report](https://srbarrios.github.io/test-report-vibes/examples/sample_report_with_classifiers.html)
142
+
143
+ ## Development
144
+
145
+ Install dev dependencies:
146
+
147
+ ```bash
148
+ pip install -e ".[dev]"
149
+ ```
150
+
151
+ Run tests:
152
+
153
+ ```bash
154
+ pytest
155
+ ```
156
+
157
+ Format and lint:
158
+
159
+ ```bash
160
+ black src/
161
+ ruff check src/
162
+ ```
163
+
164
+ ## License
165
+
166
+ This project is licensed under the MIT License. See `LICENSE` for details.
167
+
@@ -0,0 +1,136 @@
1
+ # Test Report Vibes
2
+
3
+ <img width="150" src="https://github.com/user-attachments/assets/2d347d22-b88e-472f-8416-595caaed4524" />
4
+
5
+
6
+ **Deterministic HTML summaries of Cucumber test reports.**
7
+
8
+ `test-report-vibes` turns Cucumber JSON output into a focused, self-contained HTML report so you can quickly review what failed and why.
9
+
10
+ ## What it does
11
+
12
+ - Filters report data to failing, undefined, and pending steps
13
+ - Keeps scenario context (all steps), plus optional screenshots from embeddings/hooks
14
+ - Groups issues by feature and includes pass/fail stats across the full run
15
+ - Builds a deterministic executive summary (no external AI/LLM calls)
16
+ - Optionally classifies failing scenarios by tags such as `@new_issue` and `@flaky`
17
+
18
+ ## Installation
19
+
20
+ From source:
21
+
22
+ ```bash
23
+ git clone https://github.com/srbarrios/test-report-vibes.git
24
+ cd test-report-vibes
25
+ pip install -e .
26
+ ```
27
+
28
+ ## Quick start
29
+
30
+ ```bash
31
+ test-report-vibes examples/sample_report_with_classifiers.json
32
+ ```
33
+
34
+ This creates `examples/sample_report_with_classifiers.html`.
35
+
36
+ ## CLI usage
37
+
38
+ ```text
39
+ test-report-vibes [OPTIONS] INPUT_FILE
40
+
41
+ Arguments:
42
+ INPUT_FILE Path to Cucumber JSON report [required]
43
+
44
+ Options:
45
+ -o, --output PATH Output HTML file path (default: INPUT_FILE.html)
46
+ -v, --verbose Verbose output with detailed exception trace on errors
47
+ --no-classify Skip the tag-based classification section
48
+ --help Show this message and exit
49
+ ```
50
+
51
+ Examples:
52
+
53
+ ```bash
54
+ # Default output path: INPUT_FILE.html
55
+ test-report-vibes cucumber-report.json
56
+
57
+ # Custom output file
58
+ test-report-vibes cucumber-report.json -o report-summary.html
59
+
60
+ # Disable tag-based classification section
61
+ test-report-vibes cucumber-report.json --no-classify
62
+
63
+ # Run as module
64
+ python -m test_report_vibes cucumber-report.json
65
+ ```
66
+
67
+ ## Input format
68
+
69
+ The tool expects standard Cucumber JSON (root array of features). At minimum, each feature should include:
70
+
71
+ - `uri`, `id`, `name`, `keyword`, `elements`
72
+
73
+ Steps support these statuses:
74
+
75
+ - `passed`, `failed`, `skipped`, `pending`, `undefined`
76
+
77
+ Screenshots are supported through base64 embeddings with image mime types (for example `image/png`) on steps and hooks.
78
+
79
+ ## Output report highlights
80
+
81
+ Generated HTML includes:
82
+
83
+ - Overall run dashboard (passed/failed/skipped feature percentages)
84
+ - Executive summary cards such as:
85
+ - Most impacted features
86
+ - Recurring normalized error patterns
87
+ - Top error types
88
+ - Framework gaps (undefined/pending steps)
89
+ - Slowest failing scenarios and steps
90
+ - Failing features with collapsible scenario details
91
+ - Full step context including status, duration, location, and error text
92
+ - Embedded screenshots when present
93
+ - Optional "Classified features" section from tag-based mapping
94
+
95
+ ## Tag-based classification
96
+
97
+ When classification is enabled (default), statuses are derived from tags in this priority order:
98
+
99
+ - `@new_issue` -> `New and reported`
100
+ - `@under_debugging` -> `Debugging`
101
+ - `@bug_reported` -> `Bug reported`
102
+ - `@test_issue` -> `Test Framework issue`
103
+ - `@flaky` -> `Flaky Test`
104
+ - no match -> `Not reported`
105
+
106
+ ## Sample Report
107
+
108
+ Check out a live example of the generated report:
109
+
110
+ [View Sample Report](https://srbarrios.github.io/test-report-vibes/examples/sample_report_with_classifiers.html)
111
+
112
+ ## Development
113
+
114
+ Install dev dependencies:
115
+
116
+ ```bash
117
+ pip install -e ".[dev]"
118
+ ```
119
+
120
+ Run tests:
121
+
122
+ ```bash
123
+ pytest
124
+ ```
125
+
126
+ Format and lint:
127
+
128
+ ```bash
129
+ black src/
130
+ ruff check src/
131
+ ```
132
+
133
+ ## License
134
+
135
+ This project is licensed under the MIT License. See `LICENSE` for details.
136
+
@@ -0,0 +1,67 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "test-report-vibes"
7
+ version = "0.2.0"
8
+ description = "Deterministic HTML summaries of Cucumber test reports"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = {text = "MIT"}
12
+ authors = [
13
+ {name = "Oscar Barrios Torrero", email = "srbarrios@gmail.com"}
14
+ ]
15
+ keywords = ["cucumber", "testing", "report", "html"]
16
+ classifiers = [
17
+ "Development Status :: 3 - Alpha",
18
+ "Intended Audience :: Developers",
19
+ "Topic :: Software Development :: Testing",
20
+ "License :: OSI Approved :: MIT License",
21
+ "Programming Language :: Python :: 3",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ ]
26
+
27
+ dependencies = [
28
+ "click>=8.1.0",
29
+ "pydantic>=2.0.0",
30
+ "jinja2>=3.1.0",
31
+ "rich>=13.0.0",
32
+ ]
33
+
34
+ [project.optional-dependencies]
35
+ dev = [
36
+ "pytest>=8.0.0",
37
+ "pytest-cov>=4.1.0",
38
+ "black>=24.0.0",
39
+ "ruff>=0.3.0",
40
+ ]
41
+
42
+ [project.scripts]
43
+ test-report-vibes = "test_report_vibes.cli:main"
44
+
45
+ [project.urls]
46
+ Homepage = "https://github.com/srbarrios/test-report-vibes"
47
+ Issues = "https://github.com/srbarrios/test-report-vibes/issues"
48
+
49
+ [tool.setuptools.packages.find]
50
+ where = ["src"]
51
+
52
+ [tool.setuptools]
53
+ license-files = ["LICENSE"]
54
+
55
+ [tool.black]
56
+ line-length = 100
57
+ target-version = ["py310", "py311", "py312"]
58
+
59
+ [tool.ruff]
60
+ line-length = 100
61
+ target-version = "py310"
62
+
63
+ [tool.pytest.ini_options]
64
+ testpaths = ["tests"]
65
+ python_files = ["test_*.py"]
66
+ python_classes = ["Test*"]
67
+ python_functions = ["test_*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,26 @@
1
+ """Test Report Vibes - deterministic HTML summaries of Cucumber test reports."""
2
+
3
+ __version__ = "0.2.0"
4
+ __author__ = "Test Report Vibes Contributors"
5
+ __description__ = "Deterministic HTML summaries of Cucumber test reports"
6
+
7
+ from .parser import parse_cucumber_json
8
+ from .filter import filter_issues, calculate_summary_stats, group_issues_by_feature
9
+ from .html_generator import generate_html_report, build_default_executive_summary
10
+ from .classifier import classify_features, build_classification_summary_html
11
+ from .models import Feature, Scenario, Step, FilteredIssue
12
+
13
+ __all__ = [
14
+ "parse_cucumber_json",
15
+ "filter_issues",
16
+ "calculate_summary_stats",
17
+ "group_issues_by_feature",
18
+ "generate_html_report",
19
+ "build_default_executive_summary",
20
+ "classify_features",
21
+ "build_classification_summary_html",
22
+ "Feature",
23
+ "Scenario",
24
+ "Step",
25
+ "FilteredIssue",
26
+ ]
@@ -0,0 +1,6 @@
1
+ """Entry point for running test-report-vibes as a module."""
2
+
3
+ from .cli import main
4
+
5
+ if __name__ == "__main__":
6
+ main()
@@ -0,0 +1,187 @@
1
+ """Tag-based issue classification for Cucumber test results.
2
+
3
+ Classifies failing scenarios by their tags and highlights the first failing
4
+ scenario per feature as the most important one to review.
5
+ """
6
+
7
+ import html
8
+ from typing import Dict, List, Any, Optional
9
+
10
+ # Default mapping from Cucumber tags to human-readable status labels.
11
+ # Priority is determined by order: the first matching tag wins.
12
+ DEFAULT_TAG_MAPPING: Dict[str, str] = {
13
+ "@new_issue": "New and reported",
14
+ "@under_debugging": "Debugging",
15
+ "@bug_reported": "Bug reported",
16
+ "@test_issue": "Test Framework issue",
17
+ "@flaky": "Flaky Test",
18
+ }
19
+
20
+ NOT_REPORTED = "Not reported"
21
+
22
+
23
+ def classify_status(tags: List[str], tag_mapping: Optional[Dict[str, str]] = None) -> str:
24
+ """Return a human-readable status for a set of tags.
25
+
26
+ Iterates through *tags* in order and returns the label of the first tag
27
+ present in *tag_mapping*. Falls back to ``"Not reported"``.
28
+ """
29
+ mapping = tag_mapping or DEFAULT_TAG_MAPPING
30
+ for tag in tags:
31
+ if tag in mapping:
32
+ return mapping[tag]
33
+ return NOT_REPORTED
34
+
35
+
36
+ def classify_features(
37
+ feature_groups: List[Dict[str, Any]],
38
+ tag_mapping: Optional[Dict[str, str]] = None,
39
+ ) -> Dict[str, Any]:
40
+ """Classify every failing feature group.
41
+
42
+ For each feature group (as produced by ``filter.group_issues_by_feature``),
43
+ determines:
44
+
45
+ * The *first* failing scenario (by position / line number) — this is the
46
+ most important one to review because later scenarios may cascade.
47
+ * A classification status derived from the combined feature + scenario tags
48
+ using ``classify_status``.
49
+
50
+ Returns a dict with:
51
+ ``classified_features`` – list of per-feature classification dicts
52
+ ``status_counts`` – breakdown ``{status_label: count}``
53
+ ``total_failed_features`` – number of features with failures
54
+ ``total_failed_scenarios`` – total failing scenarios across all features
55
+ """
56
+ mapping = tag_mapping or DEFAULT_TAG_MAPPING
57
+
58
+ classified: List[Dict[str, Any]] = []
59
+ status_counts: Dict[str, int] = {}
60
+ total_failed_scenarios = 0
61
+
62
+ for group in feature_groups:
63
+ feature_name: str = group.get("feature_name", "Unknown Feature")
64
+ feature_tags: List[str] = group.get("feature_tags", [])
65
+ scenarios: List[Dict[str, Any]] = group.get("scenarios", [])
66
+
67
+ first_failing: Optional[Dict[str, Any]] = None
68
+
69
+ for scenario in scenarios:
70
+ # Check if this scenario actually has failures (not just a passing scenario added for context)
71
+ has_failures = (
72
+ scenario.get("failed_steps", 0) > 0 or
73
+ scenario.get("undefined_steps", 0) > 0 or
74
+ scenario.get("pending_steps", 0) > 0
75
+ )
76
+
77
+ if not has_failures:
78
+ continue # Skip passing scenarios
79
+
80
+ total_failed_scenarios += 1
81
+
82
+ # Merge feature + scenario tags for classification
83
+ all_tags = feature_tags + scenario.get("tags", [])
84
+ status = classify_status(all_tags, mapping)
85
+
86
+ # Count every failing scenario by status
87
+ status_counts[status] = status_counts.get(status, 0) + 1
88
+
89
+ # The first scenario in the list is by definition the earliest
90
+ # (group_issues_by_feature sorts by line number).
91
+ if first_failing is None:
92
+ first_failing = {
93
+ "scenario_name": scenario.get("name", "Unnamed Scenario"),
94
+ "status": status,
95
+ "tags": all_tags,
96
+ }
97
+
98
+ # Count only scenarios with actual failures
99
+ failing_count = sum(
100
+ 1 for s in scenarios
101
+ if s.get("failed_steps", 0) > 0 or s.get("undefined_steps", 0) > 0 or s.get("pending_steps", 0) > 0
102
+ )
103
+
104
+ classified.append({
105
+ "feature_name": feature_name,
106
+ "first_failing": first_failing,
107
+ "failing_scenario_count": failing_count,
108
+ })
109
+
110
+ return {
111
+ "classified_features": classified,
112
+ "status_counts": status_counts,
113
+ "total_failed_features": len(classified),
114
+ "total_failed_scenarios": total_failed_scenarios,
115
+ }
116
+
117
+
118
+ # ---------------------------------------------------------------------------
119
+ # HTML rendering
120
+ # ---------------------------------------------------------------------------
121
+
122
+ _STATUS_COLORS: Dict[str, str] = {
123
+ "New and reported": "#9cf3af",
124
+ "Debugging": "#3b89f6",
125
+ "Bug reported": "#ef0000",
126
+ "Test Framework issue": "#8989ff",
127
+ "Flaky Test": "#f59e0b",
128
+ NOT_REPORTED: "#efbb85",
129
+ }
130
+
131
+
132
+ def _status_color(status: str) -> str:
133
+ return _STATUS_COLORS.get(status, "#6b7280")
134
+
135
+
136
+ def build_classification_summary_html(classification: Dict[str, Any]) -> str:
137
+ """Render an HTML ``<section>`` with the tag-based classification summary.
138
+
139
+ The section has CSS class ``classification-summary`` and is designed to sit
140
+ alongside (but independently of) the executive summary.
141
+ """
142
+ classified = classification["classified_features"]
143
+ status_counts = classification["status_counts"]
144
+ total_features = classification["total_failed_features"]
145
+ total_scenarios = classification["total_failed_scenarios"]
146
+
147
+ if total_features == 0:
148
+ return (
149
+ '<section class="classification-summary">'
150
+ "<h2>Classified features</h2>"
151
+ "<p>No failing features to classify.</p>"
152
+ "</section>"
153
+ )
154
+
155
+ # --- Feature list (first failing scenario only) ---
156
+ list_items = []
157
+ for entry in classified:
158
+ fname = html.escape(entry["feature_name"])
159
+ first = entry.get("first_failing")
160
+ if first:
161
+ sname = html.escape(first["scenario_name"])
162
+ sstatus = html.escape(first["status"])
163
+ color = _status_color(first["status"])
164
+ list_items.append(
165
+ f"<li>"
166
+ f"<strong>{fname}</strong>"
167
+ f'<ul><li><span class="cls-pill" style="background:{color};">'
168
+ f"{sstatus}</span> {sname}</li></ul>"
169
+ f"</li>"
170
+ )
171
+ else:
172
+ list_items.append(f"<li><strong>{fname}</strong></li>")
173
+
174
+ features_html = "\n".join(list_items)
175
+
176
+ return (
177
+ '<section class="classification-summary">\n'
178
+ "<h2>Classified features</h2>\n"
179
+ f"<p><strong>Failed Features:</strong> {total_features}</p>\n"
180
+ f"<p><strong>Failed Scenarios:</strong> {total_scenarios}</p>\n"
181
+ '<p style="margin-bottom:0.25rem;color:var(--color-text-light);font-size:0.875rem;">'
182
+ "The first failing scenario in each feature is usually the most important to review, "
183
+ "later failures may cascade from it.</p>\n"
184
+ f"<ul>{features_html}</ul>\n"
185
+ "</section>"
186
+ )
187
+