Skip to content

Commit e6d35b4

Browse files
committed
Refactoring
1 parent 5fd88b1 commit e6d35b4

File tree

5 files changed

+102
-66
lines changed

5 files changed

+102
-66
lines changed

seleniumbase/common/exceptions.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
""" SeleniumBase Exceptions
22
NoSuchFileException => Called when self.assert_downloaded_file(...) fails.
33
NotUsingChromeException => Used by Chrome-only methods if not using Chrome.
4+
NotUsingChromiumException => Used by Chromium-only methods if not Chromium.
45
OutOfScopeException => Used by BaseCase methods when setUp() is skipped.
56
TextNotVisibleException => Called when expected text fails to appear.
67
TimeLimitExceededException => Called when exceeding "--time-limit=SECONDS".
@@ -16,6 +17,10 @@ class NotUsingChromeException(Exception):
1617
pass
1718

1819

20+
class NotUsingChromiumException(Exception):
21+
pass
22+
23+
1924
class OutOfScopeException(Exception):
2025
pass
2126

seleniumbase/core/browser_launcher.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import time
1010
import urllib3
1111
import warnings
12+
import zipfile
1213
from selenium import webdriver
1314
from selenium.webdriver.chrome.service import Service as ChromeService
1415
from selenium.webdriver.edge.service import Service as EdgeService
@@ -351,8 +352,6 @@ def _unzip_to_new_folder(zip_file, folder):
351352
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
352353
with proxy_dir_lock:
353354
if not os.path.exists(folder):
354-
import zipfile
355-
356355
zip_ref = zipfile.ZipFile(zip_file, "r")
357356
os.makedirs(folder)
358357
zip_ref.extractall(folder)

seleniumbase/fixtures/base_case.py

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ def test_anything(self):
6161
from seleniumbase import config as sb_config
6262
from seleniumbase.__version__ import __version__
6363
from seleniumbase.common import decorators
64+
from seleniumbase.common.exceptions import (
65+
NotUsingChromeException,
66+
NotUsingChromiumException,
67+
OutOfScopeException,
68+
VisualException,
69+
)
6470
from seleniumbase.config import settings
6571
from seleniumbase.core import download_helper
6672
from seleniumbase.core import log_helper
@@ -6493,6 +6499,12 @@ def get_browser_downloads_folder(self):
64936499
and self.headless
64946500
):
64956501
return os.path.join(os.path.expanduser("~"), "downloads")
6502+
elif (
6503+
self.driver.capabilities["browserName"].lower() == "chrome"
6504+
and int(self.get_chromedriver_version().split(".")[0]) >= 110
6505+
and self.headless
6506+
):
6507+
return os.path.abspath(".")
64966508
else:
64976509
return download_helper.get_downloads_folder()
64986510
return os.path.join(os.path.expanduser("~"), "downloads")
@@ -7092,17 +7104,27 @@ def __fail_if_not_using_chrome(self, method):
70927104
if browser_name.lower() == "chrome":
70937105
chrome = True
70947106
if not chrome:
7095-
from seleniumbase.common.exceptions import NotUsingChromeException
7096-
70977107
message = (
7098-
'Error: "%s" should only be called '
7099-
'by tests running with self.browser == "chrome"! '
7108+
'Error: "%s" should only be called by tests '
7109+
'running with "--browser=chrome" / "--chrome"! '
71007110
'You should add an "if" statement to your code before calling '
71017111
"this method if using browsers that are Not Chrome! "
71027112
'The browser detected was: "%s".' % (method, browser_name)
71037113
)
71047114
raise NotUsingChromeException(message)
71057115

7116+
def __fail_if_not_using_chromium(self, method):
7117+
browser_name = self.driver.capabilities["browserName"]
7118+
if not self.is_chromium():
7119+
message = (
7120+
'Error: "%s" should only be called by tests '
7121+
'running with a Chromium browser! (Chrome or Edge) '
7122+
'You should add an "if" statement to your code before calling '
7123+
"this method if using browsers that are Not Chromium! "
7124+
'The browser detected was: "%s".' % (method, browser_name)
7125+
)
7126+
raise NotUsingChromiumException(message)
7127+
71067128
def get_chrome_version(self):
71077129
self.__check_scope()
71087130
self.__fail_if_not_using_chrome("get_chrome_version()")
@@ -7113,6 +7135,11 @@ def get_chrome_version(self):
71137135
chrome_version = driver_capabilities["browserVersion"]
71147136
return chrome_version
71157137

7138+
def get_chromium_version(self):
7139+
self.__check_scope()
7140+
self.__fail_if_not_using_chromium("get_chromium_version()")
7141+
return self.__get_major_browser_version()
7142+
71167143
def get_chromedriver_version(self):
71177144
self.__check_scope()
71187145
self.__fail_if_not_using_chrome("get_chromedriver_version()")
@@ -7121,14 +7148,19 @@ def get_chromedriver_version(self):
71217148
chromedriver_version = chromedriver_version.split(" ")[0]
71227149
return chromedriver_version
71237150

7124-
def is_chromedriver_too_old(self):
7125-
"""Before chromedriver 73, there was no version check, which
7126-
means it's possible to run a new Chrome with old drivers."""
7151+
def get_chromium_driver_version(self):
71277152
self.__check_scope()
7128-
self.__fail_if_not_using_chrome("is_chromedriver_too_old()")
7129-
if int(self.get_chromedriver_version().split(".")[0]) < 73:
7130-
return True # chromedriver is too old! Please upgrade!
7131-
return False
7153+
self.__fail_if_not_using_chromium("get_chromium_version()")
7154+
driver_version = None
7155+
if "chrome" in self.driver.capabilities:
7156+
chrome_dict = self.driver.capabilities["chrome"]
7157+
driver_version = chrome_dict["chromedriverVersion"]
7158+
driver_version = driver_version.split(" ")[0]
7159+
elif "msedge" in self.driver.capabilities:
7160+
edge_dict = self.driver.capabilities["msedge"]
7161+
driver_version = edge_dict["msedgedriverVersion"]
7162+
driver_version = driver_version.split(" ")[0]
7163+
return driver_version
71327164

