seleniumbase 4.41.3__py3-none-any.whl → 4.45.10__py3-none-any.whl
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.
- sbase/steps.py +9 -0
- seleniumbase/__version__.py +1 -1
- seleniumbase/behave/behave_helper.py +2 -0
- seleniumbase/behave/behave_sb.py +21 -8
- seleniumbase/common/decorators.py +3 -1
- seleniumbase/console_scripts/run.py +1 -0
- seleniumbase/console_scripts/sb_caseplans.py +3 -4
- seleniumbase/console_scripts/sb_install.py +142 -11
- seleniumbase/console_scripts/sb_mkchart.py +1 -2
- seleniumbase/console_scripts/sb_mkdir.py +99 -29
- seleniumbase/console_scripts/sb_mkfile.py +1 -2
- seleniumbase/console_scripts/sb_mkpres.py +1 -2
- seleniumbase/console_scripts/sb_mkrec.py +26 -2
- seleniumbase/console_scripts/sb_objectify.py +4 -5
- seleniumbase/console_scripts/sb_print.py +1 -1
- seleniumbase/console_scripts/sb_recorder.py +40 -3
- seleniumbase/core/browser_launcher.py +474 -151
- seleniumbase/core/detect_b_ver.py +258 -16
- seleniumbase/core/log_helper.py +15 -21
- seleniumbase/core/mysql.py +1 -1
- seleniumbase/core/recorder_helper.py +3 -0
- seleniumbase/core/report_helper.py +9 -12
- seleniumbase/core/sb_cdp.py +734 -215
- seleniumbase/core/sb_driver.py +46 -5
- seleniumbase/core/session_helper.py +2 -4
- seleniumbase/core/tour_helper.py +1 -2
- seleniumbase/drivers/atlas_drivers/__init__.py +0 -0
- seleniumbase/drivers/brave_drivers/__init__.py +0 -0
- seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
- seleniumbase/drivers/comet_drivers/__init__.py +0 -0
- seleniumbase/drivers/opera_drivers/__init__.py +0 -0
- seleniumbase/fixtures/base_case.py +448 -251
- seleniumbase/fixtures/constants.py +36 -9
- seleniumbase/fixtures/js_utils.py +77 -18
- seleniumbase/fixtures/page_actions.py +41 -13
- seleniumbase/fixtures/page_utils.py +19 -12
- seleniumbase/fixtures/shared_utils.py +64 -6
- seleniumbase/masterqa/master_qa.py +16 -2
- seleniumbase/plugins/base_plugin.py +8 -0
- seleniumbase/plugins/basic_test_info.py +2 -3
- seleniumbase/plugins/driver_manager.py +131 -5
- seleniumbase/plugins/page_source.py +2 -3
- seleniumbase/plugins/pytest_plugin.py +244 -79
- seleniumbase/plugins/sb_manager.py +143 -20
- seleniumbase/plugins/selenium_plugin.py +144 -12
- seleniumbase/translate/translator.py +2 -3
- seleniumbase/undetected/__init__.py +17 -13
- seleniumbase/undetected/cdp.py +1 -12
- seleniumbase/undetected/cdp_driver/browser.py +330 -129
- seleniumbase/undetected/cdp_driver/cdp_util.py +328 -61
- seleniumbase/undetected/cdp_driver/config.py +110 -14
- seleniumbase/undetected/cdp_driver/connection.py +18 -48
- seleniumbase/undetected/cdp_driver/element.py +105 -33
- seleniumbase/undetected/cdp_driver/tab.py +414 -39
- seleniumbase/utilities/selenium_grid/download_selenium_server.py +1 -1
- seleniumbase/utilities/selenium_grid/grid_hub.py +1 -2
- seleniumbase/utilities/selenium_grid/grid_node.py +2 -3
- seleniumbase/utilities/selenium_ide/convert_ide.py +2 -3
- {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/METADATA +193 -166
- {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/RECORD +64 -59
- {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/licenses/LICENSE +1 -1
- {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/WHEEL +0 -0
- {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/top_level.txt +0 -0
|
@@ -32,7 +32,6 @@ Improvements include making WebDriver more robust, reliable, and flexible.
|
|
|
32
32
|
Page elements are given enough time to load before WebDriver acts on them.
|
|
33
33
|
Code becomes greatly simplified and easier to maintain."""
|
|
34
34
|
|
|
35
|
-
import codecs
|
|
36
35
|
import colorama
|
|
37
36
|
import fasteners
|
|
38
37
|
import json
|
|
@@ -114,9 +113,7 @@ class BaseCase(unittest.TestCase):
|
|
|
114
113
|
self.driver = None
|
|
115
114
|
self.environment = None
|
|
116
115
|
self.env = None # Add a shortened version of self.environment
|
|
117
|
-
self.version_list =
|
|
118
|
-
int(i) for i in __version__.split(".") if i.isdigit()
|
|
119
|
-
]
|
|
116
|
+
self.version_list = shared_utils.make_version_list(__version__)
|
|
120
117
|
self.version_tuple = tuple(self.version_list)
|
|
121
118
|
self.version_info = self.version_tuple
|
|
122
119
|
self.time = time.time
|
|
@@ -134,6 +131,7 @@ class BaseCase(unittest.TestCase):
|
|
|
134
131
|
self.__requests_timeout = None
|
|
135
132
|
self.__page_source_count = 0
|
|
136
133
|
self.__screenshot_count = 0
|
|
134
|
+
self.__saved_pdf_count = 0
|
|
137
135
|
self.__logs_data_count = 0
|
|
138
136
|
self.__last_data_file = None
|
|
139
137
|
self.__level_0_visual_f = False
|
|
@@ -223,11 +221,28 @@ class BaseCase(unittest.TestCase):
|
|
|
223
221
|
[sys.executable, "-m", "pytest", file, "-s", *all_args]
|
|
224
222
|
)
|
|
225
223
|
|
|
226
|
-
def open(self, url):
|
|
224
|
+
def open(self, url, **kwargs):
|
|
227
225
|
"""Navigates the current browser window to the specified page."""
|
|
228
226
|
self.__check_scope()
|
|
229
227
|
if self.__is_cdp_swap_needed():
|
|
230
|
-
self.cdp.open(url)
|
|
228
|
+
self.cdp.open(url, **kwargs)
|
|
229
|
+
return
|
|
230
|
+
elif (
|
|
231
|
+
getattr(self.driver, "_is_using_uc", None)
|
|
232
|
+
# and getattr(self.driver, "_is_using_auth", None)
|
|
233
|
+
and not getattr(self.driver, "_is_using_cdp", None)
|
|
234
|
+
):
|
|
235
|
+
# Auth in UC Mode requires CDP Mode
|
|
236
|
+
# (and now we're always forcing it)
|
|
237
|
+
logging.info("open() in UC Mode now always activates CDP Mode.")
|
|
238
|
+
self.activate_cdp_mode(url, **kwargs)
|
|
239
|
+
return
|
|
240
|
+
elif (
|
|
241
|
+
getattr(self.driver, "_is_using_uc", None)
|
|
242
|
+
and getattr(self.driver, "_is_using_cdp", None)
|
|
243
|
+
):
|
|
244
|
+
self.disconnect()
|
|
245
|
+
self.cdp.open(url, **kwargs)
|
|
231
246
|
return
|
|
232
247
|
self._check_browser()
|
|
233
248
|
if self.__needs_minimum_wait():
|
|
@@ -1295,8 +1310,11 @@ class BaseCase(unittest.TestCase):
|
|
|
1295
1310
|
self.__check_scope()
|
|
1296
1311
|
return self.execute_script("return window.location.origin;")
|
|
1297
1312
|
|
|
1298
|
-
def
|
|
1299
|
-
|
|
1313
|
+
def get_html(self, *args, **kwargs):
|
|
1314
|
+
return self.get_page_source(*args, **kwargs)
|
|
1315
|
+
|
|
1316
|
+
def get_page_source(self, *args, **kwargs):
|
|
1317
|
+
if self.__is_cdp_swap_needed(*args, **kwargs):
|
|
1300
1318
|
return self.cdp.get_page_source()
|
|
1301
1319
|
self.wait_for_ready_state_complete()
|
|
1302
1320
|
if self.__needs_minimum_wait:
|
|
@@ -1335,7 +1353,7 @@ class BaseCase(unittest.TestCase):
|
|
|
1335
1353
|
if self.__is_cdp_swap_needed():
|
|
1336
1354
|
self.cdp.go_back()
|
|
1337
1355
|
return
|
|
1338
|
-
if
|
|
1356
|
+
if getattr(self, "recorder_mode", None):
|
|
1339
1357
|
self.save_recorded_actions()
|
|
1340
1358
|
pre_action_url = None
|
|
1341
1359
|
with suppress(Exception):
|
|
@@ -1363,7 +1381,7 @@ class BaseCase(unittest.TestCase):
|
|
|
1363
1381
|
if self.__is_cdp_swap_needed():
|
|
1364
1382
|
self.cdp.go_forward()
|
|
1365
1383
|
return
|
|
1366
|
-
if
|
|
1384
|
+
if getattr(self, "recorder_mode", None):
|
|
1367
1385
|
self.save_recorded_actions()
|
|
1368
1386
|
self.__last_page_load_url = None
|
|
1369
1387
|
self.driver.forward()
|
|
@@ -1502,6 +1520,10 @@ class BaseCase(unittest.TestCase):
|
|
|
1502
1520
|
):
|
|
1503
1521
|
"""Returns True if the element attribute/value is found.
|
|
1504
1522
|
If the value is not specified, the attribute only needs to exist."""
|
|
1523
|
+
if self.__is_cdp_swap_needed():
|
|
1524
|
+
return self.cdp.is_attribute_present(
|
|
1525
|
+
selector, attribute, value=value
|
|
1526
|
+
)
|
|
1505
1527
|
self.wait_for_ready_state_complete()
|
|
1506
1528
|
time.sleep(0.01)
|
|
1507
1529
|
selector, by = self.__recalculate_selector(selector, by)
|
|
@@ -1615,14 +1637,14 @@ class BaseCase(unittest.TestCase):
|
|
|
1615
1637
|
def click_link_text(self, link_text, timeout=None):
|
|
1616
1638
|
"""This method clicks link text on a page."""
|
|
1617
1639
|
self.__check_scope()
|
|
1618
|
-
if self.__is_cdp_swap_needed():
|
|
1619
|
-
self.cdp.find_element(link_text, timeout=timeout).click()
|
|
1620
|
-
return
|
|
1621
|
-
self.__skip_if_esc()
|
|
1622
1640
|
if not timeout:
|
|
1623
1641
|
timeout = settings.SMALL_TIMEOUT
|
|
1624
1642
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
|
1625
1643
|
timeout = self.__get_new_timeout(timeout)
|
|
1644
|
+
if self.__is_cdp_swap_needed():
|
|
1645
|
+
self.cdp.find_element(link_text, timeout=timeout).click()
|
|
1646
|
+
return
|
|
1647
|
+
self.__skip_if_esc()
|
|
1626
1648
|
link_text = self.__get_type_checked_text(link_text)
|
|
1627
1649
|
if self.__is_cdp_swap_needed():
|
|
1628
1650
|
self.cdp.click_link(link_text)
|
|
@@ -2695,7 +2717,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2695
2717
|
original_by = by
|
|
2696
2718
|
selector, by = self.__recalculate_selector(selector, by)
|
|
2697
2719
|
if self.__is_cdp_swap_needed():
|
|
2698
|
-
self.cdp.
|
|
2720
|
+
self.cdp.hover_element(selector)
|
|
2699
2721
|
return
|
|
2700
2722
|
self.wait_for_element_visible(
|
|
2701
2723
|
original_selector, by=original_by, timeout=timeout
|
|
@@ -2740,7 +2762,7 @@ class BaseCase(unittest.TestCase):
|
|
|
2740
2762
|
click_selector, click_by
|
|
2741
2763
|
)
|
|
2742
2764
|
if self.__is_cdp_swap_needed():
|
|
2743
|
-
self.cdp.
|
|
2765
|
+
self.cdp.hover_and_click(hover_selector, click_selector)
|
|
2744
2766
|
return
|
|
2745
2767
|
dropdown_element = self.wait_for_element_visible(
|
|
2746
2768
|
original_selector, by=original_by, timeout=timeout
|
|
@@ -3198,6 +3220,9 @@ class BaseCase(unittest.TestCase):
|
|
|
3198
3220
|
timeout = settings.SMALL_TIMEOUT
|
|
3199
3221
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
|
3200
3222
|
timeout = self.__get_new_timeout(timeout)
|
|
3223
|
+
if self.__is_cdp_swap_needed():
|
|
3224
|
+
self.cdp.select_option_by_index(dropdown_selector, option)
|
|
3225
|
+
return
|
|
3201
3226
|
self.__select_option(
|
|
3202
3227
|
dropdown_selector,
|
|
3203
3228
|
option,
|
|
@@ -3222,6 +3247,9 @@ class BaseCase(unittest.TestCase):
|
|
|
3222
3247
|
timeout = settings.SMALL_TIMEOUT
|
|
3223
3248
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
|
3224
3249
|
timeout = self.__get_new_timeout(timeout)
|
|
3250
|
+
if self.__is_cdp_swap_needed():
|
|
3251
|
+
self.cdp.select_option_by_value(dropdown_selector, option)
|
|
3252
|
+
return
|
|
3225
3253
|
self.__select_option(
|
|
3226
3254
|
dropdown_selector,
|
|
3227
3255
|
option,
|
|
@@ -3418,6 +3446,46 @@ class BaseCase(unittest.TestCase):
|
|
|
3418
3446
|
file_path = os.path.join(abs_path, html_file)
|
|
3419
3447
|
self.open("file://" + file_path)
|
|
3420
3448
|
|
|
3449
|
+
def evaluate(self, expression):
|
|
3450
|
+
"""Run a JavaScript expression and return the result."""
|
|
3451
|
+
self.__check_scope()
|
|
3452
|
+
if self.__is_cdp_swap_needed():
|
|
3453
|
+
return self.cdp.evaluate(expression)
|
|
3454
|
+
self._check_browser()
|
|
3455
|
+
original_expression = expression
|
|
3456
|
+
expression = expression.strip()
|
|
3457
|
+
exp_list = expression.split("\n")
|
|
3458
|
+
if exp_list and exp_list[-1].strip().startswith("return "):
|
|
3459
|
+
expression = (
|
|
3460
|
+
"\n".join(exp_list[0:-1]) + "\n"
|
|
3461
|
+
+ exp_list[-1].strip()[len("return "):]
|
|
3462
|
+
).strip()
|
|
3463
|
+
evaluation = self.driver.execute_cdp_cmd(
|
|
3464
|
+
"Runtime.evaluate",
|
|
3465
|
+
{
|
|
3466
|
+
"expression": expression
|
|
3467
|
+
},
|
|
3468
|
+
)
|
|
3469
|
+
if "value" in evaluation["result"]:
|
|
3470
|
+
return evaluation["result"]["value"]
|
|
3471
|
+
elif evaluation["result"]["type"] == "undefined":
|
|
3472
|
+
return None
|
|
3473
|
+
elif "exceptionDetails" in evaluation:
|
|
3474
|
+
raise Exception(evaluation["result"]["description"], expression)
|
|
3475
|
+
elif evaluation["result"]["type"] == "object":
|
|
3476
|
+
if "return " not in original_expression:
|
|
3477
|
+
expression = "return " + original_expression.strip()
|
|
3478
|
+
# Need to use execute_script() to return a WebDriver object.
|
|
3479
|
+
# If this causes duplicate evaluation, don't use evaluate().
|
|
3480
|
+
return self.execute_script(expression)
|
|
3481
|
+
elif evaluation["result"]["type"] == "function":
|
|
3482
|
+
return {} # This is what sb.cdp.evaluate returns
|
|
3483
|
+
elif "description" in evaluation["result"]:
|
|
3484
|
+
# At this point, the description is the exception
|
|
3485
|
+
raise Exception(evaluation["result"]["description"], expression)
|
|
3486
|
+
else: # Possibly an unhandled case if reached
|
|
3487
|
+
return None
|
|
3488
|
+
|
|
3421
3489
|
def execute_script(self, script, *args, **kwargs):
|
|
3422
3490
|
self.__check_scope()
|
|
3423
3491
|
if self.__is_cdp_swap_needed():
|
|
@@ -3569,9 +3637,18 @@ class BaseCase(unittest.TestCase):
|
|
|
3569
3637
|
self.cdp.maximize()
|
|
3570
3638
|
return
|
|
3571
3639
|
self._check_browser()
|
|
3572
|
-
|
|
3640
|
+
try:
|
|
3641
|
+
self.driver.maximize_window()
|
|
3642
|
+
except Exception:
|
|
3643
|
+
with suppress(Exception):
|
|
3644
|
+
width = self.execute_script("return screen.availWidth;")
|
|
3645
|
+
height = self.execute_script("return screen.availHeight;")
|
|
3646
|
+
self.set_window_rect(0, 0, width, height)
|
|
3573
3647
|
self.__demo_mode_pause_if_active(tiny=True)
|
|
3574
3648
|
|
|
3649
|
+
def maximize(self):
|
|
3650
|
+
self.maximize_window()
|
|
3651
|
+
|
|
3575
3652
|
def minimize_window(self):
|
|
3576
3653
|
self.__check_scope()
|
|
3577
3654
|
if self.__is_cdp_swap_needed():
|
|
@@ -3581,6 +3658,9 @@ class BaseCase(unittest.TestCase):
|
|
|
3581
3658
|
self.driver.minimize_window()
|
|
3582
3659
|
self.__demo_mode_pause_if_active(tiny=True)
|
|
3583
3660
|
|
|
3661
|
+
def minimize(self):
|
|
3662
|
+
self.minimize_window()
|
|
3663
|
+
|
|
3584
3664
|
def reset_window_size(self):
|
|
3585
3665
|
self.__check_scope()
|
|
3586
3666
|
if self.__is_cdp_swap_needed():
|
|
@@ -3920,10 +4000,23 @@ class BaseCase(unittest.TestCase):
|
|
|
3920
4000
|
Reverts self.set_content_to_frame()."""
|
|
3921
4001
|
self.set_content_to_default(nested=True)
|
|
3922
4002
|
|
|
3923
|
-
def open_new_window(self, switch_to=True):
|
|
4003
|
+
def open_new_window(self, switch_to=True, **kwargs):
|
|
3924
4004
|
"""Opens a new browser tab/window and switches to it by default."""
|
|
4005
|
+
url = None
|
|
4006
|
+
if self.__looks_like_a_page_url(str(switch_to)):
|
|
4007
|
+
# Different API for CDP Mode: First arg is a `url`.
|
|
4008
|
+
# (Also, don't break backwards compat for reg mode)
|
|
4009
|
+
url = switch_to
|
|
4010
|
+
switch_to = True
|
|
3925
4011
|
if self.__is_cdp_swap_needed():
|
|
3926
|
-
self.cdp.open_new_tab(switch_to=switch_to)
|
|
4012
|
+
self.cdp.open_new_tab(url=url, switch_to=switch_to, **kwargs)
|
|
4013
|
+
return
|
|
4014
|
+
elif (
|
|
4015
|
+
getattr(self.driver, "_is_using_uc", None)
|
|
4016
|
+
and getattr(self.driver, "_is_using_cdp", None)
|
|
4017
|
+
):
|
|
4018
|
+
self.disconnect()
|
|
4019
|
+
self.cdp.open_new_tab(url=url, switch_to=switch_to, **kwargs)
|
|
3927
4020
|
return
|
|
3928
4021
|
self.wait_for_ready_state_complete()
|
|
3929
4022
|
if switch_to:
|
|
@@ -4336,7 +4429,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4336
4429
|
if self.is_chromium():
|
|
4337
4430
|
try:
|
|
4338
4431
|
if self.maximize_option:
|
|
4339
|
-
self.
|
|
4432
|
+
self.maximize_window()
|
|
4340
4433
|
self.wait_for_ready_state_complete()
|
|
4341
4434
|
else:
|
|
4342
4435
|
pass # Now handled in browser_launcher.py
|
|
@@ -4346,7 +4439,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4346
4439
|
elif self.browser == "firefox":
|
|
4347
4440
|
try:
|
|
4348
4441
|
if self.maximize_option:
|
|
4349
|
-
self.
|
|
4442
|
+
self.maximize_window()
|
|
4350
4443
|
self.wait_for_ready_state_complete()
|
|
4351
4444
|
else:
|
|
4352
4445
|
with suppress(Exception):
|
|
@@ -4356,7 +4449,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4356
4449
|
elif self.browser == "safari":
|
|
4357
4450
|
if self.maximize_option:
|
|
4358
4451
|
try:
|
|
4359
|
-
self.
|
|
4452
|
+
self.maximize_window()
|
|
4360
4453
|
self.wait_for_ready_state_complete()
|
|
4361
4454
|
except Exception:
|
|
4362
4455
|
pass # Keep existing browser resolution
|
|
@@ -4486,7 +4579,8 @@ class BaseCase(unittest.TestCase):
|
|
|
4486
4579
|
If a provided selector is not found, then takes a full-page screenshot.
|
|
4487
4580
|
(The last_page / failure screenshot is always "screenshot.png")
|
|
4488
4581
|
The screenshot will be in PNG format."""
|
|
4489
|
-
self.
|
|
4582
|
+
if not self.__is_cdp_swap_needed():
|
|
4583
|
+
self.wait_for_ready_state_complete()
|
|
4490
4584
|
test_logpath = os.path.join(self.log_path, self.__get_test_id())
|
|
4491
4585
|
self.__create_log_path_as_needed(test_logpath)
|
|
4492
4586
|
if name:
|
|
@@ -4504,6 +4598,11 @@ class BaseCase(unittest.TestCase):
|
|
|
4504
4598
|
if selector and by:
|
|
4505
4599
|
selector, by = self.__recalculate_selector(selector, by)
|
|
4506
4600
|
if page_actions.is_element_present(self.driver, selector, by):
|
|
4601
|
+
if self.__is_cdp_swap_needed():
|
|
4602
|
+
selector = self.convert_to_css_selector(selector, by=by)
|
|
4603
|
+
return self.cdp.save_screenshot(
|
|
4604
|
+
name, folder=test_logpath, selector=selector
|
|
4605
|
+
)
|
|
4507
4606
|
return page_actions.save_screenshot(
|
|
4508
4607
|
self.driver, name, test_logpath, selector, by
|
|
4509
4608
|
)
|
|
@@ -4517,8 +4616,49 @@ class BaseCase(unittest.TestCase):
|
|
|
4517
4616
|
action = ["ss_tl", "", origin, time_stamp]
|
|
4518
4617
|
self.__extra_actions.append(action)
|
|
4519
4618
|
sb_config._has_logs = True
|
|
4619
|
+
if self.__is_cdp_swap_needed():
|
|
4620
|
+
return self.cdp.save_screenshot(name, folder=test_logpath)
|
|
4520
4621
|
return page_actions.save_screenshot(self.driver, name, test_logpath)
|
|
4521
4622
|
|
|
4623
|
+
def save_as_pdf(self, name, folder=None):
|
|
4624
|
+
"""Same as self.print_to_pdf()"""
|
|
4625
|
+
return self.print_to_pdf(name, folder=folder)
|
|
4626
|
+
|
|
4627
|
+
def save_as_pdf_to_logs(self, name=None):
|
|
4628
|
+
"""Saves the page as a PDF to the "latest_logs/" folder.
|
|
4629
|
+
Naming is automatic:
|
|
4630
|
+
If NO NAME provided: "_1_PDF.pdf", "_2_PDF.pdf", etc.
|
|
4631
|
+
If NAME IS provided, then: "_1_name.pdf", "_2_name.pdf", etc."""
|
|
4632
|
+
if not self.__is_cdp_swap_needed():
|
|
4633
|
+
self.wait_for_ready_state_complete()
|
|
4634
|
+
test_logpath = os.path.join(self.log_path, self.__get_test_id())
|
|
4635
|
+
self.__create_log_path_as_needed(test_logpath)
|
|
4636
|
+
if name:
|
|
4637
|
+
name = str(name)
|
|
4638
|
+
self.__saved_pdf_count += 1
|
|
4639
|
+
if not name or len(name) == 0:
|
|
4640
|
+
name = "_%s_PDF.pdf" % self.__saved_pdf_count
|
|
4641
|
+
else:
|
|
4642
|
+
pre_name = "_%s_" % self.__saved_pdf_count
|
|
4643
|
+
if len(name) >= 4 and name[-4:].lower() == ".pdf":
|
|
4644
|
+
name = name[:-4]
|
|
4645
|
+
if len(name) == 0:
|
|
4646
|
+
name = "PDF"
|
|
4647
|
+
name = "%s%s.pdf" % (pre_name, name)
|
|
4648
|
+
if self.recorder_mode:
|
|
4649
|
+
url = self.get_current_url()
|
|
4650
|
+
if url and len(url) > 0:
|
|
4651
|
+
if ("http:") in url or ("https:") in url or ("file:") in url:
|
|
4652
|
+
if self.get_session_storage_item("pause_recorder") == "no":
|
|
4653
|
+
time_stamp = self.execute_script("return Date.now();")
|
|
4654
|
+
origin = self.get_origin()
|
|
4655
|
+
action = ["pdftl", "", origin, time_stamp]
|
|
4656
|
+
self.__extra_actions.append(action)
|
|
4657
|
+
sb_config._has_logs = True
|
|
4658
|
+
if self.__is_cdp_swap_needed():
|
|
4659
|
+
return self.cdp.print_to_pdf(name, folder=test_logpath)
|
|
4660
|
+
return self.print_to_pdf(name, test_logpath)
|
|
4661
|
+
|
|
4522
4662
|
def save_page_source_to_logs(self, name=None):
|
|
4523
4663
|
"""Saves the page HTML to the "latest_logs/" folder.
|
|
4524
4664
|
Naming is automatic:
|
|
@@ -4627,7 +4767,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4627
4767
|
if not os.path.exists(file_path):
|
|
4628
4768
|
os.makedirs(file_path)
|
|
4629
4769
|
cookies_file_path = os.path.join(file_path, name)
|
|
4630
|
-
cookies_file =
|
|
4770
|
+
cookies_file = open(cookies_file_path, mode="w+", encoding="utf-8")
|
|
4631
4771
|
cookies_file.writelines(json_cookies)
|
|
4632
4772
|
cookies_file.close()
|
|
4633
4773
|
|
|
@@ -4786,7 +4926,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4786
4926
|
self.driver.add_cookie(cookie)
|
|
4787
4927
|
|
|
4788
4928
|
def __set_esc_skip(self):
|
|
4789
|
-
if
|
|
4929
|
+
if getattr(self, "esc_end", None):
|
|
4790
4930
|
script = (
|
|
4791
4931
|
"""document.onkeydown = function(evt) {
|
|
4792
4932
|
evt = evt || window.event;
|
|
@@ -4804,7 +4944,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4804
4944
|
self.execute_script(script)
|
|
4805
4945
|
|
|
4806
4946
|
def __skip_if_esc(self):
|
|
4807
|
-
if
|
|
4947
|
+
if getattr(self, "esc_end", None):
|
|
4808
4948
|
if self.execute_script("return document.sb_esc_end;") == "yes":
|
|
4809
4949
|
self.skip()
|
|
4810
4950
|
|
|
@@ -4826,8 +4966,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4826
4966
|
self.__disable_beforeunload_as_needed()
|
|
4827
4967
|
if (
|
|
4828
4968
|
self.page_load_strategy == "none"
|
|
4829
|
-
and
|
|
4830
|
-
and settings.SKIP_JS_WAITS
|
|
4969
|
+
and getattr(settings, "SKIP_JS_WAITS", None)
|
|
4831
4970
|
):
|
|
4832
4971
|
time.sleep(0.01)
|
|
4833
4972
|
if self.undetectable:
|
|
@@ -4848,10 +4987,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4848
4987
|
|
|
4849
4988
|
def sleep(self, seconds):
|
|
4850
4989
|
self.__check_scope()
|
|
4851
|
-
if (
|
|
4852
|
-
not hasattr(sb_config, "time_limit")
|
|
4853
|
-
or (hasattr(sb_config, "time_limit") and not sb_config.time_limit)
|
|
4854
|
-
):
|
|
4990
|
+
if not getattr(sb_config, "time_limit", None):
|
|
4855
4991
|
time.sleep(seconds)
|
|
4856
4992
|
elif seconds < 0.4:
|
|
4857
4993
|
shared_utils.check_if_time_limit_exceeded()
|
|
@@ -4866,11 +5002,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4866
5002
|
if now_ms >= stop_ms:
|
|
4867
5003
|
break
|
|
4868
5004
|
time.sleep(0.2)
|
|
4869
|
-
if (
|
|
4870
|
-
self.recorder_mode
|
|
4871
|
-
and hasattr(sb_config, "record_sleep")
|
|
4872
|
-
and sb_config.record_sleep
|
|
4873
|
-
):
|
|
5005
|
+
if self.recorder_mode and getattr(sb_config, "record_sleep", None):
|
|
4874
5006
|
time_stamp = self.execute_script("return Date.now();")
|
|
4875
5007
|
origin = self.get_origin()
|
|
4876
5008
|
action = ["sleep", seconds, origin, time_stamp]
|
|
@@ -4928,23 +5060,32 @@ class BaseCase(unittest.TestCase):
|
|
|
4928
5060
|
|
|
4929
5061
|
def activate_cdp_mode(self, url=None, **kwargs):
|
|
4930
5062
|
"""Activate CDP Mode with the URL and kwargs."""
|
|
4931
|
-
if
|
|
5063
|
+
if getattr(self.driver, "_is_using_uc", None):
|
|
4932
5064
|
if self.__is_cdp_swap_needed():
|
|
4933
5065
|
return # CDP Mode is already active
|
|
4934
5066
|
if not self.is_connected():
|
|
4935
5067
|
self.driver.connect()
|
|
4936
5068
|
current_url = self.get_current_url()
|
|
4937
5069
|
if not current_url.startswith(("about", "data", "chrome")):
|
|
4938
|
-
self.
|
|
5070
|
+
self.driver.get("about:blank")
|
|
4939
5071
|
self.driver.uc_open_with_cdp_mode(url, **kwargs)
|
|
4940
5072
|
else:
|
|
4941
5073
|
self.get_new_driver(undetectable=True)
|
|
4942
5074
|
self.driver.uc_open_with_cdp_mode(url, **kwargs)
|
|
4943
5075
|
self.cdp = self.driver.cdp
|
|
5076
|
+
if hasattr(self.cdp, "solve_captcha"):
|
|
5077
|
+
self.solve_captcha = self.cdp.solve_captcha
|
|
5078
|
+
if hasattr(self.cdp, "click_captcha"):
|
|
5079
|
+
self.click_captcha = self.cdp.click_captcha
|
|
5080
|
+
if hasattr(self.cdp, "find_element_by_text"):
|
|
5081
|
+
self.find_element_by_text = self.cdp.find_element_by_text
|
|
5082
|
+
if getattr(self.driver, "_is_using_auth", None):
|
|
5083
|
+
with suppress(Exception):
|
|
5084
|
+
self.cdp.loop.run_until_complete(self.cdp.page.wait(0.25))
|
|
5085
|
+
self.undetectable = True
|
|
4944
5086
|
|
|
4945
5087
|
def activate_recorder(self):
|
|
4946
|
-
"""Activate Recorder Mode on the
|
|
4947
|
-
For persistent Recorder Mode, use the extension instead."""
|
|
5088
|
+
"""Activate Recorder Mode on the newest tab / window."""
|
|
4948
5089
|
from seleniumbase.js_code.recorder_js import recorder_js
|
|
4949
5090
|
|
|
4950
5091
|
if not self.is_chromium():
|
|
@@ -4957,7 +5098,7 @@ class BaseCase(unittest.TestCase):
|
|
|
4957
5098
|
"The %s Recorder is for Chromium only!\n"
|
|
4958
5099
|
" (Supported browsers: Chrome and Edge)" % sc
|
|
4959
5100
|
)
|
|
4960
|
-
url = self.
|
|
5101
|
+
url = self.get_current_url()
|
|
4961
5102
|
if url.startswith(("data:", "about:", "chrome:", "edge:")):
|
|
4962
5103
|
message = (
|
|
4963
5104
|
"The URL in Recorder-Mode cannot start with: "
|
|
@@ -4965,8 +5106,9 @@ class BaseCase(unittest.TestCase):
|
|
|
4965
5106
|
)
|
|
4966
5107
|
print("\n" + message)
|
|
4967
5108
|
return
|
|
4968
|
-
if self.recorder_ext:
|
|
5109
|
+
if self.recorder_ext and not self.undetectable:
|
|
4969
5110
|
return # The Recorder extension is already active
|
|
5111
|
+
self.switch_to_newest_tab()
|
|
4970
5112
|
with suppress(Exception):
|
|
4971
5113
|
recorder_on = self.get_session_storage_item("recorder_activated")
|
|
4972
5114
|
if not recorder_on == "yes":
|
|
@@ -5365,6 +5507,20 @@ class BaseCase(unittest.TestCase):
|
|
|
5365
5507
|
)
|
|
5366
5508
|
):
|
|
5367
5509
|
srt_actions[n][0] = "_skip"
|
|
5510
|
+
for n in range(len(srt_actions)):
|
|
5511
|
+
if (
|
|
5512
|
+
(srt_actions[n][0] == "begin" or srt_actions[n][0] == "_url_")
|
|
5513
|
+
and n > 1
|
|
5514
|
+
and (
|
|
5515
|
+
srt_actions[n - 1][0] == "f_url"
|
|
5516
|
+
or srt_actions[n - 1][0] == "_url_"
|
|
5517
|
+
)
|
|
5518
|
+
and srt_actions[n][2] == srt_actions[n - 1][2]
|
|
5519
|
+
and (
|
|
5520
|
+
int(srt_actions[n][3]) - int(srt_actions[n - 1][3]) < 4800
|
|
5521
|
+
)
|
|
5522
|
+
):
|
|
5523
|
+
srt_actions[n][0] = "_skip"
|
|
5368
5524
|
for n in range(len(srt_actions)):
|
|
5369
5525
|
if (
|
|
5370
5526
|
srt_actions[n][0] == "input"
|
|
@@ -5511,6 +5667,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5511
5667
|
ext_actions.append("s_scr")
|
|
5512
5668
|
ext_actions.append("ss_tf")
|
|
5513
5669
|
ext_actions.append("ss_tl")
|
|
5670
|
+
ext_actions.append("pdftl")
|
|
5514
5671
|
ext_actions.append("spstl")
|
|
5515
5672
|
ext_actions.append("da_el")
|
|
5516
5673
|
ext_actions.append("da_ep")
|
|
@@ -5574,14 +5731,13 @@ class BaseCase(unittest.TestCase):
|
|
|
5574
5731
|
methodname = self._testMethodName
|
|
5575
5732
|
context_filename = None
|
|
5576
5733
|
if (
|
|
5577
|
-
|
|
5578
|
-
and sb_config.is_context_manager
|
|
5734
|
+
getattr(sb_config, "is_context_manager", None)
|
|
5579
5735
|
and (filename == "base_case.py" or methodname == "runTest")
|
|
5580
5736
|
):
|
|
5581
5737
|
import traceback
|
|
5582
5738
|
stack_base = traceback.format_stack()[0].split(os.sep)[-1]
|
|
5583
5739
|
test_base = stack_base.split(", in ")[0]
|
|
5584
|
-
if
|
|
5740
|
+
if getattr(self, "cm_filename", None):
|
|
5585
5741
|
filename = self.cm_filename
|
|
5586
5742
|
else:
|
|
5587
5743
|
filename = test_base.split('"')[0]
|
|
@@ -5598,7 +5754,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5598
5754
|
classname = "MyTestClass"
|
|
5599
5755
|
methodname = methodname.replace("[", "__").replace("]", "")
|
|
5600
5756
|
methodname = re.sub(r"[\W]", "_", methodname)
|
|
5601
|
-
if
|
|
5757
|
+
if getattr(self, "is_behave", None):
|
|
5602
5758
|
classname = sb_config.behave_feature.name
|
|
5603
5759
|
classname = classname.replace("/", " ").replace(" & ", " ")
|
|
5604
5760
|
classname = re.sub(r"[^\w" + r"_ " + r"]", "", classname)
|
|
@@ -5666,7 +5822,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5666
5822
|
extra_file_name = "__init__.py"
|
|
5667
5823
|
extra_file_path = os.path.join(recordings_folder, extra_file_name)
|
|
5668
5824
|
if not os.path.exists(extra_file_path):
|
|
5669
|
-
out_file =
|
|
5825
|
+
out_file = open(extra_file_path, mode="w+", encoding="utf-8")
|
|
5670
5826
|
out_file.writelines("\r\n".join(data))
|
|
5671
5827
|
out_file.close()
|
|
5672
5828
|
sys.stdout.write("\nCreated recordings%s__init__.py" % os.sep)
|
|
@@ -5714,7 +5870,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5714
5870
|
extra_file_name = "pytest.ini"
|
|
5715
5871
|
extra_file_path = os.path.join(recordings_folder, extra_file_name)
|
|
5716
5872
|
if not os.path.exists(extra_file_path):
|
|
5717
|
-
out_file =
|
|
5873
|
+
out_file = open(extra_file_path, mode="w+", encoding="utf-8")
|
|
5718
5874
|
out_file.writelines("\r\n".join(data))
|
|
5719
5875
|
out_file.close()
|
|
5720
5876
|
sys.stdout.write("\nCreated recordings%spytest.ini" % os.sep)
|
|
@@ -5735,7 +5891,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5735
5891
|
extra_file_name = "setup.cfg"
|
|
5736
5892
|
extra_file_path = os.path.join(recordings_folder, extra_file_name)
|
|
5737
5893
|
if not os.path.exists(extra_file_path):
|
|
5738
|
-
out_file =
|
|
5894
|
+
out_file = open(extra_file_path, mode="w+", encoding="utf-8")
|
|
5739
5895
|
out_file.writelines("\r\n".join(data))
|
|
5740
5896
|
out_file.close()
|
|
5741
5897
|
sys.stdout.write("\nCreated recordings%ssetup.cfg" % os.sep)
|
|
@@ -5746,14 +5902,14 @@ class BaseCase(unittest.TestCase):
|
|
|
5746
5902
|
test_id = sb_config._test_id
|
|
5747
5903
|
file_name = test_id.split("::")[0].split("/")[-1].split("\\")[-1]
|
|
5748
5904
|
file_name = file_name.split(".py")[0] + "_rec.py"
|
|
5749
|
-
if
|
|
5905
|
+
if getattr(self, "is_behave", None):
|
|
5750
5906
|
file_name = sb_config.behave_scenario.filename.replace(".", "_")
|
|
5751
5907
|
file_name = file_name.split("/")[-1].split("\\")[-1] + "_rec.py"
|
|
5752
5908
|
file_name = file_name
|
|
5753
5909
|
elif context_filename:
|
|
5754
5910
|
file_name = context_filename
|
|
5755
5911
|
file_path = os.path.join(recordings_folder, file_name)
|
|
5756
|
-
out_file =
|
|
5912
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
5757
5913
|
out_file.writelines("\r\n".join(data))
|
|
5758
5914
|
out_file.close()
|
|
5759
5915
|
rec_message = ">>> RECORDING SAVED as: "
|
|
@@ -5765,7 +5921,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5765
5921
|
if terminal_size > 30 and star_len > terminal_size:
|
|
5766
5922
|
star_len = terminal_size
|
|
5767
5923
|
spc = "\n\n"
|
|
5768
|
-
if
|
|
5924
|
+
if getattr(self, "rec_print", None):
|
|
5769
5925
|
spc = ""
|
|
5770
5926
|
sys.stdout.write("\nCreated recordings%s%s" % (os.sep, file_name))
|
|
5771
5927
|
print()
|
|
@@ -5786,7 +5942,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5786
5942
|
rec_message = rec_message.replace(">>>", c2 + ">>>" + cr)
|
|
5787
5943
|
print("%s%s%s%s%s\n%s" % (spc, rec_message, c1, file_path, cr, stars))
|
|
5788
5944
|
|
|
5789
|
-
if
|
|
5945
|
+
if getattr(self, "rec_behave", None):
|
|
5790
5946
|
# Also generate necessary behave-gherkin files.
|
|
5791
5947
|
self.__process_recorded_behave_actions(srt_actions, colorama)
|
|
5792
5948
|
|
|
@@ -5797,7 +5953,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5797
5953
|
filename = self.__get_filename()
|
|
5798
5954
|
feature_class = None
|
|
5799
5955
|
scenario_test = None
|
|
5800
|
-
if
|
|
5956
|
+
if getattr(self, "is_behave", None):
|
|
5801
5957
|
feature_class = sb_config.behave_feature.name
|
|
5802
5958
|
scenario_test = sb_config.behave_scenario.name
|
|
5803
5959
|
else:
|
|
@@ -5851,11 +6007,11 @@ class BaseCase(unittest.TestCase):
|
|
|
5851
6007
|
os.makedirs(steps_folder)
|
|
5852
6008
|
|
|
5853
6009
|
file_name = filename.split(".")[0]
|
|
5854
|
-
if
|
|
6010
|
+
if getattr(self, "is_behave", None):
|
|
5855
6011
|
file_name = sb_config.behave_scenario.filename.replace(".", "_")
|
|
5856
6012
|
file_name = file_name.split("/")[-1].split("\\")[-1] + "_rec.feature"
|
|
5857
6013
|
file_path = os.path.join(features_folder, file_name)
|
|
5858
|
-
out_file =
|
|
6014
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
5859
6015
|
out_file.writelines("\r\n".join(data))
|
|
5860
6016
|
out_file.close()
|
|
5861
6017
|
|
|
@@ -5868,7 +6024,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5868
6024
|
if terminal_size > 30 and star_len > terminal_size:
|
|
5869
6025
|
star_len = terminal_size
|
|
5870
6026
|
spc = "\n"
|
|
5871
|
-
if
|
|
6027
|
+
if getattr(self, "rec_print", None):
|
|
5872
6028
|
spc = ""
|
|
5873
6029
|
print()
|
|
5874
6030
|
if " " not in file_path:
|
|
@@ -5893,7 +6049,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5893
6049
|
file_name = "__init__.py"
|
|
5894
6050
|
file_path = os.path.join(features_folder, file_name)
|
|
5895
6051
|
if not os.path.exists(file_path):
|
|
5896
|
-
out_file =
|
|
6052
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
5897
6053
|
out_file.writelines("\r\n".join(data))
|
|
5898
6054
|
out_file.close()
|
|
5899
6055
|
print("Created recordings/features/__init__.py")
|
|
@@ -5906,7 +6062,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5906
6062
|
file_name = "behave.ini"
|
|
5907
6063
|
file_path = os.path.join(features_folder, file_name)
|
|
5908
6064
|
if not os.path.exists(file_path):
|
|
5909
|
-
out_file =
|
|
6065
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
5910
6066
|
out_file.writelines("\r\n".join(data))
|
|
5911
6067
|
out_file.close()
|
|
5912
6068
|
print("Created recordings/features/behave.ini")
|
|
@@ -5945,7 +6101,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5945
6101
|
file_name = "environment.py"
|
|
5946
6102
|
file_path = os.path.join(features_folder, file_name)
|
|
5947
6103
|
if not os.path.exists(file_path):
|
|
5948
|
-
out_file =
|
|
6104
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
5949
6105
|
out_file.writelines("\r\n".join(data))
|
|
5950
6106
|
out_file.close()
|
|
5951
6107
|
print("Created recordings/features/environment.py")
|
|
@@ -5955,7 +6111,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5955
6111
|
file_name = "__init__.py"
|
|
5956
6112
|
file_path = os.path.join(steps_folder, file_name)
|
|
5957
6113
|
if not os.path.exists(file_path):
|
|
5958
|
-
out_file =
|
|
6114
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
5959
6115
|
out_file.writelines("\r\n".join(data))
|
|
5960
6116
|
out_file.close()
|
|
5961
6117
|
print("Created recordings/features/steps/__init__.py")
|
|
@@ -5966,7 +6122,7 @@ class BaseCase(unittest.TestCase):
|
|
|
5966
6122
|
file_name = "imported.py"
|
|
5967
6123
|
file_path = os.path.join(steps_folder, file_name)
|
|
5968
6124
|
if not os.path.exists(file_path):
|
|
5969
|
-
out_file =
|
|
6125
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
5970
6126
|
out_file.writelines("\r\n".join(data))
|
|
5971
6127
|
out_file.close()
|
|
5972
6128
|
print("Created recordings/features/steps/imported.py")
|
|
@@ -6193,7 +6349,12 @@ class BaseCase(unittest.TestCase):
|
|
|
6193
6349
|
scroll - the option to scroll to the element first (Default: True)
|
|
6194
6350
|
timeout - the time to wait for the element to appear """
|
|
6195
6351
|
self.__check_scope()
|
|
6196
|
-
if
|
|
6352
|
+
if self.__is_cdp_swap_needed():
|
|
6353
|
+
if page_utils.is_xpath_selector(selector):
|
|
6354
|
+
if "contains(" in selector:
|
|
6355
|
+
self.cdp.highlight(selector)
|
|
6356
|
+
return
|
|
6357
|
+
else:
|
|
6197
6358
|
self._check_browser()
|
|
6198
6359
|
self.__skip_if_esc()
|
|
6199
6360
|
if isinstance(selector, WebElement):
|
|
@@ -6238,6 +6399,7 @@ class BaseCase(unittest.TestCase):
|
|
|
6238
6399
|
By default, "html" will be used as the CSS Selector target.
|
|
6239
6400
|
You can specify how many times in-a-row the action happens."""
|
|
6240
6401
|
self.__check_scope()
|
|
6402
|
+
self._check_browser()
|
|
6241
6403
|
if times < 1:
|
|
6242
6404
|
return
|
|
6243
6405
|
element = self.wait_for_element_present(selector)
|
|
@@ -6260,6 +6422,7 @@ class BaseCase(unittest.TestCase):
|
|
|
6260
6422
|
By default, "html" will be used as the CSS Selector target.
|
|
6261
6423
|
You can specify how many times in-a-row the action happens."""
|
|
6262
6424
|
self.__check_scope()
|
|
6425
|
+
self._check_browser()
|
|
6263
6426
|
if times < 1:
|
|
6264
6427
|
return
|
|
6265
6428
|
element = self.wait_for_element_present(selector)
|
|
@@ -6282,6 +6445,7 @@ class BaseCase(unittest.TestCase):
|
|
|
6282
6445
|
By default, "html" will be used as the CSS Selector target.
|
|
6283
6446
|
You can specify how many times in-a-row the action happens."""
|
|
6284
6447
|
self.__check_scope()
|
|
6448
|
+
self._check_browser()
|
|
6285
6449
|
if times < 1:
|
|
6286
6450
|
return
|
|
6287
6451
|
element = self.wait_for_element_present(selector)
|
|
@@ -6304,6 +6468,7 @@ class BaseCase(unittest.TestCase):
|
|
|
6304
6468
|
By default, "html" will be used as the CSS Selector target.
|
|
6305
6469
|
You can specify how many times in-a-row the action happens."""
|
|
6306
6470
|
self.__check_scope()
|
|
6471
|
+
self._check_browser()
|
|
6307
6472
|
if times < 1:
|
|
6308
6473
|
return
|
|
6309
6474
|
element = self.wait_for_element_present(selector)
|
|
@@ -6441,6 +6606,34 @@ class BaseCase(unittest.TestCase):
|
|
|
6441
6606
|
self.execute_script(scroll_script)
|
|
6442
6607
|
time.sleep(0.012)
|
|
6443
6608
|
|
|
6609
|
+
def scroll_by_y(self, y):
|
|
6610
|
+
"""Scrolls page by y pixels."""
|
|
6611
|
+
self.__check_scope()
|
|
6612
|
+
y = int(y)
|
|
6613
|
+
if self.__is_cdp_swap_needed():
|
|
6614
|
+
self.cdp.scroll_by_y(y)
|
|
6615
|
+
return
|
|
6616
|
+
scroll_script = "window.scrollBy(0, %s);" % y
|
|
6617
|
+
with suppress(Exception):
|
|
6618
|
+
self.execute_script(scroll_script)
|
|
6619
|
+
time.sleep(0.012)
|
|
6620
|
+
|
|
6621
|
+
def scroll_up(self, amount=25):
|
|
6622
|
+
"""Scrolls up as a percentage of the page."""
|
|
6623
|
+
if self.__is_cdp_swap_needed():
|
|
6624
|
+
self.cdp.scroll_up(amount)
|
|
6625
|
+
return
|
|
6626
|
+
amount = self.get_window_size()["height"] * amount / 100
|
|
6627
|
+
self.execute_script("window.scrollBy(0, -%s);" % amount)
|
|
6628
|
+
|
|
6629
|
+
def scroll_down(self, amount=25):
|
|
6630
|
+
"""Scrolls down as a percentage of the page."""
|
|
6631
|
+
if self.__is_cdp_swap_needed():
|
|
6632
|
+
self.cdp.scroll_down(amount)
|
|
6633
|
+
return
|
|
6634
|
+
amount = self.get_window_size()["height"] * amount / 100
|
|
6635
|
+
self.execute_script("window.scrollBy(0, %s);" % amount)
|
|
6636
|
+
|
|
6444
6637
|
def click_xpath(self, xpath):
|
|
6445
6638
|
"""Technically, self.click() automatically detects xpath selectors,
|
|
6446
6639
|
so self.click_xpath() is just a longer name for the same action."""
|
|
@@ -7184,21 +7377,6 @@ class BaseCase(unittest.TestCase):
|
|
|
7184
7377
|
with pip_find_lock:
|
|
7185
7378
|
with suppress(Exception):
|
|
7186
7379
|
shared_utils.make_writable(constants.PipInstall.FINDLOCK)
|
|
7187
|
-
if sys.version_info < (3, 9):
|
|
7188
|
-
# Fix bug in newer cryptography for Python 3.7 and 3.8:
|
|
7189
|
-
# "pyo3_runtime.PanicException: Python API call failed"
|
|
7190
|
-
try:
|
|
7191
|
-
import cryptography
|
|
7192
|
-
if cryptography.__version__ != "39.0.2":
|
|
7193
|
-
del cryptography # To get newer ver
|
|
7194
|
-
shared_utils.pip_install(
|
|
7195
|
-
"cryptography", version="39.0.2"
|
|
7196
|
-
)
|
|
7197
|
-
import cryptography
|
|
7198
|
-
except Exception:
|
|
7199
|
-
shared_utils.pip_install(
|
|
7200
|
-
"cryptography", version="39.0.2"
|
|
7201
|
-
)
|
|
7202
7380
|
try:
|
|
7203
7381
|
from pdfminer.high_level import extract_text
|
|
7204
7382
|
except Exception:
|
|
@@ -7239,6 +7417,7 @@ class BaseCase(unittest.TestCase):
|
|
|
7239
7417
|
page_search = [page]
|
|
7240
7418
|
else:
|
|
7241
7419
|
page_search = None
|
|
7420
|
+
logging.getLogger("pdfminer").setLevel(logging.ERROR)
|
|
7242
7421
|
pdf_text = extract_text(
|
|
7243
7422
|
file_path,
|
|
7244
7423
|
password="",
|
|
@@ -7489,7 +7668,11 @@ class BaseCase(unittest.TestCase):
|
|
|
7489
7668
|
destination_folder = constants.Files.DOWNLOADS_FOLDER
|
|
7490
7669
|
if not os.path.exists(destination_folder):
|
|
7491
7670
|
os.makedirs(destination_folder)
|
|
7492
|
-
|
|
7671
|
+
agent = self.get_user_agent()
|
|
7672
|
+
headers = {"user-agent": agent}
|
|
7673
|
+
page_utils._download_file_to(
|
|
7674
|
+
file_url, destination_folder, headers=headers
|
|
7675
|
+
)
|
|
7493
7676
|
if self.recorder_mode and self.__current_url_is_recordable():
|
|
7494
7677
|
if self.get_session_storage_item("pause_recorder") == "no":
|
|
7495
7678
|
time_stamp = self.execute_script("return Date.now();")
|
|
@@ -7513,8 +7696,10 @@ class BaseCase(unittest.TestCase):
|
|
|
7513
7696
|
destination_folder = constants.Files.DOWNLOADS_FOLDER
|
|
7514
7697
|
if not os.path.exists(destination_folder):
|
|
7515
7698
|
os.makedirs(destination_folder)
|
|
7699
|
+
agent = self.get_user_agent()
|
|
7700
|
+
headers = {"user-agent": agent}
|
|
7516
7701
|
page_utils._download_file_to(
|
|
7517
|
-
file_url, destination_folder, new_file_name
|
|
7702
|
+
file_url, destination_folder, new_file_name, headers=headers
|
|
7518
7703
|
)
|
|
7519
7704
|
|
|
7520
7705
|
def save_data_as(self, data, file_name, destination_folder=None):
|
|
@@ -7546,6 +7731,47 @@ class BaseCase(unittest.TestCase):
|
|
|
7546
7731
|
folder = constants.Files.DOWNLOADS_FOLDER
|
|
7547
7732
|
return page_utils._get_file_data(folder, file_name)
|
|
7548
7733
|
|
|
7734
|
+
def print_to_pdf(self, name, folder=None):
|
|
7735
|
+
"""Saves the current page as a PDF.
|
|
7736
|
+
If no folder is specified, uses the folder where pytest was called.
|
|
7737
|
+
If the folder provided doesn't exist, it will get created.
|
|
7738
|
+
@Params
|
|
7739
|
+
name - The name to give the PDF file. Must end in ".pdf".
|
|
7740
|
+
folder - The directory where you want to save the PDF."""
|
|
7741
|
+
import base64
|
|
7742
|
+
from selenium.webdriver.common.print_page_options import PrintOptions
|
|
7743
|
+
|
|
7744
|
+
if not name.lower().endswith(".pdf"):
|
|
7745
|
+
raise Exception('PDF name {%s} must end in ".pdf"!)' % name)
|
|
7746
|
+
download_file_lock = fasteners.InterProcessLock(
|
|
7747
|
+
constants.MultiBrowser.DOWNLOAD_FILE_LOCK
|
|
7748
|
+
)
|
|
7749
|
+
if self.__is_cdp_swap_needed():
|
|
7750
|
+
with download_file_lock:
|
|
7751
|
+
with suppress(Exception):
|
|
7752
|
+
shared_utils.make_writable(
|
|
7753
|
+
constants.MultiBrowser.DOWNLOAD_FILE_LOCK
|
|
7754
|
+
)
|
|
7755
|
+
if folder and not os.path.exists(folder):
|
|
7756
|
+
os.makedirs(folder)
|
|
7757
|
+
self.cdp.print_to_pdf(name, folder)
|
|
7758
|
+
return
|
|
7759
|
+
self.wait_for_ready_state_complete()
|
|
7760
|
+
print_options = PrintOptions()
|
|
7761
|
+
pdf_base64 = self.driver.print_page(print_options)
|
|
7762
|
+
with download_file_lock:
|
|
7763
|
+
with suppress(Exception):
|
|
7764
|
+
shared_utils.make_writable(
|
|
7765
|
+
constants.MultiBrowser.DOWNLOAD_FILE_LOCK
|
|
7766
|
+
)
|
|
7767
|
+
if folder and not os.path.exists(folder):
|
|
7768
|
+
os.makedirs(folder)
|
|
7769
|
+
filename = name
|
|
7770
|
+
if folder:
|
|
7771
|
+
filename = os.path.join(folder, name)
|
|
7772
|
+
with open(filename, "wb") as f:
|
|
7773
|
+
f.write(base64.b64decode(pdf_base64))
|
|
7774
|
+
|
|
7549
7775
|
def get_downloads_folder(self):
|
|
7550
7776
|
"""Returns the path of the SeleniumBase "downloaded_files/" folder.
|
|
7551
7777
|
Calling self.download_file(file_url) will put that file in here.
|
|
@@ -8399,33 +8625,9 @@ class BaseCase(unittest.TestCase):
|
|
|
8399
8625
|
|
|
8400
8626
|
def get_mfa_code(self, totp_key=None):
|
|
8401
8627
|
"""Same as get_totp_code() and get_google_auth_password().
|
|
8402
|
-
Returns a time-based one-time password based on the
|
|
8403
|
-
|
|
8404
|
-
|
|
8405
|
-
to using the one provided in [seleniumbase/config/settings.py].
|
|
8406
|
-
Google Authenticator codes expire & change at 30-sec intervals.
|
|
8407
|
-
If the fetched password expires in the next 1.2 seconds, waits
|
|
8408
|
-
for a new one before returning it (may take up to 1.2 seconds).
|
|
8409
|
-
See https://pyotp.readthedocs.io/en/latest/ for details."""
|
|
8410
|
-
import pyotp
|
|
8411
|
-
|
|
8412
|
-
if not totp_key:
|
|
8413
|
-
totp_key = settings.TOTP_KEY
|
|
8414
|
-
|
|
8415
|
-
epoch_interval = time.time() / 30.0
|
|
8416
|
-
cycle_lifespan = float(epoch_interval) - int(epoch_interval)
|
|
8417
|
-
if float(cycle_lifespan) > 0.96:
|
|
8418
|
-
# Password expires in the next 1.2 seconds. Wait for a new one.
|
|
8419
|
-
for i in range(30):
|
|
8420
|
-
time.sleep(0.04)
|
|
8421
|
-
epoch_interval = time.time() / 30.0
|
|
8422
|
-
cycle_lifespan = float(epoch_interval) - int(epoch_interval)
|
|
8423
|
-
if not float(cycle_lifespan) > 0.96:
|
|
8424
|
-
# The new password cycle has begun
|
|
8425
|
-
break
|
|
8426
|
-
|
|
8427
|
-
totp = pyotp.TOTP(totp_key)
|
|
8428
|
-
return str(totp.now())
|
|
8628
|
+
Returns a time-based one-time password based on the Google
|
|
8629
|
+
Authenticator algorithm for multi-factor authentication."""
|
|
8630
|
+
return shared_utils.get_mfa_code(totp_key)
|
|
8429
8631
|
|
|
8430
8632
|
def enter_mfa_code(
|
|
8431
8633
|
self, selector, totp_key=None, by="css selector", timeout=None
|
|
@@ -8609,6 +8811,9 @@ class BaseCase(unittest.TestCase):
|
|
|
8609
8811
|
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
|
8610
8812
|
timeout = self.__get_new_timeout(timeout)
|
|
8611
8813
|
selector, by = self.__recalculate_selector(selector, by)
|
|
8814
|
+
if self.__is_cdp_swap_needed():
|
|
8815
|
+
self.cdp.set_value(selector, text)
|
|
8816
|
+
return
|
|
8612
8817
|
self.wait_for_ready_state_complete()
|
|
8613
8818
|
element = page_actions.wait_for_element_present(
|
|
8614
8819
|
self.driver, selector, by, timeout
|
|
@@ -8629,10 +8834,14 @@ class BaseCase(unittest.TestCase):
|
|
|
8629
8834
|
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
|
8630
8835
|
timeout = self.__get_new_timeout(timeout)
|
|
8631
8836
|
selector, by = self.__recalculate_selector(selector, by)
|
|
8632
|
-
|
|
8633
|
-
|
|
8634
|
-
self.
|
|
8635
|
-
|
|
8837
|
+
element = None
|
|
8838
|
+
if self.__is_cdp_swap_needed():
|
|
8839
|
+
element = self.cdp.select(selector, timeout=timeout)
|
|
8840
|
+
else:
|
|
8841
|
+
self.wait_for_ready_state_complete()
|
|
8842
|
+
element = page_actions.wait_for_element_present(
|
|
8843
|
+
self.driver, selector, by, timeout
|
|
8844
|
+
)
|
|
8636
8845
|
if element.tag_name.lower() in ["input", "textarea"]:
|
|
8637
8846
|
self.js_update_text(selector, text, by=by, timeout=timeout)
|
|
8638
8847
|
return
|
|
@@ -8824,7 +9033,7 @@ class BaseCase(unittest.TestCase):
|
|
|
8824
9033
|
self.__passed_then_skipped = True
|
|
8825
9034
|
self.__will_be_skipped = True
|
|
8826
9035
|
sb_config._results[test_id] = "Skipped"
|
|
8827
|
-
if
|
|
9036
|
+
if getattr(self, "with_db_reporting", None):
|
|
8828
9037
|
if self.is_pytest:
|
|
8829
9038
|
self.__skip_reason = reason
|
|
8830
9039
|
else:
|
|
@@ -9095,9 +9304,9 @@ class BaseCase(unittest.TestCase):
|
|
|
9095
9304
|
"""Same as self.refresh_page()"""
|
|
9096
9305
|
self.refresh_page()
|
|
9097
9306
|
|
|
9098
|
-
def open_new_tab(self, switch_to=True):
|
|
9307
|
+
def open_new_tab(self, switch_to=True, **kwargs):
|
|
9099
9308
|
"""Same as self.open_new_window()"""
|
|
9100
|
-
self.open_new_window(switch_to=switch_to)
|
|
9309
|
+
self.open_new_window(switch_to=switch_to, **kwargs)
|
|
9101
9310
|
|
|
9102
9311
|
def switch_to_tab(self, tab, timeout=None):
|
|
9103
9312
|
"""Same as self.switch_to_window()
|
|
@@ -9114,85 +9323,50 @@ class BaseCase(unittest.TestCase):
|
|
|
9114
9323
|
"""Same as self.switch_to_newest_window()"""
|
|
9115
9324
|
self.switch_to_newest_window()
|
|
9116
9325
|
|
|
9326
|
+
def save_as_html(self, name, folder=None):
|
|
9327
|
+
"""Same as self.save_page_source()"""
|
|
9328
|
+
self.save_page_source(name, folder=folder)
|
|
9329
|
+
|
|
9117
9330
|
def input(
|
|
9118
9331
|
self, selector, text, by="css selector", timeout=None, retry=False
|
|
9119
9332
|
):
|
|
9120
9333
|
"""Same as self.update_text()"""
|
|
9121
|
-
self.__check_scope()
|
|
9122
|
-
if not timeout:
|
|
9123
|
-
timeout = settings.LARGE_TIMEOUT
|
|
9124
|
-
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
|
9125
|
-
timeout = self.__get_new_timeout(timeout)
|
|
9126
|
-
selector, by = self.__recalculate_selector(selector, by)
|
|
9127
9334
|
self.update_text(selector, text, by=by, timeout=timeout, retry=retry)
|
|
9128
9335
|
|
|
9129
9336
|
def fill(
|
|
9130
9337
|
self, selector, text, by="css selector", timeout=None, retry=False
|
|
9131
9338
|
):
|
|
9132
9339
|
"""Same as self.update_text()"""
|
|
9133
|
-
self.__check_scope()
|
|
9134
|
-
if not timeout:
|
|
9135
|
-
timeout = settings.LARGE_TIMEOUT
|
|
9136
|
-
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
|
9137
|
-
timeout = self.__get_new_timeout(timeout)
|
|
9138
|
-
selector, by = self.__recalculate_selector(selector, by)
|
|
9139
9340
|
self.update_text(selector, text, by=by, timeout=timeout, retry=retry)
|
|
9140
9341
|
|
|
9141
9342
|
def write(
|
|
9142
9343
|
self, selector, text, by="css selector", timeout=None, retry=False
|
|
9143
9344
|
):
|
|
9144
9345
|
"""Same as self.update_text()"""
|
|
9145
|
-
self.__check_scope()
|
|
9146
|
-
if not timeout:
|
|
9147
|
-
timeout = settings.LARGE_TIMEOUT
|
|
9148
|
-
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
|
9149
|
-
timeout = self.__get_new_timeout(timeout)
|
|
9150
|
-
selector, by = self.__recalculate_selector(selector, by)
|
|
9151
9346
|
self.update_text(selector, text, by=by, timeout=timeout, retry=retry)
|
|
9152
9347
|
|
|
9153
9348
|
def click_link(self, link_text, timeout=None):
|
|
9154
9349
|
"""Same as self.click_link_text()"""
|
|
9155
|
-
self.__check_scope()
|
|
9156
|
-
if not timeout:
|
|
9157
|
-
timeout = settings.SMALL_TIMEOUT
|
|
9158
|
-
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
|
9159
|
-
timeout = self.__get_new_timeout(timeout)
|
|
9160
9350
|
self.click_link_text(link_text, timeout=timeout)
|
|
9161
9351
|
|
|
9162
9352
|
def click_partial_link(self, partial_link_text, timeout=None):
|
|
9163
9353
|
"""Same as self.click_partial_link_text()"""
|
|
9164
|
-
self.__check_scope()
|
|
9165
|
-
if not timeout:
|
|
9166
|
-
timeout = settings.SMALL_TIMEOUT
|
|
9167
|
-
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
|
9168
|
-
timeout = self.__get_new_timeout(timeout)
|
|
9169
9354
|
self.click_partial_link_text(partial_link_text, timeout=timeout)
|
|
9170
9355
|
|
|
9171
9356
|
def right_click(self, selector, by="css selector", timeout=None):
|
|
9172
9357
|
"""Same as self.context_click()"""
|
|
9173
|
-
self.__check_scope()
|
|
9174
|
-
if not timeout:
|
|
9175
|
-
timeout = settings.SMALL_TIMEOUT
|
|
9176
|
-
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
|
9177
|
-
timeout = self.__get_new_timeout(timeout)
|
|
9178
9358
|
self.context_click(selector, by=by, timeout=timeout)
|
|
9179
9359
|
|
|
9360
|
+
def hover_element(self, selector, by="css selector", timeout=None):
|
|
9361
|
+
"""Same as self.hover()"""
|
|
9362
|
+
return self.hover(selector, by=by, timeout=timeout)
|
|
9363
|
+
|
|
9180
9364
|
def hover_on_element(self, selector, by="css selector", timeout=None):
|
|
9181
9365
|
"""Same as self.hover()"""
|
|
9182
|
-
self.__check_scope()
|
|
9183
|
-
if not timeout:
|
|
9184
|
-
timeout = settings.SMALL_TIMEOUT
|
|
9185
|
-
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
|
9186
|
-
timeout = self.__get_new_timeout(timeout)
|
|
9187
9366
|
return self.hover(selector, by=by, timeout=timeout)
|
|
9188
9367
|
|
|
9189
9368
|
def hover_over_element(self, selector, by="css selector", timeout=None):
|
|
9190
9369
|
"""Same as self.hover()"""
|
|
9191
|
-
self.__check_scope()
|
|
9192
|
-
if not timeout:
|
|
9193
|
-
timeout = settings.SMALL_TIMEOUT
|
|
9194
|
-
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
|
9195
|
-
timeout = self.__get_new_timeout(timeout)
|
|
9196
9370
|
return self.hover(selector, by=by, timeout=timeout)
|
|
9197
9371
|
|
|
9198
9372
|
def wait_for_element_visible(
|
|
@@ -9517,7 +9691,7 @@ class BaseCase(unittest.TestCase):
|
|
|
9517
9691
|
To force a print during multithreaded tests, use: "sys.stderr.write()".
|
|
9518
9692
|
To print without the new-line character end, use: "sys.stdout.write()".
|
|
9519
9693
|
"""
|
|
9520
|
-
if
|
|
9694
|
+
if getattr(sb_config, "_multithreaded", None):
|
|
9521
9695
|
if not isinstance(msg, str):
|
|
9522
9696
|
with suppress(Exception):
|
|
9523
9697
|
msg = str(msg)
|
|
@@ -9563,9 +9737,11 @@ class BaseCase(unittest.TestCase):
|
|
|
9563
9737
|
|
|
9564
9738
|
def activate_messenger(self):
|
|
9565
9739
|
self.__check_scope()
|
|
9566
|
-
self.
|
|
9740
|
+
if not self.__is_cdp_swap_needed():
|
|
9741
|
+
self._check_browser()
|
|
9567
9742
|
js_utils.activate_messenger(self.driver)
|
|
9568
|
-
self.
|
|
9743
|
+
if not self.__is_cdp_swap_needed():
|
|
9744
|
+
self.wait_for_ready_state_complete()
|
|
9569
9745
|
|
|
9570
9746
|
def set_messenger_theme(
|
|
9571
9747
|
self, theme="default", location="default", max_messages="default"
|
|
@@ -9954,6 +10130,21 @@ class BaseCase(unittest.TestCase):
|
|
|
9954
10130
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
|
9955
10131
|
timeout = self.__get_new_timeout(timeout)
|
|
9956
10132
|
if self.__is_cdp_swap_needed():
|
|
10133
|
+
if self.demo_mode:
|
|
10134
|
+
selector, by = self.__recalculate_selector(
|
|
10135
|
+
selector, by, xp_ok=False
|
|
10136
|
+
)
|
|
10137
|
+
a_t = "ASSERT"
|
|
10138
|
+
if self._language != "English":
|
|
10139
|
+
from seleniumbase.fixtures.words import SD
|
|
10140
|
+
|
|
10141
|
+
a_t = SD.translate_assert(self._language)
|
|
10142
|
+
messenger_post = "<b>%s %s</b>: %s" % (
|
|
10143
|
+
a_t, by.upper(), selector
|
|
10144
|
+
)
|
|
10145
|
+
self.__highlight_with_assert_success(
|
|
10146
|
+
messenger_post, selector, by
|
|
10147
|
+
)
|
|
9957
10148
|
self.cdp.assert_element(selector, timeout=timeout)
|
|
9958
10149
|
return True
|
|
9959
10150
|
if isinstance(selector, list):
|
|
@@ -10078,7 +10269,7 @@ class BaseCase(unittest.TestCase):
|
|
|
10078
10269
|
text = self.__get_type_checked_text(text)
|
|
10079
10270
|
selector, by = self.__recalculate_selector(selector, by)
|
|
10080
10271
|
if self.__is_cdp_swap_needed():
|
|
10081
|
-
return self.cdp.
|
|
10272
|
+
return self.cdp.wait_for_text(text, selector, timeout=timeout)
|
|
10082
10273
|
elif self.__is_shadow_selector(selector):
|
|
10083
10274
|
return self.__wait_for_shadow_text_visible(text, selector, timeout)
|
|
10084
10275
|
return page_actions.wait_for_text_visible(
|
|
@@ -10248,6 +10439,20 @@ class BaseCase(unittest.TestCase):
|
|
|
10248
10439
|
messenger_post, selector, by
|
|
10249
10440
|
)
|
|
10250
10441
|
elif self.__is_cdp_swap_needed():
|
|
10442
|
+
if self.demo_mode:
|
|
10443
|
+
a_t = "ASSERT TEXT"
|
|
10444
|
+
i_n = "in"
|
|
10445
|
+
if self._language != "English":
|
|
10446
|
+
from seleniumbase.fixtures.words import SD
|
|
10447
|
+
|
|
10448
|
+
a_t = SD.translate_assert_text(self._language)
|
|
10449
|
+
i_n = SD.translate_in(self._language)
|
|
10450
|
+
messenger_post = "<b>%s</b>: {%s} %s %s: %s" % (
|
|
10451
|
+
a_t, text, i_n, by.upper(), selector
|
|
10452
|
+
)
|
|
10453
|
+
self.__highlight_with_assert_success(
|
|
10454
|
+
messenger_post, selector, by
|
|
10455
|
+
)
|
|
10251
10456
|
self.cdp.assert_text(text, selector, timeout=timeout)
|
|
10252
10457
|
return True
|
|
10253
10458
|
elif self.__is_shadow_selector(selector):
|
|
@@ -10435,6 +10640,8 @@ class BaseCase(unittest.TestCase):
|
|
|
10435
10640
|
timeout = settings.LARGE_TIMEOUT
|
|
10436
10641
|
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
|
10437
10642
|
timeout = self.__get_new_timeout(timeout)
|
|
10643
|
+
if self.__is_cdp_swap_needed():
|
|
10644
|
+
return self.cdp.find_element_by_text(text=link_text, tag_name="a")
|
|
10438
10645
|
return self.wait_for_element_visible(
|
|
10439
10646
|
link_text, by="link text", timeout=timeout
|
|
10440
10647
|
)
|
|
@@ -10981,7 +11188,7 @@ class BaseCase(unittest.TestCase):
|
|
|
10981
11188
|
return # Skip the rest when deferred visual asserts are used
|
|
10982
11189
|
the_html = visual_helper.get_sbs_html()
|
|
10983
11190
|
file_path = os.path.join(test_logpath, constants.SideBySide.HTML_FILE)
|
|
10984
|
-
out_file =
|
|
11191
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
10985
11192
|
out_file.writelines(the_html)
|
|
10986
11193
|
out_file.close()
|
|
10987
11194
|
|
|
@@ -11141,16 +11348,16 @@ class BaseCase(unittest.TestCase):
|
|
|
11141
11348
|
self.save_screenshot(
|
|
11142
11349
|
baseline_png, visual_baseline_path, selector="body"
|
|
11143
11350
|
)
|
|
11144
|
-
out_file =
|
|
11351
|
+
out_file = open(page_url_file, mode="w+", encoding="utf-8")
|
|
11145
11352
|
out_file.writelines(page_url)
|
|
11146
11353
|
out_file.close()
|
|
11147
|
-
out_file =
|
|
11354
|
+
out_file = open(level_1_file, mode="w+", encoding="utf-8")
|
|
11148
11355
|
out_file.writelines(json.dumps(level_1))
|
|
11149
11356
|
out_file.close()
|
|
11150
|
-
out_file =
|
|
11357
|
+
out_file = open(level_2_file, mode="w+", encoding="utf-8")
|
|
11151
11358
|
out_file.writelines(json.dumps(level_2))
|
|
11152
11359
|
out_file.close()
|
|
11153
|
-
out_file =
|
|
11360
|
+
out_file = open(level_3_file, mode="w+", encoding="utf-8")
|
|
11154
11361
|
out_file.writelines(json.dumps(level_3))
|
|
11155
11362
|
out_file.close()
|
|
11156
11363
|
|
|
@@ -11289,7 +11496,7 @@ class BaseCase(unittest.TestCase):
|
|
|
11289
11496
|
alpha_n_d_name = "".join([x if x.isalnum() else "_" for x in name])
|
|
11290
11497
|
side_by_side_name = "side_by_side_%s.html" % alpha_n_d_name
|
|
11291
11498
|
file_path = os.path.join(test_logpath, side_by_side_name)
|
|
11292
|
-
out_file =
|
|
11499
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
11293
11500
|
out_file.writelines(the_html)
|
|
11294
11501
|
out_file.close()
|
|
11295
11502
|
|
|
@@ -11312,7 +11519,14 @@ class BaseCase(unittest.TestCase):
|
|
|
11312
11519
|
|
|
11313
11520
|
def __is_cdp_swap_needed(self):
|
|
11314
11521
|
"""If the driver is disconnected, use a CDP method when available."""
|
|
11315
|
-
|
|
11522
|
+
cdp_swap_needed = shared_utils.is_cdp_swap_needed(self.driver)
|
|
11523
|
+
if cdp_swap_needed:
|
|
11524
|
+
if not self.cdp:
|
|
11525
|
+
self.cdp = self.driver.cdp
|
|
11526
|
+
self.undetectable = True
|
|
11527
|
+
return True
|
|
11528
|
+
else:
|
|
11529
|
+
return False
|
|
11316
11530
|
|
|
11317
11531
|
############
|
|
11318
11532
|
|
|
@@ -11974,7 +12188,7 @@ class BaseCase(unittest.TestCase):
|
|
|
11974
12188
|
with suppress(Exception):
|
|
11975
12189
|
os.makedirs(saved_presentations_folder)
|
|
11976
12190
|
file_path = os.path.join(saved_presentations_folder, filename)
|
|
11977
|
-
out_file =
|
|
12191
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
11978
12192
|
out_file.writelines(the_html)
|
|
11979
12193
|
out_file.close()
|
|
11980
12194
|
if self._output_file_saves:
|
|
@@ -12669,7 +12883,7 @@ class BaseCase(unittest.TestCase):
|
|
|
12669
12883
|
with suppress(Exception):
|
|
12670
12884
|
os.makedirs(saved_charts_folder)
|
|
12671
12885
|
file_path = os.path.join(saved_charts_folder, filename)
|
|
12672
|
-
out_file =
|
|
12886
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
12673
12887
|
out_file.writelines(the_html)
|
|
12674
12888
|
out_file.close()
|
|
12675
12889
|
if self._output_file_saves:
|
|
@@ -13728,7 +13942,7 @@ class BaseCase(unittest.TestCase):
|
|
|
13728
13942
|
simulateClick(someLink);"""
|
|
13729
13943
|
% css_selector
|
|
13730
13944
|
)
|
|
13731
|
-
if
|
|
13945
|
+
if getattr(self, "recorder_mode", None):
|
|
13732
13946
|
self.save_recorded_actions()
|
|
13733
13947
|
try:
|
|
13734
13948
|
self.execute_script(script)
|
|
@@ -13776,7 +13990,7 @@ class BaseCase(unittest.TestCase):
|
|
|
13776
13990
|
var someLink = arguments[0];
|
|
13777
13991
|
simulateClick(someLink);"""
|
|
13778
13992
|
)
|
|
13779
|
-
if
|
|
13993
|
+
if getattr(self, "recorder_mode", None):
|
|
13780
13994
|
self.save_recorded_actions()
|
|
13781
13995
|
try:
|
|
13782
13996
|
self.execute_script(script, element)
|
|
@@ -13828,6 +14042,9 @@ class BaseCase(unittest.TestCase):
|
|
|
13828
14042
|
timeout=None,
|
|
13829
14043
|
center=None,
|
|
13830
14044
|
):
|
|
14045
|
+
if self.__is_cdp_swap_needed():
|
|
14046
|
+
self.cdp.click_with_offset(selector, x, y, center=center)
|
|
14047
|
+
return
|
|
13831
14048
|
self.wait_for_ready_state_complete()
|
|
13832
14049
|
if self.__needs_minimum_wait():
|
|
13833
14050
|
time.sleep(0.14)
|
|
@@ -13905,7 +14122,7 @@ class BaseCase(unittest.TestCase):
|
|
|
13905
14122
|
)
|
|
13906
14123
|
raise Exception(message)
|
|
13907
14124
|
except InvalidArgumentException:
|
|
13908
|
-
if not self.
|
|
14125
|
+
if not self.is_chromium():
|
|
13909
14126
|
raise
|
|
13910
14127
|
chrome_version = self.driver.capabilities["browserVersion"]
|
|
13911
14128
|
major_chrome_version = chrome_version.split(".")[0]
|
|
@@ -13980,7 +14197,7 @@ class BaseCase(unittest.TestCase):
|
|
|
13980
14197
|
selector = self.convert_to_css_selector(selector, by=by)
|
|
13981
14198
|
selector = self.__make_css_match_first_element_only(selector)
|
|
13982
14199
|
click_script = """jQuery('%s')[0].click();""" % selector
|
|
13983
|
-
if
|
|
14200
|
+
if getattr(self, "recorder_mode", None):
|
|
13984
14201
|
self.save_recorded_actions()
|
|
13985
14202
|
self.safe_execute_script(click_script)
|
|
13986
14203
|
|
|
@@ -14136,8 +14353,7 @@ class BaseCase(unittest.TestCase):
|
|
|
14136
14353
|
def __needs_minimum_wait(self):
|
|
14137
14354
|
if (
|
|
14138
14355
|
self.page_load_strategy == "none"
|
|
14139
|
-
and
|
|
14140
|
-
and settings.SKIP_JS_WAITS
|
|
14356
|
+
and getattr(settings, "SKIP_JS_WAITS", None)
|
|
14141
14357
|
):
|
|
14142
14358
|
return True
|
|
14143
14359
|
else:
|
|
@@ -14373,7 +14589,8 @@ class BaseCase(unittest.TestCase):
|
|
|
14373
14589
|
sb_config._virtual_display = self._xvfb_display
|
|
14374
14590
|
if "DISPLAY" not in os.environ.keys():
|
|
14375
14591
|
print(
|
|
14376
|
-
"\
|
|
14592
|
+
"\n X11 display failed! Is Xvfb installed? "
|
|
14593
|
+
"\n Try this: `sudo apt install -y xvfb`"
|
|
14377
14594
|
)
|
|
14378
14595
|
self.__activate_standard_virtual_display()
|
|
14379
14596
|
else:
|
|
@@ -14386,7 +14603,10 @@ class BaseCase(unittest.TestCase):
|
|
|
14386
14603
|
print("\n" + str(e.msg))
|
|
14387
14604
|
else:
|
|
14388
14605
|
print(e)
|
|
14389
|
-
print(
|
|
14606
|
+
print(
|
|
14607
|
+
"\n X11 display failed! Is Xvfb installed? "
|
|
14608
|
+
"\n Try this: `sudo apt install -y xvfb`"
|
|
14609
|
+
)
|
|
14390
14610
|
self.__activate_standard_virtual_display()
|
|
14391
14611
|
return
|
|
14392
14612
|
pyautogui_is_installed = False
|
|
@@ -14394,11 +14614,11 @@ class BaseCase(unittest.TestCase):
|
|
|
14394
14614
|
import pyautogui
|
|
14395
14615
|
with suppress(Exception):
|
|
14396
14616
|
use_pyautogui_ver = constants.PyAutoGUI.VER
|
|
14397
|
-
|
|
14617
|
+
u_pv = shared_utils.make_version_tuple(use_pyautogui_ver)
|
|
14618
|
+
pv = shared_utils.make_version_tuple(pyautogui.__version__)
|
|
14619
|
+
if pv < u_pv:
|
|
14398
14620
|
del pyautogui # To get newer ver
|
|
14399
|
-
shared_utils.pip_install(
|
|
14400
|
-
"pyautogui", version=use_pyautogui_ver
|
|
14401
|
-
)
|
|
14621
|
+
shared_utils.pip_install("pyautogui", version="Latest")
|
|
14402
14622
|
import pyautogui
|
|
14403
14623
|
pyautogui_is_installed = True
|
|
14404
14624
|
except Exception:
|
|
@@ -14407,9 +14627,7 @@ class BaseCase(unittest.TestCase):
|
|
|
14407
14627
|
"Installing now..."
|
|
14408
14628
|
)
|
|
14409
14629
|
print("\n" + message)
|
|
14410
|
-
shared_utils.pip_install(
|
|
14411
|
-
"pyautogui", version=constants.PyAutoGUI.VER
|
|
14412
|
-
)
|
|
14630
|
+
shared_utils.pip_install("pyautogui", version="Latest")
|
|
14413
14631
|
import pyautogui
|
|
14414
14632
|
pyautogui_is_installed = True
|
|
14415
14633
|
if (
|
|
@@ -14467,10 +14685,7 @@ class BaseCase(unittest.TestCase):
|
|
|
14467
14685
|
|
|
14468
14686
|
def __disable_beforeunload_as_needed(self):
|
|
14469
14687
|
"""Disables beforeunload as needed. Also resets frame_switch state."""
|
|
14470
|
-
if (
|
|
14471
|
-
hasattr(self, "_disable_beforeunload")
|
|
14472
|
-
and self._disable_beforeunload
|
|
14473
|
-
):
|
|
14688
|
+
if getattr(self, "_disable_beforeunload", None):
|
|
14474
14689
|
self.disable_beforeunload()
|
|
14475
14690
|
if self.recorder_mode:
|
|
14476
14691
|
try:
|
|
@@ -14522,7 +14737,7 @@ class BaseCase(unittest.TestCase):
|
|
|
14522
14737
|
try:
|
|
14523
14738
|
shadow_root = element.shadow_root
|
|
14524
14739
|
except Exception:
|
|
14525
|
-
if self.
|
|
14740
|
+
if self.is_chromium():
|
|
14526
14741
|
chrome_dict = self.driver.capabilities["chrome"]
|
|
14527
14742
|
chrome_dr_version = chrome_dict["chromedriverVersion"]
|
|
14528
14743
|
chromedriver_version = chrome_dr_version.split(" ")[0]
|
|
@@ -15271,9 +15486,9 @@ class BaseCase(unittest.TestCase):
|
|
|
15271
15486
|
self.__skip_reason = None
|
|
15272
15487
|
self.testcase_manager.insert_testcase_data(data_payload)
|
|
15273
15488
|
self.case_start_time = int(time.time() * 1000.0)
|
|
15274
|
-
elif
|
|
15489
|
+
elif getattr(self, "is_behave", None):
|
|
15275
15490
|
self.__initialize_variables()
|
|
15276
|
-
elif
|
|
15491
|
+
elif getattr(self, "is_nosetest", None):
|
|
15277
15492
|
pass # Setup performed in plugins for pynose
|
|
15278
15493
|
else:
|
|
15279
15494
|
# Pure Python run. (Eg. SB() and Driver() Managers)
|
|
@@ -15364,7 +15579,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15364
15579
|
"Invalid input for Mobile Emulator device metrics!\n"
|
|
15365
15580
|
"Expecting a comma-separated string with integer values\n"
|
|
15366
15581
|
"for Width/Height, and an int or float for Pixel-Ratio.\n"
|
|
15367
|
-
'Example: --metrics="
|
|
15582
|
+
'Example: --metrics="412,732,3" '
|
|
15368
15583
|
)
|
|
15369
15584
|
if len(metrics_list) != 3:
|
|
15370
15585
|
raise Exception(exception_string)
|
|
@@ -15465,7 +15680,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15465
15680
|
)
|
|
15466
15681
|
raise Exception(message)
|
|
15467
15682
|
|
|
15468
|
-
if not
|
|
15683
|
+
if not getattr(self, "is_nosetest", None):
|
|
15469
15684
|
# Xvfb Virtual Display activation for Linux
|
|
15470
15685
|
self.__activate_virtual_display_as_needed()
|
|
15471
15686
|
|
|
@@ -15641,10 +15856,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15641
15856
|
self.__last_page_screenshot_png is for all screenshot log files."""
|
|
15642
15857
|
SCREENSHOT_SKIPPED = constants.Warnings.SCREENSHOT_SKIPPED
|
|
15643
15858
|
SCREENSHOT_UNDEFINED = constants.Warnings.SCREENSHOT_UNDEFINED
|
|
15644
|
-
if (
|
|
15645
|
-
hasattr(self, "no_screenshot_after_test")
|
|
15646
|
-
and self.no_screenshot_after_test
|
|
15647
|
-
):
|
|
15859
|
+
if getattr(self, "no_screenshot_after_test", None):
|
|
15648
15860
|
from seleniumbase.core import encoded_images
|
|
15649
15861
|
|
|
15650
15862
|
NO_SCREENSHOT = encoded_images.get_no_screenshot_png()
|
|
@@ -15675,10 +15887,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15675
15887
|
ignore_test_time_limit=True,
|
|
15676
15888
|
)
|
|
15677
15889
|
try:
|
|
15678
|
-
if (
|
|
15679
|
-
hasattr(settings, "SCREENSHOT_WITH_BACKGROUND")
|
|
15680
|
-
and settings.SCREENSHOT_WITH_BACKGROUND
|
|
15681
|
-
):
|
|
15890
|
+
if getattr(settings, "SCREENSHOT_WITH_BACKGROUND", None):
|
|
15682
15891
|
self.__last_page_screenshot = (
|
|
15683
15892
|
self.driver.get_screenshot_as_base64()
|
|
15684
15893
|
)
|
|
@@ -15749,11 +15958,10 @@ class BaseCase(unittest.TestCase):
|
|
|
15749
15958
|
exc_message = None
|
|
15750
15959
|
if (
|
|
15751
15960
|
hasattr(self, "_outcome")
|
|
15752
|
-
and
|
|
15753
|
-
and self._outcome.errors
|
|
15961
|
+
and getattr(self._outcome, "errors", None)
|
|
15754
15962
|
):
|
|
15755
15963
|
try:
|
|
15756
|
-
exc_message = self._outcome.errors[
|
|
15964
|
+
exc_message = self._outcome.errors[-1][1][1]
|
|
15757
15965
|
except Exception:
|
|
15758
15966
|
exc_message = "(Unknown Exception)"
|
|
15759
15967
|
else:
|
|
@@ -15838,8 +16046,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15838
16046
|
def __delay_driver_quit(self):
|
|
15839
16047
|
delay_driver_quit = False
|
|
15840
16048
|
if (
|
|
15841
|
-
|
|
15842
|
-
and self._using_sb_fixture
|
|
16049
|
+
getattr(self, "_using_sb_fixture", None)
|
|
15843
16050
|
and "--pdb" in sys.argv
|
|
15844
16051
|
and self.__has_exception()
|
|
15845
16052
|
and len(self._drivers_list) == 1
|
|
@@ -15895,14 +16102,22 @@ class BaseCase(unittest.TestCase):
|
|
|
15895
16102
|
has_exception = False
|
|
15896
16103
|
if hasattr(sys, "last_traceback") and sys.last_traceback is not None:
|
|
15897
16104
|
has_exception = True
|
|
15898
|
-
elif
|
|
16105
|
+
elif getattr(self, "is_context_manager", None):
|
|
15899
16106
|
if self.with_testing_base and self._has_failure:
|
|
15900
16107
|
return True
|
|
15901
16108
|
else:
|
|
15902
16109
|
return False
|
|
15903
16110
|
elif hasattr(self, "_outcome") and hasattr(self._outcome, "errors"):
|
|
15904
|
-
if
|
|
15905
|
-
|
|
16111
|
+
if python3_11_or_newer:
|
|
16112
|
+
if (
|
|
16113
|
+
self._outcome.errors
|
|
16114
|
+
and self._outcome.errors[-1]
|
|
16115
|
+
and self._outcome.errors[-1][1]
|
|
16116
|
+
):
|
|
16117
|
+
has_exception = True
|
|
16118
|
+
else:
|
|
16119
|
+
if self._outcome.errors:
|
|
16120
|
+
has_exception = True
|
|
15906
16121
|
else:
|
|
15907
16122
|
has_exception = sys.exc_info()[1] is not None
|
|
15908
16123
|
if self.__will_be_skipped and hasattr(self, "_using_sb_fixture"):
|
|
@@ -15911,7 +16126,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15911
16126
|
|
|
15912
16127
|
def __get_test_id(self):
|
|
15913
16128
|
"""The id used in various places such as the test log path."""
|
|
15914
|
-
if
|
|
16129
|
+
if getattr(self, "is_behave", None):
|
|
15915
16130
|
file_name = sb_config.behave_scenario.filename
|
|
15916
16131
|
file_name = file_name.replace("/", ".").replace("\\", ".")
|
|
15917
16132
|
scenario_name = sb_config.behave_scenario.name
|
|
@@ -15921,7 +16136,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15921
16136
|
scenario_name = scenario_name.replace(" ", "_")
|
|
15922
16137
|
test_id = "%s.%s" % (file_name, scenario_name)
|
|
15923
16138
|
return test_id
|
|
15924
|
-
elif
|
|
16139
|
+
elif getattr(self, "is_context_manager", None):
|
|
15925
16140
|
if hasattr(self, "_manager_saved_id"):
|
|
15926
16141
|
self.__saved_id = self._manager_saved_id
|
|
15927
16142
|
if self.__saved_id:
|
|
@@ -15933,7 +16148,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15933
16148
|
import traceback
|
|
15934
16149
|
stack_base = traceback.format_stack()[0].split(os.sep)[-1]
|
|
15935
16150
|
test_base = stack_base.split(", in ")[0]
|
|
15936
|
-
if
|
|
16151
|
+
if getattr(self, "cm_filename", None):
|
|
15937
16152
|
filename = self.cm_filename
|
|
15938
16153
|
else:
|
|
15939
16154
|
filename = test_base.split('"')[0]
|
|
@@ -15948,12 +16163,12 @@ class BaseCase(unittest.TestCase):
|
|
|
15948
16163
|
)
|
|
15949
16164
|
if self._sb_test_identifier and len(str(self._sb_test_identifier)) > 6:
|
|
15950
16165
|
test_id = self._sb_test_identifier
|
|
15951
|
-
elif
|
|
16166
|
+
elif getattr(self, "_using_sb_fixture", None):
|
|
15952
16167
|
test_id = sb_config._latest_display_id
|
|
15953
16168
|
test_id = test_id.replace(".py::", ".").replace("::", ".")
|
|
15954
16169
|
test_id = test_id.replace("/", ".").replace("\\", ".")
|
|
15955
16170
|
test_id = test_id.replace(" ", "_")
|
|
15956
|
-
# Linux filename length limit for `
|
|
16171
|
+
# Linux filename length limit for `open(filename)` = 255
|
|
15957
16172
|
# 255 - len("latest_logs/") - len("/basic_test_info.txt") = 223
|
|
15958
16173
|
if len(test_id) <= 223:
|
|
15959
16174
|
return test_id
|
|
@@ -15970,7 +16185,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15970
16185
|
return full_name.split("] ")[0] + "]"
|
|
15971
16186
|
else:
|
|
15972
16187
|
return full_name.split(" ")[0]
|
|
15973
|
-
if
|
|
16188
|
+
if getattr(self, "is_behave", None):
|
|
15974
16189
|
return self.__get_test_id()
|
|
15975
16190
|
test_id = "%s.%s.%s" % (
|
|
15976
16191
|
self.__class__.__module__.split(".")[-1],
|
|
@@ -15991,7 +16206,7 @@ class BaseCase(unittest.TestCase):
|
|
|
15991
16206
|
return full_name.split("] ")[0] + "]"
|
|
15992
16207
|
else:
|
|
15993
16208
|
return full_name.split(" ")[0]
|
|
15994
|
-
if
|
|
16209
|
+
if getattr(self, "is_behave", None):
|
|
15995
16210
|
file_name = sb_config.behave_scenario.filename
|
|
15996
16211
|
line_num = sb_config.behave_line_num
|
|
15997
16212
|
scenario_name = sb_config.behave_scenario.name
|
|
@@ -16024,7 +16239,7 @@ class BaseCase(unittest.TestCase):
|
|
|
16024
16239
|
if "PYTEST_CURRENT_TEST" in os.environ:
|
|
16025
16240
|
test_id = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]
|
|
16026
16241
|
filename = test_id.split("::")[0].split("/")[-1]
|
|
16027
|
-
elif
|
|
16242
|
+
elif getattr(self, "is_behave", None):
|
|
16028
16243
|
filename = sb_config.behave_scenario.filename
|
|
16029
16244
|
filename = filename.split("/")[-1].split("\\")[-1]
|
|
16030
16245
|
else:
|
|
@@ -16060,8 +16275,7 @@ class BaseCase(unittest.TestCase):
|
|
|
16060
16275
|
sb_config._pdb_failure = True
|
|
16061
16276
|
elif (
|
|
16062
16277
|
self.is_pytest
|
|
16063
|
-
and
|
|
16064
|
-
and sb_config._pdb_failure
|
|
16278
|
+
and getattr(sb_config, "_pdb_failure", None)
|
|
16065
16279
|
and not has_exception
|
|
16066
16280
|
):
|
|
16067
16281
|
return # Handle case where "pytest --pdb" marks failures as Passed
|
|
@@ -16231,7 +16445,7 @@ class BaseCase(unittest.TestCase):
|
|
|
16231
16445
|
dash_pie = json.dumps(sb_config._saved_dashboard_pie)
|
|
16232
16446
|
dash_pie_loc = constants.Dashboard.DASH_PIE
|
|
16233
16447
|
pie_path = os.path.join(abs_path, dash_pie_loc)
|
|
16234
|
-
pie_file =
|
|
16448
|
+
pie_file = open(pie_path, mode="w+", encoding="utf-8")
|
|
16235
16449
|
pie_file.writelines(dash_pie)
|
|
16236
16450
|
pie_file.close()
|
|
16237
16451
|
DASH_PIE_PNG_1 = constants.Dashboard.get_dash_pie_1()
|
|
@@ -16391,7 +16605,7 @@ class BaseCase(unittest.TestCase):
|
|
|
16391
16605
|
)
|
|
16392
16606
|
abs_path = os.path.abspath(".")
|
|
16393
16607
|
file_path = os.path.join(abs_path, "dashboard.html")
|
|
16394
|
-
out_file =
|
|
16608
|
+
out_file = open(file_path, mode="w+", encoding="utf-8")
|
|
16395
16609
|
out_file.writelines(the_html)
|
|
16396
16610
|
out_file.close()
|
|
16397
16611
|
sb_config._dash_html = the_html
|
|
@@ -16404,7 +16618,7 @@ class BaseCase(unittest.TestCase):
|
|
|
16404
16618
|
dash_json = json.dumps((_results, _display_id, _rt, _tlp, d_stats))
|
|
16405
16619
|
dash_json_loc = constants.Dashboard.DASH_JSON
|
|
16406
16620
|
dash_jsonpath = os.path.join(abs_path, dash_json_loc)
|
|
16407
|
-
dash_json_file =
|
|
16621
|
+
dash_json_file = open(dash_jsonpath, mode="w+", encoding="utf-8")
|
|
16408
16622
|
dash_json_file.writelines(dash_json)
|
|
16409
16623
|
dash_json_file.close()
|
|
16410
16624
|
|
|
@@ -16455,7 +16669,7 @@ class BaseCase(unittest.TestCase):
|
|
|
16455
16669
|
self.__check_scope()
|
|
16456
16670
|
except Exception:
|
|
16457
16671
|
return
|
|
16458
|
-
if
|
|
16672
|
+
if getattr(self, "recorder_mode", None):
|
|
16459
16673
|
# In case tearDown() leaves the origin, save actions first.
|
|
16460
16674
|
self.save_recorded_actions()
|
|
16461
16675
|
if (
|
|
@@ -16650,7 +16864,7 @@ class BaseCase(unittest.TestCase):
|
|
|
16650
16864
|
if not hasattr(self, "_using_sb_fixture") and self.__called_teardown:
|
|
16651
16865
|
# This test already called tearDown()
|
|
16652
16866
|
return
|
|
16653
|
-
if
|
|
16867
|
+
if getattr(self, "recorder_mode", None):
|
|
16654
16868
|
page_actions._reconnect_if_disconnected(self.driver)
|
|
16655
16869
|
try:
|
|
16656
16870
|
self.__process_recorded_actions()
|
|
@@ -16885,7 +17099,7 @@ class BaseCase(unittest.TestCase):
|
|
|
16885
17099
|
self.testcase_manager.update_testcase_log_url(data_payload)
|
|
16886
17100
|
else:
|
|
16887
17101
|
# (Pynose / Behave / Pure Python)
|
|
16888
|
-
if
|
|
17102
|
+
if getattr(self, "is_behave", None):
|
|
16889
17103
|
if sb_config.behave_scenario.status.name == "failed":
|
|
16890
17104
|
has_exception = True
|
|
16891
17105
|
sb_config._has_exception = True
|
|
@@ -16945,8 +17159,8 @@ class BaseCase(unittest.TestCase):
|
|
|
16945
17159
|
self._last_page_url = self.get_current_url()
|
|
16946
17160
|
except Exception:
|
|
16947
17161
|
self._last_page_url = "(Error: Unknown URL)"
|
|
16948
|
-
if
|
|
16949
|
-
if
|
|
17162
|
+
if getattr(self, "is_behave", None) and has_exception:
|
|
17163
|
+
if getattr(sb_config, "pdb_option", None):
|
|
16950
17164
|
if (
|
|
16951
17165
|
hasattr(sb_config, "behave_step")
|
|
16952
17166
|
and hasattr(sb_config.behave_step, "exc_traceback")
|
|
@@ -16954,24 +17168,14 @@ class BaseCase(unittest.TestCase):
|
|
|
16954
17168
|
self.__activate_behave_post_mortem_debug_mode()
|
|
16955
17169
|
if self._final_debug:
|
|
16956
17170
|
self.__activate_debug_mode_in_teardown()
|
|
16957
|
-
elif (
|
|
16958
|
-
hasattr(sb_config, "_do_sb_post_mortem")
|
|
16959
|
-
and sb_config._do_sb_post_mortem
|
|
16960
|
-
):
|
|
17171
|
+
elif getattr(sb_config, "_do_sb_post_mortem", None):
|
|
16961
17172
|
self.__activate_sb_mgr_post_mortem_debug_mode()
|
|
16962
|
-
elif (
|
|
16963
|
-
hasattr(sb_config, "_do_sb_final_trace")
|
|
16964
|
-
and sb_config._do_sb_final_trace
|
|
16965
|
-
):
|
|
17173
|
+
elif getattr(sb_config, "_do_sb_final_trace", None):
|
|
16966
17174
|
self.__activate_debug_mode_in_teardown()
|
|
16967
17175
|
# (Pynose / Behave / Pure Python) Close all open browser windows
|
|
16968
17176
|
self.__quit_all_drivers()
|
|
16969
17177
|
# Resume tearDown() for all test runners, (Pytest / Pynose / Behave)
|
|
16970
|
-
if (
|
|
16971
|
-
hasattr(self, "_xvfb_display")
|
|
16972
|
-
and self._xvfb_display
|
|
16973
|
-
and not self._reuse_session
|
|
16974
|
-
):
|
|
17178
|
+
if getattr(self, "_xvfb_display", None) and not self._reuse_session:
|
|
16975
17179
|
# Stop the Xvfb virtual display launched from BaseCase
|
|
16976
17180
|
try:
|
|
16977
17181
|
if hasattr(self._xvfb_display, "stop"):
|
|
@@ -16983,16 +17187,9 @@ class BaseCase(unittest.TestCase):
|
|
|
16983
17187
|
except Exception:
|
|
16984
17188
|
pass
|
|
16985
17189
|
if (
|
|
16986
|
-
|
|
16987
|
-
and sb_config._virtual_display
|
|
17190
|
+
getattr(sb_config, "_virtual_display", None)
|
|
16988
17191
|
and hasattr(sb_config._virtual_display, "stop")
|
|
16989
|
-
and (
|
|
16990
|
-
not hasattr(sb_config, "reuse_session")
|
|
16991
|
-
or (
|
|
16992
|
-
hasattr(sb_config, "reuse_session")
|
|
16993
|
-
and not sb_config.reuse_session
|
|
16994
|
-
)
|
|
16995
|
-
)
|
|
17192
|
+
and not getattr(sb_config, "reuse_session", None)
|
|
16996
17193
|
):
|
|
16997
17194
|
# CDP Mode may launch a 2nd Xvfb virtual display
|
|
16998
17195
|
try:
|