Skip to content

Commit b023adf

Browse files
authored
Merge pull request #1743 from seleniumbase/multithreaded-downloads
Make improvements to multithreaded downloads, and more
2 parents 9f1f745 + cf19728 commit b023adf

File tree

13 files changed

+138
-86
lines changed

13 files changed

+138
-86
lines changed

examples/coffee_cart_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
class CoffeeCartTests(BaseCase):
77
def test_1_verify_nav_link_to_coffee_cart(self):
8-
self.open("https://seleniumbase.io/")
8+
self.open("https://seleniumbase.io/help_docs/customizing_test_runs/")
99
self.js_click('nav a:contains("Coffee Cart")')
1010
self.assert_title("Coffee Cart")
1111
self.assert_element('h4:contains("Espresso")')

examples/test_canvas.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ def test_canvas_click_from_center(self):
3232

3333
def test_click_with_offset(self):
3434
self.open("https://seleniumbase.io/canvas/")
35+
if self.undetectable:
36+
self.skip("Skip this test in undetectable mode.")
3537
self.assert_title_contains("Canvas")
3638
self.highlight("canvas")
3739
rgb = self.get_pixel_colors()

examples/test_download_files.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ def test_download_files_from_pypi(self):
4242
or self.browser == "ie"
4343
or (self.is_chromium() and self.guest_mode and not self.headless)
4444
or (self.undetectable and (self.headless or self.headless2))
45+
or (
46+
self.is_chromium()
47+
and int(self.get_chromium_version()) >= 110
48+
and self.headless
49+
)
4550
):
4651
whl_href = self.get_attribute(whl_selector, "href")
4752
tar_href = self.get_attribute(tar_selector, "href")

help_docs/method_summary.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,8 +485,12 @@ self.is_chromium()
485485

486486
self.get_chrome_version()
487487

488+
self.get_chromium_version()
489+
488490
self.get_chromedriver_version()
489491

492+
self.get_chromium_driver_version()
493+
490494
self.get_mfa_code(totp_key=None)
491495
# Duplicates: self.get_totp_code(totp_key=None)
492496
# self.get_google_auth_password(totp_key=None)

requirements.txt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,16 +67,14 @@ pytest-ordering==0.6
6767
pytest-rerunfailures==10.3;python_version<"3.7"
6868
pytest-rerunfailures==11.0;python_version>="3.7"
6969
pytest-xdist==2.5.0;python_version<"3.7"
70-
pytest-xdist==3.1.0;python_version>="3.7"
70+
pytest-xdist==3.2.0;python_version>="3.7"
7171
parameterized==0.8.1
7272
sbvirtualdisplay==1.2.0
7373
behave==1.2.6
74-
parse==1.19.0
75-
parse-type==0.6.0
7674
soupsieve==2.3.2.post1
7775
beautifulsoup4==4.11.2
7876
cryptography==36.0.2;python_version<"3.7"
79-
cryptography==39.0.0;python_version>="3.7"
77+
cryptography==39.0.1;python_version>="3.7"
8078
pygments==2.14.0
8179
pyreadline3==3.4.1;platform_system=="Windows"
8280
tabcompleter==1.1.0

