Merge pull request #2770 from seleniumbase/update-options-and-uc-mode

Update options and UC Mode
This commit is contained in:
Michael Mintz 2024-05-13 00:52:03 -04:00 committed by GitHub
commit 3656fd68a0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 191 additions and 19 deletions

View File

@ -671,6 +671,7 @@ pytest test_coffee_cart.py --trace
--block-images # (Block images from loading during tests.) --block-images # (Block images from loading during tests.)
--do-not-track # (Indicate to websites that you don't want to be tracked.) --do-not-track # (Indicate to websites that you don't want to be tracked.)
--verify-delay=SECONDS # (The delay before MasterQA verification checks.) --verify-delay=SECONDS # (The delay before MasterQA verification checks.)
--ee | --esc-end # (Lets the user end the current test via the ESC key.)
--recorder # (Enables the Recorder for turning browser actions into code.) --recorder # (Enables the Recorder for turning browser actions into code.)
--rec-behave # (Same as Recorder Mode, but also generates behave-gherkin.) --rec-behave # (Same as Recorder Mode, but also generates behave-gherkin.)
--rec-sleep # (If the Recorder is enabled, also records self.sleep calls.) --rec-sleep # (If the Recorder is enabled, also records self.sleep calls.)

View File

@ -76,6 +76,7 @@ if pure_python:
sb._reuse_session = False sb._reuse_session = False
sb._crumbs = False sb._crumbs = False
sb._final_debug = False sb._final_debug = False
sb.esc_end = False
sb.use_wire = False sb.use_wire = False
sb.enable_3d_apis = False sb.enable_3d_apis = False
sb.window_size = None sb.window_size = None

View File

@ -164,6 +164,7 @@ pytest my_first_test.py --settings-file=custom_settings.py
--block-images # (Block images from loading during tests.) --block-images # (Block images from loading during tests.)
--do-not-track # (Indicate to websites that you don't want to be tracked.) --do-not-track # (Indicate to websites that you don't want to be tracked.)
--verify-delay=SECONDS # (The delay before MasterQA verification checks.) --verify-delay=SECONDS # (The delay before MasterQA verification checks.)
--ee | --esc-end # (Lets the user end the current test via the ESC key.)
--recorder # (Enables the Recorder for turning browser actions into code.) --recorder # (Enables the Recorder for turning browser actions into code.)
--rec-behave # (Same as Recorder Mode, but also generates behave-gherkin.) --rec-behave # (Same as Recorder Mode, but also generates behave-gherkin.)
--rec-sleep # (If the Recorder is enabled, also records self.sleep calls.) --rec-sleep # (If the Recorder is enabled, also records self.sleep calls.)

View File

@ -1046,6 +1046,8 @@ driver.uc_open_with_tab(url) # (New tab with default reconnect_time)
driver.uc_open_with_reconnect(url, reconnect_time=None) # (New tab) driver.uc_open_with_reconnect(url, reconnect_time=None) # (New tab)
driver.uc_open_with_disconnect(url) # Open in new tab + disconnect()
driver.reconnect(timeout) # disconnect() + sleep(timeout) + connect() driver.reconnect(timeout) # disconnect() + sleep(timeout) + connect()
driver.disconnect() # Stops the webdriver service to prevent detection driver.disconnect() # Stops the webdriver service to prevent detection

View File

@ -121,6 +121,8 @@ pytest TEST_NAME.py --trace --rec -s
⏺️ Inside recorded tests, you might find the <code>self.open_if_not_url(URL)</code> method, which opens the URL given if the browser is not currently on that page. SeleniumBase uses this method in recorded scripts when the Recorder detects that a browser action changed the current URL. This method prevents an unnecessary page load and shows what page the test visited after a browser action. ⏺️ Inside recorded tests, you might find the <code>self.open_if_not_url(URL)</code> method, which opens the URL given if the browser is not currently on that page. SeleniumBase uses this method in recorded scripts when the Recorder detects that a browser action changed the current URL. This method prevents an unnecessary page load and shows what page the test visited after a browser action.
⏺️ By launching the Recorder App with <code>sbase recorder --ee</code>, you can end the recording by pressing {<code>SHIFT</code>+<code>ESC</code>} instead of the usual way of ending the recording by typing <code>c</code> from a <code>breakpoint()</code> and pressing <code>Enter</code>. Those buttons don't need to be pressed at the same time, but <code>SHIFT</code> must be pressed directly before <code>ESC</code>.
-------- --------
<div>To learn more about SeleniumBase, check out the Docs Site:</div> <div>To learn more about SeleniumBase, check out the Docs Site:</div>

