seleniumbase 4.42.1__py3-none-any.whl → 4.42.3__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.
- seleniumbase/__version__.py +1 -1
- seleniumbase/core/browser_launcher.py +4 -0
- seleniumbase/core/sb_cdp.py +35 -1
- seleniumbase/fixtures/base_case.py +7 -27
- seleniumbase/fixtures/page_actions.py +4 -0
- seleniumbase/fixtures/shared_utils.py +29 -0
- seleniumbase/undetected/cdp_driver/browser.py +6 -8
- seleniumbase/undetected/cdp_driver/cdp_util.py +210 -4
- {seleniumbase-4.42.1.dist-info → seleniumbase-4.42.3.dist-info}/METADATA +11 -11
- {seleniumbase-4.42.1.dist-info → seleniumbase-4.42.3.dist-info}/RECORD +14 -14
- {seleniumbase-4.42.1.dist-info → seleniumbase-4.42.3.dist-info}/WHEEL +0 -0
- {seleniumbase-4.42.1.dist-info → seleniumbase-4.42.3.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.42.1.dist-info → seleniumbase-4.42.3.dist-info}/licenses/LICENSE +0 -0
- {seleniumbase-4.42.1.dist-info → seleniumbase-4.42.3.dist-info}/top_level.txt +0 -0
seleniumbase/__version__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# seleniumbase package
|
2
|
-
__version__ = "4.42.
|
2
|
+
__version__ = "4.42.3"
|
@@ -766,6 +766,8 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
|
|
766
766
|
cdp.set_local_storage_item = CDPM.set_local_storage_item
|
767
767
|
cdp.set_session_storage_item = CDPM.set_session_storage_item
|
768
768
|
cdp.set_attributes = CDPM.set_attributes
|
769
|
+
cdp.is_attribute_present = CDPM.is_attribute_present
|
770
|
+
cdp.is_online = CDPM.is_online
|
769
771
|
cdp.gui_press_key = CDPM.gui_press_key
|
770
772
|
cdp.gui_press_keys = CDPM.gui_press_keys
|
771
773
|
cdp.gui_write = CDPM.gui_write
|
@@ -815,10 +817,12 @@ def uc_open_with_cdp_mode(driver, url=None, **kwargs):
|
|
815
817
|
cdp.get_screen_rect = CDPM.get_screen_rect
|
816
818
|
cdp.get_window_rect = CDPM.get_window_rect
|
817
819
|
cdp.get_window_size = CDPM.get_window_size
|
820
|
+
cdp.get_mfa_code = CDPM.get_mfa_code
|
818
821
|
cdp.nested_click = CDPM.nested_click
|
819
822
|
cdp.select_option_by_text = CDPM.select_option_by_text
|
820
823
|
cdp.select_option_by_index = CDPM.select_option_by_index
|
821
824
|
cdp.select_option_by_value = CDPM.select_option_by_value
|
825
|
+
cdp.enter_mfa_code = CDPM.enter_mfa_code
|
822
826
|
cdp.flash = CDPM.flash
|
823
827
|
cdp.highlight = CDPM.highlight
|
824
828
|
cdp.focus = CDPM.focus
|
seleniumbase/core/sb_cdp.py
CHANGED
@@ -712,7 +712,10 @@ class CDPMethods():
|
|
712
712
|
self.__slow_mode_pause_if_set()
|
713
713
|
element = self.find_element(selector, timeout=timeout)
|
714
714
|
element.scroll_into_view()
|
715
|
-
element.
|
715
|
+
if element.tag_name == "div" or element.tag_name == "input":
|
716
|
+
element.mouse_click() # Simulated click (not PyAutoGUI)
|
717
|
+
else:
|
718
|
+
element.click()
|
716
719
|
self.__slow_mode_pause_if_set()
|
717
720
|
self.loop.run_until_complete(self.page.wait())
|
718
721
|
|
@@ -1393,6 +1396,17 @@ class CDPMethods():
|
|
1393
1396
|
)
|
1394
1397
|
)
|
1395
1398
|
|
1399
|
+
def get_mfa_code(self, totp_key=None):
|
1400
|
+
"""Returns a time-based one-time password based on the Google
|
1401
|
+
Authenticator algorithm for multi-factor authentication."""
|
1402
|
+
return shared_utils.get_mfa_code(totp_key)
|
1403
|
+
|
1404
|
+
def enter_mfa_code(self, selector, totp_key=None, timeout=None):
|
1405
|
+
if not timeout:
|
1406
|
+
timeout = settings.SMALL_TIMEOUT
|
1407
|
+
mfa_code = self.get_mfa_code(totp_key)
|
1408
|
+
self.type(selector, mfa_code + "\n", timeout=timeout)
|
1409
|
+
|
1396
1410
|
def set_locale(self, locale):
|
1397
1411
|
"""(Settings will take effect on the next page load)"""
|
1398
1412
|
self.loop.run_until_complete(self.page.set_locale(locale))
|
@@ -1430,6 +1444,26 @@ class CDPMethods():
|
|
1430
1444
|
with suppress(Exception):
|
1431
1445
|
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1432
1446
|
|
1447
|
+
def is_attribute_present(self, selector, attribute, value=None):
|
1448
|
+
try:
|
1449
|
+
element = self.find_element(selector, timeout=0.1)
|
1450
|
+
found_value = element.get_attribute(attribute)
|
1451
|
+
if found_value is None:
|
1452
|
+
return False
|
1453
|
+
if value is not None:
|
1454
|
+
if found_value == value:
|
1455
|
+
return True
|
1456
|
+
else:
|
1457
|
+
return False
|
1458
|
+
else:
|
1459
|
+
return True
|
1460
|
+
except Exception:
|
1461
|
+
return False
|
1462
|
+
|
1463
|
+
def is_online(self):
|
1464
|
+
js_code = "navigator.onLine;"
|
1465
|
+
return self.loop.run_until_complete(self.page.evaluate(js_code))
|
1466
|
+
|
1433
1467
|
def __make_sure_pyautogui_lock_is_writable(self):
|
1434
1468
|
with suppress(Exception):
|
1435
1469
|
shared_utils.make_writable(constants.MultiBrowser.PYAUTOGUILOCK)
|
@@ -1503,6 +1503,10 @@ class BaseCase(unittest.TestCase):
|
|
1503
1503
|
):
|
1504
1504
|
"""Returns True if the element attribute/value is found.
|
1505
1505
|
If the value is not specified, the attribute only needs to exist."""
|
1506
|
+
if self.__is_cdp_swap_needed():
|
1507
|
+
return self.cdp.is_attribute_present(
|
1508
|
+
selector, attribute, value=value
|
1509
|
+
)
|
1506
1510
|
self.wait_for_ready_state_complete()
|
1507
1511
|
time.sleep(0.01)
|
1508
1512
|
selector, by = self.__recalculate_selector(selector, by)
|
@@ -8510,33 +8514,9 @@ class BaseCase(unittest.TestCase):
|
|
8510
8514
|
|
8511
8515
|
def get_mfa_code(self, totp_key=None):
|
8512
8516
|
"""Same as get_totp_code() and get_google_auth_password().
|
8513
|
-
Returns a time-based one-time password based on the
|
8514
|
-
|
8515
|
-
|
8516
|
-
to using the one provided in [seleniumbase/config/settings.py].
|
8517
|
-
Google Authenticator codes expire & change at 30-sec intervals.
|
8518
|
-
If the fetched password expires in the next 1.2 seconds, waits
|
8519
|
-
for a new one before returning it (may take up to 1.2 seconds).
|
8520
|
-
See https://pyotp.readthedocs.io/en/latest/ for details."""
|
8521
|
-
import pyotp
|
8522
|
-
|
8523
|
-
if not totp_key:
|
8524
|
-
totp_key = settings.TOTP_KEY
|
8525
|
-
|
8526
|
-
epoch_interval = time.time() / 30.0
|
8527
|
-
cycle_lifespan = float(epoch_interval) - int(epoch_interval)
|
8528
|
-
if float(cycle_lifespan) > 0.96:
|
8529
|
-
# Password expires in the next 1.2 seconds. Wait for a new one.
|
8530
|
-
for i in range(30):
|
8531
|
-
time.sleep(0.04)
|
8532
|
-
epoch_interval = time.time() / 30.0
|
8533
|
-
cycle_lifespan = float(epoch_interval) - int(epoch_interval)
|
8534
|
-
if not float(cycle_lifespan) > 0.96:
|
8535
|
-
# The new password cycle has begun
|
8536
|
-
break
|
8537
|
-
|
8538
|
-
totp = pyotp.TOTP(totp_key)
|
8539
|
-
return str(totp.now())
|
8517
|
+
Returns a time-based one-time password based on the Google
|
8518
|
+
Authenticator algorithm for multi-factor authentication."""
|
8519
|
+
return shared_utils.get_mfa_code(totp_key)
|
8540
8520
|
|
8541
8521
|
def enter_mfa_code(
|
8542
8522
|
self, selector, totp_key=None, by="css selector", timeout=None
|
@@ -193,6 +193,10 @@ def is_attribute_present(
|
|
193
193
|
@Returns
|
194
194
|
Boolean (is attribute present)
|
195
195
|
"""
|
196
|
+
if __is_cdp_swap_needed(driver):
|
197
|
+
return driver.cdp.is_attribute_present(
|
198
|
+
selector, attribute, value=value
|
199
|
+
)
|
196
200
|
_reconnect_if_disconnected(driver)
|
197
201
|
try:
|
198
202
|
element = driver.find_element(by=by, value=selector)
|
@@ -7,6 +7,7 @@ import sys
|
|
7
7
|
import time
|
8
8
|
from contextlib import suppress
|
9
9
|
from seleniumbase import config as sb_config
|
10
|
+
from seleniumbase.config import settings
|
10
11
|
from seleniumbase.fixtures import constants
|
11
12
|
|
12
13
|
|
@@ -29,6 +30,34 @@ def pip_install(package, version=None):
|
|
29
30
|
)
|
30
31
|
|
31
32
|
|
33
|
+
def get_mfa_code(totp_key=None):
|
34
|
+
"""Returns a time-based one-time password based on the
|
35
|
+
Google Authenticator algorithm for multi-factor authentication.
|
36
|
+
If the "totp_key" is not specified, this method defaults
|
37
|
+
to using the one provided in [seleniumbase/config/settings.py].
|
38
|
+
Google Authenticator codes expire & change at 30-sec intervals.
|
39
|
+
If the fetched password expires in the next 1.2 seconds, waits
|
40
|
+
for a new one before returning it (may take up to 1.2 seconds).
|
41
|
+
See https://pyotp.readthedocs.io/en/latest/ for details."""
|
42
|
+
import pyotp
|
43
|
+
|
44
|
+
if not totp_key:
|
45
|
+
totp_key = settings.TOTP_KEY
|
46
|
+
epoch_interval = time.time() / 30.0
|
47
|
+
cycle_lifespan = float(epoch_interval) - int(epoch_interval)
|
48
|
+
if float(cycle_lifespan) > 0.96:
|
49
|
+
# Password expires in the next 1.2 seconds. Wait for a new one.
|
50
|
+
for i in range(30):
|
51
|
+
time.sleep(0.04)
|
52
|
+
epoch_interval = time.time() / 30.0
|
53
|
+
cycle_lifespan = float(epoch_interval) - int(epoch_interval)
|
54
|
+
if not float(cycle_lifespan) > 0.96:
|
55
|
+
# The new password cycle has begun
|
56
|
+
break
|
57
|
+
totp = pyotp.TOTP(totp_key)
|
58
|
+
return str(totp.now())
|
59
|
+
|
60
|
+
|
32
61
|
def is_arm_linux():
|
33
62
|
"""Returns True if machine is ARM Linux.
|
34
63
|
This will be useful once Google adds
|
@@ -350,7 +350,8 @@ class Browser:
|
|
350
350
|
if _cdp_geolocation:
|
351
351
|
await connection.send(cdp.page.navigate("about:blank"))
|
352
352
|
await connection.set_geolocation(_cdp_geolocation)
|
353
|
-
#
|
353
|
+
# This part isn't needed now, but may be needed later
|
354
|
+
"""
|
354
355
|
if (
|
355
356
|
hasattr(sb_config, "_cdp_proxy")
|
356
357
|
and "@" in sb_config._cdp_proxy
|
@@ -360,17 +361,14 @@ class Browser:
|
|
360
361
|
username_and_password = sb_config._cdp_proxy.split("@")[0]
|
361
362
|
proxy_user = username_and_password.split(":")[0]
|
362
363
|
proxy_pass = username_and_password.split(":")[1]
|
363
|
-
await connection.set_auth(
|
364
|
-
proxy_user, proxy_pass, self.tabs[0]
|
365
|
-
)
|
364
|
+
await connection.set_auth(proxy_user, proxy_pass, self.tabs[0])
|
366
365
|
time.sleep(0.25)
|
367
|
-
|
366
|
+
"""
|
367
|
+
if "auth" in kwargs and kwargs["auth"] and ":" in kwargs["auth"]:
|
368
368
|
username_and_password = kwargs["auth"]
|
369
369
|
proxy_user = username_and_password.split(":")[0]
|
370
370
|
proxy_pass = username_and_password.split(":")[1]
|
371
|
-
await connection.set_auth(
|
372
|
-
proxy_user, proxy_pass, self.tabs[0]
|
373
|
-
)
|
371
|
+
await connection.set_auth(proxy_user, proxy_pass, self.tabs[0])
|
374
372
|
time.sleep(0.25)
|
375
373
|
frame_id, loader_id, *_ = await connection.send(
|
376
374
|
cdp.page.navigate(url)
|
@@ -258,13 +258,13 @@ async def start(
|
|
258
258
|
config: Optional[Config] = None,
|
259
259
|
*,
|
260
260
|
user_data_dir: Optional[PathLike] = None,
|
261
|
-
headless: Optional[bool] =
|
262
|
-
incognito: Optional[bool] =
|
263
|
-
guest: Optional[bool] =
|
261
|
+
headless: Optional[bool] = None,
|
262
|
+
incognito: Optional[bool] = None,
|
263
|
+
guest: Optional[bool] = None,
|
264
264
|
browser_executable_path: Optional[PathLike] = None,
|
265
265
|
browser_args: Optional[List[str]] = None,
|
266
266
|
xvfb_metrics: Optional[List[str]] = None, # "Width,Height" for Linux
|
267
|
-
ad_block: Optional[bool] =
|
267
|
+
ad_block: Optional[bool] = None,
|
268
268
|
sandbox: Optional[bool] = True,
|
269
269
|
lang: Optional[str] = None, # Set the Language Locale Code
|
270
270
|
host: Optional[str] = None, # Chrome remote-debugging-host
|
@@ -318,6 +318,210 @@ async def start(
|
|
318
318
|
(For example, ensuring shadow-root is always in "open" mode.)
|
319
319
|
:type expert: bool
|
320
320
|
"""
|
321
|
+
sys_argv = sys.argv
|
322
|
+
arg_join = " ".join(sys_argv)
|
323
|
+
if headless is None:
|
324
|
+
if "--headless" in sys_argv:
|
325
|
+
headless = True
|
326
|
+
else:
|
327
|
+
headless = False
|
328
|
+
if headed is None:
|
329
|
+
if "--gui" in sys_argv or "--headed" in sys_argv:
|
330
|
+
headed = True
|
331
|
+
else:
|
332
|
+
headed = False
|
333
|
+
if xvfb is None:
|
334
|
+
if "--xvfb" in sys_argv:
|
335
|
+
xvfb = True
|
336
|
+
else:
|
337
|
+
xvfb = False
|
338
|
+
if incognito is None:
|
339
|
+
if "--incognito" in sys_argv:
|
340
|
+
incognito = True
|
341
|
+
else:
|
342
|
+
incognito = False
|
343
|
+
if guest is None:
|
344
|
+
if "--guest" in sys_argv:
|
345
|
+
guest = True
|
346
|
+
else:
|
347
|
+
guest = False
|
348
|
+
if ad_block is None:
|
349
|
+
if "--ad-block" in sys_argv or "--ad_block" in sys_argv:
|
350
|
+
ad_block = True
|
351
|
+
else:
|
352
|
+
ad_block = False
|
353
|
+
if xvfb_metrics is None and "--xvfb-metrics" in arg_join:
|
354
|
+
x_m = xvfb_metrics
|
355
|
+
count = 0
|
356
|
+
for arg in sys_argv:
|
357
|
+
if arg.startswith("--xvfb-metrics="):
|
358
|
+
x_m = arg.split("--xvfb-metrics=")[1]
|
359
|
+
break
|
360
|
+
elif arg == "--xvfb-metrics" and len(sys_argv) > count + 1:
|
361
|
+
x_m = sys_argv[count + 1]
|
362
|
+
if x_m.startswith("-"):
|
363
|
+
x_m = None
|
364
|
+
break
|
365
|
+
count += 1
|
366
|
+
if x_m:
|
367
|
+
if x_m.startswith('"') and x_m.endswith('"'):
|
368
|
+
x_m = x_m[1:-1]
|
369
|
+
elif x_m.startswith("'") and x_m.endswith("'"):
|
370
|
+
x_m = x_m[1:-1]
|
371
|
+
xvfb_metrics = x_m
|
372
|
+
if agent is None and "user_agent" not in kwargs and "--agent" in arg_join:
|
373
|
+
count = 0
|
374
|
+
for arg in sys_argv:
|
375
|
+
if arg.startswith("--agent="):
|
376
|
+
agent = arg.split("--agent=")[1]
|
377
|
+
break
|
378
|
+
elif arg == "--agent" and len(sys_argv) > count + 1:
|
379
|
+
agent = sys_argv[count + 1]
|
380
|
+
if agent.startswith("-"):
|
381
|
+
agent = None
|
382
|
+
break
|
383
|
+
count += 1
|
384
|
+
if agent:
|
385
|
+
if agent.startswith('"') and agent.endswith('"'):
|
386
|
+
agent = agent[1:-1]
|
387
|
+
elif agent.startswith("'") and agent.endswith("'"):
|
388
|
+
agent = agent[1:-1]
|
389
|
+
if (
|
390
|
+
geoloc is None
|
391
|
+
and "geolocation" not in kwargs
|
392
|
+
and "--geolocation" in arg_join
|
393
|
+
):
|
394
|
+
count = 0
|
395
|
+
for arg in sys_argv:
|
396
|
+
if arg.startswith("--geolocation="):
|
397
|
+
geoloc = arg.split("--geolocation=")[1]
|
398
|
+
break
|
399
|
+
elif arg == "--geolocation" and len(sys_argv) > count + 1:
|
400
|
+
geoloc = sys_argv[count + 1]
|
401
|
+
if geoloc.startswith("-"):
|
402
|
+
geoloc = None
|
403
|
+
break
|
404
|
+
count += 1
|
405
|
+
if geoloc:
|
406
|
+
if geoloc.startswith('"') and geoloc.endswith('"'):
|
407
|
+
geoloc = geoloc[1:-1]
|
408
|
+
elif geoloc.startswith("'") and geoloc.endswith("'"):
|
409
|
+
geoloc = geoloc[1:-1]
|
410
|
+
if geoloc:
|
411
|
+
import ast
|
412
|
+
geoloc = ast.literal_eval(geoloc)
|
413
|
+
if not lang and "locale" not in kwargs and "locale_code" not in kwargs:
|
414
|
+
if "--locale" in arg_join:
|
415
|
+
count = 0
|
416
|
+
for arg in sys_argv:
|
417
|
+
if arg.startswith("--locale="):
|
418
|
+
lang = arg.split("--locale=")[1]
|
419
|
+
break
|
420
|
+
elif arg == "--locale" and len(sys_argv) > count + 1:
|
421
|
+
lang = sys_argv[count + 1]
|
422
|
+
if lang.startswith("-"):
|
423
|
+
lang = None
|
424
|
+
break
|
425
|
+
count += 1
|
426
|
+
elif "--lang" in arg_join:
|
427
|
+
count = 0
|
428
|
+
for arg in sys_argv:
|
429
|
+
if arg.startswith("--lang="):
|
430
|
+
lang = arg.split("--lang=")[1]
|
431
|
+
break
|
432
|
+
elif arg == "--lang" and len(sys_argv) > count + 1:
|
433
|
+
lang = sys_argv[count + 1]
|
434
|
+
if lang.startswith("-"):
|
435
|
+
lang = None
|
436
|
+
break
|
437
|
+
count += 1
|
438
|
+
if lang:
|
439
|
+
if lang.startswith('"') and lang.endswith('"'):
|
440
|
+
lang = lang[1:-1]
|
441
|
+
elif lang.startswith("'") and lang.endswith("'"):
|
442
|
+
lang = lang[1:-1]
|
443
|
+
if not browser_executable_path and "binary_location" not in kwargs:
|
444
|
+
bin_loc = None
|
445
|
+
if "--binary-location" in arg_join or "--binary_location" in arg_join:
|
446
|
+
bin_loc_cmd = "--binary-location"
|
447
|
+
if "--binary_location" in arg_join:
|
448
|
+
bin_loc_cmd = "--binary_location"
|
449
|
+
count = 0
|
450
|
+
bin_loc = None
|
451
|
+
for arg in sys_argv:
|
452
|
+
if arg.startswith("%s=" % bin_loc_cmd):
|
453
|
+
bin_loc = arg.split("%s=" % bin_loc_cmd)[1]
|
454
|
+
break
|
455
|
+
elif arg == bin_loc_cmd and len(sys_argv) > count + 1:
|
456
|
+
bin_loc = sys_argv[count + 1]
|
457
|
+
if bin_loc.startswith("-"):
|
458
|
+
bin_loc = None
|
459
|
+
break
|
460
|
+
count += 1
|
461
|
+
elif "--bl=" in arg_join:
|
462
|
+
count = 0
|
463
|
+
bin_loc = None
|
464
|
+
for arg in sys_argv:
|
465
|
+
if arg.startswith("--bl="):
|
466
|
+
bin_loc = arg.split("--bl=")[1]
|
467
|
+
break
|
468
|
+
count += 1
|
469
|
+
if bin_loc:
|
470
|
+
if bin_loc.startswith('"') and bin_loc.endswith('"'):
|
471
|
+
bin_loc = bin_loc[1:-1]
|
472
|
+
elif bin_loc.startswith("'") and bin_loc.endswith("'"):
|
473
|
+
bin_loc = bin_loc[1:-1]
|
474
|
+
if bin_loc and not os.path.exists(bin_loc):
|
475
|
+
print(" No browser executable at PATH {%s}! " % bin_loc)
|
476
|
+
print(" Using default Chrome browser instead!")
|
477
|
+
bin_loc = None
|
478
|
+
browser_executable_path = bin_loc
|
479
|
+
if proxy is None and "--proxy" in arg_join:
|
480
|
+
proxy_string = None
|
481
|
+
if "--proxy=" in arg_join:
|
482
|
+
proxy_string = arg_join.split("--proxy=")[1].split(" ")[0]
|
483
|
+
elif "--proxy " in arg_join:
|
484
|
+
proxy_string = arg_join.split("--proxy ")[1].split(" ")[0]
|
485
|
+
if proxy_string:
|
486
|
+
if proxy_string.startswith('"') and proxy_string.endswith('"'):
|
487
|
+
proxy_string = proxy_string[1:-1]
|
488
|
+
elif proxy_string.startswith("'") and proxy_string.endswith("'"):
|
489
|
+
proxy_string = proxy_string[1:-1]
|
490
|
+
proxy = proxy_string
|
491
|
+
if tzone is None and "timezone" not in kwargs and "--timezone" in arg_join:
|
492
|
+
tz_string = None
|
493
|
+
if "--timezone=" in arg_join:
|
494
|
+
tz_string = arg_join.split("--timezone=")[1].split(" ")[0]
|
495
|
+
elif "--timezone " in arg_join:
|
496
|
+
tz_string = arg_join.split("--timezone ")[1].split(" ")[0]
|
497
|
+
if tz_string:
|
498
|
+
if tz_string.startswith('"') and tz_string.endswith('"'):
|
499
|
+
tz_string = proxy_string[1:-1]
|
500
|
+
elif tz_string.startswith("'") and tz_string.endswith("'"):
|
501
|
+
tz_string = proxy_string[1:-1]
|
502
|
+
tzone = tz_string
|
503
|
+
platform_var = None
|
504
|
+
if (
|
505
|
+
"platform" not in kwargs
|
506
|
+
and "plat" not in kwargs
|
507
|
+
and "--platform" in arg_join
|
508
|
+
):
|
509
|
+
count = 0
|
510
|
+
for arg in sys_argv:
|
511
|
+
if arg.startswith("--platform="):
|
512
|
+
platform_var = arg.split("--platform=")[1]
|
513
|
+
break
|
514
|
+
elif arg == "--platform" and len(sys_argv) > count + 1:
|
515
|
+
platform_var = sys_argv[count + 1]
|
516
|
+
if platform_var.startswith("-"):
|
517
|
+
platform_var = None
|
518
|
+
break
|
519
|
+
count += 1
|
520
|
+
if platform_var:
|
521
|
+
if platform_var.startswith('"') and platform_var.endswith('"'):
|
522
|
+
platform_var = platform_var[1:-1]
|
523
|
+
elif platform_var.startswith("'") and platform_var.endswith("'"):
|
524
|
+
platform_var = platform_var[1:-1]
|
321
525
|
if IS_LINUX and not headless and not headed and not xvfb:
|
322
526
|
xvfb = True # The default setting on Linux
|
323
527
|
__activate_virtual_display_as_needed(headless, headed, xvfb, xvfb_metrics)
|
@@ -404,6 +608,8 @@ async def start(
|
|
404
608
|
sb_config._cdp_platform = kwargs["platform"]
|
405
609
|
elif "plat" in kwargs:
|
406
610
|
sb_config._cdp_platform = kwargs["plat"]
|
611
|
+
elif platform_var:
|
612
|
+
sb_config._cdp_platform = platform_var
|
407
613
|
else:
|
408
614
|
sb_config._cdp_platform = None
|
409
615
|
return driver
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: seleniumbase
|
3
|
-
Version: 4.42.
|
3
|
+
Version: 4.42.3
|
4
4
|
Summary: A complete web automation framework for end-to-end testing.
|
5
5
|
Home-page: https://github.com/seleniumbase/SeleniumBase
|
6
6
|
Author: Michael Mintz
|
@@ -28,7 +28,6 @@ Classifier: Environment :: Web Environment
|
|
28
28
|
Classifier: Framework :: Pytest
|
29
29
|
Classifier: Intended Audience :: Developers
|
30
30
|
Classifier: Intended Audience :: Information Technology
|
31
|
-
Classifier: License :: OSI Approved :: MIT License
|
32
31
|
Classifier: Operating System :: MacOS :: MacOS X
|
33
32
|
Classifier: Operating System :: Microsoft :: Windows
|
34
33
|
Classifier: Operating System :: POSIX :: Linux
|
@@ -66,7 +65,8 @@ Requires-Dist: packaging>=25.0
|
|
66
65
|
Requires-Dist: setuptools~=70.2; python_version < "3.10"
|
67
66
|
Requires-Dist: setuptools>=80.9.0; python_version >= "3.10"
|
68
67
|
Requires-Dist: wheel>=0.45.1
|
69
|
-
Requires-Dist: attrs
|
68
|
+
Requires-Dist: attrs~=25.3.0; python_version < "3.9"
|
69
|
+
Requires-Dist: attrs>=25.4.0; python_version >= "3.9"
|
70
70
|
Requires-Dist: certifi>=2025.10.5
|
71
71
|
Requires-Dist: exceptiongroup>=1.3.0
|
72
72
|
Requires-Dist: websockets~=13.1; python_version < "3.9"
|
@@ -107,7 +107,8 @@ Requires-Dist: trio==0.27.0; python_version < "3.9"
|
|
107
107
|
Requires-Dist: trio<1,>=0.31.0; python_version >= "3.9"
|
108
108
|
Requires-Dist: trio-websocket~=0.12.2
|
109
109
|
Requires-Dist: wsproto==1.2.0
|
110
|
-
Requires-Dist: websocket-client~=1.8.0
|
110
|
+
Requires-Dist: websocket-client~=1.8.0; python_version < "3.9"
|
111
|
+
Requires-Dist: websocket-client~=1.9.0; python_version >= "3.9"
|
111
112
|
Requires-Dist: selenium==4.27.1; python_version < "3.9"
|
112
113
|
Requires-Dist: selenium==4.32.0; python_version >= "3.9" and python_version < "3.10"
|
113
114
|
Requires-Dist: selenium==4.36.0; python_version >= "3.10"
|
@@ -134,6 +135,7 @@ Requires-Dist: soupsieve~=2.8; python_version >= "3.9"
|
|
134
135
|
Requires-Dist: beautifulsoup4~=4.14.2
|
135
136
|
Requires-Dist: pyotp==2.9.0
|
136
137
|
Requires-Dist: python-xlib==0.33; platform_system == "Linux"
|
138
|
+
Requires-Dist: PyAutoGUI>=0.9.54; platform_system == "Linux"
|
137
139
|
Requires-Dist: markdown-it-py==3.0.0; python_version < "3.10"
|
138
140
|
Requires-Dist: markdown-it-py==4.0.0; python_version >= "3.10"
|
139
141
|
Requires-Dist: mdurl==0.1.2
|
@@ -180,12 +182,12 @@ Requires-Dist: proxy.py==2.4.3; extra == "proxy"
|
|
180
182
|
Provides-Extra: psutil
|
181
183
|
Requires-Dist: psutil==7.1.0; extra == "psutil"
|
182
184
|
Provides-Extra: pyautogui
|
183
|
-
Requires-Dist: PyAutoGUI
|
185
|
+
Requires-Dist: PyAutoGUI>=0.9.54; platform_system != "Linux" and extra == "pyautogui"
|
184
186
|
Provides-Extra: selenium-stealth
|
185
187
|
Requires-Dist: selenium-stealth==1.0.6; extra == "selenium-stealth"
|
186
188
|
Provides-Extra: selenium-wire
|
187
189
|
Requires-Dist: selenium-wire==5.1.0; extra == "selenium-wire"
|
188
|
-
Requires-Dist: pyOpenSSL
|
190
|
+
Requires-Dist: pyOpenSSL>=24.2.1; extra == "selenium-wire"
|
189
191
|
Requires-Dist: pyparsing>=3.1.4; extra == "selenium-wire"
|
190
192
|
Requires-Dist: Brotli==1.1.0; extra == "selenium-wire"
|
191
193
|
Requires-Dist: blinker==1.7.0; extra == "selenium-wire"
|
@@ -194,7 +196,7 @@ Requires-Dist: hpack==4.0.0; extra == "selenium-wire"
|
|
194
196
|
Requires-Dist: hyperframe==6.0.1; extra == "selenium-wire"
|
195
197
|
Requires-Dist: kaitaistruct==0.10; extra == "selenium-wire"
|
196
198
|
Requires-Dist: pyasn1==0.6.1; extra == "selenium-wire"
|
197
|
-
Requires-Dist: zstandard
|
199
|
+
Requires-Dist: zstandard>=0.23.0; extra == "selenium-wire"
|
198
200
|
Dynamic: author
|
199
201
|
Dynamic: author-email
|
200
202
|
Dynamic: classifier
|
@@ -314,7 +316,7 @@ with SB(uc=True, test=True, locale="en") as sb:
|
|
314
316
|
sb.assert_text("Username", '[for="user_login"]', timeout=3)
|
315
317
|
sb.assert_element('label[for="user_login"]')
|
316
318
|
sb.highlight('button:contains("Sign in")')
|
317
|
-
sb.highlight('h1:contains("GitLab
|
319
|
+
sb.highlight('h1:contains("GitLab")')
|
318
320
|
sb.post_message("SeleniumBase wasn't detected", duration=4)
|
319
321
|
```
|
320
322
|
|
@@ -329,13 +331,11 @@ url = "https://gitlab.com/users/sign_in"
|
|
329
331
|
sb = sb_cdp.Chrome(url)
|
330
332
|
sb.sleep(2.5)
|
331
333
|
sb.gui_click_captcha()
|
332
|
-
sb.highlight('h1:contains("GitLab
|
334
|
+
sb.highlight('h1:contains("GitLab")')
|
333
335
|
sb.highlight('button:contains("Sign in")')
|
334
336
|
sb.driver.stop()
|
335
337
|
```
|
336
338
|
|
337
|
-
> (Due to changes in Chrome 137 where the `--load-extension` switch was removed, you can't load extensions directly from this format.)
|
338
|
-
|
339
339
|
--------
|
340
340
|
|
341
341
|
<p align="left">📗 Here's <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_get_swag.py">SeleniumBase/examples/test_get_swag.py</a>, which tests an e-commerce site:</p>
|
@@ -3,7 +3,7 @@ sbase/__main__.py,sha256=G0bVB1-DM4PGwQ1KyOupaWCs4ePbChZNNWuX2htim5U,647
|
|
3
3
|
sbase/steps.py,sha256=wiPSWZhFpBlWvkqXRJ18btpBu3nQaw9K5AzIJNAX5RM,43521
|
4
4
|
seleniumbase/__init__.py,sha256=JFEY9P5QJqsa1M6ghzLMH2eIPQyh85iglCaQwg8Y8z4,2498
|
5
5
|
seleniumbase/__main__.py,sha256=dn1p6dgCchmcH1zzTzzQvFwwdQQqnTGH6ULV9m4hv24,654
|
6
|
-
seleniumbase/__version__.py,sha256=
|
6
|
+
seleniumbase/__version__.py,sha256=NxDRTsK1en5AnzYTB768P_f5d5U9tzqllrffTcSIHfY,46
|
7
7
|
seleniumbase/behave/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
seleniumbase/behave/behave_helper.py,sha256=lJtagtivSbEpbRj0EKV4l4PuPU6NANONJJAnYwgVCe0,24379
|
9
9
|
seleniumbase/behave/behave_sb.py,sha256=guLihFsr1uJ4v2AR3r3Vy_BTeHrHwy2JEJxhp-MVnZA,59872
|
@@ -36,7 +36,7 @@ seleniumbase/console_scripts/sb_print.py,sha256=tNy-bMDgwHJO3bZxMpmo9weSE8uhbH0C
|
|
36
36
|
seleniumbase/console_scripts/sb_recorder.py,sha256=DH-n2fN7N9qyHMl7wjtn8MiliBgfw-1kwgmfg1GUuhk,10772
|
37
37
|
seleniumbase/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
38
|
seleniumbase/core/application_manager.py,sha256=e_0sjtI8cjY5BNyZj1QBR0j6_oCScxGmSXYEpcYwuZE,576
|
39
|
-
seleniumbase/core/browser_launcher.py,sha256=
|
39
|
+
seleniumbase/core/browser_launcher.py,sha256=6o5oVuploZ-DSU3edaroFR98uqxIYyiRRP9DB-Y7xR0,250719
|
40
40
|
seleniumbase/core/capabilities_parser.py,sha256=meIS2uHapTCq2ldfNAToC7r0cKmZDRXuYNKExM1GHDY,6038
|
41
41
|
seleniumbase/core/colored_traceback.py,sha256=DrRWfg7XEnKcgY59Xj7Jdk09H-XqHYBSUpB-DiZt6iY,2020
|
42
42
|
seleniumbase/core/create_db_tables.sql,sha256=VWPtrdiW_HQ6yETHjqTu-VIrTwvd8I8o1NfBeaVSHpU,972
|
@@ -50,7 +50,7 @@ seleniumbase/core/proxy_helper.py,sha256=pZ1NboNfziHU3vWZLOZLX-qkfM3oKSnpc3omQf9
|
|
50
50
|
seleniumbase/core/recorder_helper.py,sha256=gDION28OK4NAYsE-7ohKZ9Z3sxQQ0FEjf859LDpqsg4,25320
|
51
51
|
seleniumbase/core/report_helper.py,sha256=AIl6Qava2yW1uSzbLpJBlPlYDz0KE-rVhogh8hsGWBo,12201
|
52
52
|
seleniumbase/core/s3_manager.py,sha256=z_4qx2jI_gtK5r3niGXgEOBpfMUicUCOciowai50MP4,3529
|
53
|
-
seleniumbase/core/sb_cdp.py,sha256=
|
53
|
+
seleniumbase/core/sb_cdp.py,sha256=E_7sbQjyqDAO38IiGYSSB57xrTqxAi8nnqUEWIAXYt8,104551
|
54
54
|
seleniumbase/core/sb_driver.py,sha256=-IQsskc7HpXQbcBL04IPjmGpyYchyo7v9OPF2WcahDw,14159
|
55
55
|
seleniumbase/core/session_helper.py,sha256=s9zD3PVZEWVzG2h81cCUskbNWLfdjC_LwwQjKptHCak,558
|
56
56
|
seleniumbase/core/settings_parser.py,sha256=gqVohHVlE_5L5Cqe2L24uYrRzvoK-saX8E_Df7_-_3I,7609
|
@@ -67,14 +67,14 @@ seleniumbase/extensions/disable_csp.zip,sha256=5RvomXnm2PdivUVcxTV6jfvD8WhTEsQYH
|
|
67
67
|
seleniumbase/extensions/recorder.zip,sha256=JEE_FVEvlS63cFQbVLEroIyPSS91nWCDL0MhjVrmIpk,11813
|
68
68
|
seleniumbase/extensions/sbase_ext.zip,sha256=3s1N8zrVaMz8RQEOIoBzC3KDjtmHwVZRvVsX25Odr_s,8175
|
69
69
|
seleniumbase/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
70
|
-
seleniumbase/fixtures/base_case.py,sha256=
|
70
|
+
seleniumbase/fixtures/base_case.py,sha256=wHXUqjQmALFdWTk5Lf7GX0zWLji3FqzjhnJ91RYrOII,741117
|
71
71
|
seleniumbase/fixtures/constants.py,sha256=WMrItuNyKq3XVJ64NluLIRc4gJCxDw8K8qXED0b9S2w,13752
|
72
72
|
seleniumbase/fixtures/css_to_xpath.py,sha256=9ouDB1xl4MJ2os6JOgTIAyHKOQfuxtxvXC3O5hSnEKA,1954
|
73
73
|
seleniumbase/fixtures/errors.py,sha256=KyxuEVx_e3MPhVrJfNIa_3ltMpbCFxfy_jxK8RFNTns,555
|
74
74
|
seleniumbase/fixtures/js_utils.py,sha256=Ykt019DFP8S27o8Kai_ihT01qQWo3RqHUeljqOEUdFU,52247
|
75
|
-
seleniumbase/fixtures/page_actions.py,sha256=
|
75
|
+
seleniumbase/fixtures/page_actions.py,sha256=bkCo3s-t2hglV_SYTPPt849R9l0h1gKKboTmZar0n9Y,73213
|
76
76
|
seleniumbase/fixtures/page_utils.py,sha256=H1iV8f9vDyEy87DBntyiBXC_tg8HskcebUOAJVn0hxE,12160
|
77
|
-
seleniumbase/fixtures/shared_utils.py,sha256=
|
77
|
+
seleniumbase/fixtures/shared_utils.py,sha256=LFXGGdvXhNZCShsRxyuEOcO2dcEbSkMSW5seQR2oE08,9950
|
78
78
|
seleniumbase/fixtures/unittest_helper.py,sha256=sfZ92rZeBAn_sF_yQ3I6_I7h3lyU5-cV_UMegBNoEm8,1294
|
79
79
|
seleniumbase/fixtures/words.py,sha256=FOA4mAYvl3EPVpBTvgvK6YwCL8BdlRCmed685kEe7Vg,7827
|
80
80
|
seleniumbase/fixtures/xpath_to_css.py,sha256=lML56k656fElXJ4NJF07r35FjctrbgQkXUotNk7A-as,8876
|
@@ -117,8 +117,8 @@ seleniumbase/undetected/reactor.py,sha256=NropaXcO54pzmDq6quR27qPJxab6636H7LRAaq
|
|
117
117
|
seleniumbase/undetected/webelement.py,sha256=OOpUYbEiOG52KsYYyuDW9tYLdA2SMnukvwQHUdPVn9E,1389
|
118
118
|
seleniumbase/undetected/cdp_driver/__init__.py,sha256=Ga9alwuaZZy4_XOShc0HjgFnNqpPdrcbjAicz5gE7a4,215
|
119
119
|
seleniumbase/undetected/cdp_driver/_contradict.py,sha256=lP4b0h5quAy573ETn_TBbYV889cL1AuPLVInpJ0ZkiU,3183
|
120
|
-
seleniumbase/undetected/cdp_driver/browser.py,sha256=
|
121
|
-
seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=
|
120
|
+
seleniumbase/undetected/cdp_driver/browser.py,sha256=JQlwMuwZgK0vWlvH4SU6DA7LcZo2I4hJRUIcv4gbZCQ,35609
|
121
|
+
seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=83NOk962PCiERr6YQ_QjTS5yS8Uwcnsyaw9BdgnGoAQ,33481
|
122
122
|
seleniumbase/undetected/cdp_driver/config.py,sha256=pjcGq0azNQpNb3XT2sTjxShLEmcIPdzTzB_mitu6GkM,13557
|
123
123
|
seleniumbase/undetected/cdp_driver/connection.py,sha256=WgZ4QamXSdTzP4Xfgkn8mxv-JFivMG6hqbyHy2_LQlg,25717
|
124
124
|
seleniumbase/undetected/cdp_driver/element.py,sha256=FIC6v7OmumLCT-_vIc3H4oju_oBbaLpWJUJIKm2c_q4,40467
|
@@ -137,9 +137,9 @@ seleniumbase/utilities/selenium_grid/start-grid-hub.bat,sha256=Ftq-GrAKRYH2ssDPr
|
|
137
137
|
seleniumbase/utilities/selenium_grid/start-grid-hub.sh,sha256=KADv0RUHONLL2_I443QFK8PryBpDmKn5Gy0s4o0vDSM,106
|
138
138
|
seleniumbase/utilities/selenium_ide/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
139
139
|
seleniumbase/utilities/selenium_ide/convert_ide.py,sha256=pZFnqEJQEKZPyNFjkLD29s2HPQgCrWW9XJWpCPhWOoM,31691
|
140
|
-
seleniumbase-4.42.
|
141
|
-
seleniumbase-4.42.
|
142
|
-
seleniumbase-4.42.
|
143
|
-
seleniumbase-4.42.
|
144
|
-
seleniumbase-4.42.
|
145
|
-
seleniumbase-4.42.
|
140
|
+
seleniumbase-4.42.3.dist-info/licenses/LICENSE,sha256=BRblZsX7HyPUjQmYTiyWr_e9tzWvmR3R4SFclM2R3W0,1085
|
141
|
+
seleniumbase-4.42.3.dist-info/METADATA,sha256=spZZe0MvPKyG3c0xO1JF1Dp_dKNSthItrviEXMnMmss,89304
|
142
|
+
seleniumbase-4.42.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
143
|
+
seleniumbase-4.42.3.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
|
144
|
+
seleniumbase-4.42.3.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
|
145
|
+
seleniumbase-4.42.3.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|