Skip to content

Commit 650978e

Browse files
authored
Merge pull request #3272 from seleniumbase/cdp-mode-patch-11
CDP Mode - Patch 11
2 parents 5ccee49 + 9fe83b0 commit 650978e

File tree

8 files changed

+111
-46
lines changed

8 files changed

+111
-46
lines changed

examples/cdp_mode/ReadMe.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@ sb.cdp.find_elements_by_text(text, tag_name=None)
310310
sb.cdp.select(selector)
311311
sb.cdp.select_all(selector)
312312
sb.cdp.find_elements(selector)
313+
sb.cdp.find_visible_elements(selector)
314+
sb.cdp.click_nth_element(selector, number)
315+
sb.cdp.click_nth_visible_element(selector, number)
313316
sb.cdp.click_link(link_text)
314317
sb.cdp.tile_windows(windows=None, max_columns=0)
315318
sb.cdp.get_all_cookies(*args, **kwargs)
@@ -325,7 +328,7 @@ sb.cdp.get_active_element_css()
325328
sb.cdp.click(selector)
326329
sb.cdp.click_active_element()
327330
sb.cdp.click_if_visible(selector)
328-
sb.cdp.click_visible_elements(selector)
331+
sb.cdp.click_visible_elements(selector, limit=0)
329332
sb.cdp.mouse_click(selector)
330333
sb.cdp.nested_click(parent_selector, selector)
331334
sb.cdp.get_nested_element(parent_selector, selector)

seleniumbase/__version__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.32.10"
2+
__version__ = "4.32.11"

seleniumbase/core/browser_launcher.py

+3
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,9 @@ def uc_open_with_cdp_mode(driver, url=None):
599599
cdp.select = CDPM.select
600600
cdp.select_all = CDPM.select_all
601601
cdp.find_elements = CDPM.find_elements
602+
cdp.find_visible_elements = CDPM.find_visible_elements
603+
cdp.click_nth_element = CDPM.click_nth_element
604+
cdp.click_nth_visible_element = CDPM.click_nth_visible_element
602605
cdp.click_link = CDPM.click_link
603606
cdp.tile_windows = CDPM.tile_windows
604607
cdp.get_all_cookies = CDPM.get_all_cookies

seleniumbase/core/log_helper.py

+12-18
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import shutil
44
import sys
55
import time
6+
from contextlib import suppress
67
from seleniumbase import config as sb_config
78
from seleniumbase.config import settings
89
from seleniumbase.fixtures import constants
@@ -281,14 +282,13 @@ def log_test_failure_data(test, test_logpath, driver, browser, url=None):
281282
sb_config._report_time = the_time
282283
sb_config._report_traceback = traceback_message
283284
sb_config._report_exception = exc_message
284-
try:
285+
with suppress(Exception):
285286
if not os.path.exists(test_logpath):
286287
os.makedirs(test_logpath)
287-
except Exception:
288-
pass
289-
log_file = codecs.open(basic_file_path, "w+", "utf-8")
290-
log_file.writelines("\r\n".join(data_to_save))
291-
log_file.close()
288+
with suppress(Exception):
289+
log_file = codecs.open(basic_file_path, "w+", encoding="utf-8")
290+
log_file.writelines("\r\n".join(data_to_save))
291+
log_file.close()
292292

293293

294294
def log_skipped_test_data(test, test_logpath, driver, browser, reason):
@@ -297,16 +297,12 @@ def log_skipped_test_data(test, test_logpath, driver, browser, reason):
297297
browser_version = None
298298
driver_version = None
299299
driver_name = None
300-
try:
300+
with suppress(Exception):
301301
browser_version = get_browser_version(driver)
302-
except Exception:
303-
pass
304-
try:
302+
with suppress(Exception):
305303
driver_name, driver_version = get_driver_name_and_version(
306304
driver, browser
307305
)
308-
except Exception:
309-
pass
310306
if browser_version:
311307
headless = ""
312308
if test.headless and browser in ["chrome", "edge", "firefox"]:
@@ -368,13 +364,11 @@ def log_page_source(test_logpath, driver, source=None):
368364
"unresponsive, or closed prematurely!</h4>"
369365
)
370366
)
371-
try:
367+
with suppress(Exception):
372368
if not os.path.exists(test_logpath):
373369
os.makedirs(test_logpath)
374-
except Exception:
375-
pass
376370
html_file_path = os.path.join(test_logpath, html_file_name)
377-
html_file = codecs.open(html_file_path, "w+", "utf-8")
371+
html_file = codecs.open(html_file_path, "w+", encoding="utf-8")
378372
html_file.write(page_source)
379373
html_file.close()
380374