View File

@ -159,6 +159,8 @@ driver.uc_open_with_tab(url)
driver.uc_open_with_reconnect(url, reconnect_time=None) driver.uc_open_with_reconnect(url, reconnect_time=None)
driver.uc_open_with_disconnect(url)
driver.reconnect(timeout) driver.reconnect(timeout)
driver.disconnect() driver.disconnect()

View File

@ -1,26 +1,26 @@
# mkdocs dependencies for generating the seleniumbase.io website # mkdocs dependencies for generating the seleniumbase.io website
# Minimum Python version: 3.8 (for generating docs only) # Minimum Python version: 3.8 (for generating docs only)
regex>=2024.4.28 regex>=2024.5.10
pymdown-extensions>=10.8.1 pymdown-extensions>=10.8.1
pipdeptree>=2.19.1 pipdeptree>=2.20.0
python-dateutil>=2.8.2 python-dateutil>=2.8.2
Markdown==3.6 Markdown==3.6
markdown2==2.4.13 markdown2==2.4.13
MarkupSafe==2.1.5 MarkupSafe==2.1.5
Jinja2==3.1.3 Jinja2==3.1.4
click==8.1.7 click==8.1.7
ghp-import==2.1.0 ghp-import==2.1.0
watchdog==4.0.0 watchdog==4.0.0
cairocffi==1.7.0 cairocffi==1.7.0
pathspec==0.12.1 pathspec==0.12.1
Babel==2.14.0 Babel==2.15.0
paginate==0.5.6 paginate==0.5.6
lxml==5.2.1 lxml==5.2.1
pyquery==2.0.0 pyquery==2.0.0
readtime==3.0.0 readtime==3.0.0
mkdocs==1.6.0 mkdocs==1.6.0
mkdocs-material==9.5.21 mkdocs-material==9.5.22
mkdocs-exclude-search==0.6.6 mkdocs-exclude-search==0.6.6
mkdocs-simple-hooks==0.1.5 mkdocs-simple-hooks==0.1.5
mkdocs-material-extensions==1.3.1 mkdocs-material-extensions==1.3.1

View File

@ -57,7 +57,8 @@ behave==1.2.6
soupsieve==2.4.1;python_version<"3.8" soupsieve==2.4.1;python_version<"3.8"
soupsieve==2.5;python_version>="3.8" soupsieve==2.5;python_version>="3.8"
beautifulsoup4==4.12.3 beautifulsoup4==4.12.3
pygments==2.17.2 pygments==2.17.2;python_version<"3.8"
pygments==2.18.0;python_version>="3.8"
pyreadline3==3.4.1;platform_system=="Windows" pyreadline3==3.4.1;platform_system=="Windows"
tabcompleter==1.3.0 tabcompleter==1.3.0
pdbp==1.5.0 pdbp==1.5.0
@ -72,7 +73,7 @@ rich==13.7.1
# ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.) # ("pip install -r requirements.txt" also installs this, but "pip install -e ." won't.)
coverage==7.2.7;python_version<"3.8" coverage==7.2.7;python_version<"3.8"
coverage>=7.5.0;python_version>="3.8" coverage>=7.5.1;python_version>="3.8"
pytest-cov==4.1.0;python_version<"3.8" pytest-cov==4.1.0;python_version<"3.8"
pytest-cov>=5.0.0;python_version>="3.8" pytest-cov>=5.0.0;python_version>="3.8"
flake8==5.0.4;python_version<"3.9" flake8==5.0.4;python_version<"3.9"

View File

