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
|
@@ -5,7 +5,11 @@ import secrets
|
|
|
5
5
|
import sys
|
|
6
6
|
import tempfile
|
|
7
7
|
import zipfile
|
|
8
|
+
from contextlib import suppress
|
|
8
9
|
from seleniumbase.config import settings
|
|
10
|
+
from seleniumbase.drivers import chromium_drivers
|
|
11
|
+
from seleniumbase.fixtures import constants
|
|
12
|
+
from seleniumbase.fixtures import shared_utils
|
|
9
13
|
from typing import Union, List, Optional
|
|
10
14
|
|
|
11
15
|
__all__ = [
|
|
@@ -22,6 +26,12 @@ is_posix = sys.platform.startswith(("darwin", "cygwin", "linux", "linux2"))
|
|
|
22
26
|
|
|
23
27
|
PathLike = Union[str, pathlib.Path]
|
|
24
28
|
AUTO = None
|
|
29
|
+
IS_MAC = shared_utils.is_mac()
|
|
30
|
+
IS_LINUX = shared_utils.is_linux()
|
|
31
|
+
IS_WINDOWS = shared_utils.is_windows()
|
|
32
|
+
CHROMIUM_DIR = os.path.dirname(
|
|
33
|
+
os.path.realpath(chromium_drivers.__file__)
|
|
34
|
+
)
|
|
25
35
|
|
|
26
36
|
|
|
27
37
|
class Config:
|
|
@@ -78,12 +88,64 @@ class Config:
|
|
|
78
88
|
if not browser_args:
|
|
79
89
|
browser_args = []
|
|
80
90
|
if not user_data_dir:
|
|
81
|
-
self.
|
|
91
|
+
self.user_data_dir = temp_profile_dir()
|
|
92
|
+
self._user_data_dir = self.user_data_dir
|
|
82
93
|
self._custom_data_dir = False
|
|
83
94
|
else:
|
|
84
95
|
self.user_data_dir = user_data_dir
|
|
96
|
+
profile = os.path.join(self.user_data_dir, "Default")
|
|
97
|
+
preferences_file = os.path.join(profile, "Preferences")
|
|
98
|
+
preferences = get_default_preferences()
|
|
99
|
+
if not os.path.exists(profile):
|
|
100
|
+
with suppress(Exception):
|
|
101
|
+
os.makedirs(profile)
|
|
102
|
+
with open(preferences_file, "w") as f:
|
|
103
|
+
f.write(preferences)
|
|
104
|
+
mock_keychain = False
|
|
85
105
|
if not browser_executable_path:
|
|
86
106
|
browser_executable_path = find_chrome_executable()
|
|
107
|
+
elif browser_executable_path == "_chromium_":
|
|
108
|
+
from filelock import FileLock
|
|
109
|
+
binary_folder = None
|
|
110
|
+
if IS_MAC:
|
|
111
|
+
binary_folder = "chrome-mac"
|
|
112
|
+
elif IS_LINUX:
|
|
113
|
+
binary_folder = "chrome-linux"
|
|
114
|
+
elif IS_WINDOWS:
|
|
115
|
+
binary_folder = "chrome-win"
|
|
116
|
+
binary_location = os.path.join(CHROMIUM_DIR, binary_folder)
|
|
117
|
+
gui_lock = FileLock(constants.MultiBrowser.DRIVER_FIXING_LOCK)
|
|
118
|
+
with gui_lock:
|
|
119
|
+
with suppress(Exception):
|
|
120
|
+
shared_utils.make_writable(
|
|
121
|
+
constants.MultiBrowser.DRIVER_FIXING_LOCK
|
|
122
|
+
)
|
|
123
|
+
if not os.path.exists(binary_location):
|
|
124
|
+
from seleniumbase.console_scripts import sb_install
|
|
125
|
+
sys_args = sys.argv # Save a copy of sys args
|
|
126
|
+
sb_install.log_d("\nWarning: Chromium binary not found...")
|
|
127
|
+
sb_install.main(override="chromium")
|
|
128
|
+
sys.argv = sys_args # Put back original args
|
|
129
|
+
binary_name = binary_location.split("/")[-1].split("\\")[-1]
|
|
130
|
+
if binary_name in ["chrome-mac"]:
|
|
131
|
+
binary_name = "Chromium"
|
|
132
|
+
binary_location += "/Chromium.app"
|
|
133
|
+
binary_location += "/Contents/MacOS/Chromium"
|
|
134
|
+
elif binary_name == "chrome-linux":
|
|
135
|
+
binary_name = "chrome"
|
|
136
|
+
binary_location += "/chrome"
|
|
137
|
+
elif binary_name in ["chrome-win"]:
|
|
138
|
+
binary_name = "chrome.exe"
|
|
139
|
+
binary_location += "\\chrome.exe"
|
|
140
|
+
if os.path.exists(binary_location):
|
|
141
|
+
mock_keychain = True
|
|
142
|
+
browser_executable_path = binary_location
|
|
143
|
+
else:
|
|
144
|
+
print(
|
|
145
|
+
f"{binary_location} not found. "
|
|
146
|
+
f"Defaulting to regular Chrome!"
|
|
147
|
+
)
|
|
148
|
+
browser_executable_path = find_chrome_executable()
|
|
87
149
|
self._browser_args = browser_args
|
|
88
150
|
self.browser_executable_path = browser_executable_path
|
|
89
151
|
self.headless = headless
|
|
@@ -113,40 +175,43 @@ class Config:
|
|
|
113
175
|
self._default_browser_args = [
|
|
114
176
|
"--window-size=%s,%s" % (start_width, start_height),
|
|
115
177
|
"--window-position=%s,%s" % (start_x, start_y),
|
|
116
|
-
"--remote-allow-origins=*",
|
|
117
178
|
"--no-first-run",
|
|
118
179
|
"--no-service-autorun",
|
|
119
180
|
"--disable-auto-reload",
|
|
120
181
|
"--no-default-browser-check",
|
|
121
182
|
"--homepage=about:blank",
|
|
122
183
|
"--no-pings",
|
|
184
|
+
"--enable-unsafe-extension-debugging",
|
|
123
185
|
"--wm-window-animations-disabled",
|
|
124
186
|
"--animation-duration-scale=0",
|
|
125
187
|
"--enable-privacy-sandbox-ads-apis",
|
|
126
188
|
"--safebrowsing-disable-download-protection",
|
|
127
189
|
'--simulate-outdated-no-au="Tue, 31 Dec 2099 23:59:59 GMT"',
|
|
190
|
+
"--test-type",
|
|
191
|
+
"--ash-no-nudges",
|
|
128
192
|
"--password-store=basic",
|
|
129
193
|
"--deny-permission-prompts",
|
|
130
|
-
"--disable-infobars",
|
|
131
194
|
"--disable-breakpad",
|
|
195
|
+
"--disable-setuid-sandbox",
|
|
132
196
|
"--disable-prompt-on-repost",
|
|
197
|
+
"--disable-application-cache",
|
|
133
198
|
"--disable-password-generation",
|
|
199
|
+
"--disable-save-password-bubble",
|
|
200
|
+
"--disable-single-click-autofill",
|
|
134
201
|
"--disable-ipc-flooding-protection",
|
|
135
202
|
"--disable-background-timer-throttling",
|
|
136
203
|
"--disable-search-engine-choice-screen",
|
|
137
204
|
"--disable-backgrounding-occluded-windows",
|
|
138
205
|
"--disable-client-side-phishing-detection",
|
|
206
|
+
"--disable-device-discovery-notifications",
|
|
139
207
|
"--disable-top-sites",
|
|
140
208
|
"--disable-translate",
|
|
209
|
+
"--dns-prefetch-disable",
|
|
141
210
|
"--disable-renderer-backgrounding",
|
|
142
|
-
"--disable-background-networking",
|
|
143
211
|
"--disable-dev-shm-usage",
|
|
144
|
-
"--disable-features=IsolateOrigins,site-per-process,Translate,"
|
|
145
|
-
"InsecureDownloadWarnings,DownloadBubble,DownloadBubbleV2,"
|
|
146
|
-
"OptimizationTargetPrediction,OptimizationGuideModelDownloading,"
|
|
147
|
-
"SidePanelPinning,UserAgentClientHint,PrivacySandboxSettings4,"
|
|
148
|
-
"DisableLoadExtensionCommandLineSwitch",
|
|
149
212
|
]
|
|
213
|
+
if mock_keychain:
|
|
214
|
+
self._default_browser_args.append("--use-mock-keychain")
|
|
150
215
|
|
|
151
216
|
@property
|
|
152
217
|
def browser_args(self):
|
|
@@ -193,8 +258,16 @@ class Config:
|
|
|
193
258
|
# By the time it starts, the port is probably already taken.
|
|
194
259
|
args = self._default_browser_args.copy()
|
|
195
260
|
args += ["--user-data-dir=%s" % self.user_data_dir]
|
|
196
|
-
args += [
|
|
197
|
-
|
|
261
|
+
args += [
|
|
262
|
+
"--disable-features=IsolateOrigins,site-per-process,Translate,"
|
|
263
|
+
"InsecureDownloadWarnings,DownloadBubble,DownloadBubbleV2,"
|
|
264
|
+
"OptimizationTargetPrediction,OptimizationGuideModelDownloading,"
|
|
265
|
+
"SidePanelPinning,UserAgentClientHint,PrivacySandboxSettings4,"
|
|
266
|
+
"OptimizationHintsFetching,InterestFeedContentSuggestions,"
|
|
267
|
+
"Bluetooth,WebBluetooth,UnifiedWebBluetooth,ComponentUpdater,"
|
|
268
|
+
"DisableLoadExtensionCommandLineSwitch,"
|
|
269
|
+
"WebAuthentication,PasskeyAuth"
|
|
270
|
+
]
|
|
198
271
|
if self.expert:
|
|
199
272
|
args += [
|
|
200
273
|
"--disable-web-security",
|
|
@@ -270,9 +343,25 @@ def is_root():
|
|
|
270
343
|
return ctypes.windll.shell32.IsUserAnAdmin() != 0
|
|
271
344
|
|
|
272
345
|
|
|
346
|
+
def get_default_preferences():
|
|
347
|
+
return (
|
|
348
|
+
"""{"credentials_enable_service": false,
|
|
349
|
+
"password_manager_enabled": false,
|
|
350
|
+
"password_manager_leak_detection": false}"""
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
273
354
|
def temp_profile_dir():
|
|
274
355
|
"""Generate a temp dir (path)"""
|
|
275
356
|
path = os.path.normpath(tempfile.mkdtemp(prefix="uc_"))
|
|
357
|
+
profile = os.path.join(path, "Default")
|
|
358
|
+
preferences_file = os.path.join(profile, "Preferences")
|
|
359
|
+
preferences = get_default_preferences()
|
|
360
|
+
if not os.path.exists(profile):
|
|
361
|
+
with suppress(Exception):
|
|
362
|
+
os.makedirs(profile)
|
|
363
|
+
with open(preferences_file, "w") as f:
|
|
364
|
+
f.write(preferences)
|
|
276
365
|
return path
|
|
277
366
|
|
|
278
367
|
|
|
@@ -286,10 +375,13 @@ def find_chrome_executable(return_all=False):
|
|
|
286
375
|
for item in os.environ.get("PATH").split(os.pathsep):
|
|
287
376
|
for subitem in (
|
|
288
377
|
"google-chrome",
|
|
378
|
+
"google-chrome-stable",
|
|
379
|
+
"google-chrome-beta",
|
|
380
|
+
"google-chrome-dev",
|
|
381
|
+
"google-chrome-unstable",
|
|
382
|
+
"chrome",
|
|
289
383
|
"chromium",
|
|
290
384
|
"chromium-browser",
|
|
291
|
-
"chrome",
|
|
292
|
-
"google-chrome-stable",
|
|
293
385
|
):
|
|
294
386
|
candidates.append(os.sep.join((item, subitem)))
|
|
295
387
|
if "darwin" in sys.platform:
|
|
@@ -318,7 +410,11 @@ def find_chrome_executable(return_all=False):
|
|
|
318
410
|
)
|
|
319
411
|
rv = []
|
|
320
412
|
for candidate in candidates:
|
|
321
|
-
if
|
|
413
|
+
if (
|
|
414
|
+
os.path.exists(candidate)
|
|
415
|
+
and os.access(candidate, os.R_OK)
|
|
416
|
+
and os.access(candidate, os.X_OK)
|
|
417
|
+
):
|
|
322
418
|
logger.debug("%s is a valid candidate... " % candidate)
|
|
323
419
|
rv.append(candidate)
|
|
324
420
|
else:
|
|
@@ -7,7 +7,6 @@ import json
|
|
|
7
7
|
import logging
|
|
8
8
|
import sys
|
|
9
9
|
import types
|
|
10
|
-
from asyncio import iscoroutine, iscoroutinefunction
|
|
11
10
|
from typing import (
|
|
12
11
|
Optional,
|
|
13
12
|
Generator,
|
|
@@ -33,6 +32,7 @@ GLOBAL_DELAY = 0.005
|
|
|
33
32
|
MAX_SIZE: int = 2**28
|
|
34
33
|
PING_TIMEOUT: int = 1800 # 30 minutes
|
|
35
34
|
TargetType = Union[cdp.target.TargetInfo, cdp.target.TargetID]
|
|
35
|
+
logging.getLogger("asyncio").setLevel(logging.CRITICAL)
|
|
36
36
|
logger = logging.getLogger("uc.connection")
|
|
37
37
|
|
|
38
38
|
|
|
@@ -184,13 +184,13 @@ class Connection(metaclass=CantTouchThis):
|
|
|
184
184
|
self,
|
|
185
185
|
websocket_url=None,
|
|
186
186
|
target=None,
|
|
187
|
-
|
|
187
|
+
browser=None,
|
|
188
188
|
**kwargs,
|
|
189
189
|
):
|
|
190
190
|
super().__init__()
|
|
191
191
|
self._target = target
|
|
192
192
|
self.__count__ = itertools.count(0)
|
|
193
|
-
self.
|
|
193
|
+
self.browser = browser
|
|
194
194
|
self.websocket_url: str = websocket_url
|
|
195
195
|
self.websocket = None
|
|
196
196
|
self.mapper = {}
|
|
@@ -370,8 +370,8 @@ class Connection(metaclass=CantTouchThis):
|
|
|
370
370
|
|
|
371
371
|
async def set_geolocation(self, geolocation: Optional[tuple] = None):
|
|
372
372
|
"""Sets the User Agent via set_geolocation_override."""
|
|
373
|
-
await self.send(cdp.browser.
|
|
374
|
-
|
|
373
|
+
await self.send(cdp.browser.set_permission(
|
|
374
|
+
permission={"name": "geolocation"}, setting="granted"
|
|
375
375
|
))
|
|
376
376
|
await self.send(cdp.emulation.set_geolocation_override(
|
|
377
377
|
latitude=geolocation[0],
|
|
@@ -379,36 +379,6 @@ class Connection(metaclass=CantTouchThis):
|
|
|
379
379
|
accuracy=100,
|
|
380
380
|
))
|
|
381
381
|
|
|
382
|
-
async def set_auth(self, username, password, tab):
|
|
383
|
-
async def auth_challenge_handler(event: cdp.fetch.AuthRequired):
|
|
384
|
-
await tab.send(
|
|
385
|
-
cdp.fetch.continue_with_auth(
|
|
386
|
-
request_id=event.request_id,
|
|
387
|
-
auth_challenge_response=cdp.fetch.AuthChallengeResponse(
|
|
388
|
-
response="ProvideCredentials",
|
|
389
|
-
username=username,
|
|
390
|
-
password=password,
|
|
391
|
-
),
|
|
392
|
-
)
|
|
393
|
-
)
|
|
394
|
-
|
|
395
|
-
async def req_paused(event: cdp.fetch.RequestPaused):
|
|
396
|
-
await tab.send(
|
|
397
|
-
cdp.fetch.continue_request(request_id=event.request_id)
|
|
398
|
-
)
|
|
399
|
-
|
|
400
|
-
tab.add_handler(
|
|
401
|
-
cdp.fetch.RequestPaused,
|
|
402
|
-
lambda event: asyncio.create_task(req_paused(event)),
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
tab.add_handler(
|
|
406
|
-
cdp.fetch.AuthRequired,
|
|
407
|
-
lambda event: asyncio.create_task(auth_challenge_handler(event)),
|
|
408
|
-
)
|
|
409
|
-
|
|
410
|
-
await tab.send(cdp.fetch.enable(handle_auth_requests=True))
|
|
411
|
-
|
|
412
382
|
def __getattr__(self, item):
|
|
413
383
|
""":meta private:"""
|
|
414
384
|
try:
|
|
@@ -456,8 +426,8 @@ class Connection(metaclass=CantTouchThis):
|
|
|
456
426
|
await self.aopen()
|
|
457
427
|
if not self.websocket or self.websocket.state is State.CLOSED:
|
|
458
428
|
return
|
|
459
|
-
if self.
|
|
460
|
-
browser = self.
|
|
429
|
+
if self.browser:
|
|
430
|
+
browser = self.browser
|
|
461
431
|
if browser.config:
|
|
462
432
|
if browser.config.expert:
|
|
463
433
|
await self._prepare_expert()
|
|
@@ -520,8 +490,6 @@ class Connection(metaclass=CantTouchThis):
|
|
|
520
490
|
except BaseException:
|
|
521
491
|
logger.debug("NOT GOOD", exc_info=True)
|
|
522
492
|
continue
|
|
523
|
-
finally:
|
|
524
|
-
continue
|
|
525
493
|
for ed in enabled_domains:
|
|
526
494
|
# Items still present at this point are unused and need removal.
|
|
527
495
|
self.enabled_domains.remove(ed)
|
|
@@ -642,11 +610,11 @@ class Listener:
|
|
|
642
610
|
# Probably an event
|
|
643
611
|
try:
|
|
644
612
|
event = cdp.util.parse_json_event(message)
|
|
645
|
-
event_tx = EventTransaction(event)
|
|
646
|
-
if not self.connection.mapper:
|
|
647
|
-
|
|
648
|
-
event_tx.id = next(self.connection.__count__)
|
|
649
|
-
self.connection.mapper[event_tx.id] = event_tx
|
|
613
|
+
# event_tx = EventTransaction(event)
|
|
614
|
+
# if not self.connection.mapper:
|
|
615
|
+
# self.connection.__count__ = itertools.count(0)
|
|
616
|
+
# event_tx.id = next(self.connection.__count__)
|
|
617
|
+
# self.connection.mapper[event_tx.id] = event_tx
|
|
650
618
|
except Exception as e:
|
|
651
619
|
logger.info(
|
|
652
620
|
"%s: %s during parsing of json from event : %s"
|
|
@@ -667,13 +635,15 @@ class Listener:
|
|
|
667
635
|
for callback in callbacks:
|
|
668
636
|
try:
|
|
669
637
|
if (
|
|
670
|
-
iscoroutinefunction(callback)
|
|
671
|
-
or iscoroutine(callback)
|
|
638
|
+
inspect.iscoroutinefunction(callback)
|
|
639
|
+
or inspect.iscoroutine(callback)
|
|
672
640
|
):
|
|
673
641
|
try:
|
|
674
|
-
|
|
642
|
+
asyncio.create_task(
|
|
643
|
+
callback(event, self.connection)
|
|
644
|
+
)
|
|
675
645
|
except TypeError:
|
|
676
|
-
|
|
646
|
+
asyncio.create_task(callback(event))
|
|
677
647
|
else:
|
|
678
648
|
try:
|
|
679
649
|
callback(event, self.connection)
|
|
@@ -372,7 +372,10 @@ class Element:
|
|
|
372
372
|
arguments = [cdp.runtime.CallArgument(
|
|
373
373
|
object_id=self._remote_object.object_id
|
|
374
374
|
)]
|
|
375
|
-
|
|
375
|
+
script = 'sessionStorage.getItem("pxsid") !== null;'
|
|
376
|
+
using_px = await self.tab.evaluate(script)
|
|
377
|
+
if not using_px:
|
|
378
|
+
await self.flash_async(0.25)
|
|
376
379
|
await self._tab.send(
|
|
377
380
|
cdp.runtime.call_function_on(
|
|
378
381
|
"(el) => el.click()",
|
|
@@ -484,17 +487,14 @@ class Element:
|
|
|
484
487
|
buttons: typing.Optional[int] = 1,
|
|
485
488
|
modifiers: typing.Optional[int] = 0,
|
|
486
489
|
hold: bool = False,
|
|
487
|
-
_until_event: typing.Optional[type] = None,
|
|
488
490
|
):
|
|
489
491
|
"""
|
|
490
492
|
Native click (on element).
|
|
491
|
-
Note: This likely does not work at the moment. Use click() instead.
|
|
492
493
|
:param button: str (default = "left")
|
|
493
494
|
:param buttons: which button (default 1 = left)
|
|
494
495
|
:param modifiers: *(Optional)*
|
|
495
496
|
Bit field representing pressed modifier keys.
|
|
496
497
|
Alt=1, Ctrl=2, Meta/Command=4, Shift=8 (default: 0).
|
|
497
|
-
:param _until_event: Internal. Event to wait for before returning.
|
|
498
498
|
"""
|
|
499
499
|
try:
|
|
500
500
|
center = (await self.get_position_async()).center
|
|
@@ -504,7 +504,11 @@ class Element:
|
|
|
504
504
|
logger.warning("Could not calculate box model for %s", self)
|
|
505
505
|
return
|
|
506
506
|
logger.debug("Clicking on location: %.2f, %.2f" % center)
|
|
507
|
-
|
|
507
|
+
script = 'sessionStorage.getItem("pxsid") !== null;'
|
|
508
|
+
using_px = await self.tab.evaluate(script)
|
|
509
|
+
if not using_px:
|
|
510
|
+
asyncio.create_task(self.flash_async(0.25))
|
|
511
|
+
asyncio.create_task(
|
|
508
512
|
self._tab.send(
|
|
509
513
|
cdp.input_.dispatch_mouse_event(
|
|
510
514
|
"mousePressed",
|
|
@@ -516,6 +520,8 @@ class Element:
|
|
|
516
520
|
click_count=1,
|
|
517
521
|
)
|
|
518
522
|
),
|
|
523
|
+
)
|
|
524
|
+
asyncio.create_task(
|
|
519
525
|
self._tab.send(
|
|
520
526
|
cdp.input_.dispatch_mouse_event(
|
|
521
527
|
"mouseReleased",
|
|
@@ -528,10 +534,73 @@ class Element:
|
|
|
528
534
|
)
|
|
529
535
|
),
|
|
530
536
|
)
|
|
537
|
+
|
|
538
|
+
async def mouse_click_with_offset_async(
|
|
539
|
+
self,
|
|
540
|
+
x: typing.Union[float, int],
|
|
541
|
+
y: typing.Union[float, int],
|
|
542
|
+
center: bool = False,
|
|
543
|
+
button: str = "left",
|
|
544
|
+
buttons: typing.Optional[int] = 1,
|
|
545
|
+
modifiers: typing.Optional[int] = 0,
|
|
546
|
+
):
|
|
547
|
+
x_offset = int(x)
|
|
548
|
+
y_offset = int(y)
|
|
531
549
|
try:
|
|
532
|
-
await self.
|
|
533
|
-
|
|
534
|
-
|
|
550
|
+
position = (await self.get_position_async())
|
|
551
|
+
width = position.width
|
|
552
|
+
height = position.height
|
|
553
|
+
x_pos = position.left
|
|
554
|
+
y_pos = position.top
|
|
555
|
+
center_pos = (await self.get_position_async()).center
|
|
556
|
+
except AttributeError:
|
|
557
|
+
return
|
|
558
|
+
if not center_pos:
|
|
559
|
+
logger.warning("Could not calculate box model for %s", self)
|
|
560
|
+
return
|
|
561
|
+
if center:
|
|
562
|
+
x_pos = center_pos[0]
|
|
563
|
+
y_pos = center_pos[1]
|
|
564
|
+
width = 0
|
|
565
|
+
height = 0
|
|
566
|
+
logger.debug("Clicking on location: %.2f, %.2f" % center_pos)
|
|
567
|
+
else:
|
|
568
|
+
logger.debug("Clicking on location: %.2f, %.2f" % (x_pos, y_pos))
|
|
569
|
+
script = 'sessionStorage.getItem("pxsid") !== null;'
|
|
570
|
+
using_px = await self.tab.evaluate(script)
|
|
571
|
+
if not using_px:
|
|
572
|
+
asyncio.create_task(
|
|
573
|
+
self.flash_async(
|
|
574
|
+
x_offset=x_offset - (width / 2),
|
|
575
|
+
y_offset=y_offset - (height / 2),
|
|
576
|
+
),
|
|
577
|
+
)
|
|
578
|
+
asyncio.create_task(
|
|
579
|
+
self._tab.send(
|
|
580
|
+
cdp.input_.dispatch_mouse_event(
|
|
581
|
+
"mousePressed",
|
|
582
|
+
x=x_pos + x_offset,
|
|
583
|
+
y=y_pos + y_offset,
|
|
584
|
+
modifiers=modifiers,
|
|
585
|
+
button=cdp.input_.MouseButton(button),
|
|
586
|
+
buttons=buttons,
|
|
587
|
+
click_count=1,
|
|
588
|
+
)
|
|
589
|
+
)
|
|
590
|
+
)
|
|
591
|
+
asyncio.create_task(
|
|
592
|
+
self._tab.send(
|
|
593
|
+
cdp.input_.dispatch_mouse_event(
|
|
594
|
+
"mouseReleased",
|
|
595
|
+
x=x_pos + x_offset,
|
|
596
|
+
y=y_pos + y_offset,
|
|
597
|
+
modifiers=modifiers,
|
|
598
|
+
button=cdp.input_.MouseButton(button),
|
|
599
|
+
buttons=buttons,
|
|
600
|
+
click_count=1,
|
|
601
|
+
)
|
|
602
|
+
),
|
|
603
|
+
)
|
|
535
604
|
|
|
536
605
|
async def mouse_move_async(self):
|
|
537
606
|
"""
|
|
@@ -548,16 +617,13 @@ class Element:
|
|
|
548
617
|
*center,
|
|
549
618
|
self,
|
|
550
619
|
)
|
|
551
|
-
await
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
cdp.input_.dispatch_mouse_event(
|
|
559
|
-
"mouseReleased", x=center[0], y=center[1]
|
|
560
|
-
)
|
|
620
|
+
await asyncio.gather(
|
|
621
|
+
self._tab.send(
|
|
622
|
+
cdp.input_.dispatch_mouse_event(
|
|
623
|
+
"mouseMoved", x=center[0], y=center[1]
|
|
624
|
+
)
|
|
625
|
+
),
|
|
626
|
+
self._tab.sleep(0.05),
|
|
561
627
|
)
|
|
562
628
|
|
|
563
629
|
async def mouse_drag_async(
|
|
@@ -664,7 +730,7 @@ class Element:
|
|
|
664
730
|
return
|
|
665
731
|
# await self.apply("""(el) => el.scrollIntoView(false)""")
|
|
666
732
|
|
|
667
|
-
async def clear_input_async(self
|
|
733
|
+
async def clear_input_async(self):
|
|
668
734
|
"""Clears an input field."""
|
|
669
735
|
try:
|
|
670
736
|
await self.apply('function (element) { element.value = "" } ')
|
|
@@ -752,7 +818,10 @@ class Element:
|
|
|
752
818
|
|
|
753
819
|
async def get_html_async(self):
|
|
754
820
|
return await self._tab.send(
|
|
755
|
-
cdp.dom.get_outer_html(
|
|
821
|
+
cdp.dom.get_outer_html(
|
|
822
|
+
backend_node_id=self.backend_node_id,
|
|
823
|
+
include_shadow_dom=True,
|
|
824
|
+
)
|
|
756
825
|
)
|
|
757
826
|
|
|
758
827
|
@property
|
|
@@ -771,7 +840,7 @@ class Element:
|
|
|
771
840
|
"""
|
|
772
841
|
with suppress(Exception):
|
|
773
842
|
if self.node.node_name.lower() in ["input", "textarea"]:
|
|
774
|
-
input_node = self.node.shadow_roots[0].children[
|
|
843
|
+
input_node = self.node.shadow_roots[0].children[-1].children[0]
|
|
775
844
|
if input_node:
|
|
776
845
|
return input_node.node_value
|
|
777
846
|
text_nodes = util.filter_recurse_all(
|
|
@@ -784,7 +853,7 @@ class Element:
|
|
|
784
853
|
"""Same as text(). Kept for backwards compatibility."""
|
|
785
854
|
with suppress(Exception):
|
|
786
855
|
if self.node.node_name.lower() in ["input", "textarea"]:
|
|
787
|
-
input_node = self.node.shadow_roots[0].children[
|
|
856
|
+
input_node = self.node.shadow_roots[0].children[-1].children[0]
|
|
788
857
|
if input_node:
|
|
789
858
|
return input_node.node_value
|
|
790
859
|
text_nodes = util.filter_recurse_all(
|
|
@@ -952,18 +1021,21 @@ class Element:
|
|
|
952
1021
|
.replace(" ", "")
|
|
953
1022
|
.replace("\n", "")
|
|
954
1023
|
)
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
1024
|
+
try:
|
|
1025
|
+
arguments = [cdp.runtime.CallArgument(
|
|
1026
|
+
object_id=self._remote_object.object_id
|
|
1027
|
+
)]
|
|
1028
|
+
await self._tab.send(
|
|
1029
|
+
cdp.runtime.call_function_on(
|
|
1030
|
+
script,
|
|
1031
|
+
object_id=self._remote_object.object_id,
|
|
1032
|
+
arguments=arguments,
|
|
1033
|
+
await_promise=True,
|
|
1034
|
+
user_gesture=True,
|
|
1035
|
+
)
|
|
965
1036
|
)
|
|
966
|
-
|
|
1037
|
+
except Exception:
|
|
1038
|
+
pass
|
|
967
1039
|
|
|
968
1040
|
async def highlight_overlay_async(self):
|
|
969
1041
|
"""
|