Merge pull request #1548 from seleniumbase/context-managers-and-more

Context Managers and more
This commit is contained in:
Michael Mintz 2022-10-14 04:11:58 -04:00 committed by GitHub
commit c406380b6e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 1710 additions and 109 deletions

View File

@ -22,9 +22,9 @@ jobs:
python-version: ["2.7", "3.6", "3.7", "3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v1
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies

View File

@ -3,11 +3,11 @@
<meta property="og:description" content="Fast, easy, and reliable Web/UI testing with Python." />
<meta property="og:keywords" content="Python, pytest, selenium, webdriver, testing, automation, seleniumbase, framework, RPA, behave, BDD, nosetests, dashboard, recorder, reports, gui, screenshots">
<meta property="og:image" content="https://seleniumbase.github.io/cdn/img/mac_sb_logo_5b.png" />
<link rel="icon" href="https://seleniumbase.github.io/img/green_logo2.png" />
<link rel="icon" href="https://seleniumbase.github.io/img/logo3b.png" />
<p align="center"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.github.io/cdn/img/sb_logo_gs.png" alt="SeleniumBase" title="SeleniumBase" width="450" /></a></p>
<p align="center"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.github.io/cdn/img/sb_media_logo_t4.png" alt="SeleniumBase" title="SeleniumBase" width="408" /></a></p>
<p align="center"><b>SeleniumBase</b> simplifies <a href="https://www.selenium.dev/documentation/webdriver/" target="_blank">WebDriver</a> automation with <a href="https://docs.pytest.org/en/latest/how-to/usage.html" target="_blank">pytest</a>.</p>
<p align="center"><b>SeleniumBase</b> simplifies <a href="https://www.selenium.dev/documentation/webdriver/" target="_blank">WebDriver</a> automation with <b>Python</b>.</p>
<p align="center"><a href="https://pypi.python.org/pypi/seleniumbase" target="_blank"><img src="https://img.shields.io/pypi/v/seleniumbase.svg?color=3399EE" alt="PyPI version" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/releases" target="_blank"><img src="https://img.shields.io/github/v/release/seleniumbase/SeleniumBase.svg?color=22AAEE" alt="GitHub version" /></a> <a href="https://seleniumbase.io"><img src="https://img.shields.io/badge/docs-seleniumbase.io-11BBAA.svg" alt="SeleniumBase Docs" /></a> <a href="https://github.com/seleniumbase/SeleniumBase/actions" target="_blank"><img src="https://github.com/seleniumbase/SeleniumBase/workflows/CI%20build/badge.svg" alt="SeleniumBase GitHub Actions" /></a> <a href="https://gitter.im/seleniumbase/SeleniumBase" target="_blank"><img src="https://badges.gitter.im/seleniumbase/SeleniumBase.svg" alt="SeleniumBase" /></a></p>
@ -123,7 +123,7 @@ pytest test_swag_labs.py --demo
<h4>Here are a few scripts to test that app with SeleniumBase:</h4>
<p align="left">📘📝 An example test with the <b>BaseCase</b> class. Runs with <code>pytest</code> or <code>nosetests</code>. (<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/ReadMe.md">Learn more</a>)</p>
<p align="left">📘📝 An example test with the <b>BaseCase</b> class. Runs with <b><a href="https://docs.pytest.org/en/latest/how-to/usage.html">pytest</a></b> or <b>nosetests</b>. (<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/ReadMe.md">Learn more</a>)</p>
```python
from seleniumbase import BaseCase
@ -140,7 +140,7 @@ class TestMFALogin(BaseCase):
self.save_screenshot_to_logs()
```
<p align="left">📗📝 An example test with the <b>sb</b> <code>pytest</code> fixture. Runs with <code>pytest</code>.</p>
<p align="left">📗📝 An example test with the <b><code>sb</code></b> <code>pytest</code> fixture. Runs with <b><a href="https://docs.pytest.org/en/latest/how-to/usage.html">pytest</a></b>.</p>
```python
def test_mfa_login(sb):
@ -154,7 +154,25 @@ def test_mfa_login(sb):
sb.save_screenshot_to_logs()
```
<p align="left">📕📝 An example test with <b>behave-BDD</b> <a href="https://behave.readthedocs.io/en/stable/gherkin.html" target="_blank">Gherkin</a> structure. Runs with <code>behave</code>. (<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd/ReadMe.md">Learn more</a>)</p>
<p align="left">📙📝 An example test with the <b><code>SB</code></b> Context Manager. Runs with pure <b><code>python</code></b>.</p>
```python
from seleniumbase import SB
with SB() as sb: # By default, browser="chrome" if not set.
sb.open("https://seleniumbase.github.io/realworld/login")
sb.type("#username", "demo_user")
sb.type("#password", "secret_pass")
sb.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit
sb.assert_text("Welcome!", "h1")
sb.highlight("img#image1") # A fancier assert_element() call
sb.click('a:contains("This Page")') # Use :contains() on any tag
sb.click_link("Sign out") # Link must be "a" tag. Not "button".
sb.assert_element('a:contains("Sign in")')
sb.assert_exact_text("You have been signed out!", "#top_message")
```
<p align="left">📕📝 An example test with <b>behave-BDD</b> <a href="https://behave.readthedocs.io/en/stable/gherkin.html" target="_blank">Gherkin</a> structure. Runs with <b><code>behave</code></b>. (<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd/ReadMe.md">Learn more</a>)</p>
```gherkin
Feature: SeleniumBase scenarios for the RealWorld App
@ -178,7 +196,7 @@ Feature: SeleniumBase scenarios for the RealWorld App
🔵 Using a <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/virtualenv_instructions.md">Python virtual env</a> is recommended.
<a id="install_seleniumbase"></a>
<h2><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Install SeleniumBase:</h2>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Install SeleniumBase:</h2>
**You can install ``seleniumbase`` from [GitHub](https://github.com/seleniumbase/SeleniumBase) or [PyPI](https://pypi.org/project/seleniumbase/):**
@ -261,7 +279,7 @@ COMMANDS:
```
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Downloading web drivers:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Downloading web drivers:</h3>
✅ SeleniumBase automatically downloads web drivers as needed, such as ``chromedriver`` and ``geckodriver`` (Firefox).
@ -269,7 +287,7 @@ COMMANDS:
<a id="basic_example_and_usage"></a>
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Basic Example & Usage:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Basic Example & Usage:</h3>
🔵 If you've cloned SeleniumBase, you can run tests from the [examples/](https://github.com/seleniumbase/SeleniumBase/tree/master/examples) folder.
@ -320,7 +338,7 @@ class MyTestClass(BaseCase):
<a id="common_methods"></a>
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Here are some common SeleniumBase methods that you might find in tests:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Here are some common SeleniumBase methods that you might find in tests:</h3>
```python
self.open(url) # Navigate the browser window to the URL.
@ -361,7 +379,7 @@ self.assert_no_js_errors() # Verify there are no JS errors.
<a id="fun_facts"></a>
<h2><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Fun Facts / Learn More:</h2>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Fun Facts / Learn More:</h2>
<p>✅ SeleniumBase automatically handles common WebDriver actions such as launching web browsers before tests, saving screenshots during failures, and closing web browsers after tests.</p>
@ -418,7 +436,7 @@ nosetests [FILE_NAME.py]:[CLASS_NAME].[METHOD_NAME]
<a id="detailed_instructions"></a>
<h2><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Detailed Instructions:</h2>
<h2><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Detailed Instructions:</h2>
<a id="seleniumbase_demo_mode"></a>
🔵 <b>Demo Mode</b> helps you see what a test is doing. If a test is moving too fast for your eyes, run it in <b>Demo Mode</b>, which pauses the browser briefly between actions, highlights page elements being acted on, and displays assertions:
@ -528,7 +546,8 @@ pytest my_first_test.py --pdb
--enable-ws # (Enable Web Security on Chromium-based browsers.)
--enable-sync # (Enable "Chrome Sync" on websites.)
--use-auto-ext # (Use Chrome's automation extension.)
--undetected | --uc # (Use undetected-chromedriver to evade bot-detection.)
--uc | --undetected # (Use undetected-chromedriver to evade bot-detection.)
--uc-sub | --uc-subprocess # (Use undetected-chromedriver as a subprocess.)
--remote-debug # (Enable Chrome's Remote Debugger on http://localhost:9222)
--final-debug # (Enter Debug Mode after each test ends. Don't use with CI!)
--dashboard # (Enable the SeleniumBase Dashboard. Saved at: dashboard.html)
@ -570,7 +589,7 @@ Here's the command-line option to add to tests: (See [examples/custom_settings.p
Inside your tests, you can use ``self.data`` to access that.
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Test Directory Configuration:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Test Directory Configuration:</h3>
🔵 When running tests with **pytest**, you'll want a copy of **[pytest.ini](https://github.com/seleniumbase/SeleniumBase/blob/master/pytest.ini)** in your root folders. When running tests with **nosetests**, you'll want a copy of **[setup.cfg](https://github.com/seleniumbase/SeleniumBase/blob/master/setup.cfg)** in your root folders. These files specify default configuration details for tests. Folders should also include a blank ``__init__.py`` file, which allows your tests to import files from that folder.
@ -626,7 +645,7 @@ Of those files, the ``pytest.ini`` config file is the most important, followed b
--------
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Log files from failed tests:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Log files from failed tests:</h3>
Let's try an example of a test that fails:
@ -651,7 +670,7 @@ pytest test_fail.py
--------
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> The SeleniumBase Dashboard:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> The SeleniumBase Dashboard:</h3>
🔵 The ``--dashboard`` option for pytest generates a SeleniumBase Dashboard located at ``dashboard.html``, which updates automatically as tests run and produce results. Example:
@ -680,7 +699,7 @@ pytest test_suite.py --dashboard --rs --headless
--------
<a id="creating_visual_reports"></a>
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Generating Test Reports:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Generating Test Reports:</h3>
<h4><b>Pytest Reports:</b></h4>
@ -755,7 +774,7 @@ pytest test_suite.py --alluredir=allure_results
```
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Using a Proxy Server:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Using a Proxy Server:</h3>
If you wish to use a proxy server for your browser tests (Chromium or Firefox), you can add ``--proxy=IP_ADDRESS:PORT`` as an argument on the command line.
@ -784,7 +803,7 @@ pytest proxy_test.py --proxy=proxy1
```
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Changing the User-Agent:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Changing the User-Agent:</h3>
🔵 If you wish to change the User-Agent for your browser tests (Chromium and Firefox only), you can add ``--agent="USER AGENT STRING"`` as an argument on the command-line.
@ -793,12 +812,12 @@ pytest user_agent_test.py --agent="Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1
```
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Handling Pop-Up / Pop Up Alerts:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Handling Pop-Up / Pop Up Alerts:</h3>
🔵 <code>self.accept_alert()</code> automatically waits for and accepts alert pop-ups. <code>self.dismiss_alert()</code> automatically waits for and dismisses alert pop-ups. On occasion, some methods like <code>self.click(SELECTOR)</code> might dismiss a pop-up on its own because they call JavaScript to make sure that the <code>readyState</code> of the page is <code>complete</code> before advancing. If you're trying to accept a pop-up that got dismissed this way, use this workaround: Call <code>self.find_element(SELECTOR).click()</code> instead, (which will let the pop-up remain on the screen), and then use <code>self.accept_alert()</code> to accept the pop-up (<a href="https://github.com/seleniumbase/SeleniumBase/issues/600#issuecomment-647270426">more on that here</a>). If pop-ups are intermittent, wrap code in a try/except block.
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Building Guided Tours for Websites:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Building Guided Tours for Websites:</h3>
🔵 Learn about <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/ReadMe.md">SeleniumBase Interactive Walkthroughs</a> (in the ``examples/tour_examples/`` folder). It's great for prototyping a website onboarding experience.
@ -808,7 +827,7 @@ pytest user_agent_test.py --agent="Mozilla/5.0 (Nintendo 3DS; U; ; en) Version/1
--------
<div></div>
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Production Environments & Integrations:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Production Environments & Integrations:</h3>
<div></div>
<details>
@ -834,7 +853,7 @@ pytest [YOUR_TEST_FILE.py] --with-db-reporting --with-s3-logging
<a id="detailed_method_specifications"></a>
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Detailed Method Specifications and Examples:</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Detailed Method Specifications and Examples:</h3>
🔵 Navigating to a web page: (and related commands)
@ -1128,7 +1147,7 @@ pytest --reruns=1 --reruns-delay=1
<p>You can use the <code>@retry_on_exception()</code> decorator to retry failing methods. (First import: <code>from seleniumbase import decorators</code>). To learn more about SeleniumBase decorators, <a href="https://github.com/seleniumbase/SeleniumBase/tree/master/seleniumbase/common">click here</a>.</p>
<h3><img src="https://seleniumbase.github.io/img/green_logo2.png" title="SeleniumBase" width="32" /> Wrap-Up</h3>
<h3><img src="https://seleniumbase.github.io/img/logo3b.png" title="SeleniumBase" width="32" /> Wrap-Up</h3>
<b>Congratulations on getting started with SeleniumBase!</b>

View File

@ -4,7 +4,6 @@
class HomePage(object):
dialog_box = '[role="dialog"] div'
search_box = 'input[title="Search"]'
list_box = '[role="listbox"]'
search_button = 'input[value="Google Search"]'
feeling_lucky_button = """input[value="I'm Feeling Lucky"]"""

View File

@ -8,7 +8,6 @@ class GoogleTests(BaseCase):
def test_google_dot_com(self):
self.open("https://google.com/ncr")
self.type(HomePage.search_box, "github")
self.assert_element(HomePage.list_box)
self.assert_element(HomePage.search_button)
self.assert_element(HomePage.feeling_lucky_button)
self.click(HomePage.search_button)

View File

@ -0,0 +1,22 @@
"""Context Manager Tests"""
from seleniumbase import SB
with SB(test=True) as sb:
sb.open("https://google.com/ncr")
sb.type('[name="q"]', "SeleniumBase on GitHub\n")
sb.click('a[href*="github.com/seleniumbase"]')
sb.highlight("div.Layout-main")
sb.highlight("div.Layout-sidebar")
sb.sleep(0.5)
with SB(test=True, rtf=True, demo=True) as sb:
sb.open("seleniumbase.github.io/demo_page")
sb.type("#myTextInput", "This is Automated")
sb.assert_text("This is Automated", "#myTextInput")
sb.assert_text("This Text is Green", "#pText")
sb.click('button:contains("Click Me")')
sb.assert_text("This Text is Purple", "#pText")
sb.click("#checkBox1")
sb.assert_element_not_visible("div#drop2 img#logo")
sb.drag_and_drop("img#logo", "div#drop2")
sb.assert_element("div#drop2 img#logo")

View File

@ -10,8 +10,10 @@ class GitHubTests(BaseCase):
self.open("https://github.com/search?q=SeleniumBase")
self.slow_click('a[href="/seleniumbase/SeleniumBase"]')
self.click_if_visible('[data-action="click:signup-prompt#dismiss"]')
self.highlight("div.Layout-main")
self.highlight("div.Layout-sidebar")
self.assert_element("div.repository-content")
self.assert_text("SeleniumBase", "strong a")
self.slow_click('a[title="seleniumbase"]')
self.click('a[title="seleniumbase"]')
self.slow_click('a[title="fixtures"]')
self.assert_element('a[title="base_case.py"]')

22
examples/raw_driver.py Normal file
View File

@ -0,0 +1,22 @@
from seleniumbase import js_utils
from seleniumbase import page_actions
from seleniumbase import Driver
with Driver() as driver:
driver.get("https://google.com/ncr")
js_utils.highlight_with_js(driver, 'img[alt="Google"]', 6, "")
with Driver() as driver: # By default, browser="chrome"
driver.get("https://seleniumbase.github.io/demo_page")
js_utils.highlight_with_js(driver, "h2", 5, "")
CSS = "css selector"
driver.find_element(CSS, "#myTextInput").send_keys("Automation")
driver.find_element(CSS, "#checkBox1").click()
js_utils.highlight_with_js(driver, "img", 5, "")
with Driver(browser="chrome", incognito=True) as driver:
driver.get("https://seleniumbase.io/apps/calculator")
page_actions.wait_for_element_visible(driver, "4", "id").click()
page_actions.wait_for_element_visible(driver, "2", "id").click()
page_actions.wait_for_text_visible(driver, "42", "output", "id")
js_utils.highlight_with_js(driver, "#output", 6, "")

View File

@ -66,6 +66,7 @@ if pure_python:
sb.enable_sync = False
sb.use_auto_ext = False
sb.undetectable = False
sb.uc_subprocess = False
sb.no_sandbox = False
sb.disable_js = False
sb.disable_gpu = False

14
examples/raw_sb.py Normal file
View File

@ -0,0 +1,14 @@
"""Context Manager Tests"""
from seleniumbase import SB
with SB() as sb: # By default, browser="chrome" if not set.
sb.open("https://seleniumbase.github.io/realworld/login")
sb.type("#username", "demo_user")
sb.type("#password", "secret_pass")
sb.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit
sb.assert_text("Welcome!", "h1")
sb.highlight("img#image1") # A fancier assert_element() call
sb.click('a:contains("This Page")') # Use :contains() on any tag
sb.click_link("Sign out") # Link must be "a" tag. Not "button".
sb.assert_element('a:contains("Sign in")')
sb.assert_exact_text("You have been signed out!", "#top_message")

View File

@ -9,8 +9,8 @@ class TestMFALogin(BaseCase):
self.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit
self.assert_text("Welcome!", "h1")
self.highlight("img#image1") # A fancier assert_element() call
self.click('a:contains("This Page")')
self.click('a:contains("This Page")') # Use :contains() on any tag
self.save_screenshot_to_logs() # In "./latest_logs/" folder.
self.click_link("Sign out") # Must be "a" tag. Not "button".
self.click_link("Sign out") # Link must be "a" tag. Not "button".
self.assert_element('a:contains("Sign in")')
self.assert_exact_text("You have been signed out!", "#top_message")

View File

@ -11,8 +11,8 @@ class Test_UseFixtures:
sb.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit
sb.assert_text("Welcome!", "h1")
sb.highlight("img#image1") # A fancier assert_element() call
sb.click('a:contains("This Page")')
sb.click('a:contains("This Page")') # Use :contains() on any tag
sb.save_screenshot_to_logs() # In "./latest_logs/" folder.
sb.click_link("Sign out") # Must be "a" tag. Not "button".
sb.click_link("Sign out") # Link must be "a" tag. Not "button".
sb.assert_element('a:contains("Sign in")')
sb.assert_exact_text("You have been signed out!", "#top_message")

View File

@ -1,4 +1,4 @@
<p align="center"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.io/cdn/img/sb_logo_f6.png" alt="SeleniumBase" width="330" /></a></p>
<p align="center"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.io/cdn/img/sb_logo_f6.png" alt="SeleniumBase" width="445" /></a></p>
## [<img src="https://seleniumbase.io/img/logo6.png" title="SeleniumBase" width="32">](https://github.com/seleniumbase/SeleniumBase/) Automated Visual Regression Testing

View File

@ -61,9 +61,8 @@ class WordleTests(BaseCase):
def test_wordle(self):
self.skip_if_incorrect_env()
self.open("https://www.nytimes.com/games/wordle/index.html")
self.remove_elements("div.ad")
self.click('svg[data-testid="icon-close"]')
self.remove_elements("div.ad")
self.click_if_visible('svg[data-testid="icon-close"]', timeout=2)
self.remove_elements("div.place-ad")
self.initialize_word_list()
word = random.choice(self.word_list)
num_attempts = 0

View File

@ -162,7 +162,8 @@ pytest my_first_test.py --settings-file=custom_settings.py
--enable-ws # (Enable Web Security on Chromium-based browsers.)
--enable-sync # (Enable "Chrome Sync" on websites.)
--use-auto-ext # (Use Chrome's automation extension.)
--undetected | --uc # (Use undetected-chromedriver to evade bot-detection.)
--uc | --undetected # (Use undetected-chromedriver to evade bot-detection.)
--uc-sub | --uc-subprocess # (Use undetected-chromedriver as a subprocess.)
--remote-debug # (Enable Chrome's Remote Debugger on http://localhost:9222)
--final-debug # (Enter Debug Mode after each test ends. Don't use with CI!)
--dashboard # (Enable the SeleniumBase Dashboard. Saved at: dashboard.html)

View File

@ -31,9 +31,9 @@ class TestMFALogin(BaseCase):
self.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit
self.assert_text("Welcome!", "h1")
self.highlight("img#image1") # A fancier assert_element() call
self.click('a:contains("This Page")')
self.click('a:contains("This Page")') # Use :contains() on any tag
self.save_screenshot_to_logs() # In "./latest_logs/" folder.
self.click_link("Sign out") # Must be "a" tag. Not "button".
self.click_link("Sign out") # Link must be "a" tag. Not "button".
self.assert_element('a:contains("Sign in")')
self.assert_exact_text("You have been signed out!", "#top_message")
```

View File

@ -126,7 +126,7 @@ self.click_visible_elements(selector, by="css selector", limit=0, timeout=None)
self.click_nth_visible_element(selector, number, by="css selector", timeout=None)
self.click_if_visible(selector, by="css selector")
self.click_if_visible(selector, by="css selector", timeout=0)
self.click_active_element()
@ -356,9 +356,9 @@ self.click_xpath(xpath)
self.js_click(selector, by="css selector", all_matches=False, scroll=True)
self.js_click_if_present(selector, by="css selector")
self.js_click_if_present(selector, by="css selector", timeout=0)
self.js_click_if_visible(selector, by="css selector")
self.js_click_if_visible(selector, by="css selector", timeout=0)
self.js_click_all(selector, by="css selector")

View File

@ -1,9 +1,42 @@
<a id="syntax_formats"></a>
## [<img src="https://seleniumbase.io/img/logo6.png" title="SeleniumBase" width="32">](https://github.com/seleniumbase/SeleniumBase/) The 20 Syntax Formats
## [<img src="https://seleniumbase.io/img/logo6.png" title="SeleniumBase" width="32">](https://github.com/seleniumbase/SeleniumBase/) The 22 Syntax Formats
<b>SeleniumBase</b> supports 20 different syntax formats (<i>design patterns</i>) for structuring tests. (<i>The first 6 are the most common.</i>)
<b>SeleniumBase</b> supports 22 different syntax formats (<i>design patterns</i>) for structuring tests.
--------
<blockquote>
<p dir="auto"><strong>Table of Contents / Navigation:</strong></p>
<ul dir="auto">
<li><a href="#sb_sf_01"><strong>01. BaseCase direct inheritance</strong></a></li>
<li><a href="#sb_sf_02"><strong>02. BaseCase subclass inheritance</strong></a></li>
<li><a href="#sb_sf_03"><strong>03. The "sb" pytest fixture (no class)</strong></a></li>
<li><a href="#sb_sf_04"><strong>04. The "sb" pytest fixture (in class)</strong></a></li>
<li><a href="#sb_sf_05"><strong>05. Page Object Model with BaseCase</strong></a></li>
<li><a href="#sb_sf_06"><strong>06. Page Object Model with "sb" fixture</strong></a></li>
<li><a href="#sb_sf_07"><strong>07. Using "request" to get "sb" (no class)</strong></a></li>
<li><a href="#sb_sf_08"><strong>08. Using "request" to get "sb" (in class)</strong></a></li>
<li><a href="#sb_sf_09"><strong>09. BaseCase while overriding driver setup</strong></a></li>
<li><a href="#sb_sf_10"><strong>10. The driver manager without BaseCase</strong></a></li>
<li><a href="#sb_sf_11"><strong>11. SeleniumBase translated into Chinese</strong></a></li>
<li><a href="#sb_sf_12"><strong>12. SeleniumBase translated into Dutch</strong></a></li>
<li><a href="#sb_sf_13"><strong>13. SeleniumBase translated into French</strong></a></li>
<li><a href="#sb_sf_14"><strong>14. SeleniumBase translated into Italian</strong></a></li>
<li><a href="#sb_sf_15"><strong>15. SeleniumBase translated into Japanese</strong></a></li>
<li><a href="#sb_sf_16"><strong>16. SeleniumBase translated into Korean</strong></a></li>
<li><a href="#sb_sf_17"><strong>17. SeleniumBase translated into Portuguese</strong></a></li>
<li><a href="#sb_sf_18"><strong>18. SeleniumBase translated into Russian</strong></a></li>
<li><a href="#sb_sf_19"><strong>19. SeleniumBase translated into Spanish</strong></a></li>
<li><a href="#sb_sf_20"><strong>20. SeleniumBase "behave" Gherkin format</strong></a></li>
<li><a href="#sb_sf_21"><strong>21. SeleniumBase as a Python context manager</strong></a></li>
<li><a href="#sb_sf_22"><strong>22. The driver manager as a context manager</strong></a></li>
</ul>
</blockquote>
--------
<a id="sb_sf_01"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 1. <code>BaseCase</code> direct inheritance</h3>
This format is used by most of the examples in the <a href="https://github.com/seleniumbase/SeleniumBase/tree/master/examples">SeleniumBase examples folder</a>. It's a great starting point for anyone learning SeleniumBase, and it follows good object-oriented programming principles. In this format, <code>BaseCase</code> is imported at the top of a Python file, followed by a Python class inheriting <code>BaseCase</code>. Then, any test method defined in that class automatically gains access to SeleniumBase methods, including the <code>setUp()</code> and <code>tearDown()</code> methods that are automatically called to spin up and spin down web browsers at the beginning and end of test methods. Here's an example of that:
@ -25,6 +58,7 @@ class MyTestClass(BaseCase):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_demo_site.py">examples/test_demo_site.py</a> for the full test.)
<a id="sb_sf_02"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 2. <code>BaseCase</code> subclass inheritance</h3>
There are situations where you may want to customize the <code>setUp</code> and <code>tearDown</code> of your tests. Maybe you want to have all your tests login to a specific web site first, or maybe you want to have your tests report results through an API call depending on whether a test passed or failed. <b>This can be done by creating a subclass of <code>BaseCase</code> and then carefully creating custom <code>setUp()</code> and <code>tearDown()</code> methods that don't overwrite the critical functionality of the default SeleniumBase <code>setUp()</code> and <code>tearDown()</code> methods.</b> Afterwards, your test classes will inherit the subclass of <code>BaseCase</code> with the added functionality, rather than directly inheriting <code>BaseCase</code> itself. Here's an example of that:
@ -70,6 +104,7 @@ class MyTests(BaseTestCase):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/boilerplates/base_test_case.py">examples/boilerplates/base_test_case.py</a> for more info.)
<a id="sb_sf_03"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 3. The <code>sb</code> pytest fixture (no class)</h3>
The pytest framework comes with a unique system called fixtures, which replaces import statements at the top of Python files by importing libraries directly into test definitions. More than just being an import, a pytest fixture can also automatically call predefined <code>setUp()</code> and <code>tearDown()</code> methods at the beginning and end of test methods. To work, <code>sb</code> is added as an argument to each test method definition that needs SeleniumBase functionality. This means you no longer need import statements in your Python files to use SeleniumBase. <b>If using other pytest fixtures in your tests, you may need to use the SeleniumBase fixture (instead of <code>BaseCase</code> class inheritance) for compatibility reasons.</b> Here's an example of the <code>sb</code> fixture in a test that does not use Python classes:
@ -84,6 +119,7 @@ def test_sb_fixture_with_no_class(sb):
(See the top of <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_sb_fixture.py">examples/test_sb_fixture.py</a> for the test.)
<a id="sb_sf_04"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 4. The <code>sb</code> pytest fixture (in class)</h3>
The <code>sb</code> pytest fixture can also be used inside of a class. There is a slight change to the syntax because that means test methods must also include <code>self</code> in their argument definitions when test methods are defined. (The <code>self</code> argument represents the class object, and is used in every test method that lives inside of a class.) Once again, no import statements are needed in your Python files for this to work. Here's an example of using the <code>sb</code> fixture in a test method that lives inside of a Python class:
@ -99,6 +135,7 @@ class Test_SB_Fixture:
(See the bottom of <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_sb_fixture.py">examples/test_sb_fixture.py</a> for the test.)
<a id="sb_sf_05"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 5. The classic Page Object Model with <code>BaseCase</code> inheritance</h3>
With SeleniumBase, you can use Page Objects to break out code from tests, but remember, the <code>self</code> variable (from test methods that inherit <code>BaseCase</code>) contains the driver and all other framework-specific variable definitions. Therefore, that <code>self</code> must be passed as an arg into any outside class method in order to call SeleniumBase methods from there. In the example below, the <code>self</code> variable from the test method is passed into the <code>sb</code> arg of the Page Object class method because the <code>self</code> arg of the Page Object class method is already being used for its own class. Every Python class method definition must include the <code>self</code> as the first arg.
@ -122,6 +159,7 @@ class MyTests(BaseCase):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/boilerplates/samples/swag_labs_test.py">examples/boilerplates/samples/swag_labs_test.py</a> for the full test.)
<a id="sb_sf_06"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 6. The classic Page Object Model with the <code>sb</code> pytest fixture</h3>
This is similar to the classic Page Object Model with <code>BaseCase</code> inheritance, except that this time we pass the <code>sb</code> pytest fixture from the test into the <code>sb</code> arg of the page object class method, (instead of passing <code>self</code>). Now that you're using <code>sb</code> as a pytest fixture, you no longer need to import <code>BaseCase</code> anywhere in your code. See the example below:
@ -143,6 +181,7 @@ class MyTests:
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/boilerplates/samples/sb_swag_test.py">examples/boilerplates/samples/sb_swag_test.py</a> for the full test.)
<a id="sb_sf_07"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 7. Using the <code>request</code> fixture to get the <code>sb</code> fixture (no class)</h3>
The pytest <code>request</code> fixture can be used to retrieve other pytest fixtures from within tests, such as the <code>sb</code> fixture. This allows you to have more control over when fixtures get initialized because the fixture no longer needs to be loaded at the very beginning of test methods. This is done by calling <code>request.getfixturevalue('sb')</code> from the test. Here's an example of using the pytest <code>request</code> fixture to load the <code>sb</code> fixture in a test method that does not use Python classes:
@ -160,6 +199,7 @@ def test_request_sb_fixture(request):
(See the top of <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_request_sb_fixture.py">examples/test_request_sb_fixture.py</a> for the test.)
<a id="sb_sf_08"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 8. Using the <code>request</code> fixture to get the <code>sb</code> fixture (in class)</h3>
The pytest <code>request</code> fixture can also be used to get the <code>sb</code> fixture from inside a Python class. Here's an example of that:
@ -179,6 +219,7 @@ class Test_Request_Fixture:
(See the bottom of <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/test_request_sb_fixture.py">examples/test_request_sb_fixture.py</a> for the test.)
<a id="sb_sf_09"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 9. Overriding the SeleniumBase browser launcher </h3>
When you want to use SeleniumBase methods, but you want total freedom to control how you spin up your web browsers, this is the format you want. Although SeleniumBase gives you plenty of command-line options to change how your browsers are launched, this format gives you even more control. Here's an example of that:
@ -233,6 +274,7 @@ class WireTestCase(BaseCase):
print(request.url)
```
<a id="sb_sf_10"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 10. Using the SeleniumBase browser launcher without BaseCase </h3>
One way of running Selenium tests with pure ``python`` (as opposed to using ``pytest`` or ``nosetests``) is by using this format, which bypasses [BaseCase](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py) methods while still giving you ``browser_launcher`` with its powerful webdriver management software. SeleniumBase includes helper files such as [page_actions.py](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/page_actions.py), which may help you get around some of the limitations of bypassing ``BaseCase``. Here's an example:
@ -257,6 +299,7 @@ finally:
The above format can be used as a drop-in replacement for virtually every Python/selenium framework, as it uses the raw ``driver`` for handling commands. The ``get_driver()`` method simplifies the work of managing drivers and spinning them up with optimal settings. Note that now you'll need to manage the spin-up and spin-down of browsers in tests, which was done automatically in tests that inherit ``BaseCase`` (or ones that use the ``sb`` pytest fixture). You'll also need to use extra code (as shown above) to make sure you don't leave any browsers hanging after your tests complete.
<a id="sb_sf_11"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 11. SeleniumBase in Chinese</h3>
This format is similar to the English version with <code>BaseCase</code> inheritance, but there's a different import statement, and method names have been translated into Chinese. Here's an example of that:
@ -286,6 +329,7 @@ class 我的测试类(硒测试用例):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/chinese_test_1.py">examples/translations/chinese_test_1.py</a> for the Chinese test.)
<a id="sb_sf_12"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 12. SeleniumBase in Dutch</h3>
This format is similar to the English version with <code>BaseCase</code> inheritance, but there's a different import statement, and method names have been translated into Dutch. Here's an example of that:
@ -314,6 +358,7 @@ class MijnTestklasse(Testgeval):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/dutch_test_1.py">examples/translations/dutch_test_1.py</a> for the Dutch test.)
<a id="sb_sf_13"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 13. SeleniumBase in French</h3>
This format is similar to the English version with <code>BaseCase</code> inheritance, but there's a different import statement, and method names have been translated into French. Here's an example of that:
@ -342,6 +387,7 @@ class MaClasseDeTest(CasDeBase):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/french_test_1.py">examples/translations/french_test_1.py</a> for the French test.)
<a id="sb_sf_14"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 14. SeleniumBase in Italian</h3>
This format is similar to the English version with <code>BaseCase</code> inheritance, but there's a different import statement, and method names have been translated into Italian. Here's an example of that:
@ -370,6 +416,7 @@ class MiaClasseDiTest(CasoDiProva):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/italian_test_1.py">examples/translations/italian_test_1.py</a> for the Italian test.)
<a id="sb_sf_15"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 15. SeleniumBase in Japanese</h3>
This format is similar to the English version with <code>BaseCase</code> inheritance, but there's a different import statement, and method names have been translated into Japanese. Here's an example of that:
@ -399,6 +446,7 @@ class 私のテストクラス(セレニウムテストケース):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/japanese_test_1.py">examples/translations/japanese_test_1.py</a> for the Japanese test.)
<a id="sb_sf_16"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 16. SeleniumBase in Korean</h3>
This format is similar to the English version with <code>BaseCase</code> inheritance, but there's a different import statement, and method names have been translated into Korean. Here's an example of that:
@ -426,6 +474,7 @@ class 테스트_클래스(셀레늄_테스트_케이스):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/korean_test_1.py">examples/translations/korean_test_1.py</a> for the Korean test.)
<a id="sb_sf_17"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 17. SeleniumBase in Portuguese</h3>
This format is similar to the English version with <code>BaseCase</code> inheritance, but there's a different import statement, and method names have been translated into Portuguese. Here's an example of that:
@ -457,6 +506,7 @@ class MinhaClasseDeTeste(CasoDeTeste):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/portuguese_test_1.py">examples/translations/portuguese_test_1.py</a> for the Portuguese test.)
<a id="sb_sf_18"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 18. SeleniumBase in Russian</h3>
This format is similar to the English version with <code>BaseCase</code> inheritance, but there's a different import statement, and method names have been translated into Russian. Here's an example of that:
@ -485,6 +535,7 @@ class МойТестовыйКласс(ТестНаСелен):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/russian_test_1.py">examples/translations/russian_test_1.py</a> for the Russian test.)
<a id="sb_sf_19"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 19. SeleniumBase in Spanish</h3>
This format is similar to the English version with <code>BaseCase</code> inheritance, but there's a different import statement, and method names have been translated into Spanish. Here's an example of that:
@ -513,6 +564,7 @@ class MiClaseDePrueba(CasoDePrueba):
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/spanish_test_1.py">examples/translations/spanish_test_1.py</a> for the Spanish test.)
<a id="sb_sf_20"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 20. Behave-BDD Gherkin tests that use SeleniumBase </h3>
With [Behave's BDD Gherkin format](https://behave.readthedocs.io/en/stable/gherkin.html), you can use natural language to write tests that work with SeleniumBase methods. Behave tests are run by calling ``behave`` on the command-line. This requires some special files in a specific directory structure. Here's an example of that structure:
@ -614,6 +666,89 @@ def login_to_swag_labs(context, user):
(For more information, see the <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/behave_bdd/ReadMe.md">SeleniumBase Behave BDD ReadMe</a>.)
<a id="sb_sf_21"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 21. Using the <code>SB</code> context manager to get an instance of <code>sb</code> in a <code>with</code> block.</h3>
This format provides a pure Python way of using SeleniumBase without a test runner. Options can be passed via method instantiation or from the command-line. When setting the <code>test</code> option to <code>True</code> (or calling <code>python --test</code>), then standard test logging will occur, such as screenshots and reports for failing tests. All the usual SeleniumBase options are available, such as customizing the browser settings, etc. Here are some examples:
```python
from seleniumbase import SB
with SB() as sb: # By default, browser="chrome" if not set.
sb.open("https://seleniumbase.github.io/realworld/login")
sb.type("#username", "demo_user")
sb.type("#password", "secret_pass")
sb.enter_mfa_code("#totpcode", "GAXG2MTEOR3DMMDG") # 6-digit
sb.assert_text("Welcome!", "h1")
sb.highlight("img#image1") # A fancier assert_element() call
sb.click('a:contains("This Page")') # Use :contains() on any tag
sb.click_link("Sign out") # Link must be "a" tag. Not "button".
sb.assert_element('a:contains("Sign in")')
sb.assert_exact_text("You have been signed out!", "#top_message")
```
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_sb.py">examples/raw_sb.py</a> for the test.)
Here's another example, which uses <code>test</code> mode:
```python
from seleniumbase import SB
with SB(test=True) as sb:
sb.open("https://google.com/ncr")
sb.type('[name="q"]', "SeleniumBase on GitHub\n")
sb.click('a[href*="github.com/seleniumbase"]')
sb.highlight("div.Layout-main")
sb.highlight("div.Layout-sidebar")
sb.sleep(0.5)
with SB(test=True, rtf=True, demo=True) as sb:
sb.open("seleniumbase.github.io/demo_page")
sb.type("#myTextInput", "This is Automated")
sb.assert_text("This is Automated", "#myTextInput")
sb.assert_text("This Text is Green", "#pText")
sb.click('button:contains("Click Me")')
sb.assert_text("This Text is Purple", "#pText")
sb.click("#checkBox1")
sb.assert_element_not_visible("div#drop2 img#logo")
sb.drag_and_drop("img#logo", "div#drop2")
sb.assert_element("div#drop2 img#logo")
```
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/context_scripts.py">examples/context_scripts.py</a> for the test.)
<a id="sb_sf_22"></a>
<h3><img src="https://seleniumbase.io/img/green_logo.png" title="SeleniumBase" width="32" /> 22. Using the <code>GetDriver</code> context manager to get an instance of <code>driver</code> in a <code>with</code> block.</h3>
This pure Python format gives you a raw <code>webdriver</code> instance in a <code>with</code> block. The SeleniumBase Driver Manager will automatically make sure that your driver is compatible with your browser version. It gives you full access to customize driver options via method args or via the command-line. The driver will automatically call <code>quit()</code> after the code leaves the <code>with</code> block. Here are some examples:
```python
from seleniumbase import js_utils
from seleniumbase import page_actions
from seleniumbase import Driver
with Driver() as driver:
driver.get("https://google.com/ncr")
js_utils.highlight_with_js(driver, 'img[alt="Google"]', 6, "")
with Driver() as driver: # By default, browser="chrome"
driver.get("https://seleniumbase.github.io/demo_page")
js_utils.highlight_with_js(driver, "h2", 5, "")
CSS = "css selector"
driver.find_element(CSS, "#myTextInput").send_keys("Automation")
driver.find_element(CSS, "#checkBox1").click()
js_utils.highlight_with_js(driver, "img", 5, "")
with Driver(browser="chrome", incognito=True) as driver:
driver.get("https://seleniumbase.io/apps/calculator")
page_actions.wait_for_element_visible(driver, "4", "id").click()
page_actions.wait_for_element_visible(driver, "2", "id").click()
page_actions.wait_for_text_visible(driver, "42", "output", "id")
js_utils.highlight_with_js(driver, "#output", 6, "")
```
(See <a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/raw_driver.py">examples/raw_driver.py</a> for the test.)
--------
<h3 align="left"><a href="https://github.com/seleniumbase/SeleniumBase/"><img src="https://seleniumbase.io/img/sb_logo_10.png" title="SeleniumBase" width="280" /></a></h3>

View File

@ -7,8 +7,11 @@ from seleniumbase.common import encryption # noqa
from seleniumbase.core.browser_launcher import get_driver # noqa
from seleniumbase.fixtures import js_utils # noqa
from seleniumbase.fixtures import page_actions # noqa
from seleniumbase.fixtures import page_utils # noqa
from seleniumbase.fixtures.base_case import BaseCase # noqa
from seleniumbase.masterqa.master_qa import MasterQA # noqa
from seleniumbase.plugins.sb_manager import SB # noqa
from seleniumbase.plugins.driver_manager import Driver # noqa
if sys.version_info[0] >= 3:
from seleniumbase import translate # noqa

View File

@ -1,2 +1,2 @@
# seleniumbase package
__version__ = "4.5.6"
__version__ = "4.6.0"

View File

@ -72,7 +72,8 @@ behave -D agent="User Agent String" -D demo
-D enable-ws (Enable Web Security on Chromium-based browsers.)
-D enable-sync (Enable "Chrome Sync".)
-D use-auto-ext (Use Chrome's automation extension.)
-D undetected | -D uc (Use undetected-chromedriver to evade bot-detection)
-D uc | -D undetected (Use undetected-chromedriver to evade bot-detection)
-D uc-sub | -D uc-subprocess (Use undetected-chromedriver as a subprocess)
-D remote-debug (Enable Chrome's Remote Debugger on http://localhost:9222)
-D dashboard (Enable the SeleniumBase Dashboard. Saved at: dashboard.html)
-D dash-title=STRING (Set the title shown for the generated dashboard.)
@ -169,6 +170,7 @@ def get_configured_sb(context):
sb.enable_sync = False
sb.use_auto_ext = False
sb.undetectable = False
sb.uc_subprocess = False
sb.no_sandbox = False
sb.disable_gpu = False
sb._multithreaded = False
@ -178,6 +180,7 @@ def get_configured_sb(context):
sb.visual_baseline = False
sb.window_size = None
sb.maximize_option = False
sb.is_context_manager = False
sb.save_screenshot_after_test = False
sb.timeout_multiplier = None
sb.pytest_html_report = None
@ -213,6 +216,7 @@ def get_configured_sb(context):
sb.proxy_pac_url = None
sb.swiftshader = False
sb.ad_block_on = False
sb.is_nosetest = False
sb.highlights = None
sb.interval = None
sb.cap_file = None
@ -497,10 +501,15 @@ def get_configured_sb(context):
if low_key in ["use-auto-ext", "use_auto_ext", "auto-ext"]:
sb.use_auto_ext = True
continue
# Handle: -D use-auto-ext / use_auto_ext / auto-ext
# Handle: -D undetected / undetectable / uc
if low_key in ["undetected", "undetectable", "uc"]:
sb.undetectable = True
continue
# Handle: -D uc-subprocess / uc_subprocess / uc-sub
if low_key in ["uc-subprocess", "uc_subprocess", "uc-sub"]:
sb.uc_subprocess = True
sb.undetectable = True
continue
# Handle: -D no-sandbox / no_sandbox
if low_key in ["no-sandbox", "no_sandbox"]:
sb.no_sandbox = True
@ -748,7 +757,10 @@ def get_configured_sb(context):
if sb.recorder_ext and sb.headless:
sb.headless = False
sb.headless2 = True
if sb.browser not in ["chrome", "edge"]:
if sb.headless2 and sb.browser == "firefox":
sb.headless2 = False # Only for Chromium browsers
sb.headless = True # Firefox has regular headless
elif sb.browser not in ["chrome", "edge"]:
sb.headless2 = False # Only for Chromium browsers
# Recorder Mode only supports Chromium browsers.
if sb.recorder_ext and (sb.browser not in ["chrome", "edge"]):
@ -756,10 +768,6 @@ def get_configured_sb(context):
"\n\n Recorder Mode ONLY supports Chrome and Edge!"
'\n (Your browser choice was: "%s")\n' % sb.browser
)
# Recorder Mode can still optimize scripts in --headless2 mode.
if sb.recorder_mode and sb.headless:
sb.headless = False
sb.headless2 = True
# The Xvfb virtual display server is for Linux OS Only.
if sb.xvfb and "linux" not in sys.platform:
sb.xvfb = False
@ -778,6 +786,10 @@ def get_configured_sb(context):
'or by calling the new "-D headless2".)'
)
sb.headless = True
# Recorder Mode can still optimize scripts in --headless2 mode.
if sb.recorder_mode and sb.headless:
sb.headless = False
sb.headless2 = True
if not sb.headless and not sb.headless2:
sb.headed = True
if sb.servername != "localhost":
@ -816,6 +828,10 @@ def get_configured_sb(context):
sb_config.headless = sb.headless
sb_config.headless_active = False
sb_config.headed = sb.headed
sb_config.is_behave = True
sb_config.is_pytest = False
sb_config.is_nosetest = False
sb_config.is_context_manager = False
sb_config.window_size = sb.window_size
sb_config.maximize_option = sb.maximize_option
sb_config.xvfb = sb.xvfb

View File

@ -60,13 +60,13 @@ sbase install [DRIVER] [OPTIONS]
sbase get chromedriver # (Default: 72.0.3626.69 - Tries to detect first.)
sbase get geckodriver
sbase get edgedriver
sbase get chromedriver 105
sbase get chromedriver 105.0.5195.52
sbase get chromedriver 106
sbase get chromedriver 106.0.5249.61
sbase get chromedriver latest
sbase get chromedriver latest-1 # (Latest minus one)
sbase get chromedriver -p
sbase get chromedriver latest -p
sbase get edgedriver 105.0.1343.53
sbase get edgedriver 106.0.1370.42
```
(Drivers: ``chromedriver``, ``geckodriver``, ``edgedriver``,

View File

@ -144,13 +144,13 @@ def show_install_usage():
print(" sbase get chromedriver")
print(" sbase get geckodriver")
print(" sbase get edgedriver")
print(" sbase get chromedriver 105")
print(" sbase get chromedriver 105.0.5195.52")
print(" sbase get chromedriver 106")
print(" sbase get chromedriver 106.0.5249.61")
print(" sbase get chromedriver latest")
print(" sbase get chromedriver latest-1")
print(" sbase get chromedriver -p")
print(" sbase get chromedriver latest -p")
print(" sbase get edgedriver 105.0.1343.53")
print(" sbase get edgedriver 106.0.1370.42")
print(" Output:")
print(" Downloads the chosen webdriver to seleniumbase/drivers")
print(" (chromedriver is required for Chrome automation)")

View File

@ -15,13 +15,13 @@ Examples:
sbase get chromedriver
sbase get geckodriver
sbase get edgedriver
sbase get chromedriver 105.0.5195.52
sbase get chromedriver 105
sbase get chromedriver 106.0.5249.61
sbase get chromedriver 106
sbase get chromedriver latest
sbase get chromedriver latest-1 # (Latest minus one)
sbase get chromedriver -p
sbase get chromedriver latest -p
sbase get edgedriver 105.0.1343.53
sbase get edgedriver 106.0.1370.42
Output:
Downloads the chosen webdriver to seleniumbase/drivers
(chromedriver is required for Chrome automation)
@ -49,9 +49,9 @@ if sys.version_info[0] == 3 and sys.version_info[1] >= 7:
selenium4_or_newer = True
DRIVER_DIR = os.path.dirname(os.path.realpath(drivers.__file__))
LOCAL_PATH = "/usr/local/bin/" # On Mac and Linux systems
DEFAULT_CHROMEDRIVER_VERSION = "72.0.3626.69" # (Specify "latest" for latest)
DEFAULT_GECKODRIVER_VERSION = "v0.31.0"
DEFAULT_EDGEDRIVER_VERSION = "102.0.1245.44" # (Looks for LATEST_STABLE first)
DEFAULT_CHROMEDRIVER_VERSION = "72.0.3626.69" # (If can't find LATEST_STABLE)
DEFAULT_GECKODRIVER_VERSION = "v0.32.0"
DEFAULT_EDGEDRIVER_VERSION = "106.0.1370.42" # (If can't find LATEST_STABLE)
DEFAULT_OPERADRIVER_VERSION = "v.96.0.4664.45"
@ -545,7 +545,7 @@ def main(override=None):
remote_file = requests_get_with_retry(headless_ie_url)
with open(headless_ie_file_path, "wb") as file:
file.write(remote_file.content)
print("Download Complete!\n")
print("%sDownload Complete!%s\n" % (c1, cr))
zip_file_path = headless_ie_file_path
zip_ref = zipfile.ZipFile(zip_file_path, "r")
contents = zip_ref.namelist()
@ -589,7 +589,7 @@ def main(override=None):
zip_ref.close()
os.remove(zip_file_path)
shutil.copyfile(driver_path, os.path.join(downloads_folder, filename))
print("Unzip Complete!\n")
print("%sUnzip Complete!%s\n" % (c2, cr))
to_remove = [
"%s/%s/ruby_example/Gemfile" % (downloads_folder, h_ie_fn),
"%s/%s/ruby_example/Gemfile.lock" % (downloads_folder, h_ie_fn),
@ -614,13 +614,16 @@ def main(override=None):
)
print("Making [%s %s] executable ..." % (driver_file, use_version))
make_executable(driver_path)
print("%s[%s] is now ready for use!%s" % (c1, driver_file, cr))
print(
"%s[%s %s] is now ready for use!%s"
% (c1, driver_file, use_version, cr)
)
print("\nDownloading %s from:\n%s ..." % (file_name, download_url))
remote_file = requests_get_with_retry(download_url)
with open(file_path, "wb") as file:
file.write(remote_file.content)
print("Download Complete!\n")
print("%sDownload Complete!%s\n" % (c1, cr))
if file_name.endswith(".zip"):
zip_file_path = file_path
@ -639,14 +642,17 @@ def main(override=None):
zip_ref.extractall(downloads_folder)
zip_ref.close()
os.remove(zip_file_path)
print("Unzip Complete!\n")
print("%sUnzip Complete!%s\n" % (c2, cr))
for f_name in contents:
new_file = os.path.join(downloads_folder, str(f_name))
pr_file = c3 + new_file + cr
print("The file [%s] was saved to:\n%s\n" % (f_name, pr_file))
print("Making [%s %s] executable ..." % (f_name, use_version))
make_executable(new_file)
print("%s[%s] is now ready for use!%s" % (c1, f_name, cr))
print(
"%s[%s %s] is now ready for use!%s" %
(c1, f_name, use_version, cr)
)
if copy_to_path and os.path.exists(LOCAL_PATH):
path_file = LOCAL_PATH + f_name
shutil.copyfile(new_file, path_file)
@ -709,7 +715,7 @@ def main(override=None):
zip_ref.extractall(downloads_folder)
zip_ref.close()
os.remove(zip_file_path)
print("Unzip Complete!\n")
print("%sUnzip Complete!%s\n" % (c2, cr))
to_remove = [
"%s/Driver_Notes/credits.html" % downloads_folder,
"%s/Driver_Notes/EULA" % downloads_folder,
@ -721,13 +727,17 @@ def main(override=None):
if os.path.exists(os.path.join(downloads_folder, "Driver_Notes/")):
# Only works if the directory is empty
os.rmdir(os.path.join(downloads_folder, "Driver_Notes/"))
pr_driver_path = c3 + driver_path + cr
print(
"The file [%s] was saved to:\n%s\n"
% (driver_file, driver_path)
% (driver_file, pr_driver_path)
)
print("Making [%s %s] executable ..." % (driver_file, use_version))
make_executable(driver_path)
print("%s[%s] is now ready for use!%s" % (c1, driver_file, cr))
print(
"%s[%s %s] is now ready for use!%s"
% (c1, driver_file, use_version, cr)
)
if copy_to_path and os.path.exists(LOCAL_PATH):
path_file = LOCAL_PATH + f_name
shutil.copyfile(new_file, path_file)
@ -755,7 +765,7 @@ def main(override=None):
zip_ref.extractall(downloads_folder)
zip_ref.close()
os.remove(zip_file_path)
print("Unzip Complete!\n")
print("%sUnzip Complete!%s\n" % (c2, cr))
inner_driver = os.path.join(
downloads_folder, inner_folder, driver_file
)
@ -770,7 +780,10 @@ def main(override=None):
)
print("Making [%s %s] executable ..." % (driver_file, use_version))
make_executable(driver_path)
print("%s[%s] is now ready for use!%s" % (c1, driver_file, cr))
print(
"%s[%s %s] is now ready for use!%s"
% (c1, driver_file, use_version, cr)
)
if copy_to_path and os.path.exists(LOCAL_PATH):
path_file = LOCAL_PATH + driver_file
shutil.copyfile(driver_path, path_file)
@ -804,14 +817,17 @@ def main(override=None):
tar.extractall(downloads_folder)
tar.close()
os.remove(tar_file_path)
print("Unzip Complete!\n")
print("%sUnzip Complete!%s\n" % (c2, cr))
for f_name in contents:
new_file = os.path.join(downloads_folder, str(f_name))
pr_file = c3 + new_file + cr
print("The file [%s] was saved to:\n%s\n" % (f_name, pr_file))
print("Making [%s %s] executable ..." % (f_name, use_version))
make_executable(new_file)
print("%s[%s] is now ready for use!%s" % (c1, f_name, cr))
print(
"%s[%s %s] is now ready for use!%s"
% (c1, f_name, use_version, cr)
)
if copy_to_path and os.path.exists(LOCAL_PATH):
path_file = LOCAL_PATH + f_name
shutil.copyfile(new_file, path_file)

View File

@ -613,7 +613,6 @@ def main():
data.append(" def test_google_dot_com(self):")
data.append(' self.open("https://google.com/ncr")')
data.append(' self.type(HomePage.search_box, "github")')
data.append(" self.assert_element(HomePage.list_box)")
data.append(" self.assert_element(HomePage.search_button)")
data.append(" self.assert_element(HomePage.feeling_lucky_button)")
data.append(" self.click(HomePage.search_button)")
@ -631,7 +630,6 @@ def main():
data.append("class HomePage(object):")
data.append(" dialog_box = '[role=\"dialog\"] div'")
data.append(" search_box = 'input[title=\"Search\"]'")
data.append(" list_box = '[role=\"listbox\"]'")
data.append(" search_button = 'input[value=\"Google Search\"]'")
data.append(
' feeling_lucky_button = """input[value="I\'m Feeling Lucky"]"""'

View File

@ -354,6 +354,7 @@ def _set_chrome_options(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,
@ -875,7 +876,7 @@ def validate_proxy_string(proxy_string):
def get_driver(
browser_name,
browser_name=None,
headless=False,
locale_code=None,
use_grid=False,
@ -895,6 +896,7 @@ def get_driver(
enable_sync=False,
use_auto_ext=False,
undetectable=False,
uc_subprocess=False,
no_sandbox=False,
disable_gpu=False,
headless2=False,
@ -919,7 +921,19 @@ def get_driver(
device_width=None,
device_height=None,
device_pixel_ratio=None,
browser=None, # A duplicate of browser_name to avoid confusion
):
if not browser_name:
if browser:
browser_name = browser
else:
browser_name = "chrome" # The default if not specified
browser_name = browser_name.lower()
if headless2 and browser_name == constants.Browser.FIREFOX:
headless2 = False # Only for Chromium
headless = True
if uc_subprocess and not undetectable:
undetectable = True
proxy_auth = False
proxy_user = None
proxy_pass = None
@ -1046,6 +1060,7 @@ def get_driver(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,
@ -1091,6 +1106,7 @@ def get_driver(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,
@ -1140,6 +1156,7 @@ def get_remote_driver(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,
@ -1239,6 +1256,7 @@ def get_remote_driver(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,
@ -1467,6 +1485,7 @@ def get_remote_driver(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,
@ -1661,6 +1680,7 @@ def get_local_driver(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,
@ -2260,6 +2280,7 @@ def get_local_driver(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,
@ -2322,6 +2343,7 @@ def get_local_driver(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,
@ -2592,6 +2614,7 @@ def get_local_driver(
driver_executable_path=uc_path,
headless=False, # Xvfb needed!
version_main=uc_chrome_version,
use_subprocess=uc_subprocess,
)
except URLError as e:
if (
@ -2609,6 +2632,7 @@ def get_local_driver(
driver_executable_path=uc_path,
headless=False, # Xvfb needed!
version_main=uc_chrome_version,
use_subprocess=uc_subprocess,
)
else:
raise
@ -2700,6 +2724,7 @@ def get_local_driver(
enable_sync,
use_auto_ext,
undetectable,
uc_subprocess,
no_sandbox,
disable_gpu,
headless2,

View File

@ -330,6 +330,24 @@ def get_test_id(test):
scenario_name = scenario_name.split(" -- @")[0]
test_id = "%s:%s => %s" % (file_name, line_num, scenario_name)
return test_id
elif hasattr(test, "is_context_manager") and test.is_context_manager:
filename = test.__class__.__module__.split(".")[-1] + ".py"
classname = test.__class__.__name__
methodname = test._testMethodName
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(test, "cm_filename") and test.cm_filename:
filename = test.cm_filename
else:
filename = test_base.split('"')[0]
classname = "SB"
methodname = ".py:" + test_base.split(", line ")[-1]
context_id = filename.split(".")[0] + methodname + ":" + classname
return context_id
test_id = None
try:
test_id = get_test_name(test)

View File

@ -1902,13 +1902,24 @@ class BaseCase(unittest.TestCase):
):
self.__switch_to_newest_window_if_not_blank()
def click_if_visible(self, selector, by="css selector"):
def click_if_visible(self, selector, by="css selector", timeout=0):
"""If the page selector exists and is visible, clicks on the element.
This method only clicks on the first matching element found.
(Use click_visible_elements() to click all matching elements.)"""
Use click_visible_elements() to click all matching elements.
If a "timeout" is provided, waits that long for the element
to appear before giving up and returning without a click()."""
self.wait_for_ready_state_complete()
if self.is_element_visible(selector, by=by):
self.click(selector, by=by)
elif timeout > 0:
try:
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()
@ -3170,6 +3181,7 @@ class BaseCase(unittest.TestCase):
enable_sync=None,
use_auto_ext=None,
undetectable=None,
uc_subprocess=None,
no_sandbox=None,
disable_gpu=None,
headless2=None,
@ -3218,6 +3230,7 @@ class BaseCase(unittest.TestCase):
enable_sync - the option to enable the Chrome Sync feature (Chrome)
use_auto_ext - the option to enable Chrome's Automation Extension
undetectable - the option to use an undetectable chromedriver
uc_subprocess - use the undetectable chromedriver as a subprocess
no_sandbox - the option to enable the "No-Sandbox" feature (Chrome)
disable_gpu - the option to enable Chrome's "Disable GPU" feature
headless2 - the option to use the newer headless mode (Chromium)
@ -3312,6 +3325,8 @@ class BaseCase(unittest.TestCase):
use_auto_ext = self.use_auto_ext
if undetectable is None:
undetectable = self.undetectable
if uc_subprocess is None:
uc_subprocess = self.uc_subprocess
if no_sandbox is None:
no_sandbox = self.no_sandbox
if disable_gpu is None:
@ -3393,6 +3408,7 @@ class BaseCase(unittest.TestCase):
enable_sync=enable_sync,
use_auto_ext=use_auto_ext,
undetectable=undetectable,
uc_subprocess=uc_subprocess,
no_sandbox=no_sandbox,
disable_gpu=disable_gpu,
headless2=headless2,
@ -3417,6 +3433,7 @@ class BaseCase(unittest.TestCase):
device_width=d_width,
device_height=d_height,
device_pixel_ratio=d_p_r,
browser=browser_name,
)
self._drivers_list.append(new_driver)
self._drivers_browser_map[new_driver] = browser_name
@ -4555,6 +4572,23 @@ class BaseCase(unittest.TestCase):
filename = self.__get_filename()
classname = self.__class__.__name__
methodname = self._testMethodName
context_filename = None
if (
hasattr(sb_config, "is_context_manager")
and sb_config.is_context_manager
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:
filename = self.cm_filename
else:
filename = test_base.split('"')[0]
classname = "SB"
methodname = "test_line_" + test_base.split(", line ")[-1]
context_filename = filename.split(".")[0] + "_rec.py"
if hasattr(self, "is_behave") and self.is_behave:
classname = sb_config.behave_feature.name
classname = classname.replace("/", " ").replace(" & ", " ")
@ -4611,6 +4645,8 @@ class BaseCase(unittest.TestCase):
file_name = sb_config.behave_scenario.filename.replace(".", "_")
file_name = file_name.split("/")[-1].split("\\")[-1]
file_name = file_name + "_rec.py"
elif context_filename:
file_name = context_filename
file_path = os.path.join(recordings_folder, file_name)
out_file = codecs.open(file_path, "w+", "utf-8")
out_file.writelines("\r\n".join(data))
@ -5292,19 +5328,41 @@ class BaseCase(unittest.TestCase):
pass
self.__demo_mode_pause_if_active()
def js_click_if_present(self, selector, by="css selector"):
def js_click_if_present(self, selector, by="css selector", timeout=0):
"""If the page selector exists, js_click() the element.
This method only clicks on the first matching element found."""
This method only clicks on the first matching element found.
If a "timeout" is provided, waits that long for the element to
be present before giving up and returning without a js_click()."""
self.wait_for_ready_state_complete()
if self.is_element_present(selector, by=by):
self.js_click(selector, by=by)
elif timeout > 0:
try:
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)
def js_click_if_visible(self, selector, by="css selector"):
def js_click_if_visible(self, selector, by="css selector", timeout=0):
"""If the page selector exists and is visible, js_click() the element.
This method only clicks on the first matching element found."""
This method only clicks on the first matching element found.
If a "timeout" is provided, waits that long for the element
to appear before giving up and returning without a js_click()."""
self.wait_for_ready_state_complete()
if self.is_element_visible(selector, by=by):
self.js_click(selector, by=by)
elif timeout > 0:
try:
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)
def js_click_all(self, selector, by="css selector"):
"""Clicks all matching elements using pure JS. (No jQuery)"""
@ -11436,16 +11494,14 @@ class BaseCase(unittest.TestCase):
page_domain = self.get_domain_url(page_url)
page_data_domain = self.get_domain_url(page_url_data)
unittest.TestCase.maxDiff = 3200
unittest.TestCase.maxDiff = 65536 # 2^16
if level != 0 and check_domain:
self.assertEqual(page_data_domain, page_domain, domain_fail)
unittest.TestCase.maxDiff = 6400 # Use `None` for no limit
if level == 3:
if not full_diff:
self.__assert_eq(level_3_data, level_3, level_3_failure)
else:
self.assertEqual(level_3_data, level_3, level_3_failure)
unittest.TestCase.maxDiff = 3200
if level == 2:
if not full_diff:
self.__assert_eq(level_2_data, level_2, level_2_failure)
@ -11456,10 +11512,8 @@ class BaseCase(unittest.TestCase):
self.__assert_eq(level_1_data, level_1, level_1_failure)
else:
self.assertEqual(level_1_data, level_1, level_1_failure)
unittest.TestCase.maxDiff = 6400 # Use `None` for no limit
if level == 0:
try:
unittest.TestCase.maxDiff = 3200
if check_domain:
self.assertEqual(
page_domain, page_data_domain, domain_fail
@ -11486,7 +11540,6 @@ class BaseCase(unittest.TestCase):
)
except Exception as e:
print(e)
unittest.TestCase.maxDiff = 6400 # Use `None` for no limit
if not full_diff:
self.__assert_eq(
level_3_data, level_3, level_3_failure
@ -12517,6 +12570,7 @@ class BaseCase(unittest.TestCase):
self.display = Display(visible=0, size=(width, height))
self.display.start()
self.headless_active = True
sb_config.headless_active = True
except Exception:
pass
@ -12602,6 +12656,7 @@ class BaseCase(unittest.TestCase):
self.with_selenium = sb_config.with_selenium # Should be True
self.headless = sb_config.headless
self.headless_active = False
sb_config.headless_active = False
self.headed = sb_config.headed
self.xvfb = sb_config.xvfb
self.locale_code = sb_config.locale_code
@ -12659,6 +12714,7 @@ class BaseCase(unittest.TestCase):
self.enable_sync = sb_config.enable_sync
self.use_auto_ext = sb_config.use_auto_ext
self.undetectable = sb_config.undetectable
self.uc_subprocess = sb_config.uc_subprocess
self.no_sandbox = sb_config.no_sandbox
self.disable_gpu = sb_config.disable_gpu
self.headless2 = sb_config.headless2
@ -12950,6 +13006,7 @@ class BaseCase(unittest.TestCase):
enable_sync=self.enable_sync,
use_auto_ext=self.use_auto_ext,
undetectable=self.undetectable,
uc_subprocess=self.uc_subprocess,
no_sandbox=self.no_sandbox,
disable_gpu=self.disable_gpu,
headless2=self.headless2,
@ -13208,6 +13265,11 @@ class BaseCase(unittest.TestCase):
has_exception = False
if hasattr(sys, "last_traceback") and sys.last_traceback is not None:
has_exception = True
elif hasattr(self, "is_context_manager") and self.is_context_manager:
if self.with_testing_base and self._has_failure:
return True
else:
return False
elif python3 and hasattr(self, "_outcome"):
if hasattr(self._outcome, "errors") and self._outcome.errors:
has_exception = True
@ -13240,6 +13302,22 @@ class BaseCase(unittest.TestCase):
scenario_name = scenario_name.replace(" ", "_")
test_id = "%s.%s" % (file_name, scenario_name)
return test_id
elif hasattr(self, "is_context_manager") and self.is_context_manager:
filename = self.__class__.__module__.split(".")[-1] + ".py"
methodname = self._testMethodName
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:
filename = self.cm_filename
else:
filename = test_base.split('"')[0]
methodname = ".line_" + test_base.split(", line ")[-1]
context_id = filename.split(".")[0] + methodname
return context_id
test_id = "%s.%s.%s" % (
self.__class__.__module__,
self.__class__.__name__,

View File

@ -7,6 +7,7 @@ import ast
import sys
import time
from nose.plugins import Plugin
from seleniumbase import config as sb_config
from seleniumbase.config import settings
from seleniumbase.core import download_helper
from seleniumbase.core import log_helper
@ -200,10 +201,12 @@ class Base(Plugin):
archive_logs = options.archive_logs
log_helper.log_folder_setup(log_path, archive_logs)
download_helper.reset_downloads_folder()
sb_config.is_nosetest = True
if self.report_on:
report_helper.clear_out_old_report_logs(archive_past_runs=False)
def beforeTest(self, test):
sb_config._context_of_runner = False # Context Manager Compatibility
variables = self.options.variables
if variables and type(variables) is str and len(variables) > 0:
bad_input = False

View File

@ -0,0 +1,366 @@
from contextlib import contextmanager
@contextmanager # Usage: -> ``with Driver() as driver:``
def Driver(
browser=None, # Choose from "chrome", "edge", "firefox", or "safari".
headless=None, # The original headless mode for Chromium and Firefox.
headless2=None, # Chromium's new headless mode. (Has more features)
headed=None, # Run tests in headed/GUI mode on Linux, where not default.
locale_code=None, # Set the Language Locale Code for the web browser.
protocol=None, # The Selenium Grid protocol: "http" or "https".
servername=None, # The Selenium Grid server/IP used for tests.
port=None, # The Selenium Grid port used by the test server.
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)
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.
recorder_ext=None, # Enables the SeleniumBase Recorder Chromium extension.
disable_js=None, # Disable JavaScript on websites. Pages might break!
disable_csp=None, # Disable the Content Security Policy of websites.
enable_ws=None, # Enable Web Security on Chromium-based browsers.
enable_sync=None, # Enable "Chrome Sync" on websites.
use_auto_ext=None, # Use Chrome's automation extension.
undetectable=None, # Use undetected-chromedriver to evade bot-detection.
uc_subprocess=None, # Use undetected-chromedriver as a subprocess.
no_sandbox=None, # (DEPRECATED) - "--no-sandbox" is always used now.
disable_gpu=None, # (DEPRECATED) - GPU is disabled if no "swiftshader".
incognito=None, # Enable Chromium's Incognito mode.
guest_mode=None, # Enable Chromium's Guest mode.
devtools=None, # Open Chromium's DevTools when the browser opens.
remote_debug=None, # Enable Chrome's Debugger on "http://localhost:9222".
swiftshader=None, # Use Chrome's "--use-gl=swiftshader" feature.
ad_block_on=None, # Block some types of display ads from loading.
block_images=None, # Block images from loading during tests.
do_not_track=None, # Tell websites that you don't want to be tracked.
chromium_arg=None, # "ARG=N,ARG2" (Set Chromium args, ","-separated.)
firefox_arg=None, # "ARG=N,ARG2" (Set Firefox args, comma-separated.)
firefox_pref=None, # SET (Set Firefox PREFERENCE:VALUE set, ","-separated)
user_data_dir=None, # Set the Chrome user data directory to use.
extension_zip=None, # Load a Chrome Extension .zip|.crx, comma-separated.)
extension_dir=None, # Load a Chrome Extension directory, comma-separated.)
page_load_strategy=None, # Set Chrome PLS to "normal", "eager", or "none".
external_pdf=None, # Set Chrome "plugins.always_open_pdf_externally":True.
is_mobile=None, # Use the mobile device emulator while running tests.
d_width=None, # Set device width
d_height=None, # Set device height
d_p_r=None, # Set device pixel ratio
uc=None, # Shortcut / Duplicate of "undetectable" to avoid confusion.
undetected=None, # Duplicate of "undetectable" to avoid confusion.
uc_sub=None, # Duplicate of "uc_subprocess" to avoid confusion.
):
""" Context Manager for the SeleniumBase Driver Manager.
Usage example:
from seleniumbase import Driver
with Driver() as driver:
driver.get("https://google.com/ncr")
# The browser exits automatically after the "with" block ends.
"""
import sys
from seleniumbase.fixtures import constants
sys_argv = sys.argv
browser_changes = 0
browser_set = None
browser_text = None
browser_list = []
# As a shortcut, you can use "--edge" instead of "--browser=edge", etc,
# but you can only specify one default browser for tests. (Default: chrome)
if "--browser=chrome" in sys_argv or "--browser chrome" in sys_argv:
browser_changes += 1
browser_set = "chrome"
browser_list.append("--browser=chrome")
if "--browser=edge" in sys_argv or "--browser edge" in sys_argv:
browser_changes += 1
browser_set = "edge"
browser_list.append("--browser=edge")
if "--browser=firefox" in sys_argv or "--browser firefox" in sys_argv:
browser_changes += 1
browser_set = "firefox"
browser_list.append("--browser=firefox")
if "--browser=opera" in sys_argv or "--browser opera" in sys_argv:
browser_changes += 1
browser_set = "opera"
browser_list.append("--browser=opera")
if "--browser=safari" in sys_argv or "--browser safari" in sys_argv:
browser_changes += 1
browser_set = "safari"
browser_list.append("--browser=safari")
if "--browser=ie" in sys_argv or "--browser ie" in sys_argv:
browser_changes += 1
browser_set = "ie"
browser_list.append("--browser=ie")
if "--browser=phantomjs" in sys_argv or "--browser phantomjs" in sys_argv:
browser_changes += 1
browser_set = "phantomjs"
browser_list.append("--browser=phantomjs")
if "--browser=remote" in sys_argv or "--browser remote" in sys_argv:
browser_changes += 1
browser_set = "remote"
browser_list.append("--browser=remote")
browser_text = browser_set
if "--chrome" in sys_argv and not browser_set == "chrome":
browser_changes += 1
browser_text = "chrome"
browser_list.append("--chrome")
if "--edge" in sys_argv and not browser_set == "edge":
browser_changes += 1
browser_text = "edge"
browser_list.append("--edge")
if "--firefox" in sys_argv and not browser_set == "firefox":
browser_changes += 1
browser_text = "firefox"
browser_list.append("--firefox")
if "--ie" in sys_argv and not browser_set == "ie":
browser_changes += 1
browser_text = "ie"
browser_list.append("--ie")
if "--opera" in sys_argv and not browser_set == "opera":
browser_changes += 1
browser_text = "opera"
browser_list.append("--opera")
if "--safari" in sys_argv and not browser_set == "safari":
browser_changes += 1
browser_text = "safari"
browser_list.append("--safari")
if browser_changes > 1:
message = "\n\n TOO MANY browser types were entered!"
message += "\n There were %s found:\n > %s" % (
browser_changes,
", ".join(browser_list),
)
message += "\n ONLY ONE default browser is allowed!"
message += "\n Select a single browser & try again!\n"
if not browser:
raise Exception(message)
if browser is None:
if browser_text:
browser = browser_text
else:
browser = "chrome"
else:
browser = browser.lower()
valid_browsers = constants.ValidBrowsers.valid_browsers
if browser not in valid_browsers:
raise Exception(
"Browser: {%s} is not a valid browser option. "
"Valid options = {%s}" % (browser, valid_browsers)
)
if headless is None:
if "--headless" in sys_argv:
headless = True
else:
headless = False
if headless2 is None:
if "--headless2" in sys_argv:
headless2 = True
else:
headless2 = False
if protocol is None:
protocol = "http" # For the Selenium Grid only!
if servername is None:
servername = "localhost" # For the Selenium Grid only!
use_grid = False
if servername != "localhost":
# Use Selenium Grid (Use "127.0.0.1" for localhost Grid)
use_grid = True
if port is None:
port = "4444" # For the Selenium Grid only!
if incognito is None:
if "--incognito" in sys_argv:
incognito = True
else:
incognito = False
if guest_mode is None:
if "--guest" in sys_argv:
guest_mode = True
else:
guest_mode = False
if devtools is None:
if "--devtools" in sys_argv:
devtools = True
else:
devtools = False
if is_mobile is None:
if "--mobile" in sys_argv:
is_mobile = True
else:
is_mobile = False
test_id = "direct_driver"
proxy_string = proxy
user_agent = agent
recorder_mode = False
if recorder_ext:
recorder_mode = True
if (
"--recorder" in sys_argv
or "--record" in sys_argv
or "--rec" in sys_argv
):
recorder_mode = True
recorder_ext = True
if (
"linux" in sys.platform
and not headed
and not headless
and not headless2
):
headless = True
if recorder_mode and headless:
headless = False
headless2 = True
if headless2 and browser == "firefox":
headless2 = False # Only for Chromium browsers
headless = True # Firefox has regular headless
elif browser not in ["chrome", "edge"]:
headless2 = False # Only for Chromium browsers
if disable_csp is None:
disable_csp = False
if enable_ws is None:
enable_ws = False
if enable_sync is None:
enable_sync = False
if (
enable_ws is None
or (enable_ws is not None and enable_ws)
):
enable_ws = True
else:
enable_ws = False
if undetectable or undetected or uc or uc_subprocess or uc_sub:
undetectable = True
elif (
"--undetectable" in sys_argv
or "--undetected" in sys_argv
or "--uc" in sys_argv
or "--uc-subprocess" in sys_argv
or "--uc_subprocess" in sys_argv
or "--uc-sub" in sys_argv
):
undetectable = True
else:
undetectable = False
if uc_subprocess or uc_sub:
uc_subprocess = True
elif (
"--uc-subprocess" in sys_argv
or "--uc_subprocess" in sys_argv
or "--uc-sub" in sys_argv
):
uc_subprocess = True
else:
uc_subprocess = False
if use_auto_ext is None:
if "--use-auto-ext" in sys_argv:
use_auto_ext = True
else:
use_auto_ext = False
if disable_js is None:
if "--disable-js" in sys_argv:
disable_js = True
else:
disable_js = False
if page_load_strategy is not None:
if page_load_strategy.lower() not in ["normal", "eager", "none"]:
raise Exception(
'page_load_strategy must be "normal", "eager", or "none"!'
)
page_load_strategy = page_load_strategy.lower()
elif "--pls=normal" in sys_argv or '--pls="normal"' in sys_argv:
page_load_strategy = "normal"
elif "--pls=eager" in sys_argv or '--pls="eager"' in sys_argv:
page_load_strategy = "eager"
elif "--pls=none" in sys_argv or '--pls="none"' in sys_argv:
page_load_strategy = "none"
if block_images is None:
if "--block-images" in sys_argv:
block_images = True
else:
block_images = False
if do_not_track is None:
if "--do-not-track" in sys_argv:
do_not_track = True
else:
do_not_track = False
if external_pdf is None:
if "--external-pdf" in sys_argv:
external_pdf = True
else:
external_pdf = False
if remote_debug is None:
if "--remote-debug" in sys_argv:
remote_debug = True
else:
remote_debug = False
if swiftshader is None:
if "--swiftshader" in sys_argv:
swiftshader = True
else:
swiftshader = False
if ad_block_on is None:
if "--ad-block" in sys_argv:
ad_block_on = True
else:
ad_block_on = False
browser_name = browser
# Launch a web browser
from seleniumbase.core import browser_launcher
driver = browser_launcher.get_driver(
browser_name=browser_name,
headless=headless,
locale_code=locale_code,
use_grid=use_grid,
protocol=protocol,
servername=servername,
port=port,
proxy_string=proxy_string,
proxy_bypass_list=proxy_bypass_list,
proxy_pac_url=proxy_pac_url,
user_agent=user_agent,
cap_file=cap_file,
cap_string=cap_string,
recorder_ext=recorder_ext,
disable_js=disable_js,
disable_csp=disable_csp,
enable_ws=enable_ws,
enable_sync=enable_sync,
use_auto_ext=use_auto_ext,
undetectable=undetectable,
uc_subprocess=uc_subprocess,
no_sandbox=no_sandbox,
disable_gpu=disable_gpu,
headless2=headless2,
incognito=incognito,
guest_mode=guest_mode,
devtools=devtools,
remote_debug=remote_debug,
swiftshader=swiftshader,
ad_block_on=ad_block_on,
block_images=block_images,
do_not_track=do_not_track,
chromium_arg=chromium_arg,
firefox_arg=firefox_arg,
firefox_pref=firefox_pref,
user_data_dir=user_data_dir,
extension_zip=extension_zip,
extension_dir=extension_dir,
page_load_strategy=page_load_strategy,
external_pdf=external_pdf,
test_id=test_id,
mobile_emulator=is_mobile,
device_width=d_width,
device_height=d_height,
device_pixel_ratio=d_p_r,
browser=browser_name,
)
try:
yield driver
finally:
try:
driver.quit()
except Exception:
pass

View File

@ -90,7 +90,8 @@ def pytest_addoption(parser):
--enable-ws (Enable Web Security on Chromium-based browsers.)
--enable-sync (Enable "Chrome Sync" on websites.)
--use-auto-ext (Use Chrome's automation extension.)
--undetected | --uc (Use undetected-chromedriver to evade bot-detection.)
--uc | --undetected (Use undetected-chromedriver to evade bot-detection.)
--uc-sub | --uc-subprocess (Use undetected-chromedriver as a subprocess.)
--remote-debug (Enable Chrome's Remote Debugger on http://localhost:9222)
--final-debug (Enter Debug Mode after each test ends. Don't use with CI!)
--dashboard (Enable the SeleniumBase Dashboard. Saved at: dashboard.html)
@ -910,6 +911,17 @@ def pytest_addoption(parser):
to websites that use anti-bot services to block
automation tools from navigating them freely.""",
)
parser.addoption(
"--uc_subprocess",
"--uc-subprocess",
"--uc-sub", # undetected-chromedriver subprocess mode
action="store_true",
dest="uc_subprocess",
default=False,
help="""Use undetectable-chromedriver as a subprocess,
which can help avoid issues that might result.
It may reduce UC's ability to avoid detection.""",
)
parser.addoption(
"--no_sandbox",
"--no-sandbox",
@ -1223,7 +1235,7 @@ def pytest_addoption(parser):
browser_list.append("--opera")
if "--safari" in sys_argv and not browser_set == "safari":
browser_changes += 1
browser_text = "opera"
browser_text = "safari"
sb_config._browser_shortcut = "safari"
browser_list.append("--safari")
if browser_changes > 1:
@ -1252,6 +1264,9 @@ def pytest_addoption(parser):
"--undetected" in sys_argv
or "--undetectable" in sys_argv
or "--uc" in sys_argv
or "--uc-subprocess" in sys_argv
or "--uc_subprocess" in sys_argv
or "--uc-sub" in sys_argv
)
):
message = (
@ -1270,6 +1285,9 @@ def pytest_configure(config):
sb_config.item_count_skipped = 0
sb_config.item_count_untested = 0
sb_config.is_pytest = True
sb_config.is_behave = False
sb_config.is_nosetest = False
sb_config.is_context_manager = False
sb_config.pytest_config = config
sb_config.browser = config.getoption("browser")
if sb_config._browser_shortcut:
@ -1287,7 +1305,10 @@ def pytest_configure(config):
sb_config.device_metrics = config.getoption("device_metrics")
sb_config.headless = config.getoption("headless")
sb_config.headless2 = config.getoption("headless2")
if sb_config.browser not in ["chrome", "edge"]:
if sb_config.headless2 and sb_config.browser == "firefox":
sb_config.headless2 = False # Only for Chromium browsers
sb_config.headless = True # Firefox has regular headless
elif sb_config.browser not in ["chrome", "edge"]:
sb_config.headless2 = False # Only for Chromium browsers
sb_config.headed = config.getoption("headed")
sb_config.xvfb = config.getoption("xvfb")
@ -1363,6 +1384,9 @@ def pytest_configure(config):
sb_config.enable_sync = config.getoption("enable_sync")
sb_config.use_auto_ext = config.getoption("use_auto_ext")
sb_config.undetectable = config.getoption("undetectable")
sb_config.uc_subprocess = config.getoption("uc_subprocess")
if sb_config.uc_subprocess and not sb_config.undetectable:
sb_config.undetectable = True
sb_config.no_sandbox = config.getoption("no_sandbox")
sb_config.disable_gpu = config.getoption("disable_gpu")
sb_config.remote_debug = config.getoption("remote_debug")
@ -1457,11 +1481,6 @@ def pytest_configure(config):
sb_config.recorder_mode = False
sb_config.recorder_ext = False
# Recorder Mode can still optimize scripts in --headless2 mode.
if sb_config.recorder_mode and sb_config.headless:
sb_config.headless = False
sb_config.headless2 = True
if sb_config.xvfb and "linux" not in sys.platform:
# The Xvfb virtual display server is for Linux OS Only!
sb_config.xvfb = False
@ -1480,6 +1499,12 @@ def pytest_configure(config):
"or by calling the new --headless2.)"
)
sb_config.headless = True
# Recorder Mode can still optimize scripts in --headless2 mode.
if sb_config.recorder_mode and sb_config.headless:
sb_config.headless = False
sb_config.headless2 = True
if not sb_config.headless and not sb_config.headless2:
sb_config.headed = True
@ -1605,6 +1630,7 @@ def pytest_collection_finish(session):
"""This runs after item collection is finalized.
https://docs.pytest.org/en/stable/reference.html
"""
sb_config._context_of_runner = False # Context Manager Compatibility
if "--co" in sys_argv or "--collect-only" in sys_argv:
return
if len(session.items) > 0 and not sb_config._multithreaded:
@ -1694,14 +1720,20 @@ def pytest_runtest_teardown(item):
if hasattr(self, "xvfb") and self.xvfb:
if self.headless_active and "--pdb" not in sys_argv:
if hasattr(self, "display") and self.display:
self.headless_active = False
sb_config.headless_active = False
self.display.stop()
elif hasattr(self, "headless") and self.headless:
if self.headless_active and "--pdb" not in sys_argv:
if hasattr(self, "display") and self.display:
self.headless_active = False
sb_config.headless_active = False
self.display.stop()
elif hasattr(self, "headless2") and self.headless2:
if self.headless_active and "--pdb" not in sys_argv:
if hasattr(self, "display") and self.display:
self.headless_active = False
sb_config.headless_active = False
self.display.stop()
except Exception:
pass

View File

@ -0,0 +1,800 @@
from contextlib import contextmanager
@contextmanager # Usage: -> ``with SB() as sb:``
def SB(
test=None, # Test Mode: Output, Logging, Continue on failure unless "rtf".
raise_test_failure=None, # In "test" mode, raise Exception at 1st failure.
rtf=None, # Short form of "raise_test_failure". (Less typing, same thing!)
browser=None, # Choose from "chrome", "edge", "firefox", or "safari".
headless=None, # The original headless mode for Chromium and Firefox.
headless2=None, # Chromium's new headless mode. (Has more features)
locale_code=None, # Set the Language Locale Code for the web browser.
protocol=None, # The Selenium Grid protocol: "http" or "https".
servername=None, # The Selenium Grid server/IP used for tests.
port=None, # The Selenium Grid port used by the test server.
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)
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.
recorder_ext=None, # Enables the SeleniumBase Recorder Chromium extension.
disable_js=None, # Disable JavaScript on websites. Pages might break!
disable_csp=None, # Disable the Content Security Policy of websites.
enable_ws=None, # Enable Web Security on Chromium-based browsers.
enable_sync=None, # Enable "Chrome Sync" on websites.
use_auto_ext=None, # Use Chrome's automation extension.
undetectable=None, # Use undetected-chromedriver to evade bot-detection.
uc_subprocess=None, # Use undetected-chromedriver as a subprocess.
incognito=None, # Enable Chromium's Incognito mode.
guest_mode=None, # Enable Chromium's Guest mode.
devtools=None, # Open Chromium's DevTools when the browser opens.
remote_debug=None, # Enable Chrome's Debugger on "http://localhost:9222".
swiftshader=None, # Use Chrome's "--use-gl=swiftshader" feature.
ad_block_on=None, # Block some types of display ads from loading.
block_images=None, # Block images from loading during tests.
do_not_track=None, # Tell websites that you don't want to be tracked.
chromium_arg=None, # "ARG=N,ARG2" (Set Chromium args, ","-separated.)
firefox_arg=None, # "ARG=N,ARG2" (Set Firefox args, comma-separated.)
firefox_pref=None, # SET (Set Firefox PREFERENCE:VALUE set, ","-separated)
user_data_dir=None, # Set the Chrome user data directory to use.
extension_zip=None, # Load a Chrome Extension .zip|.crx, comma-separated.)
extension_dir=None, # Load a Chrome Extension directory, comma-separated.)
page_load_strategy=None, # Set Chrome PLS to "normal", "eager", or "none".
external_pdf=None, # Set Chrome "plugins.always_open_pdf_externally":True.
is_mobile=None, # Use the mobile device emulator while running tests.
device_metrics=None, # Set mobile metrics: "CSSWidth,CSSHeight,PixelRatio"
xvfb=None, # Run tests using the Xvfb virtual display server on Linux OS.
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.
record_sleep=None, # If Recorder enabled, also records self.sleep calls.
data=None, # Extra test data. Access with "self.data" in tests.
var1=None, # Extra test data. Access with "self.var1" in tests.
var2=None, # Extra test data. Access with "self.var2" in tests.
var3=None, # Extra test data. Access with "self.var3" in tests.
variables=None, # DICT (Extra test data. Access with "self.variables")
account=None, # Set account. Access with "self.account" in tests.
environment=None, # Set the test env. Access with "self.env" in tests.
headed=None, # Run tests in headed/GUI mode on Linux, where not default.
maximize=None, # Start tests with the browser window maximized.
disable_ws=None, # Reverse of "enable_ws". (None and False are different)
disable_beforeunload=None, # Disable the "beforeunload" event on Chromium.
settings_file=None, # A file for overriding default SeleniumBase settings.
uc=None, # Shortcut / Duplicate of "undetectable" to avoid confusion.
undetected=None, # Duplicate of "undetectable" to avoid confusion.
uc_sub=None, # Duplicate of "uc_subprocess" to avoid confusion.
save_screenshot=None, # Save a screenshot at the end of each test.
timeout_multiplier=None, # Multiplies the default timeout values.
js_checking_on=None, # Check for JavaScript errors after page loads.
slow=None, # Slow down the automation. Faster than using Demo Mode.
demo=None, # Slow down and visually see test actions as they occur.
demo_sleep=None, # SECONDS (Set wait time after Slow & Demo Mode actions.)
message_duration=None, # SECONDS (The time length for Messenger alerts.)
highlights=None, # Number of highlight animations for Demo Mode actions.
interval=None, # SECONDS (Autoplay interval for SB Slides & Tour steps.)
time_limit=None, # SECONDS (Safely fail tests that exceed the time limit.)
):
"""Context Manager for SeleniumBase.
Usage example ->
from seleniumbase import SB
with SB() as sb:
sb.open("https://google.com/ncr")
sb.type('[name="q"]', "SeleniumBase on GitHub\n")
sb.click('a[href*="github.com/seleniumbase"]')
sb.highlight("div.Layout-main")
sb.highlight("div.Layout-sidebar")
sb.sleep(0.5)
# The browser exits automatically after the "with" block ends.
"""
import sys
import time
import traceback
from seleniumbase import BaseCase
from seleniumbase import config as sb_config
from seleniumbase.config import settings
from seleniumbase.fixtures import constants
sb_config_backup = sb_config
sys_argv = sys.argv
archive_logs = False
existing_runner = False
do_log_folder_setup = False # The first "test=True" run does it
if (
(hasattr(sb_config, "is_behave") and sb_config.is_behave)
or (hasattr(sb_config, "is_pytest") and sb_config.is_pytest)
or (hasattr(sb_config, "is_nosetest") and sb_config.is_nosetest)
):
existing_runner = True
test = False # Already using a test runner. Skip extra test steps.
elif test is None and "--test" in sys_argv:
test = True
if (
existing_runner
and not hasattr(sb_config, "_context_of_runner")
):
sb_config._context_of_runner = True
if hasattr(sb_config, "is_pytest") and sb_config.is_pytest:
print(
"\n SB Manager script was triggered by pytest collection!"
'\n (Prevent that by using: `if __name__ == "__main__":`)'
)
elif hasattr(sb_config, "is_nosetest") and sb_config.is_nosetest:
print(
"\n SB Manager script was triggered by nosetest collection!"
'\n (Prevent that by using: ``if __name__ == "__main__":``)'
)
if (
not existing_runner
and not hasattr(sb_config, "_has_older_context")
and test
):
# This is the first "test" from context manager scripts run.
sb_config._has_older_context = True
do_log_folder_setup = True
else:
if test:
pass # Not the first "test" of context manager scripts.
else:
pass # Not in "test" mode. (No special output/logging.)
with_testing_base = False
if test:
with_testing_base = True
if (
raise_test_failure
or rtf
or "--raise-test-failure" in sys_argv
or "--rtf" in sys_argv
or "-x" in sys_argv # Carry-over from "pytest"
or "--exitfirst" in sys_argv # Carry-over from "pytest"
):
raise_test_failure = True # Exit on first error or failed test.
else:
raise_test_failure = False
browser_changes = 0
browser_set = None
browser_text = None
browser_list = []
# As a shortcut, you can use "--edge" instead of "--browser=edge", etc,
# but you can only specify one default browser for tests. (Default: chrome)
if "--browser=chrome" in sys_argv or "--browser chrome" in sys_argv:
browser_changes += 1
browser_set = "chrome"
browser_list.append("--browser=chrome")
if "--browser=edge" in sys_argv or "--browser edge" in sys_argv:
browser_changes += 1
browser_set = "edge"
browser_list.append("--browser=edge")
if "--browser=firefox" in sys_argv or "--browser firefox" in sys_argv:
browser_changes += 1
browser_set = "firefox"
browser_list.append("--browser=firefox")
if "--browser=opera" in sys_argv or "--browser opera" in sys_argv:
browser_changes += 1
browser_set = "opera"
browser_list.append("--browser=opera")
if "--browser=safari" in sys_argv or "--browser safari" in sys_argv:
browser_changes += 1
browser_set = "safari"
browser_list.append("--browser=safari")
if "--browser=ie" in sys_argv or "--browser ie" in sys_argv:
browser_changes += 1
browser_set = "ie"
browser_list.append("--browser=ie")
if "--browser=phantomjs" in sys_argv or "--browser phantomjs" in sys_argv:
browser_changes += 1
browser_set = "phantomjs"
browser_list.append("--browser=phantomjs")
if "--browser=remote" in sys_argv or "--browser remote" in sys_argv:
browser_changes += 1
browser_set = "remote"
browser_list.append("--browser=remote")
browser_text = browser_set
if "--chrome" in sys_argv and not browser_set == "chrome":
browser_changes += 1
browser_text = "chrome"
sb_config._browser_shortcut = "chrome"
browser_list.append("--chrome")
if "--edge" in sys_argv and not browser_set == "edge":
browser_changes += 1
browser_text = "edge"
sb_config._browser_shortcut = "edge"
browser_list.append("--edge")
if "--firefox" in sys_argv and not browser_set == "firefox":
browser_changes += 1
browser_text = "firefox"
sb_config._browser_shortcut = "firefox"
browser_list.append("--firefox")
if "--ie" in sys_argv and not browser_set == "ie":
browser_changes += 1
browser_text = "ie"
sb_config._browser_shortcut = "ie"
browser_list.append("--ie")
if "--opera" in sys_argv and not browser_set == "opera":
browser_changes += 1
browser_text = "opera"
sb_config._browser_shortcut = "opera"
browser_list.append("--opera")
if "--safari" in sys_argv and not browser_set == "safari":
browser_changes += 1
browser_text = "safari"
sb_config._browser_shortcut = "safari"
browser_list.append("--safari")
if browser_changes > 1:
message = "\n\n TOO MANY browser types were entered!"
message += "\n There were %s found:\n > %s" % (
browser_changes,
", ".join(browser_list),
)
message += "\n ONLY ONE default browser is allowed!"
message += "\n Select a single browser & try again!\n"
if not browser:
raise Exception(message)
if browser is None:
if browser_text:
browser = browser_text
else:
browser = "chrome"
else:
browser = browser.lower()
valid_browsers = constants.ValidBrowsers.valid_browsers
if browser not in valid_browsers:
raise Exception(
"Browser: {%s} is not a valid browser option. "
"Valid options = {%s}" % (browser, valid_browsers)
)
if headless is None:
if "--headless" in sys_argv:
headless = True
else:
headless = False
if headless2 is None:
if "--headless2" in sys_argv:
headless2 = True
else:
headless2 = False
if protocol is None:
protocol = "http" # For the Selenium Grid only!
if servername is None:
servername = "localhost" # For the Selenium Grid only!
if port is None:
port = "4444" # For the Selenium Grid only!
if not environment:
environment = "test"
if incognito is None:
if "--incognito" in sys_argv:
incognito = True
else:
incognito = False
if guest_mode is None:
if "--guest" in sys_argv:
guest_mode = True
else:
guest_mode = False
if devtools is None:
if "--devtools" in sys_argv:
devtools = True
else:
devtools = False
if is_mobile is None:
if "--mobile" in sys_argv:
is_mobile = True
else:
is_mobile = False
proxy_string = proxy
user_agent = agent
recorder_mode = False
if recorder_ext:
recorder_mode = True
if (
"--recorder" in sys_argv
or "--record" in sys_argv
or "--rec" in sys_argv
):
recorder_mode = True
recorder_ext = True
if rec_print is None:
if "--rec-print" in sys_argv:
rec_print = True
else:
rec_print = False
if rec_behave is None:
if "--rec-behave" in sys_argv:
rec_behave = True
else:
rec_behave = False
if record_sleep is None:
if "--rec-sleep" in sys_argv or "--record-sleep" in sys_argv:
record_sleep = True
else:
record_sleep = False
if "linux" not in sys.platform:
# The Xvfb virtual display server is for Linux OS Only!
xvfb = False
if (
"linux" in sys.platform
and not headed
and not headless
and not headless2
and not xvfb
):
headless = True
if headless2 and browser == "firefox":
headless2 = False # Only for Chromium browsers
headless = True # Firefox has regular headless
elif browser not in ["chrome", "edge"]:
headless2 = False # Only for Chromium browsers
if not headless and not headless2:
headed = True
if rec_print and not recorder_mode:
recorder_mode = True
recorder_ext = True
elif rec_behave and not recorder_mode:
recorder_mode = True
recorder_ext = True
elif record_sleep and not recorder_mode:
recorder_mode = True
recorder_ext = True
if recorder_mode and headless:
headless = False
headless2 = True
if variables and type(variables) is str and len(variables) > 0:
import ast
bad_input = False
if (
not variables.startswith("{")
or not variables.endswith("}")
):
bad_input = True
else:
try:
variables = ast.literal_eval(variables)
if not type(variables) is dict:
bad_input = True
except Exception:
bad_input = True
if bad_input:
raise Exception(
'\nExpecting a Python dictionary for "variables"!'
"\nEg. --variables=\"{'KEY1':'VALUE', 'KEY2':123}\""
)
else:
variables = {}
if disable_csp is None:
disable_csp = False
if enable_ws is None:
enable_ws = False
if enable_sync is None:
enable_sync = False
if (
(enable_ws is None and disable_ws is None)
or (disable_ws is not None and not disable_ws)
or (enable_ws is not None and enable_ws)
):
enable_ws = True
disable_ws = False
else:
enable_ws = False
disable_ws = True
if undetectable or undetected or uc or uc_subprocess or uc_sub:
undetectable = True
elif (
"--undetectable" in sys_argv
or "--undetected" in sys_argv
or "--uc" in sys_argv
or "--uc-subprocess" in sys_argv
or "--uc_subprocess" in sys_argv
or "--uc-sub" in sys_argv
):
undetectable = True
else:
undetectable = False
if uc_subprocess or uc_sub:
uc_subprocess = True
elif (
"--uc-subprocess" in sys_argv
or "--uc_subprocess" in sys_argv
or "--uc-sub" in sys_argv
):
uc_subprocess = True
else:
uc_subprocess = False
if use_auto_ext is None:
if "--use-auto-ext" in sys_argv:
use_auto_ext = True
else:
use_auto_ext = False
if disable_js is None:
if "--disable-js" in sys_argv:
disable_js = True
else:
disable_js = False
maximize_option = False
if maximize or "--maximize" in sys_argv:
maximize_option = True
_disable_beforeunload = False
if disable_beforeunload:
_disable_beforeunload = True
if page_load_strategy is not None:
if page_load_strategy.lower() not in ["normal", "eager", "none"]:
raise Exception(
'page_load_strategy must be "normal", "eager", or "none"!'
)
page_load_strategy = page_load_strategy.lower()
elif "--pls=normal" in sys_argv or '--pls="normal"' in sys_argv:
page_load_strategy = "normal"
elif "--pls=eager" in sys_argv or '--pls="eager"' in sys_argv:
page_load_strategy = "eager"
elif "--pls=none" in sys_argv or '--pls="none"' in sys_argv:
page_load_strategy = "none"
if (
"--sjw" in sys_argv
or "--skip_js_waits" in sys_argv
or "--skip-js-waits" in sys_argv
):
settings.SKIP_JS_WAITS = True
if save_screenshot is None:
if "--screenshot" in sys_argv or "--save-screenshot" in sys_argv:
save_screenshot = True
else:
save_screenshot = False
if js_checking_on is None:
if "--check-js" in sys_argv:
js_checking_on = True
else:
js_checking_on = False
slow_mode = False
if slow:
slow_mode = True
elif "--slow" in sys_argv:
slow_mode = True
demo_mode = False
if demo:
demo_mode = True
elif "--demo" in sys_argv:
demo_mode = True
if block_images is None:
if "--block-images" in sys_argv:
block_images = True
else:
block_images = False
if do_not_track is None:
if "--do-not-track" in sys_argv:
do_not_track = True
else:
do_not_track = False
if external_pdf is None:
if "--external-pdf" in sys_argv:
external_pdf = True
else:
external_pdf = False
if remote_debug is None:
if "--remote-debug" in sys_argv:
remote_debug = True
else:
remote_debug = False
if swiftshader is None:
if "--swiftshader" in sys_argv:
swiftshader = True
else:
swiftshader = False
if ad_block_on is None:
if "--ad-block" in sys_argv:
ad_block_on = True
else:
ad_block_on = False
if highlights is not None:
try:
highlights = int(highlights)
except Exception:
raise Exception('"highlights" must be an integer!')
if interval is not None:
try:
interval = float(interval)
except Exception:
raise Exception('"interval" must be numeric!')
if time_limit is not None:
try:
time_limit = float(time_limit)
except Exception:
raise Exception('"time_limit" must be numeric!')
sb_config.with_testing_base = with_testing_base
sb_config.browser = browser
if not hasattr(sb_config, "is_behave"):
sb_config.is_behave = False
if not hasattr(sb_config, "is_pytest"):
sb_config.is_pytest = False
if not hasattr(sb_config, "is_nosetest"):
sb_config.is_nosetest = False
sb_config.is_context_manager = True
sb_config.headless = headless
sb_config.headless2 = headless2
sb_config.headed = headed
sb_config.xvfb = xvfb
sb_config.start_page = start_page
sb_config.locale_code = locale_code
sb_config.protocol = protocol
sb_config.servername = servername
sb_config.port = port
sb_config.data = data
sb_config.var1 = var1
sb_config.var2 = var2
sb_config.var3 = var3
sb_config.variables = variables
sb_config.account = account
sb_config.environment = environment
sb_config.env = environment
sb_config.user_agent = user_agent
sb_config.incognito = incognito
sb_config.guest_mode = guest_mode
sb_config.devtools = devtools
sb_config.mobile_emulator = is_mobile
sb_config.device_metrics = device_metrics
sb_config.extension_zip = extension_zip
sb_config.extension_dir = extension_dir
sb_config.database_env = "test"
sb_config.log_path = "latest_logs"
sb_config.archive_logs = archive_logs
sb_config.disable_csp = disable_csp
sb_config.disable_ws = disable_ws
sb_config.enable_ws = enable_ws
sb_config.enable_sync = enable_sync
sb_config.use_auto_ext = use_auto_ext
sb_config.undetectable = undetectable
sb_config.uc_subprocess = uc_subprocess
sb_config.no_sandbox = None
sb_config.disable_gpu = None
sb_config.disable_js = disable_js
sb_config._multithreaded = False
sb_config.reuse_session = False
sb_config.crumbs = False
sb_config.final_debug = False
sb_config.visual_baseline = False
sb_config.window_size = None
sb_config.maximize_option = maximize_option
sb_config._disable_beforeunload = _disable_beforeunload
sb_config.save_screenshot = save_screenshot
sb_config.page_load_strategy = page_load_strategy
sb_config.timeout_multiplier = timeout_multiplier
sb_config.pytest_html_report = None
sb_config.with_db_reporting = False
sb_config.with_s3_logging = False
sb_config.js_checking_on = js_checking_on
sb_config.recorder_mode = recorder_mode
sb_config.recorder_ext = recorder_ext
sb_config.record_sleep = record_sleep
sb_config.rec_behave = rec_behave
sb_config.rec_print = rec_print
sb_config.report_on = False
sb_config.slow_mode = slow_mode
sb_config.demo_mode = demo_mode
sb_config._time_limit = time_limit
sb_config.demo_sleep = demo_sleep
sb_config.dashboard = False
sb_config._dashboard_initialized = False
sb_config.message_duration = message_duration
sb_config.block_images = block_images
sb_config.do_not_track = do_not_track
sb_config.external_pdf = external_pdf
sb_config.remote_debug = remote_debug
sb_config.settings_file = settings_file
sb_config.user_data_dir = user_data_dir
sb_config.chromium_arg = chromium_arg
sb_config.firefox_arg = firefox_arg
sb_config.firefox_pref = firefox_pref
sb_config.proxy_string = proxy_string
sb_config.proxy_bypass_list = proxy_bypass_list
sb_config.proxy_pac_url = proxy_pac_url
sb_config.swiftshader = swiftshader
sb_config.ad_block_on = ad_block_on
sb_config.highlights = highlights
sb_config.interval = interval
sb_config.cap_file = cap_file
sb_config.cap_string = cap_string
sb = BaseCase()
sb.with_testing_base = sb_config.with_testing_base
sb.browser = sb_config.browser
sb.is_behave = False
sb.is_pytest = False
sb.is_nosetest = False
sb.is_context_manager = sb_config.is_context_manager
sb.headless = sb_config.headless
sb.headless2 = sb_config.headless2
sb.headed = sb_config.headed
sb.xvfb = sb_config.xvfb
sb.start_page = sb_config.start_page
sb.locale_code = sb_config.locale_code
sb.protocol = sb_config.protocol
sb.servername = sb_config.servername
sb.port = sb_config.port
sb.data = sb_config.data
sb.var1 = sb_config.var1
sb.var2 = sb_config.var2
sb.var3 = sb_config.var3
sb.variables = sb_config.variables
sb.account = sb_config.account
sb.environment = sb_config.environment
sb.env = sb_config.env
sb.user_agent = sb_config.user_agent
sb.incognito = sb_config.incognito
sb.guest_mode = sb_config.guest_mode
sb.devtools = sb_config.devtools
sb.mobile_emulator = sb_config.mobile_emulator
sb.device_metrics = sb_config.device_metrics
sb.extension_zip = sb_config.extension_zip
sb.extension_dir = sb_config.extension_dir
sb.database_env = sb_config.database_env
sb.log_path = sb_config.log_path
sb.archive_logs = sb_config.archive_logs
sb.disable_csp = sb_config.disable_csp
sb.disable_ws = sb_config.disable_ws
sb.enable_ws = sb_config.enable_ws
sb.enable_sync = sb_config.enable_sync
sb.use_auto_ext = sb_config.use_auto_ext
sb.undetectable = sb_config.undetectable
sb.uc_subprocess = sb_config.uc_subprocess
sb.no_sandbox = sb_config.no_sandbox
sb.disable_gpu = sb_config.disable_gpu
sb.disable_js = sb_config.disable_js
sb._multithreaded = sb_config._multithreaded
sb._reuse_session = sb_config.reuse_session
sb._crumbs = sb_config.crumbs
sb._final_debug = sb_config.final_debug
sb.visual_baseline = sb_config.visual_baseline
sb.window_size = sb_config.window_size
sb.maximize_option = sb_config.maximize_option
sb._disable_beforeunload = sb_config._disable_beforeunload
sb.save_screenshot_after_test = sb_config.save_screenshot
sb.page_load_strategy = sb_config.page_load_strategy
sb.timeout_multiplier = sb_config.timeout_multiplier
sb.pytest_html_report = sb_config.pytest_html_report
sb.with_db_reporting = sb_config.with_db_reporting
sb.with_s3_logging = sb_config.with_s3_logging
sb.js_checking_on = sb_config.js_checking_on
sb.recorder_mode = sb_config.recorder_mode
sb.recorder_ext = sb_config.recorder_ext
sb.record_sleep = sb_config.record_sleep
sb.rec_behave = sb_config.rec_behave
sb.rec_print = sb_config.rec_print
sb.report_on = sb_config.report_on
sb.slow_mode = sb_config.slow_mode
sb.demo_mode = sb_config.demo_mode
sb.time_limit = sb_config._time_limit
sb.demo_sleep = sb_config.demo_sleep
sb.dashboard = sb_config.dashboard
sb._dash_initialized = sb_config._dashboard_initialized
sb.message_duration = sb_config.message_duration
sb.block_images = sb_config.block_images
sb.do_not_track = sb_config.do_not_track
sb.external_pdf = sb_config.external_pdf
sb.remote_debug = sb_config.remote_debug
sb.settings_file = sb_config.settings_file
sb.user_data_dir = sb_config.user_data_dir
sb.chromium_arg = sb_config.chromium_arg
sb.firefox_arg = sb_config.firefox_arg
sb.firefox_pref = sb_config.firefox_pref
sb.proxy_string = sb_config.proxy_string
sb.proxy_bypass_list = sb_config.proxy_bypass_list
sb.proxy_pac_url = sb_config.proxy_pac_url
sb.swiftshader = sb_config.swiftshader
sb.ad_block_on = sb_config.ad_block_on
sb.highlights = sb_config.highlights
sb.interval = sb_config.interval
sb.cap_file = sb_config.cap_file
sb.cap_string = sb_config.cap_string
sb._has_failure = False # This may change
if hasattr(sb_config, "headless_active"):
sb.headless_active = sb_config.headless_active
else:
sb.headless_active = False
test_name = None
if test:
import colorama
import os
colorama.init(autoreset=True)
c1 = colorama.Fore.GREEN
b1 = colorama.Style.BRIGHT
cr = colorama.Style.RESET_ALL
stack_base = traceback.format_stack()[0].split(os.sep)[-1]
test_name = stack_base.split(", in ")[0].replace('", line ', ":")
test_name += ":SB"
terminal_width = os.get_terminal_size()[0]
start_text = "=== {%s} starts ===" % test_name
remaining_spaces = terminal_width - len(start_text)
left_space = ""
right_space = ""
if remaining_spaces > 0:
left_spaces = int(remaining_spaces / 2)
left_space = left_spaces * "="
right_spaces = remaining_spaces - left_spaces
right_space = right_spaces * "="
if not test_name.startswith("runpy.py:"):
print("%s%s%s%s%s" % (b1, left_space, start_text, right_space, cr))
if do_log_folder_setup:
from seleniumbase.core import log_helper
from seleniumbase.core import download_helper
from seleniumbase.core import proxy_helper
log_helper.log_folder_setup(sb_config.log_path)
download_helper.reset_downloads_folder()
proxy_helper.remove_proxy_zip_if_present()
start_time = time.time()
sb.setUp()
test_passed = True # This can change later
teardown_exception = None
try:
yield sb
except Exception as e:
sb._has_failure = True
exception = e
test_passed = False
if not test_name:
raise
else:
the_traceback = traceback.format_exc().strip()
try:
p2 = the_traceback.split(', in ')[1].split('", line ')[0]
filename = p2.split("/")[-1]
sb.cm_filename = filename
except Exception:
sb.cm_filename = None
# Tests will raise an exception later if "raise_test_failure"
finally:
try:
sb.tearDown()
except Exception as t_e:
teardown_exception = t_e
print(traceback.format_exc().strip())
if test and not test_passed:
print("********** ERROR: The test AND the tearDown() FAILED!")
end_time = time.time()
run_time = end_time - start_time
sb_config = sb_config_backup
if test:
sb_config._has_older_context = True
if existing_runner:
sb_config._context_of_runner = True
if test_name:
result = "passed"
if not test_passed:
result = "failed"
c1 = colorama.Fore.RED
terminal_width = os.get_terminal_size()[0]
end_text = (
"=== {%s} %s in %.2fs ==="
% (test_name, result, run_time)
)
remaining_spaces = terminal_width - len(end_text)
end_text = (
"=== %s%s{%s} %s%s%s in %.2fs ==="
% (b1, c1, test_name, result, cr, c1, run_time)
)
left_space = ""
right_space = ""
if remaining_spaces > 0:
left_spaces = int(remaining_spaces / 2)
left_space = left_spaces * "="
right_spaces = remaining_spaces - left_spaces
right_space = right_spaces * "="
if not test_passed:
print(the_traceback)
if not test_name.startswith("runpy.py:"):
print(
"%s%s%s%s%s"
% (c1, left_space, end_text, right_space, cr)
)
if test and test_name and not test_passed and raise_test_failure:
raise exception
elif (
teardown_exception
and (
not test
or (test_passed and raise_test_failure)
)
):
raise teardown_exception

View File

@ -68,7 +68,8 @@ class SeleniumBrowser(Plugin):
--enable-ws (Enable Web Security on Chromium-based browsers.)
--enable-sync (Enable "Chrome Sync" on websites.)
--use-auto-ext (Use Chrome's automation extension.)
--undetected | --uc (Use undetected-chromedriver to evade bot-detection.)
--uc | --undetected (Use undetected-chromedriver to evade bot-detection.)
--uc-sub | --uc-subprocess (Use undetected-chromedriver as a subprocess.)
--remote-debug (Enable Chrome's Remote Debugger on http://localhost:9222)
--final-debug (Enter Debug Mode after each test ends. Don't use with CI!)
--swiftshader (Use Chrome's "--use-gl=swiftshader" feature.)
@ -634,6 +635,17 @@ class SeleniumBrowser(Plugin):
to websites that use anti-bot services to block
automation tools from navigating them freely.""",
)
parser.add_option(
"--uc_subprocess",
"--uc-subprocess",
"--uc-sub", # undetected-chromedriver subprocess mode
action="store_true",
dest="uc_subprocess",
default=False,
help="""Use undetectable-chromedriver as a subprocess,
which can help avoid issues that might result.
It may reduce UC's ability to avoid detection.""",
)
parser.add_option(
"--no_sandbox",
"--no-sandbox",
@ -789,9 +801,12 @@ class SeleniumBrowser(Plugin):
self.enabled = True # Used if test class inherits BaseCase
self.options = options
self.headless_active = False # Default setting
sb_config.headless_active = False
sb_config.is_nosetest = True
proxy_helper.remove_proxy_zip_if_present()
def beforeTest(self, test):
sb_config._context_of_runner = False # Context Manager Compatibility
browser = self.options.browser
if self.options.recorder_mode and browser not in ["chrome", "edge"]:
message = (
@ -824,12 +839,24 @@ class SeleniumBrowser(Plugin):
settings.HEADLESS_START_WIDTH = width
settings.HEADLESS_START_HEIGHT = height
test.test.is_nosetest = True
test.test.is_behave = False
test.test.is_pytest = False
test.test.is_context_manager = False
sb_config.is_nosetest = True
sb_config.is_behave = False
sb_config.is_pytest = False
sb_config.is_context_manager = False
test.test.browser = self.options.browser
test.test.cap_file = self.options.cap_file
test.test.cap_string = self.options.cap_string
test.test.headless = self.options.headless
test.test.headless2 = self.options.headless2
if test.test.browser not in ["chrome", "edge"]:
if test.test.headless2 and test.test.browser == "firefox":
test.test.headless2 = False # Only for Chromium browsers
test.test.headless = True # Firefox has regular headless
self.options.headless2 = False
self.options.headless = True
elif test.test.browser not in ["chrome", "edge"]:
test.test.headless2 = False # Only for Chromium browsers
self.options.headless2 = False
test.test.headed = self.options.headed
@ -889,6 +916,9 @@ class SeleniumBrowser(Plugin):
test.test.enable_sync = self.options.enable_sync
test.test.use_auto_ext = self.options.use_auto_ext
test.test.undetectable = self.options.undetectable
test.test.uc_subprocess = self.options.uc_subprocess
if test.test.uc_subprocess and not test.test.undetectable:
test.test.undetectable = True
test.test.no_sandbox = self.options.no_sandbox
test.test.disable_gpu = self.options.disable_gpu
test.test.remote_debug = self.options.remote_debug
@ -912,12 +942,6 @@ class SeleniumBrowser(Plugin):
# (Set --server="127.0.0.1" for localhost Grid)
if str(self.options.port) == "443":
test.test.protocol = "https"
# Recorder Mode can still optimize scripts in --headless2 mode.
if self.options.recorder_mode and self.options.headless:
self.options.headless = False
self.options.headless2 = True
test.test.headless = False
test.test.headless2 = True
if self.options.xvfb and "linux" not in sys.platform:
# The Xvfb virtual display server is for Linux OS Only!
self.options.xvfb = False
@ -937,6 +961,12 @@ class SeleniumBrowser(Plugin):
)
self.options.headless = True
test.test.headless = True
# Recorder Mode can still optimize scripts in --headless2 mode.
if self.options.recorder_mode and self.options.headless:
self.options.headless = False
self.options.headless2 = True
test.test.headless = False
test.test.headless2 = True
if not self.options.headless and not self.options.headless2:
self.options.headed = True
test.test.headed = True
@ -952,6 +982,7 @@ class SeleniumBrowser(Plugin):
self.display = Display(visible=0, size=(1440, 1880))
self.display.start()
self.headless_active = True
sb_config.headless_active = True
except Exception:
# pyvirtualdisplay might not be necessary anymore because
# Chrome and Firefox now have built-in headless displays
@ -983,6 +1014,8 @@ class SeleniumBrowser(Plugin):
if self.options.headless or self.options.xvfb:
if self.headless_active:
try:
self.headless_active = False
sb_config.headless_active = False
self.display.stop()
except AttributeError:
pass