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
|
@@ -4,9 +4,15 @@ import base64
|
|
|
4
4
|
import datetime
|
|
5
5
|
import logging
|
|
6
6
|
import pathlib
|
|
7
|
+
import re
|
|
7
8
|
import urllib.parse
|
|
8
9
|
import warnings
|
|
10
|
+
from contextlib import suppress
|
|
11
|
+
from filelock import FileLock
|
|
9
12
|
from seleniumbase import config as sb_config
|
|
13
|
+
from seleniumbase.fixtures import constants
|
|
14
|
+
from seleniumbase.fixtures import js_utils
|
|
15
|
+
from seleniumbase.fixtures import shared_utils
|
|
10
16
|
from typing import Dict, List, Union, Optional, Tuple
|
|
11
17
|
from . import browser as cdp_browser
|
|
12
18
|
from . import element
|
|
@@ -244,23 +250,7 @@ class Tab(Connection):
|
|
|
244
250
|
Raise timeout exception when after this many seconds nothing is found.
|
|
245
251
|
:type timeout: float,int
|
|
246
252
|
"""
|
|
247
|
-
|
|
248
|
-
start_time = loop.time()
|
|
249
|
-
selector = selector.strip()
|
|
250
|
-
item = None
|
|
251
|
-
try:
|
|
252
|
-
item = await self.query_selector(selector)
|
|
253
|
-
except (Exception, TypeError):
|
|
254
|
-
pass
|
|
255
|
-
while not item:
|
|
256
|
-
await self
|
|
257
|
-
item = await self.query_selector(selector)
|
|
258
|
-
if loop.time() - start_time > timeout:
|
|
259
|
-
raise asyncio.TimeoutError(
|
|
260
|
-
"Time ran out while waiting for: {%s}" % selector
|
|
261
|
-
)
|
|
262
|
-
await self.sleep(0.5)
|
|
263
|
-
return item
|
|
253
|
+
return await self.wait_for(selector=selector, timeout=timeout)
|
|
264
254
|
|
|
265
255
|
async def find_all(
|
|
266
256
|
self,
|
|
@@ -358,13 +348,19 @@ class Tab(Connection):
|
|
|
358
348
|
if new_window and not new_tab:
|
|
359
349
|
new_tab = True
|
|
360
350
|
if new_tab:
|
|
361
|
-
if
|
|
351
|
+
if (
|
|
352
|
+
getattr(sb_config, "incognito", None)
|
|
353
|
+
or (
|
|
354
|
+
getattr(sb_config, "_cdp_browser", None)
|
|
355
|
+
in ["comet", "atlas"]
|
|
356
|
+
)
|
|
357
|
+
):
|
|
362
358
|
return await self.browser.get(
|
|
363
359
|
url, new_tab=False, new_window=True, **kwargs
|
|
364
360
|
)
|
|
365
361
|
else:
|
|
366
362
|
return await self.browser.get(
|
|
367
|
-
url, new_tab, new_window, **kwargs
|
|
363
|
+
url, new_tab=True, new_window=False, **kwargs
|
|
368
364
|
)
|
|
369
365
|
else:
|
|
370
366
|
if not kwargs:
|
|
@@ -375,9 +371,12 @@ class Tab(Connection):
|
|
|
375
371
|
return self
|
|
376
372
|
else:
|
|
377
373
|
return await self.browser.get(
|
|
378
|
-
url, new_tab, new_window, **kwargs
|
|
374
|
+
url, new_tab=False, new_window=False, **kwargs
|
|
379
375
|
)
|
|
380
376
|
|
|
377
|
+
async def open(self, url="about:blank"):
|
|
378
|
+
return await self.get(url=url)
|
|
379
|
+
|
|
381
380
|
async def query_selector_all(
|
|
382
381
|
self,
|
|
383
382
|
selector: str,
|
|
@@ -705,10 +704,12 @@ class Tab(Connection):
|
|
|
705
704
|
raise ProtocolException(errors)
|
|
706
705
|
if remote_object:
|
|
707
706
|
if return_by_value:
|
|
708
|
-
if remote_object.value:
|
|
707
|
+
if remote_object.value is not None:
|
|
709
708
|
return remote_object.value
|
|
710
709
|
else:
|
|
711
|
-
|
|
710
|
+
if remote_object.deep_serialized_value is not None:
|
|
711
|
+
return remote_object.deep_serialized_value.value
|
|
712
|
+
return None
|
|
712
713
|
|
|
713
714
|
async def js_dumps(
|
|
714
715
|
self, obj_name: str, return_by_value: Optional[bool] = True
|
|
@@ -899,7 +900,10 @@ class Tab(Connection):
|
|
|
899
900
|
"""Gets the current page source content (html)"""
|
|
900
901
|
doc: cdp.dom.Node = await self.send(cdp.dom.get_document(-1, True))
|
|
901
902
|
return await self.send(
|
|
902
|
-
cdp.dom.get_outer_html(
|
|
903
|
+
cdp.dom.get_outer_html(
|
|
904
|
+
backend_node_id=doc.backend_node_id,
|
|
905
|
+
include_shadow_dom=True,
|
|
906
|
+
)
|
|
903
907
|
)
|
|
904
908
|
|
|
905
909
|
async def maximize(self):
|
|
@@ -1075,7 +1079,7 @@ class Tab(Connection):
|
|
|
1075
1079
|
raise asyncio.TimeoutError(
|
|
1076
1080
|
"Time ran out while waiting for: {%s}" % selector
|
|
1077
1081
|
)
|
|
1078
|
-
await self.sleep(0.
|
|
1082
|
+
await self.sleep(0.068)
|
|
1079
1083
|
return item
|
|
1080
1084
|
if text:
|
|
1081
1085
|
item = await self.find_element_by_text(text)
|
|
@@ -1085,9 +1089,42 @@ class Tab(Connection):
|
|
|
1085
1089
|
raise asyncio.TimeoutError(
|
|
1086
1090
|
"Time ran out while waiting for: {%s}" % text
|
|
1087
1091
|
)
|
|
1088
|
-
await self.sleep(0.
|
|
1092
|
+
await self.sleep(0.068)
|
|
1089
1093
|
return item
|
|
1090
1094
|
|
|
1095
|
+
async def set_attributes(self, selector, attribute, value):
|
|
1096
|
+
"""This method uses JavaScript to set/update a common attribute.
|
|
1097
|
+
All matching selectors from querySelectorAll() are used.
|
|
1098
|
+
Example => (Make all links on a website redirect to Google):
|
|
1099
|
+
self.set_attributes("a", "href", "https://google.com")"""
|
|
1100
|
+
attribute = re.escape(attribute)
|
|
1101
|
+
attribute = js_utils.escape_quotes_if_needed(attribute)
|
|
1102
|
+
value = re.escape(value)
|
|
1103
|
+
value = js_utils.escape_quotes_if_needed(value)
|
|
1104
|
+
if selector.startswith(("/", "./", "(")):
|
|
1105
|
+
with suppress(Exception):
|
|
1106
|
+
selector = js_utils.convert_to_css_selector(selector, "xpath")
|
|
1107
|
+
css_selector = selector
|
|
1108
|
+
css_selector = re.escape(css_selector) # Add "\\" to special chars
|
|
1109
|
+
css_selector = js_utils.escape_quotes_if_needed(css_selector)
|
|
1110
|
+
js_code = (
|
|
1111
|
+
"""var $elements = document.querySelectorAll('%s');
|
|
1112
|
+
var index = 0, length = $elements.length;
|
|
1113
|
+
for(; index < length; index++){
|
|
1114
|
+
$elements[index].setAttribute('%s','%s');}""" % (
|
|
1115
|
+
css_selector,
|
|
1116
|
+
attribute,
|
|
1117
|
+
value,
|
|
1118
|
+
)
|
|
1119
|
+
)
|
|
1120
|
+
with suppress(Exception):
|
|
1121
|
+
await self.evaluate(js_code)
|
|
1122
|
+
|
|
1123
|
+
async def internalize_links(self):
|
|
1124
|
+
"""All `target="_blank"` links become `target="_self"`.
|
|
1125
|
+
This prevents those links from opening in a new tab."""
|
|
1126
|
+
await self.set_attributes('[target="_blank"]', "target", "_self")
|
|
1127
|
+
|
|
1091
1128
|
async def download_file(
|
|
1092
1129
|
self, url: str, filename: Optional[PathLike] = None
|
|
1093
1130
|
):
|
|
@@ -1287,19 +1324,355 @@ class Tab(Connection):
|
|
|
1287
1324
|
res.append(abs_url)
|
|
1288
1325
|
return res
|
|
1289
1326
|
|
|
1290
|
-
async def
|
|
1291
|
-
""
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1327
|
+
async def get_html(self):
|
|
1328
|
+
element = await self.find("html", timeout=1)
|
|
1329
|
+
return await element.get_html_async()
|
|
1330
|
+
|
|
1331
|
+
async def get_page_source(self):
|
|
1332
|
+
return await self.get_html()
|
|
1333
|
+
|
|
1334
|
+
async def is_element_present(self, selector):
|
|
1335
|
+
try:
|
|
1336
|
+
await self.select(selector, timeout=0.01)
|
|
1337
|
+
return True
|
|
1338
|
+
except Exception:
|
|
1339
|
+
return False
|
|
1340
|
+
|
|
1341
|
+
async def is_element_visible(self, selector):
|
|
1342
|
+
try:
|
|
1343
|
+
element = await self.select(selector, timeout=0.01)
|
|
1344
|
+
except Exception:
|
|
1345
|
+
return False
|
|
1346
|
+
if not element:
|
|
1347
|
+
return False
|
|
1348
|
+
try:
|
|
1349
|
+
position = await element.get_position_async()
|
|
1350
|
+
return (position.width != 0 or position.height != 0)
|
|
1351
|
+
except Exception:
|
|
1352
|
+
return False
|
|
1353
|
+
|
|
1354
|
+
async def __on_a_cf_turnstile_page(self, source=None):
|
|
1355
|
+
if not source or len(source) < 400:
|
|
1356
|
+
await self.sleep(0.22)
|
|
1357
|
+
source = await self.get_html()
|
|
1358
|
+
if (
|
|
1359
|
+
(
|
|
1360
|
+
'data-callback="onCaptchaSuccess"' in source
|
|
1361
|
+
and 'title="reCAPTCHA"' not in source
|
|
1362
|
+
and 'id="recaptcha-token"' not in source
|
|
1363
|
+
)
|
|
1364
|
+
or "/challenge-platform/h/b/" in source
|
|
1365
|
+
or 'id="challenge-widget-' in source
|
|
1366
|
+
or "challenges.cloudf" in source
|
|
1367
|
+
or "cf-turnstile-" in source
|
|
1368
|
+
):
|
|
1369
|
+
return True
|
|
1370
|
+
return False
|
|
1371
|
+
|
|
1372
|
+
async def __on_a_g_recaptcha_page(self, *args, **kwargs):
|
|
1373
|
+
await self.sleep(0.4) # reCAPTCHA may need a moment to appear
|
|
1374
|
+
source = await self.get_html()
|
|
1375
|
+
if (
|
|
1376
|
+
(
|
|
1377
|
+
'id="recaptcha-token"' in source
|
|
1378
|
+
or 'title="reCAPTCHA"' in source
|
|
1379
|
+
)
|
|
1380
|
+
and await self.is_element_present('iframe[title="reCAPTCHA"]')
|
|
1381
|
+
):
|
|
1382
|
+
await self.sleep(0.1)
|
|
1383
|
+
return True
|
|
1384
|
+
elif "com/recaptcha/api.js" in source:
|
|
1385
|
+
await self.sleep(1.6) # Still loading
|
|
1386
|
+
return True
|
|
1387
|
+
return False
|
|
1388
|
+
|
|
1389
|
+
async def __gui_click_recaptcha(self):
|
|
1390
|
+
selector = None
|
|
1391
|
+
if await self.is_element_present('iframe[title="reCAPTCHA"]'):
|
|
1392
|
+
selector = 'iframe[title="reCAPTCHA"]'
|
|
1393
|
+
else:
|
|
1394
|
+
return
|
|
1395
|
+
await self.sleep(0.5)
|
|
1396
|
+
with suppress(Exception):
|
|
1397
|
+
element_rect = await self.get_gui_element_rect(selector, timeout=1)
|
|
1398
|
+
e_x = element_rect["x"]
|
|
1399
|
+
e_y = element_rect["y"]
|
|
1400
|
+
x_offset = 26
|
|
1401
|
+
y_offset = 35
|
|
1402
|
+
if await asyncio.to_thread(shared_utils.is_windows):
|
|
1403
|
+
x_offset = 29
|
|
1404
|
+
x = e_x + x_offset
|
|
1405
|
+
y = e_y + y_offset
|
|
1406
|
+
sb_config._saved_cf_x_y = (x, y) # For debugging later
|
|
1407
|
+
await self.sleep(0.11)
|
|
1408
|
+
gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
|
|
1409
|
+
with await asyncio.to_thread(gui_lock.acquire):
|
|
1410
|
+
await self.bring_to_front()
|
|
1411
|
+
await self.sleep(0.05)
|
|
1412
|
+
await self.click_with_offset(
|
|
1413
|
+
selector, x_offset, y_offset, timeout=1
|
|
1414
|
+
)
|
|
1415
|
+
await self.sleep(0.22)
|
|
1416
|
+
|
|
1417
|
+
async def get_element_rect(self, selector, timeout=5):
|
|
1418
|
+
element = await self.select(selector, timeout=timeout)
|
|
1419
|
+
coordinates = None
|
|
1420
|
+
if ":contains(" in selector:
|
|
1421
|
+
position = await element.get_position_async()
|
|
1422
|
+
x = position.x
|
|
1423
|
+
y = position.y
|
|
1424
|
+
width = position.width
|
|
1425
|
+
height = position.height
|
|
1426
|
+
coordinates = {"x": x, "y": y, "width": width, "height": height}
|
|
1427
|
+
else:
|
|
1428
|
+
coordinates = await self.js_dumps(
|
|
1429
|
+
"""document.querySelector('%s').getBoundingClientRect()"""
|
|
1430
|
+
% js_utils.escape_quotes_if_needed(re.escape(selector))
|
|
1431
|
+
)
|
|
1432
|
+
return coordinates
|
|
1433
|
+
|
|
1434
|
+
async def get_window_rect(self):
|
|
1435
|
+
coordinates = {}
|
|
1436
|
+
innerWidth = await self.evaluate("window.innerWidth")
|
|
1437
|
+
innerHeight = await self.evaluate("window.innerHeight")
|
|
1438
|
+
outerWidth = await self.evaluate("window.outerWidth")
|
|
1439
|
+
outerHeight = await self.evaluate("window.outerHeight")
|
|
1440
|
+
pageXOffset = await self.evaluate("window.pageXOffset")
|
|
1441
|
+
pageYOffset = await self.evaluate("window.pageYOffset")
|
|
1442
|
+
scrollX = await self.evaluate("window.scrollX")
|
|
1443
|
+
scrollY = await self.evaluate("window.scrollY")
|
|
1444
|
+
screenLeft = await self.evaluate("window.screenLeft")
|
|
1445
|
+
screenTop = await self.evaluate("window.screenTop")
|
|
1446
|
+
x = await self.evaluate("window.screenX")
|
|
1447
|
+
y = await self.evaluate("window.screenY")
|
|
1448
|
+
coordinates["innerWidth"] = innerWidth
|
|
1449
|
+
coordinates["innerHeight"] = innerHeight
|
|
1450
|
+
coordinates["outerWidth"] = outerWidth
|
|
1451
|
+
coordinates["outerHeight"] = outerHeight
|
|
1452
|
+
coordinates["width"] = outerWidth
|
|
1453
|
+
coordinates["height"] = outerHeight
|
|
1454
|
+
coordinates["pageXOffset"] = pageXOffset if pageXOffset else 0
|
|
1455
|
+
coordinates["pageYOffset"] = pageYOffset if pageYOffset else 0
|
|
1456
|
+
coordinates["scrollX"] = scrollX if scrollX else 0
|
|
1457
|
+
coordinates["scrollY"] = scrollY if scrollY else 0
|
|
1458
|
+
coordinates["screenLeft"] = screenLeft if screenLeft else 0
|
|
1459
|
+
coordinates["screenTop"] = screenTop if screenTop else 0
|
|
1460
|
+
coordinates["x"] = x if x else 0
|
|
1461
|
+
coordinates["y"] = y if y else 0
|
|
1462
|
+
return coordinates
|
|
1463
|
+
|
|
1464
|
+
async def get_gui_element_rect(self, selector, timeout=5):
|
|
1465
|
+
"""(Coordinates are relative to the screen. Not the window.)"""
|
|
1466
|
+
element_rect = await self.get_element_rect(selector, timeout=timeout)
|
|
1467
|
+
e_width = element_rect["width"]
|
|
1468
|
+
e_height = element_rect["height"]
|
|
1469
|
+
window_rect = await self.get_window_rect()
|
|
1470
|
+
w_bottom_y = window_rect["y"] + window_rect["height"]
|
|
1471
|
+
viewport_height = window_rect["innerHeight"]
|
|
1472
|
+
x = window_rect["x"] + element_rect["x"]
|
|
1473
|
+
y = w_bottom_y - viewport_height + element_rect["y"]
|
|
1474
|
+
y_scroll_offset = window_rect["pageYOffset"]
|
|
1475
|
+
if (
|
|
1476
|
+
hasattr(sb_config, "_cdp_browser")
|
|
1477
|
+
and sb_config._cdp_browser == "opera"
|
|
1478
|
+
):
|
|
1479
|
+
# Handle special case where Opera side panel shifts coordinates
|
|
1480
|
+
x_offset = window_rect["outerWidth"] - window_rect["innerWidth"]
|
|
1481
|
+
if x_offset > 56:
|
|
1482
|
+
x_offset = 56
|
|
1483
|
+
elif x_offset < 22:
|
|
1484
|
+
x_offset = 0
|
|
1485
|
+
x = x + x_offset
|
|
1486
|
+
y = y - y_scroll_offset
|
|
1487
|
+
x = x + window_rect["scrollX"]
|
|
1488
|
+
y = y + window_rect["scrollY"]
|
|
1489
|
+
return ({"height": e_height, "width": e_width, "x": x, "y": y})
|
|
1490
|
+
|
|
1491
|
+
async def get_title(self):
|
|
1492
|
+
return await self.evaluate("document.title")
|
|
1493
|
+
|
|
1494
|
+
async def get_current_url(self):
|
|
1495
|
+
return await self.evaluate("window.location.href")
|
|
1496
|
+
|
|
1497
|
+
async def send_keys(self, selector, text, timeout=5):
|
|
1498
|
+
element = await self.find(selector, timeout=timeout)
|
|
1499
|
+
await element.send_keys_async(text)
|
|
1500
|
+
|
|
1501
|
+
async def type(self, selector, text, timeout=5):
|
|
1502
|
+
await self.send_keys(selector, text, timeout=timeout)
|
|
1503
|
+
|
|
1504
|
+
async def click(self, selector, timeout=5):
|
|
1505
|
+
element = await self.find(selector, timeout=timeout)
|
|
1506
|
+
await element.click_async()
|
|
1507
|
+
|
|
1508
|
+
async def click_with_offset(self, selector, x, y, center=False, timeout=5):
|
|
1509
|
+
element = await self.find(selector, timeout=timeout)
|
|
1510
|
+
await element.scroll_into_view_async()
|
|
1511
|
+
await element.mouse_click_with_offset_async(x=x, y=y, center=center)
|
|
1512
|
+
|
|
1513
|
+
async def solve_captcha(self):
|
|
1514
|
+
await self.sleep(0.11)
|
|
1515
|
+
source = await self.get_html()
|
|
1516
|
+
if await self.__on_a_cf_turnstile_page(source):
|
|
1517
|
+
pass
|
|
1518
|
+
elif await self.__on_a_g_recaptcha_page(source):
|
|
1519
|
+
await self.__gui_click_recaptcha()
|
|
1520
|
+
return
|
|
1521
|
+
else:
|
|
1522
|
+
return
|
|
1523
|
+
selector = None
|
|
1524
|
+
if await self.is_element_present('[class="cf-turnstile"]'):
|
|
1525
|
+
selector = '[class="cf-turnstile"]'
|
|
1526
|
+
elif await self.is_element_present("#challenge-form div > div"):
|
|
1527
|
+
selector = "#challenge-form div > div"
|
|
1528
|
+
elif await self.is_element_present('[style="display: grid;"] div div'):
|
|
1529
|
+
selector = '[style="display: grid;"] div div'
|
|
1530
|
+
elif await self.is_element_present("[class*=spacer] + div div"):
|
|
1531
|
+
selector = '[class*=spacer] + div div'
|
|
1532
|
+
elif await self.is_element_present(".spacer div:not([class])"):
|
|
1533
|
+
selector = ".spacer div:not([class])"
|
|
1534
|
+
elif await self.is_element_present('[data-testid*="challenge-"] div'):
|
|
1535
|
+
selector = '[data-testid*="challenge-"] div'
|
|
1536
|
+
elif await self.is_element_present(
|
|
1537
|
+
"div#turnstile-widget div:not([class])"
|
|
1538
|
+
):
|
|
1539
|
+
selector = "div#turnstile-widget div:not([class])"
|
|
1540
|
+
elif await self.is_element_present("ngx-turnstile div:not([class])"):
|
|
1541
|
+
selector = "ngx-turnstile div:not([class])"
|
|
1542
|
+
elif await self.is_element_present(
|
|
1543
|
+
'form div:not([class]):has(input[name*="cf-turn"])'
|
|
1544
|
+
):
|
|
1545
|
+
selector = 'form div:not([class]):has(input[name*="cf-turn"])'
|
|
1546
|
+
elif await self.is_element_present("form div:not(:has(*))"):
|
|
1547
|
+
selector = "form div:not(:has(*))"
|
|
1548
|
+
elif await self.is_element_present(
|
|
1549
|
+
"body > div#check > div:not([class])"
|
|
1550
|
+
):
|
|
1551
|
+
selector = "body > div#check > div:not([class])"
|
|
1552
|
+
elif await self.is_element_present(".cf-turnstile-wrapper"):
|
|
1553
|
+
selector = ".cf-turnstile-wrapper"
|
|
1554
|
+
elif await self.is_element_present(
|
|
1555
|
+
'[id*="turnstile"] div:not([class])'
|
|
1556
|
+
):
|
|
1557
|
+
selector = '[id*="turnstile"] div:not([class])'
|
|
1558
|
+
elif await self.is_element_present(
|
|
1559
|
+
'[class*="turnstile"] div:not([class])'
|
|
1560
|
+
):
|
|
1561
|
+
selector = '[class*="turnstile"] div:not([class])'
|
|
1562
|
+
elif await self.is_element_present(
|
|
1563
|
+
'[data-callback="onCaptchaSuccess"]'
|
|
1564
|
+
):
|
|
1565
|
+
selector = '[data-callback="onCaptchaSuccess"]'
|
|
1566
|
+
elif await self.is_element_present(
|
|
1567
|
+
"div:not([class]) > div:not([class])"
|
|
1568
|
+
):
|
|
1569
|
+
selector = "div:not([class]) > div:not([class])"
|
|
1570
|
+
else:
|
|
1571
|
+
return
|
|
1572
|
+
if not selector:
|
|
1573
|
+
return
|
|
1574
|
+
if (
|
|
1575
|
+
await self.is_element_present("form")
|
|
1576
|
+
and (
|
|
1577
|
+
await self.is_element_present('form[class*="center"]')
|
|
1578
|
+
or await self.is_element_present('form[class*="right"]')
|
|
1579
|
+
or await self.is_element_present('form div[class*="center"]')
|
|
1580
|
+
or await self.is_element_present('form div[class*="right"]')
|
|
1581
|
+
)
|
|
1582
|
+
):
|
|
1583
|
+
script = (
|
|
1584
|
+
"""var $elements = document.querySelectorAll(
|
|
1585
|
+
'form[class], form div[class]');
|
|
1586
|
+
var index = 0, length = $elements.length;
|
|
1587
|
+
for(; index < length; index++){
|
|
1588
|
+
the_class = $elements[index].getAttribute('class');
|
|
1589
|
+
new_class = the_class.replaceAll('center', 'left');
|
|
1590
|
+
new_class = new_class.replaceAll('right', 'left');
|
|
1591
|
+
$elements[index].setAttribute('class', new_class);}"""
|
|
1592
|
+
)
|
|
1593
|
+
with suppress(Exception):
|
|
1594
|
+
await self.evaluate(script)
|
|
1595
|
+
elif (
|
|
1596
|
+
await self.is_element_present("form")
|
|
1597
|
+
and (
|
|
1598
|
+
await self.is_element_present('form div[style*="center"]')
|
|
1599
|
+
or await self.is_element_present('form div[style*="right"]')
|
|
1600
|
+
)
|
|
1601
|
+
):
|
|
1602
|
+
script = (
|
|
1603
|
+
"""var $elements = document.querySelectorAll(
|
|
1604
|
+
'form[style], form div[style]');
|
|
1605
|
+
var index = 0, length = $elements.length;
|
|
1606
|
+
for(; index < length; index++){
|
|
1607
|
+
the_style = $elements[index].getAttribute('style');
|
|
1608
|
+
new_style = the_style.replaceAll('center', 'left');
|
|
1609
|
+
new_style = new_style.replaceAll('right', 'left');
|
|
1610
|
+
$elements[index].setAttribute('style', new_style);}"""
|
|
1611
|
+
)
|
|
1612
|
+
with suppress(Exception):
|
|
1613
|
+
await self.evaluate(script)
|
|
1614
|
+
elif (
|
|
1615
|
+
await self.is_element_present(
|
|
1616
|
+
'form [id*="turnstile"] div:not([class])'
|
|
1617
|
+
)
|
|
1618
|
+
or await self.is_element_present(
|
|
1619
|
+
'form [class*="turnstile"] div:not([class])'
|
|
1620
|
+
)
|
|
1621
|
+
):
|
|
1622
|
+
script = (
|
|
1623
|
+
"""var $elements = document.querySelectorAll(
|
|
1624
|
+
'form [id*="turnstile"]');
|
|
1625
|
+
var index = 0, length = $elements.length;
|
|
1626
|
+
for(; index < length; index++){
|
|
1627
|
+
$elements[index].setAttribute('align', 'left');}
|
|
1628
|
+
var $elements = document.querySelectorAll(
|
|
1629
|
+
'form [class*="turnstile"]');
|
|
1630
|
+
var index = 0, length = $elements.length;
|
|
1631
|
+
for(; index < length; index++){
|
|
1632
|
+
$elements[index].setAttribute('align', 'left');}"""
|
|
1633
|
+
)
|
|
1634
|
+
with suppress(Exception):
|
|
1635
|
+
await self.evaluate(script)
|
|
1636
|
+
elif (
|
|
1637
|
+
await self.is_element_present(
|
|
1638
|
+
'[style*="text-align: center;"] div:not([class])'
|
|
1639
|
+
)
|
|
1640
|
+
):
|
|
1641
|
+
script = (
|
|
1642
|
+
"""var $elements = document.querySelectorAll(
|
|
1643
|
+
'[style*="text-align: center;"]');
|
|
1644
|
+
var index = 0, length = $elements.length;
|
|
1645
|
+
for(; index < length; index++){
|
|
1646
|
+
the_style = $elements[index].getAttribute('style');
|
|
1647
|
+
new_style = the_style.replaceAll('center', 'left');
|
|
1648
|
+
$elements[index].setAttribute('style', new_style);}"""
|
|
1649
|
+
)
|
|
1650
|
+
with suppress(Exception):
|
|
1651
|
+
await self.evaluate(script)
|
|
1652
|
+
with suppress(Exception):
|
|
1653
|
+
await self.sleep(0.05)
|
|
1654
|
+
element_rect = await self.get_gui_element_rect(selector, timeout=1)
|
|
1655
|
+
e_x = element_rect["x"]
|
|
1656
|
+
e_y = element_rect["y"]
|
|
1657
|
+
x_offset = 32
|
|
1658
|
+
y_offset = 32
|
|
1659
|
+
if await asyncio.to_thread(shared_utils.is_windows):
|
|
1660
|
+
y_offset = 28
|
|
1661
|
+
x = e_x + x_offset
|
|
1662
|
+
y = e_y + y_offset
|
|
1663
|
+
sb_config._saved_cf_x_y = (x, y) # For debugging later
|
|
1664
|
+
await self.sleep(0.11)
|
|
1665
|
+
gui_lock = FileLock(constants.MultiBrowser.PYAUTOGUILOCK)
|
|
1666
|
+
with await asyncio.to_thread(gui_lock.acquire):
|
|
1667
|
+
await self.bring_to_front()
|
|
1668
|
+
await self.sleep(0.05)
|
|
1669
|
+
await self.click_with_offset(
|
|
1670
|
+
selector, x_offset, y_offset, timeout=1
|
|
1671
|
+
)
|
|
1672
|
+
await self.sleep(0.22)
|
|
1673
|
+
|
|
1674
|
+
async def click_captcha(self):
|
|
1675
|
+
await self.solve_captcha()
|
|
1303
1676
|
|
|
1304
1677
|
async def get_document(self):
|
|
1305
1678
|
return await self.send(cdp.dom.get_document())
|
|
@@ -1365,7 +1738,9 @@ class Tab(Connection):
|
|
|
1365
1738
|
:param selector: css selector string
|
|
1366
1739
|
:type selector: str
|
|
1367
1740
|
"""
|
|
1368
|
-
return self.wait_for(
|
|
1741
|
+
return self.wait_for(
|
|
1742
|
+
selector=selector, text=text, timeout=timeout
|
|
1743
|
+
)
|
|
1369
1744
|
|
|
1370
1745
|
def __eq__(self, other: Tab):
|
|
1371
1746
|
try:
|
|
@@ -19,7 +19,7 @@ FULL_DOWNLOAD_PATH = os.getcwd() + "/" + RENAMED_JAR_FILE
|
|
|
19
19
|
def download_selenium_server():
|
|
20
20
|
"""Downloads the Selenium Server JAR file."""
|
|
21
21
|
try:
|
|
22
|
-
local_file = open(JAR_FILE, "wb")
|
|
22
|
+
local_file = open(JAR_FILE, mode="wb")
|
|
23
23
|
remote_file = urlopen(SELENIUM_JAR)
|
|
24
24
|
print("Downloading the Selenium Server JAR file...\n")
|
|
25
25
|
local_file.write(remote_file.read())
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import codecs
|
|
2
1
|
import os
|
|
3
2
|
import subprocess
|
|
4
3
|
import sys
|
|
@@ -78,7 +77,7 @@ def main():
|
|
|
78
77
|
data = []
|
|
79
78
|
data.append(verbose)
|
|
80
79
|
file_path = os.path.join(dir_path, "verbose_hub_server.dat")
|
|
81
|
-
file =
|
|
80
|
+
file = open(file_path, mode="w+", encoding="utf-8")
|
|
82
81
|
file.writelines("\r\n".join(data))
|
|
83
82
|
file.close()
|
|
84
83
|
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import codecs
|
|
2
1
|
import os
|
|
3
2
|
import subprocess
|
|
4
3
|
import sys
|
|
@@ -88,14 +87,14 @@ def main():
|
|
|
88
87
|
data = []
|
|
89
88
|
data.append(server_ip)
|
|
90
89
|
file_path = os.path.join(dir_path, "ip_of_grid_hub.dat")
|
|
91
|
-
file =
|
|
90
|
+
file = open(file_path, mode="w+", encoding="utf-8")
|
|
92
91
|
file.writelines("\r\n".join(data))
|
|
93
92
|
file.close()
|
|
94
93
|
|
|
95
94
|
data = []
|
|
96
95
|
data.append(verbose)
|
|
97
96
|
file_path = os.path.join(dir_path, "verbose_node_server.dat")
|
|
98
|
-
file =
|
|
97
|
+
file = open(file_path, mode="w+", encoding="utf-8")
|
|
99
98
|
file.writelines("\r\n".join(data))
|
|
100
99
|
file.close()
|
|
101
100
|
|
|
@@ -8,7 +8,6 @@ Usage:
|
|
|
8
8
|
Output:
|
|
9
9
|
[NEW_FILE_SB].py (adds "_SB" to the original file name)
|
|
10
10
|
(the original file is kept intact)"""
|
|
11
|
-
import codecs
|
|
12
11
|
import re
|
|
13
12
|
import sys
|
|
14
13
|
from seleniumbase.fixtures import js_utils
|
|
@@ -60,7 +59,7 @@ def main():
|
|
|
60
59
|
uses_keys = False
|
|
61
60
|
uses_select = False
|
|
62
61
|
|
|
63
|
-
with open(webdriver_python_file, "r", encoding="utf-8") as f:
|
|
62
|
+
with open(webdriver_python_file, mode="r", encoding="utf-8") as f:
|
|
64
63
|
all_code = f.read()
|
|
65
64
|
if "def test_" not in all_code:
|
|
66
65
|
raise Exception(
|
|
@@ -894,7 +893,7 @@ def main():
|
|
|
894
893
|
# Create SeleniumBase test file
|
|
895
894
|
base_file_name = webdriver_python_file.split(".py")[0]
|
|
896
895
|
converted_file_name = base_file_name + "_SB.py"
|
|
897
|
-
out_file =
|
|
896
|
+
out_file = open(converted_file_name, mode="w+", encoding="utf-8")
|
|
898
897
|
out_file.writelines(seleniumbase_code)
|
|
899
898
|
out_file.close()
|
|
900
899
|
print(
|