diff --git a/README.md b/README.md
index 075a9dc32a9..6312b683ec9 100755
--- a/README.md
+++ b/README.md
@@ -657,6 +657,7 @@ pytest test_coffee_cart.py --trace
--headless2 # (Use the new headless mode, which supports extensions.)
--headed # (Run tests in headed/GUI mode on Linux OS, where not default.)
--xvfb # (Run tests using the Xvfb virtual display server on Linux OS.)
+--xvfb-metrics=STRING # (Set Xvfb display size on Linux: "Width,Height".)
--locale=LOCALE_CODE # (Set the Language Locale Code for the web browser.)
--interval=SECONDS # (The autoplay interval for presentations & tour steps)
--start-page=URL # (The starting URL for the web browser when tests begin.)
@@ -701,6 +702,7 @@ pytest test_coffee_cart.py --trace
--rcs | --reuse-class-session # (Reuse session for tests in class.)
--crumbs # (Delete all cookies between tests reusing a session.)
--disable-beforeunload # (Disable the "beforeunload" event on Chrome.)
+--window-position=X,Y # (Set the browser's starting window position.)
--window-size=WIDTH,HEIGHT # (Set the browser's starting window size.)
--maximize # (Start tests with the browser window maximized.)
--screenshot # (Save a screenshot at the end of each test.)
@@ -869,7 +871,7 @@ pytest test_suite.py --dashboard --html=report.html
-If viewing pytest html reports in [Jenkins](https://www.jenkins.io/), you may need to [configure Jenkins settings](https://stackoverflow.com/a/46197356) for the html to render correctly. This is due to [Jenkins CSP changes](https://www.jenkins.io/doc/book/system-administration/security/configuring-content-security-policy/).
+If viewing pytest html reports in [Jenkins](https://www.jenkins.io/), you may need to [configure Jenkins settings](https://stackoverflow.com/a/46197356/7058266) for the html to render correctly. This is due to [Jenkins CSP changes](https://www.jenkins.io/doc/book/system-administration/security/configuring-content-security-policy/).
You can also use ``--junit-xml=report.xml`` to get an xml report instead. Jenkins can use this file to display better reporting for your tests.
diff --git a/examples/custom_settings.py b/examples/custom_settings.py
index 2eccc6bd8eb..a9479015b74 100644
--- a/examples/custom_settings.py
+++ b/examples/custom_settings.py
@@ -64,6 +64,10 @@
# If True and --proxy=IP_ADDRESS:PORT is invalid, then error immediately.
RAISE_INVALID_PROXY_STRING_EXCEPTION = True
+# Default browser coordinates when opening new windows for tests.
+WINDOW_START_X = 20
+WINDOW_START_Y = 54
+
# Default browser resolutions when opening new windows for tests.
# (Headless resolutions take priority, and include all browsers.)
# (Firefox starts maximized by default when running in GUI Mode.)
diff --git a/examples/dialog_boxes/dialog_box_tour.py b/examples/dialog_boxes/dialog_box_tour.py
index 2e061732cc7..e02f830d318 100644
--- a/examples/dialog_boxes/dialog_box_tour.py
+++ b/examples/dialog_boxes/dialog_box_tour.py
@@ -108,7 +108,9 @@ def test_dialog_boxes(self):
self.highlight_type('input[aria-label="Search"]', text + "\n")
else:
self.open("https://en.wikipedia.org/wiki/Special:Search")
- self.highlight_type('input[id*="search"]', text + "\n")
+ self.highlight_type('input[id*="search"]', text)
+ self.sleep(1)
+ self.click("#searchform button")
self.wait_for_ready_state_complete()
self.sleep(1)
self.highlight("body")
diff --git a/examples/example_logs/ReadMe.md b/examples/example_logs/ReadMe.md
index fc77d59af70..11c130b55da 100644
--- a/examples/example_logs/ReadMe.md
+++ b/examples/example_logs/ReadMe.md
@@ -74,9 +74,9 @@ pytest test_suite.py --dashboard --html=report.html
--------
-If viewing ``pytest-html`` reports in [Jenkins](https://www.jenkins.io/), you may need to [configure Jenkins settings](https://stackoverflow.com/a/46197356) for the HTML to render correctly. This is due to [Jenkins CSP changes](https://www.jenkins.io/doc/book/security/configuring-content-security-policy/). That setting can be changed from ``Manage Jenkins`` > ``Script Console`` by running:
+If viewing ``pytest-html`` reports in [Jenkins](https://www.jenkins.io/), you may need to [configure Jenkins settings](https://stackoverflow.com/a/46197356/7058266) for the HTML to render correctly. This is due to [Jenkins CSP changes](https://www.jenkins.io/doc/book/security/configuring-content-security-policy/). That setting can be changed from ``Manage Jenkins`` > ``Script Console`` by running:
-```
+```js
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")
```
diff --git a/examples/presenter/uc_presentation.py b/examples/presenter/uc_presentation.py
index ae5ff4dc2f2..c52dc7ad328 100644
--- a/examples/presenter/uc_presentation.py
+++ b/examples/presenter/uc_presentation.py
@@ -1,5 +1,6 @@
import os
import subprocess
+from contextlib import suppress
from seleniumbase import BaseCase
from seleniumbase import SB
BaseCase.main(__name__, __file__)
@@ -8,6 +9,7 @@
class UCPresentationClass(BaseCase):
def test_presentation(self):
self.open("data:,")
+ self._output_file_saves = False
self.create_presentation(theme="beige", transition="fade")
self.add_slide(
"
A deep dive into undetectable automation, with:
"
@@ -235,7 +237,8 @@ def test_presentation(self):
"from seleniumbase import Driver\n\n"
"driver = Driver(uc=True)\n"
"try:\n"
- ' driver.get("https://nowsecure.nl/#relax")\n'
+ ' driver.get("https://gitlab.com/users/sign_in")'
+ '\n'
" driver.sleep(4)\n"
" # DO MORE STUFF\n"
"finally:\n"
@@ -263,7 +266,7 @@ def test_presentation(self):
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(uc=True) as sb:
sb.get("https://seleniumbase.io/simple/login")
sb.type("#username", "demo_user")
@@ -274,8 +277,6 @@ def test_presentation(self):
sb.highlight("#image1")
sb.click_link("Sign out")
sb.assert_text("signed out", "#top_message")
- except Exception:
- pass
self.create_presentation(theme="serif", transition="fade")
self.add_slide(
@@ -298,7 +299,7 @@ def test_presentation(self):
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(uc=True, demo=True) as sb:
sb.get("https://seleniumbase.io/simple/login")
sb.type("#username", "demo_user")
@@ -309,8 +310,6 @@ def test_presentation(self):
sb.highlight("#image1")
sb.click_link("Sign out")
sb.assert_text("signed out", "#top_message")
- except Exception:
- pass
self.create_presentation(theme="serif", transition="fade")
self.add_slide(
@@ -339,29 +338,22 @@ def test_presentation(self):
code=(
"from seleniumbase import SB\n\n"
"with SB(uc=True) as sb:\n"
- ' sb.get("https://nowsecure.nl/#relax")\n'
- " sb.sleep(1)\n"
- ' if not sb.is_text_visible("OH YEAH, you passed", "h1"):\n'
- " sb.get_new_driver(undetectable=True)\n"
- ' sb.get("https://nowsecure.nl/#relax")\n'
- " sb.sleep(1)\n"
- ' sb.activate_demo_mode()\n'
- ' sb.assert_text("OH YEAH, you passed!", "h1", timeout=3)\n'
+ ' url = "https://gitlab.com/users/sign_in"\n'
+ " sb.uc_open_with_reconnect(url, 4)\n\n"
+ " ...\n"
),
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(uc=True) as sb:
- sb.uc_open_with_tab("https://nowsecure.nl/#relax")
- sb.sleep(1)
- if not sb.is_text_visible("OH YEAH, you passed", "h1"):
- sb.uc_open_with_tab("https://nowsecure.nl/#relax")
- sb.sleep(1)
- sb.activate_demo_mode()
- sb.assert_text("OH YEAH, you passed!", "h1", timeout=3)
- except Exception:
- pass
+ url = "https://gitlab.com/users/sign_in"
+ sb.uc_open_with_reconnect(url, 4)
+ sb.assert_text("Username", '[for="user_login"]', timeout=3)
+ sb.assert_element('[for="user_login"]')
+ sb.highlight('button:contains("Sign in")')
+ sb.highlight('h1:contains("GitLab.com")')
+ sb.post_message("SeleniumBase wasn't detected", duration=4)
self.create_presentation(theme="serif", transition="fade")
self.add_slide(
@@ -495,11 +487,12 @@ def test_presentation(self):
code=(
"# Example:\n"
"driver.uc_open_with_reconnect(\n"
- ' "https://nowsecure.nl/#relax", reconnect_time=6\n)'
+ ' "https://steamdb.info/login/", reconnect_time=6\n)'
+ ""
"\n\n"
"# Short form example:\n"
"driver.uc_open_with_reconnect("
- '"https://nowsecure.nl/#relax", 6)\n'
+ '"https://steamdb.info/login/", 6)\n'
),
)
self.add_slide(
diff --git a/examples/presenter/uc_presentation_3.py b/examples/presenter/uc_presentation_3.py
index a3da212dcd4..1b66fef2090 100644
--- a/examples/presenter/uc_presentation_3.py
+++ b/examples/presenter/uc_presentation_3.py
@@ -1,4 +1,5 @@
import sys
+from contextlib import suppress
from seleniumbase import BaseCase
from seleniumbase import SB
BaseCase.main(__name__, __file__)
@@ -54,7 +55,7 @@ def test_presentation_3(self):
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(uc=True) as sb:
url = "https://gitlab.com/users/sign_in"
sb.uc_open_with_reconnect(url, 4)
@@ -63,8 +64,6 @@ def test_presentation_3(self):
sb.highlight('button:contains("Sign in")')
sb.highlight('h1:contains("GitLab.com")')
sb.post_message("SeleniumBase wasn't detected", duration=4)
- except Exception:
- pass
self.create_presentation(theme="serif", transition="none")
self.add_slide(
@@ -151,7 +150,7 @@ def test_presentation_3(self):
if "linux" in sys.platform or "win32" in sys.platform:
agent = None # Use the default UserAgent
- try:
+ with suppress(Exception):
with SB(uc=True, test=True, agent=agent) as sb:
url = "https://gitlab.com/users/sign_in"
sb.uc_open_with_reconnect(url, 4)
@@ -159,8 +158,6 @@ def test_presentation_3(self):
sb.assert_element('label[for="user_login"]')
sb.set_messenger_theme(location="bottom_center")
sb.post_message("SeleniumBase wasn't detected!")
- except Exception:
- pass
self.create_presentation(theme="serif", transition="none")
self.add_slide(
@@ -201,7 +198,7 @@ def test_presentation_3(self):
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(uc=True, test=True, agent=agent) as sb:
url = "https://gitlab.com/users/sign_in"
sb.uc_open_with_reconnect(url, 4)
@@ -209,8 +206,6 @@ def test_presentation_3(self):
sb.assert_element('label[for="user_login"]')
sb.set_messenger_theme(location="bottom_center")
sb.post_message("SeleniumBase wasn't detected!")
- except Exception:
- pass
self.create_presentation(theme="serif", transition="none")
self.add_slide(
@@ -276,7 +271,7 @@ def test_presentation_3(self):
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(uc=True, incognito=True, locale_code="en") as sb:
url = "https://ahrefs.com/website-authority-checker"
input_field = 'input[placeholder="Enter domain"]'
@@ -291,8 +286,6 @@ def test_presentation_3(self):
sb.highlight('a:contains("Top 100 backlinks")')
sb.set_messenger_theme(location="bottom_center")
sb.post_message("SeleniumBase wasn't detected!")
- except Exception:
- pass
self.create_presentation(theme="serif", transition="none")
self.add_slide(
@@ -312,7 +305,7 @@ def test_presentation_3(self):
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(uc=True, test=True, disable_csp=True) as sb:
url = "https://steamdb.info/"
sb.uc_open_with_reconnect(url, 3)
@@ -324,8 +317,6 @@ def test_presentation_3(self):
sb.highlight('button:contains("Sign in")', scroll=False)
sb.set_messenger_theme(location="top_center")
sb.post_message("SeleniumBase wasn't detected", duration=4)
- except Exception:
- pass
self.create_presentation(theme="serif", transition="none")
self.add_slide(
@@ -405,7 +396,7 @@ def test_presentation_3(self):
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(uc=True, test=True) as sb:
url = "https://seleniumbase.io/apps/recaptcha"
sb.uc_open_with_reconnect(url)
@@ -413,8 +404,6 @@ def test_presentation_3(self):
sb.assert_element("img#captcha-success", timeout=3)
sb.set_messenger_theme(location="top_left")
sb.post_message("SeleniumBase wasn't detected")
- except Exception:
- pass
self.create_presentation(theme="serif", transition="none")
self.add_slide(
@@ -673,7 +662,7 @@ def test_presentation_3(self):
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(test=True) as sb:
url = "https://seleniumbase.io/hobbit/login"
sb.open(url)
@@ -682,8 +671,6 @@ def test_presentation_3(self):
sb.click("img")
sb.highlight("h1")
sb.sleep(3) # Gandalf: "You Shall Not Pass!"
- except Exception:
- pass
self.create_presentation(theme="serif", transition="none")
self.add_slide(
@@ -692,7 +679,7 @@ def test_presentation_3(self):
)
self.begin_presentation(filename="uc_presentation.html")
- try:
+ with suppress(Exception):
with SB(uc=True, test=True) as sb:
url = "https://seleniumbase.io/hobbit/login"
sb.uc_open_with_disconnect(url, 2.2)
@@ -703,8 +690,6 @@ def test_presentation_3(self):
sb.post_message("SeleniumBase wasn't detected!")
sb.click("img")
sb.sleep(5.888) # Cool animation happening now!
- except Exception:
- pass
self.create_presentation(theme="serif", transition="none")
self.add_slide(
diff --git a/examples/test_hack_search.py b/examples/test_hack_search.py
index 7ddb4691b3a..80090729a57 100644
--- a/examples/test_hack_search.py
+++ b/examples/test_hack_search.py
@@ -25,8 +25,9 @@ def test_hack_search(self):
self.highlight_click('[href*="github.com/seleniumbase/SeleniumBase"]')
self.highlight_click('[href="/seleniumbase/SeleniumBase"]')
self.assert_text("SeleniumBase", "strong a")
+ self.highlight("strong a")
self.js_click('a[title="examples"]')
- self.highlight('td[class*="large"] a[title="test_hack_search.py"]')
- self.click('td[class*="large"] a[title="test_hack_search.py"]')
+ self.highlight('#repo-content-turbo-frame')
+ self.js_click('a[title="test_hack_search.py"]')
self.assert_text("test_hack_search.py", "#file-name-id-wide")
self.highlight("#file-name-id-wide")
diff --git a/examples/test_parse_soup.py b/examples/test_parse_soup.py
index ee5cfa28a6a..4d8205cf1ac 100644
--- a/examples/test_parse_soup.py
+++ b/examples/test_parse_soup.py
@@ -14,6 +14,9 @@ def click_menu_item(self, text):
self.click("#%s" % the_id)
def test_beautiful_soup_parsing(self):
+ if self.headless:
+ self.open_if_not_url("about:blank")
+ self.skip("Skip this test in headless mode!")
self.open("https://seleniumbase.io/tinymce/")
self.wait_for_element("div.mce-container-body")
self.click_menu_item("File")
diff --git a/examples/test_shadow_dom.py b/examples/test_shadow_dom.py
index cdc51167d7a..2196155be1f 100644
--- a/examples/test_shadow_dom.py
+++ b/examples/test_shadow_dom.py
@@ -55,7 +55,7 @@ def test_shadow_dom(self):
)
remove_button = (
"downloads-manager::shadow #downloadsList"
- " downloads-item::shadow #remove-old"
+ " downloads-item::shadow #quick-remove"
)
no_downloads_area = "downloads-manager::shadow #no-downloads"
diff --git a/examples/test_tinymce.py b/examples/test_tinymce.py
index 5dfef56319c..9c81cd69cfe 100644
--- a/examples/test_tinymce.py
+++ b/examples/test_tinymce.py
@@ -4,6 +4,9 @@
class TinyMceTests(BaseCase):
def test_tinymce(self):
+ if self.headless:
+ self.open_if_not_url("about:blank")
+ self.skip("Skip this test in headless mode!")
self.open("https://seleniumbase.io/tinymce/")
self.wait_for_element("div.mce-container-body")
self.click('span:contains("File")')
diff --git a/examples/youtube_search_test.py b/examples/youtube_search_test.py
index 55448a61e35..96f119340f1 100644
--- a/examples/youtube_search_test.py
+++ b/examples/youtube_search_test.py
@@ -12,20 +12,19 @@ def test_youtube_autocomplete_results(self):
self.open("https://www.youtube.com/c/MichaelMintz")
search_term = "seleniumbase"
search_selector = "input#search"
- result_selector = 'li[role="presentation"]'
+ results_selector = '[role="listbox"]'
self.click_if_visible('button[aria-label="Close"]')
self.double_click(search_selector)
self.sleep(0.15)
self.type(search_selector, search_term)
self.sleep(0.15)
# First verify that an autocomplete result exists
- self.assert_element(result_selector)
- top_result = self.get_text(result_selector)
+ self.assert_element(results_selector)
+ top_results = self.get_text(results_selector)
# Now verify that the autocomplete result is good
self.assert_true(
- search_term in top_result,
- 'Expected text "%s" not found in top result! '
- 'Actual text was "%s"!' % (search_term, top_result),
+ search_term in top_results,
+ 'Expected text "%s" not found in top results! '
+ 'Actual text was "%s"!' % (search_term, top_results),
)
- self.click(result_selector)
self.sleep(1)
diff --git a/help_docs/customizing_test_runs.md b/help_docs/customizing_test_runs.md
index 64ccda7a5ae..8566d968346 100644
--- a/help_docs/customizing_test_runs.md
+++ b/help_docs/customizing_test_runs.md
@@ -148,6 +148,7 @@ pytest my_first_test.py --settings-file=custom_settings.py
--headless2 # (Use the new headless mode, which supports extensions.)
--headed # (Run tests in headed/GUI mode on Linux OS, where not default.)
--xvfb # (Run tests using the Xvfb virtual display server on Linux OS.)
+--xvfb-metrics=STRING # (Set Xvfb display size on Linux: "Width,Height".)
--locale=LOCALE_CODE # (Set the Language Locale Code for the web browser.)
--interval=SECONDS # (The autoplay interval for presentations & tour steps)
--start-page=URL # (The starting URL for the web browser when tests begin.)
@@ -192,6 +193,7 @@ pytest my_first_test.py --settings-file=custom_settings.py
--rcs | --reuse-class-session # (Reuse session for tests in class.)
--crumbs # (Delete all cookies between tests reusing a session.)
--disable-beforeunload # (Disable the "beforeunload" event on Chrome.)
+--window-position=X,Y # (Set the browser's starting window position.)
--window-size=WIDTH,HEIGHT # (Set the browser's starting window size.)
--maximize # (Start tests with the browser window maximized.)
--screenshot # (Save a screenshot at the end of each test.)
@@ -380,7 +382,7 @@ pytest test_suite.py --dashboard --html=report.html
-If viewing pytest html reports in [Jenkins](https://www.jenkins.io/), you may need to [configure Jenkins settings](https://stackoverflow.com/a/46197356) for the html to render correctly. This is due to [Jenkins CSP changes](https://www.jenkins.io/doc/book/system-administration/security/configuring-content-security-policy/).
+If viewing pytest html reports in [Jenkins](https://www.jenkins.io/), you may need to [configure Jenkins settings](https://stackoverflow.com/a/46197356/7058266) for the html to render correctly. This is due to [Jenkins CSP changes](https://www.jenkins.io/doc/book/system-administration/security/configuring-content-security-policy/).
You can also use ``--junit-xml=report.xml`` to get an xml report instead. Jenkins can use this file to display better reporting for your tests.
diff --git a/help_docs/webdriver_installation.md b/help_docs/webdriver_installation.md
index 6365fe7f098..f5b8dd16c2b 100644
--- a/help_docs/webdriver_installation.md
+++ b/help_docs/webdriver_installation.md
@@ -2,7 +2,7 @@
## [](https://github.com/seleniumbase/SeleniumBase/) Installing webdrivers
-To run web automation, you'll need webdrivers for each browser you plan on using. With SeleniumBase, drivers are downloaded automatically as needed into the SeleniumBase ``drivers`` folder.
+To run web automation, you need webdrivers for each browser you plan on using. With SeleniumBase, drivers are downloaded automatically (as needed) into the SeleniumBase `drivers/` folder.
You can also download drivers manually with these commands:
@@ -12,7 +12,7 @@ seleniumbase get geckodriver
seleniumbase get edgedriver
```
-After running the commands above, web drivers will get downloaded into the ``seleniumbase/drivers/`` folder. SeleniumBase uses those drivers during tests. (The drivers don't come with SeleniumBase by default.)
+After running the commands above, web drivers will get downloaded into the `seleniumbase/drivers/` folder. SeleniumBase uses those drivers during tests. (The drivers don't come with SeleniumBase by default.)
If the necessary driver is not found in this location while running tests, SeleniumBase will instead look for the driver on the System PATH. If the necessary driver is not on the System PATH either, SeleniumBase will automatically attempt to download the required driver.
diff --git a/integrations/node_js/ReadMe.md b/integrations/node_js/ReadMe.md
index 20558e59ffa..ec59492a1a7 100644
--- a/integrations/node_js/ReadMe.md
+++ b/integrations/node_js/ReadMe.md
@@ -19,13 +19,13 @@ You can create a customized web app for running SeleniumBase tests by using Node
npm install -g npm@latest
```
-#### 3. Install the Example Test Runner for SeleniumBase from the [integrations/node_js](https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/node_js) folder (``npm ci`` has a speed improvement over ``npm install`` because it uses the ``npm-shrinkwrap.json`` file that's generated via ``npm shrinkwrap``.)
+#### 3. Install the example Test Runner for SeleniumBase from [integrations/node_js](https://github.com/seleniumbase/SeleniumBase/tree/master/integrations/node_js). (If dependencies were already installed, you can use `npm ci` for a speed improvement over `npm i` / `npm install` because `npm ci` uses `npm-shrinkwrap.json`, which is generated via ``npm shrinkwrap``.)
```bash
-npm ci
+npm install
```
-(You should see a ``node_modules`` folder appear in your ``node_js`` folder.)
+(You should see a `node_modules` folder appear in your `node_js` folder.)
#### 4. Run the NodeJS server for your SeleniumBase Test Runner web app
diff --git a/integrations/node_js/index.html b/integrations/node_js/index.html
index 50f37027260..dc6c64ebbad 100644
--- a/integrations/node_js/index.html
+++ b/integrations/node_js/index.html
@@ -1,11 +1,17 @@
-
+
diff --git a/integrations/node_js/npm-shrinkwrap.json b/integrations/node_js/npm-shrinkwrap.json
index 7507126ad50..2fc839d54a8 100644
--- a/integrations/node_js/npm-shrinkwrap.json
+++ b/integrations/node_js/npm-shrinkwrap.json
@@ -8,7 +8,7 @@
"name": "app",
"version": "0.0.0",
"dependencies": {
- "express": "~4.19.2"
+ "express": "~4.21.0"
}
},
"node_modules/accepts": {
@@ -29,9 +29,9 @@
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
},
"node_modules/body-parser": {
- "version": "1.20.2",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
- "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
+ "version": "1.20.3",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
+ "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
"dependencies": {
"bytes": "3.1.2",
"content-type": "~1.0.5",
@@ -41,7 +41,7 @@
"http-errors": "2.0.0",
"iconv-lite": "0.4.24",
"on-finished": "2.4.1",
- "qs": "6.11.0",
+ "qs": "6.13.0",
"raw-body": "2.5.2",
"type-is": "~1.6.18",
"unpipe": "1.0.0"
@@ -156,9 +156,9 @@
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
},
"node_modules/encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
"engines": {
"node": ">= 0.8"
}
@@ -196,36 +196,36 @@
}
},
"node_modules/express": {
- "version": "4.19.2",
- "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz",
- "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==",
+ "version": "4.21.0",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz",
+ "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
- "body-parser": "1.20.2",
+ "body-parser": "1.20.3",
"content-disposition": "0.5.4",
"content-type": "~1.0.4",
"cookie": "0.6.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
- "encodeurl": "~1.0.2",
+ "encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
- "finalhandler": "1.2.0",
+ "finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
- "merge-descriptors": "1.0.1",
+ "merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
- "path-to-regexp": "0.1.7",
+ "path-to-regexp": "0.1.10",
"proxy-addr": "~2.0.7",
- "qs": "6.11.0",
+ "qs": "6.13.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
- "send": "0.18.0",
- "serve-static": "1.15.0",
+ "send": "0.19.0",
+ "serve-static": "1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"type-is": "~1.6.18",
@@ -237,12 +237,12 @@
}
},
"node_modules/finalhandler": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
- "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
+ "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
"dependencies": {
"debug": "2.6.9",
- "encodeurl": "~1.0.2",
+ "encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"on-finished": "2.4.1",
"parseurl": "~1.3.3",
@@ -398,9 +398,12 @@
}
},
"node_modules/merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
},
"node_modules/methods": {
"version": "1.1.2",
@@ -484,9 +487,9 @@
}
},
"node_modules/path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
+ "version": "0.1.10",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz",
+ "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w=="
},
"node_modules/proxy-addr": {
"version": "2.0.7",
@@ -501,11 +504,11 @@
}
},
"node_modules/qs": {
- "version": "6.11.0",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
- "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
+ "version": "6.13.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
+ "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
"dependencies": {
- "side-channel": "^1.0.4"
+ "side-channel": "^1.0.6"
},
"engines": {
"node": ">=0.6"
@@ -561,9 +564,9 @@
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"node_modules/send": {
- "version": "0.18.0",
- "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
- "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
+ "version": "0.19.0",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
+ "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
"dependencies": {
"debug": "2.6.9",
"depd": "2.0.0",
@@ -583,20 +586,28 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/send/node_modules/encodeurl": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
+ "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
"node_modules/send/node_modules/ms": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/serve-static": {
- "version": "1.15.0",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
- "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
+ "version": "1.16.2",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
+ "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
"dependencies": {
- "encodeurl": "~1.0.2",
+ "encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"parseurl": "~1.3.3",
- "send": "0.18.0"
+ "send": "0.19.0"
},
"engines": {
"node": ">= 0.8.0"
diff --git a/integrations/node_js/package.json b/integrations/node_js/package.json
index 4873a0f1362..9a00008425b 100644
--- a/integrations/node_js/package.json
+++ b/integrations/node_js/package.json
@@ -6,6 +6,6 @@
"start": "node ./bin/www"
},
"dependencies": {
- "express": "~4.19.2"
+ "express": "~4.21.0"
}
}
\ No newline at end of file
diff --git a/mkdocs_build/requirements.txt b/mkdocs_build/requirements.txt
index eb8f5c71152..bb862790a7b 100644
--- a/mkdocs_build/requirements.txt
+++ b/mkdocs_build/requirements.txt
@@ -2,8 +2,8 @@
# Minimum Python version: 3.8 (for generating docs only)
regex>=2024.9.11
-pymdown-extensions>=10.9
-pipdeptree>=2.23.3
+pymdown-extensions>=10.10.1
+pipdeptree>=2.23.4
python-dateutil>=2.8.2
Markdown==3.7
markdown2==2.5.0
@@ -20,7 +20,7 @@ lxml==5.3.0
pyquery==2.0.1
readtime==3.0.0
mkdocs==1.6.1
-mkdocs-material==9.5.34
+mkdocs-material==9.5.36
mkdocs-exclude-search==0.6.6
mkdocs-simple-hooks==0.1.5
mkdocs-material-extensions==1.3.1
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 00000000000..2697d39bd76
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,72 @@
+[build-system]
+requires = ["setuptools>=70.2.0", "wheel>=0.44.0"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "seleniumbase"
+readme = "README.md"
+dynamic = [
+ "version",
+ "license",
+ "authors",
+ "scripts",
+ "keywords",
+ "classifiers",
+ "description",
+ "entry-points",
+ "dependencies",
+ "requires-python",
+ "optional-dependencies",
+]
+
+[project.urls]
+"Homepage" = "https://github.com/seleniumbase/SeleniumBase"
+"Changelog" = "https://github.com/seleniumbase/SeleniumBase/releases"
+"Download" = "https://pypi.org/project/seleniumbase/#files"
+"Blog" = "https://seleniumbase.com/"
+"Discord" = "https://discord.gg/EdhQTn3EyE"
+"PyPI" = "https://pypi.org/project/seleniumbase/"
+"Source" = "https://github.com/seleniumbase/SeleniumBase"
+"Repository" = "https://github.com/seleniumbase/SeleniumBase"
+"Documentation" = "https://seleniumbase.io/"
+
+[tool.setuptools]
+packages = [
+ "seleniumbase",
+ "sbase",
+ "seleniumbase.behave",
+ "seleniumbase.common",
+ "seleniumbase.config",
+ "seleniumbase.console_scripts",
+ "seleniumbase.core",
+ "seleniumbase.drivers",
+ "seleniumbase.extensions",
+ "seleniumbase.fixtures",
+ "seleniumbase.js_code",
+ "seleniumbase.masterqa",
+ "seleniumbase.plugins",
+ "seleniumbase.resources",
+ "seleniumbase.translate",
+ "seleniumbase.undetected",
+ "seleniumbase.utilities",
+ "seleniumbase.utilities.selenium_grid",
+ "seleniumbase.utilities.selenium_ide",
+]
+
+[tool.pytest.ini_options]
+addopts = ["--capture=no", "-p no:cacheprovider"]
+norecursedirs = [".*", "build", "dist", "recordings", "temp", "assets"]
+filterwarnings = [
+ "ignore::pytest.PytestWarning",
+ "ignore:.*U.*mode is deprecated:DeprecationWarning",
+]
+junit_family = ["legacy"]
+python_files = ["test_*.py", "*_test.py", "*_tests.py", "*_suite.py"]
+python_classes = ["Test*", "*Test*", "*Test", "*Tests", "*Suite"]
+python_functions = ["test_*"]
+markers = [
+ "marker1", "marker2", "marker3", "marker_test_suite",
+ "local", "remote", "offline", "expected_failure",
+ "qa", "ci", "e2e", "smoke", "ready", "master", "deploy",
+ "develop", "staging", "production", "release", "active",
+]
diff --git a/requirements.txt b/requirements.txt
index 86b35e3b534..d4040dab303 100755
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,87 +1,67 @@
-pip>=24.0;python_version<"3.8"
-pip>=24.1.2;python_version>="3.8"
-packaging>=24.0;python_version<"3.8"
-packaging>=24.1;python_version>="3.8"
-setuptools>=68.0.0;python_version<"3.8"
-setuptools~=70.2;python_version>="3.8" and python_version<"3.10"
+pip>=24.1.2
+packaging>=24.1
+setuptools~=70.2;python_version<"3.10"
setuptools>=70.2.0;python_version>="3.10"
-wheel>=0.42.0;python_version<"3.8"
-wheel>=0.44.0;python_version>="3.8"
+wheel>=0.44.0
attrs>=24.2.0
certifi>=2024.8.30
exceptiongroup>=1.2.2
-filelock>=3.12.2;python_version<"3.8"
-filelock>=3.16.0;python_version>="3.8"
-platformdirs>=4.0.0;python_version<"3.8"
-platformdirs>=4.3.2;python_version>="3.8"
-typing-extensions>=4.12.2;python_version>="3.8"
+filelock>=3.16.1
+fasteners>=0.19
+pynose>=1.5.3
+platformdirs>=4.3.6
+typing-extensions>=4.12.2
+sbvirtualdisplay>=1.3.0
+six>=1.16.0
parse>=1.20.2
parse-type>=0.6.3
-pyyaml==6.0.1;python_version<"3.8"
-pyyaml>=6.0.2;python_version>="3.8"
-six==1.16.0
-idna==3.8
+colorama>=0.4.6
+pyyaml>=6.0.2
+pygments>=2.18.0
+pyreadline3>=3.5.3;platform_system=="Windows"
+tabcompleter>=1.3.3
+pdbp>=1.5.4
+idna==3.10
chardet==5.2.0
charset-normalizer==3.3.2
urllib3>=1.26.20,<2;python_version<"3.10"
urllib3>=1.26.20,<2.3.0;python_version>="3.10"
requests==2.31.0
-pynose==1.5.2
sniffio==1.3.1
h11==0.14.0
outcome==1.3.0.post0
-trio==0.22.2;python_version<"3.8"
-trio==0.26.2;python_version>="3.8"
+trio==0.26.2
trio-websocket==0.11.1
wsproto==1.2.0
-websocket-client==1.8.0;python_version>="3.8"
-selenium==4.11.2;python_version<"3.8"
-selenium==4.24.0;python_version>="3.8"
+websocket-client==1.8.0
+selenium==4.25.0
cssselect==1.2.0
sortedcontainers==2.4.0
-fasteners==0.19
-execnet==2.0.2;python_version<"3.8"
-execnet==2.1.1;python_version>="3.8"
+execnet==2.1.1
iniconfig==2.0.0
-pluggy==1.2.0;python_version<"3.8"
-pluggy==1.5.0;python_version>="3.8"
+pluggy==1.5.0
py==1.11.0
-pytest==7.4.4;python_version<"3.8"
-pytest==8.3.3;python_version>="3.8"
+pytest==8.3.3
pytest-html==2.0.1
-pytest-metadata==3.0.0;python_version<"3.8"
-pytest-metadata==3.1.1;python_version>="3.8"
+pytest-metadata==3.1.1
pytest-ordering==0.6
-pytest-rerunfailures==13.0;python_version<"3.8"
-pytest-rerunfailures==14.0;python_version>="3.8"
-pytest-xdist==3.5.0;python_version<"3.8"
-pytest-xdist==3.6.1;python_version>="3.8"
+pytest-rerunfailures==14.0
+pytest-xdist==3.6.1
parameterized==0.9.0
-sbvirtualdisplay==1.3.0
behave==1.2.6
-soupsieve==2.4.1;python_version<"3.8"
-soupsieve==2.6;python_version>="3.8"
+soupsieve==2.6
beautifulsoup4==4.12.3
-pygments==2.17.2;python_version<"3.8"
-pygments==2.18.0;python_version>="3.8"
-pyreadline3==3.4.1;platform_system=="Windows"
-tabcompleter==1.3.3
-pdbp==1.5.4
-colorama==0.4.6
pyotp==2.9.0
python-xlib==0.33;platform_system=="Linux"
-markdown-it-py==2.2.0;python_version<"3.8"
-markdown-it-py==3.0.0;python_version>="3.8"
+markdown-it-py==3.0.0
mdurl==0.1.2
rich==13.8.1
# --- Testing Requirements --- #
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)
-coverage==7.2.7;python_version<"3.8"
-coverage>=7.6.1;python_version>="3.8"
-pytest-cov==4.1.0;python_version<"3.8"
-pytest-cov>=5.0.0;python_version>="3.8"
+coverage>=7.6.1
+pytest-cov>=5.0.0
flake8==5.0.4;python_version<"3.9"
flake8==7.1.1;python_version>="3.9"
mccabe==0.7.0
diff --git a/sbase/__init__.py b/sbase/__init__.py
index b45beac8e68..ff1a31a52a3 100644
--- a/sbase/__init__.py
+++ b/sbase/__init__.py
@@ -11,3 +11,4 @@
from seleniumbase import page_actions # noqa
from seleniumbase import page_utils # noqa
from seleniumbase import SB # noqa
+from seleniumbase import translate # noqa
diff --git a/seleniumbase/__init__.py b/seleniumbase/__init__.py
index e84a7d3628c..0f536be75ab 100644
--- a/seleniumbase/__init__.py
+++ b/seleniumbase/__init__.py
@@ -10,7 +10,7 @@
from seleniumbase.__version__ import __version__
from seleniumbase.common import decorators # noqa
from seleniumbase.common import encryption # noqa
-from seleniumbase.core import colored_traceback
+from seleniumbase.core import colored_traceback # noqa
from seleniumbase.core.browser_launcher import get_driver # noqa
from seleniumbase.fixtures import js_utils # noqa
from seleniumbase.fixtures import page_actions # noqa
@@ -36,8 +36,7 @@
pdb.DefaultConfig.sticky_by_default = True
colored_traceback.add_hook()
os.environ["SE_AVOID_STATS"] = "true" # Disable Selenium Manager stats
-if sys.version_info >= (3, 7):
- webdriver.TouchActions = None # Lifeline for past selenium-wire versions
+webdriver.TouchActions = None # Lifeline for past selenium-wire versions
if sys.version_info >= (3, 10):
collections.Callable = collections.abc.Callable # Lifeline for nosetests
del collections # Undo "import collections" / Simplify "dir(seleniumbase)"
diff --git a/seleniumbase/__version__.py b/seleniumbase/__version__.py
index ba052211991..429f6b81506 100755
--- a/seleniumbase/__version__.py
+++ b/seleniumbase/__version__.py
@@ -1,2 +1,2 @@
# seleniumbase package
-__version__ = "4.30.8"
+__version__ = "4.31.0"
diff --git a/seleniumbase/behave/behave_sb.py b/seleniumbase/behave/behave_sb.py
index 901bf76cbc5..17609334fcc 100644
--- a/seleniumbase/behave/behave_sb.py
+++ b/seleniumbase/behave/behave_sb.py
@@ -48,6 +48,7 @@
-D headless2 (Use the new headless mode, which supports extensions.)
-D headed (Run tests in headed/GUI mode on Linux OS, where not default.)
-D xvfb (Run tests using the Xvfb virtual display server on Linux OS.)
+-D xvfb-metrics=STRING (Set Xvfb display size on Linux: "Width,Height".)
-D locale=LOCALE_CODE (Set the Language Locale Code for the web browser.)
-D pdb (Activate Post Mortem Debug Mode if a test fails.)
-D interval=SECONDS (The autoplay interval for presentations & tour steps)
@@ -90,6 +91,7 @@
-D rcs | -D reuse-class-session (Reuse session for tests in class/feature)
-D crumbs (Delete all cookies between tests reusing a session.)
-D disable-beforeunload (Disable the "beforeunload" event on Chrome.)
+-D window-position=X,Y (Set the browser's starting window position.)
-D window-size=WIDTH,HEIGHT (Set the browser's starting window size.)
-D maximize (Start tests with the browser window maximized.)
-D screenshot (Save a screenshot at the end of each test.)
@@ -104,6 +106,7 @@
import os
import re
import sys
+from contextlib import suppress
from seleniumbase import config as sb_config
from seleniumbase.config import settings
from seleniumbase.core import download_helper
@@ -145,6 +148,7 @@ def get_configured_sb(context):
sb.headless_active = False
sb.headed = False
sb.xvfb = False
+ sb.xvfb_metrics = None
sb.start_page = None
sb.locale_code = None
sb.pdb_option = False
@@ -193,6 +197,7 @@ def get_configured_sb(context):
sb._disable_beforeunload = False
sb.visual_baseline = False
sb.use_wire = False
+ sb.window_position = None
sb.window_size = None
sb.maximize_option = False
sb.is_context_manager = False
@@ -302,6 +307,13 @@ def get_configured_sb(context):
if low_key == "xvfb":
sb.xvfb = True
continue
+ # Handle: -D xvfb-metrics=STR / xvfb_metrics=STR
+ if low_key in ["xvfb-metrics", "xvfb_metrics"]:
+ xvfb_metrics = userdata[key]
+ if xvfb_metrics == "true":
+ xvfb_metrics = sb.xvfb_metrics # revert to default
+ sb.xvfb_metrics = xvfb_metrics
+ continue
# Handle: -D start-page=URL / start_page=URL / url=URL
if low_key in ["start-page", "start_page", "url"]:
start_page = userdata[key]
@@ -601,6 +613,13 @@ def get_configured_sb(context):
if low_key == "wire":
sb.use_wire = True
continue
+ # Handle: -D window-position=X,Y / window_position=X,Y
+ if low_key in ["window-position", "window_position"]:
+ window_position = userdata[key]
+ if window_position == "true":
+ window_position = sb.window_position # revert to default
+ sb.window_position = window_position
+ continue
# Handle: -D window-size=Width,Height / window_size=Width,Height
if low_key in ["window-size", "window_size"]:
window_size = userdata[key]
@@ -904,6 +923,29 @@ def get_configured_sb(context):
else:
sb.enable_ws = False
sb.disable_ws = True
+ if sb.window_position:
+ window_position = sb.window_position
+ if window_position.count(",") != 1:
+ message = (
+ '\n\n window_position expects an "x,y" string!'
+ '\n (Your input was: "%s")\n' % window_position
+ )
+ raise Exception(message)
+ window_position = window_position.replace(" ", "")
+ win_x = None
+ win_y = None
+ try:
+ win_x = int(window_position.split(",")[0])
+ win_y = int(window_position.split(",")[1])
+ except Exception:
+ message = (
+ '\n\n Expecting integer values for "x,y"!'
+ '\n (window_position input was: "%s")\n'
+ % window_position
+ )
+ raise Exception(message)
+ settings.WINDOW_START_X = win_x
+ settings.WINDOW_START_Y = win_y
if sb.window_size:
window_size = sb.window_size
if window_size.count(",") != 1:
@@ -938,9 +980,11 @@ def get_configured_sb(context):
sb_config.is_pytest = False
sb_config.is_nosetest = False
sb_config.is_context_manager = False
+ sb_config.window_position = sb.window_position
sb_config.window_size = sb.window_size
sb_config.maximize_option = sb.maximize_option
sb_config.xvfb = sb.xvfb
+ sb_config.xvfb_metrics = sb.xvfb_metrics
sb_config.reuse_class_session = sb._reuse_class_session
sb_config.save_screenshot = sb.save_screenshot_after_test
sb_config.no_screenshot = sb.no_screenshot_after_test
@@ -1162,12 +1206,10 @@ def behave_dashboard_prepare():
sb_config.item_count_untested = sb_config.item_count
dash_path = os.path.join(os.getcwd(), "dashboard.html")
star_len = len("Dashboard: ") + len(dash_path)
- try:
+ with suppress(Exception):
terminal_size = os.get_terminal_size().columns
if terminal_size > 30 and star_len > terminal_size:
star_len = terminal_size
- except Exception:
- pass
stars = "*" * star_len
c1 = ""
cr = ""
@@ -1263,7 +1305,7 @@ def _perform_behave_unconfigure_():
def do_final_driver_cleanup_as_needed():
- try:
+ with suppress(Exception):
if hasattr(sb_config, "last_driver") and sb_config.last_driver:
if (
not is_windows
@@ -1271,8 +1313,6 @@ def do_final_driver_cleanup_as_needed():
or sb_config.last_driver.service.process
):
sb_config.last_driver.quit()
- except Exception:
- pass
def _perform_behave_terminal_summary_():
@@ -1281,12 +1321,10 @@ def _perform_behave_terminal_summary_():
)
dash_path = os.path.join(os.getcwd(), "dashboard.html")
equals_len = len("Dashboard: ") + len(dash_path)
- try:
+ with suppress(Exception):
terminal_size = os.get_terminal_size().columns
if terminal_size > 30 and equals_len > terminal_size:
equals_len = terminal_size
- except Exception:
- pass
equals = "=" * (equals_len + 2)
c2 = ""
cr = ""
diff --git a/seleniumbase/config/settings.py b/seleniumbase/config/settings.py
index 0b2822100f3..c0980f01f02 100644
--- a/seleniumbase/config/settings.py
+++ b/seleniumbase/config/settings.py
@@ -110,6 +110,10 @@
# (This applies when using --proxy=[PROXY_STRING] for using a proxy server.)
RAISE_INVALID_PROXY_STRING_EXCEPTION = True
+# Default browser coordinates when opening new windows for tests.
+WINDOW_START_X = 20
+WINDOW_START_Y = 54
+
# Default browser resolutions when opening new windows for tests.
# (Headless resolutions take priority, and include all browsers.)
# (Firefox starts maximized by default when running in GUI Mode.)
diff --git a/seleniumbase/console_scripts/sb_behave_gui.py b/seleniumbase/console_scripts/sb_behave_gui.py
index 2708d6e36c0..6902c46ad39 100644
--- a/seleniumbase/console_scripts/sb_behave_gui.py
+++ b/seleniumbase/console_scripts/sb_behave_gui.py
@@ -16,16 +16,16 @@
import colorama
import subprocess
import sys
+import tkinter as tk
+from seleniumbase.fixtures import shared_utils
+from tkinter.scrolledtext import ScrolledText
-if sys.version_info <= (3, 7):
+if sys.version_info <= (3, 8):
current_version = ".".join(str(ver) for ver in sys.version_info[:3])
raise Exception(
- "\n* SBase Commander requires Python 3.7 or newer!"
+ "\n* SBase Commander requires Python 3.8 or newer!"
"\n** You are currently using Python %s" % current_version
)
-from seleniumbase.fixtures import shared_utils
-import tkinter as tk # noqa: E402
-from tkinter.scrolledtext import ScrolledText # noqa: E402
def set_colors(use_colors):
diff --git a/seleniumbase/console_scripts/sb_caseplans.py b/seleniumbase/console_scripts/sb_caseplans.py
index 57e287c08b4..63c5a3a0aae 100644
--- a/seleniumbase/console_scripts/sb_caseplans.py
+++ b/seleniumbase/console_scripts/sb_caseplans.py
@@ -20,17 +20,17 @@
import os
import subprocess
import sys
+import tkinter as tk
+from seleniumbase.fixtures import shared_utils
+from tkinter import messagebox
+from tkinter.scrolledtext import ScrolledText
-if sys.version_info <= (3, 7):
+if sys.version_info <= (3, 8):
current_version = ".".join(str(ver) for ver in sys.version_info[:3])
raise Exception(
- "\n* SBase Case Plans Generator requires Python 3.7 or newer!"
+ "\n* SBase Case Plans Generator requires Python 3.8 or newer!"
"\n** You are currently using Python %s" % current_version
)
-from seleniumbase.fixtures import shared_utils
-import tkinter as tk # noqa: E402
-from tkinter import messagebox # noqa: E402
-from tkinter.scrolledtext import ScrolledText # noqa: E402
def set_colors(use_colors):
diff --git a/seleniumbase/console_scripts/sb_commander.py b/seleniumbase/console_scripts/sb_commander.py
index bc23cc6985c..a5a15b8d434 100644
--- a/seleniumbase/console_scripts/sb_commander.py
+++ b/seleniumbase/console_scripts/sb_commander.py
@@ -21,16 +21,16 @@
import os
import subprocess
import sys
+import tkinter as tk
+from seleniumbase.fixtures import shared_utils
+from tkinter.scrolledtext import ScrolledText
-if sys.version_info <= (3, 7):
+if sys.version_info <= (3, 8):
current_version = ".".join(str(ver) for ver in sys.version_info[:3])
raise Exception(
- "\n* SBase Commander requires Python 3.7 or newer!"
+ "\n* SBase Commander requires Python 3.8 or newer!"
"\n** You are currently using Python %s" % current_version
)
-from seleniumbase.fixtures import shared_utils
-import tkinter as tk # noqa: E402
-from tkinter.scrolledtext import ScrolledText # noqa: E402
def set_colors(use_colors):
diff --git a/seleniumbase/console_scripts/sb_recorder.py b/seleniumbase/console_scripts/sb_recorder.py
index 0e9e3365f67..173ee954839 100644
--- a/seleniumbase/console_scripts/sb_recorder.py
+++ b/seleniumbase/console_scripts/sb_recorder.py
@@ -18,23 +18,23 @@
import os
import subprocess
import sys
+import tkinter as tk
from seleniumbase import config as sb_config
from seleniumbase.fixtures import page_utils
from seleniumbase.fixtures import shared_utils
+from tkinter import messagebox
sb_config.rec_subprocess_p = None
sb_config.rec_subprocess_used = False
sys_executable = sys.executable
if " " in sys_executable:
sys_executable = "python"
-if sys.version_info <= (3, 7):
+if sys.version_info <= (3, 8):
current_version = ".".join(str(ver) for ver in sys.version_info[:3])
raise Exception(
- "\n* Recorder Desktop requires Python 3.7 or newer!"
+ "\n* Recorder Desktop requires Python 3.8 or newer!"
"\n*** You are currently using Python %s" % current_version
)
-import tkinter as tk # noqa: E402
-from tkinter import messagebox # noqa: E402
def set_colors(use_colors):
diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py
index 2a2dd82963e..03845961959 100644
--- a/seleniumbase/core/browser_launcher.py
+++ b/seleniumbase/core/browser_launcher.py
@@ -9,6 +9,7 @@
import types
import urllib3
import warnings
+from contextlib import suppress
from selenium import webdriver
from selenium.common.exceptions import ElementClickInterceptedException
from selenium.common.exceptions import InvalidSessionIdException
@@ -277,7 +278,7 @@ def chromedriver_on_path():
def get_uc_driver_version(full=False):
uc_driver_version = None
if os.path.exists(LOCAL_UC_DRIVER):
- try:
+ with suppress(Exception):
output = subprocess.check_output(
'"%s" --version' % LOCAL_UC_DRIVER, shell=True
)
@@ -292,8 +293,6 @@ def get_uc_driver_version(full=False):
uc_driver_version = full_version
else:
uc_driver_version = output
- except Exception:
- pass
return uc_driver_version
@@ -372,7 +371,7 @@ def uc_special_open_if_cf(
):
if url.startswith("http:") or url.startswith("https:"):
special = False
- try:
+ with suppress(Exception):
req_get = requests_get(url, proxy_string)
status_str = str(req_get.status_code)
if (
@@ -384,8 +383,6 @@ def uc_special_open_if_cf(
special = True
if status_str == "403" or status_str == "429":
time.sleep(0.06) # Forbidden / Blocked! (Wait first!)
- except Exception:
- pass
if special:
time.sleep(0.05)
with driver:
@@ -416,13 +413,11 @@ def uc_special_open_if_cf(
"mobile": True
}
)
- try:
+ with suppress(Exception):
driver.execute_cdp_cmd(
'Emulation.setDeviceMetricsOverride',
set_device_metrics_override
)
- except Exception:
- pass
if not mobile_emulator:
page_actions.switch_to_window(
driver, driver.window_handles[-1], 2
@@ -529,13 +524,11 @@ def uc_click(
timeout=settings.SMALL_TIMEOUT,
reconnect_time=None,
):
- try:
+ with suppress(Exception):
rct = float(by) # Add shortcut: driver.uc_click(selector, RCT)
if not reconnect_time:
reconnect_time = rct
by = "css selector"
- except Exception:
- pass
element = driver.wait_for_selector(selector, by=by, timeout=timeout)
tag_name = element.tag_name
if not tag_name == "span" and not tag_name == "input": # Must be "visible"
@@ -574,7 +567,7 @@ def install_pyautogui_if_missing(driver):
with pip_find_lock: # Prevent issues with multiple processes
try:
import pyautogui
- try:
+ with suppress(Exception):
use_pyautogui_ver = constants.PyAutoGUI.VER
if pyautogui.__version__ != use_pyautogui_ver:
del pyautogui
@@ -582,8 +575,6 @@ def install_pyautogui_if_missing(driver):
"pyautogui", version=use_pyautogui_ver
)
import pyautogui
- except Exception:
- pass
except Exception:
print("\nPyAutoGUI required! Installing now...")
shared_utils.pip_install(
@@ -602,16 +593,32 @@ def install_pyautogui_if_missing(driver):
and not (sb_config.headless or sb_config.headless2)
):
from sbvirtualdisplay import Display
- try:
+ xvfb_width = 1366
+ xvfb_height = 768
+ if (
+ hasattr(sb_config, "_xvfb_width")
+ and sb_config._xvfb_width
+ and isinstance(sb_config._xvfb_width, int)
+ and hasattr(sb_config, "_xvfb_height")
+ and sb_config._xvfb_height
+ and isinstance(sb_config._xvfb_height, int)
+ ):
+ xvfb_width = sb_config._xvfb_width
+ xvfb_height = sb_config._xvfb_height
+ if xvfb_width < 1024:
+ xvfb_width = 1024
+ sb_config._xvfb_width = xvfb_width
+ if xvfb_height < 768:
+ xvfb_height = 768
+ sb_config._xvfb_height = xvfb_height
+ with suppress(Exception):
xvfb_display = Display(
visible=True,
- size=(1366, 768),
+ size=(xvfb_width, xvfb_height),
backend="xvfb",
use_xauth=True,
)
xvfb_display.start()
- except Exception:
- pass
def get_configured_pyautogui(pyautogui_copy):
@@ -778,12 +785,10 @@ def uc_gui_click_x_y(driver, x, y, timeframe=0.25):
y = y * width_ratio
_uc_gui_click_x_y(driver, x, y, timeframe=timeframe, uc_lock=False)
return
- try:
+ with suppress(Exception):
page_actions.switch_to_window(
driver, driver.current_window_handle, 2, uc_lock=False
)
- except Exception:
- pass
_uc_gui_click_x_y(driver, x, y, timeframe=timeframe, uc_lock=False)
@@ -986,10 +991,8 @@ def _uc_gui_click_captcha(
driver.uc_open_with_disconnect(driver.current_url, 3.8)
else:
driver.disconnect()
- try:
- _uc_gui_click_x_y(driver, x, y, timeframe=0.54321)
- except Exception:
- pass
+ with suppress(Exception):
+ _uc_gui_click_x_y(driver, x, y, timeframe=0.32)
reconnect_time = (float(constants.UC.RECONNECT_TIME) / 2.0) + 0.6
if IS_LINUX:
reconnect_time = constants.UC.RECONNECT_TIME + 0.2
@@ -1031,12 +1034,12 @@ def _uc_gui_click_captcha(
return
if blind:
driver.uc_open_with_disconnect(driver.current_url, 3.8)
- _uc_gui_click_x_y(driver, x, y, timeframe=1.05)
+ _uc_gui_click_x_y(driver, x, y, timeframe=0.32)
else:
driver.uc_open_with_reconnect(driver.current_url, 3.8)
if _on_a_captcha_page(driver):
driver.disconnect()
- _uc_gui_click_x_y(driver, x, y, timeframe=1.05)
+ _uc_gui_click_x_y(driver, x, y, timeframe=0.32)
driver.reconnect(reconnect_time)
@@ -1216,13 +1219,11 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
and sb_config._saved_cf_tab_count
):
driver.uc_open_with_disconnect(driver.current_url, 3.8)
- try:
+ with suppress(Exception):
for i in range(sb_config._saved_cf_tab_count):
pyautogui.press("\t")
time.sleep(0.027)
pyautogui.press(" ")
- except Exception:
- pass
else:
driver.disconnect()
pyautogui.press(" ")
@@ -1669,18 +1670,49 @@ def _set_chrome_options(
chrome_options.add_experimental_option(
"mobileEmulation", emulator_settings
)
+ # Handle Window Position
+ if (headless or headless2) and IS_WINDOWS:
+ # https://stackoverflow.com/a/78999088/7058266
+ chrome_options.add_argument("--window-position=-2400,-2400")
+ else:
+ if (
+ hasattr(settings, "WINDOW_START_X")
+ and isinstance(settings.WINDOW_START_X, int)
+ and hasattr(settings, "WINDOW_START_Y")
+ and isinstance(settings.WINDOW_START_Y, int)
+ ):
+ chrome_options.add_argument(
+ "--window-position=%s,%s" % (
+ settings.WINDOW_START_X, settings.WINDOW_START_Y
+ )
+ )
+ # Handle Window Size
if headless or headless2:
- chrome_options.add_argument(
- "--window-size=%s,%s" % (
- settings.HEADLESS_START_WIDTH, settings.HEADLESS_START_HEIGHT
+ if (
+ hasattr(settings, "HEADLESS_START_WIDTH")
+ and isinstance(settings.HEADLESS_START_WIDTH, int)
+ and hasattr(settings, "HEADLESS_START_HEIGHT")
+ and isinstance(settings.HEADLESS_START_HEIGHT, int)
+ ):
+ chrome_options.add_argument(
+ "--window-size=%s,%s" % (
+ settings.HEADLESS_START_WIDTH,
+ settings.HEADLESS_START_HEIGHT,
+ )
)
- )
else:
- chrome_options.add_argument(
- "--window-size=%s,%s" % (
- settings.CHROME_START_WIDTH, settings.CHROME_START_HEIGHT
+ if (
+ hasattr(settings, "CHROME_START_WIDTH")
+ and isinstance(settings.CHROME_START_WIDTH, int)
+ and hasattr(settings, "CHROME_START_HEIGHT")
+ and isinstance(settings.CHROME_START_HEIGHT, int)
+ ):
+ chrome_options.add_argument(
+ "--window-size=%s,%s" % (
+ settings.CHROME_START_WIDTH,
+ settings.CHROME_START_HEIGHT,
+ )
)
- )
if (
not proxy_auth
and not disable_csp
@@ -1858,8 +1890,12 @@ def _set_chrome_options(
binary_location = binary_loc
extra_disabled_features = []
if chromium_arg:
- # Can be a comma-separated list of Chromium args
- chromium_arg_list = chromium_arg.split(",")
+ # Can be a comma-separated list of Chromium args or a list
+ chromium_arg_list = None
+ if isinstance(chromium_arg, (list, tuple)):
+ chromium_arg_list = chromium_arg
+ else:
+ chromium_arg_list = chromium_arg.split(",")
for chromium_arg_item in chromium_arg_list:
chromium_arg_item = chromium_arg_item.strip()
if not chromium_arg_item.startswith("--"):
@@ -1868,13 +1904,11 @@ def _set_chrome_options(
else:
chromium_arg_item = "--" + chromium_arg_item
if "remote-debugging-port=" in chromium_arg_item:
- try:
+ with suppress(Exception):
# Extra processing for UC Mode
chrome_options._remote_debugging_port = int(
chromium_arg_item.split("remote-debugging-port=")[1]
)
- except Exception:
- pass
if "set-binary" in chromium_arg_item and not binary_location:
br_app = "google-chrome"
binary_loc = detect_b_ver.get_binary_location(
@@ -2526,14 +2560,12 @@ def get_remote_driver(
try:
from seleniumwire import webdriver
import blinker
- try:
+ with suppress(Exception):
use_blinker_ver = constants.SeleniumWire.BLINKER_VER
if blinker.__version__ != use_blinker_ver:
shared_utils.pip_install(
"blinker", version=use_blinker_ver
)
- except Exception:
- pass
del blinker
except Exception:
shared_utils.pip_install(
@@ -2957,14 +2989,12 @@ def get_local_driver(
try:
from seleniumwire import webdriver
import blinker
- try:
+ with suppress(Exception):
use_blinker_ver = constants.SeleniumWire.BLINKER_VER
if blinker.__version__ != use_blinker_ver:
shared_utils.pip_install(
"blinker", version=use_blinker_ver
)
- except Exception:
- pass
del blinker
except Exception:
shared_utils.pip_install(
@@ -3255,7 +3285,7 @@ def get_local_driver(
edge_driver_version = None
edgedriver_upgrade_needed = False
if os.path.exists(LOCAL_EDGEDRIVER):
- try:
+ with suppress(Exception):
output = subprocess.check_output(
'"%s" --version' % LOCAL_EDGEDRIVER, shell=True
)
@@ -3281,8 +3311,6 @@ def get_local_driver(
edge_driver_version = output
if driver_version == "keep":
driver_version = edge_driver_version
- except Exception:
- pass
use_version = find_edgedriver_version_to_use(
use_version, driver_version
)
@@ -3386,7 +3414,7 @@ def get_local_driver(
edge_options.add_argument("--headless=new")
elif headless and undetectable:
# (For later: UC Mode doesn't support Edge now)
- try:
+ with suppress(Exception):
if int(use_version) >= 109:
edge_options.add_argument("--headless=new")
elif (
@@ -3396,8 +3424,6 @@ def get_local_driver(
edge_options.add_argument("--headless=chrome")
else:
pass # Will need Xvfb on Linux
- except Exception:
- pass
elif headless:
if "--headless" not in edge_options.arguments:
edge_options.add_argument("--headless")
@@ -3422,20 +3448,49 @@ def get_local_driver(
edge_options.add_experimental_option(
"mobileEmulation", emulator_settings
)
+ # Handle Window Position
+ if (headless or headless2) and IS_WINDOWS:
+ # https://stackoverflow.com/a/78999088/7058266
+ edge_options.add_argument("--window-position=-2400,-2400")
+ else:
+ if (
+ hasattr(settings, "WINDOW_START_X")
+ and isinstance(settings.WINDOW_START_X, int)
+ and hasattr(settings, "WINDOW_START_Y")
+ and isinstance(settings.WINDOW_START_Y, int)
+ ):
+ edge_options.add_argument(
+ "--window-position=%s,%s" % (
+ settings.WINDOW_START_X, settings.WINDOW_START_Y
+ )
+ )
+ # Handle Window Size
if headless or headless2:
- edge_options.add_argument(
- "--window-size=%s,%s" % (
- settings.HEADLESS_START_WIDTH,
- settings.HEADLESS_START_HEIGHT,
+ if (
+ hasattr(settings, "HEADLESS_START_WIDTH")
+ and isinstance(settings.HEADLESS_START_WIDTH, int)
+ and hasattr(settings, "HEADLESS_START_HEIGHT")
+ and isinstance(settings.HEADLESS_START_HEIGHT, int)
+ ):
+ edge_options.add_argument(
+ "--window-size=%s,%s" % (
+ settings.HEADLESS_START_WIDTH,
+ settings.HEADLESS_START_HEIGHT,
+ )
)
- )
else:
- edge_options.add_argument(
- "--window-size=%s,%s" % (
- settings.CHROME_START_WIDTH,
- settings.CHROME_START_HEIGHT,
+ if (
+ hasattr(settings, "CHROME_START_WIDTH")
+ and isinstance(settings.CHROME_START_WIDTH, int)
+ and hasattr(settings, "CHROME_START_HEIGHT")
+ and isinstance(settings.CHROME_START_HEIGHT, int)
+ ):
+ edge_options.add_argument(
+ "--window-size=%s,%s" % (
+ settings.CHROME_START_WIDTH,
+ settings.CHROME_START_HEIGHT,
+ )
)
- )
if user_data_dir and not is_using_uc(undetectable, browser_name):
abs_path = os.path.abspath(user_data_dir)
edge_options.add_argument("--user-data-dir=%s" % abs_path)
@@ -3569,7 +3624,11 @@ def get_local_driver(
set_binary = False
if chromium_arg:
# Can be a comma-separated list of Chromium args
- chromium_arg_list = chromium_arg.split(",")
+ chromium_arg_list = None
+ if isinstance(chromium_arg, (list, tuple)):
+ chromium_arg_list = chromium_arg
+ else:
+ chromium_arg_list = chromium_arg.split(",")
for chromium_arg_item in chromium_arg_list:
chromium_arg_item = chromium_arg_item.strip()
if not chromium_arg_item.startswith("--"):
@@ -3664,19 +3723,15 @@ def get_local_driver(
constants.MultiBrowser.DRIVER_FIXING_LOCK
)
with edgedriver_fixing_lock:
- try:
+ with suppress(Exception):
if not _was_driver_repaired():
_repair_edgedriver(edge_version)
_mark_driver_repaired()
- except Exception:
- pass
else:
- try:
+ with suppress(Exception):
if not _was_driver_repaired():
_repair_edgedriver(edge_version)
_mark_driver_repaired()
- except Exception:
- pass
driver = Edge(service=service, options=edge_options)
return extend_driver(driver)
elif browser_name == constants.Browser.SAFARI:
@@ -3828,7 +3883,7 @@ def get_local_driver(
ch_driver_version = None
path_chromedriver = chromedriver_on_path()
if os.path.exists(LOCAL_CHROMEDRIVER):
- try:
+ with suppress(Exception):
output = subprocess.check_output(
'"%s" --version' % LOCAL_CHROMEDRIVER, shell=True
)
@@ -3842,8 +3897,6 @@ def get_local_driver(
ch_driver_version = output
if driver_version == "keep":
driver_version = ch_driver_version
- except Exception:
- pass
elif path_chromedriver:
try:
make_driver_executable_if_not(path_chromedriver)
@@ -3852,7 +3905,7 @@ def get_local_driver(
"\nWarning: Could not make chromedriver"
" executable: %s" % e
)
- try:
+ with suppress(Exception):
output = subprocess.check_output(
'"%s" --version' % path_chromedriver, shell=True
)
@@ -3866,8 +3919,6 @@ def get_local_driver(
ch_driver_version = output
if driver_version == "keep":
use_version = ch_driver_version
- except Exception:
- pass
disable_build_check = True
uc_driver_version = None
if is_using_uc(undetectable, browser_name):
@@ -4060,7 +4111,7 @@ def get_local_driver(
if IS_ARM_MAC and use_uc:
intel_for_uc = True # Use Intel driver for UC Mode
if os.path.exists(LOCAL_CHROMEDRIVER):
- try:
+ with suppress(Exception):
output = subprocess.check_output(
'"%s" --version' % LOCAL_CHROMEDRIVER,
shell=True,
@@ -4073,8 +4124,6 @@ def get_local_driver(
output = full_ch_driver_version.split(".")[0]
if int(output) >= 2:
ch_driver_version = output
- except Exception:
- pass
if (
(
not use_uc
@@ -4212,7 +4261,7 @@ def get_local_driver(
chrome_options.add_argument(
"--user-agent=%s" % user_agent
)
- try:
+ with suppress(Exception):
if (
(
not user_agent
@@ -4318,7 +4367,7 @@ def get_local_driver(
service=service,
options=headless_options,
)
- try:
+ with suppress(Exception):
user_agent = driver.execute_script(
"return navigator.userAgent;"
)
@@ -4340,11 +4389,7 @@ def get_local_driver(
"--user-agent=%s" % user_agent
)
sb_config.uc_agent_cache = user_agent
- except Exception:
- pass
driver.quit()
- except Exception:
- pass
uc_path = None
if os.path.exists(LOCAL_UC_DRIVER):
uc_path = LOCAL_UC_DRIVER
@@ -4667,13 +4712,11 @@ def get_local_driver(
"mobile": True
}
)
- try:
+ with suppress(Exception):
driver.execute_cdp_cmd(
'Emulation.setDeviceMetricsOverride',
set_device_metrics_override
)
- except Exception:
- pass
return extend_driver(driver)
else: # Running headless on Linux (and not using --uc)
try:
@@ -4719,23 +4762,19 @@ def get_local_driver(
)
with chromedr_fixing_lock:
if not _was_driver_repaired():
- try:
+ with suppress(Exception):
_repair_chromedriver(
chrome_options, chrome_options, mcv
)
_mark_driver_repaired()
- except Exception:
- pass
else:
if not _was_driver_repaired():
- try:
+ with suppress(Exception):
_repair_chromedriver(
chrome_options, chrome_options, mcv
)
- except Exception:
- pass
_mark_driver_repaired()
- try:
+ with suppress(Exception):
service = ChromeService(
log_output=os.devnull,
service_args=["--disable-build-check"],
@@ -4745,8 +4784,6 @@ def get_local_driver(
options=chrome_options,
)
return extend_driver(driver)
- except Exception:
- pass
# Use the virtual display on Linux during headless errors
logging.debug(
"\nWarning: Chrome failed to launch in"
@@ -4767,14 +4804,12 @@ def get_local_driver(
if is_using_uc(undetectable, browser_name):
raise
# Try again if Chrome didn't launch
- try:
+ with suppress(Exception):
service = ChromeService(service_args=["--disable-build-check"])
driver = webdriver.Chrome(
service=service, options=chrome_options
)
return extend_driver(driver)
- except Exception:
- pass
if user_data_dir:
print("\nUnable to set user_data_dir while starting Chrome!\n")
raise
diff --git a/seleniumbase/core/mysql.py b/seleniumbase/core/mysql.py
index 1a2edb6ce4d..a1bf47ddc84 100644
--- a/seleniumbase/core/mysql.py
+++ b/seleniumbase/core/mysql.py
@@ -35,10 +35,7 @@ def __init__(self, database_env="test", conf_creds=None):
import cryptography # noqa: F401
import pymysql
except Exception:
- if sys.version_info < (3, 7):
- shared_utils.pip_install("PyMySQL[rsa]", version="1.0.2")
- else:
- shared_utils.pip_install("PyMySQL[rsa]", version="1.1.0")
+ shared_utils.pip_install("PyMySQL[rsa]", version="1.1.1")
import pymysql
db_server = settings.DB_HOST
db_port = settings.DB_PORT
diff --git a/seleniumbase/core/recorder_helper.py b/seleniumbase/core/recorder_helper.py
index 3a13729710c..40f9cef4a6f 100644
--- a/seleniumbase/core/recorder_helper.py
+++ b/seleniumbase/core/recorder_helper.py
@@ -422,9 +422,14 @@ def generate_sbase_code(srt_actions):
):
import unicodedata
- action[1][0] = unicodedata.normalize("NFKC", action[1][0])
- action[1][0] = action[1][0].replace("\n", "\\n")
- action[1][0] = action[1][0].replace("\u00B6", "")
+ text_list = False
+ try:
+ action[1][0] = unicodedata.normalize("NFKC", action[1][0])
+ action[1][0] = action[1][0].replace("\n", "\\n")
+ action[1][0] = action[1][0].replace("\u00B6", "")
+ except Exception:
+ text_list = True
+
method = "assert_text"
if action[0] == "as_et":
method = "assert_exact_text"
@@ -437,7 +442,17 @@ def generate_sbase_code(srt_actions):
elif action[0] == "da_et":
method = "deferred_assert_exact_text"
if action[1][1] != "html":
- if '"' not in action[1][0] and '"' not in action[1][1]:
+ if text_list and '"' not in action[1][1]:
+ sb_actions.append(
+ 'self.%s(%s, "%s")'
+ % (method, action[1][0], action[1][1])
+ )
+ elif text_list and "'" not in action[1][1]:
+ sb_actions.append(
+ "self.%s(%s, '%s')"
+ % (method, action[1][0], action[1][1])
+ )
+ elif '"' not in action[1][0] and '"' not in action[1][1]:
sb_actions.append(
'self.%s("%s", "%s")'
% (method, action[1][0], action[1][1])
@@ -458,7 +473,11 @@ def generate_sbase_code(srt_actions):
% (method, action[1][0], action[1][1])
)
else:
- if '"' not in action[1][0]:
+ if text_list:
+ sb_actions.append(
+ 'self.%s(%s)' % (method, action[1][0])
+ )
+ elif '"' not in action[1][0]:
sb_actions.append(
'self.%s("%s")' % (method, action[1][0])
)
diff --git a/seleniumbase/core/sb_driver.py b/seleniumbase/core/sb_driver.py
index 3ca7a4955f1..02dd0de4ab0 100644
--- a/seleniumbase/core/sb_driver.py
+++ b/seleniumbase/core/sb_driver.py
@@ -1,4 +1,5 @@
"""Add new methods to extend the driver"""
+from contextlib import suppress
from selenium.webdriver.remote.webelement import WebElement
from seleniumbase.fixtures import js_utils
from seleniumbase.fixtures import page_actions
@@ -36,10 +37,8 @@ def locator(self, selector, by=None):
selector, by = page_utils.swap_selector_and_by_if_reversed(
selector, by
)
- try:
+ with suppress(Exception):
return self.driver.default_find_element(by=by, value=selector)
- except Exception:
- pass
raise Exception('No such Element: {%s} (by="%s")!' % (selector, by))
def get_attribute(self, selector, attribute, by="css selector"):
diff --git a/seleniumbase/core/settings_parser.py b/seleniumbase/core/settings_parser.py
index c45a20e748b..c13b8cc84c4 100644
--- a/seleniumbase/core/settings_parser.py
+++ b/seleniumbase/core/settings_parser.py
@@ -113,6 +113,10 @@ def set_settings(settings_file):
settings.RAISE_INVALID_PROXY_STRING_EXCEPTION = override_settings[
key
]
+ elif key == "WINDOW_START_X":
+ settings.WINDOW_START_X = override_settings[key]
+ elif key == "WINDOW_START_Y":
+ settings.WINDOW_START_Y = override_settings[key]
elif key == "CHROME_START_WIDTH":
settings.CHROME_START_WIDTH = override_settings[key]
elif key == "CHROME_START_HEIGHT":
diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py
index 241d4f37300..e8a81c4a002 100644
--- a/seleniumbase/fixtures/base_case.py
+++ b/seleniumbase/fixtures/base_case.py
@@ -33,6 +33,7 @@ def test_anything(self):
Code becomes greatly simplified and easier to maintain."""
import codecs
+import colorama
import fasteners
import json
import logging
@@ -45,7 +46,7 @@ def test_anything(self):
import time
import unittest
import urllib3
-from contextlib import contextmanager
+from contextlib import contextmanager, suppress
from selenium.common.exceptions import (
ElementClickInterceptedException as ECI_Exception,
ElementNotInteractableException as ENI_Exception,
@@ -57,6 +58,7 @@ def test_anything(self):
TimeoutException,
WebDriverException,
)
+from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.remote.remote_connection import LOGGER
@@ -73,9 +75,11 @@ def test_anything(self):
VisualException,
)
from seleniumbase.config import settings
+from seleniumbase.core import browser_launcher
from seleniumbase.core import download_helper
from seleniumbase.core import log_helper
from seleniumbase.core import session_helper
+from seleniumbase.core import visual_helper
from seleniumbase.fixtures import constants
from seleniumbase.fixtures import css_to_xpath
from seleniumbase.fixtures import js_utils
@@ -180,6 +184,8 @@ def __initialize_variables(self):
self._chart_series_count = {}
self._tour_steps = {}
self._xvfb_display = None
+ self._xvfb_width = None
+ self._xvfb_height = None
@classmethod
def main(self, name, file, *args):
@@ -221,10 +227,8 @@ def open(self, url):
if self.__needs_minimum_wait():
time.sleep(0.04)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
url = str(url).strip() # Remove leading and trailing whitespace
if not self.__looks_like_a_page_url(url):
# url should start with one of the following:
@@ -352,18 +356,12 @@ def open(self, url):
if self.undetectable:
self.__uc_frame_layer = 0
if self.demo_mode:
- if (
- self.driver.current_url.startswith("http")
- or self.driver.current_url.startswith("file")
- or self.driver.current_url.startswith("data")
- ):
+ if self.driver.current_url.startswith(("http", "file", "data")):
if not js_utils.is_jquery_activated(self.driver):
- try:
+ with suppress(Exception):
js_utils.add_js_link(
self.driver, constants.JQuery.MIN_JS
)
- except Exception:
- pass
self.__demo_mode_pause_if_active()
def get(self, url):
@@ -421,10 +419,8 @@ def click(
if scroll and not self.demo_mode and not self.slow_mode:
self.__scroll_to_element(element, selector, by)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
try:
if (
@@ -438,7 +434,7 @@ def click(
href = None
new_tab = False
onclick = None
- try:
+ with suppress(Exception):
if self.headless and element.tag_name.lower() == "a":
# Handle a special case of opening a new tab (headless)
href = element.get_attribute("href").strip()
@@ -448,20 +444,14 @@ def click(
new_tab = True
if new_tab and self.__looks_like_a_page_url(href):
if onclick:
- try:
+ with suppress(Exception):
self.execute_script(onclick)
- except Exception:
- pass
current_window = self.driver.current_window_handle
self.open_new_window()
- try:
+ with suppress(Exception):
self.open(href)
- except Exception:
- pass
self.switch_to_window(current_window)
return
- except Exception:
- pass
# Normal click
self.__element_click(element)
except Stale_Exception:
@@ -474,10 +464,8 @@ def click(
timeout=timeout,
original_selector=original_selector,
)
- try:
+ with suppress(Exception):
self.__scroll_to_element(element, selector, by)
- except Exception:
- pass
if self.browser == "safari" and by == By.LINK_TEXT:
self.__jquery_click(selector, by=by)
elif self.browser == "safari":
@@ -485,7 +473,7 @@ def click(
else:
self.__element_click(element)
except ENI_Exception as e:
- try:
+ with suppress(Exception):
if (
"element has zero size" in e.msg
and element.tag_name.lower() == "a"
@@ -497,8 +485,6 @@ def click(
if self.__needs_minimum_wait():
time.sleep(0.04)
return
- except Exception:
- pass
self.wait_for_ready_state_complete()
time.sleep(0.1)
element = page_actions.wait_for_element_visible(
@@ -511,12 +497,10 @@ def click(
if not page_actions.is_element_clickable(
self.driver, selector, by
):
- try:
+ with suppress(Exception):
self.wait_for_element_clickable(
selector, by, timeout=1.8
)
- except Exception:
- pass # Find out which element would get the click instead
element = page_actions.wait_for_element_visible(
self.driver,
selector,
@@ -527,7 +511,7 @@ def click(
href = None
new_tab = False
onclick = None
- try:
+ with suppress(Exception):
if element.tag_name.lower() == "a":
# Handle a special case of opening a new tab (non-headless)
href = element.get_attribute("href").strip()
@@ -537,20 +521,14 @@ def click(
new_tab = True
if new_tab and self.__looks_like_a_page_url(href):
if onclick:
- try:
+ with suppress(Exception):
self.execute_script(onclick)
- except Exception:
- pass
current_window = self.driver.current_window_handle
self.open_new_window()
- try:
+ with suppress(Exception):
self.open(href)
- except Exception:
- pass
self.switch_to_window(current_window)
return
- except Exception:
- pass
if scroll and not self.demo_mode and not self.slow_mode:
self.__scroll_to_element(element, selector, by)
if self.browser == "firefox" or self.browser == "safari":
@@ -632,10 +610,8 @@ def click(
self.switch_to_window(-1)
if settings.WAIT_FOR_RSC_ON_CLICKS:
if not self.undetectable:
- try:
+ with suppress(Exception):
self.wait_for_ready_state_complete()
- except Exception:
- pass
if self.__needs_minimum_wait() or self.browser == "safari":
time.sleep(0.05)
else:
@@ -643,10 +619,8 @@ def click(
else:
if not self.undetectable:
# A smaller subset of self.wait_for_ready_state_complete()
- try:
+ with suppress(Exception):
self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)
- except Exception:
- pass
if self.__needs_minimum_wait() or self.browser == "safari":
time.sleep(0.045)
try:
@@ -656,10 +630,8 @@ def click(
if self.__needs_minimum_wait():
time.sleep(0.075)
except Exception:
- try:
+ with suppress(Exception):
self.wait_for_ready_state_complete()
- except Exception:
- pass
if self.__needs_minimum_wait():
time.sleep(0.05)
else:
@@ -697,8 +669,6 @@ def slow_click(self, selector, by="css selector", timeout=None):
self.click(selector, by=by, timeout=timeout, delay=0.25)
def double_click(self, selector, by="css selector", timeout=None):
- from selenium.webdriver.common.action_chains import ActionChains
-
self.__check_scope()
if not timeout:
timeout = settings.SMALL_TIMEOUT
@@ -729,10 +699,8 @@ def double_click(self, selector, by="css selector", timeout=None):
original_selector=original_selector,
)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
try:
if self.browser == "safari":
# Jump to the "except" block where the other script should work
@@ -782,8 +750,6 @@ def double_click(self, selector, by="css selector", timeout=None):
def context_click(self, selector, by="css selector", timeout=None):
"""(A context click is a right-click that opens a context menu.)"""
- from selenium.webdriver.common.action_chains import ActionChains
-
self.__check_scope()
if not timeout:
timeout = settings.SMALL_TIMEOUT
@@ -814,10 +780,8 @@ def context_click(self, selector, by="css selector", timeout=None):
original_selector=original_selector,
)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
try:
if self.browser == "safari":
# Jump to the "except" block where the other script should work
@@ -935,19 +899,15 @@ def update_text(
element = self.wait_for_element_clickable(
selector, by=by, timeout=timeout
)
- try:
+ with suppress(Exception):
element.clear()
- except Exception:
- pass # Clearing the text field first might not be necessary
except Exception:
pass # Clearing the text field first might not be necessary
self.__demo_mode_pause_if_active(tiny=True)
pre_action_url = None
if self.demo_mode:
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
text = self.__get_type_checked_text(text)
try:
if not text.endswith("\n"):
@@ -1057,10 +1017,8 @@ def add_text(self, selector, text, by="css selector", timeout=None):
self.__scroll_to_element(element, selector, by)
pre_action_url = None
if self.demo_mode:
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
text = self.__get_type_checked_text(text)
try:
if not text.endswith("\n"):
@@ -1233,11 +1191,9 @@ def clear(self, selector, by="css selector", timeout=None):
selector, by=by, timeout=timeout
)
element.clear()
- try:
+ with suppress(Exception):
backspaces = Keys.BACK_SPACE * 42 # Autofill Defense
element.send_keys(backspaces)
- except Exception:
- pass
except Exception:
element.clear()
@@ -1334,17 +1290,13 @@ def go_back(self):
if hasattr(self, "recorder_mode") and self.recorder_mode:
self.save_recorded_actions()
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
self.__last_page_load_url = None
self.driver.back()
- try:
+ with suppress(Exception):
if pre_action_url == self.driver.current_url:
self.driver.back() # Again because the page was redirected
- except Exception:
- pass
if self.recorder_mode:
time_stamp = self.execute_script("return Date.now();")
origin = self.get_origin()
@@ -1424,6 +1376,7 @@ def open_if_not_url(self, url):
self.open(url)
def is_element_present(self, selector, by="css selector"):
+ """Returns whether the element exists in the HTML."""
self.wait_for_ready_state_complete()
selector, by = self.__recalculate_selector(selector, by)
if self.__is_shadow_selector(selector):
@@ -1431,6 +1384,7 @@ def is_element_present(self, selector, by="css selector"):
return page_actions.is_element_present(self.driver, selector, by)
def is_element_visible(self, selector, by="css selector"):
+ """Returns whether the element is visible on the page."""
self.wait_for_ready_state_complete()
selector, by = self.__recalculate_selector(selector, by)
if self.__is_shadow_selector(selector):
@@ -1452,6 +1406,7 @@ def is_element_enabled(self, selector, by="css selector"):
return page_actions.is_element_enabled(self.driver, selector, by)
def is_text_visible(self, text, selector="html", by="css selector"):
+ """Returns whether the text substring is visible in the element."""
self.wait_for_ready_state_complete()
time.sleep(0.01)
selector, by = self.__recalculate_selector(selector, by)
@@ -1460,6 +1415,8 @@ def is_text_visible(self, text, selector="html", by="css selector"):
return page_actions.is_text_visible(self.driver, text, selector, by)
def is_exact_text_visible(self, text, selector="html", by="css selector"):
+ """Returns whether the text is exactly equal to the element text.
+ (Leading and trailing whitespace is ignored in the verification.)"""
self.wait_for_ready_state_complete()
time.sleep(0.01)
selector, by = self.__recalculate_selector(selector, by)
@@ -1498,6 +1455,7 @@ def is_attribute_present(
)
def is_link_text_visible(self, link_text):
+ """Returns whether there's an exact match for the link text."""
self.wait_for_ready_state_complete()
time.sleep(0.01)
return page_actions.is_element_visible(
@@ -1505,6 +1463,7 @@ def is_link_text_visible(self, link_text):
)
def is_partial_link_text_visible(self, partial_link_text):
+ """Returns whether there's a substring match for the link text."""
self.wait_for_ready_state_complete()
time.sleep(0.01)
return page_actions.is_element_visible(
@@ -1633,10 +1592,8 @@ def click_link_text(self, link_text, timeout=None):
if self.__needs_minimum_wait():
time.sleep(0.04)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
try:
element = self.wait_for_link_text_visible(link_text, timeout=0.2)
@@ -1707,10 +1664,8 @@ def click_link_text(self, link_text, timeout=None):
# switch to the last one if it exists.
self.switch_to_window(-1)
if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:
- try:
+ with suppress(Exception):
self.wait_for_ready_state_complete()
- except Exception:
- pass
if self.demo_mode:
if self.driver.current_url != pre_action_url:
if not js_utils.is_jquery_activated(self.driver):
@@ -1736,10 +1691,8 @@ def click_partial_link_text(self, partial_link_text, timeout=None):
partial_link_text, timeout=timeout
)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
try:
element = self.wait_for_partial_link_text(
@@ -1822,10 +1775,8 @@ def click_partial_link_text(self, partial_link_text, timeout=None):
# switch to the last one if it exists.
self.switch_to_window(-1)
if settings.WAIT_FOR_RSC_ON_PAGE_LOADS:
- try:
+ with suppress(Exception):
self.wait_for_ready_state_complete()
- except Exception:
- pass
if self.demo_mode:
if self.driver.current_url != pre_action_url:
if not js_utils.is_jquery_activated(self.driver):
@@ -1941,10 +1892,8 @@ def set_attribute(
original_attribute = attribute
original_value = value
if scroll and self.is_element_visible(selector, by=by):
- try:
+ with suppress(Exception):
self.scroll_to(selector, by=by, timeout=timeout)
- except Exception:
- pass
attribute = re.escape(attribute)
attribute = self.__escape_quotes_if_needed(attribute)
value = re.escape(value)
@@ -1989,10 +1938,8 @@ def set_attributes(self, selector, attribute, value, by="css selector"):
attribute,
value,
)
- try:
+ with suppress(Exception):
self.execute_script(script)
- except Exception:
- pass
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
time_stamp = self.execute_script("return Date.now();")
@@ -2021,10 +1968,8 @@ def remove_attribute(
timeout = self.__get_new_timeout(timeout)
selector, by = self.__recalculate_selector(selector, by)
if self.is_element_visible(selector, by=by):
- try:
+ with suppress(Exception):
self.scroll_to(selector, by=by, timeout=timeout)
- except Exception:
- pass
attribute = re.escape(attribute)
attribute = self.__escape_quotes_if_needed(attribute)
css_selector = self.convert_to_css_selector(selector, by=by)
@@ -2053,10 +1998,8 @@ def remove_attributes(self, selector, attribute, by="css selector"):
css_selector,
attribute,
)
- try:
+ with suppress(Exception):
self.execute_script(script)
- except Exception:
- pass
def get_property(
self, selector, property, by="css selector", timeout=None
@@ -2202,20 +2145,16 @@ def click_visible_elements(
element = self.wait_for_element_present(
selector, by=by, timeout=timeout
)
- try:
+ with suppress(Exception):
# If the first element isn't visible, wait a little.
if not element.is_displayed():
time.sleep(0.16)
if self.undetectable:
time.sleep(0.06)
- except Exception:
- pass
elements = self.find_elements(selector, by=by)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
click_count = 0
for element in elements:
@@ -2298,10 +2237,8 @@ def click_nth_visible_element(
number = 0
element = elements[number]
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
try:
self.__scroll_to_element(element)
@@ -2344,22 +2281,18 @@ def click_if_visible(self, selector, by="css selector", timeout=0):
if self.is_element_visible(selector, by=by):
self.click(selector, by=by)
elif timeout > 0:
- try:
+ with suppress(Exception):
self.wait_for_element_visible(
selector, by=by, timeout=timeout
)
- except Exception:
- pass
if self.is_element_visible(selector, by=by):
self.click(selector, by=by)
def click_active_element(self):
self.wait_for_ready_state_complete()
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
if self.recorder_mode:
selector = js_utils.get_active_element_css(self.driver)
@@ -2492,12 +2425,10 @@ def check_if_unchecked(self, selector, by="css selector"):
)
# Handle switches that sit on checkboxes with zero opacity:
# Change the opacity a bit to allow the click to succeed.
- try:
+ with suppress(Exception):
self.execute_script(
'arguments[0].style.opacity="0.001";', element
)
- except Exception:
- pass
if self.is_element_visible(selector, by=by):
self.click(selector, by=by)
else:
@@ -2505,14 +2436,12 @@ def check_if_unchecked(self, selector, by="css selector"):
self.__dont_record_js_click = True
self.js_click(selector, by="css selector")
self.__dont_record_js_click = False
- try:
+ with suppress(Exception):
self.execute_script(
'arguments[0].style.opacity="arguments[1]";',
element,
opacity,
)
- except Exception:
- pass
def select_if_unselected(self, selector, by="css selector"):
"""Same as check_if_unchecked()"""
@@ -2532,12 +2461,10 @@ def uncheck_if_checked(self, selector, by="css selector"):
)
# Handle switches that sit on checkboxes with zero opacity:
# Change the opacity a bit to allow the click to succeed.
- try:
+ with suppress(Exception):
self.execute_script(
'arguments[0].style.opacity="0.001";', element
)
- except Exception:
- pass
if self.is_element_visible(selector, by=by):
self.click(selector, by=by)
else:
@@ -2545,14 +2472,12 @@ def uncheck_if_checked(self, selector, by="css selector"):
self.__dont_record_js_click = True
self.js_click(selector, by="css selector")
self.__dont_record_js_click = False
- try:
+ with suppress(Exception):
self.execute_script(
'arguments[0].style.opacity="arguments[1]";',
element,
opacity,
)
- except Exception:
- pass
def unselect_if_selected(self, selector, by="css selector"):
"""Same as uncheck_if_checked()"""
@@ -2611,14 +2536,12 @@ def switch_to_frame_of_element(self, selector, by="css selector"):
iframe_identifier = '[class="%s"]' % iframe_class
else:
continue
- try:
+ with suppress(Exception):
self.switch_to_frame(iframe_identifier, timeout=1)
if self.__needs_minimum_wait():
time.sleep(0.02)
if self.is_element_present(selector, by=by):
return iframe_identifier
- except Exception:
- pass
self.switch_to_default_content()
if self.__needs_minimum_wait():
time.sleep(0.02)
@@ -2692,10 +2615,8 @@ def hover_and_click(
self.__demo_mode_highlight_if_active(original_selector, original_by)
self.scroll_to(hover_selector, by=hover_by)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
@@ -2759,10 +2680,8 @@ def hover_and_click(
self.__switch_to_newest_window_if_not_blank()
elif self.browser == "safari":
# Release the hover by hovering elsewhere
- try:
+ with suppress(Exception):
page_actions.hover_on_element(self.driver, "body")
- except Exception:
- pass
if self.demo_mode:
if self.driver.current_url != pre_action_url:
if not js_utils.is_jquery_activated(self.driver):
@@ -2822,10 +2741,8 @@ def hover_and_double_click(
self.__demo_mode_highlight_if_active(original_selector, original_by)
self.scroll_to(hover_selector, by=hover_by)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
outdated_driver = False
element = None
@@ -2886,7 +2803,7 @@ def drag_and_drop(
timeout=None,
jquery=False,
):
- """Drag and drop an element from one selector to another."""
+ """Drag-and-drop an element from one selector to another."""
self.__check_scope()
if not timeout:
timeout = settings.SMALL_TIMEOUT
@@ -2933,7 +2850,7 @@ def drag_and_drop(
def drag_and_drop_with_offset(
self, selector, x, y, by="css selector", timeout=None
):
- """Drag and drop an element to an {X,Y}-offset location."""
+ """Drag-and-drop an element to an {X,Y}-offset location."""
self.__check_scope()
if not timeout:
timeout = settings.SMALL_TIMEOUT
@@ -3013,10 +2930,8 @@ def __select_option(
dropdown_selector, dropdown_by
)
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
try:
if option_by == "index":
@@ -3291,10 +3206,8 @@ def load_html_string(self, html_string, new_page=True):
self.open("data:text/html,")
inner_head = """document.getElementsByTagName("head")[0].innerHTML"""
inner_body = """document.getElementsByTagName("body")[0].innerHTML"""
- try:
+ with suppress(Exception):
self.wait_for_element_present("body", timeout=1)
- except Exception:
- pass
if not found_body:
self.execute_script('''%s = \"%s\"''' % (inner_body, html_string))
elif found_body and not found_head:
@@ -4096,8 +4009,6 @@ def get_new_driver(
"Valid options = {%s}" % (browser, valid_browsers)
)
# Launch a web browser
- from seleniumbase.core import browser_launcher
-
new_driver = browser_launcher.get_driver(
browser_name=browser_name,
headless=headless,
@@ -4193,7 +4104,8 @@ def get_new_driver(
self.driver.maximize_window()
self.wait_for_ready_state_complete()
else:
- self.driver.set_window_size(width, height)
+ with suppress(Exception):
+ self.driver.set_window_size(width, height)
except Exception:
pass # Keep existing browser resolution
elif self.browser == "safari":
@@ -4204,10 +4116,8 @@ def get_new_driver(
except Exception:
pass # Keep existing browser resolution
else:
- try:
- self.driver.set_window_rect(10, 20, width, height)
- except Exception:
- pass
+ with suppress(Exception):
+ self.driver.set_window_rect(10, 46, width, height)
if self.start_page and len(self.start_page) >= 4:
if page_utils.is_valid_url(self.start_page):
self.open(self.start_page)
@@ -4655,14 +4565,17 @@ def activate_recorder(self):
from seleniumbase.js_code.recorder_js import recorder_js
if not self.is_chromium():
+ if "linux" not in sys.platform:
+ c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX
+ c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX
+ cr = colorama.Style.RESET_ALL
+ sc = c1 + "Selenium" + c2 + "Base" + cr
raise Exception(
- "The Recorder is only for Chromium browsers: (Chrome or Edge)"
+ "The %s Recorder is for Chromium only!\n"
+ " (Supported browsers: Chrome and Edge)" % sc
)
url = self.driver.current_url
- if (
- url.startswith("data:") or url.startswith("about:")
- or url.startswith("chrome:") or url.startswith("edge:")
- ):
+ if url.startswith(("data:", "about:", "chrome:", "edge:")):
message = (
"The URL in Recorder-Mode cannot start with: "
'"data:", "about:", "chrome:", or "edge:"!'
@@ -4671,7 +4584,7 @@ def activate_recorder(self):
return
if self.recorder_ext:
return # The Recorder extension is already active
- try:
+ with suppress(Exception):
recorder_on = self.get_session_storage_item("recorder_activated")
if not recorder_on == "yes":
self.execute_script(recorder_js)
@@ -4680,8 +4593,6 @@ def activate_recorder(self):
print("\n" + message)
p_msg = "Recorder Mode ACTIVE. [ESC]: Pause. [~`]: Resume."
self.post_message(p_msg, pause=False, style="error")
- except Exception:
- pass
def __current_url_is_recordable(self):
url = self.get_current_url()
@@ -4721,10 +4632,7 @@ def save_recorded_actions(self):
def __get_recorded_actions_on_active_tab(self):
url = self.driver.current_url
- if (
- url.startswith("data:") or url.startswith("about:")
- or url.startswith("chrome:") or url.startswith("edge:")
- ):
+ if url.startswith(("data:", "about:", "chrome:", "edge:")):
return []
self.__origins_to_save.append(self.get_origin())
actions = self.get_session_storage_item("recorded_actions")
@@ -4735,9 +4643,9 @@ def __get_recorded_actions_on_active_tab(self):
return []
def __process_recorded_actions(self):
+ """Generates code after the SeleniumBase Recorder runs."""
if self.driver is None:
return
- import colorama
from seleniumbase.core import recorder_helper
raw_actions = [] # All raw actions from sessionStorage
@@ -4939,10 +4847,7 @@ def __process_recorded_actions(self):
or srt_actions[n - 1][0] == "jq_cl"
or srt_actions[n - 1][0] == "jq_ca"
):
- if (
- srt_actions[n - 1][1].startswith("input")
- or srt_actions[n - 1][1].startswith("button")
- ):
+ if srt_actions[n - 1][1].startswith(("input", "button")):
srt_actions[n][0] = "f_url"
elif srt_actions[n - 1][0] == "input":
if srt_actions[n - 1][2].endswith("\n"):
@@ -5290,7 +5195,6 @@ def __process_recorded_actions(self):
and (filename == "base_case.py" or methodname == "runTest")
):
import traceback
-
stack_base = traceback.format_stack()[0].split(os.sep)[-1]
test_base = stack_base.split(", in ")[0]
if hasattr(self, "cm_filename") and self.cm_filename:
@@ -5360,11 +5264,9 @@ def __process_recorded_actions(self):
if recordings_folder.endswith("/"):
recordings_folder = recordings_folder[:-1]
if not os.path.exists(recordings_folder):
- try:
+ with suppress(Exception):
os.makedirs(recordings_folder)
sys.stdout.write("\nCreated recordings%s" % os.sep)
- except Exception:
- pass
data = []
data.append("")
@@ -5465,12 +5367,10 @@ def __process_recorded_actions(self):
if not new_file:
rec_message = ">>> RECORDING ADDED to: "
star_len = len(rec_message) + len(file_path)
- try:
+ with suppress(Exception):
terminal_size = os.get_terminal_size().columns
if terminal_size > 30 and star_len > terminal_size:
star_len = terminal_size
- except Exception:
- pass
spc = "\n\n"
if hasattr(self, "rec_print") and self.rec_print:
spc = ""
@@ -5543,22 +5443,16 @@ def __process_recorded_behave_actions(self, srt_actions, colorama):
if recordings_folder.endswith("/"):
recordings_folder = recordings_folder[:-1]
if not os.path.exists(recordings_folder):
- try:
+ with suppress(Exception):
os.makedirs(recordings_folder)
- except Exception:
- pass
features_folder = os.path.join(recordings_folder, "features")
if not os.path.exists(features_folder):
- try:
+ with suppress(Exception):
os.makedirs(features_folder)
- except Exception:
- pass
steps_folder = os.path.join(features_folder, "steps")
if not os.path.exists(steps_folder):
- try:
+ with suppress(Exception):
os.makedirs(steps_folder)
- except Exception:
- pass
file_name = filename.split(".")[0]
if hasattr(self, "is_behave") and self.is_behave:
@@ -5573,12 +5467,10 @@ def __process_recorded_behave_actions(self, srt_actions, colorama):
if not new_file:
rec_message = ">>> RECORDING ADDED to: "
star_len = len(rec_message) + len(file_path)
- try:
+ with suppress(Exception):
terminal_size = os.get_terminal_size().columns
if terminal_size > 30 and star_len > terminal_size:
star_len = terminal_size
- except Exception:
- pass
spc = "\n"
if hasattr(self, "rec_print") and self.rec_print:
spc = ""
@@ -5684,16 +5576,14 @@ def __process_recorded_behave_actions(self, srt_actions, colorama):
print("Created recordings/features/steps/imported.py")
def bring_active_window_to_front(self):
- """Brings the active browser window to the front.
- This is useful when multiple drivers are being used."""
+ """Brings the active browser window to the front (on top).
+ Useful when multiple drivers are being used at the same time."""
self.__check_scope()
- try:
+ with suppress(Exception):
if not self.__is_in_frame():
# Only bring the window to the front if not in a frame
# because the driver resets itself to default content.
self.switch_to_window(self.driver.current_window_handle)
- except Exception:
- pass
def bring_to_front(self, selector, by="css selector"):
"""Updates the Z-index of a page element to bring it into view.
@@ -5723,7 +5613,7 @@ def bring_to_front(self, selector, by="css selector"):
def highlight_click(
self, selector, by="css selector", loops=3, scroll=True, timeout=None,
):
- """Highlights the element and then clicks it."""
+ """Highlights the element, and then clicks it."""
self.__check_scope()
if not timeout:
timeout = settings.SMALL_TIMEOUT
@@ -5782,10 +5672,8 @@ def __highlight_element(self, element, loops=None, scroll=True):
if not loops:
loops = settings.HIGHLIGHTS
if scroll and self.browser != "safari":
- try:
+ with suppress(Exception):
self.__slow_scroll_to_element(element)
- except Exception:
- pass
if self.highlights:
loops = self.highlights
if self.browser == "ie":
@@ -5815,7 +5703,7 @@ def __highlight(
self, selector, by="css selector", loops=None, scroll=True
):
"""This method uses fancy JavaScript to highlight an element.
- (Commonly used during Demo Mode automatically)"""
+ (Automatically using in S_e_l_e_n_i_u_m_B_a_s_e Demo Mode)"""
self.__check_scope()
selector, by = self.__recalculate_selector(selector, by, xp_ok=False)
element = self.wait_for_element_visible(
@@ -5935,14 +5823,12 @@ def highlight_elements(
count = 0
elements = self.find_elements(selector, by=by)
for element in elements:
- try:
+ with suppress(Exception):
if element.is_displayed():
self.__highlight_element(
element, loops=loops, scroll=scroll
)
count += 1
- except Exception:
- pass
if limit > 0 and count >= limit:
break
@@ -6189,10 +6075,8 @@ def js_click(
time_stamp = 0
action = ["", "", "", time_stamp]
pre_action_url = None
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
pre_window_count = len(self.driver.window_handles)
if self.recorder_mode and not self.__dont_record_js_click:
time_stamp = self.execute_script("return Date.now();")
@@ -6294,10 +6178,8 @@ def js_click(
# If a click closes the active window,
# switch to the last one if it exists.
self.switch_to_window(-1)
- try:
+ with suppress(Exception):
self.wait_for_ready_state_complete()
- except Exception:
- pass
self.__demo_mode_pause_if_active()
def js_click_if_present(self, selector, by="css selector", timeout=0):
@@ -6309,10 +6191,8 @@ def js_click_if_present(self, selector, by="css selector", timeout=0):
if self.is_element_present(selector, by=by):
self.js_click(selector, by=by)
elif timeout > 0:
- try:
+ with suppress(Exception):
self.wait_for_element_present(selector, by=by, timeout=timeout)
- except Exception:
- pass
if self.is_element_present(selector, by=by):
self.js_click(selector, by=by)
@@ -6325,10 +6205,8 @@ def js_click_if_visible(self, selector, by="css selector", timeout=0):
if self.is_element_visible(selector, by=by):
self.js_click(selector, by=by)
elif timeout > 0:
- try:
+ with suppress(Exception):
self.wait_for_element_visible(selector, by=by, timeout=timeout)
- except Exception:
- pass
if self.is_element_visible(selector, by=by):
self.js_click(selector, by=by)
@@ -6421,13 +6299,11 @@ def hide_element(self, selector, by="css selector"):
"""Hide the first element on the page that matches the selector."""
self.__check_scope()
element = None
- try:
+ with suppress(Exception):
self.wait_for_element_visible("body", timeout=1.5)
element = self.wait_for_element_present(
selector, by=by, timeout=0.5
)
- except Exception:
- pass
selector, by = self.__recalculate_selector(selector, by)
css_selector = self.convert_to_css_selector(selector, by=by)
if ":contains(" in css_selector and element:
@@ -6453,10 +6329,8 @@ def hide_element(self, selector, by="css selector"):
def hide_elements(self, selector, by="css selector"):
"""Hide all elements on the page that match the selector."""
self.__check_scope()
- try:
+ with suppress(Exception):
self.wait_for_element_visible("body", timeout=1.5)
- except Exception:
- pass
selector, by = self.__recalculate_selector(selector, by)
css_selector = self.convert_to_css_selector(selector, by=by)
if ":contains(" in css_selector:
@@ -6479,11 +6353,9 @@ def show_element(self, selector, by="css selector"):
"""Show the first element on the page that matches the selector."""
self.__check_scope()
element = None
- try:
+ with suppress(Exception):
self.wait_for_element_visible("body", timeout=1.5)
element = self.wait_for_element_present(selector, by=by, timeout=1)
- except Exception:
- pass
selector, by = self.__recalculate_selector(selector, by)
css_selector = self.convert_to_css_selector(selector, by=by)
if ":contains(" in css_selector and element:
@@ -6509,10 +6381,8 @@ def show_element(self, selector, by="css selector"):
def show_elements(self, selector, by="css selector"):
"""Show all elements on the page that match the selector."""
self.__check_scope()
- try:
+ with suppress(Exception):
self.wait_for_element_visible("body", timeout=1.5)
- except Exception:
- pass
selector, by = self.__recalculate_selector(selector, by)
css_selector = self.convert_to_css_selector(selector, by=by)
if ":contains(" in css_selector:
@@ -6535,13 +6405,11 @@ def remove_element(self, selector, by="css selector"):
"""Remove the first element on the page that matches the selector."""
self.__check_scope()
element = None
- try:
+ with suppress(Exception):
self.wait_for_element_visible("body", timeout=1.5)
element = self.wait_for_element_present(
selector, by=by, timeout=0.5
)
- except Exception:
- pass
selector, by = self.__recalculate_selector(selector, by)
css_selector = self.convert_to_css_selector(selector, by=by)
if ":contains(" in css_selector and element:
@@ -6567,10 +6435,8 @@ def remove_element(self, selector, by="css selector"):
def remove_elements(self, selector, by="css selector"):
"""Remove all elements on the page that match the selector."""
self.__check_scope()
- try:
+ with suppress(Exception):
self.wait_for_element_visible("body", timeout=1.5)
- except Exception:
- pass
selector, by = self.__recalculate_selector(selector, by)
css_selector = self.convert_to_css_selector(selector, by=by)
if ":contains(" in css_selector:
@@ -6635,10 +6501,8 @@ def show_file_choosers(self):
$elements[index].setAttribute('class', new_class);}"""
% css_selector
)
- try:
+ with suppress(Exception):
self.execute_script(script)
- except Exception:
- pass
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
time_stamp = self.execute_script("return Date.now();")
@@ -6656,10 +6520,8 @@ def disable_beforeunload(self):
self.is_chromium()
and self.driver.current_url.startswith("http")
):
- try:
+ with suppress(Exception):
self.driver.execute_script("window.onbeforeunload=null;")
- except Exception:
- pass
def get_domain_url(self, url):
self.__check_scope()
@@ -6675,12 +6537,10 @@ def get_beautiful_soup(self, source=None):
from bs4 import BeautifulSoup
if not source:
- try:
+ with suppress(Exception):
self.wait_for_element_visible(
"body", timeout=settings.MINI_TIMEOUT
)
- except Exception:
- pass
source = self.get_page_source()
return BeautifulSoup(source, "html.parser")
@@ -6693,11 +6553,9 @@ def get_unique_links(self):
time.sleep(0.08)
if self.undetectable:
time.sleep(0.02)
- try:
+ with suppress(Exception):
self.wait_for_element_present("body", timeout=1.5)
self.wait_for_element_visible("body", timeout=1.5)
- except Exception:
- pass
if self.__needs_minimum_wait():
time.sleep(0.25)
if self.undetectable:
@@ -6911,12 +6769,7 @@ def get_pdf_text(
try:
from pdfminer.high_level import extract_text
except Exception:
- if not sys.version_info >= (3, 8):
- shared_utils.pip_install(
- "pdfminer.six", version="20221105"
- )
- else:
- shared_utils.pip_install("pdfminer.six")
+ shared_utils.pip_install("pdfminer.six")
from pdfminer.high_level import extract_text
if not password:
password = ""
@@ -7036,10 +6889,8 @@ def create_folder(self, folder):
if len(folder) < 1:
raise Exception("Minimum folder name length = 1.")
if not os.path.exists(folder):
- try:
+ with suppress(Exception):
os.makedirs(folder)
- except Exception:
- pass
def choose_file(
self, selector, file_path, by="css selector", timeout=None
@@ -7079,10 +6930,8 @@ def choose_file(
self.__scroll_to_element(element, selector, by)
pre_action_url = None
if self.demo_mode:
- try:
+ with suppress(Exception):
pre_action_url = self.driver.current_url
- except Exception:
- pass
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
time_stamp = self.execute_script("return Date.now();")
@@ -7370,10 +7219,8 @@ def delete_downloaded_file_if_present(self, file, browser=False):
Those paths are usually the same. (browser-dependent)."""
if self.is_downloaded_file_present(file, browser=browser):
file_path = self.get_path_of_downloaded_file(file, browser=browser)
- try:
+ with suppress(Exception):
os.remove(file_path)
- except Exception:
- pass
def delete_downloaded_file(self, file, browser=False):
"""Same as self.delete_downloaded_file_if_present()
@@ -7390,10 +7237,8 @@ def delete_downloaded_file(self, file, browser=False):
Those paths are usually the same. (browser-dependent)."""
if self.is_downloaded_file_present(file, browser=browser):
file_path = self.get_path_of_downloaded_file(file, browser=browser)
- try:
+ with suppress(Exception):
os.remove(file_path)
- except Exception:
- pass
def assert_downloaded_file(self, file, timeout=None, browser=False):
"""Asserts that the file exists in SeleniumBase's [Downloads Folder].
@@ -7450,13 +7295,11 @@ def assert_downloaded_file(self, file, timeout=None, browser=False):
self.__extra_actions.append(action)
if self.demo_mode:
messenger_post = "ASSERT DOWNLOADED FILE: [%s]" % file
- try:
+ with suppress(Exception):
js_utils.activate_jquery(self.driver)
js_utils.post_messenger_success_message(
self.driver, messenger_post, self.message_duration
)
- except Exception:
- pass
def assert_downloaded_file_regex(self, regex, timeout=None, browser=False):
"""Assert the filename regex exists in SeleniumBase's Downloads Folder.
@@ -7505,13 +7348,11 @@ def assert_downloaded_file_regex(self, regex, timeout=None, browser=False):
messenger_post = (
"ASSERT DOWNLOADED FILE REGEX: [%s]" % regex
)
- try:
+ with suppress(Exception):
js_utils.activate_jquery(self.driver)
js_utils.post_messenger_success_message(
self.driver, messenger_post, self.message_duration
)
- except Exception:
- pass
def assert_data_in_downloaded_file(
self, data, file, timeout=None, browser=False
@@ -7530,37 +7371,37 @@ def assert_data_in_downloaded_file(
def assert_true(self, expr, msg=None):
"""Asserts that the expression is True.
- Will raise an exception if the statement if False."""
+ Raises an exception if the statement if False."""
self.assertTrue(expr, msg=msg)
def assert_false(self, expr, msg=None):
"""Asserts that the expression is False.
- Will raise an exception if the statement if True."""
+ Raises an exception if the statement if True."""
self.assertFalse(expr, msg=msg)
def assert_equal(self, first, second, msg=None):
"""Asserts that the two values are equal.
- Will raise an exception if the values are not equal."""
+ Raises an exception if the values are not equal."""
self.assertEqual(first, second, msg=msg)
def assert_not_equal(self, first, second, msg=None):
"""Asserts that the two values are not equal.
- Will raise an exception if the values are equal."""
+ Raises an exception if the values are equal."""
self.assertNotEqual(first, second, msg=msg)
def assert_in(self, first, second, msg=None):
"""Asserts that the first string is in the second string.
- Will raise an exception if the first string is not in the second."""
+ Raises an exception if the first string is not in the second."""
self.assertIn(first, second, msg=msg)
def assert_not_in(self, first, second, msg=None):
"""Asserts that the first string is not in the second string.
- Will raise an exception if the first string is in the second string."""
+ Raises an exception if the first string is in the second string."""
self.assertNotIn(first, second, msg=msg)
def assert_raises(self, *args, **kwargs):
"""Asserts that the following block of code raises an exception.
- Will raise an exception if the block of code has no exception.
+ Raises an exception if the block of code has no exception.
Usage Example =>
# Verify that the expected exception is raised.
with self.assert_raises(Exception):
@@ -8180,17 +8021,15 @@ def set_value(
else:
if the_type == "range" and ":contains\\(" not in css_selector:
# Some input sliders need a mouse event to trigger listeners.
- try:
+ with suppress(Exception):
mouse_move_script = (
"""m_elm = document.querySelector('%s');"""
"""m_evt = new Event('mousemove');"""
"""m_elm.dispatchEvent(m_evt);""" % css_selector
)
self.execute_script(mouse_move_script)
- except Exception:
- pass
elif the_type == "range" and ":contains\\(" in css_selector:
- try:
+ with suppress(Exception):
element = self.wait_for_element_present(
original_selector, by=by, timeout=1
)
@@ -8200,8 +8039,6 @@ def set_value(
"""m_elm.dispatchEvent(m_evt);"""
)
self.execute_script(mouse_move_script, element)
- except Exception:
- pass
self.__demo_mode_pause_if_active()
if not self.demo_mode and not self.slow_mode:
if self.__needs_minimum_wait():
@@ -8221,13 +8058,11 @@ def js_update_text(self, selector, text, by="css selector", timeout=None):
text = self.__get_type_checked_text(text)
self.set_value(selector, text, by=by, timeout=timeout)
if not text.endswith("\n"):
- try:
+ with suppress(Exception):
element = page_actions.wait_for_element_present(
self.driver, selector, by, timeout=0.2
)
element.send_keys(" " + Keys.BACK_SPACE)
- except Exception:
- pass
def js_type(self, selector, text, by="css selector", timeout=None):
"""Same as self.js_update_text()
@@ -8347,13 +8182,11 @@ def jquery_update_text(
)
element.send_keys(Keys.RETURN)
else:
- try:
+ with suppress(Exception):
element = self.wait_for_element_present(
original_selector, by=original_by, timeout=0.2
)
element.send_keys(" " + Keys.BACK_SPACE)
- except Exception:
- pass
self.__demo_mode_pause_if_active()
def jquery_type(self, selector, text, by="css selector", timeout=None):
@@ -8530,10 +8363,8 @@ def console_log_script(self, script):
def get_recorded_console_logs(self):
"""Get console logs recorded after "start_recording_console_logs()"."""
logs = []
- try:
+ with suppress(Exception):
logs = self.execute_script("return console.logs;")
- except Exception:
- pass
return logs
############
@@ -8916,7 +8747,7 @@ def assert_element_not_present(
self, selector, by="css selector", timeout=None
):
"""Same as self.assert_element_absent()
- Will raise an exception if the element stays present.
+ Raises an exception if the element stays present.
A hidden element counts as a present element, which fails this assert.
If you want to assert that elements are hidden instead of nonexistent,
use assert_element_not_visible() instead.
@@ -8978,10 +8809,8 @@ def _check_browser(self):
"""This method raises an exception if the active window is closed.
(This provides a much cleaner exception message in this situation.)"""
active_window = None
- try:
+ with suppress(Exception):
active_window = self.driver.current_window_handle # Fails if None
- except Exception:
- pass
if not active_window:
raise NoSuchWindowException("Active window was already closed!")
@@ -8995,10 +8824,8 @@ def _print(self, msg):
"""
if hasattr(sb_config, "_multithreaded") and sb_config._multithreaded:
if not isinstance(msg, str):
- try:
+ with suppress(Exception):
msg = str(msg)
- except Exception:
- pass
sys.stderr.write(msg + "\n")
else:
print(msg)
@@ -9412,7 +9239,7 @@ def find_element(self, selector, by="css selector", timeout=None):
def assert_element(self, selector, by="css selector", timeout=None):
"""Similar to wait_for_element_visible(), but returns nothing.
- As above, will raise an exception if nothing can be found.
+ As above, raises an exception if nothing can be found.
Returns True if successful. Default timeout = SMALL_TIMEOUT."""
self.__check_scope()
if not timeout:
@@ -9452,7 +9279,7 @@ def assert_element_visible(
self, selector, by="css selector", timeout=None
):
"""Same as self.assert_element()
- As above, will raise an exception if nothing can be found."""
+ As above, raises an exception if nothing can be found."""
self.__check_scope()
if not timeout:
timeout = settings.SMALL_TIMEOUT
@@ -9678,6 +9505,7 @@ def assert_text(
"""Similar to wait_for_text_visible()
Raises an exception if the element or the text is not found.
The text only needs to be a subset within the complete text.
+ The text can be a string or a list/tuple of text substrings.
Returns True if successful. Default timeout = SMALL_TIMEOUT."""
self.__check_scope()
if not timeout:
@@ -9686,26 +9514,45 @@ def assert_text(
timeout = self.__get_new_timeout(timeout)
original_selector = selector
selector, by = self.__recalculate_selector(selector, by)
- if self.__is_shadow_selector(selector):
+ if isinstance(text, (list, tuple)):
+ text_list = text
+ for _text in text_list:
+ self.wait_for_text_visible(
+ _text, selector, by=by, timeout=timeout
+ )
+ if self.demo_mode:
+ a_t = "ASSERT TEXT"
+ i_n = "in"
+ if self._language != "English":
+ from seleniumbase.fixtures.words import SD
+
+ a_t = SD.translate_assert_text(self._language)
+ i_n = SD.translate_in(self._language)
+ messenger_post = "%s: {%s} %s %s: %s" % (
+ a_t, _text, i_n, by.upper(), selector
+ )
+ self.__highlight_with_assert_success(
+ messenger_post, selector, by
+ )
+ elif self.__is_shadow_selector(selector):
self.__assert_shadow_text_visible(text, selector, timeout)
return True
- self.wait_for_text_visible(text, selector, by=by, timeout=timeout)
- if self.demo_mode:
- a_t = "ASSERT TEXT"
- i_n = "in"
- if self._language != "English":
- from seleniumbase.fixtures.words import SD
+ else:
+ self.wait_for_text_visible(text, selector, by=by, timeout=timeout)
+ if self.demo_mode:
+ a_t = "ASSERT TEXT"
+ i_n = "in"
+ if self._language != "English":
+ from seleniumbase.fixtures.words import SD
- a_t = SD.translate_assert_text(self._language)
- i_n = SD.translate_in(self._language)
- messenger_post = "%s: {%s} %s %s: %s" % (
- a_t,
- text,
- i_n,
- by.upper(),
- selector,
- )
- self.__highlight_with_assert_success(messenger_post, selector, by)
+ a_t = SD.translate_assert_text(self._language)
+ i_n = SD.translate_in(self._language)
+ messenger_post = "%s: {%s} %s %s: %s" % (
+ a_t, text, i_n, by.upper(), selector
+ )
+ self.__highlight_with_assert_success(
+ messenger_post, selector, by
+ )
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
if by == By.XPATH:
@@ -9747,11 +9594,7 @@ def assert_exact_text(
a_t = SD.translate_assert_exact_text(self._language)
i_n = SD.translate_in(self._language)
messenger_post = "%s: {%s} %s %s: %s" % (
- a_t,
- text,
- i_n,
- by.upper(),
- selector,
+ a_t, text, i_n, by.upper(), selector
)
self.__highlight_with_assert_success(messenger_post, selector, by)
if self.recorder_mode and self.__current_url_is_recordable():
@@ -9791,10 +9634,7 @@ def assert_non_empty_text(
a_t = SD.translate_assert_non_empty_text(self._language)
i_n = SD.translate_in(self._language)
messenger_post = "%s %s %s: %s" % (
- a_t,
- i_n,
- by.upper(),
- selector,
+ a_t, i_n, by.upper(), selector
)
self.__highlight_with_assert_success(messenger_post, selector, by)
if self.recorder_mode and self.__current_url_is_recordable():
@@ -9889,7 +9729,7 @@ def find_link_text(self, link_text, timeout=None):
def assert_link_text(self, link_text, timeout=None):
"""Similar to wait_for_link_text_visible(), but returns nothing.
- As above, will raise an exception if nothing can be found.
+ As above, raises an exception if nothing can be found.
Returns True if successful. Default timeout = SMALL_TIMEOUT."""
self.__check_scope()
if not timeout:
@@ -9938,7 +9778,7 @@ def find_partial_link_text(self, partial_link_text, timeout=None):
def assert_partial_link_text(self, partial_link_text, timeout=None):
"""Similar to wait_for_partial_link_text(), but returns nothing.
- As above, will raise an exception if nothing can be found.
+ As above, raises an exception if nothing can be found.
Returns True if successful. Default timeout = SMALL_TIMEOUT."""
self.__check_scope()
if not timeout:
@@ -9984,7 +9824,7 @@ def wait_for_element_absent(
def assert_element_absent(self, selector, by="css selector", timeout=None):
"""Similar to wait_for_element_absent()
- As above, will raise an exception if the element stays present.
+ As above, raises an exception if the element stays present.
A hidden element counts as a present element, which fails this assert.
If you want to assert that elements are hidden instead of nonexistent,
use assert_element_not_visible() instead.
@@ -10025,7 +9865,7 @@ def assert_element_not_visible(
self, selector, by="css selector", timeout=None
):
"""Similar to wait_for_element_not_visible()
- As above, will raise an exception if the element stays visible.
+ As above, raises an exception if the element stays visible.
Returns True if successful. Default timeout = SMALL_TIMEOUT."""
self.__check_scope()
if not timeout:
@@ -10341,7 +10181,7 @@ def __assert_eq(self, *args, **kwargs):
countdown_on = True
countdown = 3
minified_exception += line + "\n"
- elif line.startswith("+") or line.startswith("-"):
+ elif line.startswith(("+", "-")):
minified_exception += line + "\n"
elif line.startswith("?"):
minified_exception += line + "\n"
@@ -10390,8 +10230,6 @@ def __process_visual_baseline_logs(self):
shutil.copy(latest_png_path, latest_copy_path)
if len(self.__visual_baseline_copies) != 1:
return # Skip the rest when deferred visual asserts are used
- from seleniumbase.core import visual_helper
-
the_html = visual_helper.get_sbs_html()
file_path = os.path.join(test_logpath, constants.SideBySide.HTML_FILE)
out_file = codecs.open(file_path, "w+", encoding="utf-8")
@@ -10478,12 +10316,10 @@ def check_window(
self.check_window(name="github_page", level=2)
self.check_window(name="wikipedia_page", level=3) """
self.wait_for_ready_state_complete()
- try:
+ with suppress(Exception):
self.wait_for_element_visible(
"body", timeout=settings.MINI_TIMEOUT
)
- except Exception:
- pass
if self.__needs_minimum_wait():
time.sleep(0.08)
if level == "0":
@@ -10509,7 +10345,6 @@ def check_window(
if not name or len(name) < 1:
name = "default"
name = str(name)
- from seleniumbase.core import visual_helper
visual_helper.visual_baseline_folder_setup()
baseline_dir = constants.VisualBaseline.STORAGE_FOLDER
@@ -10792,14 +10627,12 @@ def deferred_assert_element(
if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
self.__deferred_assert_count += 1
- try:
+ with suppress(Exception):
url = self.get_current_url()
if url == self.__last_url_of_deferred_assert:
timeout = 0.6 # Was already on page (full wait not needed)
else:
self.__last_url_of_deferred_assert = url
- except Exception:
- pass
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
time_stamp = self.execute_script("return Date.now();")
@@ -10829,14 +10662,12 @@ def deferred_assert_element_present(
if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
self.__deferred_assert_count += 1
- try:
+ with suppress(Exception):
url = self.get_current_url()
if url == self.__last_url_of_deferred_assert:
timeout = 0.6 # Was already on page (full wait not needed)
else:
self.__last_url_of_deferred_assert = url
- except Exception:
- pass
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
time_stamp = self.execute_script("return Date.now();")
@@ -10866,14 +10697,12 @@ def deferred_assert_text(
if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
self.__deferred_assert_count += 1
- try:
+ with suppress(Exception):
url = self.get_current_url()
if url == self.__last_url_of_deferred_assert:
timeout = 0.6 # Was already on page (full wait not needed)
else:
self.__last_url_of_deferred_assert = url
- except Exception:
- pass
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
time_stamp = self.execute_script("return Date.now();")
@@ -10904,14 +10733,12 @@ def deferred_assert_exact_text(
if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
self.__deferred_assert_count += 1
- try:
+ with suppress(Exception):
url = self.get_current_url()
if url == self.__last_url_of_deferred_assert:
timeout = 0.6 # Was already on page (full wait not needed)
else:
self.__last_url_of_deferred_assert = url
- except Exception:
- pass
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
time_stamp = self.execute_script("return Date.now();")
@@ -10948,14 +10775,12 @@ def deferred_assert_non_empty_text(
if self.timeout_multiplier and timeout == settings.MINI_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
self.__deferred_assert_count += 1
- try:
+ with suppress(Exception):
url = self.get_current_url()
if url == self.__last_url_of_deferred_assert:
timeout = 0.6 # Was already on page (full wait not needed)
else:
self.__last_url_of_deferred_assert = url
- except Exception:
- pass
if self.recorder_mode and self.__current_url_is_recordable():
if self.get_session_storage_item("pause_recorder") == "no":
time_stamp = self.execute_script("return Date.now();")
@@ -11391,10 +11216,8 @@ def save_presentation(
if saved_presentations_folder.endswith("/"):
saved_presentations_folder = saved_presentations_folder[:-1]
if not os.path.exists(saved_presentations_folder):
- try:
+ with suppress(Exception):
os.makedirs(saved_presentations_folder)
- except Exception:
- pass
file_path = os.path.join(saved_presentations_folder, filename)
out_file = codecs.open(file_path, "w+", encoding="utf-8")
out_file.writelines(the_html)
@@ -11448,7 +11271,7 @@ def begin_presentation(
self._presentation_slides[name].pop()
self.open_html_file(file_path)
presentation_folder = constants.Presentations.SAVED_FOLDER
- try:
+ with suppress(Exception):
while (
len(self.driver.window_handles) > 0
and presentation_folder in self.get_current_url()
@@ -11459,8 +11282,6 @@ def begin_presentation(
):
break
time.sleep(0.05)
- except Exception:
- pass
############
@@ -12090,10 +11911,8 @@ def save_chart(self, chart_name=None, filename=None, folder=None):
if saved_charts_folder.endswith("/"):
saved_charts_folder = saved_charts_folder[:-1]
if not os.path.exists(saved_charts_folder):
- try:
+ with suppress(Exception):
os.makedirs(saved_charts_folder)
- except Exception:
- pass
file_path = os.path.join(saved_charts_folder, filename)
out_file = codecs.open(file_path, "w+", encoding="utf-8")
out_file.writelines(the_html)
@@ -12133,17 +11952,15 @@ def display_chart(self, chart_name=None, filename=None, interval=0):
self.open_html_file(file_path)
chart_folder = constants.Charts.SAVED_FOLDER
if interval == 0:
- try:
+ with suppress(Exception):
print("\n*** Close the browser window to continue ***")
# Will also continue if manually navigating to a new page
while len(self.driver.window_handles) > 0 and (
chart_folder in self.get_current_url()
):
time.sleep(0.05)
- except Exception:
- pass
else:
- try:
+ with suppress(Exception):
start_ms = time.time() * 1000.0
stop_ms = start_ms + (interval * 1000.0)
for x in range(int(interval * 10)):
@@ -12155,8 +11972,6 @@ def display_chart(self, chart_name=None, filename=None, interval=0):
if chart_folder not in self.get_current_url():
break
time.sleep(0.1)
- except Exception:
- pass
def extract_chart(self, chart_name=None):
"""Extracts the HTML from a SeleniumBase-generated chart.
@@ -12968,10 +12783,8 @@ def get_jqc_button_input(self, message, buttons, options=None):
)
time.sleep(0.02)
jf = "document.querySelector('.jconfirm-box').focus();"
- try:
+ with suppress(Exception):
self.execute_script(jf)
- except Exception:
- pass
waiting_for_response = True
while waiting_for_response:
time.sleep(0.05)
@@ -13043,10 +12856,8 @@ def get_jqc_text_input(self, message, button=None, options=None):
)
time.sleep(0.02)
jf = "document.querySelector('.jconfirm-box input.jqc_input').focus();"
- try:
+ with suppress(Exception):
self.execute_script(jf)
- except Exception:
- pass
waiting_for_response = True
while waiting_for_response:
time.sleep(0.05)
@@ -13107,10 +12918,8 @@ def get_jqc_form_inputs(self, message, buttons, options=None):
)
time.sleep(0.02)
jf = "document.querySelector('.jconfirm-box input.jqc_input').focus();"
- try:
+ with suppress(Exception):
self.execute_script(jf)
- except Exception:
- pass
waiting_for_response = True
while waiting_for_response:
time.sleep(0.05)
@@ -13185,12 +12994,10 @@ def __js_click(self, selector, by="css selector"):
if not page_actions.is_element_clickable(
self.driver, selector, by
):
- try:
+ with suppress(Exception):
self.wait_for_element_clickable(
selector, by, timeout=1.8
)
- except Exception:
- pass
# If the regular mouse-simulated click fails, do a basic JS click
script = (
"""document.querySelector('%s').click();"""
@@ -13266,8 +13073,6 @@ def __click_with_offset(
timeout=None,
center=None,
):
- from selenium.webdriver.common.action_chains import ActionChains
-
self.wait_for_ready_state_complete()
if self.__needs_minimum_wait():
time.sleep(0.14)
@@ -13464,7 +13269,7 @@ def __click_dropdown_link_text(self, link_text, link_css):
for dropdown in matching_dropdowns:
# The same class names might be used for multiple dropdowns
if dropdown.is_displayed():
- try:
+ with suppress(Exception):
try:
page_actions.hover_element(
self.driver,
@@ -13485,9 +13290,6 @@ def __click_dropdown_link_text(self, link_text, link_css):
timeout=0.12,
)
return True
- except Exception:
- pass
-
return False
def __get_href_from_partial_link_text(self, link_text, hard_fail=True):
@@ -13528,7 +13330,7 @@ def __click_dropdown_partial_link_text(self, link_text, link_css):
for dropdown in matching_dropdowns:
# The same class names might be used for multiple dropdowns
if dropdown.is_displayed():
- try:
+ with suppress(Exception):
try:
page_actions.hover_element(
self.driver, dropdown
@@ -13550,8 +13352,6 @@ def __click_dropdown_partial_link_text(self, link_text, link_css):
timeout=0.12,
)
return True
- except Exception:
- pass
return False
def __recalculate_selector(self, selector, by, xp_ok=True):
@@ -13778,7 +13578,7 @@ def __activate_standard_virtual_display(self):
from sbvirtualdisplay import Display
width = settings.HEADLESS_START_WIDTH
height = settings.HEADLESS_START_HEIGHT
- try:
+ with suppress(Exception):
self._xvfb_display = Display(
visible=0, size=(width, height)
)
@@ -13786,11 +13586,9 @@ def __activate_standard_virtual_display(self):
sb_config._virtual_display = self._xvfb_display
self.headless_active = True
sb_config.headless_active = True
- except Exception:
- pass
def __activate_virtual_display_as_needed(self):
- """Should be needed only on Linux.
+ """This is only needed on Linux.
The "--xvfb" arg is still useful, as it prevents headless mode,
which is the default mode on Linux unless using another arg."""
if "linux" in sys.platform and (not self.headed or self.xvfb):
@@ -13802,9 +13600,13 @@ def __activate_virtual_display_as_needed(self):
if self.undetectable and not (self.headless or self.headless2):
import Xlib.display
try:
+ if not self._xvfb_width:
+ self._xvfb_width = 1366
+ if not self._xvfb_height:
+ self._xvfb_height = 768
self._xvfb_display = Display(
visible=True,
- size=(1366, 768),
+ size=(self._xvfb_width, self._xvfb_height),
backend="xvfb",
use_xauth=True,
)
@@ -13825,7 +13627,7 @@ def __activate_virtual_display_as_needed(self):
pyautogui_is_installed = False
try:
import pyautogui
- try:
+ with suppress(Exception):
use_pyautogui_ver = constants.PyAutoGUI.VER
if pyautogui.__version__ != use_pyautogui_ver:
del pyautogui # To get newer ver
@@ -13833,8 +13635,6 @@ def __activate_virtual_display_as_needed(self):
"pyautogui", version=use_pyautogui_ver
)
import pyautogui
- except Exception:
- pass
pyautogui_is_installed = True
except Exception:
message = (
@@ -14086,12 +13886,10 @@ def __shadow_type(self, selector, text, timeout, clear_first=True):
selector, timeout=timeout, must_be_visible=True
)
if clear_first:
- try:
+ with suppress(Exception):
element.clear()
backspaces = Keys.BACK_SPACE * 42 # Autofill Defense
element.send_keys(backspaces)
- except Exception:
- pass
text = self.__get_type_checked_text(text)
if not text.endswith("\n"):
element.send_keys(text)
@@ -14107,12 +13905,10 @@ def __shadow_clear(self, selector, timeout):
element = self.__get_shadow_element(
selector, timeout=timeout, must_be_visible=True
)
- try:
+ with suppress(Exception):
element.clear()
backspaces = Keys.BACK_SPACE * 42 # Autofill Defense
element.send_keys(backspaces)
- except Exception:
- pass
def __get_shadow_text(self, selector, timeout):
element = self.__get_shadow_element(
@@ -14232,19 +14028,13 @@ def __assert_shadow_text_visible(self, text, selector, timeout):
a_t = SD.translate_assert_text(self._language)
i_n = SD.translate_in(self._language)
messenger_post = "%s: {%s} %s %s: %s" % (
- a_t,
- text,
- i_n,
- by.upper(),
- selector,
+ a_t, text, i_n, by.upper(), selector
)
- try:
+ with suppress(Exception):
js_utils.activate_jquery(self.driver)
js_utils.post_messenger_success_message(
self.driver, messenger_post, self.message_duration
)
- except Exception:
- pass
def __assert_exact_shadow_text_visible(self, text, selector, timeout):
self.__wait_for_exact_shadow_text_visible(text, selector, timeout)
@@ -14258,19 +14048,13 @@ def __assert_exact_shadow_text_visible(self, text, selector, timeout):
a_t = SD.translate_assert_exact_text(self._language)
i_n = SD.translate_in(self._language)
messenger_post = "%s: {%s} %s %s: %s" % (
- a_t,
- text,
- i_n,
- by.upper(),
- selector,
+ a_t, text, i_n, by.upper(), selector
)
- try:
+ with suppress(Exception):
js_utils.activate_jquery(self.driver)
js_utils.post_messenger_success_message(
self.driver, messenger_post, self.message_duration
)
- except Exception:
- pass
def __assert_non_empty_shadow_text_visible(self, selector, timeout, strip):
self.__wait_for_non_empty_shadow_text_visible(selector, timeout, strip)
@@ -14284,18 +14068,13 @@ def __assert_non_empty_shadow_text_visible(self, selector, timeout, strip):
a_t = SD.translate_assert_non_empty_text(self._language)
i_n = SD.translate_in(self._language)
messenger_post = "%s %s %s: %s" % (
- a_t,
- i_n,
- by.upper(),
- selector,
+ a_t, i_n, by.upper(), selector
)
- try:
+ with suppress(Exception):
js_utils.activate_jquery(self.driver)
js_utils.post_messenger_success_message(
self.driver, messenger_post, self.message_duration
)
- except Exception:
- pass
def __is_shadow_element_present(self, selector):
try:
@@ -14447,13 +14226,11 @@ def __assert_shadow_element_present(self, selector):
a_t = SD.translate_assert(self._language)
messenger_post = "%s %s: %s" % (a_t, by.upper(), selector)
- try:
+ with suppress(Exception):
js_utils.activate_jquery(self.driver)
js_utils.post_messenger_success_message(
self.driver, messenger_post, self.message_duration
)
- except Exception:
- pass
def __assert_shadow_element_visible(self, selector):
element = self.__get_shadow_element(selector)
@@ -14468,13 +14245,11 @@ def __assert_shadow_element_visible(self, selector):
a_t = SD.translate_assert(self._language)
messenger_post = "%s %s: %s" % (a_t, by.upper(), selector)
- try:
+ with suppress(Exception):
js_utils.activate_jquery(self.driver)
js_utils.post_messenger_success_message(
self.driver, messenger_post, self.message_duration
)
- except Exception:
- pass
############
@@ -14553,6 +14328,7 @@ def setUp(self, masterqa_mode=False):
sb_config.headless_active = False
self.headed = sb_config.headed
self.xvfb = sb_config.xvfb
+ self.xvfb_metrics = sb_config.xvfb_metrics
self.locale_code = sb_config.locale_code
self.interval = sb_config.interval
self.start_page = sb_config.start_page
@@ -14644,31 +14420,8 @@ def setUp(self, masterqa_mode=False):
self.use_wire = sb_config.use_wire
self.external_pdf = sb_config.external_pdf
self._final_debug = sb_config.final_debug
+ self.window_position = sb_config.window_position
self.window_size = sb_config.window_size
- window_size = self.window_size
- if window_size:
- if window_size.count(",") != 1:
- message = (
- '\n\n window_size expects a "width,height" string!'
- '\n (Your input was: "%s")\n' % window_size
- )
- raise Exception(message)
- window_size = window_size.replace(" ", "")
- width = None
- height = None
- try:
- width = int(window_size.split(",")[0])
- height = int(window_size.split(",")[1])
- except Exception:
- message = (
- '\n\n Expecting integer values for "width,height"!'
- '\n (window_size input was: "%s")\n' % window_size
- )
- raise Exception(message)
- settings.CHROME_START_WIDTH = width
- settings.CHROME_START_HEIGHT = height
- settings.HEADLESS_START_WIDTH = width
- settings.HEADLESS_START_HEIGHT = height
self.maximize_option = sb_config.maximize_option
self.save_screenshot_after_test = sb_config.save_screenshot
self.no_screenshot_after_test = sb_config.no_screenshot
@@ -14832,9 +14585,87 @@ def setUp(self, masterqa_mode=False):
self.mobile_emulator = True
except Exception:
raise Exception(exception_string)
+
+ window_position = self.window_position
+ if window_position:
+ if window_position.count(",") != 1:
+ message = (
+ '\n\n window_position expects an "x,y" string!'
+ '\n (Your input was: "%s")\n' % window_position
+ )
+ raise Exception(message)
+ window_position = window_position.replace(" ", "")
+ win_x = None
+ win_y = None
+ try:
+ win_x = int(window_position.split(",")[0])
+ win_y = int(window_position.split(",")[1])
+ except Exception:
+ message = (
+ '\n\n Expecting integer values for "x,y"!'
+ '\n (window_position input was: "%s")\n'
+ % window_position
+ )
+ raise Exception(message)
+ settings.WINDOW_START_X = win_x
+ settings.WINDOW_START_Y = win_y
+
+ window_size = self.window_size
+ if window_size:
+ if window_size.count(",") != 1:
+ message = (
+ '\n\n window_size expects a "width,height" string!'
+ '\n (Your input was: "%s")\n' % window_size
+ )
+ raise Exception(message)
+ window_size = window_size.replace(" ", "")
+ width = None
+ height = None
+ try:
+ width = int(window_size.split(",")[0])
+ height = int(window_size.split(",")[1])
+ except Exception:
+ message = (
+ '\n\n Expecting integer values for "width,height"!'
+ '\n (window_size input was: "%s")\n' % window_size
+ )
+ raise Exception(message)
+ settings.CHROME_START_WIDTH = width
+ settings.CHROME_START_HEIGHT = height
+ settings.HEADLESS_START_WIDTH = width
+ settings.HEADLESS_START_HEIGHT = height
+
+ if self.xvfb_metrics:
+ metrics_string = self.xvfb_metrics
+ metrics_string = metrics_string.replace(" ", "")
+ metrics_list = metrics_string.split(",")[0:2]
+ exception_string = (
+ "Invalid input for xvfb_metrics!\n"
+ "Expecting a comma-separated string\n"
+ "with integer values for Width/Height.\n"
+ 'Eg. --xvfb-metrics="1920,1080".\n'
+ "(Minimum: 1024,768) (Default: 1366,768)"
+ )
+ if len(metrics_list) != 2:
+ raise Exception(exception_string)
+ try:
+ self._xvfb_width = int(metrics_list[0])
+ self._xvfb_height = int(metrics_list[1])
+ # The minimum width,height is: 1024,768
+ if self._xvfb_width < 1024:
+ self._xvfb_width = 1024
+ sb_config._xvfb_width = self._xvfb_width
+ if self._xvfb_height < 768:
+ self._xvfb_height = 768
+ sb_config._xvfb_height = self._xvfb_height
+ self.xvfb = True
+ except Exception:
+ raise Exception(exception_string)
+
if self.mobile_emulator and not self.user_agent:
# Use a Pixel user agent by default if not specified
self.user_agent = constants.Mobile.AGENT
+
if self.browser in ["firefox", "ie", "safari"]:
# The Recorder Mode browser extension is only for Chrome/Edge.
if self.recorder_mode:
@@ -14868,7 +14699,7 @@ def setUp(self, masterqa_mode=False):
if not hasattr(sb_config, "shared_driver"):
sb_config.shared_driver = None
if sb_config.shared_driver:
- try:
+ with suppress(Exception):
self._default_driver = sb_config.shared_driver
self.driver = sb_config.shared_driver
self._drivers_list = [sb_config.shared_driver]
@@ -14882,12 +14713,8 @@ def setUp(self, masterqa_mode=False):
self.switch_to_window(0)
if self._crumbs:
self.wait_for_ready_state_complete()
- try:
+ with suppress(Exception):
self.driver.delete_all_cookies()
- except Exception:
- pass
- except Exception:
- pass
if self._reuse_session and sb_config.shared_driver and has_url:
good_start_page = False
if self.recorder_ext:
@@ -14974,10 +14801,8 @@ def setUp(self, masterqa_mode=False):
if self.driver.timeouts.implicit_wait > 0:
self.driver.implicitly_wait(0)
except Exception:
- try:
+ with suppress(Exception):
self.driver.implicitly_wait(0)
- except Exception:
- pass
self._default_driver = self.driver
if self._reuse_session:
sb_config.shared_driver = self.driver
@@ -14996,13 +14821,11 @@ def setUp(self, masterqa_mode=False):
self.set_time_limit(self.time_limit)
# Configure the page load timeout
- try:
+ with suppress(Exception):
if hasattr(settings, "PAGE_LOAD_TIMEOUT"):
self.driver.set_page_load_timeout(settings.PAGE_LOAD_TIMEOUT)
else:
self.driver.set_page_load_timeout(120) # Selenium uses 300
- except Exception:
- pass
# Set the start time for the test (in ms).
# Although the pytest clock starts before setUp() begins,
@@ -15031,7 +14854,7 @@ def __set_last_page_screenshot(self):
not self.__last_page_screenshot
and not self.__last_page_screenshot_png
):
- try:
+ with suppress(Exception):
try:
element = page_actions.wait_for_element_visible(
self.driver,
@@ -15061,14 +14884,10 @@ def __set_last_page_screenshot(self):
element.screenshot_as_base64
)
except Exception:
- try:
+ with suppress(Exception):
self.__last_page_screenshot = (
self.driver.get_screenshot_as_base64()
)
- except Exception:
- pass
- except Exception:
- pass
if not self.__last_page_screenshot:
self.__last_page_screenshot = SCREENSHOT_UNDEFINED
self.__last_page_screenshot_png = SCREENSHOT_UNDEFINED
@@ -15078,12 +14897,10 @@ def __set_last_page_screenshot(self):
element.screenshot_as_png
)
except Exception:
- try:
+ with suppress(Exception):
self.__last_page_screenshot_png = (
self.driver.get_screenshot_as_png()
)
- except Exception:
- pass
else:
import base64
@@ -15098,12 +14915,10 @@ def __set_last_page_screenshot(self):
element.screenshot_as_png
)
except Exception:
- try:
+ with suppress(Exception):
self.__last_page_screenshot_png = (
self.driver.get_screenshot_as_png()
)
- except Exception:
- pass
sb_config._last_page_screenshot_png = self.__last_page_screenshot_png
def __set_last_page_url(self):
@@ -15155,7 +14970,6 @@ def __insert_test_result(self, state, err):
data_payload.state = state
if err:
import traceback
-
tb_string = traceback.format_exc()
if "Message: " in tb_string:
data_payload.message = (
@@ -15190,7 +15004,7 @@ def _add_pytest_html_extra(self):
def __add_pytest_html_extra(self):
if not self.__added_pytest_html_extra:
- try:
+ with suppress(Exception):
if self.with_selenium:
if not self.__last_page_screenshot:
self.__set_last_page_screenshot()
@@ -15217,8 +15031,6 @@ def __add_pytest_html_extra(self):
):
self._html_report_extra.append(extra_url)
self._html_report_extra.append(extra_image)
- except Exception:
- pass
def __delay_driver_quit(self):
delay_driver_quit = False
@@ -15312,7 +15124,6 @@ def __get_test_id(self):
context_id = None
if filename == "base_case.py" or methodname == "runTest":
import traceback
-
stack_base = traceback.format_stack()[0].split(", in ")[0]
test_base = stack_base.split(", in ")[0].split(os.sep)[-1]
if hasattr(self, "cm_filename") and self.cm_filename:
@@ -16034,7 +15845,14 @@ def tearDown(self):
self.driver.window_handles
except Exception:
self.driver.connect()
- self.__process_recorded_actions()
+ try:
+ self.__process_recorded_actions()
+ except Exception as e:
+ print("\n (Recorder) Code-generation exception:")
+ if hasattr(e, "msg"):
+ print("\n" + str(e.msg))
+ else:
+ print(e)
self.__called_teardown = True
self.__called_setup = False
try:
@@ -16071,10 +15889,8 @@ def tearDown(self):
try:
self.driver.window_handles
except Exception:
- try:
+ with suppress(Exception):
self.driver.connect()
- except Exception:
- pass
self.__slow_mode_pause_if_active()
has_exception = self.__has_exception()
sb_config._has_exception = has_exception
@@ -16264,7 +16080,6 @@ def tearDown(self):
else:
# (Pynose / Behave / Pure Python)
if hasattr(self, "is_behave") and self.is_behave:
- import colorama
if sb_config.behave_scenario.status.name == "failed":
has_exception = True
sb_config._has_exception = True
diff --git a/seleniumbase/fixtures/js_utils.py b/seleniumbase/fixtures/js_utils.py
index de7420a8bd1..ff85ed57f32 100644
--- a/seleniumbase/fixtures/js_utils.py
+++ b/seleniumbase/fixtures/js_utils.py
@@ -2,6 +2,7 @@
import re
import requests
import time
+from contextlib import suppress
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import WebDriverException
from selenium.webdriver.common.by import By
@@ -56,11 +57,9 @@ def execute_async_script(driver, script, timeout=settings.LARGE_TIMEOUT):
def wait_for_angularjs(driver, timeout=settings.LARGE_TIMEOUT, **kwargs):
if hasattr(settings, "SKIP_JS_WAITS") and settings.SKIP_JS_WAITS:
return
- try:
+ with suppress(Exception):
# This closes pop-up alerts
driver.execute_script("")
- except Exception:
- pass
if (
(hasattr(driver, "_is_using_uc") and driver._is_using_uc)
or not settings.WAIT_FOR_ANGULARJS
@@ -92,10 +91,8 @@ def wait_for_angularjs(driver, timeout=settings.LARGE_TIMEOUT, **kwargs):
"handler": handler,
"suffix": suffix,
}
- try:
+ with suppress(Exception):
execute_async_script(driver, script, timeout=timeout)
- except Exception:
- pass
def convert_to_css_selector(selector, by=By.CSS_SELECTOR):
@@ -191,14 +188,12 @@ def raise_unable_to_load_jquery_exception(driver):
def activate_jquery(driver):
# If "jQuery is not defined" on a website, use this method to activate it.
# This method is needed because jQuery is not always defined on web sites.
- try:
+ with suppress(Exception):
# Let's first find out if jQuery is already defined.
driver.execute_script("jQuery('html');")
# Since that command worked, jQuery is defined. Let's return.
return
- except Exception:
- # jQuery is not currently defined. Let's proceed by defining it.
- pass
+ # jQuery is not defined. It will be loaded in the next part.
jquery_js = constants.JQuery.MIN_JS
add_js_link(driver, jquery_js)
for x in range(36):
@@ -390,11 +385,9 @@ def highlight(driver, selector, by="css selector", loops=4):
def highlight_with_js(driver, selector, loops=4, o_bs=""):
- try:
+ with suppress(Exception):
# This closes any pop-up alerts
driver.execute_script("")
- except Exception:
- pass
if selector == "html":
selector = "body"
selector_no_spaces = selector.replace(" ", "")
@@ -490,11 +483,9 @@ def highlight_with_js(driver, selector, loops=4, o_bs=""):
def highlight_element_with_js(driver, element, loops=4, o_bs=""):
- try:
+ with suppress(Exception):
# This closes any pop-up alerts
driver.execute_script("")
- except Exception:
- pass
script = (
"""arguments[0].style.boxShadow =
'0px 0px 6px 6px rgba(128, 128, 128, 0.5)';"""
@@ -566,11 +557,9 @@ def highlight_element_with_js(driver, element, loops=4, o_bs=""):
def highlight_with_jquery(driver, selector, loops=4, o_bs=""):
- try:
+ with suppress(Exception):
# This closes any pop-up alerts
driver.execute_script("")
- except Exception:
- pass
if selector == "html":
selector = "body"
selector_no_spaces = selector.replace(" ", "")
@@ -908,11 +897,9 @@ def set_messenger_theme(
time.sleep(0.03)
activate_messenger(driver)
time.sleep(0.15)
- try:
+ with suppress(Exception):
driver.execute_script(msg_style)
time.sleep(0.02)
- except Exception:
- pass
time.sleep(0.05)
@@ -949,7 +936,7 @@ def post_messenger_success_message(driver, message, msg_dur=None):
if not msg_dur:
msg_dur = settings.DEFAULT_MESSAGE_DURATION
msg_dur = float(msg_dur)
- try:
+ with suppress(Exception):
theme = "future"
location = "bottom_right"
if hasattr(sb_config, "mobile_emulator") and sb_config.mobile_emulator:
@@ -957,28 +944,22 @@ def post_messenger_success_message(driver, message, msg_dur=None):
set_messenger_theme(driver, theme=theme, location=location)
post_message(driver, message, msg_dur, style="success")
time.sleep(msg_dur + 0.07)
- except Exception:
- pass
def post_messenger_error_message(driver, message, msg_dur=None):
if not msg_dur:
msg_dur = settings.DEFAULT_MESSAGE_DURATION
msg_dur = float(msg_dur)
- try:
+ with suppress(Exception):
set_messenger_theme(driver, theme="block", location="top_center")
post_message(driver, message, msg_dur, style="error")
time.sleep(msg_dur + 0.07)
- except Exception:
- pass
def highlight_with_js_2(driver, message, selector, o_bs, msg_dur):
- try:
+ with suppress(Exception):
# This closes any pop-up alerts
driver.execute_script("")
- except Exception:
- pass
if selector == "html":
selector = "body"
selector_no_spaces = selector.replace(" ", "")
@@ -991,11 +972,9 @@ def highlight_with_js_2(driver, message, selector, o_bs, msg_dur):
else:
early_exit = True # Changing the box-shadow changes the selector
if early_exit:
- try:
+ with suppress(Exception):
activate_jquery(driver)
post_messenger_success_message(driver, message, msg_dur)
- except Exception:
- pass
return
script = (
"""document.querySelector('%s').style.boxShadow =
@@ -1047,11 +1026,9 @@ def highlight_with_js_2(driver, message, selector, o_bs, msg_dur):
except Exception:
return
time.sleep(0.0181)
- try:
+ with suppress(Exception):
activate_jquery(driver)
post_messenger_success_message(driver, message, msg_dur)
- except Exception:
- pass
script = """document.querySelector('%s').style.boxShadow = '%s';""" % (
selector,
o_bs,
@@ -1063,11 +1040,9 @@ def highlight_with_js_2(driver, message, selector, o_bs, msg_dur):
def highlight_element_with_js_2(driver, message, element, o_bs, msg_dur):
- try:
+ with suppress(Exception):
# This closes any pop-up alerts
driver.execute_script("")
- except Exception:
- pass
script = (
"""arguments[0].style.boxShadow =
'0px 0px 6px 6px rgba(128, 128, 128, 0.5)';"""
@@ -1113,11 +1088,9 @@ def highlight_element_with_js_2(driver, message, element, o_bs, msg_dur):
except Exception:
return
time.sleep(0.0181)
- try:
+ with suppress(Exception):
activate_jquery(driver)
post_messenger_success_message(driver, message, msg_dur)
- except Exception:
- pass
script = """arguments[0].style.boxShadow = '%s';""" % (o_bs)
try:
driver.execute_script(script, element)
@@ -1138,11 +1111,9 @@ def highlight_with_jquery_2(driver, message, selector, o_bs, msg_dur):
else:
early_exit = True # Changing the box-shadow changes the selector
if early_exit:
- try:
+ with suppress(Exception):
activate_jquery(driver)
post_messenger_success_message(driver, message, msg_dur)
- except Exception:
- pass
return
script = (
"""jQuery('%s').css('box-shadow',
@@ -1195,11 +1166,9 @@ def highlight_with_jquery_2(driver, message, selector, o_bs, msg_dur):
return
time.sleep(0.0181)
- try:
+ with suppress(Exception):
activate_jquery(driver)
post_messenger_success_message(driver, message, msg_dur)
- except Exception:
- pass
script = """jQuery('%s').css('box-shadow', '%s');""" % (selector, o_bs)
try:
@@ -1484,12 +1453,10 @@ def get_drag_and_drop_with_offset_script(selector, x, y):
def clear_out_console_logs(driver):
- try:
+ with suppress(Exception):
# Clear out the current page log before navigating to a new page
# (To make sure that assert_no_js_errors() uses current results)
driver.get_log("browser")
- except Exception:
- pass
def _jq_format(code):
diff --git a/seleniumbase/fixtures/page_actions.py b/seleniumbase/fixtures/page_actions.py
index f6c657faaae..3e0b1506cd1 100644
--- a/seleniumbase/fixtures/page_actions.py
+++ b/seleniumbase/fixtures/page_actions.py
@@ -21,6 +21,7 @@
import fasteners
import os
import time
+from contextlib import suppress
from selenium.common.exceptions import ElementNotInteractableException
from selenium.common.exceptions import ElementNotVisibleException
from selenium.common.exceptions import NoAlertPresentException
@@ -1409,12 +1410,10 @@ def switch_to_frame(driver, frame, timeout=settings.SMALL_TIMEOUT):
else:
by = "css selector"
if is_element_visible(driver, frame, by=by):
- try:
+ with suppress(Exception):
element = driver.find_element(by=by, value=frame)
driver.switch_to.frame(element)
return True
- except Exception:
- pass
now_ms = time.time() * 1000.0
if now_ms >= stop_ms:
break
@@ -1548,12 +1547,10 @@ def click_if_visible(
if is_element_visible(driver, selector, by=by):
click(driver, selector, by=by, timeout=1)
elif timeout > 0:
- try:
+ with suppress(Exception):
wait_for_element_visible(
driver, selector, by=by, timeout=timeout
)
- except Exception:
- pass
if is_element_visible(driver, selector, by=by):
click(driver, selector, by=by, timeout=1)
diff --git a/seleniumbase/fixtures/page_utils.py b/seleniumbase/fixtures/page_utils.py
index 18044a8b1d3..ae02add9c76 100644
--- a/seleniumbase/fixtures/page_utils.py
+++ b/seleniumbase/fixtures/page_utils.py
@@ -16,7 +16,7 @@ def get_domain_url(url):
Into this:
https://blog.xkcd.com
"""
- if not url.startswith("http://") and not url.startswith("https://"):
+ if not url.startswith(("http://", "https://")):
return url
url_header = url.split("://")[0]
simple_url = url.split("://")[1]
@@ -40,45 +40,25 @@ def swap_selector_and_by_if_reversed(selector, by):
def is_xpath_selector(selector):
"""Determine if a selector is an xpath selector."""
- if (
- selector.startswith("/")
- or selector.startswith("./")
- or selector.startswith("(")
- ):
- return True
- return False
+ return selector.startswith(("/", "./", "("))
def is_link_text_selector(selector):
"""Determine if a selector is a link text selector."""
- if (
- selector.startswith("link=")
- or selector.startswith("link_text=")
- or selector.startswith("text=")
- ):
- return True
- return False
+ return selector.startswith(("link=", "link_text=", "text="))
def is_partial_link_text_selector(selector):
"""Determine if a selector is a partial link text selector."""
- if (
- selector.startswith("partial_link=")
- or selector.startswith("partial_link_text=")
- or selector.startswith("partial_text=")
- or selector.startswith("p_link=")
- or selector.startswith("p_link_text=")
- or selector.startswith("p_text=")
- ):
- return True
- return False
+ return selector.startswith((
+ "partial_link=", "partial_link_text=", "partial_text=",
+ "p_link=", "p_link_text=", "p_text="
+ ))
def is_name_selector(selector):
"""Determine if a selector is a name selector."""
- if selector.startswith("name=") or selector.startswith("&"):
- return True
- return False
+ return selector.startswith(("name=", "&"))
def recalculate_selector(selector, by, xp_ok=True):
@@ -108,10 +88,9 @@ def recalculate_selector(selector, by, xp_ok=True):
name = get_name_from_selector(selector)
selector = '[name="%s"]' % name
by = By.CSS_SELECTOR
- if xp_ok:
- if ":contains(" in selector and by == By.CSS_SELECTOR:
- selector = css_to_xpath.convert_css_to_xpath(selector)
- by = By.XPATH
+ if xp_ok and ":contains(" in selector and by == By.CSS_SELECTOR:
+ selector = css_to_xpath.convert_css_to_xpath(selector)
+ by = By.XPATH
if by == "":
by = By.CSS_SELECTOR
if not is_valid_by(by):
@@ -130,21 +109,10 @@ def looks_like_a_page_url(url):
possible typos when calling self.get(url), which will try to
navigate to the page if a URL is detected, but will instead call
self.get_element(URL_AS_A_SELECTOR) if the input is not a URL."""
- if (
- url.startswith("http:")
- or url.startswith("https:")
- or url.startswith("://")
- or url.startswith("about:")
- or url.startswith("blob:")
- or url.startswith("chrome:")
- or url.startswith("data:")
- or url.startswith("edge:")
- or url.startswith("file:")
- or url.startswith("view-source:")
- ):
- return True
- else:
- return False
+ return url.startswith((
+ "http:", "https:", "://", "about:", "blob:", "chrome:",
+ "data:", "edge:", "file:", "view-source:"
+ ))
def get_link_text_from_selector(selector):
@@ -195,18 +163,12 @@ def is_valid_url(url):
r"(?:/?|[/?]\S+)$",
re.IGNORECASE,
)
- if (
+ return (
regex.match(url)
- or url.startswith("about:")
- or url.startswith("blob:")
- or url.startswith("chrome:")
- or url.startswith("data:")
- or url.startswith("edge:")
- or url.startswith("file:")
- ):
- return True
- else:
- return False
+ or url.startswith((
+ "about:", "blob:", "chrome:", "data:", "edge:", "file:"
+ ))
+ )
def _get_unique_links(page_url, soup):
diff --git a/seleniumbase/plugins/base_plugin.py b/seleniumbase/plugins/base_plugin.py
index 61b6da01afe..2c4f945a2cd 100644
--- a/seleniumbase/plugins/base_plugin.py
+++ b/seleniumbase/plugins/base_plugin.py
@@ -2,6 +2,7 @@
import ast
import sys
import time
+from contextlib import suppress
from nose.plugins import Plugin
from seleniumbase import config as sb_config
from seleniumbase.config import settings
@@ -305,14 +306,12 @@ def add_fails_or_errors(self, test, err):
if python3_11_or_newer and py311_patch2:
# Handle a bug on Python 3.11 where exceptions aren't seen
sb_config._browser_version = None
- try:
+ with suppress(Exception):
test._BaseCase__set_last_page_screenshot()
test._BaseCase__set_last_page_url()
test._BaseCase__set_last_page_source()
sb_config._browser_version = test._get_browser_version()
test._log_fail_data()
- except Exception:
- pass
sb_config._excinfo_tb = err
log_path = None
if hasattr(sb_config, "_test_logpath"):
diff --git a/seleniumbase/plugins/driver_manager.py b/seleniumbase/plugins/driver_manager.py
index 6c292433e5e..a3f19e5a48e 100644
--- a/seleniumbase/plugins/driver_manager.py
+++ b/seleniumbase/plugins/driver_manager.py
@@ -7,7 +7,7 @@
Example -->
-```
+```python
from seleniumbase import DriverContext
with DriverContext() as driver:
@@ -27,7 +27,7 @@
Example -->
-```
+```python
from seleniumbase import Driver
driver = Driver()
@@ -75,7 +75,7 @@ def Driver(
proxy=None, # Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT".
proxy_bypass_list=None, # Skip proxy when using the listed domains.
proxy_pac_url=None, # Use PAC file. (Format: URL or USERNAME:PASSWORD@URL)
- multi_proxy=False, # Allow multiple proxies with auth when multi-threaded.
+ multi_proxy=None, # Allow multiple proxies with auth when multi-threaded.
agent=None, # Modify the web browser's User-Agent string.
cap_file=None, # The desired capabilities to use with a Selenium Grid.
cap_string=None, # The desired capabilities to use with a Selenium Grid.
@@ -115,6 +115,8 @@ def Driver(
page_load_strategy=None, # Set Chrome PLS to "normal", "eager", or "none".
use_wire=None, # Use selenium-wire's webdriver over selenium webdriver.
external_pdf=None, # Set Chrome "plugins.always_open_pdf_externally":True.
+ window_position=None, # Set the browser's starting window position: "X,Y"
+ window_size=None, # Set the browser's starting window size: "Width,Height"
is_mobile=None, # Use the mobile device emulator while running tests.
mobile=None, # Shortcut / Duplicate of "is_mobile".
d_width=None, # Set device width
@@ -131,7 +133,98 @@ def Driver(
wire=None, # Shortcut / Duplicate of "use_wire".
pls=None, # Shortcut / Duplicate of "page_load_strategy".
):
+ """
+ * SeleniumBase Driver as a Python Context Manager or a returnable object. *
+
+ Example 1: (context manager format)
+ -----------------------------------
+ .. code-block:: python
+ from seleniumbase import DriverContext
+
+ with DriverContext() as driver:
+ driver.get("https://google.com/ncr")
+
+ Example 2: (as a Python returnable)
+ -----------------------------------
+ .. code-block:: python
+ from seleniumbase import Driver
+
+ driver = Driver()
+ driver.get("https://google.com/ncr")
+
+ Optional Parameters:
+ --------------------
+ browser: # Choose from "chrome", "edge", "firefox", or "safari".
+ headless: # The original headless mode for Chromium and Firefox.
+ headless2: # Chromium's new headless mode. (Has more features)
+ headed: # Run tests in headed/GUI mode on Linux, where not default.
+ locale_code: # Set the Language Locale Code for the web browser.
+ protocol: # The Selenium Grid protocol: "http" or "https".
+ servername: # The Selenium Grid server/IP used for tests.
+ port: # The Selenium Grid port used by the test server.
+ proxy: # Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT".
+ proxy_bypass_list: # Skip proxy when using the listed domains.
+ proxy_pac_url: # Use PAC file. (Format: URL or USERNAME:PASSWORD@URL)
+ multi_proxy: # Allow multiple proxies with auth when multi-threaded.
+ agent: # Modify the web browser's User-Agent string.
+ cap_file: # The desired capabilities to use with a Selenium Grid.
+ cap_string: # The desired capabilities to use with a Selenium Grid.
+ recorder_ext: # Enables the SeleniumBase Recorder Chromium extension.
+ disable_js: # Disable JavaScript on websites. Pages might break!
+ disable_csp: # Disable the Content Security Policy of websites.
+ enable_ws: # Enable Web Security on Chromium-based browsers.
+ disable_ws: # Reverse of "enable_ws". (None and False are different)
+ enable_sync: # Enable "Chrome Sync" on websites.
+ use_auto_ext: # Use Chrome's automation extension.
+ undetectable: # Use undetected-chromedriver to evade bot-detection.
+ uc_cdp_events: # Capture CDP events in undetected-chromedriver mode.
+ uc_subprocess: # Use undetected-chromedriver as a subprocess.
+ log_cdp_events: # Capture {"performance": "ALL", "browser": "ALL"}
+ no_sandbox: # (DEPRECATED) - "--no-sandbox" is always used now.
+ disable_gpu: # (DEPRECATED) - GPU is disabled if not "swiftshader".
+ incognito: # Enable Chromium's Incognito mode.
+ guest_mode: # Enable Chromium's Guest mode.
+ dark_mode: # Enable Chromium's Dark mode.
+ devtools: # Open Chromium's DevTools when the browser opens.
+ remote_debug: # Enable Chrome's Debugger on "http://localhost:9222".
+ enable_3d_apis: # Enable WebGL and 3D APIs.
+ swiftshader: # Chrome: --use-gl=angle / --use-angle=swiftshader-webgl
+ ad_block_on: # Block some types of display ads from loading.
+ host_resolver_rules: # Set host-resolver-rules, comma-separated.
+ block_images: # Block images from loading during tests.
+ do_not_track: # Tell websites that you don't want to be tracked.
+ chromium_arg: # "ARG=N,ARG2" (Set Chromium args, ","-separated.)
+ firefox_arg: # "ARG=N,ARG2" (Set Firefox args, comma-separated.)
+ firefox_pref: # SET (Set Firefox PREFERENCE:VALUE set, ","-separated)
+ user_data_dir: # Set the Chrome user data directory to use.
+ extension_zip: # Load a Chrome Extension .zip|.crx, comma-separated.
+ extension_dir: # Load a Chrome Extension directory, comma-separated.
+ disable_features: # "F1,F2" (Disable Chrome features, ","-separated.)
+ binary_location: # Set path of the Chromium browser binary to use.
+ driver_version: # Set the chromedriver or uc_driver version to use.
+ page_load_strategy: # Set Chrome PLS to "normal", "eager", or "none".
+ use_wire: # Use selenium-wire's webdriver over selenium webdriver.
+ external_pdf: # Set Chrome "plugins.always_open_pdf_externally":True.
+ window_position: # Set the browser's starting window position: "X,Y"
+ window_size: # Set the browser's starting window size: "Width,Height"
+ is_mobile: # Use the mobile device emulator while running tests.
+ mobile: # Shortcut / Duplicate of "is_mobile".
+ d_width: # Set device width
+ d_height: # Set device height
+ d_p_r: # Set device pixel ratio
+ uc: # Shortcut / Duplicate of "undetectable".
+ undetected: # Shortcut / Duplicate of "undetectable".
+ uc_cdp: # Shortcut / Duplicate of "uc_cdp_events".
+ uc_sub: # Shortcut / Duplicate of "uc_subprocess".
+ log_cdp: # Shortcut / Duplicate of "log_cdp_events".
+ ad_block: # Shortcut / Duplicate of "ad_block_on".
+ server: # Shortcut / Duplicate of "servername".
+ guest: # Shortcut / Duplicate of "guest_mode".
+ wire: # Shortcut / Duplicate of "use_wire".
+ pls: # Shortcut / Duplicate of "page_load_strategy".
+ """
from seleniumbase import config as sb_config
+ from seleniumbase.config import settings
from seleniumbase.fixtures import constants
from seleniumbase.fixtures import shared_utils
@@ -329,6 +422,79 @@ def Driver(
break
count += 1
disable_features = d_f
+ w_p = window_position
+ if w_p is None and "--window-position" in arg_join:
+ count = 0
+ for arg in sys_argv:
+ if arg.startswith("--window-position="):
+ w_p = arg.split("--window-position=")[1]
+ break
+ elif arg == "--window-position" and len(sys_argv) > count + 1:
+ w_p = sys_argv[count + 1]
+ if w_p.startswith("-"):
+ w_p = None
+ break
+ count += 1
+ window_position = w_p
+ if window_position:
+ if window_position.count(",") != 1:
+ message = (
+ '\n\n window_position expects an "x,y" string!'
+ '\n (Your input was: "%s")\n' % window_position
+ )
+ raise Exception(message)
+ window_position = window_position.replace(" ", "")
+ win_x = None
+ win_y = None
+ try:
+ win_x = int(window_position.split(",")[0])
+ win_y = int(window_position.split(",")[1])
+ except Exception:
+ message = (
+ '\n\n Expecting integer values for "x,y"!'
+ '\n (window_position input was: "%s")\n'
+ % window_position
+ )
+ raise Exception(message)
+ settings.WINDOW_START_X = win_x
+ settings.WINDOW_START_Y = win_y
+ w_s = window_size
+ if w_s is None and "--window-size" in arg_join:
+ count = 0
+ for arg in sys_argv:
+ if arg.startswith("--window-size="):
+ w_s = arg.split("--window-size=")[1]
+ break
+ elif arg == "--window-size" and len(sys_argv) > count + 1:
+ w_s = sys_argv[count + 1]
+ if w_s.startswith("-"):
+ w_s = None
+ break
+ count += 1
+ window_size = w_s
+ if window_size:
+ if window_size.count(",") != 1:
+ message = (
+ '\n\n window_size expects a "width,height" string!'
+ '\n (Your input was: "%s")\n' % window_size
+ )
+ raise Exception(message)
+ window_size = window_size.replace(" ", "")
+ width = None
+ height = None
+ try:
+ width = int(window_size.split(",")[0])
+ height = int(window_size.split(",")[1])
+ except Exception:
+ message = (
+ '\n\n Expecting integer values for "width,height"!'
+ '\n (window_size input was: "%s")\n' % window_size
+ )
+ raise Exception(message)
+ settings.CHROME_START_WIDTH = width
+ settings.CHROME_START_HEIGHT = height
+ settings.HEADLESS_START_WIDTH = width
+ settings.HEADLESS_START_HEIGHT = height
if agent is None and "--agent" in arg_join:
count = 0
for arg in sys_argv:
diff --git a/seleniumbase/plugins/pytest_plugin.py b/seleniumbase/plugins/pytest_plugin.py
index 9a5efe76d86..16de9d68010 100644
--- a/seleniumbase/plugins/pytest_plugin.py
+++ b/seleniumbase/plugins/pytest_plugin.py
@@ -4,6 +4,7 @@
import pytest
import sys
import time
+from contextlib import suppress
from seleniumbase import config as sb_config
from seleniumbase.config import settings
from seleniumbase.core import log_helper
@@ -65,6 +66,7 @@ def pytest_addoption(parser):
--headless2 (Use the new headless mode, which supports extensions.)
--headed (Run tests in headed/GUI mode on Linux OS, where not default.)
--xvfb (Run tests using the Xvfb virtual display server on Linux OS.)
+ --xvfb-metrics=STRING (Set Xvfb display size on Linux: "Width,Height".)
--locale=LOCALE_CODE (Set the Language Locale Code for the web browser.)
--interval=SECONDS (The autoplay interval for presentations & tour steps)
--start-page=URL (The starting URL for the web browser when tests begin.)
@@ -109,6 +111,7 @@ def pytest_addoption(parser):
--rcs | --reuse-class-session (Reuse session for tests in class.)
--crumbs (Delete all cookies between tests reusing a session.)
--disable-beforeunload (Disable the "beforeunload" event on Chrome.)
+ --window-position=X,Y (Set the browser's starting window position.)
--window-size=WIDTH,HEIGHT (Set the browser's starting window size.)
--maximize (Start tests with the browser window maximized.)
--screenshot (Save a screenshot at the end of each test.)
@@ -728,6 +731,17 @@ def pytest_addoption(parser):
will no longer be enabled by default on Linux.
Default: False. (Linux-ONLY!)""",
)
+ parser.addoption(
+ "--xvfb-metrics",
+ "--xvfb_metrics",
+ action="store",
+ dest="xvfb_metrics",
+ default=None,
+ help="""Customize the Xvfb metrics (Width,Height) on Linux.
+ Format: A comma-separated string with the 2 values.
+ Examples: "1920,1080" or "1366,768" or "1024,768".
+ Default: None. (None: "1366,768". Min: "1024,768".)""",
+ )
parser.addoption(
"--locale_code",
"--locale-code",
@@ -1229,6 +1243,17 @@ def pytest_addoption(parser):
on Chromium browsers (Chrome or Edge).
This is already the default Firefox option.""",
)
+ parser.addoption(
+ "--window-position",
+ "--window_position",
+ action="store",
+ dest="window_position",
+ default=None,
+ help="""The option to set the starting window x,y position
+ Format: A comma-separated string with the 2 values.
+ Example: "55,66"
+ Default: None. (Will use default values if None)""",
+ )
parser.addoption(
"--window-size",
"--window_size",
@@ -1516,6 +1541,7 @@ def pytest_configure(config):
sb_config.headless2 = False # Only for Chromium browsers
sb_config.headed = config.getoption("headed")
sb_config.xvfb = config.getoption("xvfb")
+ sb_config.xvfb_metrics = config.getoption("xvfb_metrics")
sb_config.locale_code = config.getoption("locale_code")
sb_config.interval = config.getoption("interval")
sb_config.start_page = config.getoption("start_page")
@@ -1624,6 +1650,7 @@ def pytest_configure(config):
sb_config.shared_driver = None # The default driver for session reuse
sb_config.crumbs = config.getoption("crumbs")
sb_config._disable_beforeunload = config.getoption("_disable_beforeunload")
+ sb_config.window_position = config.getoption("window_position")
sb_config.window_size = config.getoption("window_size")
sb_config.maximize_option = config.getoption("maximize_option")
sb_config.save_screenshot = config.getoption("save_screenshot")
@@ -1822,10 +1849,8 @@ def _create_dashboard_assets_():
abs_path = os.path.abspath(".")
assets_folder = os.path.join(abs_path, "assets")
if not os.path.exists(assets_folder):
- try:
+ with suppress(Exception):
os.makedirs(assets_folder, exist_ok=True)
- except Exception:
- pass
pytest_style_css = os.path.join(assets_folder, "pytest_style.css")
add_pytest_style_css = True
if os.path.exists(pytest_style_css):
@@ -1897,12 +1922,10 @@ def pytest_collection_finish(session):
dash_path = os.path.join(os.getcwd(), "dashboard.html")
dash_url = "file://" + dash_path.replace("\\", "/")
star_len = len("Dashboard: ") + len(dash_url)
- try:
+ with suppress(Exception):
terminal_size = os.get_terminal_size().columns
if terminal_size > 30 and star_len > terminal_size:
star_len = terminal_size
- except Exception:
- pass
stars = "*" * star_len
c1 = ""
cr = ""
@@ -1944,11 +1967,11 @@ def pytest_runtest_teardown(item):
(Has zero effect on tests using --reuse-session / --rs)"""
if "--co" in sys_argv or "--collect-only" in sys_argv:
return
- try:
+ with suppress(Exception):
if hasattr(item, "_testcase") or hasattr(sb_config, "_sb_pdb_driver"):
if hasattr(item, "_testcase"):
self = item._testcase
- try:
+ with suppress(Exception):
if (
hasattr(self, "driver")
and self.driver
@@ -1956,22 +1979,18 @@ def pytest_runtest_teardown(item):
):
if not (is_windows or self.driver.service.process):
self.driver.quit()
- except Exception:
- pass
elif (
hasattr(sb_config, "_sb_pdb_driver")
and sb_config._sb_pdb_driver
):
- try:
+ with suppress(Exception):
if (
not is_windows
or sb_config._sb_pdb_driver.service.process
):
sb_config._sb_pdb_driver.quit()
sb_config._sb_pdb_driver = None
- except Exception:
- pass
- try:
+ with suppress(Exception):
if (
hasattr(self, "_xvfb_display")
and self._xvfb_display
@@ -1988,10 +2007,6 @@ def pytest_runtest_teardown(item):
):
sb_config._virtual_display.stop()
sb_config._virtual_display = None
- except Exception:
- pass
- except Exception:
- pass
if (
(
sb_config._has_exception
@@ -2372,7 +2387,7 @@ def pytest_runtest_makereport(item, call):
)
if log_path:
sb_config._log_fail_data()
- try:
+ with suppress(Exception):
extra_report = None
if hasattr(item, "_testcase"):
extra_report = item._testcase._html_report_extra
@@ -2417,5 +2432,3 @@ def pytest_runtest_makereport(item, call):
"" % constants.Dashboard.LIVE_JS
)
report.extra.append(pytest_html.extras.html(refresh_updates))
- except Exception:
- pass
diff --git a/seleniumbase/plugins/sb_manager.py b/seleniumbase/plugins/sb_manager.py
index 63355b1ce2a..4427910fc0e 100644
--- a/seleniumbase/plugins/sb_manager.py
+++ b/seleniumbase/plugins/sb_manager.py
@@ -7,7 +7,7 @@
Example -->
-```
+```python
from seleniumbase import SB
with SB() as sb: # Many args! Eg. SB(browser="edge")
@@ -41,7 +41,7 @@ def SB(
proxy=None, # Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT".
proxy_bypass_list=None, # Skip proxy when using the listed domains.
proxy_pac_url=None, # Use PAC file. (Format: URL or USERNAME:PASSWORD@URL)
- multi_proxy=False, # Allow multiple proxies with auth when multi-threaded.
+ multi_proxy=None, # Allow multiple proxies with auth when multi-threaded.
agent=None, # Modify the web browser's User-Agent string.
cap_file=None, # The desired capabilities to use with a Selenium Grid.
cap_string=None, # The desired capabilities to use with a Selenium Grid.
@@ -79,10 +79,13 @@ def SB(
wait_for_angularjs=None, # Wait for AngularJS to load after some actions.
use_wire=None, # Use selenium-wire's webdriver over selenium webdriver.
external_pdf=None, # Set Chrome "plugins.always_open_pdf_externally":True.
+ window_position=None, # Set the browser's starting window position: "X,Y"
+ window_size=None, # Set the browser's starting window size: "Width,Height"
is_mobile=None, # Use the mobile device emulator while running tests.
mobile=None, # Shortcut / Duplicate of "is_mobile".
device_metrics=None, # Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio"
xvfb=None, # Run tests using the Xvfb virtual display server on Linux OS.
+ xvfb_metrics=None, # Set Xvfb display size on Linux: "Width,Height".
start_page=None, # The starting URL for the web browser when tests begin.
rec_print=None, # If Recorder is enabled, prints output after tests end.
rec_behave=None, # Like Recorder Mode, but also generates behave-gherkin.
@@ -124,6 +127,124 @@ def SB(
interval=None, # SECONDS (Autoplay interval for SB Slides & Tour steps.)
time_limit=None, # SECONDS (Safely fail tests that exceed the time limit.)
):
+ """
+ * SeleniumBase as a Python Context Manager *
+
+ Example:
+ --------
+ .. code-block:: python
+ from seleniumbase import SB
+
+ with SB() as sb: # Many args! Eg. SB(browser="edge")
+ sb.open("https://google.com/ncr")
+ sb.type('[name="q"]', "SeleniumBase on GitHub")
+ sb.submit('[name="q"]')
+ sb.click('a[href*="github.com/seleniumbase"]')
+ sb.highlight("div.Layout-main")
+ sb.highlight("div.Layout-sidebar")
+ sb.sleep(0.5)
+
+ Optional Parameters:
+ --------------------
+ test: Test Mode: Output, Logging, Continue on failure unless "rtf".
+ rtf: Shortcut / Duplicate of "raise_test_failure".
+ raise_test_failure: If "test" mode, raise Exception on 1st failure.
+ browser: Choose from "chrome", "edge", "firefox", or "safari".
+ headless: The original headless mode for Chromium and Firefox.
+ headless2: Chromium's new headless mode. (Has more features)
+ locale_code: Set the Language Locale Code for the web browser.
+ protocol: The Selenium Grid protocol: "http" or "https".
+ servername: The Selenium Grid server/IP used for tests.
+ port: The Selenium Grid port used by the test server.
+ proxy: Use proxy. Format: "SERVER:PORT" or "USER:PASS@SERVER:PORT".
+ proxy_bypass_list: Skip proxy when using the listed domains.
+ proxy_pac_url: Use PAC file. (Format: URL or USERNAME:PASSWORD@URL)
+ multi_proxy: # Allow multiple proxies with auth when multi-threaded.
+ agent: Modify the web browser's User-Agent string.
+ cap_file: The desired capabilities to use with a Selenium Grid.
+ cap_string: The desired capabilities to use with a Selenium Grid.
+ recorder_ext: Enables the SeleniumBase Recorder Chromium extension.
+ disable_js: Disable JavaScript on websites. Pages might break!
+ disable_csp: Disable the Content Security Policy of websites.
+ enable_ws: Enable Web Security on Chromium-based browsers.
+ enable_sync: Enable "Chrome Sync" on websites.
+ use_auto_ext: Use Chrome's automation extension.
+ undetectable: Use undetected-chromedriver to evade bot-detection.
+ uc_cdp_events: Capture CDP events in undetected-chromedriver mode.
+ uc_subprocess: Use undetected-chromedriver as a subprocess.
+ log_cdp_events: Capture {"performance": "ALL", "browser": "ALL"}
+ incognito: Enable Chromium's Incognito mode.
+ guest_mode: Enable Chromium's Guest mode.
+ dark_mode: Enable Chromium's Dark mode.
+ devtools: Open Chromium's DevTools when the browser opens.
+ remote_debug: Enable Chrome's Debugger on "http://localhost:9222".
+ enable_3d_apis: Enable WebGL and 3D APIs.
+ swiftshader: Chrome: --use-gl=angle / --use-angle=swiftshader-webgl
+ ad_block_on: Block some types of display ads from loading.
+ host_resolver_rules: Set host-resolver-rules, comma-separated.
+ block_images: Block images from loading during tests.
+ do_not_track: Tell websites that you don't want to be tracked.
+ chromium_arg: "ARG=N,ARG2" (Set Chromium args, ","-separated.)
+ firefox_arg: "ARG=N,ARG2" (Set Firefox args, comma-separated.)
+ firefox_pref: SET (Set Firefox PREFERENCE:VALUE set, ","-separated)
+ user_data_dir: Set the Chrome user data directory to use.
+ extension_zip: Load a Chrome Extension .zip|.crx, comma-separated.
+ extension_dir: Load a Chrome Extension directory, comma-separated.
+ disable_features: "F1,F2" (Disable Chrome features, ","-separated.)
+ binary_location: Set path of the Chromium browser binary to use.
+ driver_version: Set the chromedriver or uc_driver version to use.
+ skip_js_waits: Skip JS Waits (readyState=="complete" and Angular).
+ wait_for_angularjs: Wait for AngularJS to load after some actions.
+ use_wire: Use selenium-wire's webdriver over selenium webdriver.
+ external_pdf: Set Chrome "plugins.always_open_pdf_externally":True.
+ window_position: Set the browser's starting window position: "X,Y"
+ window_size: Set the browser's starting window size: "Width,Height"
+ is_mobile: Use the mobile device emulator while running tests.
+ mobile: Shortcut / Duplicate of "is_mobile".
+ device_metrics: Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio"
+ xvfb: Run tests using the Xvfb virtual display server on Linux OS.
+ xvfb_metrics: Set Xvfb display size on Linux: "Width,Height".
+ start_page: The starting URL for the web browser when tests begin.
+ rec_print: If Recorder is enabled, prints output after tests end.
+ rec_behave: Like Recorder Mode, but also generates behave-gherkin.
+ record_sleep: If Recorder enabled, also records self.sleep calls.
+ data: Extra test data. Access with "self.data" in tests.
+ var1: Extra test data. Access with "self.var1" in tests.
+ var2: Extra test data. Access with "self.var2" in tests.
+ var3: Extra test data. Access with "self.var3" in tests.
+ variables: DICT (Extra test data. Access with "self.variables")
+ account: Set account. Access with "self.account" in tests.
+ environment: Set the test env. Access with "self.env" in tests.
+ headed: Run tests in headed/GUI mode on Linux, where not default.
+ maximize: Start tests with the browser window maximized.
+ disable_ws: Reverse of "enable_ws". (None and False are different)
+ disable_beforeunload: Disable the "beforeunload" event on Chromium.
+ settings_file: A file for overriding default SeleniumBase settings.
+ uc: Shortcut / Duplicate of "undetectable".
+ undetected: Shortcut / Duplicate of "undetectable".
+ uc_cdp: Shortcut / Duplicate of "uc_cdp_events".
+ uc_sub: Shortcut / Duplicate of "uc_subprocess".
+ log_cdp: Shortcut / Duplicate of "log_cdp_events".
+ ad_block: Shortcut / Duplicate of "ad_block_on".
+ server: Shortcut / Duplicate of "servername".
+ guest: Shortcut / Duplicate of "guest_mode".
+ wire: Shortcut / Duplicate of "use_wire".
+ pls: Shortcut / Duplicate of "page_load_strategy".
+ sjw: Shortcut / Duplicate of "skip_js_waits".
+ wfa: Shortcut / Duplicate of "wait_for_angularjs".
+ save_screenshot: Save a screenshot at the end of each test.
+ no_screenshot: No screenshots saved unless tests directly ask it.
+ page_load_strategy: Set Chrome PLS to "normal", "eager", or "none".
+ timeout_multiplier: Multiplies the default timeout values.
+ js_checking_on: Check for JavaScript errors after page loads.
+ slow: Slow down the automation. Faster than using Demo Mode.
+ demo: Slow down and visually see test actions as they occur.
+ demo_sleep: SECONDS (Set wait time after Slow & Demo Mode actions.)
+ message_duration: SECONDS (The time length for Messenger alerts.)
+ highlights: Number of highlight animations for Demo Mode actions.
+ interval: SECONDS (Autoplay interval for SB Slides & Tour steps.)
+ time_limit: SECONDS (Safely fail tests that exceed the time limit.)
+ """
import os
import sys
import time
@@ -365,6 +486,48 @@ def SB(
break
count += 1
disable_features = d_f
+ w_p = window_position
+ if w_p is None and "--window-position" in arg_join:
+ count = 0
+ for arg in sys_argv:
+ if arg.startswith("--window-position="):
+ w_p = arg.split("--window-position=")[1]
+ break
+ elif arg == "--window-position" and len(sys_argv) > count + 1:
+ w_p = sys_argv[count + 1]
+ if w_p.startswith("-"):
+ w_p = None
+ break
+ count += 1
+ window_position = w_p
+ w_s = window_size
+ if w_s is None and "--window-size" in arg_join:
+ count = 0
+ for arg in sys_argv:
+ if arg.startswith("--window-size="):
+ w_s = arg.split("--window-size=")[1]
+ break
+ elif arg == "--window-size" and len(sys_argv) > count + 1:
+ w_s = sys_argv[count + 1]
+ if w_s.startswith("-"):
+ w_s = None
+ break
+ count += 1
+ window_size = w_s
+ x_m = xvfb_metrics
+ if x_m is None and "--xvfb-metrics" in arg_join:
+ count = 0
+ for arg in sys_argv:
+ if arg.startswith("--xvfb-metrics="):
+ x_m = arg.split("--xvfb-metrics=")[1]
+ break
+ elif arg == "--xvfb-metrics" and len(sys_argv) > count + 1:
+ x_m = sys_argv[count + 1]
+ if x_m.startswith("-"):
+ x_m = None
+ break
+ count += 1
+ xvfb_metrics = x_m
if agent is None and "--agent" in arg_join:
count = 0
for arg in sys_argv:
@@ -761,6 +924,7 @@ def SB(
sb_config.headless2 = headless2
sb_config.headed = headed
sb_config.xvfb = xvfb
+ sb_config.xvfb_metrics = xvfb_metrics
sb_config.start_page = start_page
sb_config.locale_code = locale_code
sb_config.protocol = protocol
@@ -803,7 +967,8 @@ def SB(
sb_config.crumbs = False
sb_config.final_debug = False
sb_config.visual_baseline = False
- sb_config.window_size = None
+ sb_config.window_position = window_position
+ sb_config.window_size = window_size
sb_config.maximize_option = maximize_option
sb_config._disable_beforeunload = _disable_beforeunload
sb_config.save_screenshot = save_screenshot
@@ -864,6 +1029,7 @@ def SB(
sb.headless2 = sb_config.headless2
sb.headed = sb_config.headed
sb.xvfb = sb_config.xvfb
+ sb.xvfb_metrics = sb_config.xvfb_metrics
sb.start_page = sb_config.start_page
sb.locale_code = sb_config.locale_code
sb.protocol = sb_config.protocol
@@ -908,6 +1074,7 @@ def SB(
sb._crumbs = sb_config.crumbs
sb._final_debug = sb_config.final_debug
sb.visual_baseline = sb_config.visual_baseline
+ sb.window_position = sb_config.window_position
sb.window_size = sb_config.window_size
sb.maximize_option = sb_config.maximize_option
sb._disable_beforeunload = sb_config._disable_beforeunload
diff --git a/seleniumbase/plugins/selenium_plugin.py b/seleniumbase/plugins/selenium_plugin.py
index 0dd373f9d21..edbd340a9c6 100644
--- a/seleniumbase/plugins/selenium_plugin.py
+++ b/seleniumbase/plugins/selenium_plugin.py
@@ -1,5 +1,6 @@
"""Selenium Plugin for SeleniumBase tests that run with pynose / nosetests"""
import sys
+from contextlib import suppress
from nose.plugins import Plugin
from seleniumbase import config as sb_config
from seleniumbase.config import settings
@@ -46,6 +47,7 @@ class SeleniumBrowser(Plugin):
--headless2 (Use the new headless mode, which supports extensions.)
--headed (Run tests in headed/GUI mode on Linux OS, where not default.)
--xvfb (Run tests using the Xvfb virtual display server on Linux OS.)
+ --xvfb-metrics=STRING (Set Xvfb display size on Linux: "Width,Height".)
--locale=LOCALE_CODE (Set the Language Locale Code for the web browser.)
--interval=SECONDS (The autoplay interval for presentations & tour steps)
--start-page=URL (The starting URL for the web browser when tests begin.)
@@ -82,6 +84,7 @@ class SeleniumBrowser(Plugin):
--dark (Enable Chrome's Dark mode.)
--devtools (Open Chrome's DevTools when the browser opens.)
--disable-beforeunload (Disable the "beforeunload" event on Chrome.)
+ --window-position=X,Y (Set the browser's starting window position.)
--window-size=WIDTH,HEIGHT (Set the browser's starting window size.)
--maximize (Start tests with the browser window maximized.)
--screenshot (Save a screenshot at the end of each test.)
@@ -465,6 +468,17 @@ def options(self, parser, env):
will no longer be enabled by default on Linux.
Default: False. (Linux-ONLY!)""",
)
+ parser.addoption(
+ "--xvfb-metrics",
+ "--xvfb_metrics",
+ action="store",
+ dest="xvfb_metrics",
+ default=None,
+ help="""Customize the Xvfb metrics (Width,Height) on Linux.
+ Format: A comma-separated string with the 2 values.
+ Examples: "1920,1080" or "1366,768" or "1024,768".
+ Default: None. (None: "1366,768". Min: "1024,768".)""",
+ )
parser.addoption(
"--locale_code",
"--locale-code",
@@ -886,6 +900,17 @@ def options(self, parser, env):
on Chromium browsers (Chrome or Edge).
This is already the default Firefox option.""",
)
+ parser.addoption(
+ "--window-position",
+ "--window_position",
+ action="store",
+ dest="window_position",
+ default=None,
+ help="""The option to set the starting window x,y position.
+ Format: A comma-separated string with the 2 values.
+ Example: "55,66"
+ Default: None. (Will use default values if None)""",
+ )
parser.addoption(
"--window-size",
"--window_size",
@@ -1060,6 +1085,29 @@ def beforeTest(self, test):
'\n (Your browser choice was: "%s")\n' % browser
)
raise Exception(message)
+ window_position = self.options.window_position
+ if window_position:
+ if window_position.count(",") != 1:
+ message = (
+ '\n\n window_position expects an "x,y" string!'
+ '\n (Your input was: "%s")\n' % window_position
+ )
+ raise Exception(message)
+ window_position = window_position.replace(" ", "")
+ win_x = None
+ win_y = None
+ try:
+ win_x = int(window_position.split(",")[0])
+ win_y = int(window_position.split(",")[1])
+ except Exception:
+ message = (
+ '\n\n Expecting integer values for "x,y"!'
+ '\n (window_position input was: "%s")\n'
+ % window_position
+ )
+ raise Exception(message)
+ settings.WINDOW_START_X = win_x
+ settings.WINDOW_START_Y = win_y
window_size = self.options.window_size
if window_size:
if window_size.count(",") != 1:
@@ -1112,6 +1160,7 @@ def beforeTest(self, test):
self.options.headless2 = False
test.test.headed = self.options.headed
test.test.xvfb = self.options.xvfb
+ test.test.xvfb_metrics = self.options.xvfb_metrics
test.test.locale_code = self.options.locale_code
test.test.interval = self.options.interval
test.test.start_page = self.options.start_page
@@ -1192,6 +1241,7 @@ def beforeTest(self, test):
test.test.dark_mode = self.options.dark_mode
test.test.devtools = self.options.devtools
test.test._disable_beforeunload = self.options._disable_beforeunload
+ test.test.window_position = self.options.window_position
test.test.window_size = self.options.window_size
test.test.maximize_option = self.options.maximize_option
if self.options.save_screenshot and self.options.no_screenshot:
@@ -1259,7 +1309,7 @@ def beforeTest(self, test):
):
width = settings.HEADLESS_START_WIDTH
height = settings.HEADLESS_START_HEIGHT
- try:
+ with suppress(Exception):
from sbvirtualdisplay import Display
self._xvfb_display = Display(visible=0, size=(width, height))
@@ -1267,8 +1317,6 @@ def beforeTest(self, test):
sb_config._virtual_display = self._xvfb_display
self.headless_active = True
sb_config.headless_active = True
- except Exception:
- pass
sb_config._is_timeout_changed = False
sb_config._SMALL_TIMEOUT = settings.SMALL_TIMEOUT
sb_config._LARGE_TIMEOUT = settings.LARGE_TIMEOUT
@@ -1301,7 +1349,7 @@ def afterTest(self, test):
pass
except Exception:
pass
- try:
+ with suppress(Exception):
if (
hasattr(self, "_xvfb_display")
and self._xvfb_display
@@ -1318,5 +1366,3 @@ def afterTest(self, test):
):
sb_config._virtual_display.stop()
sb_config._virtual_display = None
- except Exception:
- pass
diff --git a/seleniumbase/undetected/__init__.py b/seleniumbase/undetected/__init__.py
index 40283f43350..36d6d3a5977 100644
--- a/seleniumbase/undetected/__init__.py
+++ b/seleniumbase/undetected/__init__.py
@@ -10,6 +10,7 @@
import selenium.webdriver.chrome.webdriver
import selenium.webdriver.common.service
import selenium.webdriver.remote.command
+from contextlib import suppress
from .cdp import CDP
from .cdp import PageElement
from .dprocess import start_detached
@@ -201,11 +202,9 @@ def __init__(
# Create a temporary folder for the user-data profile.
options.add_argument(arg)
if not language:
- try:
+ with suppress(Exception):
import locale
language = locale.getlocale()[0].replace("_", "-")
- except Exception:
- pass
if (
not language
or "English" in language
@@ -242,7 +241,7 @@ def __init__(
)
if hasattr(options, 'handle_prefs'):
options.handle_prefs(user_data_dir)
- try:
+ with suppress(Exception):
import json
with open(
os.path.join(
@@ -263,8 +262,6 @@ def __init__(
fs.seek(0, 0)
fs.truncate()
json.dump(config, fs)
- except Exception:
- pass
creationflags = 0
if "win32" in sys.platform:
creationflags = subprocess.CREATE_NO_WINDOW
@@ -293,8 +290,6 @@ def __init__(
self.browser_pid = browser.pid
service_ = None
log_output = subprocess.PIPE
- if sys.version_info < (3, 8):
- log_output = os.devnull
if patch_driver:
service_ = selenium.webdriver.chrome.service.Service(
executable_path=self.patcher.executable_path,
@@ -342,7 +337,7 @@ def __dir__(self):
def _get_cdc_props(self):
cdc_props = []
- try:
+ with suppress(Exception):
cdc_props = self.execute_script(
"""
let objectToInspect = window,
@@ -355,8 +350,6 @@ def _get_cdc_props(self):
return result.filter(i => i.match(/^[a-z]{3}_[a-z]{22}_.*/i))
"""
)
- except Exception:
- pass
return cdc_props
def _hook_remove_cdc_props(self, cdc_props):
@@ -427,50 +420,38 @@ def reconnect(self, timeout=0.1):
- Starts the chromedriver service that runs in the background.
- Recreates the session."""
if hasattr(self, "service"):
- try:
+ with suppress(Exception):
self.service.stop()
- except Exception:
- pass
if isinstance(timeout, str):
if timeout.lower() == "breakpoint":
breakpoint() # To continue:
pass # Type "c" & press ENTER!
else:
time.sleep(timeout)
- try:
+ with suppress(Exception):
self.service.start()
- except Exception:
- pass
time.sleep(0.012)
- try:
+ with suppress(Exception):
self.start_session()
- except Exception:
- pass
time.sleep(0.012)
def disconnect(self):
"""Stops the chromedriver service that runs in the background.
To use driver methods again, you MUST call driver.connect()"""
if hasattr(self, "service"):
- try:
+ with suppress(Exception):
self.service.stop()
- except Exception:
- pass
time.sleep(0.012)
def connect(self):
"""Starts the chromedriver service that runs in the background
and recreates the session."""
if hasattr(self, "service"):
- try:
+ with suppress(Exception):
self.service.start()
- except Exception:
- pass
time.sleep(0.012)
- try:
+ with suppress(Exception):
self.start_session()
- except Exception:
- pass
time.sleep(0.012)
def start_session(self, capabilities=None):
@@ -496,12 +477,10 @@ def quit(self):
if hasattr(self, "service") and getattr(self.service, "process", None):
logger.debug("Stopping webdriver service")
self.service.stop()
- try:
+ with suppress(Exception):
if self.reactor and isinstance(self.reactor, Reactor):
logger.debug("Shutting down Reactor")
self.reactor.event.set()
- except Exception:
- pass
if (
hasattr(self, "keep_user_data_dir")
and hasattr(self, "user_data_dir")
@@ -530,18 +509,14 @@ def quit(self):
self.patcher = None
def __del__(self):
- try:
+ with suppress(Exception):
if "win32" in sys.platform:
self.stop_client()
self.command_executor.close()
else:
super().quit()
- except Exception:
- pass
- try:
+ with suppress(Exception):
self.quit()
- except Exception:
- pass
def __enter__(self):
return self
diff --git a/seleniumbase/undetected/dprocess.py b/seleniumbase/undetected/dprocess.py
index acfdc558cd2..dc6009b8962 100644
--- a/seleniumbase/undetected/dprocess.py
+++ b/seleniumbase/undetected/dprocess.py
@@ -3,7 +3,10 @@
import sys
import atexit
import logging
+import platform
+from contextlib import suppress
from subprocess import PIPE
+from subprocess import Popen
CREATE_NEW_PROCESS_GROUP = 0x00000200
DETACHED_PROCESS = 0x00000008
@@ -37,9 +40,6 @@ def start_detached(executable, *args):
def _start_detached(executable, *args, writer=None):
# Configure Launch
kwargs = {}
- import platform
- from subprocess import Popen
-
if platform.system() == "Windows":
kwargs.update(
creationflags=DETACHED_PROCESS | CREATE_NEW_PROCESS_GROUP
@@ -58,11 +58,9 @@ def _cleanup():
import signal
for pid in REGISTERED:
- try:
+ with suppress(Exception):
logging.getLogger(__name__).debug("cleaning up pid %d " % pid)
os.kill(pid, signal.SIGTERM)
- except Exception:
- pass
atexit.register(_cleanup)
diff --git a/seleniumbase/undetected/options.py b/seleniumbase/undetected/options.py
index b93f3966c73..2f83cec71f3 100644
--- a/seleniumbase/undetected/options.py
+++ b/seleniumbase/undetected/options.py
@@ -1,6 +1,7 @@
#!/usr/bin/env python3
import json
import os
+from contextlib import suppress
from selenium.webdriver.chromium.options import ChromiumOptions
@@ -49,7 +50,7 @@ def handle_prefs(self, user_data_dir):
undot_prefs, self._undot_key(key, value)
)
prefs_file = os.path.join(default_path, "Preferences")
- try:
+ with suppress(Exception):
if os.path.exists(prefs_file):
with open(
prefs_file, encoding="utf-8", mode="r", errors="ignore"
@@ -57,13 +58,9 @@ def handle_prefs(self, user_data_dir):
undot_prefs = self._merge_nested(
json.load(f), undot_prefs
)
- except Exception:
- pass
- try:
+ with suppress(Exception):
with open(prefs_file, encoding="utf-8", mode="w") as f:
json.dump(undot_prefs, f)
- except Exception:
- pass
# Remove experimental_options to avoid errors
del self._experimental_options["prefs"]
exclude_switches = self.experimental_options.get("excludeSwitches")
diff --git a/seleniumbase/undetected/patcher.py b/seleniumbase/undetected/patcher.py
index 11ffa1e2ce2..1375e177b61 100644
--- a/seleniumbase/undetected/patcher.py
+++ b/seleniumbase/undetected/patcher.py
@@ -8,6 +8,7 @@
import sys
import time
import zipfile
+from contextlib import suppress
logger = logging.getLogger(__name__)
IS_POSIX = sys.platform.startswith(("darwin", "cygwin", "linux"))
@@ -53,10 +54,8 @@ def __init__(self, executable_path=None, force=False, version_main=0):
self.executable_path = None
prefix = "undetected"
if not os.path.exists(self.data_path):
- try:
+ with suppress(Exception):
os.makedirs(self.data_path, exist_ok=True)
- except Exception:
- pass
if not executable_path:
self.executable_path = os.path.join(
self.data_path, "_".join([prefix, self.exe_name])
diff --git a/setup.py b/setup.py
index 6066e850fb1..2ddf2005dd7 100755
--- a/setup.py
+++ b/setup.py
@@ -1,5 +1,5 @@
"""Setup steps for installing SeleniumBase dependencies and plugins.
-(Uses selenium 4.x and is compatible with Python 3.7+)"""
+(Uses selenium 4.x and is compatible with Python 3.8+)"""
from setuptools import setup, find_packages # noqa: F401
import os
import sys
@@ -47,7 +47,7 @@
print("\n*** Installing build: *** (Required for PyPI uploads)\n")
os.system("python -m pip install --upgrade 'build'")
print("\n*** Installing pkginfo: *** (Required for PyPI uploads)\n")
- os.system("python -m pip install --upgrade 'pkginfo'")
+ os.system("python -m pip install 'pkginfo'")
print("\n*** Installing readme-renderer: *** (For PyPI uploads)\n")
os.system("python -m pip install --upgrade 'readme-renderer'")
print("\n*** Installing jaraco.classes: *** (For PyPI uploads)\n")
@@ -79,12 +79,14 @@
long_description_content_type="text/markdown",
url="https://github.com/seleniumbase/SeleniumBase",
project_urls={
+ "Homepage": "https://github.com/seleniumbase/SeleniumBase",
"Changelog": "https://github.com/seleniumbase/SeleniumBase/releases",
"Download": "https://pypi.org/project/seleniumbase/#files",
- "Gitter": "https://gitter.im/seleniumbase/SeleniumBase",
"Blog": "https://seleniumbase.com/",
+ "Discord": "https://discord.gg/EdhQTn3EyE",
"PyPI": "https://pypi.org/project/seleniumbase/",
"Source": "https://github.com/seleniumbase/SeleniumBase",
+ "Repository": "https://github.com/seleniumbase/SeleniumBase",
"Documentation": "https://seleniumbase.io/",
},
platforms=["Windows", "Linux", "Mac OS-X"],
@@ -120,7 +122,6 @@
"Operating System :: POSIX :: Linux",
"Programming Language :: Python",
"Programming Language :: Python :: 3",
- "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
@@ -144,82 +145,64 @@
"Topic :: Software Development :: Testing :: Traffic Generation",
"Topic :: Utilities",
],
- python_requires=">=3.7",
+ python_requires=">=3.8",
install_requires=[
- 'pip>=24.0;python_version<"3.8"',
- 'pip>=24.1.2;python_version>="3.8"', # 24.2: editable install warnings
- 'packaging>=24.0;python_version<"3.8"',
- 'packaging>=24.1;python_version>="3.8"',
- 'setuptools>=68.0.0;python_version<"3.8"',
- 'setuptools~=70.2;python_version>="3.8" and python_version<"3.10"',
+ 'pip>=24.1.2', # 24.2: editable install warnings
+ 'packaging>=24.1',
+ 'setuptools~=70.2;python_version<"3.10"',
'setuptools>=70.2.0;python_version>="3.10"', # 71.0.x has issues
- 'wheel>=0.42.0;python_version<"3.8"',
- 'wheel>=0.44.0;python_version>="3.8"',
+ 'wheel>=0.44.0',
'attrs>=24.2.0',
"certifi>=2024.8.30",
"exceptiongroup>=1.2.2",
- 'filelock>=3.12.2;python_version<"3.8"',
- 'filelock>=3.16.0;python_version>="3.8"',
- 'platformdirs>=4.0.0;python_version<"3.8"',
- 'platformdirs>=4.3.2;python_version>="3.8"',
- 'typing-extensions>=4.12.2;python_version>="3.8"',
+ 'filelock>=3.16.1',
+ 'fasteners>=0.19',
+ "pynose>=1.5.3",
+ 'platformdirs>=4.3.6',
+ 'typing-extensions>=4.12.2',
+ "sbvirtualdisplay>=1.3.0",
+ "six>=1.16.0",
'parse>=1.20.2',
'parse-type>=0.6.3',
- 'pyyaml==6.0.1;python_version<"3.8"',
- 'pyyaml>=6.0.2;python_version>="3.8"',
- "six==1.16.0",
- "idna==3.8",
+ 'colorama>=0.4.6',
+ 'pyyaml>=6.0.2',
+ 'pygments>=2.18.0',
+ 'pyreadline3>=3.5.3;platform_system=="Windows"',
+ "tabcompleter>=1.3.3",
+ "pdbp>=1.5.4",
+ "idna==3.10",
'chardet==5.2.0',
'charset-normalizer==3.3.2',
'urllib3>=1.26.20,<2;python_version<"3.10"',
'urllib3>=1.26.20,<2.3.0;python_version>="3.10"',
'requests==2.31.0',
- "pynose==1.5.2",
'sniffio==1.3.1',
'h11==0.14.0',
'outcome==1.3.0.post0',
- 'trio==0.22.2;python_version<"3.8"',
- 'trio==0.26.2;python_version>="3.8"',
+ 'trio==0.26.2',
'trio-websocket==0.11.1',
'wsproto==1.2.0',
- 'websocket-client==1.8.0;python_version>="3.8"',
- 'selenium==4.11.2;python_version<"3.8"',
- 'selenium==4.24.0;python_version>="3.8"',
+ 'websocket-client==1.8.0',
+ 'selenium==4.25.0',
'cssselect==1.2.0',
"sortedcontainers==2.4.0",
- 'fasteners==0.19',
- 'execnet==2.0.2;python_version<"3.8"',
- 'execnet==2.1.1;python_version>="3.8"',
+ 'execnet==2.1.1',
'iniconfig==2.0.0',
- 'pluggy==1.2.0;python_version<"3.8"',
- 'pluggy==1.5.0;python_version>="3.8"',
+ 'pluggy==1.5.0',
"py==1.11.0", # Needed by pytest-html
- 'pytest==7.4.4;python_version<"3.8"',
- 'pytest==8.3.3;python_version>="3.8"',
+ 'pytest==8.3.3',
"pytest-html==2.0.1", # Newer ones had issues
- 'pytest-metadata==3.0.0;python_version<"3.8"',
- 'pytest-metadata==3.1.1;python_version>="3.8"',
+ 'pytest-metadata==3.1.1',
"pytest-ordering==0.6",
- 'pytest-rerunfailures==13.0;python_version<"3.8"',
- 'pytest-rerunfailures==14.0;python_version>="3.8"',
- 'pytest-xdist==3.5.0;python_version<"3.8"',
- 'pytest-xdist==3.6.1;python_version>="3.8"',
+ 'pytest-rerunfailures==14.0',
+ 'pytest-xdist==3.6.1',
'parameterized==0.9.0',
- "sbvirtualdisplay==1.3.0",
"behave==1.2.6",
- 'soupsieve==2.4.1;python_version<"3.8"',
- 'soupsieve==2.6;python_version>="3.8"',
+ 'soupsieve==2.6',
"beautifulsoup4==4.12.3",
- 'pygments==2.17.2;python_version<"3.8"',
- 'pygments==2.18.0;python_version>="3.8"',
- 'pyreadline3==3.4.1;platform_system=="Windows"',
- "tabcompleter==1.3.3",
- "pdbp==1.5.4",
- 'colorama==0.4.6',
'pyotp==2.9.0',
'python-xlib==0.33;platform_system=="Linux"',
- 'markdown-it-py==2.2.0;python_version<"3.8"',
- 'markdown-it-py==3.0.0;python_version>="3.8"',
+ 'markdown-it-py==3.0.0',
'mdurl==0.1.2',
'rich==13.8.1',
],
@@ -235,10 +218,8 @@
# pip install -e .[coverage]
# Usage: coverage run -m pytest; coverage html; coverage report
"coverage": [
- 'coverage==7.2.7;python_version<"3.8"',
- 'coverage>=7.6.1;python_version>="3.8"',
- 'pytest-cov==4.1.0;python_version<"3.8"',
- 'pytest-cov>=5.0.0;python_version>="3.8"',
+ 'coverage>=7.6.1',
+ 'pytest-cov>=5.0.0',
],
# pip install -e .[flake8]
# Usage: flake8
@@ -260,19 +241,16 @@
# pip install -e .[pdfminer]
# (An optional library for parsing PDF files.)
"pdfminer": [
- 'pdfminer.six==20221105;python_version<"3.8"',
- 'pdfminer.six==20240706;python_version>="3.8"',
+ 'pdfminer.six==20240706',
'cryptography==39.0.2;python_version<"3.9"',
'cryptography==43.0.1;python_version>="3.9"',
- 'cffi==1.15.1;python_version<"3.8"',
- 'cffi==1.17.1;python_version>="3.8"',
+ 'cffi==1.17.1',
"pycparser==2.22",
],
# pip install -e .[pillow]
# (An optional library for image-processing.)
"pillow": [
- 'Pillow==9.5.0;python_version<"3.8"',
- 'Pillow>=10.4.0;python_version>="3.8"',
+ 'Pillow>=10.4.0',
],
# pip install -e .[pip-system-certs]
# (If you see [SSL: CERTIFICATE_VERIFY_FAILED], then get this.)
@@ -309,8 +287,7 @@
'hpack==4.0.0',
'hyperframe==6.0.1',
'kaitaistruct==0.10',
- 'pyasn1==0.5.1;python_version<"3.8"',
- 'pyasn1==0.6.1;python_version>="3.8"',
+ 'pyasn1==0.6.1',
'zstandard==0.23.0',
],
},