71337165
def get_mfa_code(self, totp_key=None):
71347166
"""Same as get_totp_code() and get_google_auth_password().
@@ -9307,8 +9339,6 @@ def __assert_eq(self, *args, **kwargs):
93079339
elif line.strip().startswith("*"):
93089340
minified_exception += line + "\n"
93099341
if minified_exception:
9310-
from seleniumbase.common.exceptions import VisualException
9311-
93129342
raise VisualException(minified_exception)
93139343

93149344
def _process_visual_baseline_logs(self):
@@ -9688,8 +9718,6 @@ def __check_scope(self):
96889718
if hasattr(self, "browser"): # self.browser stores the type of browser
96899719
return # All good: setUp() already initialized variables in "self"
96909720
else:
9691-
from seleniumbase.common.exceptions import OutOfScopeException
9692-
96939721
message = (
96949722
"\n It looks like you are trying to call a SeleniumBase method"
96959723
"\n from outside the scope of your test class's `self` object,"
@@ -12734,9 +12762,18 @@ def __disable_beforeunload_as_needed(self):
1273412762

1273512763
############
1273612764

12765+
@decorators.deprecated("The Driver Manager prevents old drivers.")
12766+
def is_chromedriver_too_old(self):
12767+
"""Before chromedriver 73, there was no version check, which
12768+
means it's possible to run a new Chrome with old drivers."""
12769+
self.__fail_if_not_using_chrome("is_chromedriver_too_old()")
12770+
if int(self.get_chromedriver_version().split(".")[0]) < 73:
12771+
return True # chromedriver is too old! Please upgrade!
12772+
return False
12773+
1273712774
@decorators.deprecated("You should use re.escape() instead.")
1273812775
def jq_format(self, code):
12739-
# DEPRECATED - re.escape() already performs the intended action.
12776+
# DEPRECATED - re.escape() already performs this action.
1274012777
return js_utils._jq_format(code)
1274112778

