XPathy introduces Having operations, which allow you to define conditions on
related elements inside the same expression. The byHaving() method
acts as a predicate builder.
1. Basic Having with Direct Condition
XPathy locator = div.byAttribute(class_).equals("product-card").and()
.byHaving(
span.byText().contains("In Stock")
);
//div[@class='product-card' and ( span[contains(text(), 'In Stock')] )]
Use Case: Selects <div class="product-card"> elements
only if they contain a <span> with text containing "In Stock".
2. Having with Child
XPathy locator = table.byHaving().child(
tr.byAttribute(class_).equals("total-row")
);
//table[( ./tr[@class='total-row'] )]
Use Case: Matches an <table> if it has a direct child row
with the class total-row.
3. Having with Descendant
XPathy locator = section.byAttribute(id).equals("checkout").and()
.byHaving().descendant(
button.byText().contains("Place Order")
);
//section[@id='checkout' and ( .//button[contains(text(), 'Place Order')] )]
Use Case: Matches a <section id="checkout"> if any nested
descendant button contains the text "Place Order".
4. Having with Ancestor
XPathy locator = div.byAttribute(class_).equals("price-tag").and()
.byHaving().ancestor(
section.byAttribute(id).equals("product-details")
);
//div[@class='price-tag' and ( ancestor::section[@id='product-details'] )]
Use Case: Matches <div class="price-tag"> only if it has
a <section> ancestor with id="product-details".
5. Having with Parent
XPathy locator = ul.byAttribute(class_).equals("menu-items").and()
.byHaving().parent(
nav.byAttribute(role).equals("navigation")
);
//ul[@class='menu-items' and ( parent::nav[@role='navigation'] )]
Use Case: Matches <ul class="menu-items"> only if its
immediate parent <nav> has the role navigation.
6. Having with Siblings
Following Sibling
XPathy locator = h2.byText().equals("Features").and()
.byHaving().followingSibling(
div.byAttribute(class_).equals("description")
);
//h2[text()='Features' and ( following-sibling::div[@class='description'] )]
Preceding Sibling
XPathy locator = li.byText().equals("Contact").and()
.byHaving().precedingSibling(
li.byText().equals("About")
);
//li[text()='Contact' and ( preceding-sibling::li[text()='About'] )]
7. Simplified Workflow
XPathy locator = table.byAttribute(id).equals("invoice").and()
.byHaving().child(td).byText().contains("Subtotal");
//table[@id='invoice' and ./td[contains(text(), 'Subtotal')]]
Use Case: Matches <table id="invoice"> if it contains a
direct <td> child with text "Subtotal".
- Readability: Express complex DOM relationships inline without writing full XPath manually
- Flexibility: Mix with transformations for resilient conditions
- Reusability: You can insert any XPathy expression into
byHaving()