Skip to content
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
24 changes: 13 additions & 11 deletions src/Selenium2Library/keywords/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

from Selenium2Library.base import LibraryComponent, keyword
from Selenium2Library.keywords.formelement import FormElementKeywords
from Selenium2Library.locators.customlocator import CustomLocator
from Selenium2Library.utils import escape_xpath_value


Expand Down Expand Up @@ -716,31 +715,34 @@ def xpath_should_match_x_times(self, xpath, expected_xpath_count, message='', lo

@keyword
def add_location_strategy(self, strategy_name, strategy_keyword, persist=False):
"""Adds a custom location strategy based on a user keyword. Location strategies are
automatically removed after leaving the current scope by default. Setting `persist`
to any non-empty string will cause the location strategy to stay registered throughout
the life of the test.
"""Adds a custom location strategy based on a keyword.

Trying to add a custom location strategy with the same name as one that already exists will
cause the keyword to fail.
Location strategies are automatically removed after leaving the current
scope by default. Setting `persist` to Python True will cause the
location strategy to stay registered throughout the life of the test.

Trying to add a custom location strategy with the same name as one that
already exists will cause the keyword to fail.

Custom locator keyword example:
| Custom Locator Strategy | [Arguments] | ${browser} | ${criteria} | ${tag} | ${constraints} |
| Custom Locator Strategy |
| | [Arguments] | ${browser} | ${criteria} | ${tag} | ${constraints} |
| | ${retVal}= | Execute Javascript | return window.document.getElementById('${criteria}'); |
| | [Return] | ${retVal} |

Usage example:
| Add Location Strategy | custom | Custom Locator Strategy |
| Page Should Contain Element | custom=my_id |

See `Remove Location Strategy` for details about removing a custom location strategy.
See `Remove Location Strategy` for details about removing a custom
location strategy.
"""
strategy = CustomLocator(strategy_name, strategy_keyword)
self.element_finder.register(strategy, persist)
self.element_finder.register(strategy_name, strategy_keyword, persist)

@keyword
def remove_location_strategy(self, strategy_name):
"""Removes a previously added custom location strategy.

Will fail if a default strategy is specified.

See `Add Location Strategy` for details about adding a custom location strategy.
Expand Down
19 changes: 12 additions & 7 deletions src/Selenium2Library/locators/customlocator.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,34 @@
from robot.libraries.BuiltIn import BuiltIn

from Selenium2Library.base import ContextAware


try:
basestring
except NameError:
basestring = str


class CustomLocator(object):
class CustomLocator(ContextAware):

def __init__(self, name, finder):
def __init__(self, ctx, name, finder):
ContextAware.__init__(self, ctx)
self.name = name
self.finder = finder

def find(self, *args):
def find(self, criteria, tag, constraints):
# Allow custom locators to be keywords or normal methods
if isinstance(self.finder, basestring):
element = BuiltIn().run_keyword(self.finder, *args)
element = BuiltIn().run_keyword(self.finder, self.browser,
criteria, tag, constraints)
elif hasattr(self.finder, '__call__'):
element = self.finder(*args)
element = self.finder(self.browser, criteria, tag, constraints)
else:
raise AttributeError('Invalid type provided for Custom Locator %s' % self.name)
raise AttributeError('Invalid type provided for Custom Locator %s'
% self.name)

# Always return an array
if hasattr(element, '__len__') and (not isinstance(element, basestring)):
if hasattr(element, '__len__') and not isinstance(element, basestring):
return element
else:
return [element]
5 changes: 3 additions & 2 deletions src/Selenium2Library/locators/elementfinder.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from selenium.webdriver.remote.webelement import WebElement

from Selenium2Library.base import ContextAware
from Selenium2Library.locators.customlocator import CustomLocator

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use from .locators import ... here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is fixed in #840

from Selenium2Library.utils import escape_xpath_value, events


Expand Down Expand Up @@ -89,13 +90,13 @@ def get_value(self, locator, tag=None):
element = self.find(locator, tag, required=False)
return element.get_attribute('value') if element else None

def register(self, strategy, persist):
def register(self, strategy_name, strategy_keyword, persist=False):
strategy = CustomLocator(self.ctx, strategy_name, strategy_keyword)
if strategy.name in self._strategies:
raise RuntimeError("The custom locator '%s' cannot be registered. "
"A locator of that name already exists."
% strategy.name)
self._strategies[strategy.name] = strategy.find

if not persist:
# Unregister after current scope ends
events.on('scope_end', 'current', self.unregister, strategy.name)
Expand Down
2 changes: 1 addition & 1 deletion src/Selenium2Library/utils/events/scope_event.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def __init__(self, scope, action, *args, **kwargs):

if scope == 'current':
suite = BuiltIn().get_variable_value('${SUITE NAME}')
test = BuiltIn().get_variable_value('${TEST NAME}', '')
test = BuiltIn().get_variable_value('${TEST NAME}', '')
self.scope = suite + '.' + test if test != '' else suite

def trigger(self, *args, **kwargs):
Expand Down
14 changes: 3 additions & 11 deletions test/acceptance/locators/custom.robot
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ Resource ../resource.robot

*** Test Cases ***
Test Custom Locator
[Documentation] Test Custom Locator
[Setup] Setup Custom Locator
Page Should Contain Element custom=some_id
Page Should Not Contain Element custom=invalid_id
Expand All @@ -17,38 +16,31 @@ Ensure Locator Auto Unregisters
Setup Custom Locator

Ensure Attempting to Unregister a Default Locator Fails
[Documentation] Ensure Attempting to Unregister a Default Locator Fails
Run Keyword And Expect Error * Remove Location Strategy id

Ensure Unregistering a Non-Existent Locator Does Not Fail
[Documentation] Ensure Unregistering a Non-Existent Locator Does Not Fail
Teardown Custom Locator

Ensure a Custom Locator can be Unregistered
[Documentation] Ensure a Custom Locator can be Unregistered
Setup Custom Locator persist
Teardown Custom Locator
# Test step is used for the next test. It sets up a persistant locator.
Setup Custom Locator persist

Ensure Locators Can Persist
[Documentation] Ensure Locators Can Persist
Page Should Contain Element custom=some_id
Run Keyword And Expect Error * Setup Custom Locator
[Teardown] Teardown Custom Locator

*** Keywords ***
Setup Custom Locator
[Arguments] ${persist}=${EMPTY}
[Documentation] Setup Custom Locator
Add Location Strategy custom Custom Locator Strategy persist=${persist}

Teardown Custom Locator
[Documentation] Teardown Custom Locator
Remove Location Strategy custom

Custom Locator Strategy
[Arguments] ${criteria} ${tag} ${constraints}
[Documentation] Custom Locator Strategy
${retVal}= Execute Javascript return window.document.getElementById('${criteria}') || [];
[Return] ${retVal}
[Arguments] ${browser} ${locator} ${tag} ${constraints}
${element}= Execute Javascript return window.document.getElementById('${locator}') || [];
[Return] ${element}