Skip to content

Optimize selector detection and usage #346

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 21, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,10 +202,10 @@ pytest test_suite.py --html=report.html

![](https://cdn2.hubspot.net/hubfs/100006/images/PytestReport.png "Example Pytest Report")

You can also use ``--junitxml=report.xml`` to get an xml report instead. Jenkins can use this file to display better reporting for your tests.
You can also use ``--junit-xml=report.xml`` to get an xml report instead. Jenkins can use this file to display better reporting for your tests.

```
pytest test_suite.py --junitxml=report.xml
pytest test_suite.py --junit-xml=report.xml
```

#### **Nosetest Reports:**
Expand Down
6 changes: 3 additions & 3 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,13 @@ jobs:
pytest nothing.py
displayName: 'Make sure pytest is working'

- script: pytest examples/boilerplates/boilerplate_test.py --browser=chrome --headless -v -s --junitxml=junit/test-results.xml
- script: pytest examples/boilerplates/boilerplate_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml
displayName: 'Run pytest boilerplate_test.py --browser=chrome --headless'

- script: pytest examples/my_first_test.py --browser=chrome --headless -v -s --junitxml=junit/test-results.xml
- script: pytest examples/my_first_test.py --browser=chrome --headless -v -s --junit-xml=junit/test-results.xml
displayName: 'Run pytest my_first_test.py --browser=chrome --headless'

- script: pytest examples/my_first_test.py --browser=firefox --headless -v -s --junitxml=junit/test-results.xml
- script: pytest examples/my_first_test.py --browser=firefox --headless -v -s --junit-xml=junit/test-results.xml
displayName: 'Run pytest my_first_test.py --browser=firefox --headless'

- task: PublishTestResults@2
Expand Down
2 changes: 1 addition & 1 deletion help_docs/how_it_works.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<a id="how_seleniumbase_works"></a>
### ![http://seleniumbase.com](https://cdn2.hubspot.net/hubfs/100006/images/super_logo_tiny.png "SeleniumBase") **How SeleniumBase Works:**
### <img src="https://cdn2.hubspot.net/hubfs/100006/images/super_square_logo_3a.png" title="SeleniumBase" height="32"> **How SeleniumBase Works:**

At the core, SeleniumBase works by extending [Pytest](https://docs.pytest.org/en/latest/) and [Nosetests](http://nose.readthedocs.io/en/latest/) as a direct plugin to each one. SeleniumBase automatically spins up web browsers for tests, and then gives those tests access to the SeleniumBase libraries through the BaseCase class, [found here](https://github.com/seleniumbase/SeleniumBase/blob/master/seleniumbase/fixtures/base_case.py). Now you can use "``--browser=chrome``" to specify the web browser to use (Default = "Chrome"). You can also include additional plugins for additional features such as "``--demo_mode``" (for highlighting elements & changing the speed of test runs) and "``--proxy=IP_ADDRESS:PORT``" (for using a proxy server during test runs). There are also other plugins available such as "``--with-db_reporting``", "``--with-s3_logging``", and more.

Expand Down
2 changes: 1 addition & 1 deletion help_docs/virtualenv_instructions.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Virtual Environment Tutorial
## <img src="https://cdn2.hubspot.net/hubfs/100006/images/super_square_logo_3a.png" title="SeleniumBase" height="32"> Virtual Environment Tutorial

### **Step 1**: First install [VirtualEnv](http://virtualenv.readthedocs.org/en/latest/) and [VirtualEnvWrapper](http://virtualenvwrapper.readthedocs.org/en/latest/) (<i>if not installed</i>):

Expand Down
131 changes: 35 additions & 96 deletions seleniumbase/fixtures/base_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,17 @@ def click(self, selector, by=By.CSS_SELECTOR,
timeout=settings.SMALL_TIMEOUT, delay=0):
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
if page_utils.is_xpath_selector(selector):
by = By.XPATH
selector, by = self.__recalculate_selector(selector, by)
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
if not self.is_link_text_visible(selector):
# Handle a special case of links hidden in dropdowns
self.click_link_text(selector, timeout=timeout)
return
if page_utils.is_partial_link_text_selector(selector):
if not self.is_partial_link_text_visible(selector):
# Handle a special case of partial links hidden in dropdowns
self.click_partial_link_text(selector, timeout=timeout)
return
element = page_actions.wait_for_element_visible(
self.driver, selector, by, timeout=timeout)
self.__demo_mode_highlight_if_active(selector, by)
Expand Down Expand Up @@ -427,11 +429,7 @@ def get_attribute(self, selector, attribute, by=By.CSS_SELECTOR,
""" This method uses JavaScript to get the value of an attribute. """
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
self.wait_for_ready_state_complete()
time.sleep(0.01)
element = page_actions.wait_for_element_present(
Expand Down Expand Up @@ -496,11 +494,7 @@ def get_property_value(self, selector, property, by=By.CSS_SELECTOR,
self.assertTrue(float(opacity) > 0, "Element not visible!") """
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
self.wait_for_ready_state_complete()
page_actions.wait_for_element_present(
self.driver, selector, by, timeout)
Expand Down Expand Up @@ -704,19 +698,11 @@ def type(self, selector, text, by=By.CSS_SELECTOR,
self.update_text(selector, text, by=by, timeout=timeout, retry=retry)

def is_element_present(self, selector, by=By.CSS_SELECTOR):
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
return page_actions.is_element_present(self.driver, selector, by)

def is_element_visible(self, selector, by=By.CSS_SELECTOR):
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
return page_actions.is_element_visible(self.driver, selector, by)

def is_link_text_visible(self, link_text):
Expand All @@ -734,22 +720,14 @@ def is_partial_link_text_visible(self, partial_link_text):
def is_text_visible(self, text, selector="html", by=By.CSS_SELECTOR):
self.wait_for_ready_state_complete()
time.sleep(0.01)
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
return page_actions.is_text_visible(self.driver, text, selector, by)

def find_elements(self, selector, by=By.CSS_SELECTOR, limit=0):
""" Returns a list of matching WebElements.
If "limit" is set and > 0, will only return that many elements. """
self.wait_for_ready_state_complete()
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
elements = self.driver.find_elements(by=by, value=selector)
if limit and limit > 0 and len(elements) > limit:
elements = elements[:limit]
Expand All @@ -759,11 +737,7 @@ def find_visible_elements(self, selector, by=By.CSS_SELECTOR, limit=0):
""" Returns a list of matching WebElements that are visible.
If "limit" is set and > 0, will only return that many elements. """
self.wait_for_ready_state_complete()
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
v_elems = page_actions.find_visible_elements(self.driver, selector, by)
if limit and limit > 0 and len(v_elems) > limit:
v_elems = v_elems[:limit]
Expand Down Expand Up @@ -1965,12 +1939,10 @@ def jquery_update_text(self, selector, new_value, by=By.CSS_SELECTOR,
self.__demo_mode_pause_if_active()

def hover_on_element(self, selector, by=By.CSS_SELECTOR):
selector, by = self.__recalculate_selector(selector, by)
if page_utils.is_xpath_selector(selector):
selector = self.convert_to_css_selector(selector, By.XPATH)
by = By.CSS_SELECTOR
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
self.wait_for_element_visible(
selector, by=by, timeout=settings.SMALL_TIMEOUT)
self.__demo_mode_highlight_if_active(selector, by)
Expand All @@ -1983,20 +1955,12 @@ def hover_and_click(self, hover_selector, click_selector,
timeout=settings.SMALL_TIMEOUT):
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
if page_utils.is_xpath_selector(hover_selector):
hover_selector = self.convert_to_css_selector(
hover_selector, By.XPATH)
hover_by = By.CSS_SELECTOR
if page_utils.is_xpath_selector(click_selector):
click_by = By.XPATH
if page_utils.is_link_text_selector(hover_selector):
hover_selector = page_utils.get_link_text_from_selector(
hover_selector)
hover_by = By.LINK_TEXT
if page_utils.is_link_text_selector(click_selector):
click_selector = page_utils.get_link_text_from_selector(
click_selector)
click_by = By.LINK_TEXT
hover_selector, hover_by = self.__recalculate_selector(
hover_selector, hover_by)
hover_selector = self.convert_to_css_selector(
hover_selector, hover_by)
click_selector, click_by = self.__recalculate_selector(
click_selector, click_by)
self.wait_for_element_visible(
hover_selector, by=hover_by, timeout=timeout)
self.__demo_mode_highlight_if_active(hover_selector, hover_by)
Expand Down Expand Up @@ -2164,11 +2128,7 @@ def wait_for_element_present(self, selector, by=By.CSS_SELECTOR,
The element does not need be visible (it may be hidden). """
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
return page_actions.wait_for_element_present(
self.driver, selector, by, timeout)

Expand Down Expand Up @@ -2200,11 +2160,7 @@ def wait_for_element_visible(self, selector, by=By.CSS_SELECTOR,
timeout=settings.LARGE_TIMEOUT):
""" Waits for an element to appear in the HTML of a page.
The element must be visible (it cannot be hidden). """
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
return page_actions.wait_for_element_visible(
self.driver, selector, by, timeout)

Expand Down Expand Up @@ -2232,11 +2188,7 @@ def assert_element(self, selector, by=By.CSS_SELECTOR,
self.wait_for_element_visible(selector, by=by, timeout=timeout)

if self.demo_mode:
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
messenger_post = "ASSERT %s: %s" % (by, selector)
self.__highlight_with_assert_success(messenger_post, selector, by)
return True
Expand All @@ -2259,11 +2211,7 @@ def wait_for_text_visible(self, text, selector="html", by=By.CSS_SELECTOR,
timeout=settings.LARGE_TIMEOUT):
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
return page_actions.wait_for_text_visible(
self.driver, text, selector, by, timeout)

Expand All @@ -2272,11 +2220,7 @@ def wait_for_exact_text_visible(self, text, selector="html",
timeout=settings.LARGE_TIMEOUT):
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
return page_actions.wait_for_exact_text_visible(
self.driver, text, selector, by, timeout)

Expand Down Expand Up @@ -2313,11 +2257,7 @@ def assert_text(self, text, selector="html", by=By.CSS_SELECTOR,
self.wait_for_text_visible(text, selector, by=by, timeout=timeout)

if self.demo_mode:
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
messenger_post = ("ASSERT TEXT {%s} in %s: %s"
% (text, by, selector))
self.__highlight_with_assert_success(messenger_post, selector, by)
Expand All @@ -2336,11 +2276,7 @@ def assert_exact_text(self, text, selector="html", by=By.CSS_SELECTOR,
text, selector, by=by, timeout=timeout)

if self.demo_mode:
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
messenger_post = ("ASSERT TEXT {%s} in %s: %s"
% (text, by, selector))
self.__highlight_with_assert_success(messenger_post, selector, by)
Expand Down Expand Up @@ -2447,11 +2383,7 @@ def wait_for_element_not_visible(self, selector, by=By.CSS_SELECTOR,
to qualify as not visible. """
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
timeout = self.__get_new_timeout(timeout)
if page_utils.is_xpath_selector(selector):
by = By.XPATH
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
selector, by = self.__recalculate_selector(selector, by)
return page_actions.wait_for_element_not_visible(
self.driver, selector, by, timeout)

Expand Down Expand Up @@ -3072,6 +3004,13 @@ def __recalculate_selector(self, selector, by):
if page_utils.is_link_text_selector(selector):
selector = page_utils.get_link_text_from_selector(selector)
by = By.LINK_TEXT
if page_utils.is_partial_link_text_selector(selector):
selector = page_utils.get_partial_link_text_from_selector(selector)
by = By.PARTIAL_LINK_TEXT
if page_utils.is_name_selector(selector):
name = page_utils.get_name_from_selector(selector)
selector = '[name="%s"]' % name
by = By.CSS_SELECTOR
return (selector, by)

def __make_css_match_first_element_only(self, selector):
Expand Down
47 changes: 46 additions & 1 deletion seleniumbase/fixtures/page_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,28 @@ def is_link_text_selector(selector):
"""
A basic method to determine if a selector is a link text selector.
"""
if (selector.startswith('link=') or selector.startswith('link_text=')):
if (selector.startswith('link=') or selector.startswith('link_text=') or (
selector.startswith('text='))):
return True
return False


def is_partial_link_text_selector(selector):
"""
A basic method to determine if a selector is a partial link text selector.
"""
if (selector.startswith('partial_link=') or (
selector.startswith('partial_link_text=') or (
selector.startswith('partial_text=')))):
return True
return False


def is_name_selector(selector):
"""
A basic method to determine if a selector is a name selector.
"""
if selector.startswith('name='):
return True
return False

Expand All @@ -49,6 +70,30 @@ def get_link_text_from_selector(selector):
return selector.split('link=')[1]
elif selector.startswith('link_text='):
return selector.split('link_text=')[1]
elif selector.startswith('text='):
return selector.split('text=')[1]
return selector


def get_partial_link_text_from_selector(selector):
"""
A basic method to get the partial link text from a partial link selector.
"""
if selector.startswith('partial_link='):
return selector.split('partial_link=')[1]
elif selector.startswith('partial_link_text='):
return selector.split('partial_link_text=')[1]
elif selector.startswith('partial_text='):
return selector.split('partial_text=')[1]
return selector


def get_name_from_selector(selector):
"""
A basic method to get the name from a name selector.
"""
if selector.startswith('name='):
return selector.split('name=')[1]
return selector


Expand Down
2 changes: 1 addition & 1 deletion seleniumbase/plugins/pytest_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def pytest_addoption(parser):
default=True,
help="""This is used by the BaseCase class to tell apart
pytest runs from nosetest runs. (Automatic)""")
parser.addoption('--demo_mode', '--demo-mode',
parser.addoption('--demo_mode', '--demo-mode', '--demo',
action="store_true",
dest='demo_mode',
default=False,
Expand Down
Loading