@ -1,2 +1,2 @@
# seleniumbase package # seleniumbase package
__version__ = "4.26.3" __version__ = "4.26.4"

View File

@ -406,6 +406,7 @@ sbase codegen new_test.py --url=wikipedia.org
``--edge`` (Use Edge browser instead of Chrome.) ``--edge`` (Use Edge browser instead of Chrome.)
``--gui`` / ``--headed`` (Use headed mode on Linux.) ``--gui`` / ``--headed`` (Use headed mode on Linux.)
``--uc`` / ``--undetected`` (Use undetectable mode.) ``--uc`` / ``--undetected`` (Use undetectable mode.)
``--ee`` (Use SHIFT + ESC to end the recording.)
``--overwrite`` (Overwrite file when it exists.) ``--overwrite`` (Overwrite file when it exists.)
``--behave`` (Also output Behave/Gherkin files.) ``--behave`` (Also output Behave/Gherkin files.)

View File

@ -310,6 +310,7 @@ def show_mkrec_usage():
print(" --edge (Use Edge browser instead of Chrome.)") print(" --edge (Use Edge browser instead of Chrome.)")
print(" --gui / --headed (Use headed mode on Linux.)") print(" --gui / --headed (Use headed mode on Linux.)")
print(" --uc / --undetected (Use undetectable mode.)") print(" --uc / --undetected (Use undetectable mode.)")
print(" --ee (Use SHIFT + ESC to end the recording.)")
print(" --overwrite (Overwrite file when it exists.)") print(" --overwrite (Overwrite file when it exists.)")
print(" --behave (Also output Behave/Gherkin files.)") print(" --behave (Also output Behave/Gherkin files.)")
print(" Output:") print(" Output:")
@ -336,6 +337,7 @@ def show_codegen_usage():
print(" --edge (Use Edge browser instead of Chrome.)") print(" --edge (Use Edge browser instead of Chrome.)")
print(" --gui / --headed (Use headed mode on Linux.)") print(" --gui / --headed (Use headed mode on Linux.)")
print(" --uc / --undetected (Use undetectable mode.)") print(" --uc / --undetected (Use undetectable mode.)")
print(" --ee (Use SHIFT + ESC to end the recording.)")
print(" --overwrite (Overwrite file when it exists.)") print(" --overwrite (Overwrite file when it exists.)")
print(" --behave (Also output Behave/Gherkin files.)") print(" --behave (Also output Behave/Gherkin files.)")
print(" Output:") print(" Output:")

View File

@ -93,6 +93,7 @@ def main():
invalid_cmd = None invalid_cmd = None
use_edge = False use_edge = False
use_uc = False use_uc = False
esc_end = False
start_page = None start_page = None
next_is_url = False next_is_url = False
use_colors = True use_colors = True
@ -145,6 +146,8 @@ def main():
help_me = True help_me = True
elif option.lower() == "--edge": elif option.lower() == "--edge":
use_edge = True use_edge = True
elif option.lower() == "--ee":
esc_end = True
elif option.lower() in ("--gui", "--headed"): elif option.lower() in ("--gui", "--headed"):
if "linux" in sys.platform: if "linux" in sys.platform:
force_gui = True force_gui = True
@ -183,6 +186,42 @@ def main():
data.append(' # type "c", and press [Enter].') data.append(' # type "c", and press [Enter].')
data.append(" import pdb; pdb.set_trace()") data.append(" import pdb; pdb.set_trace()")
data.append("") data.append("")
if esc_end:
msg = ">>> Use [SHIFT + ESC] in the browser to end recording!"
d2 = []
d2.append("from seleniumbase import BaseCase")
d2.append("")
d2.append("")
d2.append("class RecorderTest(BaseCase):")
d2.append(" def test_recording(self):")
d2.append(" if self.recorder_ext:")
d2.append(" print(")
d2.append(' "\\n\\n%s\\n"' % msg)
d2.append(" )")
d2.append(' script = self._get_rec_shift_esc_script()')
d2.append(' esc = "return document.sb_esc_end;"')
d2.append(" start_time = self.time()")
d2.append(" last_handles_num = self._get_num_handles()")
d2.append(" for i in range(1200):")
d2.append(" try:")
d2.append(" self.execute_script(script)")
d2.append(" handles_num = self._get_num_handles()")
d2.append(" if handles_num < 1:")
d2.append(" return")
d2.append(" elif handles_num != last_handles_num:")
d2.append(" self.switch_to_window(-1)")
d2.append(" last_handles_num = handles_num")
d2.append(' if self.execute_script(esc) == "yes":')
d2.append(" return")
d2.append(" elif self.time() - start_time > 600:")
d2.append(" return")
d2.append(" self.sleep(0.5)")
d2.append(" except Exception:")
d2.append(" return")
d2.append("")
data = d2
file = codecs.open(file_path, "w+", "utf-8") file = codecs.open(file_path, "w+", "utf-8")
file.writelines("\r\n".join(data)) file.writelines("\r\n".join(data))
file.close() file.close()

