Promium 3.7.4__tar.gz → 3.7.5__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.
- {promium-3.7.4 → promium-3.7.5}/PKG-INFO +1 -1
- {promium-3.7.4 → promium-3.7.5}/Promium.egg-info/PKG-INFO +1 -1
- {promium-3.7.4 → promium-3.7.5}/promium/_diag.py +1 -1
- {promium-3.7.4 → promium-3.7.5}/promium/assertions.py +383 -81
- {promium-3.7.4 → promium-3.7.5}/promium/plugin.py +107 -93
- {promium-3.7.4 → promium-3.7.5}/setup.py +1 -1
- {promium-3.7.4 → promium-3.7.5}/LICENSE +0 -0
- {promium-3.7.4 → promium-3.7.5}/Promium.egg-info/SOURCES.txt +0 -0
- {promium-3.7.4 → promium-3.7.5}/Promium.egg-info/dependency_links.txt +0 -0
- {promium-3.7.4 → promium-3.7.5}/Promium.egg-info/entry_points.txt +0 -0
- {promium-3.7.4 → promium-3.7.5}/Promium.egg-info/requires.txt +0 -0
- {promium-3.7.4 → promium-3.7.5}/Promium.egg-info/top_level.txt +0 -0
- {promium-3.7.4 → promium-3.7.5}/README.rst +0 -0
- {promium-3.7.4 → promium-3.7.5}/doc/__init__.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/doc/conf.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/__init__.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/base.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/browsers.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/common.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/device_config.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/__init__.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/checkbox.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/drop_downs.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/frame.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/input_field.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/link.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/multiselect_listbox.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/radio_button.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/textarea_field.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/elements/upload_button.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/exceptions.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/expected_conditions.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/helpers.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/logger.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/support/__init__.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/support/settings.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/test_case.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/promium/waits.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/pyproject.toml +0 -0
- {promium-3.7.4 → promium-3.7.5}/setup.cfg +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/__init__.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/config.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/conftest.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/pages/__init__.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/pages/catalog_404_page.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/pages/catalog_gallery_page.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/settings/__init__.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/settings/test_base.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/settings/test_chrome.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/settings/test_edge.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/settings/test_firefox.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/settings/test_ie.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/settings/test_opera.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/test_catalog_404.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/test_catalog_gallery.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/test_enviroment.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/test_requests.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/test_soft_asserts.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/test_waits.py +0 -0
- {promium-3.7.4 → promium-3.7.5}/tests/urls.py +0 -0
|
@@ -9,7 +9,7 @@ from typing import Any, TypeVar
|
|
|
9
9
|
import allure
|
|
10
10
|
import urllib3
|
|
11
11
|
from json_checker import Checker, CheckerError
|
|
12
|
-
from selenium.webdriver.
|
|
12
|
+
from selenium.webdriver.remote.webdriver import WebDriver as SeleniumWebDriver
|
|
13
13
|
|
|
14
14
|
from promium.common import upload_screenshot
|
|
15
15
|
|
|
@@ -73,11 +73,26 @@ def get_text_with_ignore_whitespace_symbols(text: str) -> str:
|
|
|
73
73
|
return " ".join(text_list_without_space)
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
class PromiumAssertionError(AssertionError):
|
|
77
|
+
"""Immediate failure with Promium formatting."""
|
|
78
|
+
|
|
79
|
+
|
|
76
80
|
class BaseSoftAssertion:
|
|
77
81
|
"""Base class for soft assertion utilities."""
|
|
78
82
|
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
assertion_errors: list[str] = []
|
|
84
|
+
|
|
85
|
+
def _ensure_state(self) -> None:
|
|
86
|
+
"""Ensure instance fields exist when __init__ wasn't called."""
|
|
87
|
+
if (
|
|
88
|
+
not hasattr(self, "assertion_errors")
|
|
89
|
+
or self.assertion_errors is None
|
|
90
|
+
):
|
|
91
|
+
self.assertion_errors = []
|
|
92
|
+
if not hasattr(self, "hard_assert"):
|
|
93
|
+
self.hard_assert = False
|
|
94
|
+
if not hasattr(self, "driver"):
|
|
95
|
+
self.driver = None
|
|
81
96
|
|
|
82
97
|
def _relpath(self, p: str) -> str:
|
|
83
98
|
"""Return relative path from current working directory."""
|
|
@@ -96,9 +111,37 @@ class BaseSoftAssertion:
|
|
|
96
111
|
f = inspect.stack()[2]
|
|
97
112
|
return f"↳ at {self._relpath(f.filename)}:{f.lineno}"
|
|
98
113
|
|
|
114
|
+
def _format_error(self, error_msg: str, *, with_screen: bool) -> str:
|
|
115
|
+
"""Build final error text."""
|
|
116
|
+
screen = self.add_screenshot(error_msg) if with_screen else ""
|
|
117
|
+
return f"{error_msg}{screen}\n{self._callsite_line()}\n"
|
|
118
|
+
|
|
119
|
+
def _emit(
|
|
120
|
+
self,
|
|
121
|
+
error_msg: str,
|
|
122
|
+
with_screen: bool,
|
|
123
|
+
hard_assert_override: bool | None,
|
|
124
|
+
) -> None:
|
|
125
|
+
self._ensure_state()
|
|
126
|
+
formatted = self._format_error(error_msg, with_screen=with_screen)
|
|
127
|
+
effective_hard_assert = (
|
|
128
|
+
self.hard_assert
|
|
129
|
+
if hard_assert_override is None
|
|
130
|
+
else hard_assert_override
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if effective_hard_assert:
|
|
134
|
+
__tracebackhide__ = True
|
|
135
|
+
title = "Hard Assertion Error"
|
|
136
|
+
message = f"{title}\n\n{formatted}"
|
|
137
|
+
raise AssertionError(message)
|
|
138
|
+
|
|
139
|
+
self.assertion_errors.append(formatted)
|
|
140
|
+
|
|
99
141
|
def add_screenshot(self, error_msg: str = "soft_assertion") -> str:
|
|
100
142
|
"""Attach screenshot to allure report if driver is available."""
|
|
101
|
-
|
|
143
|
+
self._ensure_state()
|
|
144
|
+
if getattr(self, "driver", None):
|
|
102
145
|
try:
|
|
103
146
|
allure.attach(
|
|
104
147
|
self.driver.get_screenshot_as_png(),
|
|
@@ -111,36 +154,48 @@ class BaseSoftAssertion:
|
|
|
111
154
|
return ""
|
|
112
155
|
|
|
113
156
|
def soft_assert_true(
|
|
114
|
-
self,
|
|
157
|
+
self,
|
|
158
|
+
expr: Any,
|
|
159
|
+
msg: str | None = None,
|
|
160
|
+
*,
|
|
161
|
+
with_screen: bool = False,
|
|
162
|
+
hard_assert: bool | None = None,
|
|
115
163
|
) -> str | None:
|
|
116
164
|
"""Check that the expression is true."""
|
|
117
165
|
if not expr:
|
|
118
166
|
error = msg or "Is not true."
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
f"{error}{screen}\n{self._callsite_line()}\n"
|
|
167
|
+
self._emit(
|
|
168
|
+
error, with_screen=with_screen, hard_assert_override=hard_assert
|
|
122
169
|
)
|
|
123
170
|
return error
|
|
171
|
+
return None
|
|
124
172
|
|
|
125
173
|
def soft_assert_false(
|
|
126
|
-
self,
|
|
174
|
+
self,
|
|
175
|
+
expr: Any,
|
|
176
|
+
msg: str | None = None,
|
|
177
|
+
*,
|
|
178
|
+
with_screen: bool = False,
|
|
179
|
+
hard_assert: bool | None = None,
|
|
127
180
|
) -> str | None:
|
|
128
181
|
"""Check that the expression is false."""
|
|
129
182
|
if expr:
|
|
130
183
|
error = msg or "Is not false."
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
f"{error}{screen}\n{self._callsite_line()}\n"
|
|
184
|
+
self._emit(
|
|
185
|
+
error, with_screen=with_screen, hard_assert_override=hard_assert
|
|
134
186
|
)
|
|
135
187
|
return error
|
|
188
|
+
return None
|
|
136
189
|
|
|
137
190
|
def soft_assert_equals(
|
|
138
191
|
self,
|
|
139
192
|
current: Any,
|
|
140
193
|
expected: Any,
|
|
141
|
-
msg: str = None,
|
|
194
|
+
msg: str | None = None,
|
|
195
|
+
*,
|
|
142
196
|
show_diff: bool = False,
|
|
143
197
|
with_screen: bool = False,
|
|
198
|
+
hard_assert: bool | None = None,
|
|
144
199
|
) -> str | None:
|
|
145
200
|
"""Check that current equals expected, optionally showing diff."""
|
|
146
201
|
diff = None
|
|
@@ -165,15 +220,20 @@ class BaseSoftAssertion:
|
|
|
165
220
|
f"{difference}"
|
|
166
221
|
)
|
|
167
222
|
return self.soft_assert_true(
|
|
168
|
-
current == expected,
|
|
223
|
+
current == expected,
|
|
224
|
+
assert_message,
|
|
225
|
+
with_screen=with_screen,
|
|
226
|
+
hard_assert=hard_assert,
|
|
169
227
|
)
|
|
170
228
|
|
|
171
229
|
def soft_assert_not_equals(
|
|
172
230
|
self,
|
|
173
231
|
current: Any,
|
|
174
232
|
expected: Any,
|
|
175
|
-
msg: str = None,
|
|
233
|
+
msg: str | None = None,
|
|
234
|
+
*,
|
|
176
235
|
with_screen: bool = False,
|
|
236
|
+
hard_assert: bool | None = None,
|
|
177
237
|
) -> str | None:
|
|
178
238
|
"""Check that current does not equal expected."""
|
|
179
239
|
message = (
|
|
@@ -181,118 +241,219 @@ class BaseSoftAssertion:
|
|
|
181
241
|
f"Current - {convert_container(current)}\n"
|
|
182
242
|
f"Expected - {convert_container(expected)}\n"
|
|
183
243
|
)
|
|
184
|
-
self.soft_assert_false(
|
|
244
|
+
return self.soft_assert_false(
|
|
245
|
+
current == expected,
|
|
246
|
+
message,
|
|
247
|
+
with_screen=with_screen,
|
|
248
|
+
hard_assert=hard_assert,
|
|
249
|
+
)
|
|
185
250
|
|
|
186
251
|
def soft_assert_in(
|
|
187
252
|
self,
|
|
188
253
|
member: Any,
|
|
189
254
|
container: Any,
|
|
190
|
-
msg: str = None,
|
|
255
|
+
msg: str | None = None,
|
|
256
|
+
*,
|
|
191
257
|
with_screen: bool = False,
|
|
258
|
+
hard_assert: bool | None = None,
|
|
192
259
|
) -> str | None:
|
|
193
260
|
"""Check that member is in container."""
|
|
194
|
-
|
|
261
|
+
message = (
|
|
195
262
|
f"{base_msg(msg)}\n"
|
|
196
263
|
f"Member: '{member}'\n not found in\n"
|
|
197
264
|
f"Container: '{convert_container(container)}'\n"
|
|
198
265
|
)
|
|
199
|
-
return self.soft_assert_true(
|
|
266
|
+
return self.soft_assert_true(
|
|
267
|
+
member in container,
|
|
268
|
+
message,
|
|
269
|
+
with_screen=with_screen,
|
|
270
|
+
hard_assert=hard_assert,
|
|
271
|
+
)
|
|
200
272
|
|
|
201
273
|
def soft_assert_not_in(
|
|
202
274
|
self,
|
|
203
275
|
member: Any,
|
|
204
276
|
container: Any,
|
|
205
|
-
msg: str = None,
|
|
277
|
+
msg: str | None = None,
|
|
278
|
+
*,
|
|
206
279
|
with_screen: bool = False,
|
|
280
|
+
hard_assert: bool | None = None,
|
|
207
281
|
) -> str | None:
|
|
208
282
|
"""Check that member is not in container."""
|
|
209
|
-
|
|
283
|
+
message = (
|
|
210
284
|
f"{base_msg(msg)}\n"
|
|
211
285
|
f"Member: '{member}'\n unexpectedly found in\n"
|
|
212
286
|
f"Container: '{convert_container(container)}'\n"
|
|
213
287
|
)
|
|
214
|
-
return self.soft_assert_true(
|
|
288
|
+
return self.soft_assert_true(
|
|
289
|
+
member not in container,
|
|
290
|
+
message,
|
|
291
|
+
with_screen=with_screen,
|
|
292
|
+
hard_assert=hard_assert,
|
|
293
|
+
)
|
|
215
294
|
|
|
216
295
|
def soft_assert_less_equal(
|
|
217
|
-
self,
|
|
296
|
+
self,
|
|
297
|
+
a: Any,
|
|
298
|
+
b: Any,
|
|
299
|
+
msg: str | None = None,
|
|
300
|
+
*,
|
|
301
|
+
with_screen: bool = False,
|
|
302
|
+
hard_assert: bool | None = None,
|
|
218
303
|
) -> str | None:
|
|
219
304
|
"""Check that a is less than or equal to b."""
|
|
220
305
|
error = f"{base_msg(msg)}{a} not less than or equal to {b}\n"
|
|
221
|
-
return self.soft_assert_true(
|
|
306
|
+
return self.soft_assert_true(
|
|
307
|
+
a <= b, error, with_screen=with_screen, hard_assert=hard_assert
|
|
308
|
+
)
|
|
222
309
|
|
|
223
310
|
def soft_assert_less(
|
|
224
|
-
self,
|
|
311
|
+
self,
|
|
312
|
+
a: Any,
|
|
313
|
+
b: Any,
|
|
314
|
+
msg: str | None = None,
|
|
315
|
+
*,
|
|
316
|
+
with_screen: bool = False,
|
|
317
|
+
hard_assert: bool | None = None,
|
|
225
318
|
) -> str | None:
|
|
226
319
|
"""Check that a is less than b."""
|
|
227
320
|
error = f"{base_msg(msg)}{a} not less than {b}\n"
|
|
228
|
-
return self.soft_assert_true(
|
|
321
|
+
return self.soft_assert_true(
|
|
322
|
+
a < b, error, with_screen=with_screen, hard_assert=hard_assert
|
|
323
|
+
)
|
|
229
324
|
|
|
230
325
|
def soft_assert_greater_equal(
|
|
231
|
-
self,
|
|
326
|
+
self,
|
|
327
|
+
a: Any,
|
|
328
|
+
b: Any,
|
|
329
|
+
msg: str | None = None,
|
|
330
|
+
*,
|
|
331
|
+
with_screen: bool = False,
|
|
332
|
+
hard_assert: bool | None = None,
|
|
232
333
|
) -> str | None:
|
|
233
334
|
"""Check that a is greater than or equal to b."""
|
|
234
335
|
error = f"{base_msg(msg)}{a} not greater than or equal to {b}\n"
|
|
235
|
-
return self.soft_assert_true(
|
|
336
|
+
return self.soft_assert_true(
|
|
337
|
+
a >= b, error, with_screen=with_screen, hard_assert=hard_assert
|
|
338
|
+
)
|
|
236
339
|
|
|
237
340
|
def soft_assert_greater(
|
|
238
|
-
self,
|
|
341
|
+
self,
|
|
342
|
+
a: Any,
|
|
343
|
+
b: Any,
|
|
344
|
+
msg: str | None = None,
|
|
345
|
+
*,
|
|
346
|
+
with_screen: bool = False,
|
|
347
|
+
hard_assert: bool | None = None,
|
|
239
348
|
) -> str | None:
|
|
240
349
|
"""Check that a is greater than b."""
|
|
241
350
|
error = f"{base_msg(msg)}{a} not greater than {b}\n"
|
|
242
|
-
return self.soft_assert_true(
|
|
351
|
+
return self.soft_assert_true(
|
|
352
|
+
a > b, error, with_screen=with_screen, hard_assert=hard_assert
|
|
353
|
+
)
|
|
243
354
|
|
|
244
355
|
def soft_assert_regexp_matches(
|
|
245
|
-
self,
|
|
356
|
+
self,
|
|
357
|
+
text: str,
|
|
358
|
+
expected_regexp: str,
|
|
359
|
+
msg: str | None = None,
|
|
360
|
+
*,
|
|
361
|
+
with_screen: bool = False,
|
|
362
|
+
hard_assert: bool | None = None,
|
|
246
363
|
) -> str | None:
|
|
247
364
|
"""Fail the test unless the text matches the regular expression."""
|
|
248
365
|
pattern = re.compile(expected_regexp)
|
|
249
366
|
result = pattern.search(text)
|
|
250
|
-
|
|
251
367
|
if not result:
|
|
252
368
|
error = (
|
|
253
369
|
f"{base_msg(msg)}"
|
|
254
370
|
f"Regexp didn't match."
|
|
255
371
|
f"Pattern {str(pattern.pattern)} not found in {str(text)}\n"
|
|
256
372
|
)
|
|
257
|
-
self.
|
|
373
|
+
self._emit(
|
|
374
|
+
error, with_screen=with_screen, hard_assert_override=hard_assert
|
|
375
|
+
)
|
|
258
376
|
return error
|
|
377
|
+
return None
|
|
259
378
|
|
|
260
379
|
def soft_assert_disable(
|
|
261
|
-
self,
|
|
262
|
-
|
|
380
|
+
self,
|
|
381
|
+
element: Any,
|
|
382
|
+
msg: str | None = None,
|
|
383
|
+
*,
|
|
384
|
+
with_screen: bool = False,
|
|
385
|
+
hard_assert: bool | None = None,
|
|
386
|
+
) -> str | None:
|
|
263
387
|
"""Check that element has 'disabled' attribute."""
|
|
264
388
|
default_msg = "" if msg else f"Not disabled {element}\n"
|
|
265
389
|
error = f"{base_msg(msg)}{default_msg}"
|
|
266
390
|
return self.soft_assert_true(
|
|
267
|
-
element.get_attribute("disabled"),
|
|
391
|
+
element.get_attribute("disabled"),
|
|
392
|
+
error,
|
|
393
|
+
with_screen=with_screen,
|
|
394
|
+
hard_assert=hard_assert,
|
|
268
395
|
)
|
|
269
396
|
|
|
270
|
-
def soft_assert_is_none(
|
|
397
|
+
def soft_assert_is_none(
|
|
398
|
+
self,
|
|
399
|
+
obj: Any,
|
|
400
|
+
msg: str | None = None,
|
|
401
|
+
*,
|
|
402
|
+
with_screen: bool = False,
|
|
403
|
+
hard_assert: bool | None = None,
|
|
404
|
+
) -> str | None:
|
|
271
405
|
"""Check that object is None."""
|
|
272
406
|
default_msg = "" if msg else f"{obj} is not None.\n"
|
|
273
407
|
error = f"{base_msg(msg)}{default_msg}"
|
|
274
|
-
return self.soft_assert_true(
|
|
408
|
+
return self.soft_assert_true(
|
|
409
|
+
obj is None, error, with_screen=with_screen, hard_assert=hard_assert
|
|
410
|
+
)
|
|
275
411
|
|
|
276
|
-
def soft_assert_is_not_none(
|
|
412
|
+
def soft_assert_is_not_none(
|
|
413
|
+
self,
|
|
414
|
+
obj: Any,
|
|
415
|
+
msg: str | None = None,
|
|
416
|
+
*,
|
|
417
|
+
with_screen: bool = False,
|
|
418
|
+
hard_assert: bool | None = None,
|
|
419
|
+
) -> str | None:
|
|
277
420
|
"""Check that object is not None."""
|
|
278
421
|
default_msg = "" if msg else "Unexpectedly None.\n"
|
|
279
422
|
error = f"{base_msg(msg)}{default_msg}"
|
|
280
|
-
return self.soft_assert_true(
|
|
423
|
+
return self.soft_assert_true(
|
|
424
|
+
obj is not None,
|
|
425
|
+
error,
|
|
426
|
+
with_screen=with_screen,
|
|
427
|
+
hard_assert=hard_assert,
|
|
428
|
+
)
|
|
281
429
|
|
|
282
430
|
def soft_assert_is_instance(
|
|
283
|
-
self,
|
|
284
|
-
|
|
431
|
+
self,
|
|
432
|
+
obj: Any,
|
|
433
|
+
cls: Any,
|
|
434
|
+
msg: str | None = None,
|
|
435
|
+
*,
|
|
436
|
+
with_screen: bool = False,
|
|
437
|
+
hard_assert: bool | None = None,
|
|
438
|
+
) -> str | None:
|
|
285
439
|
"""Check that object is instance of given class."""
|
|
286
440
|
default_msg = "" if msg else f"{obj} is not an instance of {cls}.\n"
|
|
287
441
|
error = f"{base_msg(msg)}{default_msg}"
|
|
288
|
-
return self.soft_assert_true(
|
|
442
|
+
return self.soft_assert_true(
|
|
443
|
+
isinstance(obj, cls),
|
|
444
|
+
error,
|
|
445
|
+
with_screen=with_screen,
|
|
446
|
+
hard_assert=hard_assert,
|
|
447
|
+
)
|
|
289
448
|
|
|
290
449
|
def soft_assert_equals_text_with_ignore_spaces_and_register(
|
|
291
450
|
self,
|
|
292
451
|
current_text: str,
|
|
293
452
|
expected_text: str,
|
|
294
453
|
msg: str = "Invalid checked text.",
|
|
454
|
+
*,
|
|
295
455
|
with_screen: bool = False,
|
|
456
|
+
hard_assert: bool | None = None,
|
|
296
457
|
) -> None:
|
|
297
458
|
"""Compare texts ignoring spaces and case."""
|
|
298
459
|
current = get_text_with_ignore_whitespace_symbols(current_text)
|
|
@@ -304,26 +465,40 @@ class BaseSoftAssertion:
|
|
|
304
465
|
expected.lower(),
|
|
305
466
|
f"{msg}\nCurrent text without formatting: {current_text}"
|
|
306
467
|
f"\nExpected text without formatting: {expected_text}",
|
|
468
|
+
show_diff=False,
|
|
307
469
|
with_screen=with_screen,
|
|
470
|
+
hard_assert=hard_assert,
|
|
308
471
|
)
|
|
309
472
|
|
|
310
473
|
def soft_assert_schemas(
|
|
311
|
-
self,
|
|
474
|
+
self,
|
|
475
|
+
current: dict,
|
|
476
|
+
expected: dict,
|
|
477
|
+
msg: str = "",
|
|
478
|
+
*,
|
|
479
|
+
with_screen: bool = False,
|
|
480
|
+
hard_assert: bool | None = None,
|
|
312
481
|
) -> str | None:
|
|
313
482
|
"""Validate dictionary schema using json_checker."""
|
|
314
483
|
try:
|
|
315
484
|
Checker(expected, soft=True).validate(current)
|
|
316
485
|
except CheckerError as e:
|
|
317
486
|
error = f"{msg}\n{e}\n"
|
|
318
|
-
self.
|
|
487
|
+
self._emit(
|
|
488
|
+
error, with_screen=with_screen, hard_assert_override=hard_assert
|
|
489
|
+
)
|
|
319
490
|
return error
|
|
491
|
+
return None
|
|
320
492
|
|
|
321
493
|
def assert_keys_and_instances(
|
|
322
494
|
self,
|
|
323
495
|
actual_dict: dict,
|
|
324
496
|
expected_dict: dict,
|
|
325
|
-
can_be_null: list = None,
|
|
326
|
-
msg: str = None,
|
|
497
|
+
can_be_null: list | None = None,
|
|
498
|
+
msg: str | None = None,
|
|
499
|
+
*,
|
|
500
|
+
with_screen: bool = False,
|
|
501
|
+
hard_assert: bool | None = None,
|
|
327
502
|
) -> None:
|
|
328
503
|
"""Check that dict keys and value types match expected."""
|
|
329
504
|
assert actual_dict, "Actual dict is empty, check your data"
|
|
@@ -332,12 +507,16 @@ class BaseSoftAssertion:
|
|
|
332
507
|
sorted(iter(actual_dict.keys())),
|
|
333
508
|
sorted(iter(expected_dict.keys())),
|
|
334
509
|
"Wrong keys list.",
|
|
510
|
+
with_screen=with_screen,
|
|
511
|
+
hard_assert=hard_assert,
|
|
335
512
|
)
|
|
336
513
|
for actual_key, actual_value in actual_dict.items():
|
|
337
514
|
self.soft_assert_in(
|
|
338
515
|
member=actual_key,
|
|
339
516
|
container=expected_dict,
|
|
340
517
|
msg=f'Not expected key "{actual_key}".',
|
|
518
|
+
with_screen=with_screen,
|
|
519
|
+
hard_assert=hard_assert,
|
|
341
520
|
)
|
|
342
521
|
if actual_key in expected_dict:
|
|
343
522
|
expected_value = (
|
|
@@ -353,10 +532,18 @@ class BaseSoftAssertion:
|
|
|
353
532
|
f'Key "{actual_key}" value is "{type(actual_value)}", '
|
|
354
533
|
f'expected "{expected_value}". {message}'
|
|
355
534
|
),
|
|
535
|
+
with_screen=with_screen,
|
|
536
|
+
hard_assert=hard_assert,
|
|
356
537
|
)
|
|
357
538
|
|
|
358
539
|
def soft_assert_dicts_with_ignore_types(
|
|
359
|
-
self,
|
|
540
|
+
self,
|
|
541
|
+
current: Any,
|
|
542
|
+
expected: Any,
|
|
543
|
+
msg: str = "",
|
|
544
|
+
*,
|
|
545
|
+
with_screen: bool = False,
|
|
546
|
+
hard_assert: bool | None = None,
|
|
360
547
|
) -> None:
|
|
361
548
|
"""Compare two dicts ignoring value types."""
|
|
362
549
|
expected_dict = _check_namedtuple(expected)
|
|
@@ -371,19 +558,24 @@ class BaseSoftAssertion:
|
|
|
371
558
|
f"Expected: {expected_dict[actual_key]} \n"
|
|
372
559
|
)
|
|
373
560
|
if errors:
|
|
374
|
-
|
|
561
|
+
error = (
|
|
375
562
|
f"{msg}{errors}"
|
|
376
563
|
f"\nExpected dict: {convert_container(expected_dict)}"
|
|
377
564
|
f"\nCurrent dict: {convert_container(current_dict)}"
|
|
378
|
-
|
|
565
|
+
)
|
|
566
|
+
self._emit(
|
|
567
|
+
error, with_screen=with_screen, hard_assert_override=hard_assert
|
|
379
568
|
)
|
|
380
569
|
|
|
381
570
|
def soft_assert_equal_dict_from_clerk_magic(
|
|
382
571
|
self,
|
|
383
572
|
current_dict: dict,
|
|
384
573
|
expected_dict: dict,
|
|
385
|
-
msg: str = None,
|
|
574
|
+
msg: str | None = None,
|
|
386
575
|
coerce: CoerceDict = None,
|
|
576
|
+
*,
|
|
577
|
+
with_screen: bool = False,
|
|
578
|
+
hard_assert: bool | None = None,
|
|
387
579
|
) -> None:
|
|
388
580
|
"""Compare dicts by type, coercing values if needed."""
|
|
389
581
|
|
|
@@ -418,6 +610,8 @@ class BaseSoftAssertion:
|
|
|
418
610
|
f"Unequal_keys:\n\t{unequal_check}\n"
|
|
419
611
|
f"\nMissed_keys:\n\t[{missing_check}]"
|
|
420
612
|
),
|
|
613
|
+
with_screen=with_screen,
|
|
614
|
+
hard_assert=hard_assert,
|
|
421
615
|
)
|
|
422
616
|
|
|
423
617
|
|
|
@@ -426,9 +620,10 @@ class RequestSoftAssertion(BaseSoftAssertion):
|
|
|
426
620
|
|
|
427
621
|
@property
|
|
428
622
|
def url(self) -> str:
|
|
623
|
+
"""Return current URL from session."""
|
|
429
624
|
return self.session.url
|
|
430
625
|
|
|
431
|
-
def base_msg(self, msg: str = None) -> str:
|
|
626
|
+
def base_msg(self, msg: str | None = None) -> str:
|
|
432
627
|
"""Return formatted message including URL and optional extra message."""
|
|
433
628
|
url = f"\n{self.url}" if self.url else ""
|
|
434
629
|
exception = f"\n{msg}" if msg else ""
|
|
@@ -438,54 +633,112 @@ class RequestSoftAssertion(BaseSoftAssertion):
|
|
|
438
633
|
class WebDriverSoftAssertion(BaseSoftAssertion):
|
|
439
634
|
"""Soft assertions for WebDriver-based checks."""
|
|
440
635
|
|
|
636
|
+
_driver: SeleniumWebDriver | None = None
|
|
637
|
+
|
|
441
638
|
@property
|
|
442
|
-
def driver(self) ->
|
|
443
|
-
return self.
|
|
639
|
+
def driver(self) -> SeleniumWebDriver | None:
|
|
640
|
+
return self._driver
|
|
641
|
+
|
|
642
|
+
@driver.setter
|
|
643
|
+
def driver(self, value: SeleniumWebDriver | None) -> None:
|
|
644
|
+
self._driver = value
|
|
444
645
|
|
|
445
646
|
@property
|
|
446
647
|
def url(self) -> str:
|
|
447
|
-
|
|
648
|
+
"""Return current browser URL."""
|
|
649
|
+
return self.driver.current_url if self.driver else ""
|
|
448
650
|
|
|
449
|
-
def base_msg(
|
|
651
|
+
def base_msg(
|
|
652
|
+
self, default_msg: str | None = None, extend_msg: str | None = None
|
|
653
|
+
) -> str:
|
|
450
654
|
"""Return formatted message including default, extended, and URL."""
|
|
451
655
|
default = f"Default message: {default_msg}\n" if default_msg else ""
|
|
452
656
|
extend = f"Extend message: {extend_msg}\n" if extend_msg else ""
|
|
453
657
|
return f"{default}{extend}\nURL: {self.url}"
|
|
454
658
|
|
|
455
659
|
def soft_assert_page_title(
|
|
456
|
-
self,
|
|
660
|
+
self,
|
|
661
|
+
expected_title: str,
|
|
662
|
+
msg: str | None = None,
|
|
663
|
+
*,
|
|
664
|
+
with_screen: bool = False,
|
|
665
|
+
hard_assert: bool | None = None,
|
|
457
666
|
) -> None:
|
|
458
667
|
"""Check that page title matches expected."""
|
|
459
668
|
default_msg = "Wrong page title."
|
|
460
669
|
error = f"{self.base_msg(default_msg=default_msg, extend_msg=msg)}"
|
|
461
|
-
self.soft_assert_equals(
|
|
670
|
+
self.soft_assert_equals(
|
|
671
|
+
self.driver.title if self.driver else None,
|
|
672
|
+
expected_title,
|
|
673
|
+
error,
|
|
674
|
+
with_screen=with_screen,
|
|
675
|
+
hard_assert=hard_assert,
|
|
676
|
+
)
|
|
462
677
|
|
|
463
678
|
def soft_assert_current_url(
|
|
464
|
-
self,
|
|
679
|
+
self,
|
|
680
|
+
expected_url: str,
|
|
681
|
+
msg: str | None = None,
|
|
682
|
+
*,
|
|
683
|
+
with_screen: bool = False,
|
|
684
|
+
hard_assert: bool | None = None,
|
|
465
685
|
) -> None:
|
|
466
686
|
"""Check that current URL matches expected."""
|
|
467
687
|
default_msg = "Wrong current url."
|
|
468
688
|
error = f"{self.base_msg(default_msg=default_msg, extend_msg=msg)}"
|
|
469
|
-
self.soft_assert_equals(
|
|
689
|
+
self.soft_assert_equals(
|
|
690
|
+
self.url,
|
|
691
|
+
expected_url,
|
|
692
|
+
error,
|
|
693
|
+
with_screen=with_screen,
|
|
694
|
+
hard_assert=hard_assert,
|
|
695
|
+
)
|
|
470
696
|
|
|
471
697
|
def soft_assert_current_url_contains(
|
|
472
|
-
self,
|
|
698
|
+
self,
|
|
699
|
+
url_mask: str,
|
|
700
|
+
msg: str | None = None,
|
|
701
|
+
*,
|
|
702
|
+
with_screen: bool = False,
|
|
703
|
+
hard_assert: bool | None = None,
|
|
473
704
|
) -> None:
|
|
474
705
|
"""Check that current URL contains mask."""
|
|
475
706
|
default_msg = f"URL {str(self.url)} doesn't contain {str(url_mask)}."
|
|
476
707
|
error = f"{self.base_msg(default_msg=default_msg, extend_msg=msg)}"
|
|
477
|
-
self.soft_assert_in(
|
|
708
|
+
self.soft_assert_in(
|
|
709
|
+
url_mask,
|
|
710
|
+
self.url,
|
|
711
|
+
error,
|
|
712
|
+
with_screen=with_screen,
|
|
713
|
+
hard_assert=hard_assert,
|
|
714
|
+
)
|
|
478
715
|
|
|
479
716
|
def soft_assert_current_url_not_contains(
|
|
480
|
-
self,
|
|
717
|
+
self,
|
|
718
|
+
url_mask: str,
|
|
719
|
+
msg: str | None = None,
|
|
720
|
+
*,
|
|
721
|
+
with_screen: bool = False,
|
|
722
|
+
hard_assert: bool | None = None,
|
|
481
723
|
) -> None:
|
|
482
724
|
"""Check that current URL does not contain mask."""
|
|
483
725
|
default_msg = f"URL {str(self.url)} contains {str(url_mask)}."
|
|
484
726
|
error = f"{self.base_msg(default_msg=default_msg, extend_msg=msg)}"
|
|
485
|
-
self.soft_assert_not_in(
|
|
727
|
+
self.soft_assert_not_in(
|
|
728
|
+
url_mask,
|
|
729
|
+
self.url,
|
|
730
|
+
error,
|
|
731
|
+
with_screen=with_screen,
|
|
732
|
+
hard_assert=hard_assert,
|
|
733
|
+
)
|
|
486
734
|
|
|
487
735
|
def soft_assert_element_is_present(
|
|
488
|
-
self,
|
|
736
|
+
self,
|
|
737
|
+
element: Any,
|
|
738
|
+
msg: str | None = None,
|
|
739
|
+
*,
|
|
740
|
+
with_screen: bool = True,
|
|
741
|
+
hard_assert: bool | None = None,
|
|
489
742
|
) -> None:
|
|
490
743
|
"""Check that element is present in DOM."""
|
|
491
744
|
default_msg = (
|
|
@@ -493,38 +746,78 @@ class WebDriverSoftAssertion(BaseSoftAssertion):
|
|
|
493
746
|
f" on page at current time."
|
|
494
747
|
)
|
|
495
748
|
error = f"{self.base_msg(default_msg=default_msg, extend_msg=msg)}"
|
|
496
|
-
self.soft_assert_true(
|
|
749
|
+
self.soft_assert_true(
|
|
750
|
+
element.is_present(),
|
|
751
|
+
error,
|
|
752
|
+
with_screen=with_screen,
|
|
753
|
+
hard_assert=hard_assert,
|
|
754
|
+
)
|
|
497
755
|
|
|
498
756
|
def soft_assert_element_is_not_present(
|
|
499
|
-
self,
|
|
757
|
+
self,
|
|
758
|
+
element: Any,
|
|
759
|
+
msg: str | None = None,
|
|
760
|
+
*,
|
|
761
|
+
with_screen: bool = True,
|
|
762
|
+
hard_assert: bool | None = None,
|
|
500
763
|
) -> None:
|
|
501
764
|
"""Check that element is not present in DOM."""
|
|
502
765
|
default_msg = f"Element {element.by}={element.locator} is found on page."
|
|
503
766
|
error = f"{self.base_msg(default_msg=default_msg, extend_msg=msg)}"
|
|
504
|
-
self.soft_assert_false(
|
|
767
|
+
self.soft_assert_false(
|
|
768
|
+
element.is_present(),
|
|
769
|
+
error,
|
|
770
|
+
with_screen=with_screen,
|
|
771
|
+
hard_assert=hard_assert,
|
|
772
|
+
)
|
|
505
773
|
|
|
506
774
|
def soft_assert_element_is_displayed(
|
|
507
|
-
self,
|
|
775
|
+
self,
|
|
776
|
+
element: Any,
|
|
777
|
+
msg: str | None = None,
|
|
778
|
+
*,
|
|
779
|
+
with_screen: bool = True,
|
|
780
|
+
hard_assert: bool | None = None,
|
|
508
781
|
) -> None:
|
|
509
782
|
"""Check that element is visible to user."""
|
|
510
783
|
default_msg = (
|
|
511
784
|
f"Element {element.by}={element.locator} is not visible to a user."
|
|
512
785
|
)
|
|
513
786
|
error = f"{self.base_msg(default_msg=default_msg, extend_msg=msg)}"
|
|
514
|
-
self.soft_assert_true(
|
|
787
|
+
self.soft_assert_true(
|
|
788
|
+
element.is_displayed(),
|
|
789
|
+
error,
|
|
790
|
+
with_screen=with_screen,
|
|
791
|
+
hard_assert=hard_assert,
|
|
792
|
+
)
|
|
515
793
|
|
|
516
794
|
def soft_assert_element_is_not_displayed(
|
|
517
|
-
self,
|
|
795
|
+
self,
|
|
796
|
+
element: Any,
|
|
797
|
+
msg: str | None = None,
|
|
798
|
+
*,
|
|
799
|
+
with_screen: bool = True,
|
|
800
|
+
hard_assert: bool | None = None,
|
|
518
801
|
) -> None:
|
|
519
802
|
"""Check that element is not visible to user."""
|
|
520
803
|
default_msg = (
|
|
521
804
|
f"Element {element.by}={element.locator} is visible to a user."
|
|
522
805
|
)
|
|
523
806
|
error = f"{self.base_msg(default_msg=default_msg, extend_msg=msg)}"
|
|
524
|
-
self.soft_assert_false(
|
|
807
|
+
self.soft_assert_false(
|
|
808
|
+
element.is_displayed(),
|
|
809
|
+
error,
|
|
810
|
+
with_screen=with_screen,
|
|
811
|
+
hard_assert=hard_assert,
|
|
812
|
+
)
|
|
525
813
|
|
|
526
814
|
def soft_assert_element_displayed_in_viewport(
|
|
527
|
-
self,
|
|
815
|
+
self,
|
|
816
|
+
element: Any,
|
|
817
|
+
msg: str | None = None,
|
|
818
|
+
*,
|
|
819
|
+
with_screen: bool = True,
|
|
820
|
+
hard_assert: bool | None = None,
|
|
528
821
|
) -> None:
|
|
529
822
|
"""Check that element is inside viewport."""
|
|
530
823
|
element_in_viewport = self.driver.execute_script(
|
|
@@ -565,25 +858,34 @@ class WebDriverSoftAssertion(BaseSoftAssertion):
|
|
|
565
858
|
error = (
|
|
566
859
|
f"{self.base_msg(default_msg=default_msg, extend_msg=msg)}\n\n"
|
|
567
860
|
)
|
|
568
|
-
self.
|
|
861
|
+
self._emit(
|
|
862
|
+
error, with_screen=with_screen, hard_assert_override=hard_assert
|
|
863
|
+
)
|
|
569
864
|
|
|
570
865
|
def soft_assert_image_status_code(
|
|
571
|
-
self,
|
|
866
|
+
self,
|
|
867
|
+
image: Any,
|
|
868
|
+
status_code: int = 200,
|
|
869
|
+
msg: str | None = None,
|
|
870
|
+
*,
|
|
871
|
+
with_screen: bool = False,
|
|
872
|
+
hard_assert: bool | None = None,
|
|
572
873
|
) -> str | None:
|
|
573
874
|
"""Check that image URL returns expected status code."""
|
|
574
875
|
if not image.get_attribute("src"):
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
f" 'src'\n\n{self._callsite_line()}\n"
|
|
876
|
+
error = f"{base_msg(msg)}Image does not have attribute 'src'\n"
|
|
877
|
+
self._emit(
|
|
878
|
+
error, with_screen=with_screen, hard_assert_override=hard_assert
|
|
579
879
|
)
|
|
880
|
+
return error
|
|
580
881
|
|
|
581
882
|
img_url = image.get_attribute("src")
|
|
582
883
|
response = http.request(url=img_url, method="GET")
|
|
583
|
-
|
|
584
884
|
assert_msg = msg if msg else "img status code != 200"
|
|
585
|
-
self.soft_assert_equals(
|
|
885
|
+
return self.soft_assert_equals(
|
|
586
886
|
response.status,
|
|
587
887
|
status_code,
|
|
588
888
|
msg=assert_msg,
|
|
889
|
+
with_screen=with_screen,
|
|
890
|
+
hard_assert=hard_assert,
|
|
589
891
|
)
|
|
@@ -4,6 +4,10 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
from _pytest.config import Config
|
|
6
6
|
from _pytest.runner import CallInfo
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.syntax import Syntax
|
|
10
|
+
from rich.text import Text
|
|
7
11
|
from typing import NamedTuple
|
|
8
12
|
|
|
9
13
|
import allure
|
|
@@ -277,94 +281,79 @@ def color_msg(msg: str, color: str = "bold red") -> str:
|
|
|
277
281
|
return f"[{color}]{msg}[/{color}]"
|
|
278
282
|
|
|
279
283
|
|
|
284
|
+
def _panel_width() -> int:
|
|
285
|
+
env_w = os.getenv("PROMIUM_ASSERTION_PANEL_WIDTH")
|
|
286
|
+
try:
|
|
287
|
+
term_w = os.get_terminal_size().columns
|
|
288
|
+
except Exception:
|
|
289
|
+
term_w = 120
|
|
290
|
+
if env_w is None:
|
|
291
|
+
return term_w
|
|
292
|
+
try:
|
|
293
|
+
w = int(env_w)
|
|
294
|
+
if w < 0 or w > term_w:
|
|
295
|
+
return term_w
|
|
296
|
+
return w
|
|
297
|
+
except Exception:
|
|
298
|
+
return term_w
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _render_panel(text: str, title: str) -> str:
|
|
302
|
+
width = _panel_width()
|
|
303
|
+
border_style = "none" if mono_color else "red"
|
|
304
|
+
buf = io.StringIO()
|
|
305
|
+
console = Console(file=buf, force_terminal=True, width=width - 5)
|
|
306
|
+
msg = (
|
|
307
|
+
Text(text.strip(), justify="left")
|
|
308
|
+
if mono_color
|
|
309
|
+
else Syntax(text.strip(), "sql", word_wrap=True)
|
|
310
|
+
)
|
|
311
|
+
panel = Panel(
|
|
312
|
+
renderable=msg,
|
|
313
|
+
title=color_msg(title),
|
|
314
|
+
border_style=border_style,
|
|
315
|
+
padding=(1, 1),
|
|
316
|
+
)
|
|
317
|
+
console.print(panel)
|
|
318
|
+
return buf.getvalue()
|
|
319
|
+
|
|
320
|
+
|
|
280
321
|
@pytest.mark.trylast
|
|
281
322
|
def pytest_runtest_call(item: pytest.Item) -> None:
|
|
282
323
|
separator = "-" * 46
|
|
283
|
-
|
|
284
|
-
from rich.console import Console # noqa: PLC0415
|
|
285
|
-
from rich.panel import Panel # noqa: PLC0415
|
|
286
|
-
from rich.syntax import Syntax # noqa: PLC0415
|
|
287
|
-
from rich.text import Text # noqa: PLC0415
|
|
288
|
-
|
|
289
|
-
try:
|
|
290
|
-
terminal_width = os.get_terminal_size().columns
|
|
291
|
-
except Exception:
|
|
292
|
-
terminal_width = 120
|
|
293
|
-
|
|
294
|
-
try:
|
|
295
|
-
PANEL_WIDTH = int(PANEL_WIDTH)
|
|
296
|
-
if PANEL_WIDTH < 0 or terminal_width < PANEL_WIDTH:
|
|
297
|
-
PANEL_WIDTH = terminal_width
|
|
298
|
-
except Exception:
|
|
299
|
-
PANEL_WIDTH = terminal_width
|
|
300
|
-
if mono_color:
|
|
301
|
-
border_style = "none"
|
|
302
|
-
else:
|
|
303
|
-
border_style = "red"
|
|
324
|
+
use_rich = os.getenv("PROMIUM_ASSERTION_PANEL_WIDTH") is not None
|
|
304
325
|
|
|
305
326
|
if hasattr(item.config, "assertion_errors"):
|
|
306
327
|
if item.config.assertion_errors:
|
|
307
|
-
if
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
force_terminal=True,
|
|
312
|
-
width=PANEL_WIDTH - 5,
|
|
328
|
+
if use_rich:
|
|
329
|
+
rich_output = "".join(
|
|
330
|
+
_render_panel(err.strip(), "Soft Assertion Error")
|
|
331
|
+
for err in item.config.assertion_errors
|
|
313
332
|
)
|
|
314
|
-
for error_text in item.config.assertion_errors:
|
|
315
|
-
text = error_text.strip()
|
|
316
|
-
if mono_color:
|
|
317
|
-
msg = Text(text, justify="left")
|
|
318
|
-
else:
|
|
319
|
-
msg = Syntax(text, "sql", word_wrap=True)
|
|
320
|
-
rich_panel = Panel(
|
|
321
|
-
renderable=msg,
|
|
322
|
-
title=color_msg("Soft Assertion Error"),
|
|
323
|
-
border_style=border_style,
|
|
324
|
-
padding=(1, 1),
|
|
325
|
-
)
|
|
326
|
-
console.print(rich_panel)
|
|
327
|
-
rich_output = string_io.getvalue()
|
|
328
333
|
raise AssertionError(f"\n{rich_output}")
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
raise AssertionError(msg)
|
|
334
|
+
assertion_errors_list = f"{separator}\n".join(
|
|
335
|
+
item.config.assertion_errors
|
|
336
|
+
)
|
|
337
|
+
msg = f"\n{separator}\n{assertion_errors_list}{separator}\n"
|
|
338
|
+
raise AssertionError(msg)
|
|
335
339
|
|
|
336
340
|
if item.config.getoption("--check-console"):
|
|
337
341
|
if hasattr(item.config, "check_console_errors"):
|
|
338
342
|
if console_errors := item.config.check_console_errors(item):
|
|
339
|
-
if
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
force_terminal=True,
|
|
344
|
-
width=PANEL_WIDTH - 5,
|
|
345
|
-
)
|
|
346
|
-
for error_text in console_errors:
|
|
347
|
-
text = error_text.strip()
|
|
348
|
-
if mono_color:
|
|
349
|
-
msg = Text(text, justify="left")
|
|
350
|
-
else:
|
|
351
|
-
msg = Syntax(text, "sql", word_wrap=True)
|
|
352
|
-
rich_panel = Panel(
|
|
353
|
-
renderable=msg,
|
|
354
|
-
title=color_msg("Browser console js errors found"),
|
|
355
|
-
border_style=border_style,
|
|
356
|
-
padding=(1, 1),
|
|
343
|
+
if use_rich:
|
|
344
|
+
rich_output = "".join(
|
|
345
|
+
_render_panel(
|
|
346
|
+
err.strip(), "Browser console js errors found"
|
|
357
347
|
)
|
|
358
|
-
|
|
359
|
-
rich_output = string_io.getvalue()
|
|
360
|
-
raise BrowserConsoleException(f"\n{rich_output}")
|
|
361
|
-
else: # noqa: RET506
|
|
362
|
-
console_errors_list = f"{separator}\n".join(console_errors)
|
|
363
|
-
msg = (
|
|
364
|
-
f"Browser console js errors found:"
|
|
365
|
-
f"\n{separator}\n{console_errors_list}{separator}\n"
|
|
348
|
+
for err in console_errors
|
|
366
349
|
)
|
|
367
|
-
raise BrowserConsoleException(
|
|
350
|
+
raise BrowserConsoleException(f"\n{rich_output}")
|
|
351
|
+
console_errors_list = f"{separator}\n".join(console_errors)
|
|
352
|
+
msg = (
|
|
353
|
+
f"Browser console js errors found:"
|
|
354
|
+
f"\n{separator}\n{console_errors_list}{separator}\n"
|
|
355
|
+
)
|
|
356
|
+
raise BrowserConsoleException(msg)
|
|
368
357
|
|
|
369
358
|
|
|
370
359
|
@pytest.mark.tryfirst
|
|
@@ -396,7 +385,6 @@ def pytest_runtest_makereport(item: pytest.Item, call: CallInfo) -> None:
|
|
|
396
385
|
name="SCREENSHOT",
|
|
397
386
|
attachment_type=allure.attachment_type.PNG,
|
|
398
387
|
)
|
|
399
|
-
|
|
400
388
|
elif fail_debug.test_type == "request":
|
|
401
389
|
fail_info = (
|
|
402
390
|
f"\nURL: {fail_debug.url}"
|
|
@@ -423,30 +411,56 @@ def pytest_runtest_makereport(item: pytest.Item, call: CallInfo) -> None:
|
|
|
423
411
|
name="RUN COMMAND",
|
|
424
412
|
attachment_type=allure.attachment_type.HTML,
|
|
425
413
|
)
|
|
426
|
-
|
|
427
414
|
except WebDriverException as e:
|
|
428
415
|
fail_info = f"\nWebdriver instance must have crushed: {e.msg}"
|
|
429
416
|
finally:
|
|
430
417
|
longrepr = getattr(report, "longrepr", None)
|
|
431
418
|
if hasattr(longrepr, "addsection"):
|
|
432
|
-
longrepr.sections.
|
|
433
|
-
|
|
434
|
-
|
|
419
|
+
longrepr.sections.append((
|
|
420
|
+
f"Captured stdout {when}",
|
|
421
|
+
fail_info,
|
|
422
|
+
"-",
|
|
423
|
+
))
|
|
424
|
+
else:
|
|
425
|
+
report.sections.append((
|
|
426
|
+
f"Captured stdout {when}",
|
|
427
|
+
fail_info,
|
|
428
|
+
))
|
|
429
|
+
|
|
430
|
+
if call.excinfo is not None:
|
|
431
|
+
exc = call.excinfo.value
|
|
432
|
+
panel_text = getattr(exc, "_promium_panel_text", None)
|
|
433
|
+
if (
|
|
434
|
+
panel_text
|
|
435
|
+
and os.getenv("PROMIUM_ASSERTION_PANEL_WIDTH") is not None
|
|
436
|
+
):
|
|
437
|
+
title = getattr(
|
|
438
|
+
exc, "_promium_panel_title", "Soft Assertion Error"
|
|
439
|
+
)
|
|
440
|
+
rich_output = _render_panel(panel_text, title)
|
|
441
|
+
report.sections.append((title, "\n" + rich_output))
|
|
435
442
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
443
|
+
if call.when == "teardown" and hasattr(item, "capturelog_handler"):
|
|
444
|
+
root_logger = logging.getLogger()
|
|
445
|
+
try:
|
|
446
|
+
root_logger.removeHandler(item.capturelog_handler)
|
|
447
|
+
finally:
|
|
448
|
+
item.capturelog_handler.close()
|
|
449
|
+
del item.capturelog_handler
|
|
450
|
+
|
|
451
|
+
if item.config.getoption("--duration-time"):
|
|
452
|
+
duration = call.stop - call.start
|
|
453
|
+
if duration > 120:
|
|
454
|
+
report.sections.append((
|
|
455
|
+
f"Captured stdout {when}",
|
|
456
|
+
(
|
|
457
|
+
"\n\n!!!!! The very slow test. Duration time is {} !!!!!\n\n"
|
|
458
|
+
).format(
|
|
459
|
+
datetime.datetime.fromtimestamp(duration).strftime(
|
|
460
|
+
"%M min %S sec"
|
|
461
|
+
)
|
|
462
|
+
),
|
|
463
|
+
))
|
|
450
464
|
|
|
451
465
|
|
|
452
466
|
def pytest_configure(config: Config) -> None:
|
|
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
|
|
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
|
|
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
|
|
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
|