@@ -543,15 +537,15 @@ def log_folder_setup(log_path, archive_logs=False):
543537
try:
544538
os.makedirs(log_path)
545539
except Exception:
546-
pass # Should only be reachable during multi-threaded runs
540+
pass # Only reachable during multi-threaded runs
547541
else:
548542
saved_folder = "%s/../%s/" % (log_path, constants.Logs.SAVED)
549543
archived_folder = os.path.realpath(saved_folder) + "/"
550544
if not os.path.exists(archived_folder):
551545
try:
552546
os.makedirs(archived_folder)
553547
except Exception:
554-
pass # Should only be reachable during multi-threaded runs
548+
pass # Only reachable during multi-threaded runs
555549
archived_logs = "%slogs_%s" % (archived_folder, int(time.time()))
556550
if len(os.listdir(log_path)) > 0:
557551
try:

seleniumbase/core/sb_cdp.py

+71-8
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,45 @@ def select_all(self, selector, timeout=settings.SMALL_TIMEOUT):
252252
def find_elements(self, selector, timeout=settings.SMALL_TIMEOUT):
253253
return self.select_all(selector, timeout=timeout)
254254

255+
def find_visible_elements(self, selector, timeout=settings.SMALL_TIMEOUT):
256+
visible_elements = []
257+
elements = self.select_all(selector, timeout=timeout)
258+
for element in elements:
259+
with suppress(Exception):
260+
position = element.get_position()
261+
if (position.width != 0 or position.height != 0):
262+
visible_elements.append(element)
263+
return visible_elements
264+
265+
def click_nth_element(self, selector, number):
266+
elements = self.select_all(selector)
267+
if len(elements) < number:
268+
raise Exception(
269+
"Not enough matching {%s} elements to "
270+
"click number %s!" % (selector, number)
271+
)
272+
number = number - 1
273+
if number < 0:
274+
number = 0
275+
element = elements[number]
276+
element.click()
277+
278+
def click_nth_visible_element(self, selector, number):
279+
"""Finds all matching page elements and clicks the nth visible one.
280+
Example: self.click_nth_visible_element('[type="checkbox"]', 5)
281+
(Clicks the 5th visible checkbox on the page.)"""
282+
elements = self.find_visible_elements(selector)
283+
if len(elements) < number:
284+
raise Exception(
285+
"Not enough matching {%s} elements to "
286+
"click number %s!" % (selector, number)
287+
)
288+
number = number - 1
289+
if number < 0:
290+
number = 0
291+
element = elements[number]
292+
element.click()
293+
255294
def click_link(self, link_text):
256295
self.find_elements_by_text(link_text, "a")[0].click()
257296

@@ -479,18 +518,36 @@ def click_if_visible(self, selector):
479518
self.__slow_mode_pause_if_set()
480519
self.loop.run_until_complete(self.page.wait())
481520

482-
def click_visible_elements(self, selector):
521+
def click_visible_elements(self, selector, limit=0):
522+
"""Finds all matching page elements and clicks visible ones in order.
523+
If a click reloads or opens a new page, the clicking will stop.
524+
If no matching elements appear, an Exception will be raised.
525+
If "limit" is set and > 0, will only click that many elements.
526+
Also clicks elements that become visible from previous clicks.
527+
Works best for actions such as clicking all checkboxes on a page.
528+
Example: self.click_visible_elements('input[type="checkbox"]')"""
483529
elements = self.select_all(selector)
530+
click_count = 0
484531
for element in elements:
532+
if limit and limit > 0 and click_count >= limit:
533+
return
485534
try:
486-
position = element.get_position()
487-
if (position.width != 0 or position.height != 0):
535+
width = 0
536+
height = 0
537+
try:
538+
position = element.get_position()
539+
width = position.width
540+
height = position.height
541+
except Exception:
542+
continue
543+
if (width != 0 or height != 0):
488544
element.click()
545+
click_count += 1
489546
time.sleep(0.0375)
490547
self.__slow_mode_pause_if_set()
491548
self.loop.run_until_complete(self.page.wait())
492549
except Exception:
493-
pass
550+
break
494551