View File

@ -162,6 +162,8 @@ def do_recording(file_name, url, overwrite_enabled, use_chrome, window):
or "--undetectable" in command_args or "--undetectable" in command_args
): ):
command += " --uc" command += " --uc"
if "--ee" in command_args:
command += " --ee"
command += add_on command += add_on
poll = None poll = None
if sb_config.rec_subprocess_used: if sb_config.rec_subprocess_used:

View File

@ -443,8 +443,32 @@ def uc_open_with_reconnect(driver, url, reconnect_time=None):
js_utils.call_me_later(driver, script, 3) js_utils.call_me_later(driver, script, 3)
time.sleep(0.007) time.sleep(0.007)
driver.close() driver.close()
driver.reconnect(reconnect_time) if reconnect_time == "disconnect":
driver.switch_to.window(driver.window_handles[-1]) driver.disconnect()
time.sleep(0.007)
else:
driver.reconnect(reconnect_time)
driver.switch_to.window(driver.window_handles[-1])
else:
driver.default_get(url) # The original one
return None
def uc_open_with_disconnect(driver, url):
"""Open a url and disconnect chromedriver.
Note: You can't perform Selenium actions again
until after you've called driver.connect()."""
if url.startswith("//"):
url = "https:" + url
elif ":" not in url:
url = "https://" + url
if (url.startswith("http:") or url.startswith("https:")):
script = 'window.open("%s","_blank");' % url
js_utils.call_me_later(driver, script, 3)
time.sleep(0.007)
driver.close()
driver.disconnect()
time.sleep(0.007)
else: else:
driver.default_get(url) # The original one driver.default_get(url) # The original one
return None return None
@ -3754,6 +3778,11 @@ def get_local_driver(
driver, *args, **kwargs driver, *args, **kwargs
) )
) )
driver.uc_open_with_disconnect = (
lambda *args, **kwargs: uc_open_with_disconnect(
driver, *args, **kwargs
)
)
driver.uc_click = lambda *args, **kwargs: uc_click( driver.uc_click = lambda *args, **kwargs: uc_click(
driver, *args, **kwargs driver, *args, **kwargs
) )

View File