1274212779
############

seleniumbase/fixtures/page_utils.py

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,7 @@ def get_domain_url(url):
2424

2525

2626
def is_xpath_selector(selector):
27-
"""
28-
A basic method to determine if a selector is an xpath selector.
29-
"""
27+
"""Determine if a selector is an xpath selector."""
3028
if (
3129
selector.startswith("/")
3230
or selector.startswith("./")
@@ -37,9 +35,7 @@ def is_xpath_selector(selector):
3735

3836

3937
def is_link_text_selector(selector):
40-
"""
41-
A basic method to determine if a selector is a link text selector.
42-
"""
38+
"""Determine if a selector is a link text selector."""
4339
if (
4440
selector.startswith("link=")
4541
or selector.startswith("link_text=")
@@ -50,9 +46,7 @@ def is_link_text_selector(selector):
5046

5147

5248
def is_partial_link_text_selector(selector):
53-
"""
54-
A basic method to determine if a selector is a partial link text selector.
55-
"""
49+
"""Determine if a selector is a partial link text selector."""
5650
if (
5751
selector.startswith("partial_link=")
5852
or selector.startswith("partial_link_text=")
@@ -66,18 +60,14 @@ def is_partial_link_text_selector(selector):
6660

6761

6862
def is_name_selector(selector):
69-
"""
70-
A basic method to determine if a selector is a name selector.
71-
"""
63+
"""Determine if a selector is a name selector."""
7264
if selector.startswith("name=") or selector.startswith("&"):
7365
return True
7466
return False
7567

7668

7769
def get_link_text_from_selector(selector):
78-
"""
79-
A basic method to get the link text from a link text selector.
80-
"""
70+
"""Get the link text from a link text selector."""
8171
if selector.startswith("link="):
8272
return selector[len("link="):]
8373
elif selector.startswith("link_text="):
@@ -88,9 +78,7 @@ def get_link_text_from_selector(selector):
8878

8979

9080
def get_partial_link_text_from_selector(selector):
91-
"""
92-
A basic method to get the partial link text from a partial link selector.
93-
"""
81+
"""Get the partial link text from a partial link selector."""
9482
if selector.startswith("partial_link="):
9583
return selector[len("partial_link="):]
9684
elif selector.startswith("partial_link_text="):
@@ -107,9 +95,7 @@ def get_partial_link_text_from_selector(selector):
10795

10896

10997
def get_name_from_selector(selector):
110-
"""
111-
A basic method to get the name from a name selector.
112-
"""
98+
"""Get the name from a name selector."""
11399
if selector.startswith("name="):
114100
return selector[len("name="):]
115101
if selector.startswith("&"):
@@ -143,8 +129,7 @@ def is_valid_url(url):
143129

144130

145131
def _get_unique_links(page_url, soup):
146-
"""
147-
Returns all unique links.
132+
"""Returns all unique links.
148133
Includes:
149134
"a"->"href", "img"->"src", "link"->"href", and "script"->"src" links.
150135
"""
@@ -225,8 +210,7 @@ def _get_link_status_code(
225210
If the timeout is exceeded, will return a 404.
226211
If "verify" is False, will ignore certificate errors.
227212
For a list of available status codes, see:
228-
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes
229-
"""
213+
https://en.wikipedia.org/wiki/List_of_HTTP_status_codes """
230214
status_code = None
231215
try:
232216
response = requests.head(
@@ -246,8 +230,7 @@ def _print_unique_links_with_status_codes(page_url, soup):
246230
and then prints out those links with their status codes.
247231
Format: ["link" -> "status_code"] (per line)
248232
Page links include those obtained from:
249-
"a"->"href", "img"->"src", "link"->"href", and "script"->"src".
250-
"""
233+
"a"->"href", "img"->"src", "link"->"href", and "script"->"src". """
251234
links = _get_unique_links(page_url, soup)
252235
for link in links:
253236
status_code = _get_link_status_code(link)

seleniumbase/undetected/patcher.py

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import string
88
import sys
99
import time
10+
import zipfile
1011

1112
logger = logging.getLogger(__name__)
1213
IS_POSIX = sys.platform.startswith(("darwin", "cygwin", "linux"))
@@ -137,8 +138,6 @@ def unzip_package(self, fp):
137138
"""
138139
:return: path to unpacked executable
139140
"""
140-
import zipfile
141-
142141
logger.debug("unzipping %s" % fp)
143142
try:
144143
os.unlink(self.zip_path)
@@ -147,12 +146,19 @@ def unzip_package(self, fp):
147146
os.makedirs(self.zip_path, mode=0o755, exist_ok=True)
148147
with zipfile.ZipFile(fp, mode="r") as zf:
149148
zf.extract(self.exe_name, self.zip_path)
150-
os.rename(
151-
os.path.join(self.zip_path, self.exe_name), self.executable_path
152-
)
153-
os.remove(fp)
154-
os.rmdir(self.zip_path)
155-
os.chmod(self.executable_path, 0o755)
149+
try:
150+
os.rename(
151+
os.path.join(self.zip_path, self.exe_name),
152+
self.executable_path,
153+
)
154+
os.remove(fp)
155+
except PermissionError:
156+
pass
157+
try:
158+
os.rmdir(self.zip_path)
159+
os.chmod(self.executable_path, 0o755)
160+
except PermissionError:
161+
pass
156162
return self.executable_path
157163

158164
@staticmethod
@@ -170,8 +176,6 @@ def force_kill_instances(exe_name):
170176

171177
@staticmethod
172178
def gen_random_cdc():
173-
import string
174-
175179
cdc = random.choices(string.ascii_lowercase, k=26)
176180
cdc[-6:-4] = map(str.upper, cdc[-6:-4])
177181
cdc[2] = cdc[0]
@@ -220,32 +224,40 @@ def gen_call_function_js_cache_name(match):
220224
fh.write(file_bin)
221225
return True
222226

223-
def is_binary_patched_new(self, executable_path=None):
227+
@staticmethod
228+
def gen_random_cdc_beta():
229+
cdc = random.choices(string.ascii_letters, k=27)
230+
return "".join(cdc).encode()
231+
232+
def is_binary_patched_beta(self, executable_path=None):
224233
executable_path = executable_path or self.executable_path
225-
with io.open(executable_path, "rb") as fh:
226-
return fh.read().find(b"undetected chromedriver") != -1
234+
try:
235+
with io.open(executable_path, "rb") as fh:
236+
return fh.read().find(b"undetected chromedriver") != -1
237+
except FileNotFoundError:
238+
return False
227239

228-
def patch_exe_new(self):
229-
logger.info("patching driver executable %s" % self.executable_path)
240+
def patch_exe_beta(self):
230241
with io.open(self.executable_path, "r+b") as fh:
231242
content = fh.read()
232-
match_injected_codeblock = re.search(rb"{window.*;}", content)
243+
match_injected_codeblock = re.search(
244+
rb"\{window\.cdc.*?;\}", content
245+
)
246+
target_bytes = None
233247
if match_injected_codeblock:
234248
target_bytes = match_injected_codeblock[0]
235249
new_target_bytes = (
236-
b'{console.log("undetected chromedriver 1337!")}'.ljust(
250+
b'{console.log("chromedriver is undetectable!")}'.ljust(
237251
len(target_bytes), b" "
238252
)
239253
)
254+
if target_bytes:
240255
new_content = content.replace(target_bytes, new_target_bytes)
241256
if new_content == content:
242-
pass # Failure to patch driver
257+
pass # Unable to patch driver
243258
else:
244-
# Patch now
245259
fh.seek(0)
246260
fh.write(new_content)
247-
else:
248-
pass # Already patched
249261

250262
def __repr__(self):
251263
return "{0:s}({1:s})".format(

0 commit comments

Comments
 (0)