Merge pull request #2770 from seleniumbase/update-options-and-uc-mode
Update options and UC Mode
This commit is contained in:
commit
3656fd68a0
|
@ -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.)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
# seleniumbase package
|
# seleniumbase package
|
||||||
__version__ = "4.26.3"
|
__version__ = "4.26.4"
|
||||||
|
|
|
@ -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.)
|
||||||
|
|
||||||
|
|
|
@ -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:")
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
9
setup.py
9
setup.py
|
@ -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": [
|
||||||
|
|
Loading…
Reference in New Issue