495552
def mouse_click(self, selector, timeout=settings.SMALL_TIMEOUT):
496553
"""(Attempt simulating a mouse click)"""
@@ -1238,6 +1295,7 @@ def gui_drag_drop_points(self, x1, y1, x2, y2, timeframe=0.35):
12381295

12391296
def gui_drag_and_drop(self, drag_selector, drop_selector, timeframe=0.35):
12401297
self.__slow_mode_pause_if_set()
1298+
self.bring_active_window_to_front()
12411299
x1, y1 = self.get_gui_element_center(drag_selector)
12421300
self.__add_light_pause()
12431301
x2, y2 = self.get_gui_element_center(drop_selector)
@@ -1327,17 +1385,22 @@ def gui_hover_x_y(self, x, y, timeframe=0.25):
13271385

13281386
def gui_hover_element(self, selector, timeframe=0.25):
13291387
self.__slow_mode_pause_if_set()
1330-
x, y = self.get_gui_element_center(selector)
1331-
self.__add_light_pause()
1332-
self.__gui_hover_x_y(x, y, timeframe=timeframe)
1333-
self.__slow_mode_pause_if_set()
1388+
element_rect = self.get_gui_element_rect(selector)
1389+
width = element_rect["width"]
1390+
height = element_rect["height"]
1391+
if width > 0 and height > 0:
1392+
x, y = self.get_gui_element_center(selector)
1393+
self.bring_active_window_to_front()
1394+
self.__gui_hover_x_y(x, y, timeframe=timeframe)
1395+
self.__slow_mode_pause_if_set()
13341396
self.loop.run_until_complete(self.page.wait())
13351397

13361398
def gui_hover_and_click(self, hover_selector, click_selector):
13371399
gui_lock = fasteners.InterProcessLock(
13381400
constants.MultiBrowser.PYAUTOGUILOCK
13391401
)
13401402
with gui_lock:
1403+
self.bring_active_window_to_front()
13411404
self.gui_hover_element(hover_selector)
13421405
time.sleep(0.15)
13431406
self.gui_hover_element(click_selector)

seleniumbase/fixtures/base_case.py

