pytest-reporter-html 1.0.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.
- pytest_reporter_html-1.0.0/.gitignore +18 -0
- pytest_reporter_html-1.0.0/LICENSE +21 -0
- pytest_reporter_html-1.0.0/PKG-INFO +487 -0
- pytest_reporter_html-1.0.0/README.md +457 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773065204141.json +16 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773065236123.json +16 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773065272236.json +16 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773065291146.json +16 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773065313823.json +16 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773065329211.json +16 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773065912152.json +31 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773066001967.json +31 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773066521097.json +31 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773066588963.json +31 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773066726179.json +31 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773066767412.json +31 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773067129719.json +31 -0
- pytest_reporter_html-1.0.0/custom_output/json/test_no_report_html_flag_1773067472163.json +31 -0
- pytest_reporter_html-1.0.0/pyproject.toml +52 -0
- pytest_reporter_html-1.0.0/run_tests.sh +9 -0
- pytest_reporter_html-1.0.0/src/pytest_reporter_html/__init__.py +27 -0
- pytest_reporter_html-1.0.0/src/pytest_reporter_html/html_report.py +1266 -0
- pytest_reporter_html-1.0.0/src/pytest_reporter_html/logger.py +115 -0
- pytest_reporter_html-1.0.0/src/pytest_reporter_html/logging_bridge.py +135 -0
- pytest_reporter_html-1.0.0/src/pytest_reporter_html/plugin.py +415 -0
- pytest_reporter_html-1.0.0/src/pytest_reporter_html/reporter.py +200 -0
- pytest_reporter_html-1.0.0/src/pytest_reporter_html/s3_utils.py +113 -0
- pytest_reporter_html-1.0.0/src/pytest_reporter_html/step.py +120 -0
- pytest_reporter_html-1.0.0/src/pytest_reporter_html/types.py +100 -0
- pytest_reporter_html-1.0.0/tests/conftest.py +51 -0
- pytest_reporter_html-1.0.0/tests/test_html_report.py +452 -0
- pytest_reporter_html-1.0.0/tests/test_logger.py +147 -0
- pytest_reporter_html-1.0.0/tests/test_logging_bridge.py +262 -0
- pytest_reporter_html-1.0.0/tests/test_plugin.py +1177 -0
- pytest_reporter_html-1.0.0/tests/test_reporter.py +243 -0
- pytest_reporter_html-1.0.0/tests/test_step.py +218 -0
- pytest_reporter_html-1.0.0/tests/test_types.py +164 -0
- pytest_reporter_html-1.0.0/uv.lock +269 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Yevgeny Farber
|
|
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.
|
|
@@ -0,0 +1,487 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pytest-reporter-html
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Pytest plugin that generates rich HTML test reports with step tracking, log capture, and interactive filtering
|
|
5
|
+
Project-URL: Homepage, https://github.com/YevgenyFarber/pytest-reporter-html
|
|
6
|
+
Project-URL: Documentation, https://github.com/YevgenyFarber/pytest-reporter-html
|
|
7
|
+
Project-URL: Source, https://github.com/YevgenyFarber/pytest-reporter-html
|
|
8
|
+
Project-URL: Issues, https://github.com/YevgenyFarber/pytest-reporter-html/issues
|
|
9
|
+
Author: Yevgeny Farber
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: html,pytest,reporter,test-report,test-reporting
|
|
13
|
+
Classifier: Framework :: Pytest
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Software Development :: Testing
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
26
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
27
|
+
Provides-Extra: s3
|
|
28
|
+
Requires-Dist: boto3>=1.28; extra == 's3'
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# pytest-reporter-html
|
|
32
|
+
|
|
33
|
+
[](https://pypi.org/project/pytest-reporter-html/)
|
|
34
|
+
[](https://pypistats.org/packages/pytest-reporter-html)
|
|
35
|
+
[](https://pypi.org/project/pytest-reporter-html/)
|
|
36
|
+
[](https://github.com/YevgenyFarber/pytest-reporter-html/blob/main/LICENSE)
|
|
37
|
+
|
|
38
|
+
A pytest plugin that automatically generates rich, interactive HTML test reports with **log capture**, **step tracking**, **exception rendering**, and **real-time filtering**.
|
|
39
|
+
|
|
40
|
+
Install it, configure a capture mode, run `pytest`, get a report.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
pip install pytest-reporter-html
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For S3 upload support (CI environments):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install pytest-reporter-html[s3]
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Quick Start
|
|
57
|
+
|
|
58
|
+
Enable a capture mode in `pyproject.toml`:
|
|
59
|
+
|
|
60
|
+
```toml
|
|
61
|
+
[tool.pytest.ini_options]
|
|
62
|
+
report_mode = "all" # auto + step + manual (see Capture Modes below)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Run pytest:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
pytest
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
After the run:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
================================================================================
|
|
75
|
+
All Tests Completed - Aggregated Report:
|
|
76
|
+
/absolute/path/to/build/test-reports/TestReport_Latest.html
|
|
77
|
+
Open in browser: file:///absolute/path/to/build/test-reports/TestReport_Latest.html
|
|
78
|
+
================================================================================
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Open the HTML file in any browser.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## How It Works
|
|
86
|
+
|
|
87
|
+
The plugin hooks into pytest's test lifecycle and builds a structured report for every test, organized as:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
Session
|
|
91
|
+
└── Test Class / Module (collapsible group)
|
|
92
|
+
└── Test Function (collapsible)
|
|
93
|
+
└── Step (auto-created per test phase)
|
|
94
|
+
└── Event (captured log line)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Zero-Config Log Capture
|
|
98
|
+
|
|
99
|
+
The plugin attaches a handler to Python's **root logger** at test start and removes it at teardown. Any code that uses standard `logging` — your application, third-party libraries, anything — has its output captured as **events** in the report. No imports, no fixtures, no code changes.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
# your application code (not test code)
|
|
103
|
+
import logging
|
|
104
|
+
logger = logging.getLogger(__name__)
|
|
105
|
+
|
|
106
|
+
def create_order(items):
|
|
107
|
+
logger.info(f"Creating order with {len(items)} items")
|
|
108
|
+
order = db.insert(items)
|
|
109
|
+
logger.info(f"Order created: {order.id}")
|
|
110
|
+
return order
|
|
111
|
+
|
|
112
|
+
def validate_payment(order_id, method):
|
|
113
|
+
logger.debug(f"Validating payment for order {order_id}")
|
|
114
|
+
if method not in SUPPORTED_METHODS:
|
|
115
|
+
logger.error(f"Unsupported payment method: {method}")
|
|
116
|
+
raise ValueError(f"Unsupported: {method}")
|
|
117
|
+
logger.info("Payment validated")
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
# your test — no plugin imports needed
|
|
122
|
+
def test_place_order():
|
|
123
|
+
order = create_order([{"sku": "A1", "qty": 2}])
|
|
124
|
+
validate_payment(order.id, "card")
|
|
125
|
+
assert order.status == "confirmed"
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
The report captures every `logger.*()` call from `create_order` and `validate_payment` as events, even though the test itself has no logging code:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
Test: test_place_order PASSED
|
|
132
|
+
└── test_place_order PASSED 85ms
|
|
133
|
+
├── INFO Creating order with 1 items orders.py:5
|
|
134
|
+
├── INFO Order created: ord-123 orders.py:7
|
|
135
|
+
├── DEBUG Validating payment for order ord-123 payments.py:3
|
|
136
|
+
└── INFO Payment validated payments.py:7
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Each captured event records:
|
|
140
|
+
- **Level** — `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR`
|
|
141
|
+
- **Message** — the formatted log message
|
|
142
|
+
- **Source file** and **line number** — from the actual call site
|
|
143
|
+
- **Timestamp** — millisecond precision
|
|
144
|
+
|
|
145
|
+
### Exception Capture
|
|
146
|
+
|
|
147
|
+
Exceptions logged via `logger.exception()` or `logger.error(..., exc_info=True)` are captured with their **full formatted traceback**, rendered as a collapsible block in the HTML report — exactly as they would appear at runtime.
|
|
148
|
+
|
|
149
|
+
### Automatic Phase Steps
|
|
150
|
+
|
|
151
|
+
For each test, the plugin automatically creates steps for the pytest lifecycle phases:
|
|
152
|
+
|
|
153
|
+
- **Setup** — fixture setup (hidden if empty and passed)
|
|
154
|
+
- **\<test function name\>** — the test body itself
|
|
155
|
+
- **Teardown** — fixture teardown (hidden if empty and passed)
|
|
156
|
+
|
|
157
|
+
This means every test has at least one step (its own name) where all captured events are grouped — without any code on your part.
|
|
158
|
+
|
|
159
|
+
### Adding Descriptive Logs to Tests
|
|
160
|
+
|
|
161
|
+
While the plugin captures application-level logging automatically, you can add descriptive logs directly in your tests to make the report more readable:
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
import logging
|
|
165
|
+
|
|
166
|
+
logger = logging.getLogger(__name__)
|
|
167
|
+
|
|
168
|
+
class TestCheckout:
|
|
169
|
+
def test_empty_cart_rejected(self):
|
|
170
|
+
logger.info("Attempting checkout with empty cart")
|
|
171
|
+
response = client.post("/checkout", json={"items": []})
|
|
172
|
+
|
|
173
|
+
logger.info(f"Response status: {response.status_code}")
|
|
174
|
+
assert response.status_code == 400
|
|
175
|
+
|
|
176
|
+
logger.info("Empty cart correctly rejected")
|
|
177
|
+
|
|
178
|
+
def test_successful_purchase(self):
|
|
179
|
+
logger.info("Adding item to cart and checking out")
|
|
180
|
+
cart = Cart()
|
|
181
|
+
cart.add("SKU-42", qty=2)
|
|
182
|
+
response = client.post("/checkout", json=cart.to_dict())
|
|
183
|
+
|
|
184
|
+
logger.info(f"Order ID: {response.json()['order_id']}")
|
|
185
|
+
assert response.status_code == 201
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
These `logger.info()` calls appear as events in the report, making it clear what each test does when you read the HTML output.
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Explicit Steps (Optional)
|
|
193
|
+
|
|
194
|
+
For tests with multiple logical phases, you can optionally use the `step` context manager to group events into **named steps**. This is entirely optional — most tests work fine with just logging.
|
|
195
|
+
|
|
196
|
+
### `step` as Context Manager
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
from pytest_reporter_html import step
|
|
200
|
+
|
|
201
|
+
def test_user_lifecycle():
|
|
202
|
+
with step("Create user"):
|
|
203
|
+
response = client.post("/users", json={"name": "Alice"})
|
|
204
|
+
assert response.status_code == 201
|
|
205
|
+
user_id = response.json()["id"]
|
|
206
|
+
|
|
207
|
+
with step("Update profile"):
|
|
208
|
+
client.patch(f"/users/{user_id}", json={"role": "admin"})
|
|
209
|
+
|
|
210
|
+
with step("Verify changes"):
|
|
211
|
+
user = client.get(f"/users/{user_id}").json()
|
|
212
|
+
assert user["role"] == "admin"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Report output:
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
Test: test_user_lifecycle PASSED
|
|
219
|
+
├── Step 01: Create user PASSED 120ms
|
|
220
|
+
├── Step 02: Update profile PASSED 45ms
|
|
221
|
+
└── Step 03: Verify changes PASSED 30ms
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
If a step raises, it is marked **FAILED** with the exception message and stack trace.
|
|
225
|
+
|
|
226
|
+
### `step` as Decorator
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
from pytest_reporter_html import step
|
|
230
|
+
|
|
231
|
+
@step("Create admin user")
|
|
232
|
+
def create_admin(client, name):
|
|
233
|
+
return client.post("/admin", json={"name": name}).json()
|
|
234
|
+
|
|
235
|
+
def test_admin_flow():
|
|
236
|
+
admin = create_admin(client, "Bob") # → Step 01: Create admin user
|
|
237
|
+
assert admin["role"] == "admin"
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
Async functions are supported:
|
|
241
|
+
|
|
242
|
+
```python
|
|
243
|
+
@step("Fetch data")
|
|
244
|
+
async def fetch_data(session):
|
|
245
|
+
async with session.get("/data") as resp:
|
|
246
|
+
return await resp.json()
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### `report_step` Fixture
|
|
250
|
+
|
|
251
|
+
If you prefer not to import `step` directly:
|
|
252
|
+
|
|
253
|
+
```python
|
|
254
|
+
def test_example(report_step):
|
|
255
|
+
with report_step("Prepare"):
|
|
256
|
+
data = load_fixtures()
|
|
257
|
+
with report_step("Execute"):
|
|
258
|
+
result = process(data)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
## Fixtures Reference
|
|
264
|
+
|
|
265
|
+
All fixtures are available automatically — no imports or configuration needed.
|
|
266
|
+
|
|
267
|
+
### `report_log`
|
|
268
|
+
|
|
269
|
+
A logger that writes events directly to the report (bypasses Python's logging system):
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
def test_example(report_log):
|
|
273
|
+
report_log.info("Starting test")
|
|
274
|
+
report_log.debug("Debug detail")
|
|
275
|
+
report_log.warn("Something unexpected")
|
|
276
|
+
report_log.error("Something failed")
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
> Most projects don't need this — standard `logging` is captured automatically.
|
|
280
|
+
|
|
281
|
+
### `report_step`
|
|
282
|
+
|
|
283
|
+
The `step` context manager, available as a fixture:
|
|
284
|
+
|
|
285
|
+
```python
|
|
286
|
+
def test_example(report_step):
|
|
287
|
+
with report_step("Do something"):
|
|
288
|
+
...
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### `report_test_name`
|
|
292
|
+
|
|
293
|
+
Override the test name in the report (useful for parameterized tests):
|
|
294
|
+
|
|
295
|
+
```python
|
|
296
|
+
def test_firewall_rule(rule, report_test_name):
|
|
297
|
+
report_test_name(rule.name) # "Allow-HTTP-Inbound" instead of "test_firewall_rule[rule0]"
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
---
|
|
301
|
+
|
|
302
|
+
## Output Structure
|
|
303
|
+
|
|
304
|
+
```
|
|
305
|
+
build/test-reports/
|
|
306
|
+
├── json/
|
|
307
|
+
│ ├── test_login_1709912345000.json
|
|
308
|
+
│ ├── test_checkout_1709912345100.json
|
|
309
|
+
│ └── ...
|
|
310
|
+
├── TestReport_Latest.html ← open this
|
|
311
|
+
└── TestReport_All_1709912345000.html ← timestamped copy
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
- **JSON files** — one per test, containing steps, events, status, timing, and metadata
|
|
315
|
+
- **HTML report** — aggregated single-page dashboard
|
|
316
|
+
|
|
317
|
+
### JSON Structure
|
|
318
|
+
|
|
319
|
+
```json
|
|
320
|
+
{
|
|
321
|
+
"testStatus": "PASSED",
|
|
322
|
+
"className": "TestCheckout",
|
|
323
|
+
"steps": [
|
|
324
|
+
{
|
|
325
|
+
"name": "test_empty_cart_rejected",
|
|
326
|
+
"status": "PASSED",
|
|
327
|
+
"startTime": 1709912345000,
|
|
328
|
+
"endTime": 1709912345120,
|
|
329
|
+
"events": [
|
|
330
|
+
{
|
|
331
|
+
"level": "INFO",
|
|
332
|
+
"event": "Attempting checkout with empty cart",
|
|
333
|
+
"startTime": 1709912345010,
|
|
334
|
+
"sourceFileName": "test_checkout.py",
|
|
335
|
+
"sourceLineNumber": 8
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"level": "INFO",
|
|
339
|
+
"event": "Response status: 400",
|
|
340
|
+
"startTime": 1709912345080,
|
|
341
|
+
"sourceFileName": "test_checkout.py",
|
|
342
|
+
"sourceLineNumber": 11
|
|
343
|
+
}
|
|
344
|
+
]
|
|
345
|
+
}
|
|
346
|
+
],
|
|
347
|
+
"AllTestMethods": ["test_empty_cart_rejected", "test_successful_purchase"]
|
|
348
|
+
}
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
---
|
|
352
|
+
|
|
353
|
+
## HTML Report Features
|
|
354
|
+
|
|
355
|
+
- **Progress bar** — visual pass/fail ratio at the top
|
|
356
|
+
- **Search** — filter tests by name in real time
|
|
357
|
+
- **Status filter** — show All / Passed / Failed
|
|
358
|
+
- **Log level filter** — show events at TRACE / DEBUG / INFO / WARN / ERROR and above
|
|
359
|
+
- **Collapsible class groups** — tests grouped by class or module, with pass/fail badges
|
|
360
|
+
- **Collapsible tests** — click to expand and see steps and events
|
|
361
|
+
- **Collapsible steps** — each step shows its events, duration, and status
|
|
362
|
+
- **Auto-expand failures** — failed tests and steps open by default
|
|
363
|
+
- **Exception rendering** — full tracebacks in collapsible `<pre>` blocks
|
|
364
|
+
- **Expand All / Collapse All** — toolbar buttons
|
|
365
|
+
- **Copy report URL** — one-click copy
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Configuration
|
|
370
|
+
|
|
371
|
+
All options go in `pyproject.toml` under `[tool.pytest.ini_options]`:
|
|
372
|
+
|
|
373
|
+
```toml
|
|
374
|
+
[tool.pytest.ini_options]
|
|
375
|
+
report_mode = "all"
|
|
376
|
+
report_dir = "build/test-reports"
|
|
377
|
+
report_title = "My Project"
|
|
378
|
+
report_log_level = "INFO"
|
|
379
|
+
report_html = true
|
|
380
|
+
report_exclude_loggers = "urllib3,botocore,httpcore"
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Options Reference
|
|
384
|
+
|
|
385
|
+
| Option | Type | Default | Description |
|
|
386
|
+
|--------|------|---------|-------------|
|
|
387
|
+
| `report_enabled` | `bool` | `true` | Master switch — set to `false` to completely disable the reporter |
|
|
388
|
+
| `report_mode` | `string` | *(empty — disabled)* | Capture mode (see [Capture Modes](#capture-modes)). **Must be set to enable reporting.** |
|
|
389
|
+
| `report_dir` | `string` | `build/test-reports` | Output directory for JSON and HTML reports |
|
|
390
|
+
| `report_title` | `string` | `Test Report` | Title shown in the HTML report header |
|
|
391
|
+
| `report_log_level` | `string` | `DEBUG` | Minimum log level to capture: `TRACE`, `DEBUG`, `INFO`, `WARN`, `ERROR` |
|
|
392
|
+
| `report_auto_log` | `bool` | `true` | Legacy toggle for auto log capture (prefer `report_mode`) |
|
|
393
|
+
| `report_html` | `bool` | `true` | Generate the aggregated HTML report at session end |
|
|
394
|
+
| `report_exclude_loggers` | `string` | *(empty)* | Comma-separated logger name prefixes to exclude from capture |
|
|
395
|
+
|
|
396
|
+
### CLI Options
|
|
397
|
+
|
|
398
|
+
| Flag | Description |
|
|
399
|
+
|------|-------------|
|
|
400
|
+
| `--disable-reporter` | Completely disable the reporter (no JSON, no HTML) |
|
|
401
|
+
| `--no-report-html` | Disable HTML report generation (JSON still produced) |
|
|
402
|
+
|
|
403
|
+
### Capture Modes
|
|
404
|
+
|
|
405
|
+
The plugin supports three capture modes that can be used individually or combined:
|
|
406
|
+
|
|
407
|
+
| Mode | What it captures |
|
|
408
|
+
|------|-----------------|
|
|
409
|
+
| `auto` | Python's standard `logging` output — zero-config, captures application and test logs |
|
|
410
|
+
| `step` | Explicit `step()` context manager and decorator calls |
|
|
411
|
+
| `manual` | The `report_log` fixture (`report_log.info(...)`, etc.) |
|
|
412
|
+
|
|
413
|
+
By default no mode is active — **you must set `report_mode` to enable reporting**. Set it in `pyproject.toml`:
|
|
414
|
+
|
|
415
|
+
```toml
|
|
416
|
+
[tool.pytest.ini_options]
|
|
417
|
+
# Enable all modes
|
|
418
|
+
report_mode = "all"
|
|
419
|
+
|
|
420
|
+
# Only auto-capture Python logging (step() and report_log become no-ops)
|
|
421
|
+
report_mode = "auto"
|
|
422
|
+
|
|
423
|
+
# Only explicit steps (no auto-log, report_log becomes no-op)
|
|
424
|
+
report_mode = "step"
|
|
425
|
+
|
|
426
|
+
# Only report_log fixture (no auto-log, step() becomes no-op)
|
|
427
|
+
report_mode = "manual"
|
|
428
|
+
|
|
429
|
+
# Combine modes with comma-separated values
|
|
430
|
+
report_mode = "auto,step"
|
|
431
|
+
report_mode = "step,manual"
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
When a mode is not selected, its API becomes a **silent no-op** — test code using `step()` or `report_log` will still run without errors, but events won't appear in the report.
|
|
435
|
+
|
|
436
|
+
### Disabling the Reporter
|
|
437
|
+
|
|
438
|
+
To completely disable all reporting (no JSON, no HTML, no overhead):
|
|
439
|
+
|
|
440
|
+
```bash
|
|
441
|
+
# Via CLI flag
|
|
442
|
+
pytest --disable-reporter
|
|
443
|
+
|
|
444
|
+
# Via ini option
|
|
445
|
+
# pyproject.toml
|
|
446
|
+
[tool.pytest.ini_options]
|
|
447
|
+
report_enabled = false
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
When disabled, all hooks are skipped, `step()` is a no-op, and `report_log` is a no-op.
|
|
451
|
+
|
|
452
|
+
### Excluding Noisy Loggers
|
|
453
|
+
|
|
454
|
+
```toml
|
|
455
|
+
[tool.pytest.ini_options]
|
|
456
|
+
report_exclude_loggers = "urllib3,botocore,httpcore,asyncio"
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
---
|
|
460
|
+
|
|
461
|
+
## S3 Upload (CI)
|
|
462
|
+
|
|
463
|
+
Set environment variables to upload reports to S3 automatically:
|
|
464
|
+
|
|
465
|
+
| Variable | Required | Description |
|
|
466
|
+
|----------|----------|-------------|
|
|
467
|
+
| `REPORT_CI_RUN` | Yes | Set to `true` to enable S3 upload |
|
|
468
|
+
| `REPORT_RUN_ID` | Yes | Unique run identifier |
|
|
469
|
+
| `REPORT_TIMESTAMP` | No | Run timestamp (defaults to current time) |
|
|
470
|
+
| `REPORT_S3_BUCKET` | No | S3 bucket (default: `external-test-results`) |
|
|
471
|
+
| `REPORT_S3_REGION` | No | S3 region (default: `eu-central-1`) |
|
|
472
|
+
| `REPORT_TEST_TYPE` | No | Test type for S3 key prefix |
|
|
473
|
+
| `REPORT_CYCLE` | No | Test cycle for S3 key prefix |
|
|
474
|
+
| `REPORT_SUITE_NAME` | No | Suite name for S3 key prefix |
|
|
475
|
+
|
|
476
|
+
Requires: `pip install pytest-reporter-html[s3]`
|
|
477
|
+
|
|
478
|
+
## Compatibility
|
|
479
|
+
|
|
480
|
+
- Python 3.10+
|
|
481
|
+
- pytest 7.0+
|
|
482
|
+
- Works with `pytest-asyncio`, `pytest-xdist`, and standard pytest plugins
|
|
483
|
+
- Sync and async tests fully supported
|
|
484
|
+
|
|
485
|
+
## License
|
|
486
|
+
|
|
487
|
+
MIT
|