Skip to content

Commit f4c3f24

Browse files
authored
Merge pull request #3317 from seleniumbase/mostly-pytest-updates
Mostly `pytest` updates
2 parents c909b1a + 13983a9 commit f4c3f24

File tree

18 files changed

+254
-29
lines changed

18 files changed

+254
-29
lines changed

examples/cdp_mode/raw_radwell.py

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from seleniumbase import SB
2+
3+
with SB(uc=True, test=True, locale_code="en", incognito=True) as sb:
4+
url = "https://www.radwell.com/en-US/Search/Advanced/"
5+
sb.activate_cdp_mode(url)
6+
sb.sleep(3)
7+
sb.cdp.press_keys("form#basicsearch input", "821C-PM-111DA-142")
8+
sb.sleep(1)
9+
sb.cdp.click('[value="Search Icon"]')
10+
sb.sleep(3)
11+
sb.cdp.assert_text("MAC VALVES INC", "a.manufacturer-link")
12+
sb.cdp.highlight("a.manufacturer-link")
13+
sb.sleep(1)

examples/cdp_mode/raw_tiktok.py

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from seleniumbase import SB
2+
3+
with SB(
4+
uc=True, test=True, locale_code="en", incognito=True, ad_block=True
5+
) as sb:
6+
url = "https://www.tiktok.com/@startrek?lang=en"
7+
sb.activate_cdp_mode(url)
8+
sb.sleep(2.5)
9+
sb.cdp.click('button:contains("Refresh")')
10+
sb.sleep(1.5)
11+
print(sb.cdp.get_text('h2[data-e2e="user-bio"]'))
12+
for i in range(55):
13+
sb.cdp.scroll_down(12)
14+
sb.sleep(0.06)
15+
sb.sleep(1)

examples/cdp_mode/raw_united.py

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
from seleniumbase import SB
2+
3+
with SB(uc=True, test=True, locale_code="en", ad_block=True) as sb:
4+
url = "https://www.united.com/en/us"
5+
sb.activate_cdp_mode(url)
6+
sb.sleep(2.5)
7+
origin_input = 'input[placeholder="Origin"]'
8+
origin = "Boston, MA"
9+
destination_input = 'input[placeholder="Destination"]'
10+
destination = "San Diego, CA"
11+
sb.cdp.gui_click_element(origin_input)
12+
sb.sleep(1.2)
13+
sb.cdp.type(origin_input, origin)
14+
sb.sleep(1.2)
15+
sb.cdp.click('strong:contains("%s")' % origin)
16+
sb.sleep(1.2)
17+
sb.cdp.gui_click_element(destination_input)
18+
sb.sleep(1.2)
19+
sb.cdp.type(destination_input, destination)
20+
sb.sleep(1.2)
21+
sb.cdp.click('strong:contains("%s")' % destination)
22+
sb.sleep(1.2)
23+
sb.cdp.click('button[aria-label="Find flights"]')
24+
sb.sleep(6)
25+
flights = sb.find_elements('div[class*="CardContainer__block"]')
26+
print("**** Flights from %s to %s ****" % (origin, destination))
27+
if not flights:
28+
print("* No flights found!")
29+
for flight in flights:
30+
print("* " + flight.text.split(" Destination")[0])
31+
sb.sleep(1.5)

examples/master_qa/pytest.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[pytest]
22

33
# Display console output. Disable cacheprovider:
4-
addopts = --capture=no -p no:cacheprovider
4+
addopts = --capture=tee-sys -p no:cacheprovider
55

66
# Skip these directories during test collection:
77
norecursedirs = .* build dist recordings temp assets

examples/migration/raw_selenium/pytest.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[pytest]
22

33
# Display console output. Disable cacheprovider:
4-
addopts = --capture=no -p no:cacheprovider
4+
addopts = --capture=tee-sys -p no:cacheprovider
55

66
# Skip these directories during test collection:
77
norecursedirs = .* build dist recordings temp assets

examples/pytest.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[pytest]
22

33
# Display console output. Disable cacheprovider:
4-
addopts = --capture=no -p no:cacheprovider
4+
addopts = --capture=tee-sys -p no:cacheprovider
55

66
# Skip these directories during test collection:
77
norecursedirs = .* build dist recordings temp assets

examples/translations/pytest.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[pytest]
22

33
# Display console output. Disable cacheprovider:
4-
addopts = --capture=no -p no:cacheprovider
4+
addopts = --capture=tee-sys -p no:cacheprovider
55

66
# Skip these directories during test collection:
77
norecursedirs = .* build dist recordings temp assets