seleniumbase/__version__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
# seleniumbase package
2-
__version__ = "4.12.7"
2+
__version__ = "4.12.8"

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: 62 additions & 21 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
@@ -6427,11 +6433,15 @@ def save_element_as_image_file(
64276433
def download_file(self, file_url, destination_folder=None):
64286434
"""Downloads the file from the url to the destination folder.
64296435
If no destination folder is specified, the default one is used.
6430-
(The default [Downloads Folder] = "./downloaded_files")"""
6431-
if not destination_folder:
6432-
destination_folder = constants.Files.DOWNLOADS_FOLDER
6433-
if not os.path.exists(destination_folder):
6434-
os.makedirs(destination_folder)
6436+
(The default folder for downloads is "./downloaded_files")"""
6437+
download_file_lock = fasteners.InterProcessLock(
6438+
constants.MultiBrowser.DOWNLOAD_FILE_LOCK
6439+
)
6440+
with download_file_lock:
6441+
if not destination_folder:
6442+
destination_folder = constants.Files.DOWNLOADS_FOLDER
6443+
if not os.path.exists(destination_folder):
6444+
os.makedirs(destination_folder)
64356445
page_utils._download_file_to(file_url, destination_folder)
64366446
if self.recorder_mode:
64376447
url = self.get_current_url()
@@ -6489,6 +6499,12 @@ def get_browser_downloads_folder(self):
64896499
and self.headless
64906500
):
64916501
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(".")
64926508
else:
64936509
return download_helper.get_downloads_folder()
64946510
return os.path.join(os.path.expanduser("~"), "downloads")
@@ -7088,17 +7104,27 @@ def __fail_if_not_using_chrome(self, method):
70887104
if browser_name.lower() == "chrome":
70897105
chrome = True
70907106
if not chrome:
7091-
from seleniumbase.common.exceptions import NotUsingChromeException
7092-
70937107
message = (
7094-
'Error: "%s" should only be called '
7095-
'by tests running with self.browser == "chrome"! '
7108+
'Error: "%s" should only be called by tests '
7109+
'running with "--browser=chrome" / "--chrome"! '
70967110
'You should add an "if" statement to your code before calling '
70977111
"this method if using browsers that are Not Chrome! "
70987112
'The browser detected was: "%s".' % (method, browser_name)
70997113
)
71007114
raise NotUsingChromeException(message)
71017115

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+
71027128
def get_chrome_version(self):
71037129
self.__check_scope()
71047130
self.__fail_if_not_using_chrome("get_chrome_version()")
@@ -7109,6 +7135,11 @@ def get_chrome_version(self):
71097135
chrome_version = driver_capabilities["browserVersion"]
71107136
return chrome_version
71117137

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+
71127143
def get_chromedriver_version(self):
71137144
self.__check_scope()
71147145
self.__fail_if_not_using_chrome("get_chromedriver_version()")
@@ -7117,14 +7148,19 @@ def get_chromedriver_version(self):
71177148
chromedriver_version = chromedriver_version.split(" ")[0]
71187149
return chromedriver_version
71197150

7120-
def is_chromedriver_too_old(self):
7121-
"""Before chromedriver 73, there was no version check, which
7122-
means it's possible to run a new Chrome with old drivers."""
7151+
def get_chromium_driver_version(self):
71237152
self.__check_scope()
7124-
self.__fail_if_not_using_chrome("is_chromedriver_too_old()")
7125-
if int(self.get_chromedriver_version().split(".")[0]) < 73:
7126-
return True # chromedriver is too old! Please upgrade!
7127-
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
71287164

71297165
def get_mfa_code(self, totp_key=None):
71307166
"""Same as get_totp_code() and get_google_auth_password().
@@ -9303,8 +9339,6 @@ def __assert_eq(self, *args, **kwargs):
93039339
elif line.strip().startswith("*"):
93049340
minified_exception += line + "\n"
93059341
if minified_exception:
9306-
from seleniumbase.common.exceptions import VisualException
9307-
93089342
raise VisualException(minified_exception)
93099343

93109344
def _process_visual_baseline_logs(self):
@@ -9684,8 +9718,6 @@ def __check_scope(self):
96849718
if hasattr(self, "browser"): # self.browser stores the type of browser
96859719
return # All good: setUp() already initialized variables in "self"
96869720
else:
9687-
from seleniumbase.common.exceptions import OutOfScopeException
9688-
96899721
message = (
96909722
"\n It looks like you are trying to call a SeleniumBase method"
96919723
"\n from outside the scope of your test class's `self` object,"
@@ -12730,9 +12762,18 @@ def __disable_beforeunload_as_needed(self):
1273012762

1273112763
############
1273212764

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+
1273312774
@decorators.deprecated("You should use re.escape() instead.")
1273412775
def jq_format(self, code):
12735-
# DEPRECATED - re.escape() already performs the intended action.
12776+
# DEPRECATED - re.escape() already performs this action.
1273612777
return js_utils._jq_format(code)
1273712778

1273812779
############

seleniumbase/fixtures/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ class MultiBrowser:
124124
DRIVER_FIXING_LOCK = Files.DOWNLOADS_FOLDER + "/driver_fixing.lock"
125125
DRIVER_REPAIRED = Files.DOWNLOADS_FOLDER + "/driver_fixed.lock"
126126
CERT_FIXING_LOCK = Files.DOWNLOADS_FOLDER + "/cert_fixing.lock"
127+
DOWNLOAD_FILE_LOCK = Files.DOWNLOADS_FOLDER + "/download_file.lock"
127128

128129

129130
class SavedCookies:

0 commit comments

Comments
 (0)