//h4[contains(text(),'Some regular text ')]
You are starting correct, though the first part, //h4[contains(text(),'Some regular text ')]
, looks for any h4
at any depth, with among its immediate children a text node containing Some regular text
. Meaning, if that text appears after the strong
element, it will also match the h4
.
/descendant::strong[contains(text(), 'followed by strong text')]
You chose descendant::
, which means it will search at any depth from h4
for a strong
element. If you had <span><div><strong>...</strong></div></span>
it would also find it. If that's the intend, leave it as is. If not, and you want an immediate child, then use just /strong[...]
.
Your XPath as a whole will select that strong
element containing the text followed by strong text
, with an ancestor h4
as specified before.
Perhaps matching the first two-thirds is sufficient
You say this is ⅔ of what you wanted to achieve. Do you mean you need to select the text node? In that case, append the expression with /text()
.
Essentially, this XPath returns something, or nothing. It will return nothing if it can't find both texts the way you specified it. Otherwise it will return the strong
element. If you just need to verify its existence, you are done.
through the end of the strong tag
This is not quite right, but I understand the confusion. XPath does not see tags, it sees elements (or more generally: nodes), which is the whole of the opening tag and the closing tag, all its descendants, processing instructions, attributes and namespaces. As such, and as suggested, you can then query whatever is inside the strong
element.
Update (selecting following sibling)
After your comment, I think the rules are as follows:
- Any
h4
with an immediate child strong
- Before
strong
contains text X
- Inside
strong
, contains text Y
- After
strong
, contains text Z
This can be written as:
//h4[text()[1][contains(., 'X')]]
/strong
[contains(., 'Y')]
[following-sibling::text()[1][contains(., 'Z')]]
Written in such a way that it still returns the strong
element. If you want the whole h4
to be returned, write everything as a predicate of it (though for a boolean test whether this returns anything, this obviously won't matter):
//h4
[text()[1][contains(., 'X')]
and text()[last()][contains(', 'Z')]]
[strong[contains(., 'Y')]]
(for illustration, it uses a slightly different approach for the third text match)