help_docs/method_summary.md

+42-6
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,44 @@ self._print(TEXT) # Calls Python's print() / Allows for translations
970970

971971
############
972972

973+
# **** UC Mode methods. (uc=True / --uc) ****
974+
975+
# (Mainly for CDP Mode) - (For all CDP methods, see the CDP Mode Docs)
976+
977+
self.activate_cdp_mode(url=None) # Activate CDP Mode on the given URL
978+
979+
self.reconnect(timeout=0.1) # disconnect() + sleep(timeout) + connect()
980+
981+
self.disconnect() # Stops the webdriver service to prevent detection
982+
983+
self.connect() # Starts the webdriver service to allow actions again
984+
985+
# (For regular UC Mode)
986+
987+
self.uc_open(url) # (Open in same tab with default reconnect_time)
988+
989+
self.uc_open_with_tab(url) # (New tab with default reconnect_time)
990+
991+
self.uc_open_with_reconnect(url, reconnect_time=None) # (New tab)
992+
993+
self.uc_open_with_disconnect(url, timeout=None) # New tab + sleep()
994+
995+
self.uc_click(selector) # A stealthy click for evading bot-detection
996+
997+
self.uc_gui_press_key(key) # Use PyAutoGUI to press the keyboard key
998+
999+
self.uc_gui_press_keys(keys) # Use PyAutoGUI to press a list of keys
1000+
1001+
self.uc_gui_write(text) # Similar to uc_gui_press_keys(), but faster
1002+
1003+
self.uc_gui_click_x_y(x, y, timeframe=0.25) # PyAutoGUI click screen
1004+
1005+
self.uc_gui_click_captcha(frame="iframe", retry=False, blind=False)
1006+
1007+
self.uc_gui_handle_captcha(frame="iframe")
1008+
1009+
############
1010+
9731011
# "driver"-specific methods added (or modified) by SeleniumBase
9741012

9751013
driver.default_get(url) # Because driver.get(url) works differently in UC Mode
@@ -1076,7 +1114,9 @@ driver.uc_open_with_reconnect(url, reconnect_time=None) # (New tab)
10761114

10771115
driver.uc_open_with_disconnect(url, timeout=None) # New tab + sleep()
10781116

1079-
driver.reconnect(timeout) # disconnect() + sleep(timeout) + connect()
1117+
driver.uc_activate_cdp_mode(url=None) # Activate CDP Mode on the given URL
1118+
1119+
driver.reconnect(timeout=0.1) # disconnect() + sleep(timeout) + connect()
10801120

10811121
driver.disconnect() # Stops the webdriver service to prevent detection
10821122

@@ -1093,12 +1133,8 @@ driver.uc_gui_write(text) # Similar to uc_gui_press_keys(), but faster
10931133
driver.uc_gui_click_x_y(x, y, timeframe=0.25) # PyAutoGUI click screen
10941134

10951135
driver.uc_gui_click_captcha(frame="iframe", retry=False, blind=False)
1096-
# driver.uc_gui_click_cf(frame="iframe", retry=False, blind=False)
1097-
# driver.uc_gui_click_rc(frame="iframe", retry=False, blind=False)
10981136

1099-
driver.uc_gui_handle_captcha(frame="iframe") # (Auto-detects the CAPTCHA)
1100-
# driver.uc_gui_handle_cf(frame="iframe") # PyAutoGUI click CF Turnstile
1101-
# driver.uc_gui_handle_rc(frame="iframe") # PyAutoGUI click G. reCAPTCHA
1137+
driver.uc_gui_handle_captcha(frame="iframe")
11021138
```
11031139

11041140
--------

help_docs/recorder_mode.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ sbase print ./recordings/TEST_NAME_rec.py -n
7575
cp ./recordings/TEST_NAME_rec.py ./TEST_NAME.py
7676
```
7777
78-
The first command creates a boilerplate test with a breakpoint; the second command runs the test with the Recorder activated; the third command prints the completed test to the console; and the fourth command replaces the initial boilerplate with the completed test. If you're just experimenting with the Recorder, you can run the second command as many times as you want, and it'll override previous recordings saved to ``./recordings/TEST_NAME_rec.py``. (Note that ``-s`` is needed to allow breakpoints, unless you already have a ``pytest.ini`` file present with ``addopts = --capture=no`` in it. The ``-q`` is optional, which shortens ``pytest`` console output.)
78+
The first command creates a boilerplate test with a breakpoint; the second command runs the test with the Recorder activated; the third command prints the completed test to the console; and the fourth command replaces the initial boilerplate with the completed test. If you're just experimenting with the Recorder, you can run the second command as many times as you want, and it'll override previous recordings saved to ``./recordings/TEST_NAME_rec.py``. (Note that ``-s`` is needed to allow breakpoints, unless you already have a ``pytest.ini`` file present where you set it. The ``-q`` is optional, which shortens ``pytest`` console output.)
7979
8080
⏺️ You can also use the Recorder to add code to an existing test. To do that, you'll first need to create a breakpoint in your code to insert manual browser actions:
8181

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ packages = [
5656
]
5757

