Merge pull request #988 from seleniumbase/recorder-mode-improvements
Recorder Mode improvements (and more)
This commit is contained in:
commit
dc533cb9e1
|
@ -23,8 +23,8 @@
|
|||
<p align="center">
|
||||
<a href="#python_installation">🚀 Start</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/ReadMe.md">👨🏫 Examples</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/console_scripts/ReadMe.md">🧙♂️ Scripts</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/features_list.md">🏰 Features</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/console_scripts/ReadMe.md">🧙♂️ Scripts</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md">🖥️ CLI</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/mobile_testing.md">📱 Mobile</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/visual_testing/ReadMe.md">🖼️ VisualTest</a>
|
||||
|
@ -38,8 +38,8 @@
|
|||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/js_package_manager.md">🕹️ JSPkgMgr</a>
|
||||
<br />
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/integrations/github/workflows/ReadMe.md">🤖 CI</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/presenter/ReadMe.md">🎞️ Present</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/tree/master/examples/boilerplates">♻️ Boilerplate</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/presenter/ReadMe.md">🎞️ Slides</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/tree/master/examples/boilerplates">♻️ Boilerplates</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/translations.md">🌏 Translate</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/tour_examples/ReadMe.md">🗺️ Tour</a> |
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase/blob/master/examples/chart_maker/ReadMe.md">📶 Charts</a> |
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
regex>=2021.8.27
|
||||
regex>=2021.9.24
|
||||
tqdm>=4.62.3
|
||||
livereload==2.6.3;python_version>="3.6"
|
||||
joblib==1.0.1;python_version>="3.6"
|
||||
|
@ -20,7 +20,7 @@ lunr==0.6.0;python_version>="3.6"
|
|||
nltk==3.6.3;python_version>="3.6"
|
||||
watchdog==2.1.5;python_version>="3.6"
|
||||
mkdocs==1.2.2;python_version>="3.6"
|
||||
mkdocs-material==7.2.8;python_version>="3.6"
|
||||
mkdocs-material==7.3.0;python_version>="3.6"
|
||||
mkdocs-exclude-search==0.5.2;python_version>="3.6"
|
||||
mkdocs-simple-hooks==0.1.3
|
||||
mkdocs-material-extensions==1.0.3;python_version>="3.6"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<img src="https://seleniumbase.io/cdn/img/super_logo_sb.png" title="SeleniumBase" width="320" />
|
||||
[<img src="https://seleniumbase.io/cdn/img/sb_logo_10t.png" title="SeleniumBase" width="220">](https://github.com/seleniumbase/SeleniumBase/)
|
||||
|
||||
<h2><img src="https://seleniumbase.io/img/logo6.png" title="SeleniumBase" width="32" /> Running Example Tests:</h2>
|
||||
|
||||
|
@ -169,7 +169,7 @@ python gui_test_runner.py
|
|||
|
||||
--------
|
||||
|
||||
<img src="https://seleniumbase.io/cdn/img/super_logo_sb4.png" title="SeleniumBase" width="320" />
|
||||
<img src="https://seleniumbase.io/cdn/img/super_logo_sb.png" title="SeleniumBase" width="320" />
|
||||
|
||||
<a href="https://github.com/seleniumbase/SeleniumBase">
|
||||
<img src="https://img.shields.io/badge/tested%20with-SeleniumBase-04C38E.svg" alt="Tested with SeleniumBase" /></a>
|
||||
|
|
|
@ -37,7 +37,7 @@ class DialogBoxTests(BaseCase):
|
|||
buttons = [(btn_text_1, "green"), (btn_text_2, "purple")]
|
||||
choice_2 = self.get_jqc_button_input(message, buttons)
|
||||
if choice_2 == btn_text_2:
|
||||
self.open("https://xkcd.com/1287/")
|
||||
self.open_if_not_url("https://xkcd.com/1287/")
|
||||
message = "Brain sports count as sports!<br /><br />"
|
||||
message += "Are you ready for more?"
|
||||
self.get_jqc_button_input(message, ["Let's Go!"])
|
||||
|
@ -77,14 +77,12 @@ class DialogBoxTests(BaseCase):
|
|||
self.set_jqc_theme("bootstrap", color="red", width="32%")
|
||||
if self.is_text_visible("No matching documents", ".md-search-result"):
|
||||
self.get_jqc_button_input("Your search had no results!", ["OK"])
|
||||
elif self.is_element_visible("a.md-search-result__link"):
|
||||
self.click("a.md-search-result__link")
|
||||
self.set_jqc_theme("bootstrap", color="green", width="32%")
|
||||
self.get_jqc_button_input("You found search results!", ["OK"])
|
||||
elif self.is_text_visible("Type to start searching", "div.md-search"):
|
||||
self.get_jqc_button_input("You did not do a search!", ["OK"])
|
||||
else:
|
||||
self.get_jqc_button_input("We're not sure what happened.", ["OK"])
|
||||
self.click_if_visible("a.md-search-result__link")
|
||||
self.set_jqc_theme("bootstrap", color="green", width="32%")
|
||||
self.get_jqc_button_input("You found search results!", ["OK"])
|
||||
|
||||
self.open("https://seleniumbase.io/help_docs/ReadMe/")
|
||||
self.highlight("h1")
|
||||
|
@ -99,19 +97,20 @@ class DialogBoxTests(BaseCase):
|
|||
message = "Now let's combine form inputs with multiple button options!"
|
||||
message += "<br /><br />"
|
||||
message += "Pick something to search. Then pick the site to search on."
|
||||
buttons = ["XKCD.com", "Wikipedia.org"]
|
||||
buttons = ["XKCD.com Store", "Wikipedia.org"]
|
||||
text, choice = self.get_jqc_form_inputs(message, buttons)
|
||||
if choice == "XKCD.com":
|
||||
self.open("https://relevant-xkcd.github.io/")
|
||||
if choice == "XKCD.com Store":
|
||||
self.open("https://store.xkcd.com/search")
|
||||
else:
|
||||
self.open("https://en.wikipedia.org/wiki/Main_Page")
|
||||
self.highlight_update_text('input[name="search"]', text + "\n")
|
||||
self.open("https://en.wikipedia.org/wiki/Special:Search")
|
||||
self.highlight_update_text('input[id*="search"]', text + "\n")
|
||||
self.wait_for_ready_state_complete()
|
||||
self.sleep(1)
|
||||
self.highlight("body")
|
||||
self.reset_jqc_theme()
|
||||
self.get_jqc_button_input("<b>Here are your results.</b>", ["OK"])
|
||||
message = "<h3>You've reached the end of this tutorial!</h3><br />"
|
||||
message += "Thanks for learning about SeleniumBase Dialog Boxes!<br />"
|
||||
message += "<br />Check out the SeleniumBase page on GitHub for more!"
|
||||
message += "Now you know about SeleniumBase Dialog Boxes!<br />"
|
||||
message += "<br />Check out SeleniumBase on GitHub for more!"
|
||||
self.set_jqc_theme("modern", color="purple", width="56%")
|
||||
self.get_jqc_button_input(message, ["Goodbye!"])
|
||||
|
|
|
@ -7,7 +7,7 @@ class GoogleTests(BaseCase):
|
|||
[
|
||||
["PyPI", "pypi.org", 'img[alt="PyPI"]'],
|
||||
["Wikipedia", "www.wikipedia.org", "img.central-featured-logo"],
|
||||
["SeleniumBase GitHub", "SeleniumBase - GitH", 'img[title*="Se"]'],
|
||||
["SeleniumBase GitHub", "Selenium, Python", 'img[title*="Selen"]'],
|
||||
]
|
||||
)
|
||||
def test_parameterized_google_search(self, search_key, expected_text, img):
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<h3 align="left"><img src="https://seleniumbase.io/cdn/img/sb_logo_b.png" alt="SeleniumBase" width="360" /></h3>
|
||||
[<img src="https://seleniumbase.io/cdn/img/sb_logo_10t.png" title="SeleniumBase" width="220">](https://github.com/seleniumbase/SeleniumBase/)
|
||||
|
||||
<h1> 📰 Presenter 📑 </h1>
|
||||
<h1> 📑 Presenter / Slides 🎞️ </h1>
|
||||
|
||||
<p>SeleniumBase Presenter lets you use Python to generate HTML presentations from Reveal JS.</p>
|
||||
<p>SeleniumBase Presenter / Slides lets you use Python to generate HTML presentations and slide shows from Reveal-JS.</p>
|
||||
|
||||
<b>Here's a sample presentation:</b>
|
||||
|
||||
|
@ -244,3 +244,7 @@ Presentations automatically get saved when calling:
|
|||
```python
|
||||
self.begin_presentation(show_notes=True)
|
||||
```
|
||||
|
||||
--------
|
||||
|
||||
<h3 align="left"><img src="https://seleniumbase.io/cdn/img/sb_logo_b.png" alt="SeleniumBase" width="240" /></h3>
|
||||
|
|
|
@ -3,20 +3,13 @@ from seleniumbase import BaseCase
|
|||
|
||||
class CheckboxTests(BaseCase):
|
||||
def test_checkboxes_and_radio_buttons(self):
|
||||
self.open(
|
||||
"https://www.w3schools.com/tags/tryit.asp"
|
||||
"?filename=tryhtml5_input_type_checkbox"
|
||||
)
|
||||
self.ad_block()
|
||||
self.open("https://seleniumbase.io/w3schools/checkboxes")
|
||||
self.switch_to_frame("iframeResult")
|
||||
checkbox = "input#vehicle2"
|
||||
self.assert_false(self.is_selected(checkbox))
|
||||
self.click(checkbox)
|
||||
self.assert_true(self.is_selected(checkbox))
|
||||
self.open(
|
||||
"https://www.w3schools.com/tags/tryit.asp"
|
||||
"?filename=tryhtml5_input_type_radio"
|
||||
)
|
||||
self.open("https://seleniumbase.io/w3schools/radio_buttons")
|
||||
self.switch_to_frame("iframeResult")
|
||||
option_button = "input#css"
|
||||
self.assert_false(self.is_selected(option_button))
|
||||
|
|
|
@ -3,21 +3,13 @@ from seleniumbase import BaseCase
|
|||
|
||||
class DoubleClickTests(BaseCase):
|
||||
def test_switch_to_frame_and_double_click(self):
|
||||
self.open(
|
||||
"https://www.w3schools.com/jsref"
|
||||
"/tryit.asp?filename=tryjsref_ondblclick"
|
||||
)
|
||||
self.ad_block()
|
||||
self.open("https://seleniumbase.io/w3schools/double_click")
|
||||
self.switch_to_frame("iframe#iframeResult")
|
||||
self.double_click('[ondblclick="myFunction()"]')
|
||||
self.assert_text("Hello World", "#demo")
|
||||
|
||||
def test_switch_to_frame_of_element_and_double_click(self):
|
||||
self.open(
|
||||
"https://www.w3schools.com/jsref"
|
||||
"/tryit.asp?filename=tryjsref_ondblclick"
|
||||
)
|
||||
self.ad_block()
|
||||
self.open("https://seleniumbase.io/w3schools/double_click")
|
||||
self.switch_to_frame_of_element('[ondblclick="myFunction()"]')
|
||||
self.double_click('[ondblclick="myFunction()"]')
|
||||
self.assert_text("Hello World", "#demo")
|
||||
|
|
|
@ -13,8 +13,7 @@ class DragAndDropTests(BaseCase):
|
|||
self.sleep(0.8)
|
||||
|
||||
def test_w3schools_drag_and_drop(self):
|
||||
url = "://w3schools.com/html/tryit.asp?filename=tryhtml5_draganddrop"
|
||||
self.open(url)
|
||||
self.open("https://seleniumbase.io/w3schools/drag_drop")
|
||||
self.remove_elements("#tryitLeaderboard")
|
||||
self.switch_to_frame("iframeResult")
|
||||
self.assert_element_not_visible("#div1 img#drag1")
|
||||
|
|
|
@ -3,23 +3,25 @@ from seleniumbase import BaseCase
|
|||
|
||||
class FrameTests(BaseCase):
|
||||
def test_iframe_basics(self):
|
||||
self.open(
|
||||
"https://www.w3schools.com/html/tryit.asp"
|
||||
"?filename=tryhtml_iframe_height_width_css"
|
||||
)
|
||||
self.ad_block() # Reduce noise during automation
|
||||
self.switch_to_frame("iframeResult") # Enter the iFrame
|
||||
self.open("https://seleniumbase.io/w3schools/iframes.html")
|
||||
self.switch_to_frame("iframeResult") # Enter the iframe
|
||||
self.assert_text("HTML Iframes", "h2")
|
||||
self.switch_to_frame('[title*="Iframe"]') # Enter iFrame inside iFrame
|
||||
self.switch_to_frame('[title*="Iframe"]') # Enter iframe inside iframe
|
||||
self.assert_text("This page is displayed in an iframe", "h1")
|
||||
self.switch_to_default_content() # Exit all iFrames
|
||||
self.switch_to_frame("iframeResult") # Go back inside 1st iFrame
|
||||
self.click("button#runbtn")
|
||||
self.switch_to_frame("iframeResult") # Go back inside 1st iframe
|
||||
self.highlight('iframe[title="Iframe Example"]')
|
||||
|
||||
def test_set_content_to_frame(self):
|
||||
self.open(
|
||||
"https://www.w3schools.com/html/tryit.asp"
|
||||
"?filename=tryhtml_iframe_height_width_css"
|
||||
)
|
||||
self.open("https://seleniumbase.io/w3schools/iframes.html")
|
||||
self.set_content_to_frame("iframeResult")
|
||||
self.highlight('iframe[title="Iframe Example"]', loops=8)
|
||||
self.highlight('iframe[title="Iframe Example"]')
|
||||
self.set_content_to_frame("iframe")
|
||||
self.assert_element_not_visible('iframe')
|
||||
self.highlight("body")
|
||||
self.set_content_to_default(nested=False)
|
||||
self.highlight('iframe[title="Iframe Example"]')
|
||||
self.set_content_to_default()
|
||||
self.click("button#runbtn")
|
||||
self.highlight("#iframeResult")
|
||||
|
|
|
@ -6,10 +6,7 @@ from seleniumbase import BaseCase
|
|||
|
||||
class FileUploadButtonTests(BaseCase):
|
||||
def test_file_upload_button(self):
|
||||
self.open(
|
||||
"https://www.w3schools.com/jsref/tryit.asp"
|
||||
"?filename=tryjsref_fileupload_get"
|
||||
)
|
||||
self.open("https://seleniumbase.io/w3schools/file_upload")
|
||||
self.set_content_to_frame("iframeResult")
|
||||
zoom_in = 'input[type="file"]{zoom: 1.6;-moz-transform: scale(1.6);}'
|
||||
self.add_css_style(zoom_in)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[<img src="https://seleniumbase.io/cdn/img/super_logo_sb.png" title="SeleniumBase" width="320">](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md)
|
||||
[<img src="https://seleniumbase.io/cdn/img/sb_logo_10t.png" title="SeleniumBase" width="220">](https://github.com/seleniumbase/SeleniumBase/)
|
||||
|
||||
# pytest CLI Options
|
||||
|
||||
|
|
|
@ -199,6 +199,8 @@ self.switch_to_default_content()
|
|||
|
||||
self.set_content_to_frame(frame, timeout=None)
|
||||
|
||||
self.set_content_to_default(nested=True)
|
||||
|
||||
self.open_new_window(switch_to=True)
|
||||
|
||||
self.switch_to_window(window, timeout=None)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
[<img src="https://seleniumbase.io/cdn/img/sb_logo_10t.png" title="SeleniumBase" width="260">](https://github.com/seleniumbase/SeleniumBase/blob/master/README.md)
|
||||
[<img src="https://seleniumbase.io/cdn/img/sb_logo_10t.png" title="SeleniumBase" width="220">](https://github.com/seleniumbase/SeleniumBase/)
|
||||
|
||||
<h2><img src="https://seleniumbase.io/img/logo6.png" title="SeleniumBase" width="32" /> Recorder Mode</h2>
|
||||
|
||||
|
@ -21,7 +21,14 @@ import ipdb; ipdb.set_trace()
|
|||
🔴 Once you've reached the breakpoint, you can take control of the browser and add in any actions that you want recorded. When you are finished recording, type "``c``" on the command-line and press ``[Enter]`` to let the test continue from the breakpoint. After the test completes, a file called ``TEST_NAME_rec.py`` will be automatically created in the ``./recordings`` folder, which will include the actions performed by the test, and the manual actions that you added in. Below is a command-line notification:
|
||||
|
||||
```bash
|
||||
>>> RECORDING saved to: recordings/my_first_test_rec.py
|
||||
>>> RECORDING SAVED as: recordings/my_first_test_rec.py
|
||||
*******************************************************
|
||||
```
|
||||
|
||||
If a Python file contains more tests, they'll be added:
|
||||
|
||||
```bash
|
||||
>>> RECORDING ADDED to: recordings/my_first_test_rec.py
|
||||
*******************************************************
|
||||
```
|
||||
|
||||
|
@ -49,6 +56,8 @@ class RecorderTest(BaseCase):
|
|||
|
||||
<p>🔴 SeleniumBase <code>1.66.1</code> adds the ability to record changes to <i>"Choose File"</i> <code>input</code> fields. Sometimes the <i>"Choose File"</i> input field is hidden on websites, so <code>self.show_file_choosers()</code> was added to get around this edge case. Version <code>1.66.1</code> also adds <code>self.set_content_to_frame(frame)</code>, which lets you record actions inside of iframes.</p>
|
||||
|
||||
<p>🔴 SeleniumBase <code>1.66.2</code> adds the ability to save selectors using the <code>":contains(TEXT)"</code> selector. If a Python file being recorded has multiple tests being run, then all those tests will get saved to the generated "*_rec.py" file. In order to escape iframes when using <code>self.set_content_to_frame(frame)</code>, a new method was added: <code>self.set_content_to_default()</code>. The <code>self.set_content_to_*()</code> methods will be automatically used in place of <code>self.switch_to_*()</code> methods in Recorder Mode, unless a test explicitly calls <code>self._rec_overrides_switch = False</code> before the <code>self.switch_to_*()</code> methods are called. Additionally, if an iframe contains the <code>src</code> attribute, that page will get loaded in a new tab when switching to it in Recorder Mode.</p>
|
||||
|
||||
--------
|
||||
|
||||
<div>To learn more about SeleniumBase, check out the Docs Site:</div>
|
||||
|
|
|
@ -39,7 +39,7 @@ theme:
|
|||
static_templates:
|
||||
- 404.html
|
||||
features:
|
||||
- search.highlight
|
||||
# - search.highlight
|
||||
- toc.integrate
|
||||
- navigation.indexes
|
||||
# - navigation.sections
|
||||
|
|
|
@ -5,7 +5,7 @@ packaging>=21.0;python_version>="3.6"
|
|||
typing-extensions>=3.10.0.2
|
||||
setuptools>=44.1.1;python_version<"3.5"
|
||||
setuptools>=50.3.2;python_version>="3.5" and python_version<"3.6"
|
||||
setuptools>=58.0.4;python_version>="3.6"
|
||||
setuptools>=58.1.0;python_version>="3.6"
|
||||
setuptools-scm==5.0.2;python_version<"3.6"
|
||||
setuptools-scm>=6.3.2;python_version>="3.6"
|
||||
tomli>=1.2.1;python_version>="3.6"
|
||||
|
@ -27,7 +27,7 @@ idna==3.2;python_version>="3.6"
|
|||
chardet==3.0.4;python_version<"3.5"
|
||||
chardet==4.0.0;python_version>="3.5"
|
||||
charset-normalizer==2.0.6;python_version>="3.5"
|
||||
urllib3==1.26.6
|
||||
urllib3==1.26.7
|
||||
requests==2.26.0;python_version<"3.5"
|
||||
requests==2.25.1;python_version>="3.5" and python_version<"3.6"
|
||||
requests==2.26.0;python_version>="3.6"
|
||||
|
@ -83,15 +83,15 @@ decorator==5.1.0;python_version>="3.5"
|
|||
ipython==5.10.0;python_version<"3.5"
|
||||
ipython==7.9.0;python_version>="3.5" and python_version<"3.6"
|
||||
ipython==7.16.1;python_version>="3.6" and python_version<"3.7"
|
||||
ipython==7.27.0;python_version>="3.7"
|
||||
ipython==7.28.0;python_version>="3.7"
|
||||
matplotlib-inline==0.1.3;python_version>="3.7"
|
||||
colorama==0.4.4
|
||||
platformdirs==2.0.2;python_version<"3.6"
|
||||
platformdirs==2.3.0;python_version>="3.6"
|
||||
platformdirs==2.4.0;python_version>="3.6"
|
||||
pathlib2==2.3.5;python_version<"3.5"
|
||||
importlib-metadata==2.0.0;python_version<"3.5"
|
||||
importlib-metadata==2.1.1;python_version>="3.5" and python_version<"3.6"
|
||||
virtualenv>=20.8.0
|
||||
virtualenv>=20.8.1
|
||||
pymysql==0.10.1;python_version<"3.6"
|
||||
pymysql==1.0.2;python_version>="3.6"
|
||||
pyotp==2.6.0
|
||||
|
@ -101,7 +101,7 @@ toml==0.10.2
|
|||
Pillow==6.2.2;python_version<"3.5"
|
||||
Pillow==7.2.0;python_version>="3.5" and python_version<"3.6"
|
||||
Pillow==8.3.2;python_version>="3.6"
|
||||
rich==10.10.0;python_version>="3.6" and python_version<"4.0"
|
||||
rich==10.11.0;python_version>="3.6" and python_version<"4.0"
|
||||
tornado==5.1.1;python_version<"3.5"
|
||||
tornado==6.1;python_version>="3.5"
|
||||
pdfminer.six==20191110;python_version<"3.5"
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
# seleniumbase package
|
||||
__version__ = "1.66.1"
|
||||
__version__ = "1.66.2"
|
||||
|
|
|
@ -635,9 +635,10 @@ def show_options():
|
|||
op += " | return / r: Run until method returns. j: Jump to line. |\n"
|
||||
op += " | where / w: Show stack spot. u: Up stack. d: Down stack. |\n"
|
||||
op += " | longlist / ll: See code. dir(): List namespace objects. |\n"
|
||||
op += "--recorder (Record browser actions to generate test scripts.)\n"
|
||||
op += "--save-screenshot (Save a screenshot at the end of each test.)\n"
|
||||
op += "-x (Stop running the tests after the first failure is reached.)\n"
|
||||
op += "--archive-logs (Archive old log files instead of deleting them.)\n"
|
||||
op += "--save-screenshot (Save a screenshot at the end of each test.)\n"
|
||||
op += "--check-js (Check for JavaScript errors after page loads.)\n"
|
||||
op += "--start-page=URL (The browser start page when tests begin.)\n"
|
||||
op += "--agent=STRING (Modify the web browser's User-Agent string.)\n"
|
||||
|
|
|
@ -28,6 +28,7 @@ if not os.environ["PATH"].startswith(DRIVER_DIR):
|
|||
os.environ["PATH"] = DRIVER_DIR + os.pathsep + os.environ["PATH"]
|
||||
EXTENSIONS_DIR = os.path.dirname(os.path.realpath(extensions.__file__))
|
||||
DISABLE_CSP_ZIP_PATH = "%s/%s" % (EXTENSIONS_DIR, "disable_csp.zip")
|
||||
AD_BLOCK_ZIP_PATH = "%s/%s" % (EXTENSIONS_DIR, "ad_block.zip")
|
||||
RECORDER_ZIP_PATH = "%s/%s" % (EXTENSIONS_DIR, "recorder.zip")
|
||||
PROXY_ZIP_PATH = proxy_helper.PROXY_ZIP_PATH
|
||||
PROXY_ZIP_PATH_2 = proxy_helper.PROXY_ZIP_PATH_2
|
||||
|
@ -199,8 +200,17 @@ def _add_chrome_disable_csp_extension(chrome_options):
|
|||
return chrome_options
|
||||
|
||||
|
||||
def _add_chrome_ad_block_extension(chrome_options):
|
||||
"""Block Ads on Chromium Browsers with a browser extension.
|
||||
See https://github.com/slingamn/simpleblock for details."""
|
||||
ad_block_zip = AD_BLOCK_ZIP_PATH
|
||||
chrome_options.add_extension(ad_block_zip)
|
||||
return chrome_options
|
||||
|
||||
|
||||
def _add_chrome_recorder_extension(chrome_options):
|
||||
"""The SeleniumBase Recorder Chromium extension."""
|
||||
"""The SeleniumBase Recorder Chrome/Edge extension.
|
||||
https://seleniumbase.io/help_docs/recorder_mode/"""
|
||||
recorder_zip = RECORDER_ZIP_PATH
|
||||
chrome_options.add_extension(recorder_zip)
|
||||
return chrome_options
|
||||
|
@ -228,6 +238,7 @@ def _set_chrome_options(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
user_data_dir,
|
||||
|
@ -322,10 +333,10 @@ def _set_chrome_options(
|
|||
chrome_options.add_experimental_option(
|
||||
"mobileEmulation", emulator_settings
|
||||
)
|
||||
chrome_options.add_argument("--enable-sync")
|
||||
if (
|
||||
not proxy_auth
|
||||
and not disable_csp
|
||||
and not ad_block_on
|
||||
and not recorder_ext
|
||||
and (not extension_zip and not extension_dir)
|
||||
):
|
||||
|
@ -391,7 +402,8 @@ def _set_chrome_options(
|
|||
# Headless Chrome doesn't support extensions, which are required
|
||||
# for disabling the Content Security Policy on Chrome
|
||||
chrome_options = _add_chrome_disable_csp_extension(chrome_options)
|
||||
chrome_options.add_argument("--enable-sync")
|
||||
if ad_block_on and not headless:
|
||||
chrome_options = _add_chrome_ad_block_extension(chrome_options)
|
||||
if recorder_ext and not headless:
|
||||
chrome_options = _add_chrome_recorder_extension(chrome_options)
|
||||
if proxy_string:
|
||||
|
@ -687,6 +699,7 @@ def get_driver(
|
|||
devtools=None,
|
||||
remote_debug=None,
|
||||
swiftshader=None,
|
||||
ad_block_on=None,
|
||||
block_images=None,
|
||||
chromium_arg=None,
|
||||
firefox_arg=None,
|
||||
|
@ -760,6 +773,7 @@ def get_driver(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
firefox_arg,
|
||||
|
@ -796,6 +810,7 @@ def get_driver(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
firefox_arg,
|
||||
|
@ -836,6 +851,7 @@ def get_remote_driver(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
firefox_arg,
|
||||
|
@ -900,6 +916,7 @@ def get_remote_driver(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
user_data_dir,
|
||||
|
@ -1053,6 +1070,7 @@ def get_local_driver(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
firefox_arg,
|
||||
|
@ -1220,6 +1238,7 @@ def get_local_driver(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
user_data_dir,
|
||||
|
@ -1307,7 +1326,6 @@ def get_local_driver(
|
|||
edge_options.add_experimental_option(
|
||||
"mobileEmulation", emulator_settings
|
||||
)
|
||||
edge_options.add_argument("--enable-sync")
|
||||
if user_data_dir:
|
||||
abs_path = os.path.abspath(user_data_dir)
|
||||
edge_options.add_argument("user-data-dir=%s" % abs_path)
|
||||
|
@ -1342,10 +1360,10 @@ def get_local_driver(
|
|||
# Headless Edge doesn't support extensions, which are required
|
||||
# for disabling the Content Security Policy on Edge
|
||||
edge_options = _add_chrome_disable_csp_extension(edge_options)
|
||||
edge_options.add_argument("--enable-sync")
|
||||
if ad_block_on and not headless:
|
||||
edge_options = _add_chrome_ad_block_extension(edge_options)
|
||||
if recorder_ext and not headless:
|
||||
edge_options = _add_chrome_recorder_extension(edge_options)
|
||||
edge_options.add_argument("--enable-sync")
|
||||
if proxy_string:
|
||||
if proxy_auth:
|
||||
edge_options = _add_chrome_proxy_extension(
|
||||
|
@ -1431,6 +1449,7 @@ def get_local_driver(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
user_data_dir,
|
||||
|
@ -1475,6 +1494,7 @@ def get_local_driver(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
user_data_dir,
|
||||
|
@ -1556,6 +1576,7 @@ def get_local_driver(
|
|||
devtools,
|
||||
remote_debug,
|
||||
swiftshader,
|
||||
ad_block_on,
|
||||
block_images,
|
||||
chromium_arg,
|
||||
user_data_dir,
|
||||
|
|
|
@ -3,5 +3,6 @@
|
|||
<h2><img src="https://seleniumbase.io/img/logo6.png" title="SeleniumBase" width="30" /> SeleniumBase browser extension storage</h2>
|
||||
|
||||
**The List:**
|
||||
* ad_block.zip => This extension blocks ad content from appearing on any website.
|
||||
* disable_csp.zip => This extension disables a website's Content-Security-Policy.
|
||||
* recorder.zip => Save browser actions to sessionStorage with good CSS selectors.
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -78,6 +78,10 @@ class BaseCase(unittest.TestCase):
|
|||
self.driver = None
|
||||
self.environment = None
|
||||
self.env = None # Add a shortened version of self.environment
|
||||
self.__page_sources = []
|
||||
self.__extra_actions = []
|
||||
self.__js_start_time = 0
|
||||
self.__set_c_from_switch = False
|
||||
self.__called_setup = False
|
||||
self.__called_teardown = False
|
||||
self.__start_time_ms = None
|
||||
|
@ -108,6 +112,7 @@ class BaseCase(unittest.TestCase):
|
|||
self._language = "English"
|
||||
self._presentation_slides = {}
|
||||
self._presentation_transition = {}
|
||||
self._rec_overrides_switch = True # Recorder-Mode uses set_c vs switch
|
||||
self._sb_test_identifier = None
|
||||
self._html_report_extra = [] # (Used by pytest_plugin.py)
|
||||
self._default_driver = None
|
||||
|
@ -200,6 +205,7 @@ class BaseCase(unittest.TestCase):
|
|||
if scroll and not self.demo_mode and not self.slow_mode:
|
||||
self.__scroll_to_element(element, selector, by)
|
||||
pre_action_url = self.driver.current_url
|
||||
pre_window_count = len(self.driver.window_handles)
|
||||
try:
|
||||
if self.browser == "ie" and by == By.LINK_TEXT:
|
||||
# An issue with clicking Link Text on IE means using jquery
|
||||
|
@ -310,6 +316,10 @@ class BaseCase(unittest.TestCase):
|
|||
self.driver, selector, by, timeout=timeout
|
||||
)
|
||||
element.click()
|
||||
if self.recorder_mode:
|
||||
latest_window_count = len(self.driver.window_handles)
|
||||
if latest_window_count > pre_window_count:
|
||||
self.switch_to_newest_window()
|
||||
if settings.WAIT_FOR_RSC_ON_CLICKS:
|
||||
self.wait_for_ready_state_complete()
|
||||
if self.demo_mode:
|
||||
|
@ -2238,6 +2248,23 @@ class BaseCase(unittest.TestCase):
|
|||
self.scroll_to(frame, timeout=1)
|
||||
except Exception:
|
||||
pass
|
||||
if self.recorder_mode and self._rec_overrides_switch:
|
||||
url = self.get_current_url()
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
r_a = self.get_session_storage_item("recorder_activated")
|
||||
if r_a == "yes":
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["sk_op", "", "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
self.__set_c_from_switch = True
|
||||
self.set_content_to_frame(frame, timeout=timeout)
|
||||
self.__set_c_from_switch = False
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
origin = self.get_origin()
|
||||
action = ["sw_fr", frame, origin, time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
return
|
||||
page_actions.switch_to_frame(self.driver, frame, timeout)
|
||||
|
||||
def switch_to_default_content(self):
|
||||
|
@ -2246,14 +2273,162 @@ class BaseCase(unittest.TestCase):
|
|||
will be set to one level above the current frame. If the driver
|
||||
control is not currently in an iframe, nothing will happen.)"""
|
||||
self.__check_scope()
|
||||
if self.recorder_mode and self._rec_overrides_switch:
|
||||
url = self.get_current_url()
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
r_a = self.get_session_storage_item("recorder_activated")
|
||||
if r_a == "yes":
|
||||
self.__set_c_from_switch = True
|
||||
self.set_content_to_default()
|
||||
self.__set_c_from_switch = False
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
origin = self.get_origin()
|
||||
action = ["sw_dc", "", origin, time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
return
|
||||
self.driver.switch_to.default_content()
|
||||
|
||||
def set_content_to_frame(self, frame, timeout=None):
|
||||
"""Replaces the page html with an iframe's html from that page."""
|
||||
self.switch_to_frame(frame, timeout=timeout)
|
||||
"""Replaces the page html with an iframe's html from that page.
|
||||
If the iFrame contains an "src" field that includes a valid URL,
|
||||
then instead of replacing the current html, this method will then
|
||||
open up the "src" URL of the iFrame in a new browser tab.
|
||||
To return to default content, use: self.set_content_to_default().
|
||||
This method also sets the state of the browser window so that the
|
||||
self.set_content_to_default() method can bring the user back to
|
||||
the original content displayed, which is similar to how the methods
|
||||
self.switch_to_frame(frame) and self.switch_to_default_content()
|
||||
work together to get the user into frames and out of all of them.
|
||||
"""
|
||||
self.__check_scope()
|
||||
if not timeout:
|
||||
timeout = settings.SMALL_TIMEOUT
|
||||
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
||||
timeout = self.__get_new_timeout(timeout)
|
||||
current_url = self.get_current_url()
|
||||
c_tab = self.driver.current_window_handle
|
||||
current_page_source = self.get_page_source()
|
||||
self.execute_script("document.cframe_swap = 0;")
|
||||
page_actions.switch_to_frame(self.driver, frame, timeout)
|
||||
iframe_html = self.get_page_source()
|
||||
self.switch_to_default_content()
|
||||
self.set_content(iframe_html)
|
||||
self.driver.switch_to.default_content()
|
||||
self.wait_for_ready_state_complete()
|
||||
frame_found = False
|
||||
o_frame = frame
|
||||
if self.is_element_present(frame):
|
||||
frame_found = True
|
||||
elif " " not in frame:
|
||||
frame = 'iframe[name="%s"]' % frame
|
||||
if self.is_element_present(frame):
|
||||
frame_found = True
|
||||
url = None
|
||||
if frame_found:
|
||||
url = self.execute_script(
|
||||
"""return document.querySelector('%s').src;""" % frame
|
||||
)
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
pass
|
||||
else:
|
||||
url = None
|
||||
cframe_tab = False
|
||||
if url:
|
||||
cframe_tab = True
|
||||
self.__page_sources.append([current_url, current_page_source, c_tab])
|
||||
|
||||
if self.recorder_mode and not self.__set_c_from_switch:
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["sk_op", "", "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
|
||||
if cframe_tab:
|
||||
self.execute_script("document.cframe_tab = 1;")
|
||||
self.open_new_window(switch_to=True)
|
||||
self.open(url)
|
||||
self.execute_script("document.cframe_tab = 1;")
|
||||
else:
|
||||
self.set_content(iframe_html)
|
||||
if not self.execute_script("return document.cframe_swap;"):
|
||||
self.execute_script("document.cframe_swap = 1;")
|
||||
else:
|
||||
self.execute_script("document.cframe_swap += 1;")
|
||||
|
||||
if self.recorder_mode and not self.__set_c_from_switch:
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["s_c_f", o_frame, "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
|
||||
def set_content_to_default(self, nested=True):
|
||||
"""After using self.set_content_to_frame(), this reverts the page back.
|
||||
If self.set_content_to_frame() hasn't been called here, only refreshes.
|
||||
If "nested" is set to False when the content was set to nested iFrames,
|
||||
then the control will only move above the last iFrame that was entered.
|
||||
"""
|
||||
self.__check_scope()
|
||||
swap_cnt = self.execute_script("return document.cframe_swap;")
|
||||
tab_sta = self.execute_script("return document.cframe_tab;")
|
||||
|
||||
if self.recorder_mode and not self.__set_c_from_switch:
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["sk_op", "", "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
|
||||
if nested:
|
||||
if (
|
||||
len(self.__page_sources) > 0
|
||||
and (
|
||||
(swap_cnt and int(swap_cnt) > 0)
|
||||
or (tab_sta and int(tab_sta) > 0)
|
||||
)
|
||||
):
|
||||
past_content = self.__page_sources[0]
|
||||
past_url = past_content[0]
|
||||
past_source = past_content[1]
|
||||
past_tab = past_content[2]
|
||||
current_tab = self.driver.current_window_handle
|
||||
if not current_tab == past_tab:
|
||||
if past_tab in self.driver.window_handles:
|
||||
self.switch_to_window(past_tab)
|
||||
url_of_past_tab = self.get_current_url()
|
||||
if url_of_past_tab == past_url:
|
||||
self.set_content(past_source)
|
||||
else:
|
||||
self.refresh_page()
|
||||
else:
|
||||
self.refresh_page()
|
||||
self.execute_script("document.cframe_swap = 0;")
|
||||
self.__page_sources = []
|
||||
else:
|
||||
just_refresh = False
|
||||
if swap_cnt and int(swap_cnt) > 0 and len(self.__page_sources) > 0:
|
||||
self.execute_script("document.cframe_swap -= 1;")
|
||||
current_url = self.get_current_url()
|
||||
past_content = self.__page_sources.pop()
|
||||
past_url = past_content[0]
|
||||
past_source = past_content[1]
|
||||
if current_url == past_url:
|
||||
self.set_content(past_source)
|
||||
else:
|
||||
just_refresh = True
|
||||
elif tab_sta and int(tab_sta) > 0 and len(self.__page_sources) > 0:
|
||||
past_content = self.__page_sources.pop()
|
||||
past_tab = past_content[2]
|
||||
if past_tab in self.driver.window_handles:
|
||||
self.switch_to_window(past_tab)
|
||||
else:
|
||||
just_refresh = True
|
||||
else:
|
||||
just_refresh = True
|
||||
if just_refresh:
|
||||
self.refresh_page()
|
||||
self.execute_script("document.cframe_swap = 0;")
|
||||
self.__page_sources = []
|
||||
|
||||
if self.recorder_mode and not self.__set_c_from_switch:
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["s_c_d", nested, "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
|
||||
def open_new_window(self, switch_to=True):
|
||||
""" Opens a new browser tab/window and switches to it by default. """
|
||||
|
@ -2305,6 +2480,7 @@ class BaseCase(unittest.TestCase):
|
|||
devtools=None,
|
||||
remote_debug=None,
|
||||
swiftshader=None,
|
||||
ad_block_on=None,
|
||||
block_images=None,
|
||||
chromium_arg=None,
|
||||
firefox_arg=None,
|
||||
|
@ -2344,6 +2520,7 @@ class BaseCase(unittest.TestCase):
|
|||
devtools - the option to open Chrome's DevTools on start (Chrome)
|
||||
remote_debug - the option to enable Chrome's Remote Debugger
|
||||
swiftshader - the option to use Chrome's swiftshader (Chrome-only)
|
||||
ad_block_on - the option to block ads from loading (Chromium-only)
|
||||
block_images - the option to block images from loading (Chrome)
|
||||
chromium_arg - the option to add a Chromium arg to Chrome/Edge
|
||||
firefox_arg - the option to add a Firefox arg to Firefox runs
|
||||
|
@ -2429,6 +2606,8 @@ class BaseCase(unittest.TestCase):
|
|||
remote_debug = self.remote_debug
|
||||
if swiftshader is None:
|
||||
swiftshader = self.swiftshader
|
||||
if ad_block_on is None:
|
||||
ad_block_on = self.ad_block_on
|
||||
if block_images is None:
|
||||
block_images = self.block_images
|
||||
if chromium_arg is None:
|
||||
|
@ -2489,6 +2668,7 @@ class BaseCase(unittest.TestCase):
|
|||
devtools=devtools,
|
||||
remote_debug=remote_debug,
|
||||
swiftshader=swiftshader,
|
||||
ad_block_on=ad_block_on,
|
||||
block_images=block_images,
|
||||
chromium_arg=chromium_arg,
|
||||
firefox_arg=firefox_arg,
|
||||
|
@ -2579,6 +2759,7 @@ class BaseCase(unittest.TestCase):
|
|||
def switch_to_driver(self, driver):
|
||||
"""Sets self.driver to the specified driver.
|
||||
You may need this if using self.get_new_driver() in your code."""
|
||||
self.__check_scope()
|
||||
self.driver = driver
|
||||
if self.driver in self.__driver_browser_map:
|
||||
self.browser = self.__driver_browser_map[self.driver]
|
||||
|
@ -2715,13 +2896,11 @@ class BaseCase(unittest.TestCase):
|
|||
self.wait_for_angularjs(timeout=settings.MINI_TIMEOUT)
|
||||
if self.js_checking_on:
|
||||
self.assert_no_js_errors()
|
||||
if self.ad_block_on:
|
||||
# If the ad_block feature is enabled, then block ads for new URLs
|
||||
if self.ad_block_on and (self.headless or not self.is_chromium()):
|
||||
# For Chromium browsers in headed mode, the extension is used
|
||||
current_url = self.get_current_url()
|
||||
if not current_url == self.__last_page_load_url:
|
||||
time.sleep(0.02)
|
||||
self.ad_block()
|
||||
time.sleep(0.02)
|
||||
if self.is_element_present("iframe"):
|
||||
time.sleep(0.1) # iframe ads take slightly longer to load
|
||||
self.ad_block() # Do ad_block on slower-loading iframes
|
||||
|
@ -2842,13 +3021,27 @@ class BaseCase(unittest.TestCase):
|
|||
if action not in used_actions:
|
||||
used_actions.append(action)
|
||||
raw_actions.append(action)
|
||||
for action in self.__extra_actions:
|
||||
if action not in used_actions:
|
||||
used_actions.append(action)
|
||||
raw_actions.append(action)
|
||||
for action in raw_actions:
|
||||
# Use key because multiple actions can happen at the same timestamp
|
||||
if self._reuse_session:
|
||||
if int(action[3]) < int(self.__js_start_time):
|
||||
continue
|
||||
# Use key for sorting and preventing duplicates
|
||||
key = str(action[3]) + "-" + str(action[0])
|
||||
action_dict[key] = action
|
||||
for key in sorted(action_dict):
|
||||
# print(action_dict[key]) # For debugging purposes
|
||||
srt_actions.append(action_dict[key])
|
||||
for n in range(len(srt_actions)):
|
||||
if (
|
||||
(srt_actions[n][0] == "begin" or srt_actions[n][0] == "_url_")
|
||||
and n > 0
|
||||
and srt_actions[n-1][0] == "sk_op"
|
||||
):
|
||||
srt_actions[n][0] = "_skip"
|
||||
for n in range(len(srt_actions)):
|
||||
if (
|
||||
(srt_actions[n][0] == "begin" or srt_actions[n][0] == "_url_")
|
||||
|
@ -2880,6 +3073,25 @@ class BaseCase(unittest.TestCase):
|
|||
url2 = url2[:-1]
|
||||
if url1 == url2:
|
||||
srt_actions[n-1][0] = "_skip"
|
||||
for n in range(len(srt_actions)):
|
||||
if (
|
||||
(srt_actions[n][0] == "begin" or srt_actions[n][0] == "_url_")
|
||||
and n > 0
|
||||
and (
|
||||
srt_actions[n-1][0] == "click"
|
||||
or srt_actions[n-1][0] == "input"
|
||||
)
|
||||
and (int(srt_actions[n][3]) - int(srt_actions[n-1][3]) < 6500)
|
||||
):
|
||||
if srt_actions[n-1][0] == "click":
|
||||
if (
|
||||
srt_actions[n-1][1].startswith("input")
|
||||
or srt_actions[n-1][1].startswith("button")
|
||||
):
|
||||
srt_actions[n][0] = "f_url"
|
||||
elif srt_actions[n-1][0] == "input":
|
||||
if srt_actions[n-1][2].endswith("\n"):
|
||||
srt_actions[n][0] = "f_url"
|
||||
for n in range(len(srt_actions)):
|
||||
cleaned_actions.append(srt_actions[n])
|
||||
for action in srt_actions:
|
||||
|
@ -2888,94 +3100,172 @@ class BaseCase(unittest.TestCase):
|
|||
elif action[0] == "f_url":
|
||||
sb_actions.append('self.open_if_not_url("%s")' % action[2])
|
||||
elif action[0] == "click":
|
||||
method = "click"
|
||||
if '"' not in action[1]:
|
||||
sb_actions.append('self.click("%s")' % action[1])
|
||||
sb_actions.append('self.%s("%s")' % (method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.click('%s')" % action[1])
|
||||
sb_actions.append("self.%s('%s')" % (method, action[1]))
|
||||
elif action[0] == "input":
|
||||
method = "type"
|
||||
text = action[2].replace("\n", "\\n")
|
||||
if '"' not in action[1] and '"' not in text:
|
||||
sb_actions.append('self.type("%s", "%s")' % (
|
||||
action[1], text))
|
||||
sb_actions.append('self.%s("%s", "%s")' % (
|
||||
method, action[1], text))
|
||||
elif '"' not in action[1] and '"' in text:
|
||||
sb_actions.append('self.type("%s", \'%s\')' % (
|
||||
action[1], text))
|
||||
sb_actions.append('self.%s("%s", \'%s\')' % (
|
||||
method, action[1], text))
|
||||
elif '"' in action[1] and '"' not in text:
|
||||
sb_actions.append('self.type(\'%s\', "%s")' % (
|
||||
action[1], text))
|
||||
sb_actions.append('self.%s(\'%s\', "%s")' % (
|
||||
method, action[1], text))
|
||||
elif '"' in action[1] and '"' in text:
|
||||
sb_actions.append("self.type('%s', '%s')" % (
|
||||
action[1], text))
|
||||
sb_actions.append("self.%s('%s', '%s')" % (
|
||||
method, action[1], text))
|
||||
elif action[0] == "h_clk":
|
||||
method = "hover_and_click"
|
||||
if '"' not in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.hover_and_click("%s", "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' not in action[1] and '"' in action[2]:
|
||||
sb_actions.append('self.hover_and_click("%s", \'%s\')' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", \'%s\')' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.hover_and_click(\'%s\', "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s(\'%s\', "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' in action[2]:
|
||||
sb_actions.append("self.hover_and_click('%s', '%s')" % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append("self.%s('%s', '%s')" % (
|
||||
method, action[1], action[2]))
|
||||
elif action[0] == "ddrop":
|
||||
method = "drag_and_drop"
|
||||
if '"' not in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.drag_and_drop("%s", "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' not in action[1] and '"' in action[2]:
|
||||
sb_actions.append('self.drag_and_drop("%s", \'%s\')' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", \'%s\')' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.drag_and_drop(\'%s\', "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s(\'%s\', "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' in action[2]:
|
||||
sb_actions.append("self.drag_and_drop('%s', '%s')" % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append("self.%s('%s', '%s')" % (
|
||||
method, action[1], action[2]))
|
||||
elif action[0] == "s_opt":
|
||||
method = "select_option_by_text"
|
||||
if '"' not in action[1] and '"' not in action[2]:
|
||||
sb_actions.append(
|
||||
'self.select_option_by_text("%s", "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' not in action[1] and '"' in action[2]:
|
||||
sb_actions.append(
|
||||
'self.select_option_by_text("%s", \'%s\')' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", \'%s\')' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' not in action[2]:
|
||||
sb_actions.append(
|
||||
'self.select_option_by_text(\'%s\', "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s(\'%s\', "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' in action[2]:
|
||||
sb_actions.append(
|
||||
"self.select_option_by_text('%s', '%s')" % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append("self.%s('%s', '%s')" % (
|
||||
method, action[1], action[2]))
|
||||
elif action[0] == "set_v":
|
||||
method = "set_value"
|
||||
if '"' not in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.set_value("%s", "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' not in action[1] and '"' in action[2]:
|
||||
sb_actions.append('self.set_value("%s", \'%s\')' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", \'%s\')' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.set_value(\'%s\', "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s(\'%s\', "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' in action[2]:
|
||||
sb_actions.append("self.set_value('%s', '%s')" % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append("self.%s('%s', '%s')" % (
|
||||
method, action[1], action[2]))
|
||||
elif action[0] == "cho_f":
|
||||
method = "choose_file"
|
||||
action[2] = action[2].replace("\\", "\\\\")
|
||||
if '"' not in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.choose_file("%s", "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' not in action[1] and '"' in action[2]:
|
||||
sb_actions.append('self.choose_file("%s", \'%s\')' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s("%s", \'%s\')' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.choose_file(\'%s\', "%s")' % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append('self.%s(\'%s\', "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' in action[2]:
|
||||
sb_actions.append("self.choose_file('%s', '%s')" % (
|
||||
action[1], action[2]))
|
||||
sb_actions.append("self.%s('%s', '%s')" % (
|
||||
method, action[1], action[2]))
|
||||
elif action[0] == "sw_fr":
|
||||
method = "switch_to_frame"
|
||||
if '"' not in action[1]:
|
||||
sb_actions.append('self.%s("%s")' % (method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.%s('%s')" % (method, action[1]))
|
||||
elif action[0] == "sw_dc":
|
||||
sb_actions.append("self.switch_to_default_content()")
|
||||
elif action[0] == "s_c_f":
|
||||
method = "set_content_to_frame"
|
||||
if '"' not in action[1]:
|
||||
sb_actions.append('self.%s("%s")' % (method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.%s('%s')" % (method, action[1]))
|
||||
elif action[0] == "s_c_d":
|
||||
method = "set_content_to_default"
|
||||
nested = action[1]
|
||||
if nested:
|
||||
sb_actions.append("self.%s()" % method)
|
||||
else:
|
||||
sb_actions.append("self.%s(nested=False)" % method)
|
||||
elif action[0] == "as_el":
|
||||
method = "assert_element"
|
||||
if '"' not in action[1]:
|
||||
sb_actions.append('self.%s("%s")' % (method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.%s('%s')" % (method, action[1]))
|
||||
elif action[0] == "as_ep":
|
||||
method = "assert_element_present"
|
||||
if '"' not in action[1]:
|
||||
sb_actions.append('self.%s("%s")' % (method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.%s('%s')" % (method, action[1]))
|
||||
elif action[0] == "asenv":
|
||||
method = "assert_element_not_visible"
|
||||
if '"' not in action[1]:
|
||||
sb_actions.append('self.%s("%s")' % (method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.%s('%s')" % (method, action[1]))
|
||||
elif action[0] == "as_lt":
|
||||
method = "assert_link_text"
|
||||
if '"' not in action[1]:
|
||||
sb_actions.append('self.%s("%s")' % (method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.%s('%s')" % (method, action[1]))
|
||||
elif action[0] == "as_ti":
|
||||
method = "assert_title"
|
||||
if '"' not in action[1]:
|
||||
sb_actions.append('self.%s("%s")' % (method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.%s('%s')" % (method, action[1]))
|
||||
elif action[0] == "as_te" or action[0] == "as_et":
|
||||
method = "assert_text"
|
||||
if action[0] == "as_et":
|
||||
method = "assert_exact_text"
|
||||
if action[2] != "html":
|
||||
if '"' not in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.%s("%s", "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' not in action[1] and '"' in action[2]:
|
||||
sb_actions.append('self.%s("%s", \'%s\')' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' not in action[2]:
|
||||
sb_actions.append('self.%s(\'%s\', "%s")' % (
|
||||
method, action[1], action[2]))
|
||||
elif '"' in action[1] and '"' in action[2]:
|
||||
sb_actions.append("self.%s('%s', '%s')" % (
|
||||
method, action[1], action[2]))
|
||||
else:
|
||||
if '"' not in action[1]:
|
||||
sb_actions.append('self.%s("%s")' % (
|
||||
method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.%s('%s')" % (
|
||||
method, action[1]))
|
||||
elif action[0] == "c_box":
|
||||
cb_method = "check_if_unchecked"
|
||||
if action[2] == "no":
|
||||
|
@ -2984,11 +3274,19 @@ class BaseCase(unittest.TestCase):
|
|||
sb_actions.append('self.%s("%s")' % (cb_method, action[1]))
|
||||
else:
|
||||
sb_actions.append("self.%s('%s')" % (cb_method, action[1]))
|
||||
|
||||
filename = self.__get_filename()
|
||||
new_file = False
|
||||
data = []
|
||||
data.append("from seleniumbase import BaseCase")
|
||||
data.append("")
|
||||
data.append("")
|
||||
data.append("class %s(BaseCase):" % self.__class__.__name__)
|
||||
if filename not in sb_config._recorded_actions:
|
||||
new_file = True
|
||||
sb_config._recorded_actions[filename] = []
|
||||
data.append("from seleniumbase import BaseCase")
|
||||
data.append("")
|
||||
data.append("")
|
||||
data.append("class %s(BaseCase):" % self.__class__.__name__)
|
||||
else:
|
||||
data = sb_config._recorded_actions[filename]
|
||||
data.append(" def %s(self):" % self._testMethodName)
|
||||
if len(sb_actions) > 0:
|
||||
for action in sb_actions:
|
||||
|
@ -2996,6 +3294,7 @@ class BaseCase(unittest.TestCase):
|
|||
else:
|
||||
data.append(" pass")
|
||||
data.append("")
|
||||
sb_config._recorded_actions[filename] = data
|
||||
|
||||
recordings_folder = constants.Recordings.SAVED_FOLDER
|
||||
if recordings_folder.endswith("/"):
|
||||
|
@ -3011,7 +3310,9 @@ class BaseCase(unittest.TestCase):
|
|||
out_file = codecs.open(file_path, "w+", "utf-8")
|
||||
out_file.writelines("\r\n".join(data))
|
||||
out_file.close()
|
||||
rec_message = ">>> RECORDING saved to: "
|
||||
rec_message = ">>> RECORDING SAVED as: "
|
||||
if not new_file:
|
||||
rec_message = ">>> RECORDING ADDED to: "
|
||||
star_len = len(rec_message) + len(file_path)
|
||||
try:
|
||||
terminal_size = os.get_terminal_size().columns
|
||||
|
@ -4241,6 +4542,15 @@ class BaseCase(unittest.TestCase):
|
|||
a_t = SD.translate_assert_title(self._language)
|
||||
messenger_post = "%s: {%s}" % (a_t, title)
|
||||
self.__highlight_with_assert_success(messenger_post, "html")
|
||||
if self.recorder_mode:
|
||||
url = self.get_current_url()
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
if self.get_session_storage_item("pause_recorder") == "no":
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["as_ti", title, "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
return True
|
||||
|
||||
def assert_no_js_errors(self):
|
||||
"""Asserts that there are no JavaScript "SEVERE"-level page errors.
|
||||
|
@ -7746,6 +8056,14 @@ class BaseCase(unittest.TestCase):
|
|||
self.__assert_shadow_element_present(selector)
|
||||
return True
|
||||
self.wait_for_element_present(selector, by=by, timeout=timeout)
|
||||
if self.recorder_mode:
|
||||
url = self.get_current_url()
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
if self.get_session_storage_item("pause_recorder") == "no":
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["as_ep", selector, "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
return True
|
||||
|
||||
def assert_elements_present(self, *args, **kwargs):
|
||||
|
@ -7773,9 +8091,10 @@ class BaseCase(unittest.TestCase):
|
|||
if type(selector) is str:
|
||||
selectors.append(selector)
|
||||
elif type(selector) is list:
|
||||
for a_selector in selector:
|
||||
if type(a_selector) is str:
|
||||
selectors.append(a_selector)
|
||||
selectors_list = selector
|
||||
for selector in selectors_list:
|
||||
if type(selector) is str:
|
||||
selectors.append(selector)
|
||||
else:
|
||||
raise Exception('Unknown kwarg: "%s"!' % kwarg)
|
||||
if not timeout:
|
||||
|
@ -7833,6 +8152,14 @@ class BaseCase(unittest.TestCase):
|
|||
a_t = SD.translate_assert(self._language)
|
||||
messenger_post = "%s %s: %s" % (a_t, by.upper(), selector)
|
||||
self.__highlight_with_assert_success(messenger_post, selector, by)
|
||||
if self.recorder_mode:
|
||||
url = self.get_current_url()
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
if self.get_session_storage_item("pause_recorder") == "no":
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["as_el", selector, "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
return True
|
||||
|
||||
def assert_element_visible(
|
||||
|
@ -7871,9 +8198,10 @@ class BaseCase(unittest.TestCase):
|
|||
if type(selector) is str:
|
||||
selectors.append(selector)
|
||||
elif type(selector) is list:
|
||||
for a_selector in selector:
|
||||
if type(a_selector) is str:
|
||||
selectors.append(a_selector)
|
||||
selectors_list = selector
|
||||
for selector in selectors_list:
|
||||
if type(selector) is str:
|
||||
selectors.append(selector)
|
||||
else:
|
||||
raise Exception('Unknown kwarg: "%s"!' % kwarg)
|
||||
if not timeout:
|
||||
|
@ -8012,6 +8340,14 @@ class BaseCase(unittest.TestCase):
|
|||
selector,
|
||||
)
|
||||
self.__highlight_with_assert_success(messenger_post, selector, by)
|
||||
if self.recorder_mode:
|
||||
url = self.get_current_url()
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
if self.get_session_storage_item("pause_recorder") == "no":
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["as_te", text, selector, time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
return True
|
||||
|
||||
def assert_exact_text(
|
||||
|
@ -8050,6 +8386,14 @@ class BaseCase(unittest.TestCase):
|
|||
selector,
|
||||
)
|
||||
self.__highlight_with_assert_success(messenger_post, selector, by)
|
||||
if self.recorder_mode:
|
||||
url = self.get_current_url()
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
if self.get_session_storage_item("pause_recorder") == "no":
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["as_et", text, selector, time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
return True
|
||||
|
||||
############
|
||||
|
@ -8152,6 +8496,14 @@ class BaseCase(unittest.TestCase):
|
|||
self.__highlight_with_assert_success(
|
||||
messenger_post, link_text, by=By.LINK_TEXT
|
||||
)
|
||||
if self.recorder_mode:
|
||||
url = self.get_current_url()
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
if self.get_session_storage_item("pause_recorder") == "no":
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["as_lt", link_text, "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
return True
|
||||
|
||||
def wait_for_partial_link_text(self, partial_link_text, timeout=None):
|
||||
|
@ -8265,6 +8617,14 @@ class BaseCase(unittest.TestCase):
|
|||
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
||||
timeout = self.__get_new_timeout(timeout)
|
||||
self.wait_for_element_not_visible(selector, by=by, timeout=timeout)
|
||||
if self.recorder_mode:
|
||||
url = self.get_current_url()
|
||||
if url and len(url) > 0:
|
||||
if ("http:") in url or ("https:") in url or ("file:") in url:
|
||||
if self.get_session_storage_item("pause_recorder") == "no":
|
||||
time_stamp = self.execute_script("return Date.now();")
|
||||
action = ["asenv", selector, "", time_stamp]
|
||||
self.__extra_actions.append(action)
|
||||
return True
|
||||
|
||||
############
|
||||
|
@ -9468,6 +9828,10 @@ class BaseCase(unittest.TestCase):
|
|||
settings.SMALL_TIMEOUT = sb_config._SMALL_TIMEOUT
|
||||
settings.LARGE_TIMEOUT = sb_config._LARGE_TIMEOUT
|
||||
|
||||
if not hasattr(sb_config, "_recorded_actions"):
|
||||
# Only filled when Recorder Mode is enabled
|
||||
sb_config._recorded_actions = {}
|
||||
|
||||
# Parse the settings file
|
||||
if self.settings_file:
|
||||
from seleniumbase.core import settings_parser
|
||||
|
@ -9583,6 +9947,7 @@ class BaseCase(unittest.TestCase):
|
|||
devtools=self.devtools,
|
||||
remote_debug=self.remote_debug,
|
||||
swiftshader=self.swiftshader,
|
||||
ad_block_on=self.ad_block_on,
|
||||
block_images=self.block_images,
|
||||
chromium_arg=self.chromium_arg,
|
||||
firefox_arg=self.firefox_arg,
|
||||
|
@ -9615,6 +9980,11 @@ class BaseCase(unittest.TestCase):
|
|||
# Call this once in case of multiple setUp() calls in the same test
|
||||
self.__start_time_ms = sb_config.start_time_ms
|
||||
|
||||
# Set the JS start time for Recorder Mode if reusing the session.
|
||||
# Use this to skip saving recorded actions from previous tests.
|
||||
if self.recorder_mode and self._reuse_session:
|
||||
self.__js_start_time = self.execute_script("return Date.now();")
|
||||
|
||||
def __set_last_page_screenshot(self):
|
||||
"""self.__last_page_screenshot is only for pytest html report logs.
|
||||
self.__last_page_screenshot_png is for all screenshot log files."""
|
||||
|
@ -9853,6 +10223,16 @@ class BaseCase(unittest.TestCase):
|
|||
test_id = full
|
||||
return test_id
|
||||
|
||||
def __get_filename(self):
|
||||
""" The filename of the current SeleniumBase test. (NOT Path) """
|
||||
filename = None
|
||||
if "PYTEST_CURRENT_TEST" in os.environ:
|
||||
test_id = os.environ["PYTEST_CURRENT_TEST"].split(" ")[0]
|
||||
filename = test_id.split("::")[0].split("/")[-1]
|
||||
else:
|
||||
filename = self.__class__.__module__.split(".")[-1] + ".py"
|
||||
return filename
|
||||
|
||||
def __create_log_path_as_needed(self, test_logpath):
|
||||
if not os.path.exists(test_logpath):
|
||||
try:
|
||||
|
|
|
@ -107,6 +107,7 @@ var getBestSelector = function(el) {
|
|||
non_id_attributes.push('aria-label');
|
||||
non_id_attributes.push('ng-href');
|
||||
non_id_attributes.push('href');
|
||||
non_id_attributes.push('label');
|
||||
non_id_attributes.push('value');
|
||||
non_id_attributes.push('ng-model');
|
||||
non_id_attributes.push('ng-if');
|
||||
|
@ -125,18 +126,52 @@ var getBestSelector = function(el) {
|
|||
}
|
||||
child_count_by_attr[i] = ssOccurrences(selector_by_attr[i], child_sep);
|
||||
}
|
||||
tag_name = el.tagName.toLowerCase();
|
||||
basic_tags = [];
|
||||
basic_tags.push('h1');
|
||||
basic_tags.push('input');
|
||||
basic_tags.push('button');
|
||||
basic_tags.push('textarea');
|
||||
for (var i = 0; i < basic_tags.length; i++) {
|
||||
tag_name = el.tagName.toLowerCase();
|
||||
if (tag_name == basic_tags[i] &&
|
||||
el == document.querySelector(basic_tags[i]))
|
||||
{
|
||||
return basic_tags[i];
|
||||
}
|
||||
}
|
||||
contains_tags = [];
|
||||
contains_tags.push('a');
|
||||
contains_tags.push('b');
|
||||
contains_tags.push('i');
|
||||
contains_tags.push('td');
|
||||
contains_tags.push('h1');
|
||||
contains_tags.push('h2');
|
||||
contains_tags.push('h3');
|
||||
contains_tags.push('h4');
|
||||
contains_tags.push('code');
|
||||
contains_tags.push('button');
|
||||
all_by_tag = [];
|
||||
for (var i = 0; i < contains_tags.length; i++) {
|
||||
if (tag_name == contains_tags[i] &&
|
||||
el.innerText.trim().length > 1 &&
|
||||
el.innerText.trim().length <= 64)
|
||||
{
|
||||
t_count = 0;
|
||||
inner_text = el.innerText.trim();
|
||||
all_by_tag[i] = document.querySelectorAll(contains_tags[i]);
|
||||
for (var j = 0; j < all_by_tag[i].length; j++) {
|
||||
if (all_by_tag[i][j].innerText.includes(inner_text))
|
||||
{
|
||||
t_count += 1;
|
||||
}
|
||||
}
|
||||
if (t_count === 1 && !inner_text.includes('\n'))
|
||||
{
|
||||
inner_text = inner_text.replace("'", "\\'");
|
||||
inner_text = inner_text.replace('"', '\\"');
|
||||
return tag_name += ':contains("'+inner_text+'")';
|
||||
}
|
||||
}
|
||||
}
|
||||
best_selector = selector_by_id;
|
||||
lowest_child_count = child_count_by_id;
|
||||
for (var i = 0; i < non_id_attributes.length; i++) {
|
||||
|
@ -211,7 +246,7 @@ reset_recorder_state();
|
|||
document.body.addEventListener('click', function (event) {
|
||||
// Do nothing here.
|
||||
});
|
||||
document.body.addEventListener('submit', function (event) {
|
||||
document.body.addEventListener('formdata', function (event) {
|
||||
if (typeof document.recorded_actions === 'undefined')
|
||||
reset_recorder_state();
|
||||
if (sessionStorage.getItem('pause_recorder') === 'yes')
|
||||
|
|
|
@ -886,6 +886,19 @@ def pytest_addoption(parser):
|
|||
'\n (DO NOT combine "--forked" with "--rs"/"--reuse-session"!)\n'
|
||||
)
|
||||
|
||||
# Recorder Mode does not support multi-threaded / multi-process runs.
|
||||
if (
|
||||
"--recorder" in sys_argv
|
||||
or "--record" in sys_argv
|
||||
or "--rec" in sys_argv
|
||||
):
|
||||
arg_join = " ".join(sys.argv)
|
||||
if ("-n" in sys_argv) or (" -n=" in arg_join) or ("-c" in sys_argv):
|
||||
raise Exception(
|
||||
"\n\n Recorder Mode does NOT support multi-process mode (-n)!"
|
||||
'\n (DO NOT combine "--recorder" with "-n NUM_PROCESSES"!)\n'
|
||||
)
|
||||
|
||||
# As a shortcut, you can use "--edge" instead of "--browser=edge", etc,
|
||||
# but you can only specify one default browser for tests. (Default: chrome)
|
||||
browser_changes = 0
|
||||
|
|
12
setup.py
12
setup.py
|
@ -121,7 +121,7 @@ setup(
|
|||
"typing-extensions>=3.10.0.2",
|
||||
'setuptools>=44.1.1;python_version<"3.5"',
|
||||
'setuptools>=50.3.2;python_version>="3.5" and python_version<"3.6"',
|
||||
'setuptools>=58.0.4;python_version>="3.6"',
|
||||
'setuptools>=58.1.0;python_version>="3.6"',
|
||||
'setuptools-scm==5.0.2;python_version<"3.6"',
|
||||
'setuptools-scm>=6.3.2;python_version>="3.6"',
|
||||
'tomli>=1.2.1;python_version>="3.6"',
|
||||
|
@ -143,7 +143,7 @@ setup(
|
|||
'chardet==3.0.4;python_version<"3.5"', # Stay in sync with "requests"
|
||||
'chardet==4.0.0;python_version>="3.5"', # Stay in sync with "requests"
|
||||
'charset-normalizer==2.0.6;python_version>="3.5"', # Sync "requests"
|
||||
"urllib3==1.26.6", # Must stay in sync with "requests"
|
||||
"urllib3==1.26.7", # Must stay in sync with "requests"
|
||||
'requests==2.26.0;python_version<"3.5"',
|
||||
'requests==2.25.1;python_version>="3.5" and python_version<"3.6"',
|
||||
'requests==2.26.0;python_version>="3.6"',
|
||||
|
@ -199,15 +199,15 @@ setup(
|
|||
'ipython==5.10.0;python_version<"3.5"',
|
||||
'ipython==7.9.0;python_version>="3.5" and python_version<"3.6"',
|
||||
'ipython==7.16.1;python_version>="3.6" and python_version<"3.7"',
|
||||
'ipython==7.27.0;python_version>="3.7"', # Requires matplotlib-inline
|
||||
'ipython==7.28.0;python_version>="3.7"', # Requires matplotlib-inline
|
||||
'matplotlib-inline==0.1.3;python_version>="3.7"', # ipython needs this
|
||||
"colorama==0.4.4",
|
||||
'platformdirs==2.0.2;python_version<"3.6"',
|
||||
'platformdirs==2.3.0;python_version>="3.6"',
|
||||
'platformdirs==2.4.0;python_version>="3.6"',
|
||||
'pathlib2==2.3.5;python_version<"3.5"', # Sync with "virtualenv"
|
||||
'importlib-metadata==2.0.0;python_version<"3.5"',
|
||||
'importlib-metadata==2.1.1;python_version>="3.5" and python_version<"3.6"', # noqa: E501
|
||||
"virtualenv>=20.8.0", # Sync with importlib-metadata and pathlib2
|
||||
"virtualenv>=20.8.1", # Sync with importlib-metadata and pathlib2
|
||||
'pymysql==0.10.1;python_version<"3.6"',
|
||||
'pymysql==1.0.2;python_version>="3.6"',
|
||||
"pyotp==2.6.0",
|
||||
|
@ -217,7 +217,7 @@ setup(
|
|||
'Pillow==6.2.2;python_version<"3.5"',
|
||||
'Pillow==7.2.0;python_version>="3.5" and python_version<"3.6"',
|
||||
'Pillow==8.3.2;python_version>="3.6"',
|
||||
'rich==10.10.0;python_version>="3.6" and python_version<"4.0"',
|
||||
'rich==10.11.0;python_version>="3.6" and python_version<"4.0"',
|
||||
'tornado==5.1.1;python_version<"3.5"',
|
||||
'tornado==6.1;python_version>="3.5"',
|
||||
'pdfminer.six==20191110;python_version<"3.5"',
|
||||
|
|
Loading…
Reference in New Issue