@ -114,6 +114,7 @@ class BaseCase(unittest.TestCase):
] ]
self.version_tuple = tuple(self.version_list) self.version_tuple = tuple(self.version_list)
self.version_info = self.version_tuple self.version_info = self.version_tuple
self.time = time.time
self.__page_sources = [] self.__page_sources = []
self.__extra_actions = [] self.__extra_actions = []
self.__js_start_time = 0 self.__js_start_time = 0
@ -381,6 +382,7 @@ class BaseCase(unittest.TestCase):
self, selector, by="css selector", timeout=None, delay=0, scroll=True self, selector, by="css selector", timeout=None, delay=0, scroll=True
): ):
self.__check_scope() self.__check_scope()
self.__skip_if_esc()
if not timeout: if not timeout:
timeout = settings.SMALL_TIMEOUT timeout = settings.SMALL_TIMEOUT
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
@ -671,6 +673,7 @@ class BaseCase(unittest.TestCase):
self.__demo_mode_pause_if_active(tiny=True) self.__demo_mode_pause_if_active(tiny=True)
elif self.slow_mode: elif self.slow_mode:
self.__slow_mode_pause_if_active() self.__slow_mode_pause_if_active()
self.__set_esc_skip()
def slow_click(self, selector, by="css selector", timeout=None): def slow_click(self, selector, by="css selector", timeout=None):
"""Similar to click(), but pauses for a brief moment before clicking. """Similar to click(), but pauses for a brief moment before clicking.
@ -1586,6 +1589,7 @@ class BaseCase(unittest.TestCase):
def click_link_text(self, link_text, timeout=None): def click_link_text(self, link_text, timeout=None):
"""This method clicks link text on a page.""" """This method clicks link text on a page."""
self.__check_scope() self.__check_scope()
self.__skip_if_esc()
if not timeout: if not timeout:
timeout = settings.SMALL_TIMEOUT timeout = settings.SMALL_TIMEOUT
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT: if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
@ -4142,6 +4146,10 @@ class BaseCase(unittest.TestCase):
self.uc_open_with_tab = new_driver.uc_open_with_tab self.uc_open_with_tab = new_driver.uc_open_with_tab
if hasattr(new_driver, "uc_open_with_reconnect"): if hasattr(new_driver, "uc_open_with_reconnect"):
self.uc_open_with_reconnect = new_driver.uc_open_with_reconnect self.uc_open_with_reconnect = new_driver.uc_open_with_reconnect
if hasattr(new_driver, "uc_open_with_disconnect"):
self.uc_open_with_disconnect = (
new_driver.uc_open_with_disconnect
)
if hasattr(new_driver, "reconnect"): if hasattr(new_driver, "reconnect"):
self.reconnect = new_driver.reconnect self.reconnect = new_driver.reconnect
if hasattr(new_driver, "disconnect"): if hasattr(new_driver, "disconnect"):
@ -4404,11 +4412,35 @@ class BaseCase(unittest.TestCase):
for cookie_dict in cookies: for cookie_dict in cookies:
self.driver.add_cookie(cookie_dict) self.driver.add_cookie(cookie_dict)
def __set_esc_skip(self):
if hasattr(self, "esc_end") and self.esc_end:
script = (
"""document.onkeydown = function(evt) {
evt = evt || window.event;
var isEscape = false;
if ("key" in evt) {
isEscape = (evt.key === "Escape" || evt.key === "Esc");
} else {
isEscape = (evt.keyCode === 27);
}
if (isEscape) {
document.sb_esc_end = 'yes';
}
};"""
)
self.execute_script(script)
def __skip_if_esc(self):
if hasattr(self, "esc_end") and self.esc_end:
if self.execute_script("return document.sb_esc_end;") == "yes":
self.skip()
def wait_for_ready_state_complete(self, timeout=None): def wait_for_ready_state_complete(self, timeout=None):
"""Waits for the "readyState" of the page to be "complete". """Waits for the "readyState" of the page to be "complete".
Returns True when the method completes.""" Returns True when the method completes."""
self.__check_scope() self.__check_scope()
self._check_browser() self._check_browser()
self.__skip_if_esc()
if not timeout: if not timeout:
timeout = settings.EXTREME_TIMEOUT timeout = settings.EXTREME_TIMEOUT
if self.timeout_multiplier and timeout == settings.EXTREME_TIMEOUT: if self.timeout_multiplier and timeout == settings.EXTREME_TIMEOUT:
@ -4427,6 +4459,7 @@ class BaseCase(unittest.TestCase):
time.sleep(0.01) time.sleep(0.01)
if self.undetectable: if self.undetectable:
time.sleep(0.035) time.sleep(0.035)
self.__set_esc_skip()
return True return True
def wait_for_angularjs(self, timeout=None, **kwargs): def wait_for_angularjs(self, timeout=None, **kwargs):
@ -5775,6 +5808,7 @@ class BaseCase(unittest.TestCase):
scroll - the option to scroll to the element first (Default: True) scroll - the option to scroll to the element first (Default: True)
timeout - the time to wait for the element to appear """ timeout - the time to wait for the element to appear """
self.__check_scope() self.__check_scope()
self.__skip_if_esc()
if isinstance(selector, WebElement): if isinstance(selector, WebElement):
self.__highlight_element(selector, loops=loops, scroll=scroll) self.__highlight_element(selector, loops=loops, scroll=scroll)
return return
@ -6760,10 +6794,7 @@ class BaseCase(unittest.TestCase):
constants.PipInstall.FINDLOCK constants.PipInstall.FINDLOCK
) )
with pip_find_lock: with pip_find_lock:
if ( if sys.version_info < (3, 9):
sys.version_info >= (3, 7)
and sys.version_info < (3, 9)
):
# Fix bug in newer cryptography for Python 3.7 and 3.8: # Fix bug in newer cryptography for Python 3.7 and 3.8:
# "pyo3_runtime.PanicException: Python API call failed" # "pyo3_runtime.PanicException: Python API call failed"
try: try:
@ -8709,6 +8740,7 @@ class BaseCase(unittest.TestCase):
): ):
"""Same as self.wait_for_element()""" """Same as self.wait_for_element()"""
self.__check_scope() self.__check_scope()
self.__skip_if_esc()
if not timeout: if not timeout:
timeout = settings.LARGE_TIMEOUT timeout = settings.LARGE_TIMEOUT
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
@ -13478,6 +13510,7 @@ class BaseCase(unittest.TestCase):
self.slow_scroll_to(selector, by=by) self.slow_scroll_to(selector, by=by)
def __demo_mode_highlight_if_active(self, selector, by): def __demo_mode_highlight_if_active(self, selector, by):
self.__skip_if_esc()
if self.demo_mode: if self.demo_mode:
# Includes self.slow_scroll_to(selector, by=by) by default # Includes self.slow_scroll_to(selector, by=by) by default
self.__highlight(selector, by=by) self.__highlight(selector, by=by)
@ -14379,6 +14412,7 @@ class BaseCase(unittest.TestCase):
self.firefox_arg = sb_config.firefox_arg self.firefox_arg = sb_config.firefox_arg
self.firefox_pref = sb_config.firefox_pref self.firefox_pref = sb_config.firefox_pref
self.verify_delay = sb_config.verify_delay self.verify_delay = sb_config.verify_delay
self.esc_end = sb_config.esc_end
self.recorder_mode = sb_config.recorder_mode self.recorder_mode = sb_config.recorder_mode
self.recorder_ext = sb_config.recorder_mode self.recorder_ext = sb_config.recorder_mode
self.rec_print = sb_config.rec_print self.rec_print = sb_config.rec_print
@ -15698,6 +15732,31 @@ class BaseCase(unittest.TestCase):
else: else:
return None return None
def _get_num_handles(self):
return len(self.driver.window_handles)
def _get_rec_shift_esc_script(self):
return (
"""document.onkeydown = function(evt) {
evt = evt || window.event;
var isEscape = false;
if ("key" in evt) {
isEscape = (evt.key === "Escape" || evt.key === "Esc");
last_key = evt.key;
} else {
isEscape = (evt.keyCode === 27);
last_key = evt.keyCode;
if (last_key === 16) {
last_key = "Shift";
}
}
if (isEscape && document.sb_last_key === "Shift") {
document.sb_esc_end = "yes";
}
document.sb_last_key = last_key;
};"""
)
def _addSkip(self, result, test_case, reason): def _addSkip(self, result, test_case, reason):
"""This method should NOT be called directly from tests.""" """This method should NOT be called directly from tests."""
addSkip = getattr(result, 'addSkip', None) addSkip = getattr(result, 'addSkip', None)
@ -15828,6 +15887,11 @@ class BaseCase(unittest.TestCase):
) )
raise Exception(message) raise Exception(message)
# *** Start tearDown() officially *** # *** Start tearDown() officially ***
if self.undetectable:
try:
self.driver.window_handles
except urllib3.exceptions.MaxRetryError:
self.driver.connect()
self.__slow_mode_pause_if_active() self.__slow_mode_pause_if_active()
has_exception = self.__has_exception() has_exception = self.__has_exception()
sb_config._has_exception = has_exception sb_config._has_exception = has_exception