5858
[tool.pytest.ini_options]
59-
addopts = ["--capture=no", "-p no:cacheprovider"]
59+
addopts = ["--capture=tee-sys", "-p no:cacheprovider"]
6060
norecursedirs = [".*", "build", "dist", "recordings", "temp", "assets"]
6161
filterwarnings = [
6262
"ignore::pytest.PytestWarning",

pytest.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[pytest]
22

33
# Display console output. Disable cacheprovider:
4-
addopts = --capture=no -p no:cacheprovider
4+
addopts = --capture=tee-sys -p no:cacheprovider
55

66
# Skip these directories during test collection:
77
norecursedirs = .* build dist recordings temp assets

requirements.txt

+2-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pynose>=1.5.3
1515
platformdirs>=4.3.6
1616
typing-extensions>=4.12.2
1717
sbvirtualdisplay>=1.3.0
18-
six>=1.16.0
18+
six>=1.17.0
1919
parse>=1.20.2
2020
parse-type>=0.6.4
2121
colorama>=0.4.6
@@ -43,9 +43,8 @@ sortedcontainers==2.4.0
4343
execnet==2.1.1
4444
iniconfig==2.0.0
4545
pluggy==1.5.0
46-
py==1.11.0
4746
pytest==8.3.4
48-
pytest-html==2.0.1
47+
pytest-html==4.1.1
4948
pytest-metadata==3.1.1
5049
pytest-ordering==0.6
5150
pytest-rerunfailures==14.0;python_version<"3.9"

seleniumbase/__version__.py

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

seleniumbase/console_scripts/sb_mkdir.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def main():
120120

121121
data = []
122122
data.append("[pytest]")
123-
data.append("addopts = --capture=no -p no:cacheprovider")
123+
data.append("addopts = --capture=tee-sys -p no:cacheprovider")
124124
data.append("norecursedirs = .* build dist recordings temp assets")
125125
data.append("filterwarnings =")
126126
data.append(" ignore::pytest.PytestWarning")

seleniumbase/core/browser_launcher.py

+14
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,13 @@ def log_d(message):
9797
print(message)
9898

9999

100+
def make_writable(file_path):
101+
# Set permissions to: "If you can read it, you can write it."
102+
mode = os.stat(file_path).st_mode
103+
mode |= (mode & 0o444) >> 1 # copy R bits to W
104+
os.chmod(file_path, mode)
105+
106+
100107
def make_executable(file_path):
101108
# Set permissions to: "If you can read it, you can execute it."
102109
mode = os.stat(file_path).st_mode
@@ -815,6 +822,13 @@ def install_pyautogui_if_missing(driver):
815822
pip_find_lock = fasteners.InterProcessLock(
816823
constants.PipInstall.FINDLOCK
817824
)
825+
try:
826+
with pip_find_lock:
827+
pass
828+
except Exception:
829+
# Need write permissions
830+
with suppress(Exception):
831+
make_writable(constants.PipInstall.FINDLOCK)
818832
with pip_find_lock: # Prevent issues with multiple processes
819833
try:
820834
import pyautogui

seleniumbase/fixtures/base_case.py

+9
Original file line numberDiff line numberDiff line change
@@ -13983,6 +13983,15 @@ def __activate_virtual_display_as_needed(self):
1398313983
pip_find_lock = fasteners.InterProcessLock(
1398413984
constants.PipInstall.FINDLOCK
1398513985
)
13986+
try:
13987+
with pip_find_lock:
13988+
pass
13989+
except Exception:
13990+
# Need write permissions
13991+
with suppress(Exception):
13992+
mode = os.stat(constants.PipInstall.FINDLOCK).st_mode
13993+
mode |= (mode & 0o444) >> 1 # copy R bits to W
13994+
os.chmod(constants.PipInstall.FINDLOCK, mode)
1398613995
with pip_find_lock: # Prevent issues with multiple processes
1398713996
if self.undetectable and not (self.headless or self.headless2):
1398813997
import Xlib.display

0 commit comments

Comments
 (0)