qapytest 0.3.3__tar.gz → 0.3.4__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.
- {qapytest-0.3.3 → qapytest-0.3.4}/CHANGELOG.md +13 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/PKG-INFO +1 -1
- {qapytest-0.3.3 → qapytest-0.3.4}/pyproject.toml +1 -1
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_assets/_log_tree.html.jinja2 +10 -2
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_assets/report.html.jinja2 +57 -12
- qapytest-0.3.4/qapytest/_assets/scripts.js +1201 -0
- qapytest-0.3.4/qapytest/_assets/styles.css +1255 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_internal.py +5 -2
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_internal_functions.py +12 -23
- {qapytest-0.3.3 → qapytest-0.3.4}/uv.lock +9 -1
- qapytest-0.3.3/qapytest/_assets/scripts.js +0 -442
- qapytest-0.3.3/qapytest/_assets/styles.css +0 -649
- {qapytest-0.3.3 → qapytest-0.3.4}/.gitignore +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/.pre-commit-config.yaml +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/NOTICE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/README.md +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/THIRD_PARTY_NOTICES.txt +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/demo.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/docs/API.md +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/docs/CLI.md +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/faker.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/httpx.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/jinja2.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/jsonschema.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/playwright-python.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/pytest-playwright.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/pytest.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/python_dotenv.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/redis-py.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/licenses/sqlalchemy.LICENSE +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/__init__.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_attach.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_client_http.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_config.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_config_http.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_json_validation.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_plugin.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_redis.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_report.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_soft_assert.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_sql.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/_step.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/qapytest/py.typed +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/encoding.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_attach.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_graphql_client.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_http_client.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_integration.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_markers.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_redis_client.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_soft_assert.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_sql_client.py +0 -0
- {qapytest-0.3.3 → qapytest-0.3.4}/tests/test_step.py +0 -0
|
@@ -5,6 +5,18 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.4] - 2025-11-23
|
|
9
|
+
|
|
10
|
+
### Enhanced
|
|
11
|
+
|
|
12
|
+
- 📊 **Interactive HTML Report** - significantly improved report interactivity with new features:
|
|
13
|
+
- **JSON Viewer** - interactive tree view for JSON attachments with search and expand/collapse capabilities
|
|
14
|
+
- **Log Filtering** - filter logs by logger name and level, with search functionality
|
|
15
|
+
- **Component Filtering** - easily filter test results by component
|
|
16
|
+
- **Step Management** - expand/collapse all steps with a single click
|
|
17
|
+
- **Navigation** - added "Scroll to top" button for easier navigation in long reports
|
|
18
|
+
- 🛠️ **Soft Assertions** - improved display of soft assertion failures in the report
|
|
19
|
+
|
|
8
20
|
## [0.3.3] - 2025-10-31
|
|
9
21
|
|
|
10
22
|
### Fixed
|
|
@@ -157,6 +169,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
157
169
|
- Configurable attachment size limits
|
|
158
170
|
- Professional report styling with responsive design
|
|
159
171
|
|
|
172
|
+
[0.3.4]: https://github.com/o73k51i/qapytest/releases/tag/v0.3.4
|
|
160
173
|
[0.3.3]: https://github.com/o73k51i/qapytest/releases/tag/v0.3.3
|
|
161
174
|
[0.3.2]: https://github.com/o73k51i/qapytest/releases/tag/v0.3.2
|
|
162
175
|
[0.3.1]: https://github.com/o73k51i/qapytest/releases/tag/v0.3.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qapytest
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: A powerful testing framework based on pytest, specifically designed for QA engineers
|
|
5
5
|
Project-URL: Homepage, https://github.com/o73k51i/qapytest
|
|
6
6
|
Project-URL: Repository, https://github.com/o73k51i/qapytest
|
|
@@ -4,9 +4,17 @@
|
|
|
4
4
|
{% for entry in log_list %}
|
|
5
5
|
{% if entry.type == 'step' %}
|
|
6
6
|
<li class="{{ 'step-passed' if entry.passed else 'step-failed' }}">
|
|
7
|
+
{% if entry.children %}
|
|
8
|
+
<span class="step-toggle collapsed">▼</span>
|
|
9
|
+
{% else %}
|
|
10
|
+
<span class="step-toggle-placeholder"></span>
|
|
11
|
+
{% endif %}
|
|
7
12
|
{{ '✔︎' if entry.passed else '✖︎' }} <strong>Step:</strong> {{ entry.message|e }}
|
|
8
|
-
{
|
|
9
|
-
|
|
13
|
+
{% if entry.children %}
|
|
14
|
+
<div class="step-children" hidden>
|
|
15
|
+
{{ render_log_tree(entry.children, test_index, attachment_counter, assert_counter) }}
|
|
16
|
+
</div>
|
|
17
|
+
{% endif %}
|
|
10
18
|
</li>
|
|
11
19
|
{% elif entry.type == 'assert' %}
|
|
12
20
|
{% set _ = assert_counter.update({'value': assert_counter.value + 1}) %}
|
|
@@ -10,8 +10,9 @@
|
|
|
10
10
|
<header class="header">
|
|
11
11
|
<div class="container">
|
|
12
12
|
<div class="title">{{ title|e }}</div>
|
|
13
|
-
<button id="theme-toggle" class="theme-toggle" title="Toggle theme">
|
|
14
|
-
<
|
|
13
|
+
<button id="theme-toggle" class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
|
14
|
+
<svg class="theme-icon-moon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path></svg>
|
|
15
|
+
<svg class="theme-icon-sun" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="5"></circle><line x1="12" y1="1" x2="12" y2="3"></line><line x1="12" y1="21" x2="12" y2="23"></line><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line><line x1="1" y1="12" x2="3" y2="12"></line><line x1="21" y1="12" x2="23" y2="12"></line><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line></svg>
|
|
15
16
|
</button>
|
|
16
17
|
<section class="cards">
|
|
17
18
|
<div class="card"><div class="label">Start</div><div class="value">{{ fmt_datetime(session_start) }}</div></div>
|
|
@@ -39,8 +40,23 @@
|
|
|
39
40
|
<th>#</th>
|
|
40
41
|
<th>Test</th>
|
|
41
42
|
<th>Status</th>
|
|
42
|
-
<th>
|
|
43
|
-
|
|
43
|
+
<th>
|
|
44
|
+
<div class="th-filter-wrapper">
|
|
45
|
+
<span>Component</span>
|
|
46
|
+
<button class="th-filter-toggle" title="Filter Components">▾</button>
|
|
47
|
+
<div class="th-filter-menu" hidden>
|
|
48
|
+
<input type="text" class="th-filter-search" placeholder="Filter..." />
|
|
49
|
+
<label class="th-filter-item"><input type="checkbox" value="all" checked> <span>All</span></label>
|
|
50
|
+
<div class="th-filter-list"></div>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</th>
|
|
54
|
+
<th>
|
|
55
|
+
<div class="th-sort-wrapper" data-sort="duration">
|
|
56
|
+
<span>Duration (s)</span>
|
|
57
|
+
<span class="sort-icon">↕</span>
|
|
58
|
+
</div>
|
|
59
|
+
</th>
|
|
44
60
|
</tr>
|
|
45
61
|
</thead>
|
|
46
62
|
<tbody>
|
|
@@ -49,7 +65,7 @@
|
|
|
49
65
|
{% set test_index = loop.index %}
|
|
50
66
|
{% set params_str = parse_params_from_nodeid(result.nodeid) %}
|
|
51
67
|
{% set search_text = [result.nodeid, result.path, result.title, result.components|join(' ')]|join(' ')|e %}
|
|
52
|
-
<tr class="test-row" data-status="{{ result.outcome }}" data-text="{{ search_text }}" role="button" tabindex="0" aria-expanded="false" aria-controls="{{ details_id }}">
|
|
68
|
+
<tr class="test-row" data-status="{{ result.outcome }}" data-text="{{ search_text }}" data-duration="{{ result.duration }}" role="button" tabindex="0" aria-expanded="false" aria-controls="{{ details_id }}">
|
|
53
69
|
<td class="small">{{ loop.index }}</td>
|
|
54
70
|
<td>
|
|
55
71
|
<div class="mono">{{ decode_unicode_escapes(result.title)|e }}</div>
|
|
@@ -69,13 +85,20 @@
|
|
|
69
85
|
<button class="details-btn copy-btn" type="button" data-copy="#grid-{{ details_id }}" title="Copy details"><span>📋</span> Copy</button>
|
|
70
86
|
</div>
|
|
71
87
|
<div class="grid" id="grid-{{ details_id }}">
|
|
72
|
-
<div class="k">NodeID</div
|
|
88
|
+
<div class="k">NodeID</div>
|
|
89
|
+
<div class="v mono">{{ decode_unicode_escapes(result.nodeid)|e }}</div>
|
|
73
90
|
<div class="k">File</div><div class="v mono">{{ result.path|e }}</div>
|
|
74
91
|
{% if result.details.headline %}
|
|
75
92
|
<div class="k">Result</div><div class="v"><span class="status {{ result.outcome }}">{{ result.details.headline|e }}</span></div>
|
|
76
93
|
{% endif %}
|
|
77
94
|
{% if result.execution_log %}
|
|
78
|
-
<div class="k">
|
|
95
|
+
<div class="k">
|
|
96
|
+
Execution Log
|
|
97
|
+
<div style="margin-top: 4px;">
|
|
98
|
+
<button class="details-btn step-toggle-all-btn" type="button" data-expanded="false" style="font-size: 10px; padding: 2px 6px;">Expand All</button>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
<div class="v mono">
|
|
79
102
|
{% import '_log_tree.html.jinja2' as log_helpers %}
|
|
80
103
|
{{ log_helpers.render_log_tree(result.execution_log, test_index, {'value': 0}, {'value': 0}) }}
|
|
81
104
|
</div>
|
|
@@ -96,25 +119,36 @@
|
|
|
96
119
|
</div>
|
|
97
120
|
</main>
|
|
98
121
|
|
|
99
|
-
{# --- Modals --- #}
|
|
100
122
|
{% for result in results %}
|
|
101
123
|
{% set details_id = 'details-' ~ loop.index %}
|
|
102
124
|
{% set test_index = loop.index %}
|
|
103
|
-
{# Render modals for logs #}
|
|
104
125
|
{% if result.details.captured_logs and result.details.captured_logs.strip() %}
|
|
105
126
|
<div class="modal" id="logs-modal-{{ details_id }}" hidden>
|
|
106
127
|
<div class="modal-overlay" data-modal-close></div>
|
|
107
128
|
<div class="modal-content">
|
|
108
129
|
<div class="modal-header">
|
|
109
130
|
<h3 class="mono">{{ decode_unicode_escapes(result.title)|e }}</h3>
|
|
110
|
-
<
|
|
131
|
+
<div class="modal-actions">
|
|
132
|
+
<div class="attachment-controls" style="display: none;">
|
|
133
|
+
<input type="text" class="attachment-search" placeholder="Highlight text..." />
|
|
134
|
+
</div>
|
|
135
|
+
<div class="log-filter-wrapper">
|
|
136
|
+
<button class="details-btn log-filter-toggle">Filter Loggers ▾</button>
|
|
137
|
+
<div class="log-filter-menu" hidden>
|
|
138
|
+
<input type="text" class="log-filter-search" placeholder="Filter..." />
|
|
139
|
+
<label class="log-filter-item"><input type="checkbox" value="all" checked> <span>All Loggers</span></label>
|
|
140
|
+
<div class="log-filter-list"></div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
<button class="details-btn log-copy-btn" title="Copy visible logs"><span>📋</span> Copy</button>
|
|
144
|
+
<button class="modal-close" data-modal-close>×</button>
|
|
145
|
+
</div>
|
|
111
146
|
</div>
|
|
112
147
|
<div class="modal-body"><pre><code>{{ result.details.captured_logs|e }}</code></pre></div>
|
|
113
148
|
</div>
|
|
114
149
|
</div>
|
|
115
150
|
{% endif %}
|
|
116
151
|
|
|
117
|
-
{# Render modals for attachments #}
|
|
118
152
|
{% for attachment in result.attachments %}
|
|
119
153
|
{% set modal_id = 'attachment-modal-' ~ test_index ~ '-' ~ loop.index %}
|
|
120
154
|
<div class="modal" id="{{ modal_id }}" hidden>
|
|
@@ -122,7 +156,14 @@
|
|
|
122
156
|
<div class="modal-content">
|
|
123
157
|
<div class="modal-header">
|
|
124
158
|
<h3>{{ attachment.label|e }}</h3>
|
|
125
|
-
<
|
|
159
|
+
<div class="modal-actions">
|
|
160
|
+
<div class="attachment-controls" style="display: none;">
|
|
161
|
+
<input type="text" class="attachment-search" placeholder="Highlight text..." />
|
|
162
|
+
<button class="details-btn attachment-toggle-all-btn" title="Expand/Collapse All" data-expanded="true">Collapse All</button>
|
|
163
|
+
</div>
|
|
164
|
+
<button class="details-btn attachment-copy-btn" type="button" title="Copy content"><span>📋</span> Copy</button>
|
|
165
|
+
<button class="modal-close" data-modal-close>×</button>
|
|
166
|
+
</div>
|
|
126
167
|
</div>
|
|
127
168
|
<div class="modal-body">
|
|
128
169
|
{% if attachment.content_type == "image" %}
|
|
@@ -136,6 +177,10 @@
|
|
|
136
177
|
{% endfor %}
|
|
137
178
|
{% endfor %}
|
|
138
179
|
|
|
180
|
+
<button id="scroll-to-top" class="scroll-to-top" title="Scroll to top" aria-label="Scroll to top">
|
|
181
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 15l-6-6-6 6"/></svg>
|
|
182
|
+
</button>
|
|
183
|
+
|
|
139
184
|
<script>{{ js_content }}</script>
|
|
140
185
|
</body>
|
|
141
186
|
</html>
|