View File

@ -81,6 +81,7 @@ def pytest_addoption(parser):
--block-images (Block images from loading during tests.) --block-images (Block images from loading during tests.)
--do-not-track (Indicate to websites that you don't want to be tracked.) --do-not-track (Indicate to websites that you don't want to be tracked.)
--verify-delay=SECONDS (The delay before MasterQA verification checks.) --verify-delay=SECONDS (The delay before MasterQA verification checks.)
--ee / --esc-end (Lets the user end the current test via the ESC key.)
--recorder (Enables the Recorder for turning browser actions into code.) --recorder (Enables the Recorder for turning browser actions into code.)
--rec-behave (Same as Recorder Mode, but also generates behave-gherkin.) --rec-behave (Same as Recorder Mode, but also generates behave-gherkin.)
--rec-sleep (If the Recorder is enabled, also records self.sleep calls.) --rec-sleep (If the Recorder is enabled, also records self.sleep calls.)
@ -895,6 +896,16 @@ def pytest_addoption(parser):
help="""Setting this overrides the default wait time help="""Setting this overrides the default wait time
before each MasterQA verification pop-up.""", before each MasterQA verification pop-up.""",
) )
parser.addoption(
"--esc-end",
"--esc_end",
"--ee",
action="store_true",
dest="esc_end",
default=False,
help="""End the current test early via the ESC key.
The test will be marked as skipped.""",
)
parser.addoption( parser.addoption(
"--recorder", "--recorder",
"--record", "--record",
@ -1549,6 +1560,7 @@ def pytest_configure(config):
sb_config.block_images = config.getoption("block_images") sb_config.block_images = config.getoption("block_images")
sb_config.do_not_track = config.getoption("do_not_track") sb_config.do_not_track = config.getoption("do_not_track")
sb_config.verify_delay = config.getoption("verify_delay") sb_config.verify_delay = config.getoption("verify_delay")
sb_config.esc_end = config.getoption("esc_end")
sb_config.recorder_mode = config.getoption("recorder_mode") sb_config.recorder_mode = config.getoption("recorder_mode")
sb_config.recorder_ext = config.getoption("recorder_mode") # Again sb_config.recorder_ext = config.getoption("recorder_mode") # Again
sb_config.rec_behave = config.getoption("rec_behave") sb_config.rec_behave = config.getoption("rec_behave")

View File

@ -60,6 +60,7 @@ class SeleniumBrowser(Plugin):
--block-images (Block images from loading during tests.) --block-images (Block images from loading during tests.)
--do-not-track (Indicate to websites that you don't want to be tracked.) --do-not-track (Indicate to websites that you don't want to be tracked.)
--verify-delay=SECONDS (The delay before MasterQA verification checks.) --verify-delay=SECONDS (The delay before MasterQA verification checks.)
--ee / --esc-end (Lets the user end the current test via the ESC key.)
--recorder (Enables the Recorder for turning browser actions into code.) --recorder (Enables the Recorder for turning browser actions into code.)
--rec-behave (Same as Recorder Mode, but also generates behave-gherkin.) --rec-behave (Same as Recorder Mode, but also generates behave-gherkin.)
--rec-sleep (If the Recorder is enabled, also records self.sleep calls.) --rec-sleep (If the Recorder is enabled, also records self.sleep calls.)
@ -613,6 +614,16 @@ class SeleniumBrowser(Plugin):
help="""Setting this overrides the default wait time help="""Setting this overrides the default wait time
before each MasterQA verification pop-up.""", before each MasterQA verification pop-up.""",
) )
parser.addoption(
"--esc-end",
"--esc_end",
"--ee",
action="store_true",
dest="esc_end",
default=False,
help="""End the current test early via the ESC key.
The test will be marked as skipped.""",
)
parser.addoption( parser.addoption(
"--recorder", "--recorder",
"--record", "--record",
@ -1126,6 +1137,7 @@ class SeleniumBrowser(Plugin):
test.test.block_images = self.options.block_images test.test.block_images = self.options.block_images
test.test.do_not_track = self.options.do_not_track test.test.do_not_track = self.options.do_not_track
test.test.verify_delay = self.options.verify_delay # MasterQA test.test.verify_delay = self.options.verify_delay # MasterQA
test.test.esc_end = self.options.esc_end
test.test.recorder_mode = self.options.recorder_mode test.test.recorder_mode = self.options.recorder_mode
test.test.recorder_ext = self.options.recorder_mode # Again test.test.recorder_ext = self.options.recorder_mode # Again
test.test.rec_behave = self.options.rec_behave test.test.rec_behave = self.options.rec_behave

View File

@ -14,7 +14,7 @@ class WebElement(selenium.webdriver.remote.webelement.WebElement):
): ):
if driver and selector and by: if driver and selector and by:
delayed_click = False delayed_click = False
if tag_name in ["span", "button", "div", "a"]: if tag_name in ["span", "button", "div", "a", "b", "input"]:
delayed_click = True delayed_click = True
if delayed_click and ":contains" not in selector: if delayed_click and ":contains" not in selector:
selector = js_utils.convert_to_css_selector(selector, by) selector = js_utils.convert_to_css_selector(selector, by)

