Skip to content

Commit ab8f013

Browse files
committed
Support 'data-' attributes as locators
Adds new locator by data-* id's with syntax: data:id_name:actual_id Fixes #1474
1 parent 81c6425 commit ab8f013

File tree

6 files changed

+56
-1
lines changed

6 files changed

+56
-1
lines changed

atest/acceptance/locators/locator_parsing.robot

+5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ xpath with // and without prefix should work
1414
xpath with (// and without prefix should work
1515
Page Should Contain Element (//div[@id="div_id"]/a)[1]
1616

17+
Locator with with data prefix
18+
Page Should Contain Element data:id:my_id
19+
Page Should Contain Element data:automation:my_automation_id
20+
Page Should Not Contain Element data:non_existent:some_random_id
21+
1722
Locator without prefix
1823
Page Should Contain Element div_id
1924

atest/resources/html/links.html

+2-1
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@
3636
<a href="target/second.html"><img src="robot.png" alt="Image"/></a>
3737
<a href="target/second.html">Link</a>
3838
</div>
39-
<div id="emptyDiv"></div>
39+
<div id="emptyDiv" data-id="my_id"></div>
4040
<a href="index.html" target="_blank">Target opens in new window</a>
4141
<br>
4242
<button type="button" id="nothing">Does nothing</button>
43+
<button type="button" data-automation="my_automation_id">Also Does nothing</button>
4344
</body>
4445
</html>

src/SeleniumLibrary/__init__.py

+4
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ class SeleniumLibrary(DynamicCore):
136136
| link | Exact text a link has. | ``link:The example`` |
137137
| partial link | Partial link text. | ``partial link:he ex`` |
138138
| sizzle | Sizzle selector deprecated. | ``sizzle:div.example`` |
139+
| data | Element ``data-*`` attribute | ``data:id:my_id`` |
139140
| jquery | jQuery expression. | ``jquery:div.example`` |
140141
| default | Keyword specific default behavior. | ``default:example`` |
141142
@@ -171,6 +172,9 @@ class SeleniumLibrary(DynamicCore):
171172
the system under test contains the jQuery library.
172173
- Prior to SeleniumLibrary 3.0, table related keywords only supported
173174
``xpath``, ``css`` and ``sizzle/jquery`` strategies.
175+
- ``data`` strategy is conveniance locator that will construct xpath from the parameters.
176+
If you have element like `<div data-automation="automation-id-2">`, you locate the element via
177+
``data:automation:automation-id-2``. This feature was added in SeleniumLibrary 5.2.0
174178
175179
=== Implicit XPath strategy ===
176180

src/SeleniumLibrary/locators/elementfinder.py

+14
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def __init__(self, ctx):
4646
"sizzle": self._find_by_jquery_selector,
4747
"tag": self._find_by_tag_name,
4848
"scLocator": self._find_by_sc_locator,
49+
"data": self._find_by_data_locator,
4950
"default": self._find_by_default,
5051
}
5152
self._strategies = NormalizedDict(
@@ -220,6 +221,19 @@ def _find_by_tag_name(self, criteria, tag, constraints, parent):
220221
parent.find_elements(By.TAG_NAME, criteria), tag, constraints
221222
)
222223

224+
def _find_by_data_locator(self, criteria, tag, constraints, parent):
225+
try:
226+
name, value = criteria.split(":", 2)
227+
if "" in [name, value]:
228+
raise ValueError
229+
except ValueError:
230+
raise ValueError(
231+
f"Provided selector ({criteria}) is malformed. Correct format: name:value."
232+
)
233+
234+
local_criteria = f'//*[@data-{name}="{value}"]'
235+
return self._find_by_xpath(local_criteria, tag, constraints, parent)
236+
223237
def _find_by_sc_locator(self, criteria, tag, constraints, parent):
224238
self._disallow_webelement_parent(parent)
225239
criteria = criteria.replace("'", "\\'")

utest/test/api/approved_files/PluginDocumentation.test_many_plugins.approved.txt

+4
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ below. In addition to them, it is possible to register `custom locators`.
7979
| link | Exact text a link has. | ``link:The example`` |
8080
| partial link | Partial link text. | ``partial link:he ex`` |
8181
| sizzle | Sizzle selector deprecated. | ``sizzle:div.example`` |
82+
| data | Element ``data-*`` attribute | ``data:id:my_id`` |
8283
| jquery | jQuery expression. | ``jquery:div.example`` |
8384
| default | Keyword specific default behavior. | ``default:example`` |
8485

@@ -114,6 +115,9 @@ Examples:
114115
the system under test contains the jQuery library.
115116
- Prior to SeleniumLibrary 3.0, table related keywords only supported
116117
``xpath``, ``css`` and ``sizzle/jquery`` strategies.
118+
- ``data`` strategy is conveniance locator that will construct xpath from the parameters.
119+
If you have element like `<div data-automation="automation-id-2">`, you locate the element via
120+
``data:automation:automation-id-2``. This feature was added in SeleniumLibrary 5.2.0
117121

118122
=== Implicit XPath strategy ===
119123

utest/test/locators/test_elementfinder.py

+27
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ def test_strategy_case_is_not_changed():
6868
_verify_parse_locator("XPATH=//foo/bar ", "XPATH", "//foo/bar ")
6969

7070

71+
def test_data_locator_parsing():
72+
_verify_parse_locator("data:id:my_id", "data", "id:my_id")
73+
_verify_parse_locator(
74+
"data:automation:my_automation_id", "data", "automation:my_automation_id"
75+
)
76+
77+
7178
def test_remove_whitespace_around_prefix_and_separator():
7279
_verify_parse_locator("class = foo", "class", "foo")
7380
_verify_parse_locator("class : foo", "class", "foo")
@@ -271,6 +278,26 @@ def test_find_with_tag(finder):
271278
verify(driver).find_elements(By.XPATH, "//div[(@id='test1' or @name='test1')]")
272279

273280

281+
def test_find_with_data(finder):
282+
driver = _get_driver(finder)
283+
finder.find("data:id:my_id", tag="div", required=False)
284+
verify(driver).find_elements(By.XPATH, '//*[@data-id="my_id"]')
285+
286+
287+
def test_find_with_invalid_data(finder):
288+
with pytest.raises(
289+
ValueError,
290+
match=r"^Provided selector \(id:\) is malformed\. Correct format: name:value\.",
291+
):
292+
finder.find("data:id:", tag="div", required=False)
293+
294+
with pytest.raises(
295+
ValueError,
296+
match=r"^Provided selector \(\) is malformed\. Correct format: name:value\.",
297+
):
298+
finder.find("data:", tag="div", required=False)
299+
300+
274301
def test_find_with_locator_with_apos(finder):
275302
driver = _get_driver(finder)
276303
finder.find("test '1'", required=False)

0 commit comments

Comments
 (0)