diff --git a/README.md b/README.md index 964ae2d5ef7..348ca80b6d4 100755 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ - - - + + +

-SeleniumBase +SeleniumBase

Everything you need to test websites. @@ -68,7 +68,7 @@ python setup.py install ``` If multiple versions of Python are installed, be specific (E.g. use ``python3`` instead of ``python``). -* You can also install ``seleniumbase`` from [pypi](https://pypi.python.org/pypi/seleniumbase): +* You can also install ``seleniumbase`` from [pypi](https://pypi.python.org/pypi/seleniumbase). ```bash pip install seleniumbase ``` @@ -112,17 +112,17 @@ from seleniumbase import BaseCase class MyTestClass(BaseCase): def test_basic(self): + self.open("https://store.xkcd.com/search") + self.type('input[name="q"]', "xkcd book\n") + self.assert_text("xkcd: volume 0", "h3") self.open("https://xkcd.com/353/") self.assert_title("xkcd: Python") self.assert_element('img[alt="Python"]') self.click('a[rel="license"]') self.assert_text("free to copy and reuse") self.go_back() - self.click("link=About") - self.assert_text("xkcd.com", "h2") - self.open("://store.xkcd.com/collections/everything") - self.update_text("input.search-input", "xkcd book\n") - self.assert_exact_text("xkcd: volume 0", "h3") + self.click_link_text("About") + self.assert_exact_text("xkcd.com", "h2") ``` * By default, **[CSS Selectors](https://www.w3schools.com/cssref/css_selectors.asp)** are used for finding page elements. @@ -132,7 +132,7 @@ class MyTestClass(BaseCase): ```python self.open(URL) # Navigate to the web page self.click(SELECTOR) # Click a page element -self.update_text(SELECTOR, TEXT) # Type text (Add "\n" to text for pressing enter/return.) +self.type(SELECTOR, TEXT) # Type text (Add "\n" to text for pressing enter/return.) self.assert_element(SELECTOR) # Assert element is visible self.assert_text(TEXT) # Assert text is visible (has optional SELECTOR arg) self.assert_title(PAGE_TITLE) # Assert page title @@ -163,7 +163,7 @@ SeleniumBase automatically handles common WebDriver actions such as spinning up SeleniumBase uses simple syntax for commands, such as: ```python -self.update_text("input", "dogs\n") +self.type("input", "dogs\n") ``` The same command with regular WebDriver is very messy: @@ -523,10 +523,10 @@ self.click("div#my_id")

Typing Text

-self.update_text(selector, text) # updates the text from the specified element with the specified value. An exception is raised if the element is missing or if the text field is not editable. Example: +self.type(selector, text) # updates the text from the specified element with the specified value. An exception is raised if the element is missing or if the text field is not editable. Example: ```python -self.update_text("input#id_value", "2012") +self.type("input#id_value", "2012") ``` You can also use self.add_text() or the WebDriver .send_keys() command, but those won't clear the text box first if there's already text inside. If you want to type in special keys, that's easy too. Here's an example: diff --git a/docs/img/sb_logo_10.png b/docs/img/sb_logo_10.png new file mode 100644 index 00000000000..8e8d92f7237 Binary files /dev/null and b/docs/img/sb_logo_10.png differ diff --git a/examples/basic_test.py b/examples/basic_test.py index a27642c1c98..bf0cc30005f 100755 --- a/examples/basic_test.py +++ b/examples/basic_test.py @@ -8,9 +8,9 @@ class MyTestClass(BaseCase): def test_basic(self): + self.open("https://store.xkcd.com/search") + self.type('input[name="q"]', "xkcd book\n") self.open("https://xkcd.com/353/") self.click('a[rel="license"]') self.go_back() - self.click("link=About") - self.open("://store.xkcd.com/collections/everything") - self.update_text("input.search-input", "xkcd book\n") + self.click_link_text("About") diff --git a/examples/boilerplates/samples/google_test.py b/examples/boilerplates/samples/google_test.py index 29b3e0acd11..97928efe80c 100755 --- a/examples/boilerplates/samples/google_test.py +++ b/examples/boilerplates/samples/google_test.py @@ -10,7 +10,7 @@ class GoogleTests(BaseCase): def test_google_dot_com(self): self.open('https://google.com/ncr') - self.update_text(HomePage.search_box, 'github') + self.type(HomePage.search_box, 'github') self.assert_element(HomePage.list_box) self.assert_element(HomePage.search_button) self.assert_element(HomePage.feeling_lucky_button) diff --git a/examples/github_test.py b/examples/github_test.py index 2ef2bfd41fb..9dab1f539f7 100755 --- a/examples/github_test.py +++ b/examples/github_test.py @@ -18,7 +18,7 @@ def test_github(self): """AppleWebKit/537.36 (KHTML, like Gecko) """ """Chrome/75.0.3770.100 Safari/537.36""") self.open("https://github.com/") - self.update_text("input.header-search-input", "SeleniumBase\n") + self.type("input.header-search-input", "SeleniumBase\n") self.slow_click('a[href="/seleniumbase/SeleniumBase"]') self.assert_element("div.repository-content") self.assert_text("SeleniumBase", "h1") diff --git a/examples/master_qa/masterqa_test_1.py b/examples/master_qa/masterqa_test_1.py index 6a6950fdc59..6f758cdbd90 100755 --- a/examples/master_qa/masterqa_test_1.py +++ b/examples/master_qa/masterqa_test_1.py @@ -16,8 +16,8 @@ def test_xkcd(self): self.verify("Can you find the moon?") self.click('a[rel="next"]') self.verify("Do the drones look safe?") - self.open("https://store.xkcd.com/collections/everything") - self.update_text("input.search-input", "book\n") + self.open("https://store.xkcd.com/search") + self.type("input.search-input", "book\n") self.verify("Do you see books in the search results?") self.open("https://xkcd.com/213/") for i in range(5): diff --git a/examples/my_first_test.py b/examples/my_first_test.py index 97b188f01f6..ebdf8a11bf3 100755 --- a/examples/my_first_test.py +++ b/examples/my_first_test.py @@ -4,17 +4,17 @@ class MyTestClass(BaseCase): def test_basic(self): + self.open("https://store.xkcd.com/search") + self.type('input[name="q"]', "xkcd book\n") + self.assert_text("xkcd: volume 0", "h3") self.open("https://xkcd.com/353/") self.assert_title("xkcd: Python") self.assert_element('img[alt="Python"]') self.click('a[rel="license"]') self.assert_text("free to copy and reuse") self.go_back() - self.click("link=About") - self.assert_text("xkcd.com", "h2") - self.open("://store.xkcd.com/collections/everything") - self.update_text("input.search-input", "xkcd book\n") - self.assert_exact_text("xkcd: volume 0", "h3") + self.click_link_text("About") + self.assert_exact_text("xkcd.com", "h2") #### @@ -23,14 +23,17 @@ def test_basic(self): # **** NOTES / USEFUL INFO **** # # 1. By default, CSS Selectors are used to identify elements. - # Other options include: "LINK_TEXT", "PARTIAL_LINK_TEXT", "NAME", + # CSS Guide: "https://www.w3schools.com/cssref/css_selectors.asp". + # Other selectors include: "LINK_TEXT", "PARTIAL_LINK_TEXT", "NAME", # "CLASS_NAME", and "ID", but most of those can be expressed as CSS. + # # Here's an example of changing the "by": # [ # from selenium.webdriver.common.by import By # ... # self.click('Next', by=By.PARTIAL_LINK_TEXT) # ] + # # XPath is used by default if the arg starts with "/", "./", or "(": # [ # self.click('/html/body/div[3]/div[4]/p[2]/a') @@ -39,27 +42,46 @@ def test_basic(self): # If you're completely new to CSS selectors, right-click on a # web page and select "Inspect" to see the CSS in the html. # - # 2. Most methods have the optional `timeout` argument. Ex: + # 2. Most methods have the optional "timeout" argument. + # Here's an example of changing the "timeout": # [ # self.assert_element('img[alt="Python"]', timeout=15) # ] - # The `timeout` argument tells the method how many seconds to wait - # for an element to appear before raising an exception. This is + # The "timeout" argument tells the method how many seconds to wait + # for an element to appear before failing the test. This is # useful if a web page needs additional time to load an element. - # If you don't specify a `timeout`, a default timeout is used. + # If you don't specify a "timeout", a default timeout is used. # Default timeouts are configured in seleniumbase/config/settings.py # - # 3. SeleniumBase methods are very versatile. For example, - # self.update_text(SELECTOR, TEXT) does the following: - # * Waits for the element to be visible - # * Waits for the element to be interactive - # * Clears the text field - # * Types in the new text - # * Hits Enter/Submit (if the text ends in "\n") + # 3. SeleniumBase methods often perform multiple actions. For example, + # self.type(SELECTOR, TEXT) will do the following: + # * Wait for the element to be visible + # * Wait for the element to be interactive + # * Clear the text field + # * Type in the new text + # * Press Enter/Submit if the text ends in "\n" # - # self.update_text(S, T) can also be written as self.input(S, T) + # 4. Duplicate method names may exist for the same method: + # (This makes it easier to switch over from other test frameworks.) + # Example: + # self.open() = self.visit() = self.open_url() = self.goto() + # self.type() = self.update_text() = self.input() + # self.send_keys() = self.add_text() + # self.get_element() = self.wait_for_element_present() + # self.find_element() = self.wait_for_element_visible() + # = self.wait_for_element() + # self.assert_element() = self.assert_element_visible() + # self.assert_text() = self.assert_text_visible() + # self.find_text() = self.wait_for_text_visible() + # = self.wait_for_text() + # self.click_link_text(text) = self.click(link=text) + # = self.click_link(text) + # * self.get(url) is SPECIAL: * + # If {url} is a valid URL, self.get() works just like self.open() + # Otherwise {url} becomes a selector for calling self.get_element() # - # 4. There's usually more than one way to do the same thing. Ex: + # 5. There's usually more than one way to do the same thing. + # Example 1: # [ # self.assert_text("xkcd: volume 0", "h3") # ] @@ -68,33 +90,37 @@ def test_basic(self): # text = self.get_text("h3") # self.assert_true("xkcd: volume 0" in text) # ] - # Or: + # Is also the same as: # [ - # text = self.find_element("h3").text + # element = self.find_element("h3") + # text = element.text # self.assert_true("xkcd: volume 0" in text) # ] # - # And the following line: + # Example 2: + # [ + # self.assert_exact_text("xkcd.com", "h2") + # ] + # Is the same as: + # [ + # text = self.get_text("h2").strip() + # self.assert_true("xkcd.com".strip() == text) + # ] + # + # Example 3: # [ # title = self.get_attribute("#comic img", "title") # ] - # Can also be written as: + # Is the same as: # [ # element = self.find_element("#comic img") # title = element.get_attribute("title") # ] # - # 5. self.assert_exact_text(TEXT) ignores leading and trailing + # 6. self.assert_exact_text(TEXT) ignores leading and trailing # whitespace in the TEXT assertion. # So, self.assert_exact_text("Some Text") will find [" Some Text "]. # - # 6. For backwards-compatibilty, some SeleniumBase methods that do the - # same thing have multiple names, kept on from previous versions. - # Ex: self.wait_for_element() is the same as self.find_element(). - # Both search for and return the element, and raise an exception if - # the element does not appear on the page within the timeout limit. - # And self.assert_element() does this too (without returning it). - # # 7. If a URL starts with "://", then "https://" is automatically used. # Example: [self.open("://URL")] becomes [self.open("https://URL")] # This helps by reducing the line length by 5 characters. diff --git a/examples/offline_examples/test_demo_page.py b/examples/offline_examples/test_demo_page.py index 3766aca0001..8cd4d99a3eb 100755 --- a/examples/offline_examples/test_demo_page.py +++ b/examples/offline_examples/test_demo_page.py @@ -21,10 +21,10 @@ def test_demo_page(self): # Assert that the text appears within a given element self.assert_text("Demo Page", "h1") - # Update the text of various text fields on the page - self.update_text("#myTextInput", "This is Automated") - self.update_text("textarea.area1", "Testing Time!\n") - self.update_text('[name="preText2"]', "Typing Text!") + # Type/update text in text fields on the page + self.type("#myTextInput", "This is Automated") + self.type("textarea.area1", "Testing Time!\n") + self.type('[name="preText2"]', "Typing Text!") # Verify that a hover dropdown link changes page text self.assert_text("Automation Practice", "h3") @@ -92,9 +92,8 @@ def test_demo_page(self): # Assert link text - Use click_link_text() to click self.assert_link_text("seleniumbase.com") self.assert_link_text("SeleniumBase on GitHub") - - # Assert the title of the current web page - self.assert_link_text("seleniumbase.com") - self.assert_link_text("SeleniumBase on GitHub") self.assert_link_text("seleniumbase.io") self.assert_link_text("SeleniumBase Demo Page") + + # Assert exact text + self.assert_exact_text("Demo Page", "h1") diff --git a/examples/parameterized_test.py b/examples/parameterized_test.py index d88c6e632e0..159d7cf372f 100644 --- a/examples/parameterized_test.py +++ b/examples/parameterized_test.py @@ -11,6 +11,6 @@ class GoogleTestClass(BaseCase): ]) def test_parameterized_google_search(self, search_term, expected_text): self.open('https://google.com/ncr') - self.update_text('input[title="Search"]', search_term + '\n') + self.type('input[title="Search"]', search_term + '\n') self.assert_element('#result-stats') self.assert_text(expected_text, '#search') diff --git a/examples/swag_labs_suite.py b/examples/swag_labs_suite.py index 1d33264c21f..284a64454cb 100755 --- a/examples/swag_labs_suite.py +++ b/examples/swag_labs_suite.py @@ -10,8 +10,8 @@ def login(self, username="standard_user"): self.open("https://www.saucedemo.com/") if username not in self.get_text("#login_credentials"): self.fail("Invalid user for login: %s" % username) - self.update_text("#user-name", username) - self.update_text("#password", "secret_sauce") + self.type("#user-name", username) + self.type("#password", "secret_sauce") self.click('input[type="submit"]') self.assert_element("#inventory_container") self.assert_text("Products", "div.product_label") @@ -58,9 +58,9 @@ def test_swag_labs_basic_functional_flow(self, username): self.click("link=CHECKOUT") self.assert_exact_text("Checkout: Your Information", "div.subheader") self.assert_element("a.cart_cancel_link") - self.update_text("#first-name", "SeleniumBase") - self.update_text("#last-name", "Rocks") - self.update_text("#postal-code", "01720") + self.type("#first-name", "SeleniumBase") + self.type("#last-name", "Rocks") + self.type("#postal-code", "01720") # Checkout - Overview self.click("input.btn_primary") diff --git a/examples/test_apple_site.py b/examples/test_apple_site.py index 3488d4b4cdd..f3bf97caab0 100755 --- a/examples/test_apple_site.py +++ b/examples/test_apple_site.py @@ -10,7 +10,7 @@ def test_apple_developer_site_webdriver_instructions(self): self.message_duration = 2.0 self.open("https://developer.apple.com/search/") title = "Testing with WebDriver in Safari" - self.update_text('[placeholder*="developer.apple.com"]', title + "\n") + self.type('[placeholder*="developer.apple.com"]', title + "\n") self.click("link=%s" % title) self.assert_element('[href="/documentation"]') self.assert_text(title, "h1") diff --git a/examples/test_demo_site.py b/examples/test_demo_site.py index 30e9c70081a..daf2502ba3b 100755 --- a/examples/test_demo_site.py +++ b/examples/test_demo_site.py @@ -15,10 +15,10 @@ def test_demo_site(self): # Assert that the text appears within a given element self.assert_text("Demo Page", "h1") - # Update the text of various text fields on the page - self.update_text("#myTextInput", "This is Automated") - self.update_text("textarea.area1", "Testing Time!\n") - self.update_text('[name="preText2"]', "Typing Text!") + # Type/update text in text fields on the page + self.type("#myTextInput", "This is Automated") + self.type("textarea.area1", "Testing Time!\n") + self.type('[name="preText2"]', "Typing Text!") # Verify that a hover dropdown link changes page text self.assert_text("Automation Practice", "h3") @@ -93,3 +93,9 @@ def test_demo_site(self): # Assert exact text self.assert_exact_text("Demo Page", "h1") + + # Assert no broken links + self.assert_no_404_errors() + + # Assert no JavaScript errors + self.assert_no_js_errors() diff --git a/examples/test_event_firing.py b/examples/test_event_firing.py index 15b16ae6ac3..0a5cacb0b8f 100755 --- a/examples/test_event_firing.py +++ b/examples/test_event_firing.py @@ -33,6 +33,6 @@ def test_event_firing_webdriver(self): print("\n* EventFiringWebDriver example *") self.open("https://xkcd.com/1862/") self.click("link=About") - self.open("https://store.xkcd.com/collections/everything") - self.update_text("input.search-input", "xkcd book\n") + self.open("https://store.xkcd.com/search") + self.type('input[name="q"]', "xkcd book\n") self.open("https://xkcd.com/1822/") diff --git a/examples/test_hack_search.py b/examples/test_hack_search.py index ff40f1cc340..cb17c2c5c73 100755 --- a/examples/test_hack_search.py +++ b/examples/test_hack_search.py @@ -13,7 +13,7 @@ def test_hack_search(self): self.assert_element('input[title="Search"]') self.set_attribute('[action="/search"]', "action", "//bing.com/search") self.set_attributes('[value="Google Search"]', "value", "Bing Search") - self.update_text('input[title="Search"]', "SeleniumBase GitHub") + self.type('input[title="Search"]', "SeleniumBase GitHub") self.click('[value="Bing Search"]') self.assert_element("h1.b_logo") self.click('[href*="github.com/seleniumbase/SeleniumBase"]') diff --git a/examples/test_pytest_parametrize.py b/examples/test_pytest_parametrize.py index 151a2242ba5..bf57016aa55 100644 --- a/examples/test_pytest_parametrize.py +++ b/examples/test_pytest_parametrize.py @@ -4,7 +4,7 @@ @pytest.mark.parametrize('value', ["pytest", "selenium"]) def test_sb_fixture_with_no_class(sb, value): sb.open("https://google.com/ncr") - sb.update_text('input[title="Search"]', value + '\n') + sb.type('input[title="Search"]', value + '\n') sb.assert_text(value, "div#center_col") @@ -12,5 +12,5 @@ class Test_SB_Fixture(): @pytest.mark.parametrize('value', ["pytest", "selenium"]) def test_sb_fixture_inside_class(self, sb, value): sb.open("https://google.com/ncr") - sb.update_text('input[title="Search"]', value + '\n') + sb.type('input[title="Search"]', value + '\n') sb.assert_text(value, "div#center_col") diff --git a/examples/test_sb_fixture.py b/examples/test_sb_fixture.py index 4bda3de06c7..0d1dd508d6d 100644 --- a/examples/test_sb_fixture.py +++ b/examples/test_sb_fixture.py @@ -3,7 +3,7 @@ # "sb" pytest fixture test in a method with no class def test_sb_fixture_with_no_class(sb): sb.open("https://google.com/ncr") - sb.update_text('input[title="Search"]', 'SeleniumBase\n') + sb.type('input[title="Search"]', 'SeleniumBase\n') sb.click('a[href*="github.com/seleniumbase/SeleniumBase"]') sb.click('a[title="seleniumbase"]') @@ -12,6 +12,6 @@ def test_sb_fixture_with_no_class(sb): class Test_SB_Fixture(): def test_sb_fixture_inside_class(self, sb): sb.open("https://google.com/ncr") - sb.update_text('input[title="Search"]', 'SeleniumBase\n') + sb.type('input[title="Search"]', 'SeleniumBase\n') sb.click('a[href*="github.com/seleniumbase/SeleniumBase"]') sb.click('a[title="examples"]') diff --git a/examples/test_swag_labs.py b/examples/test_swag_labs.py index 51f8915ca8d..97a2fcdc4b1 100755 --- a/examples/test_swag_labs.py +++ b/examples/test_swag_labs.py @@ -8,8 +8,8 @@ def login(self, username="standard_user"): self.open("https://www.saucedemo.com/") if username not in self.get_text("#login_credentials"): self.fail("Invalid user for login: %s" % username) - self.update_text("#user-name", username) - self.update_text("#password", "secret_sauce") + self.type("#user-name", username) + self.type("#password", "secret_sauce") self.click('input[type="submit"]') self.assert_element("#inventory_container") self.assert_text("Products", "div.product_label") @@ -50,9 +50,9 @@ def test_swag_labs_basic_flow(self): self.click("link=CHECKOUT") self.assert_exact_text("Checkout: Your Information", "div.subheader") self.assert_element("a.cart_cancel_link") - self.update_text("#first-name", "SeleniumBase") - self.update_text("#last-name", "Rocks") - self.update_text("#postal-code", "01720") + self.type("#first-name", "SeleniumBase") + self.type("#last-name", "Rocks") + self.type("#postal-code", "01720") # Checkout - Overview self.click("input.btn_primary") diff --git a/examples/translations/chinese_test_1.py b/examples/translations/chinese_test_1.py index 99d2f034ce8..2e6e17988a3 100755 --- a/examples/translations/chinese_test_1.py +++ b/examples/translations/chinese_test_1.py @@ -17,6 +17,6 @@ def test_例子1(self): self.回去() self.单击链接文本("兰德尔·门罗") self.断言文本("兰德尔·门罗", "#firstHeading") - self.更新文本("#searchInput", "程式设计") + self.输入文本("#searchInput", "程式设计") self.单击("#searchButton") self.断言文本("程序设计", "#firstHeading") diff --git a/examples/translations/dutch_test_1.py b/examples/translations/dutch_test_1.py index 9929efde8d8..d300c0b2ec8 100755 --- a/examples/translations/dutch_test_1.py +++ b/examples/translations/dutch_test_1.py @@ -8,11 +8,11 @@ def test_voorbeeld_1(self): self.openen("https://nl.wikipedia.org/wiki/Hoofdpagina") self.controleren_element('a[title*="hoofdpagina gaan"]') self.controleren_tekst("Welkom op Wikipedia", "td.hp-welkom") - self.tekst_bijwerken("#searchInput", "Stroopwafel") + self.typ("#searchInput", "Stroopwafel") self.klik("#searchButton") self.controleren_tekst("Stroopwafel", "#firstHeading") self.controleren_element('img[alt="Stroopwafels"]') - self.tekst_bijwerken("#searchInput", "Rijksmuseum Amsterdam") + self.typ("#searchInput", "Rijksmuseum Amsterdam") self.klik("#searchButton") self.controleren_tekst("Rijksmuseum", "#firstHeading") self.controleren_element('img[alt="Het Rijksmuseum"]') diff --git a/examples/translations/english_test_1.py b/examples/translations/english_test_1.py index 8ccd83b7a9f..e532acd0756 100755 --- a/examples/translations/english_test_1.py +++ b/examples/translations/english_test_1.py @@ -4,10 +4,10 @@ class MyTestClass(BaseCase): def test_example_1(self): - url = "https://store.xkcd.com/collections/everything" + url = "https://store.xkcd.com/collections/posters" self.open(url) - self.update_text("input.search-input", "xkcd book\n") - self.assert_exact_text("xkcd: volume 0", "h3") + self.type("input.search-input", "xkcd book\n") + self.assert_text("xkcd: volume 0", "h3") self.click("li.checkout-link") self.assert_text("Shopping Cart", "#page-title") self.assert_element("div#umbrella") @@ -17,5 +17,5 @@ def test_example_1(self): self.click('a[rel="license"]') self.assert_text("back to this page") self.go_back() - self.click("link=About") - self.assert_text("xkcd.com", "h2") + self.click_link_text("About") + self.assert_exact_text("xkcd.com", "h2") diff --git a/examples/translations/french_test_1.py b/examples/translations/french_test_1.py index 5df0c54209d..3c2217a92ef 100755 --- a/examples/translations/french_test_1.py +++ b/examples/translations/french_test_1.py @@ -8,11 +8,11 @@ def test_exemple_1(self): self.ouvrir("https://fr.wikipedia.org/wiki/") self.vérifier_texte("Wikipédia") # noqa self.vérifier_élément('[title="Visiter la page d’accueil"]') - self.modifier_texte("#searchInput", "Crème brûlée") + self.taper("#searchInput", "Crème brûlée") self.cliquer("#searchButton") self.vérifier_texte("Crème brûlée", "#firstHeading") self.vérifier_élément('img[alt*="Crème brûlée"]') - self.modifier_texte("#searchInput", "Jardin des Tuileries") + self.taper("#searchInput", "Jardin des Tuileries") self.cliquer("#searchButton") self.vérifier_texte("Jardin des Tuileries", "#firstHeading") self.vérifier_élément('img[alt*="Jardin des Tuileries"]') diff --git a/examples/translations/italian_test_1.py b/examples/translations/italian_test_1.py index 1067e716b7d..d370bc4590a 100755 --- a/examples/translations/italian_test_1.py +++ b/examples/translations/italian_test_1.py @@ -8,11 +8,11 @@ def test_esempio_1(self): self.apri("https://it.wikipedia.org/wiki/") self.verificare_testo("Wikipedia") self.verificare_elemento('[title="Lingua italiana"]') - self.aggiornare_testo("#searchInput", "Pizza") + self.digitare("#searchInput", "Pizza") self.fare_clic("#searchButton") self.verificare_testo("Pizza", "#firstHeading") self.verificare_elemento('img[alt*="Pizza"]') - self.aggiornare_testo("#searchInput", "Colosseo") + self.digitare("#searchInput", "Colosseo") self.fare_clic("#searchButton") self.verificare_testo("Colosseo", "#firstHeading") self.verificare_elemento('img[alt*="Colosse"]') diff --git a/examples/translations/japanese_test_1.py b/examples/translations/japanese_test_1.py index 06284c33c66..6c5d6d36e23 100755 --- a/examples/translations/japanese_test_1.py +++ b/examples/translations/japanese_test_1.py @@ -8,14 +8,14 @@ def test_例1(self): self.を開く("https://ja.wikipedia.org/wiki/") self.テキストを確認する("ウィキペディア") self.要素を確認する('[title="メインページに移動する"]') - self.テキストを更新("#searchInput", "アニメ") + self.入力("#searchInput", "アニメ") self.クリックして("#searchButton") self.テキストを確認する("アニメ", "#firstHeading") - self.テキストを更新("#searchInput", "寿司") + self.入力("#searchInput", "寿司") self.クリックして("#searchButton") self.テキストを確認する("寿司", "#firstHeading") self.要素を確認する('img[alt="握り寿司"]') - self.テキストを更新("#searchInput", "レゴランド・ジャパン") + self.入力("#searchInput", "レゴランド・ジャパン") self.クリックして("#searchButton") self.要素を確認する('img[alt="Legoland japan.jpg"]') self.リンクテキストを確認する("名古屋城") diff --git a/examples/translations/korean_test_1.py b/examples/translations/korean_test_1.py index ecfdd77a68e..80d2ef0f891 100755 --- a/examples/translations/korean_test_1.py +++ b/examples/translations/korean_test_1.py @@ -8,12 +8,12 @@ def test_실시예_1(self): self.열기("https://ko.wikipedia.org/wiki/") self.텍스트_확인("위키백과") self.요소_확인('[title="위키백과:소개"]') - self.텍스트를_업데이트("#searchInput", "김치") + self.입력("#searchInput", "김치") self.클릭("#searchButton") self.텍스트_확인("김치", "#firstHeading") self.요소_확인('img[alt="Various kimchi.jpg"]') self.링크_텍스트_확인("한국 요리") - self.텍스트를_업데이트("#searchInput", "비빔밥") + self.입력("#searchInput", "비빔밥") self.클릭("#searchButton") self.텍스트_확인("비빔밥", "#firstHeading") self.요소_확인('img[alt="Dolsot-bibimbap.jpg"]') diff --git a/examples/translations/portuguese_test_1.py b/examples/translations/portuguese_test_1.py index 6884aca252a..0e877897b35 100755 --- a/examples/translations/portuguese_test_1.py +++ b/examples/translations/portuguese_test_1.py @@ -8,16 +8,16 @@ def test_exemplo_1(self): self.abrir("https://pt.wikipedia.org/wiki/") self.verificar_texto("Wikipédia") self.verificar_elemento('[title="Língua portuguesa"]') - self.atualizar_texto("#searchInput", "João Pessoa") + self.tipo("#searchInput", "João Pessoa") self.clique("#searchButton") self.verificar_texto("João Pessoa", "#firstHeading") self.verificar_elemento('img[alt*="João Pessoa"]') - self.atualizar_texto("#searchInput", "Florianópolis") + self.tipo("#searchInput", "Florianópolis") self.clique("#searchButton") self.verificar_texto("Florianópolis", "h1#firstHeading") self.verificar_elemento('img[alt*="Avenida Beira Mar"]') self.voltar() self.verificar_verdade("João" in self.obter_url_atual()) - self.atualizar_texto("#searchInput", "Moqueca\n") + self.tipo("#searchInput", "Moqueca\n") self.verificar_texto("Moqueca", "#firstHeading") self.verificar_texto_do_link("Culinária do Brasil") diff --git a/examples/translations/russian_test_1.py b/examples/translations/russian_test_1.py index 9ed2aa8aa6a..cb8a579542c 100755 --- a/examples/translations/russian_test_1.py +++ b/examples/translations/russian_test_1.py @@ -8,11 +8,11 @@ def test_пример_1(self): self.открыть("https://ru.wikipedia.org/wiki/") self.подтвердить_элемент('[title="Русский язык"]') self.подтвердить_текст("Википедия", "h2.main-wikimedia-header") - self.обновить_текст("#searchInput", "МГУ") + self.введите("#searchInput", "МГУ") self.нажмите("#searchButton") self.подтвердить_текст("университет", "#firstHeading") self.подтвердить_элемент('img[alt="МГУ, вид с воздуха.jpg"]') - self.обновить_текст("#searchInput", "приключения Шурика") + self.введите("#searchInput", "приключения Шурика") self.нажмите("#searchButton") self.подтвердить_текст("Операция «Ы» и другие приключения Шурика") self.подтвердить_элемент('img[alt="Постер фильма"]') diff --git a/examples/translations/spanish_test_1.py b/examples/translations/spanish_test_1.py index e9edc93abe1..da5c4682085 100755 --- a/examples/translations/spanish_test_1.py +++ b/examples/translations/spanish_test_1.py @@ -7,12 +7,12 @@ class MiClaseDePrueba(CasoDePrueba): def test_ejemplo_1(self): self.abrir("https://es.wikipedia.org/wiki/") self.verificar_texto("Wikipedia") - self.verificar_elemento('[title="Visitar la página principal"]') - self.actualizar_texto("#searchInput", "Parc d'Atraccions Tibidabo") + self.verificar_elemento('[title*="la página principal"]') + self.escriba("#searchInput", "Parc d'Atraccions Tibidabo") self.haga_clic("#searchButton") self.verificar_texto("Tibidabo", "#firstHeading") self.verificar_elemento('img[alt*="Tibidabo"]') - self.actualizar_texto("#searchInput", "Palma de Mallorca") + self.escriba("#searchInput", "Palma de Mallorca") self.haga_clic("#searchButton") self.verificar_texto("Palma de Mallorca", "#firstHeading") self.verificar_elemento('img[alt*="Palma"]') diff --git a/help_docs/chinese.md b/help_docs/chinese.md index 67a9582d675..692981f2777 100644 --- a/help_docs/chinese.md +++ b/help_docs/chinese.md @@ -124,7 +124,7 @@ class MyTestClass(BaseCase): self.click("link=About") self.assert_text("xkcd.com", "h2") self.open("://store.xkcd.com/collections/everything") - self.update_text("input.search-input", "xkcd book\n") + self.type("input.search-input", "xkcd book\n") self.assert_exact_text("xkcd: volume 0", "h3") ``` @@ -132,10 +132,12 @@ class MyTestClass(BaseCase): * 如果你是CSS Selectors新手, 可以通过 [Flukeout](http://flukeout.github.io/) 游戏来帮助学习掌握. * 在上述代码中可以看到以下相关的 ``SeleniumBase`` 方法: +``from seleniumbase import BaseCase``: + ```python self.open(URL) # 打开页面 self.click(SELECTOR) # 点击页面元素 -self.update_text(SELECTOR, TEXT) # 输入文字 (添加 "\n" 在"TEXT"的末尾来进行换行.) +self.type(SELECTOR, TEXT) # 输入文字 (添加 "\n" 在"TEXT"的末尾来进行换行.) self.assert_element(SELECTOR) # 断言元素是否存在并可见 self.assert_text(TEXT) # 断言文本是否存在并可见 (可以选择某个元素选择器) self.assert_title(PAGE_TITLE) # 断言标题是否存在并可见 @@ -155,6 +157,55 @@ self.switch_to_window(WINDOW_NUMBER) # 切换不同的 window/tab self.save_screenshot(FILE_NAME) # 保存当前页面的截图 ``` +[chinese_test_1.py](https://github.com/seleniumbase/SeleniumBase/blob/master/examples/translations/chinese_test_1.py): +```python +from seleniumbase.translate.chinese import 硒测试用例 + +class 我的测试类(硒测试用例): + + def test_例子1(self): + self.开启网址("https://xkcd.in/comic?lg=cn&id=353") + self.断言标题("Python - XKCD中文站") + self.断言元素("#content div.comic-body") + self.断言文本("上漫画") + self.单击("div.nextLink") + self.断言文本("老妈的逆袭", "#content h1") + self.单击链接文本("下一篇") + self.断言文本("敲桌子", "#content h1") + self.断言文本("有时候无聊就是最棒的乐趣") + self.回去() + self.单击链接文本("兰德尔·门罗") + self.断言文本("兰德尔·门罗", "#firstHeading") + self.更新文本("#searchInput", "程式设计") + self.单击("#searchButton") + self.断言文本("程序设计", "#firstHeading") +``` + +``from seleniumbase.translate.chinese import 硒测试用例``: + +```python +self.开启(URL) # 打开页面 +self.单击(SELECTOR) # 点击页面元素 +self.输入文本(SELECTOR, TEXT) # 输入文字 (添加 "\n" 在"TEXT"的末尾来进行换行.) +self.断言元素(SELECTOR) # 断言元素是否存在并可见 +self.断言文本(TEXT) # 断言文本是否存在并可见 (可以选择某个元素选择器) +self.断言标题(PAGE_TITLE) # 断言标题是否存在并可见 +self.检查断开的链接() # 断言不存在404错误,若存在则断言失败 +self.检查JS错误() # 断言不存在js错误 (Chrome-ONLY) +self.执行脚本(JAVASCRIPT) # 在页面中执行js脚本 +self.回去() # 返回到上一个url链接页面 +self.获取文本(SELECTOR) # 获取元素的文本 +self.获取属性(SELECTOR, ATTRIBUTE) # 获取某个定位元素的指定元素属性的属性值 +self.元素是否可见(SELECTOR) # 判断元素是否在页面上可见 +self.文本是否显示(TEXT) # 判断文本是否在页面上可见(可提供 SELECTOR) +self.悬停并单击(HOVER_SELECTOR, CLICK_SELECTOR) # 鼠标移动在指定元素上后点击另一个元素 +self.按文本选择选项(DROPDOWN_SELECTOR, OPTION_TEXT) # 选择下拉框中内容 +self.切换到帧(FRAME_NAME) # 切换 webdriver control 到页面上指定 iframe +self.切换到默认内容() # 切换 webdriver control out 到当前的 iframe +self.切换到窗口(WINDOW_NUMBER) # 切换不同的 window/tab +self.保存截图(FILE_NAME) # 保存当前页面的截图 +``` + 完整的 SeleniumBase methods, 可见: Method Summary

了解更多信息:

@@ -166,7 +217,7 @@ SeleniumBase 自动化控制 WebDriver 操作 web browsers(浏览器),在运行 SeleniumBase 使用简单简约的语法, 例如: ```python -self.update_text("input", "dogs\n") +self.type("input", "dogs\n") ``` 上述相似的代码在 Webdriver中变现的不是特别好: @@ -524,10 +575,10 @@ self.click("div#my_id")

输入文本

-self.update_text(selector, text) # 用指定的值更新来自指定元素的文本。如果元素丢失或文本字段不可编辑,则引发异常。例如: +self.type(selector, text) # 用指定的值更新来自指定元素的文本。如果元素丢失或文本字段不可编辑,则引发异常。例如: ```python -self.update_text("input#id_value", "2012") +self.type("input#id_value", "2012") ``` 您也可以使用self.add_text()或WebDriver .send_keys()命令,但是如果文本框中已经有文本,这些命令不会首先清除文本框 如果您想键入特殊的键,这也很容易。这里有一个例子: diff --git a/help_docs/method_summary.md b/help_docs/method_summary.md index e6df0b52624..b965d3a5b52 100755 --- a/help_docs/method_summary.md +++ b/help_docs/method_summary.md @@ -23,7 +23,7 @@ self.double_click(selector, by=By.CSS_SELECTOR, timeout=None) self.click_chain(selectors_list, by=By.CSS_SELECTOR, timeout=None, spacing=0) -self.update_text(selector, new_value, by=By.CSS_SELECTOR, timeout=None, retry=False) +self.update_text(selector, text, by=By.CSS_SELECTOR, timeout=None, retry=False) # Duplicates: self.type(selector, text, by=By.CSS_SELECTOR, timeout=None, retry=False) # self.input(selector, text, by=By.CSS_SELECTOR, timeout=None, retry=False) # self.write(selector, text, by=By.CSS_SELECTOR, timeout=None, retry=False) @@ -195,7 +195,7 @@ self.bring_to_front(selector, by=By.CSS_SELECTOR) self.highlight_click(selector, by=By.CSS_SELECTOR, loops=3, scroll=True) -self.highlight_update_text(selector, new_value, by=By.CSS_SELECTOR, loops=3, scroll=True) +self.highlight_update_text(selector, text, by=By.CSS_SELECTOR, loops=3, scroll=True) self.highlight(selector, by=By.CSS_SELECTOR, loops=4, scroll=True) @@ -251,6 +251,7 @@ self.get_link_status_code(link, allow_redirects=False, timeout=5) self.assert_link_status_code_is_not_404(link) self.assert_no_404_errors(multithreaded=True) +# Duplicates: self.assert_no_broken_links(multithreaded=True) self.print_unique_links_with_status_codes() @@ -302,11 +303,12 @@ self.convert_xpath_to_css(xpath) self.convert_to_css_selector(selector, by) -self.set_value(selector, new_value, by=By.CSS_SELECTOR, timeout=None) +self.set_value(selector, text, by=By.CSS_SELECTOR, timeout=None) -self.js_update_text(selector, new_value, by=By.CSS_SELECTOR, timeout=None) +self.js_update_text(selector, text, by=By.CSS_SELECTOR, timeout=None) +# Duplicates: self.js_type(selector, text, by=By.CSS_SELECTOR, timeout=None) -self.jquery_update_text(selector, new_value, by=By.CSS_SELECTOR, timeout=None) +self.jquery_update_text(selector, text, by=By.CSS_SELECTOR, timeout=None) self.set_time_limit(time_limit) diff --git a/integrations/node_js/my_first_test.py b/integrations/node_js/my_first_test.py index d1332597186..cb109e525f7 100755 --- a/integrations/node_js/my_first_test.py +++ b/integrations/node_js/my_first_test.py @@ -4,14 +4,14 @@ class MyTestClass(BaseCase): def test_basic(self): + self.open("https://store.xkcd.com/search") + self.type('input[name="q"]', "xkcd book\n") + self.assert_text("xkcd: volume 0", "h3") self.open("https://xkcd.com/353/") self.assert_title("xkcd: Python") self.assert_element('img[alt="Python"]') self.click('a[rel="license"]') self.assert_text("free to copy and reuse") self.go_back() - self.click("link=About") - self.assert_text("xkcd.com", "h2") - self.open("://store.xkcd.com/collections/everything") - self.update_text("input.search-input", "xkcd book\n") - self.assert_exact_text("xkcd: volume 0", "h3") + self.click_link_text("About") + self.assert_exact_text("xkcd.com", "h2") diff --git a/requirements.txt b/requirements.txt index cae97abfad7..a676600c7eb 100755 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ pluggy==0.13.1 attrs>=19.3.0 pytest==4.6.11;python_version<"3.5" pytest==5.4.3;python_version>="3.5" -pytest-cov==2.9.0 +pytest-cov==2.10.0 pytest-forked==1.1.3 pytest-html==1.22.1;python_version<"3.6" pytest-html==2.0.1;python_version>="3.6" @@ -41,9 +41,9 @@ coverage==5.1 pyotp==2.3.0 boto==2.49.0 cffi==1.14.0 -rich==2.0.0;python_version>="3.6" and python_version<"4.0" +rich==2.1.0;python_version>="3.6" and python_version<"4.0" flake8==3.7.9;python_version<"3.5" -flake8==3.8.2;python_version>="3.5" +flake8==3.8.3;python_version>="3.5" pyflakes==2.1.1;python_version<"3.5" pyflakes==2.2.0;python_version>="3.5" certifi>=2020.4.5.2 diff --git a/seleniumbase/config/proxy_list.py b/seleniumbase/config/proxy_list.py index 213c0f97707..3835f52538c 100755 --- a/seleniumbase/config/proxy_list.py +++ b/seleniumbase/config/proxy_list.py @@ -20,13 +20,10 @@ """ PROXY_LIST = { - "example1": "142.93.130.169:8118", # (Example) - set your own proxy here - "example2": "51.91.212.159:3128", # (Example) - set your own proxy here - "example3": "149.129.238.254:3128", # (Example) - set your own proxy here - "example4": "82.200.233.4:3128", # (Example) - set your own proxy here - "example5": "46.218.155.194:3128", # (Example) - set your own proxy here - "example6": "45.77.222.251:3128", # (Example) - set your own proxy here - "example7": "51.178.220.168:3128", # (Example) - set your own proxy here + "example1": "104.154.143.77:3128", # (Example) - set your own proxy here + "example2": "105.112.8.53:3128", # (Example) - set your own proxy here + "example3": "82.200.233.4:3128", # (Example) - set your own proxy here + "example4": "176.53.40.222:3128", # (Example) - set your own proxy here "proxy1": None, "proxy2": None, "proxy3": None, diff --git a/seleniumbase/console_scripts/ReadMe.md b/seleniumbase/console_scripts/ReadMe.md index 14e921067ca..05706822236 100755 --- a/seleniumbase/console_scripts/ReadMe.md +++ b/seleniumbase/console_scripts/ReadMe.md @@ -4,20 +4,28 @@ SeleniumBase console scripts help you get things done more easily, such as installing web drivers, creating a test directory with necessary configuration files, converting old WebDriver unittest scripts into SeleniumBase code, translating tests into multiple languages, and using the Selenium Grid. -Type ``seleniumbase`` on the command line to use console scripts. -You can also use the simplified name: ``sbase`` instead. +* Usage: ``seleniumbase [COMMAND] [PARAMETERS]`` + +* (simplified): ``sbase [COMMAND] [PARAMETERS]`` + +* To list all commands: ``seleniumbase --help`` (For running tests, [use pytest with SeleniumBase](https://github.com/seleniumbase/SeleniumBase/blob/master/help_docs/customizing_test_runs.md).) ### install * Usage: -``seleniumbase install [DRIVER_NAME]`` - (Drivers: ``chromedriver``, ``geckodriver``, ``edgedriver``, - ``iedriver``, ``operadriver``) +``sbase install [DRIVER_NAME] [VERSION]`` + (Drivers: ``chromedriver``, ``geckodriver``, ``edgedriver``, + ``iedriver``, ``operadriver``) + (Versions: ``latest`` or a specific driver version. + If none specified, installs the default version.) -* Example: -``seleniumbase install chromedriver`` +* Examples: +``sbase install chromedriver`` + +* Options: + ``latest``: * Output: Installs the specified webdriver. @@ -30,22 +38,50 @@ Installs the specified webdriver. ### mkdir * Usage: -``seleniumbase mkdir [DIRECTORY_NAME]`` +``sbase mkdir [DIRECTORY_NAME]`` * Example: -``seleniumbase mkdir browser_tests`` +``sbase mkdir browser_tests`` * Output: Creates a new folder for running SeleniumBase scripts. The new folder contains default config files, -sample tests for helping new users get started, and -Python boilerplates for setting up customized +sample tests for helping new users get started, +and Python boilerplates for setting up customized test frameworks. +### mkfile + +* Usage: +``sbase mkfile [FILE_NAME.py] [OPTIONS]`` + +* Example: +``sbase mkfile new_test.py`` + +* Options: +``-b`` / ``--basic`` (Basic boilerplate / single-line test) + +* Language Options: +``--en`` / ``--English`` | ``--zh`` / ``--Chinese`` +``--nl`` / ``--Dutch`` | ``--fr`` / ``--French`` +``--it`` / ``--Italian`` | ``--ja`` / ``--Japanese`` +``--ko`` / ``--Korean`` | ``--pt`` / ``--Portuguese`` +``--ru`` / ``--Russian`` | ``--es`` / ``--Spanish`` + +* Output: +Creates a new SeleniumBase test file with boilerplate code. +If the file already exists, an error is raised. +By default, uses English mode and creates a +boilerplate with the 5 most common SeleniumBase +methods, which are "open", "click", "update_text", +"assert_element", and "assert_text". If using the +basic boilerplate option, only the "open" method +is included. + ### convert * Usage: -``seleniumbase convert [PYTHON_WEBDRIVER_UNITTEST_FILE]`` +``sbase convert [PYTHON_WEBDRIVER_UNITTEST_FILE]`` * Output: Converts a Selenium IDE exported WebDriver unittest file @@ -57,7 +93,7 @@ See: http://www.katalon.com/automation-recorder ### translate * Usage: -``seleniumbase translate [SB_FILE].py [LANGUAGE] [ACTION]`` +``sbase translate [SB_FILE].py [LANGUAGE] [ACTION]`` * Languages: ``--en`` / ``--English`` | ``--zh`` / ``--Chinese`` @@ -89,7 +125,7 @@ plus the 2-letter language code of the new language. ### extract-objects * Usage: -``seleniumbase extract-objects [SB_PYTHON_FILE]`` +``sbase extract-objects [SB_PYTHON_FILE]`` * Output: Creates page objects based on selectors found in a @@ -99,7 +135,7 @@ seleniumbase Python file and saves those objects to the ### inject-objects * Usage: -``seleniumbase inject-objects [SB_PYTHON_FILE] [OPTIONS]`` +``sbase inject-objects [SB_PYTHON_FILE] [OPTIONS]`` * Options: ``-c``, ``--comments`` (Add object selectors to the comments.) @@ -112,7 +148,7 @@ the selected seleniumbase Python file. ### objectify * Usage: -``seleniumbase objectify [SB_PYTHON_FILE] [OPTIONS]`` +``sbase objectify [SB_PYTHON_FILE] [OPTIONS]`` * Options: ``-c``, ``--comments`` (Add object selectors to the comments.) @@ -127,7 +163,7 @@ have been replaced with variable names defined in ### revert-objects * Usage: -``seleniumbase revert-objects [SB_PYTHON_FILE] [OPTIONS]`` +``sbase revert-objects [SB_PYTHON_FILE] [OPTIONS]`` * Options: ``-c``, ``--comments`` (Keep existing comments for the lines.) @@ -141,11 +177,11 @@ selectors stored in the "page_objects.py" file. ### download * Usage: -``seleniumbase download [ITEM]`` +``sbase download [ITEM]`` (Options: server) * Example: -``seleniumbase download server`` +``sbase download server`` * Output: Downloads the specified item. @@ -154,7 +190,7 @@ Downloads the specified item. ### grid-hub * Usage: -``seleniumbase grid-hub {start|stop}`` +``sbase grid-hub {start|stop}`` * Options: ``-v``, ``--verbose`` (Increases verbosity of logging output.) @@ -169,7 +205,7 @@ You can start, restart, or stop the Grid Hub server. ### grid-node * Usage: -``seleniumbase grid-node {start|stop} [OPTIONS]`` +``sbase grid-node {start|stop} [OPTIONS]`` * Options: ``--hub=HUB_IP`` (The Grid Hub IP Address to connect to.) (Default: ``127.0.0.1``) diff --git a/seleniumbase/console_scripts/run.py b/seleniumbase/console_scripts/run.py index 698f11abd3f..c05c1585d41 100644 --- a/seleniumbase/console_scripts/run.py +++ b/seleniumbase/console_scripts/run.py @@ -8,6 +8,7 @@ Examples: sbase install chromedriver sbase mkdir browser_tests +sbase mkfile new_test.py sbase convert my_old_webdriver_unittest.py sbase print my_first_test.py -n sbase translate my_first_test.py --zh -p @@ -24,16 +25,6 @@ import colorama import sys -from seleniumbase.common import obfuscate -from seleniumbase.common import unobfuscate -from seleniumbase.console_scripts import logo_helper -from seleniumbase.console_scripts import objectify -from seleniumbase.console_scripts import sb_install -from seleniumbase.console_scripts import sb_mkdir -from seleniumbase.utilities.selenium_grid import download_selenium_server -from seleniumbase.utilities.selenium_grid import grid_hub -from seleniumbase.utilities.selenium_grid import grid_node -from seleniumbase.utilities.selenium_ide import convert_ide def show_usage(): @@ -56,17 +47,19 @@ def show_usage(): def show_basic_usage(): + from seleniumbase.console_scripts import logo_helper seleniumbase_logo = logo_helper.get_seleniumbase_logo() print(seleniumbase_logo) print("%s" % get_version()[0:1]) print("") sc = ("") sc += ('Usage: "seleniumbase [COMMAND] [PARAMETERS]"\n') - sc += ('(short name): "sbase [COMMAND] [PARAMETERS]"\n') + sc += ('(simplified): "sbase [COMMAND] [PARAMETERS]"\n') sc += ("\n") sc += ("Commands:\n") sc += (" install [DRIVER_NAME] [OPTIONS]\n") - sc += (" mkdir [NEW_TEST_DIRECTORY_NAME]\n") + sc += (" mkdir [DIRECTORY_NAME]\n") + sc += (" mkfile [FILE_NAME.py]\n") sc += (" convert [PYTHON_WEBDRIVER_UNITTEST_FILE]\n") sc += (" print [FILE] [OPTIONS]\n") sc += (" translate [SB_PYTHON_FILE] [LANGUAGE] [ACTION]\n") @@ -90,7 +83,11 @@ def show_basic_usage(): def show_install_usage(): - print(" ** install **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "install" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase install [DRIVER_NAME] [OPTIONS]") @@ -103,12 +100,12 @@ def show_install_usage(): print(' Use "latest" for the latest version.') print(" -p OR --path Also copy the driver to /usr/local/bin") print(" Example:") - print(" seleniumbase install chromedriver") - print(" seleniumbase install geckodriver") - print(" seleniumbase install chromedriver 83.0.4103.39") - print(" seleniumbase install chromedriver latest") - print(" seleniumbase install chromedriver -p") - print(" seleniumbase install chromedriver latest -p") + print(" sbase install chromedriver") + print(" sbase install geckodriver") + print(" sbase install chromedriver 83.0.4103.39") + print(" sbase install chromedriver latest") + print(" sbase install chromedriver -p") + print(" sbase install chromedriver latest -p") print(" Output:") print(" Installs the chosen webdriver to seleniumbase/drivers/") print(" (chromedriver is required for Chrome automation)") @@ -120,24 +117,64 @@ def show_install_usage(): def show_mkdir_usage(): - print(" ** mkdir **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "mkdir" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase mkdir [DIRECTORY_NAME]") print(" OR: sbase mkdir [DIRECTORY_NAME]") print(" Example:") - print(" seleniumbase mkdir browser_tests") + print(" sbase mkdir browser_tests") print(" Output:") - print(" Creates a new folder for running SeleniumBase scripts.") + print(" Creates a new folder for running SBase scripts.") print(" The new folder contains default config files,") - print(" sample tests for helping new users get started, and") - print(" Python boilerplates for setting up customized") + print(" sample tests for helping new users get started,") + print(" and Python boilerplates for setting up customized") print(" test frameworks.") print("") +def show_mkfile_usage(): + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "mkfile" + c2 + " **" + cr) + print(sc) + print("") + print(" Usage:") + print(" seleniumbase mkfile [FILE_NAME.py]") + print(" OR: sbase mkfile [FILE_NAME.py]") + print(" Example:") + print(" sbase mkfile new_test.py") + print(" Options:") + print(" -b / --basic (Basic boilerplate / single-line test)") + print(" Language Options:") + print(" --en / --English | --zh / --Chinese") + print(" --nl / --Dutch | --fr / --French") + print(" --it / --Italian | --ja / --Japanese") + print(" --ko / --Korean | --pt / --Portuguese") + print(" --ru / --Russian | --es / --Spanish") + print(" Output:") + print(" Creates a new SBase test file with boilerplate code.") + print(" If the file already exists, an error is raised.") + print(" By default, uses English mode and creates a") + print(" boilerplate with the 5 most common SeleniumBase") + print(' methods, which are "open", "click", "update_text",') + print(' "assert_element", and "assert_text". If using the') + print(' basic boilerplate option, only the "open" method') + print(' is included.') + print("") + + def show_convert_usage(): - print(" ** convert **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "convert" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase convert [PYTHON_WEBDRIVER_UNITTEST_FILE]") @@ -152,7 +189,12 @@ def show_convert_usage(): def show_print_usage(): - print(" ** print **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "print" + c2 + " **" + cr) + print(sc) + print("") print(" Usage:") print(" seleniumbase print [FILE] [OPTIONS]") print(" OR: sbase print [FILE] [OPTIONS]") @@ -166,7 +208,12 @@ def show_print_usage(): def show_translate_usage(): - print(" ** translate **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "translate" + c2 + " **" + cr) + print(sc) + print("") print(" Usage:") print(" seleniumbase translate [SB_FILE.py] [LANGUAGE] [ACTION]") print(" OR: sbase translate [SB_FILE.py] [LANGUAGE] [ACTION]") @@ -196,7 +243,11 @@ def show_translate_usage(): def show_extract_objects_usage(): - print(" ** extract-objects **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "extract-objects" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase extract-objects [SELENIUMBASE_PYTHON_FILE]") @@ -209,7 +260,11 @@ def show_extract_objects_usage(): def show_inject_objects_usage(): - print(" ** inject-objects **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "inject-objects" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase inject-objects [SELENIUMBASE_PYTHON_FILE]") @@ -225,7 +280,11 @@ def show_inject_objects_usage(): def show_objectify_usage(): - print(" ** objectify **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "objectify" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase objectify [SELENIUMBASE_PYTHON_FILE]") @@ -244,7 +303,11 @@ def show_objectify_usage(): def show_revert_objects_usage(): - print(" ** revert-objects **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "revert-objects" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase revert-objects [SELENIUMBASE_PYTHON_FILE]") @@ -261,7 +324,11 @@ def show_revert_objects_usage(): def show_encrypt_usage(): - print(" ** encrypt OR obfuscate **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "encrypt OR obfuscate" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase encrypt || seleniumbase obfuscate") @@ -274,7 +341,11 @@ def show_encrypt_usage(): def show_decrypt_usage(): - print(" ** decrypt OR unobfuscate **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "decrypt OR unobfuscate" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase decrypt || seleniumbase unobfuscate") @@ -287,7 +358,11 @@ def show_decrypt_usage(): def show_download_usage(): - print(" ** download **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "download" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase download server") @@ -299,7 +374,11 @@ def show_download_usage(): def show_grid_hub_usage(): - print(" ** grid-hub **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "grid-hub" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase grid-hub {start|stop}") @@ -319,7 +398,11 @@ def show_grid_hub_usage(): def show_grid_node_usage(): - print(" ** grid-node **") + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + sc = (" " + c2 + "** " + c3 + "grid-node" + c2 + " **" + cr) + print(sc) print("") print(" Usage:") print(" seleniumbase grid-node {start|stop} [OPTIONS]") @@ -354,11 +437,16 @@ def show_version_info(): def show_detailed_help(): + c2 = colorama.Fore.BLUE + colorama.Back.LIGHTGREEN_EX + c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX + c6 = colorama.Back.CYAN + cr = colorama.Style.RESET_ALL show_basic_usage() - print("More Info:") - print("") + print(c6 + " " + c2 + " Commands: " + c6 + " ") + print(cr) show_install_usage() show_mkdir_usage() + show_mkfile_usage() show_convert_usage() show_print_usage() show_translate_usage() @@ -371,8 +459,6 @@ def show_detailed_help(): show_download_usage() show_grid_hub_usage() show_grid_node_usage() - c3 = colorama.Fore.BLUE + colorama.Back.LIGHTYELLOW_EX - cr = colorama.Style.RESET_ALL print('* (Use "' + c3 + 'pytest' + cr + '" for running tests) *\n') @@ -393,18 +479,28 @@ def main(): if command == "install": if len(command_args) >= 1: + from seleniumbase.console_scripts import sb_install sb_install.main() else: show_basic_usage() show_install_usage() elif command == "mkdir": if len(command_args) >= 1: + from seleniumbase.console_scripts import sb_mkdir sb_mkdir.main() else: show_basic_usage() show_mkdir_usage() + elif command == "mkfile": + if len(command_args) >= 1: + from seleniumbase.console_scripts import sb_mkfile + sb_mkfile.main() + else: + show_basic_usage() + show_mkfile_usage() elif command == "convert": if len(command_args) == 1: + from seleniumbase.utilities.selenium_ide import convert_ide convert_ide.main() else: show_basic_usage() @@ -442,54 +538,64 @@ def main(): show_translate_usage() elif command == "extract-objects" or command == "extract_objects": if len(command_args) >= 1: + from seleniumbase.console_scripts import objectify objectify.extract_objects() else: show_basic_usage() show_extract_objects_usage() elif command == "inject-objects" or command == "inject_objects": if len(command_args) >= 1: + from seleniumbase.console_scripts import objectify objectify.inject_objects() else: show_basic_usage() show_inject_objects_usage() elif command == "objectify": if len(command_args) >= 1: + from seleniumbase.console_scripts import objectify objectify.objectify() else: show_basic_usage() show_objectify_usage() elif command == "revert-objects" or command == "revert_objects": if len(command_args) >= 1: + from seleniumbase.console_scripts import objectify objectify.revert_objects() else: show_basic_usage() show_revert_objects_usage() elif command == "encrypt" or command == "obfuscate": if len(command_args) >= 0: + from seleniumbase.common import obfuscate obfuscate.main() else: show_basic_usage() show_encrypt_usage() elif command == "decrypt" or command == "unobfuscate": if len(command_args) >= 0: + from seleniumbase.common import unobfuscate unobfuscate.main() else: show_basic_usage() show_decrypt_usage() elif command == "download": if len(command_args) >= 1 and command_args[0].lower() == "server": + from seleniumbase.utilities.selenium_grid import ( + download_selenium_server) download_selenium_server.main(force_download=True) else: show_basic_usage() show_download_usage() elif command == "grid-hub" or command == "grid_hub": if len(command_args) >= 1: + from seleniumbase.utilities.selenium_grid import grid_hub grid_hub.main() else: show_basic_usage() show_grid_hub_usage() elif command == "grid-node" or command == "grid_node": if len(command_args) >= 1: + from seleniumbase.utilities.selenium_grid import grid_node grid_node.main() else: show_basic_usage() @@ -509,6 +615,10 @@ def main(): print("") show_mkdir_usage() return + elif command_args[0] == "mkfile": + print("") + show_mkfile_usage() + return elif command_args[0] == "convert": print("") show_convert_usage() diff --git a/seleniumbase/console_scripts/sb_mkdir.py b/seleniumbase/console_scripts/sb_mkdir.py index 0b2c98877cc..2f753d08b53 100755 --- a/seleniumbase/console_scripts/sb_mkdir.py +++ b/seleniumbase/console_scripts/sb_mkdir.py @@ -2,360 +2,355 @@ Creates a new folder for running SeleniumBase scripts. Usage: -seleniumbase mkdir [DIRECTORY_NAME] + seleniumbase mkdir [DIRECTORY_NAME] + OR sbase mkdir [DIRECTORY_NAME] + +Example: + sbase mkdir browser_tests + Output: -Creates a new folder for running SeleniumBase scripts. -The new folder contains default config files, -sample tests for helping new users get started, and -Python boilerplates for setting up customized -test frameworks. + Creates a new folder for running SBase scripts. + The new folder contains default config files, + sample tests for helping new users get started, + and Python boilerplates for setting up customized + test frameworks. """ import codecs +import colorama import os import sys -def invalid_run_command(): +def invalid_run_command(msg=None): exp = (" ** mkdir **\n\n") exp += " Usage:\n" exp += " seleniumbase mkdir [DIRECTORY_NAME]\n" + exp += " OR sbase mkdir [DIRECTORY_NAME]\n" exp += " Example:\n" - exp += " seleniumbase mkdir browser_tests\n" + exp += " sbase mkdir browser_tests\n" exp += " Output:\n" - exp += " Creates a new folder for running SeleniumBase scripts.\n" + exp += " Creates a new folder for running SBase scripts.\n" exp += " The new folder contains default config files,\n" - exp += " sample tests for helping new users get started, and\n" - exp += " Python boilerplates for setting up customized\n" + exp += " sample tests for helping new users get started,\n" + exp += " and Python boilerplates for setting up customized\n" exp += " test frameworks.\n" - print("") - raise Exception('INVALID RUN COMMAND!\n\n%s' % exp) + if not msg: + raise Exception('INVALID RUN COMMAND!\n\n%s' % exp) + else: + raise Exception('INVALID RUN COMMAND!\n\n%s\n%s\n' % (exp, msg)) def main(): - num_args = len(sys.argv) - if sys.argv[0].split('/')[-1].lower() == "seleniumbase" or ( - sys.argv[0].split('\\')[-1].lower() == "seleniumbase") or ( - sys.argv[0].split('/')[-1].lower() == "sbase") or ( - sys.argv[0].split('\\')[-1].lower() == "sbase"): - if num_args < 3 or num_args > 3: - invalid_run_command() - else: + colorama.init(autoreset=True) + c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX + cr = colorama.Style.RESET_ALL + error_msg = None + command_args = sys.argv[2:] + if len(command_args) != 1: invalid_run_command() - dir_name = sys.argv[num_args - 1] + dir_name = command_args[0] if len(str(dir_name)) < 2: - raise Exception('Directory name length must be at least 2 ' - 'characters long!') - if os.path.exists(os.getcwd() + '/' + dir_name): - raise Exception('Directory "%s" already exists ' - 'in the current path!\n' % dir_name) - else: - os.mkdir(dir_name) + error_msg = ( + 'Directory name length must be at least 2 characters long!') + elif "/" in str(dir_name) or "\\" in str(dir_name): + error_msg = ( + 'Directory name must not include slashes ("/", "\\")!') + elif os.path.exists(os.getcwd() + '/' + dir_name): + error_msg = ( + 'Directory "%s" already exists in the current path!\n' + '' % dir_name) + if error_msg: + error_msg = c5 + error_msg + cr + invalid_run_command(error_msg) + + os.mkdir(dir_name) - data = [] - data.append("[pytest]") - data.append("addopts = --capture=no --ignore conftest.py " - "-p no:cacheprovider") - data.append("filterwarnings =") - data.append(" ignore::pytest.PytestWarning") - data.append(" ignore:.*U.*mode is deprecated:DeprecationWarning") - data.append("junit_family = legacy") - data.append("python_files = test_*.py *_test.py *_tests.py *_suite.py") - data.append("python_classes = Test* *Test* *Test *Tests *Suite") - data.append("python_functions = test_*") - data.append("markers =") - data.append(" marker1: custom marker") - data.append(" marker2: custom marker") - data.append(" marker3: custom marker") - data.append(" marker_test_suite: custom marker") - data.append(" expected_failure: custom marker") - data.append(" local: custom marker") - data.append(" remote: custom marker") - data.append(" offline: custom marker") - data.append(" develop: custom marker") - data.append(" qa: custom marker") - data.append(" ready: custom marker") - data.append(" master: custom marker") - data.append(" release: custom marker") - data.append(" staging: custom marker") - data.append(" production: custom marker") - data.append("") - file_path = "%s/%s" % (dir_name, "pytest.ini") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("[pytest]") + data.append("addopts = --capture=no --ignore conftest.py " + "-p no:cacheprovider") + data.append("filterwarnings =") + data.append(" ignore::pytest.PytestWarning") + data.append(" ignore:.*U.*mode is deprecated:DeprecationWarning") + data.append("junit_family = legacy") + data.append("python_files = test_*.py *_test.py *_tests.py *_suite.py") + data.append("python_classes = Test* *Test* *Test *Tests *Suite") + data.append("python_functions = test_*") + data.append("markers =") + data.append(" marker1: custom marker") + data.append(" marker2: custom marker") + data.append(" marker3: custom marker") + data.append(" marker_test_suite: custom marker") + data.append(" expected_failure: custom marker") + data.append(" local: custom marker") + data.append(" remote: custom marker") + data.append(" offline: custom marker") + data.append(" develop: custom marker") + data.append(" qa: custom marker") + data.append(" ready: custom marker") + data.append(" master: custom marker") + data.append(" release: custom marker") + data.append(" staging: custom marker") + data.append(" production: custom marker") + data.append("") + file_path = "%s/%s" % (dir_name, "pytest.ini") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - data = [] - data.append("[nosetests]") - data.append("nocapture=1") - data.append("logging-level=INFO") - data.append("") - data.append("[bdist_wheel]") - data.append("universal=1") - file_path = "%s/%s" % (dir_name, "setup.cfg") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("[nosetests]") + data.append("nocapture=1") + data.append("logging-level=INFO") + data.append("") + data.append("[bdist_wheel]") + data.append("universal=1") + file_path = "%s/%s" % (dir_name, "setup.cfg") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - data = [] - data.append("from seleniumbase import BaseCase") - data.append("") - data.append("") - data.append("class MyTestClass(BaseCase):") - data.append("") - data.append(" def test_basic(self):") - data.append(' self.open("https://xkcd.com/353/")') - data.append(' self.assert_title("xkcd: Python")') - data.append(" self.assert_element('img[alt=\"Python\"]')") - data.append(" self.click('a[rel=\"license\"]')") - data.append(' self.assert_text("free to copy and reuse")') - data.append(' self.go_back()') - data.append(' self.click("link=About")') - data.append(' self.assert_text("xkcd.com", "h2")') - data.append(' self.open(' - '"://store.xkcd.com/collections/everything")') - data.append( - ' self.update_text("input.search-input", "xkcd book\\n")') - data.append(' self.assert_text("xkcd: volume 0", "h3")') - data.append("") - file_path = "%s/%s" % (dir_name, "my_first_test.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("from seleniumbase import BaseCase") + data.append("") + data.append("") + data.append("class MyTestClass(BaseCase):") + data.append("") + data.append(" def test_basic(self):") + data.append(' self.open("https://store.xkcd.com/search")') + data.append(" self.type('input[name=\"q\"]', \"xkcd book\\n\")") + data.append(' self.assert_text("xkcd: volume 0", "h3")') + data.append(' self.open("https://xkcd.com/353/")') + data.append(' self.assert_title("xkcd: Python")') + data.append(" self.assert_element('img[alt=\"Python\"]')") + data.append(" self.click('a[rel=\"license\"]')") + data.append(' self.assert_text("free to copy and reuse")') + data.append(' self.go_back()') + data.append(' self.click("link=About")') + data.append(' self.assert_exact_text("xkcd.com", "h2")') + data.append("") + file_path = "%s/%s" % (dir_name, "my_first_test.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - data = [] - data.append("from seleniumbase import BaseCase") - data.append("") - data.append("") - data.append("class MyTestClass(BaseCase):") - data.append("") - data.append(" def test_demo_site(self):") - data.append(' self.open(' - '"https://seleniumbase.io/demo_page.html")') - data.append(' self.assert_title("Web Testing Page")') - data.append(' self.assert_element("tbody#tbodyId")') - data.append(' self.assert_text("Demo Page", "h1")') - data.append(' self.update_text("#myTextInput", ' - '"This is Automated")') - data.append(' self.update_text("textarea.area1", ' - '"Testing Time!\\n")') - data.append(" self.update_text('[name=\"preText2\"]', " - "\"Typing Text!\")") - data.append(' self.assert_text("Automation Practice", "h3")') - data.append(' self.hover_and_click("#myDropdown", ' - '"#dropOption2")') - data.append(' self.assert_text("Link Two Selected", "h3")') - data.append(' self.assert_text("This Text is Green", "#pText")') - data.append(' self.click("#myButton")') - data.append(' self.assert_text("This Text is Purple", ' - '"#pText")') - data.append(" self.assert_element('svg[name=\"svgName\"]')") - data.append(" self.assert_element('progress[value=\"50\"]')") - data.append(' self.press_right_arrow("#myslider", times=5)') - data.append(" self.assert_element('progress[value=\"100\"]')") - data.append(" self.assert_element('meter[value=\"0.25\"]')") - data.append(' self.select_option_by_text("#mySelect", ' - '"Set to 75%")') - data.append(" self.assert_element('meter[value=\"0.75\"]')") - data.append(' self.assert_false(self.is_element_visible(' - '"img"))') - data.append(' self.switch_to_frame("#myFrame1")') - data.append(' self.assert_true(self.is_element_visible("img"))') - data.append(' self.switch_to_default_content()') - data.append(' self.assert_false(self.is_text_visible(' - '"iFrame Text"))') - data.append(' self.switch_to_frame("#myFrame2")') - data.append(' self.assert_true(self.is_text_visible(' - '"iFrame Text"))') - data.append(' self.switch_to_default_content()') - data.append(' self.assert_false(self.is_selected(' - '"#radioButton2"))') - data.append(' self.click("#radioButton2")') - data.append(' self.assert_true(self.is_selected(' - '"#radioButton2"))') - data.append(' self.assert_false(self.is_selected(' - '"#checkBox1"))') - data.append(' self.click("#checkBox1")') - data.append(' self.assert_true(self.is_selected("#checkBox1"))') - data.append(' self.assert_false(self.is_selected(' - '"#checkBox2"))') - data.append(' self.assert_false(self.is_selected(' - '"#checkBox3"))') - data.append(' self.assert_false(self.is_selected(' - '"#checkBox4"))') - data.append(' self.click_visible_elements(' - '"input.checkBoxClassB")') - data.append(' self.assert_true(self.is_selected("#checkBox2"))') - data.append(' self.assert_true(self.is_selected("#checkBox3"))') - data.append(' self.assert_true(self.is_selected("#checkBox4"))') - data.append(' self.assert_false(self.is_element_visible(' - '".fBox"))') - data.append(' self.switch_to_frame("#myFrame3")') - data.append(' self.assert_true(self.is_element_visible(' - '".fBox"))') - data.append(' self.assert_false(self.is_selected(".fBox"))') - data.append(' self.click(".fBox")') - data.append(' self.assert_true(self.is_selected(".fBox"))') - data.append(' self.switch_to_default_content()') - data.append(' self.assert_link_text("seleniumbase.com")') - data.append(' self.assert_link_text("SeleniumBase on GitHub")') - data.append(' self.assert_link_text("seleniumbase.io")') - data.append(' self.click_link_text("SeleniumBase Demo Page")') - data.append(' self.assert_exact_text("Demo Page", "h1")') - data.append("") - file_path = "%s/%s" % (dir_name, "test_demo_site.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("from seleniumbase import BaseCase") + data.append("") + data.append("") + data.append("class MyTestClass(BaseCase):") + data.append("") + data.append(" def test_demo_site(self):") + data.append(' self.open("https://seleniumbase.io/demo_page.html")') + data.append(' self.assert_title("Web Testing Page")') + data.append(' self.assert_element("tbody#tbodyId")') + data.append(' self.assert_text("Demo Page", "h1")') + data.append(' self.type("#myTextInput", "This is Automated")') + data.append(' self.type("textarea.area1", "Testing Time!\\n")') + data.append(" self.type('[name=\"preText2\"]', \"Typing Text!\")") + data.append(' self.assert_text("Automation Practice", "h3")') + data.append(' self.hover_and_click("#myDropdown", "#dropOption2")') + data.append(' self.assert_text("Link Two Selected", "h3")') + data.append(' self.assert_text("This Text is Green", "#pText")') + data.append(' self.click("#myButton")') + data.append(' self.assert_text("This Text is Purple", "#pText")') + data.append(" self.assert_element('svg[name=\"svgName\"]')") + data.append(" self.assert_element('progress[value=\"50\"]')") + data.append(' self.press_right_arrow("#myslider", times=5)') + data.append(" self.assert_element('progress[value=\"100\"]')") + data.append(" self.assert_element('meter[value=\"0.25\"]')") + data.append(' self.select_option_by_text("#mySelect", ' + '"Set to 75%")') + data.append(" self.assert_element('meter[value=\"0.75\"]')") + data.append(' self.assert_false(self.is_element_visible("img"))') + data.append(' self.switch_to_frame("#myFrame1")') + data.append(' self.assert_true(self.is_element_visible("img"))') + data.append(' self.switch_to_default_content()') + data.append(' self.assert_false(self.is_text_visible(' + '"iFrame Text"))') + data.append(' self.switch_to_frame("#myFrame2")') + data.append(' self.assert_true(self.is_text_visible(' + '"iFrame Text"))') + data.append(' self.switch_to_default_content()') + data.append(' self.assert_false(self.is_selected("#radioButton2"))') + data.append(' self.click("#radioButton2")') + data.append(' self.assert_true(self.is_selected("#radioButton2"))') + data.append(' self.assert_false(self.is_selected("#checkBox1"))') + data.append(' self.click("#checkBox1")') + data.append(' self.assert_true(self.is_selected("#checkBox1"))') + data.append(' self.assert_false(self.is_selected("#checkBox2"))') + data.append(' self.assert_false(self.is_selected("#checkBox3"))') + data.append(' self.assert_false(self.is_selected("#checkBox4"))') + data.append(' self.click_visible_elements("input.checkBoxClassB")') + data.append(' self.assert_true(self.is_selected("#checkBox2"))') + data.append(' self.assert_true(self.is_selected("#checkBox3"))') + data.append(' self.assert_true(self.is_selected("#checkBox4"))') + data.append(' self.assert_false(self.is_element_visible(".fBox"))') + data.append(' self.switch_to_frame("#myFrame3")') + data.append(' self.assert_true(self.is_element_visible(".fBox"))') + data.append(' self.assert_false(self.is_selected(".fBox"))') + data.append(' self.click(".fBox")') + data.append(' self.assert_true(self.is_selected(".fBox"))') + data.append(' self.switch_to_default_content()') + data.append(' self.assert_link_text("seleniumbase.com")') + data.append(' self.assert_link_text("SeleniumBase on GitHub")') + data.append(' self.assert_link_text("seleniumbase.io")') + data.append(' self.click_link_text("SeleniumBase Demo Page")') + data.append(' self.assert_exact_text("Demo Page", "h1")') + data.append("") + file_path = "%s/%s" % (dir_name, "test_demo_site.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - data = [] - data.append("from seleniumbase import BaseCase") - data.append("from parameterized import parameterized") - data.append("") - data.append("") - data.append("class GoogleTestClass(BaseCase):") - data.append("") - data.append(" @parameterized.expand([") - data.append(' ["pypi", "pypi.org"],') - data.append(' ["wikipedia", "wikipedia.org"],') - data.append(' ["seleniumbase", "seleniumbase/SeleniumBase"],') - data.append(" ])") - data.append(" def test_parameterized_google_search(" - "self, search_term, expected_text):") - data.append(" self.open('https://google.com/ncr')") - data.append(" self.update_text('input[title=\"Search\"]', " - "search_term + '\\n')") - data.append(" self.assert_element('#result-stats')") - data.append(" self.assert_text(expected_text, '#search')") - data.append("") - file_path = "%s/%s" % (dir_name, "parameterized_test.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("from seleniumbase import BaseCase") + data.append("from parameterized import parameterized") + data.append("") + data.append("") + data.append("class GoogleTestClass(BaseCase):") + data.append("") + data.append(" @parameterized.expand([") + data.append(' ["pypi", "pypi.org"],') + data.append(' ["wikipedia", "wikipedia.org"],') + data.append(' ["seleniumbase", "seleniumbase/SeleniumBase"],') + data.append(" ])") + data.append(" def test_parameterized_google_search(" + "self, search_term, expected_text):") + data.append(" self.open('https://google.com/ncr')") + data.append(" self.type('input[title=\"Search\"]', " + "search_term + '\\n')") + data.append(" self.assert_element('#result-stats')") + data.append(" self.assert_text(expected_text, '#search')") + data.append("") + file_path = "%s/%s" % (dir_name, "parameterized_test.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - dir_name_2 = dir_name + "/" + "boilerplates" - os.mkdir(dir_name_2) + dir_name_2 = dir_name + "/" + "boilerplates" + os.mkdir(dir_name_2) - data = [] - data.append("") - file_path = "%s/%s" % (dir_name_2, "__init__.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("") + file_path = "%s/%s" % (dir_name_2, "__init__.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - data = [] - data.append("from seleniumbase import BaseCase") - data.append("") - data.append("") - data.append("class BaseTestCase(BaseCase):") - data.append("") - data.append(" def setUp(self):") - data.append(" super(BaseTestCase, self).setUp()") - data.append(" # << Add custom code AFTER the super() line >>") - data.append("") - data.append(" def tearDown(self):") - data.append(" self.save_teardown_screenshot()") - data.append(" # << Add custom code BEFORE the super() line >>") - data.append(" super(BaseTestCase, self).tearDown()") - data.append("") - data.append(" def login(self):") - data.append(" # <<< Placeholder. Add your code here. >>>") - data.append(" pass") - data.append("") - data.append(" def example_method(self):") - data.append(" # <<< Placeholder. Add your code here. >>>") - data.append(" pass") - data.append("") - file_path = "%s/%s" % (dir_name_2, "base_test_case.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("from seleniumbase import BaseCase") + data.append("") + data.append("") + data.append("class BaseTestCase(BaseCase):") + data.append("") + data.append(" def setUp(self):") + data.append(" super(BaseTestCase, self).setUp()") + data.append(" # << Add custom code AFTER the super() line >>") + data.append("") + data.append(" def tearDown(self):") + data.append(" self.save_teardown_screenshot()") + data.append(" # << Add custom code BEFORE the super() line >>") + data.append(" super(BaseTestCase, self).tearDown()") + data.append("") + data.append(" def login(self):") + data.append(" # <<< Placeholder. Add your code here. >>>") + data.append(" pass") + data.append("") + data.append(" def example_method(self):") + data.append(" # <<< Placeholder. Add your code here. >>>") + data.append(" pass") + data.append("") + file_path = "%s/%s" % (dir_name_2, "base_test_case.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - data = [] - data.append("class Page(object):") - data.append(' html = "html"') - data.append("") - file_path = "%s/%s" % (dir_name_2, "page_objects.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("class Page(object):") + data.append(' html = "html"') + data.append("") + file_path = "%s/%s" % (dir_name_2, "page_objects.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - data = [] - data.append("from .base_test_case import BaseTestCase") - data.append("from .page_objects import Page") - data.append("") - data.append("") - data.append("class MyTestClass(BaseTestCase):") - data.append("") - data.append(" def test_boilerplate(self):") - data.append(" self.login()") - data.append(" self.example_method()") - data.append(" self.assert_element(Page.html)") - data.append("") - file_path = "%s/%s" % (dir_name_2, "boilerplate_test.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("from .base_test_case import BaseTestCase") + data.append("from .page_objects import Page") + data.append("") + data.append("") + data.append("class MyTestClass(BaseTestCase):") + data.append("") + data.append(" def test_boilerplate(self):") + data.append(" self.login()") + data.append(" self.example_method()") + data.append(" self.assert_element(Page.html)") + data.append("") + file_path = "%s/%s" % (dir_name_2, "boilerplate_test.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - dir_name_3 = dir_name_2 + "/" + "samples" - os.mkdir(dir_name_3) + dir_name_3 = dir_name_2 + "/" + "samples" + os.mkdir(dir_name_3) - data = [] - data.append("") - file_path = "%s/%s" % (dir_name_3, "__init__.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("") + file_path = "%s/%s" % (dir_name_3, "__init__.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - data = [] - data.append("from seleniumbase import BaseCase") - data.append("from .google_objects import HomePage, ResultsPage") - data.append("") - data.append("") - data.append("class GoogleTests(BaseCase):") - data.append("") - data.append(" def test_google_dot_com(self):") - data.append(" self.open('https://google.com/ncr')") - data.append( - " self.update_text(HomePage.search_box, 'github')") - data.append(" self.assert_element(HomePage.list_box)") - data.append(" self.assert_element(HomePage.search_button)") - data.append( - " self.assert_element(HomePage.feeling_lucky_button)") - data.append(" self.click(HomePage.search_button)") - data.append( - " self.assert_text('github.com', " - "ResultsPage.search_results)") - data.append(" self.assert_element(ResultsPage.images_link)") - data.append("") - file_path = "%s/%s" % (dir_name_3, "google_test.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() + data = [] + data.append("from seleniumbase import BaseCase") + data.append("from .google_objects import HomePage, ResultsPage") + data.append("") + data.append("") + data.append("class GoogleTests(BaseCase):") + data.append("") + data.append(" def test_google_dot_com(self):") + data.append(" self.open('https://google.com/ncr')") + data.append(" self.type(HomePage.search_box, 'github')") + data.append(" self.assert_element(HomePage.list_box)") + data.append(" self.assert_element(HomePage.search_button)") + data.append( + " self.assert_element(HomePage.feeling_lucky_button)") + data.append(" self.click(HomePage.search_button)") + data.append( + " self.assert_text('github.com', " + "ResultsPage.search_results)") + data.append(" self.assert_element(ResultsPage.images_link)") + data.append("") + file_path = "%s/%s" % (dir_name_3, "google_test.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() - data = [] - data.append("class HomePage(object):") - data.append(" dialog_box = '[role=\"dialog\"] div'") - data.append(" search_box = 'input[title=\"Search\"]'") - data.append(" list_box = '[role=\"listbox\"]'") - data.append(" search_button = 'input[value=\"Google Search\"]'") - data.append( - " feeling_lucky_button = " - "'''input[value=\"I'm Feeling Lucky\"]'''") - data.append("") - data.append("") - data.append("class ResultsPage(object):") - data.append(" google_logo = 'img[alt=\"Google\"]'") - data.append(" images_link = 'link=Images'") - data.append(" search_results = 'div#center_col'") - data.append("") - file_path = "%s/%s" % (dir_name_3, "google_objects.py") - file = codecs.open(file_path, "w+", "utf-8") - file.writelines("\r\n".join(data)) - file.close() - print('''\n* Directory "%s" was created with config files ''' - '''and sample tests! *\n''' % dir_name) + data = [] + data.append("class HomePage(object):") + data.append(" dialog_box = '[role=\"dialog\"] div'") + data.append(" search_box = 'input[title=\"Search\"]'") + data.append(" list_box = '[role=\"listbox\"]'") + data.append(" search_button = 'input[value=\"Google Search\"]'") + data.append( + " feeling_lucky_button = " + "'''input[value=\"I'm Feeling Lucky\"]'''") + data.append("") + data.append("") + data.append("class ResultsPage(object):") + data.append(" google_logo = 'img[alt=\"Google\"]'") + data.append(" images_link = 'link=Images'") + data.append(" search_results = 'div#center_col'") + data.append("") + file_path = "%s/%s" % (dir_name_3, "google_objects.py") + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() + print('''\n* Directory "%s" was created with config files ''' + '''and sample tests! *\n''' % dir_name) if __name__ == "__main__": - main() + invalid_run_command() diff --git a/seleniumbase/console_scripts/sb_mkfile.py b/seleniumbase/console_scripts/sb_mkfile.py new file mode 100755 index 00000000000..92a78b02a5c --- /dev/null +++ b/seleniumbase/console_scripts/sb_mkfile.py @@ -0,0 +1,265 @@ +# -*- coding: utf-8 -*- +""" +Creates a new SeleniumBase test file with boilerplate code. + +Usage: + seleniumbase mkfile [FILE_NAME.py] [OPTIONS] + or sbase mkfile [FILE_NAME.py] [OPTIONS] + +Example: + sbase mkfile new_test.py + +Options: + -b / --basic (Basic boilerplate / single-line test) + +Language Options: + --en / --English | --zh / --Chinese + --nl / --Dutch | --fr / --French + --it / --Italian | --ja / --Japanese + --ko / --Korean | --pt / --Portuguese + --ru / --Russian | --es / --Spanish + +Output: + Creates a new SBase test file with boilerplate code. + If the file already exists, an error is raised. + By default, uses English mode and creates a + boilerplate with the 5 most common SeleniumBase + methods, which are "open", "click", "update_text", + "assert_element", and "assert_text". If using the + basic boilerplate option, only the "open" method + is included. +""" + +import codecs +import colorama +import os +import sys + + +def invalid_run_command(msg=None): + exp = (" ** mkfile **\n\n") + exp += " Usage:\n" + exp += " seleniumbase mkfile [FILE_NAME.py] [OPTIONS]\n" + exp += " OR sbase mkfile [FILE_NAME.py] [OPTIONS]\n" + exp += " Example:\n" + exp += " sbase mkfile new_test.py\n" + exp += " Options:\n" + exp += " -b / --basic (Basic boilerplate / single-line test)\n" + exp += " Language Options:\n" + exp += " --en / --English | --zh / --Chinese\n" + exp += " --nl / --Dutch | --fr / --French\n" + exp += " --it / --Italian | --ja / --Japanese\n" + exp += " --ko / --Korean | --pt / --Portuguese\n" + exp += " --ru / --Russian | --es / --Spanish\n" + exp += " Output:\n" + exp += " Creates a new SBase test file with boilerplate code.\n" + exp += " If the file already exists, an error is raised.\n" + exp += " By default, uses English mode and creates a\n" + exp += " boilerplate with the 5 most common SeleniumBase\n" + exp += ' methods, which are "open", "click", "update_text",\n' + exp += ' "assert_element", and "assert_text". If using the\n' + exp += ' basic boilerplate option, only the "open" method\n' + exp += ' is included.\n' + if not msg: + raise Exception('INVALID RUN COMMAND!\n\n%s' % exp) + else: + raise Exception('INVALID RUN COMMAND!\n\n%s\n%s\n' % (exp, msg)) + + +def main(): + colorama.init(autoreset=True) + c1 = colorama.Fore.BLUE + colorama.Back.LIGHTCYAN_EX + c5 = colorama.Fore.RED + colorama.Back.LIGHTYELLOW_EX + c7 = colorama.Fore.BLACK + colorama.Back.MAGENTA + cr = colorama.Style.RESET_ALL + basic = False + help_me = False + error_msg = None + invalid_cmd = None + language = "English" + + command_args = sys.argv[2:] + file_name = command_args[0] + if not file_name.endswith(".py"): + error_msg = 'File Name must end with ".py"!' + elif "*" in file_name or len(str(file_name)) < 4: + error_msg = 'Invalid File Name!' + elif "/" in str(file_name) or "\\" in str(file_name): + error_msg = 'File must be created in the current directory!' + elif os.path.exists(os.getcwd() + '/' + file_name): + error_msg = ( + 'File "%s" already exists in the current path!' % file_name) + if error_msg: + error_msg = c5 + error_msg + cr + invalid_run_command(error_msg) + + if len(command_args) >= 2: + options = command_args[1:] + for option in options: + option = option.lower() + if option == "help" or option == "--help": + help_me = True + elif option == "-b" or option == "--basic": + basic = True + elif option == "--en" or option == "--english": + language = "English" + elif option == "--zh" or option == "--chinese": + language = "Chinese" + elif option == "--nl" or option == "--dutch": + language = "Dutch" + elif option == "--fr" or option == "--french": + language = "French" + elif option == "--it" or option == "--italian": + language = "Italian" + elif option == "--ja" or option == "--japanese": + language = "Japanese" + elif option == "--ko" or option == "--korean": + language = "Korean" + elif option == "--pt" or option == "--portuguese": + language = "Portuguese" + elif option == "--ru" or option == "--russian": + language = "Russian" + elif option == "--es" or option == "--spanish": + language = "Spanish" + else: + invalid_cmd = "\n===> INVALID OPTION: >> %s <<\n" % option + invalid_cmd = invalid_cmd.replace('>> ', ">>" + c5 + " ") + invalid_cmd = invalid_cmd.replace(' <<', " " + cr + "<<") + invalid_cmd = invalid_cmd.replace('>>', c7 + ">>" + cr) + invalid_cmd = invalid_cmd.replace('<<', c7 + "<<" + cr) + help_me = True + break + if help_me: + invalid_run_command(invalid_cmd) + + if language != "English" and sys.version_info[0] == 2: + print("") + msg = 'Multi-language support for "sbase mkfile" ' + msg += 'is not available on Python 2!' + msg = "\n" + c5 + msg + cr + msg += '\nPlease run in "English" mode or upgrade to Python 3!\n' + raise Exception(msg) + + dir_name = os.getcwd() + file_path = "%s/%s" % (dir_name, file_name) + + body = "html > body" + para = "body p" + hello = "Hello" + goodbye = "Goodbye" + class_name = "MyTestClass" + if language == "Chinese": + hello = "你好" + goodbye = "再见" + class_name = "我的测试类" + elif language == "Dutch": + hello = "Hallo" + goodbye = "Dag" + class_name = "MijnTestklasse" + elif language == "French": + hello = "Bonjour" + goodbye = "Au revoir" + class_name = "MaClasseDeTest" + elif language == "Italian": + hello = "Ciao" + goodbye = "Addio" + class_name = "MiaClasseDiTest" + elif language == "Japanese": + hello = "こんにちは" + goodbye = "さようなら" + class_name = "私のテストクラス" + elif language == "Korean": + hello = "여보세요" + goodbye = "안녕" + class_name = "테스트_클래스" + elif language == "Portuguese": + hello = "Olá" + goodbye = "Tchau" + class_name = "MinhaClasseDeTeste" + elif language == "Russian": + hello = "Привет" + goodbye = "До свидания" + class_name = "МойТестовыйКласс" + elif language == "Spanish": + hello = "Hola" + goodbye = "Adiós" + class_name = "MiClaseDePrueba" + url = "" + if basic: + url = "about:blank" + elif language not in ["English", "Dutch", "French", "Italian"]: + url = "data:text/html,

%s " % hello + else: + url = "data:text/html,

%s

" % hello + + import_line = "from seleniumbase import BaseCase" + parent_class = "BaseCase" + class_line = "class MyTestClass(BaseCase):" + if language != "English": + from seleniumbase.translate.master_dict import MD_F + import_line = MD_F.get_import_line(language) + parent_class = MD_F.get_lang_parent_class(language) + class_line = "class %s(%s):" % (class_name, parent_class) + + data = [] + data.append("%s" % import_line) + data.append("") + data.append("") + data.append("%s" % class_line) + data.append("") + data.append(" def test_base(self):") + data.append(' self.open("%s")' % url) + if not basic: + data.append(' self.assert_element("%s") # selector' % body) + data.append(' self.assert_text("%s", "%s")' + ' # text, selector' % (hello, para)) + data.append(' self.type("input", "%s")' + ' # selector, text' % goodbye) + data.append(' self.click("%s") # selector' % para) + data.append("") + + new_data = [] + if language == "English": + new_data = data + else: + from seleniumbase.translate.master_dict import MD + from seleniumbase.translate.master_dict import MD_L_Codes + md = MD.md + lang_codes = MD_L_Codes.lang + nl_code = lang_codes[language] + dl_code = lang_codes["English"] + for line in data: + found_swap = False + replace_count = line.count("self.") # Total possible replacements + for key in md.keys(): + original = "self." + md[key][dl_code] + "(" + if original in line: + replacement = "self." + md[key][nl_code] + "(" + new_line = line.replace(original, replacement) + found_swap = True + replace_count -= 1 + if replace_count == 0: + break # Done making replacements + else: + # There might be another method to replace in the line. + # Example: self.assert_true("Name" in self.get_title()) + line = new_line + continue + if found_swap: + if new_line.endswith(" # noqa"): # Remove flake8 skip + new_line = new_line[0:-len(" # noqa")] + new_data.append(new_line) + continue + new_data.append(line) + data = new_data + file = codecs.open(file_path, "w+", "utf-8") + file.writelines("\r\n".join(data)) + file.close() + success = ( + '\n' + c1 + '* Test file: "' + file_name + '" was created! *' + '' + cr + '\n') + print(success) + + +if __name__ == "__main__": + invalid_run_command() diff --git a/seleniumbase/console_scripts/sb_print.py b/seleniumbase/console_scripts/sb_print.py index a9fb85771b6..80476ffbbde 100755 --- a/seleniumbase/console_scripts/sb_print.py +++ b/seleniumbase/console_scripts/sb_print.py @@ -37,19 +37,9 @@ def invalid_run_command(msg=None): def sc_ranges(): # Get the ranges of special characters of Chinese, Japanese, and Korean. special_char_ranges = ([ - {"from": ord(u"\u3300"), "to": ord(u"\u33ff")}, - {"from": ord(u"\ufe30"), "to": ord(u"\ufe4f")}, - {"from": ord(u"\uf900"), "to": ord(u"\ufaff")}, - {"from": ord(u"\U0002F800"), "to": ord(u"\U0002fa1f")}, - {'from': ord(u'\u3040'), 'to': ord(u'\u309f')}, - {"from": ord(u"\u30a0"), "to": ord(u"\u30ff")}, - {"from": ord(u"\u2e80"), "to": ord(u"\u2eff")}, - {"from": ord(u"\u4e00"), "to": ord(u"\u9fff")}, - {"from": ord(u"\u3400"), "to": ord(u"\u4dbf")}, - {"from": ord(u"\U00020000"), "to": ord(u"\U0002a6df")}, - {"from": ord(u"\U0002a700"), "to": ord(u"\U0002b73f")}, - {"from": ord(u"\U0002b740"), "to": ord(u"\U0002b81f")}, - {"from": ord(u"\U0002b820"), "to": ord(u"\U0002ceaf")} + {"from": ord(u"\u4e00"), "to": ord(u"\u9FFF")}, + {"from": ord(u"\u3040"), "to": ord(u"\u30ff")}, + {"from": ord(u"\uac00"), "to": ord(u"\ud7a3")} ]) return special_char_ranges @@ -165,6 +155,8 @@ def main(): if is_python_file: new_sb_lines = [] for line in code_lines: + if line.endswith(" # noqa") and line.count(" # noqa") == 1: + line = line.replace(" # noqa", "") line_length2 = len(line) # Normal Python string length used line_length = get_width(line) # Special characters count 2X if line_length > code_width: @@ -254,6 +246,54 @@ def main(): else: new_sb_lines.append(line) continue + elif line.count('("') == 1: + whitespace = line_length2 - len(line.lstrip()) + new_ws = line[0:whitespace] + " " + line1 = line.split('("')[0] + '(' + line2 = new_ws + '"' + line.split('("')[1] + if not ('):') in line2: + new_sb_lines.append(line1) + if get_width(line2) + w > console_width: + if line2.count('" in self.') == 1: + line2a = line2.split( + '" in self.')[0] + '" in' + line2b = new_ws + "self." + ( + line2.split('" in self.')[1]) + new_sb_lines.append(line2a) + new_sb_lines.append(line2b) + continue + new_sb_lines.append(line2) + elif get_width(line2) + 4 + w <= console_width: + line2 = " " + line2 + new_sb_lines.append(line1) + new_sb_lines.append(line2) + else: + new_sb_lines.append(line) + continue + elif line.count("('") == 1: + whitespace = line_length2 - len(line.lstrip()) + new_ws = line[0:whitespace] + " " + line1 = line.split("('")[0] + '(' + line2 = new_ws + "'" + line.split("('")[1] + if not ('):') in line2: + new_sb_lines.append(line1) + if get_width(line2) + w > console_width: + if line2.count("' in self.") == 1: + line2a = line2.split( + "' in self.")[0] + "' in" + line2b = new_ws + "self." + ( + line2.split("' in self.")[1]) + new_sb_lines.append(line2a) + new_sb_lines.append(line2b) + continue + new_sb_lines.append(line2) + elif get_width(line2) + 4 + w <= console_width: + line2 = " " + line2 + new_sb_lines.append(line1) + new_sb_lines.append(line2) + else: + new_sb_lines.append(line) + continue elif line.count('= "') == 1 and line.count('://') == 1: whitespace = line_length2 - len(line.lstrip()) new_ws = line[0:whitespace] + " " diff --git a/seleniumbase/core/browser_launcher.py b/seleniumbase/core/browser_launcher.py index 7e3c36f29f2..f4c6f87bce4 100755 --- a/seleniumbase/core/browser_launcher.py +++ b/seleniumbase/core/browser_launcher.py @@ -4,7 +4,6 @@ import random import re import sys -import threading import time import urllib3 import warnings @@ -15,7 +14,6 @@ from seleniumbase.config import settings from seleniumbase.core import download_helper from seleniumbase.core import proxy_helper -from seleniumbase.core import capabilities_parser from seleniumbase.fixtures import constants from seleniumbase.fixtures import page_utils from seleniumbase import drivers # webdriver storage folder for SeleniumBase @@ -106,6 +104,7 @@ def _add_chrome_proxy_extension( proxy_helper.create_proxy_zip(proxy_string, proxy_user, proxy_pass) else: # Pytest multi-threaded test + import threading lock = threading.Lock() with lock: time.sleep(random.uniform(0.02, 0.15)) @@ -421,6 +420,7 @@ def get_remote_driver( desired_caps = {} extra_caps = {} if cap_file: + from seleniumbase.core import capabilities_parser desired_caps = capabilities_parser.get_desired_capabilities(cap_file) if cap_string: try: diff --git a/seleniumbase/fixtures/base_case.py b/seleniumbase/fixtures/base_case.py index e76fb1cf3c1..17d2fa7bf1e 100755 --- a/seleniumbase/fixtures/base_case.py +++ b/seleniumbase/fixtures/base_case.py @@ -11,7 +11,7 @@ class MyTestClass(BaseCase): def test_anything(self): # Write your code here. Example: self.open("https://github.com/") - self.update_text("input.header-search-input", "SeleniumBase\n") + self.type("input.header-search-input", "SeleniumBase\n") self.click('a[href="/seleniumbase/SeleniumBase"]') self.assert_element("div.repository-content") .... @@ -32,7 +32,6 @@ def test_anything(self): import time import urllib3 import unittest -import uuid from selenium.common.exceptions import (StaleElementReferenceException, MoveTargetOutOfBoundsException, WebDriverException) @@ -40,17 +39,11 @@ def test_anything(self): from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.remote.remote_connection import LOGGER -from selenium.webdriver.support.ui import Select from seleniumbase import config as sb_config from seleniumbase.common import decorators from seleniumbase.config import settings -from seleniumbase.core.testcase_manager import TestcaseDataPayload -from seleniumbase.core.testcase_manager import TestcaseManager -from seleniumbase.core import download_helper from seleniumbase.core import log_helper -from seleniumbase.core import settings_parser from seleniumbase.core import tour_helper -from seleniumbase.core import visual_helper from seleniumbase.fixtures import constants from seleniumbase.fixtures import js_utils from seleniumbase.fixtures import page_actions @@ -89,6 +82,7 @@ def __init__(self, *args, **kwargs): self.__device_height = None self.__device_pixel_ratio = None # Requires self._* instead of self.__* for external class use + self._language = "English" self._html_report_extra = [] # (Used by pytest_plugin.py) self._default_driver = None self._drivers_list = [] @@ -252,7 +246,7 @@ def click_chain(self, selectors_list, by=By.CSS_SELECTOR, if spacing > 0: time.sleep(spacing) - def update_text(self, selector, new_value, by=By.CSS_SELECTOR, + def update_text(self, selector, text, by=By.CSS_SELECTOR, timeout=None, retry=False): """ This method updates an element's text field with new text. Has multiple parts: @@ -263,7 +257,7 @@ def update_text(self, selector, new_value, by=By.CSS_SELECTOR, * Hits Enter/Submit (if the text ends in "\n"). @Params selector - the selector of the text field - new_value - the new value to type into the text field + text - the new text to type into the text field by - the type of selector to search by (Default: CSS Selector) timeout - how long to wait for the selector to be visible retry - if True, use JS if the Selenium text update fails @@ -293,16 +287,15 @@ def update_text(self, selector, new_value, by=By.CSS_SELECTOR, pass # Clearing the text field first isn't critical self.__demo_mode_pause_if_active(tiny=True) pre_action_url = self.driver.current_url - if type(new_value) is int or type(new_value) is float: - new_value = str(new_value) + if type(text) is int or type(text) is float: + text = str(text) try: - if not new_value.endswith('\n'): - element.send_keys(new_value) + if not text.endswith('\n'): + element.send_keys(text) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() else: - new_value = new_value[:-1] - element.send_keys(new_value) + element.send_keys(text[:-1]) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() @@ -312,21 +305,20 @@ def update_text(self, selector, new_value, by=By.CSS_SELECTOR, element = self.wait_for_element_visible( selector, by=by, timeout=timeout) element.clear() - if not new_value.endswith('\n'): - element.send_keys(new_value) + if not text.endswith('\n'): + element.send_keys(text) else: - new_value = new_value[:-1] - element.send_keys(new_value) + element.send_keys(text[:-1]) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() except Exception: exc_message = self.__get_improved_exception_message() raise Exception(exc_message) - if (retry and element.get_attribute('value') != new_value and ( - not new_value.endswith('\n'))): + if (retry and element.get_attribute('value') != text and ( + not text.endswith('\n'))): logging.debug('update_text() is falling back to JavaScript!') - self.set_value(selector, new_value, by=by) + self.set_value(selector, text, by=by) if self.demo_mode: if self.driver.current_url != pre_action_url: self.__demo_mode_pause_if_active() @@ -353,8 +345,7 @@ def add_text(self, selector, text, by=By.CSS_SELECTOR, timeout=None): if not text.endswith('\n'): element.send_keys(text) else: - text = text[:-1] - element.send_keys(text) + element.send_keys(text[:-1]) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() @@ -366,8 +357,7 @@ def add_text(self, selector, text, by=By.CSS_SELECTOR, timeout=None): if not text.endswith('\n'): element.send_keys(text) else: - text = text[:-1] - element.send_keys(text) + element.send_keys(text[:-1]) element.send_keys(Keys.RETURN) if settings.WAIT_FOR_RSC_ON_PAGE_LOADS: self.wait_for_ready_state_complete() @@ -382,6 +372,31 @@ def add_text(self, selector, text, by=By.CSS_SELECTOR, timeout=None): elif self.slow_mode: self.__slow_mode_pause_if_active() + def type(self, selector, text, by=By.CSS_SELECTOR, + timeout=None, retry=False): + """ Same as self.update_text() + This method updates an element's text field with new text. + Has multiple parts: + * Waits for the element to be visible. + * Waits for the element to be interactive. + * Clears the text field. + * Types in the new text. + * Hits Enter/Submit (if the text ends in "\n"). + @Params + selector - the selector of the text field + text - the new text to type into the text field + by - the type of selector to search by (Default: CSS Selector) + timeout - how long to wait for the selector to be visible + retry - if True, use JS if the Selenium text update fails + DO NOT confuse self.type() with Python type()! They are different! + """ + if not timeout: + timeout = settings.LARGE_TIMEOUT + if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT: + timeout = self.__get_new_timeout(timeout) + selector, by = self.__recalculate_selector(selector, by) + self.update_text(selector, text, by=by, timeout=timeout, retry=retry) + def submit(self, selector, by=By.CSS_SELECTOR): """ Alternative to self.driver.find_element_by_*(SELECTOR).submit() """ selector, by = self.__recalculate_selector(selector, by) @@ -1281,6 +1296,7 @@ def __select_option(self, dropdown_selector, option, """ Selects an HTML