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.
Files changed (64) hide show
  1. sbase/steps.py +9 -0
  2. seleniumbase/__version__.py +1 -1
  3. seleniumbase/behave/behave_helper.py +2 -0
  4. seleniumbase/behave/behave_sb.py +21 -8
  5. seleniumbase/common/decorators.py +3 -1
  6. seleniumbase/console_scripts/run.py +1 -0
  7. seleniumbase/console_scripts/sb_caseplans.py +3 -4
  8. seleniumbase/console_scripts/sb_install.py +142 -11
  9. seleniumbase/console_scripts/sb_mkchart.py +1 -2
  10. seleniumbase/console_scripts/sb_mkdir.py +99 -29
  11. seleniumbase/console_scripts/sb_mkfile.py +1 -2
  12. seleniumbase/console_scripts/sb_mkpres.py +1 -2
  13. seleniumbase/console_scripts/sb_mkrec.py +26 -2
  14. seleniumbase/console_scripts/sb_objectify.py +4 -5
  15. seleniumbase/console_scripts/sb_print.py +1 -1
  16. seleniumbase/console_scripts/sb_recorder.py +40 -3
  17. seleniumbase/core/browser_launcher.py +474 -151
  18. seleniumbase/core/detect_b_ver.py +258 -16
  19. seleniumbase/core/log_helper.py +15 -21
  20. seleniumbase/core/mysql.py +1 -1
  21. seleniumbase/core/recorder_helper.py +3 -0
  22. seleniumbase/core/report_helper.py +9 -12
  23. seleniumbase/core/sb_cdp.py +734 -215
  24. seleniumbase/core/sb_driver.py +46 -5
  25. seleniumbase/core/session_helper.py +2 -4
  26. seleniumbase/core/tour_helper.py +1 -2
  27. seleniumbase/drivers/atlas_drivers/__init__.py +0 -0
  28. seleniumbase/drivers/brave_drivers/__init__.py +0 -0
  29. seleniumbase/drivers/chromium_drivers/__init__.py +0 -0
  30. seleniumbase/drivers/comet_drivers/__init__.py +0 -0
  31. seleniumbase/drivers/opera_drivers/__init__.py +0 -0
  32. seleniumbase/fixtures/base_case.py +448 -251
  33. seleniumbase/fixtures/constants.py +36 -9
  34. seleniumbase/fixtures/js_utils.py +77 -18
  35. seleniumbase/fixtures/page_actions.py +41 -13
  36. seleniumbase/fixtures/page_utils.py +19 -12
  37. seleniumbase/fixtures/shared_utils.py +64 -6
  38. seleniumbase/masterqa/master_qa.py +16 -2
  39. seleniumbase/plugins/base_plugin.py +8 -0
  40. seleniumbase/plugins/basic_test_info.py +2 -3
  41. seleniumbase/plugins/driver_manager.py +131 -5
  42. seleniumbase/plugins/page_source.py +2 -3
  43. seleniumbase/plugins/pytest_plugin.py +244 -79
  44. seleniumbase/plugins/sb_manager.py +143 -20
  45. seleniumbase/plugins/selenium_plugin.py +144 -12
  46. seleniumbase/translate/translator.py +2 -3
  47. seleniumbase/undetected/__init__.py +17 -13
  48. seleniumbase/undetected/cdp.py +1 -12
  49. seleniumbase/undetected/cdp_driver/browser.py +330 -129
  50. seleniumbase/undetected/cdp_driver/cdp_util.py +328 -61
  51. seleniumbase/undetected/cdp_driver/config.py +110 -14
  52. seleniumbase/undetected/cdp_driver/connection.py +18 -48
  53. seleniumbase/undetected/cdp_driver/element.py +105 -33
  54. seleniumbase/undetected/cdp_driver/tab.py +414 -39
  55. seleniumbase/utilities/selenium_grid/download_selenium_server.py +1 -1
  56. seleniumbase/utilities/selenium_grid/grid_hub.py +1 -2
  57. seleniumbase/utilities/selenium_grid/grid_node.py +2 -3
  58. seleniumbase/utilities/selenium_ide/convert_ide.py +2 -3
  59. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/METADATA +193 -166
  60. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/RECORD +64 -59
  61. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/licenses/LICENSE +1 -1
  62. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/WHEEL +0 -0
  63. {seleniumbase-4.41.3.dist-info → seleniumbase-4.45.10.dist-info}/entry_points.txt +0 -0
  64. {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._user_data_dir = temp_profile_dir()
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 += ["--disable-features=IsolateOrigins,site-per-process"]
197
- args += ["--disable-session-crashed-bubble"]
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 os.path.exists(candidate) and os.access(candidate, os.X_OK):
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
- _owner=None,
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._owner = _owner
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.grant_permissions(
374
- permissions=["geolocation"],
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._owner:
460
- browser = self._owner
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
- self.connection.__count__ = itertools.count(0)
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
- await callback(event, self.connection)
642
+ asyncio.create_task(
643
+ callback(event, self.connection)
644
+ )
675
645
  except TypeError:
676
- await callback(event)
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
- await self.flash_async(0.25)
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
- await asyncio.gather(
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.flash_async()
533
- except BaseException:
534
- pass
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 self._tab.send(
552
- cdp.input_.dispatch_mouse_event(
553
- "mouseMoved", x=center[0], y=center[1]
554
- )
555
- )
556
- await self._tab.sleep(0.05)
557
- await self._tab.send(
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, _until_event: type = None):
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(backend_node_id=self.backend_node_id)
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[0].children[0]
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[0].children[0]
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
- arguments = [cdp.runtime.CallArgument(
956
- object_id=self._remote_object.object_id
957
- )]
958
- await self._tab.send(
959
- cdp.runtime.call_function_on(
960
- script,
961
- object_id=self._remote_object.object_id,
962
- arguments=arguments,
963
- await_promise=True,
964
- user_gesture=True,
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
  """