I have a block of code like so:
<ul class="open-menu">
<span>
<li data-testid="menu-item" class="menu-item option">
<svg>...</svg>
<div>
<strong>Text Here</strong>
<small>...</small>
</div>
</li>
<li data-testid="menu-item" class="menu-item option">
<svg>...</svg>
<div>
<strong>Text</strong>
<small>...</small>
</div>
</li>
</span>
</ul>
I'm trying to select a menu item based on exact text like so in the dev tools:
$x('.//*[contains(@data-testid, "menu-item") and normalize-space() = "Text"]');
But this doesn't seem to be selecting the element. However, when I do:
$x('.//*[contains(@data-testid, "menu-item")]');
I can see both of the menu items.
UPDATE:
It seems that this works:
$x('.//*[contains(@class, "menu-item") and normalize-space() = "Text"]');
Not sure why using a class in this context works and not a data-testid. How can I get my xpath selector to work with my data-testid?
Why is this exact text selector not working
The fact that both li
elements are matched by the XPath expression
if omitting the condition normalize-space() = "Text"
is a clue.
normalize-space()
returns ... Text Here ...
for the first li
in the posted XML and ... Text ...
for the second (or some other
content in place of ...
from div/svg
or div/small
) causing
normalize-space() = "Text"
to fail.
In an update you say the same condition succeeds. This has nothing to
do with using @class
instead of @data-testid
; it must be triggered
by some content change.
How can I get my xpath selector to work with my data-testid?
By testing for an exact text match in the li
's descendant strong
element,
.//*[@data-testid = "menu-item" and div/strong = "Text"]
which matches the second li
. Making the test more robust is usually
in order, e.g.
.//*[contains(@data-testid,"menu-item") and normalize-space(div/strong) = "Text"]
Append /div/small
or /descendant::small
, for example, to the XPath
expression to extract just the small
text.