View File

@ -205,7 +205,8 @@ setup(
'soupsieve==2.4.1;python_version<"3.8"', 'soupsieve==2.4.1;python_version<"3.8"',
'soupsieve==2.5;python_version>="3.8"', 'soupsieve==2.5;python_version>="3.8"',
"beautifulsoup4==4.12.3", "beautifulsoup4==4.12.3",
'pygments==2.17.2', 'pygments==2.17.2;python_version<"3.8"',
'pygments==2.18.0;python_version>="3.8"',
'pyreadline3==3.4.1;platform_system=="Windows"', 'pyreadline3==3.4.1;platform_system=="Windows"',
"tabcompleter==1.3.0", "tabcompleter==1.3.0",
"pdbp==1.5.0", "pdbp==1.5.0",
@ -229,7 +230,7 @@ setup(
# Usage: coverage run -m pytest; coverage html; coverage report # Usage: coverage run -m pytest; coverage html; coverage report
"coverage": [ "coverage": [
'coverage==7.2.7;python_version<"3.8"', 'coverage==7.2.7;python_version<"3.8"',
'coverage>=7.5.0;python_version>="3.8"', 'coverage>=7.5.1;python_version>="3.8"',
'pytest-cov==4.1.0;python_version<"3.8"', 'pytest-cov==4.1.0;python_version<"3.8"',
'pytest-cov>=5.0.0;python_version>="3.8"', 'pytest-cov>=5.0.0;python_version>="3.8"',
], ],
@ -256,7 +257,7 @@ setup(
'pdfminer.six==20221105;python_version<"3.8"', 'pdfminer.six==20221105;python_version<"3.8"',
'pdfminer.six==20231228;python_version>="3.8"', 'pdfminer.six==20231228;python_version>="3.8"',
'cryptography==39.0.2;python_version<"3.9"', 'cryptography==39.0.2;python_version<"3.9"',
'cryptography==42.0.5;python_version>="3.9"', 'cryptography==42.0.7;python_version>="3.9"',
'cffi==1.15.1;python_version<"3.8"', 'cffi==1.15.1;python_version<"3.8"',
'cffi==1.16.0;python_version>="3.8"', 'cffi==1.16.0;python_version>="3.8"',
"pycparser==2.22", "pycparser==2.22",
@ -277,7 +278,7 @@ setup(
# Usage: proxy # Usage: proxy
# (That starts a proxy server on "127.0.0.1:8899".) # (That starts a proxy server on "127.0.0.1:8899".)
"proxy": [ "proxy": [
"proxy.py==2.4.3", "proxy.py==2.4.4",
], ],
# pip install -e .[psutil] # pip install -e .[psutil]
"psutil": [ "psutil": [