+17-15
Original file line numberDiff line numberDiff line change
@@ -2166,7 +2166,6 @@ def find_elements(self, selector, by="css selector", limit=0):
21662166
if limit and limit > 0 and len(elements) > limit:
21672167
elements = elements[:limit]
21682168
return elements
2169-
21702169
self.wait_for_ready_state_complete()
21712170
time.sleep(0.05)
21722171
elements = self.driver.find_elements(by=by, value=selector)
@@ -2178,6 +2177,11 @@ def find_visible_elements(self, selector, by="css selector", limit=0):
21782177
"""Returns a list of matching WebElements that are visible.
21792178
If "limit" is set and > 0, will only return that many elements."""
21802179
selector, by = self.__recalculate_selector(selector, by)
2180+
if self.__is_cdp_swap_needed():
2181+
elements = self.cdp.find_visible_elements(selector)
2182+
if limit and limit > 0 and len(elements) > limit:
2183+
elements = elements[:limit]
2184+
return elements
21812185
self.wait_for_ready_state_complete()
21822186
time.sleep(0.05)
21832187
return page_actions.find_visible_elements(
@@ -2201,7 +2205,7 @@ def click_visible_elements(
22012205
timeout = self.__get_new_timeout(timeout)
22022206
selector, by = self.__recalculate_selector(selector, by)
22032207
if self.__is_cdp_swap_needed():
2204-
self.cdp.click_visible_elements(selector)
2208+
self.cdp.click_visible_elements(selector, limit)
22052209
return
22062210
self.wait_for_ready_state_complete()
22072211
if self.__needs_minimum_wait():
@@ -2283,13 +2287,16 @@ def click_nth_visible_element(
22832287
):
22842288
"""Finds all matching page elements and clicks the nth visible one.
22852289
Example: self.click_nth_visible_element('[type="checkbox"]', 5)
2286-
(Clicks the 5th visible checkbox on the page.)"""
2290+
(Clicks the 5th visible checkbox on the page.)"""
22872291
self.__check_scope()
22882292
if not timeout:
22892293
timeout = settings.SMALL_TIMEOUT
22902294
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
22912295
timeout = self.__get_new_timeout(timeout)
22922296
selector, by = self.__recalculate_selector(selector, by)
2297+
if self.__is_cdp_swap_needed():
2298+
self.cdp.click_nth_visible_element(selector, number)
2299+
return
22932300
self.wait_for_ready_state_complete()
22942301
self.wait_for_element_present(selector, by=by, timeout=timeout)
22952302
elements = self.find_visible_elements(selector, by=by)
@@ -2897,6 +2904,9 @@ def drag_and_drop(
28972904
drop_selector, drop_by = self.__recalculate_selector(
28982905
drop_selector, drop_by
28992906
)
2907+
if self.__is_cdp_swap_needed():
2908+
self.cdp.gui_drag_and_drop(drag_selector, drop_selector)
2909+
return
29002910
drag_element = self.wait_for_element_clickable(
29012911
drag_selector, by=drag_by, timeout=timeout
29022912
)
@@ -15435,7 +15445,8 @@ def __get_test_id(self):
1543515445
elif hasattr(self, "_using_sb_fixture") and self._using_sb_fixture:
1543615446
test_id = sb_config._latest_display_id
1543715447
test_id = test_id.replace(".py::", ".").replace("::", ".")
15438-
test_id = test_id.replace("/", ".").replace(" ", "_")
15448+
test_id = test_id.replace("/", ".").replace("\\", ".")
15449+
test_id = test_id.replace(" ", "_")
1543915450
# Linux filename length limit for `codecs.open(filename)` = 255
1544015451
# 255 - len("latest_logs/") - len("/basic_test_info.txt") = 223
1544115452
if len(test_id) <= 223:
@@ -16132,11 +16143,7 @@ def tearDown(self):
1613216143
# This test already called tearDown()
1613316144
return
1613416145
if hasattr(self, "recorder_mode") and self.recorder_mode:
16135-
if self.undetectable:
16136-
try:
16137-
self.driver.window_handles
16138-
except Exception:
16139-
self.driver.connect()
16146+
page_actions._reconnect_if_disconnected(self.driver)
1614016147
try:
1614116148
self.__process_recorded_actions()
1614216149
except Exception as e:
@@ -16177,12 +16184,7 @@ def tearDown(self):
1617716184
)
1617816185
raise Exception(message)
1617916186
# *** Start tearDown() officially ***
16180-
if self.undetectable:
16181-
try:
16182-
self.driver.window_handles
16183-
except Exception:
16184-
with suppress(Exception):
16185-
self.driver.connect()
16187+
page_actions._reconnect_if_disconnected(self.driver)
1618616188
self.__slow_mode_pause_if_active()
1618716189
has_exception = self.__has_exception()
1618816190
sb_config._has_exception = has_exception

seleniumbase/fixtures/constants.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ class Mobile:
376376
class UC:
377377
RECONNECT_TIME = 2.4 # Seconds
378378
CDP_MODE_OPEN_WAIT = 0.9 # Seconds
379-
EXTRA_WINDOWS_WAIT = 0.2 # Seconds
379+
EXTRA_WINDOWS_WAIT = 0.3 # Seconds
380380

381381

382382
class ValidBrowsers:

seleniumbase/plugins/sb_manager.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1202,7 +1202,7 @@ def SB(
12021202
from seleniumbase.core import download_helper
12031203
from seleniumbase.core import proxy_helper
12041204

1205-
log_helper.log_folder_setup(constants.Logs.LATEST + "/")
1205+
log_helper.log_folder_setup(constants.Logs.LATEST + os.sep)
12061206
log_helper.clear_empty_logs()
12071207
download_helper.reset_downloads_folder()
12081208
if not sb_config.multi_proxy:
@@ -1228,7 +1228,7 @@ def SB(
12281228
the_traceback = traceback.format_exc().strip()
12291229
try:
12301230
p2 = the_traceback.split(', in ')[1].split('", line ')[0]
1231-
filename = p2.split("/")[-1]
1231+
filename = p2.split(os.sep)[-1]
12321232
sb.cm_filename = filename
12331233
except Exception:
12341234
sb.cm_filename = None

0 commit comments

Comments
 (0)