<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Volta Jebaprashanth</title>
    <description>The latest articles on Forem by Volta Jebaprashanth (@volta_jebaprashanth_ac7af).</description>
    <link>https://forem.com/volta_jebaprashanth_ac7af</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3495681%2Faad493dc-eace-4543-9c45-a350bd5f2301.jpg</url>
      <title>Forem: Volta Jebaprashanth</title>
      <link>https://forem.com/volta_jebaprashanth_ac7af</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/volta_jebaprashanth_ac7af"/>
    <language>en</language>
    <item>
      <title>The AI-Native Frontier: Redefining the Future of Quality Engineering</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Mon, 02 Mar 2026 06:33:23 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/the-ai-native-frontier-redefining-the-future-of-quality-engineering-kec</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/the-ai-native-frontier-redefining-the-future-of-quality-engineering-kec</guid>
      <description>&lt;p&gt;Recently, within the &lt;strong&gt;AT + GSC&amp;amp;MS&lt;/strong&gt; division at &lt;strong&gt;Sysco LABS&lt;/strong&gt;, our Quality Engineering community gathered for an event that felt less like a standard technical conference and more like a bold step into a new reality. &lt;strong&gt;QE Spark 2026&lt;/strong&gt; was a resounding success — not just as a showcase of cutting-edge technology, but as a declaration of a new strategy designed to fundamentally change how we think about scale, growth, and the role of the modern engineer.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faokkz3tfa177puti72lq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faokkz3tfa177puti72lq.jpg" alt="Sysco LABS AT + GSC&amp;amp;MS QE Team " width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This transformative milestone wouldn’t have been possible without the two pillars of this event: &lt;strong&gt;Sujith Thushara&lt;/strong&gt;, our Senior Manager Quality Engineering and &lt;strong&gt;Kasun de Silva&lt;/strong&gt;, our Architect — Quality Engineering. It was their coordination, leadership, and forward-thinking vision that laid the groundwork for this strategic shift. As a speaker at QE Spark 2026, I was incredibly proud to help bring their vision to life on stage and share our roadmap with an energized, forward-looking community.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fncjldxs8ejipp0nax6he.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fncjldxs8ejipp0nax6he.jpg" alt="Sujith Thushara" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sujith Thushara, Senior Manager Quality Engineering&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1qlmslgdur06ugkgk41.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1qlmslgdur06ugkgk41.jpg" alt="Kasun de Silva" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Kasun de Silva, Architect — Quality Engineering&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Vision: Breaking the Linear Curve
&lt;/h2&gt;

&lt;p&gt;For decades, the technology industry has operated under a predictable, yet limiting, law of physics: as business demand and revenue grow, headcount and operational costs must rise in parallel. This linear model is a hurdle to true innovation and velocity.&lt;/p&gt;

&lt;p&gt;The core strategy we unveiled at QE Spark is the creation of a &lt;strong&gt;“Growth Delta.”&lt;/strong&gt; By integrating intelligence into the very fabric of our delivery lifecycle, our goal is to empower business value to soar while keeping our operational footprint lean, agile, and efficient. We are successfully moving away from traditional, effort-based scaling and embracing intelligent growth.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3ob55yvnkkwt5mhp7jq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm3ob55yvnkkwt5mhp7jq.jpg" alt="Thanya" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Journey: From AI-Assisted to AI-Native
&lt;/h2&gt;

&lt;p&gt;A major highlight of the event was outlining our concrete roadmap. We aren’t just talking about the future; we are building it. The transition from legacy methods to an entirely AI-Native ecosystem is broken down into three distinct phases:&lt;/p&gt;

&lt;h3&gt;
  
  
  Traditional QE
&lt;/h3&gt;

&lt;p&gt;The baseline. This phase is defined by human-driven quality, heavy manual validation, and reactive defect detection.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI-Assisted QE
&lt;/h3&gt;

&lt;p&gt;Our current state of the art. Here, AI tools actively enhance human effort. We are operating with an automation-first mindset, utilizing tool-enhanced test creation, and driving dramatically faster feedback cycles.&lt;/p&gt;

&lt;h3&gt;
  
  
  AI-Native QE
&lt;/h3&gt;

&lt;p&gt;The true frontier. This future state moves far beyond mere “assistance” and into a world of Digital Twins, autonomous test evolution, and real-time quality intelligence. In this phase, AI isn’t just a tool; it is a predictive partner that identifies risks and evolves tests before a developer even pushes code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28rmw035mh61pp1f8v5f.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F28rmw035mh61pp1f8v5f.jpg" alt="Volta Jebaprashanth" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  A Mindset Shift: The Strategic Engineer
&lt;/h2&gt;

&lt;p&gt;One of the most profound takeaways from QE Spark 2026 was that this transformation isn’t just about software — it’s about people. As we move aggressively toward an AI-Native world, the role of the engineer is getting a massive upgrade.&lt;/p&gt;

&lt;p&gt;We are successfully breaking the “Regression Bottleneck,” where engineers historically spent half their time executing tests simply because those tests existed. Instead, the future Sysco Labs engineer is a systems thinker and a quality strategist. We are shifting from merely coordinating tests to reviewing intelligent decisions, architecting solutions, and validating complex business logic.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ts8ykx09s7qhgbbtswa.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7ts8ykx09s7qhgbbtswa.jpg" alt="Akila Rangalla" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Engine Room: A Collective Force of Builders
&lt;/h2&gt;

&lt;p&gt;While the vision set on stage was ambitious, the most exciting part of this transformation is what is happening off stage. This isn’t just a top-down mandate; it is a grassroots revolution driven by our entire engineering team.&lt;/p&gt;

&lt;p&gt;Our people are in the trenches right now, turning this AI-Native vision into a tangible reality. Across the floor, teams are deeply immersed in hands-on experimentation. We are spinning up rapid prototypes, stress-testing intelligent agents, and deploying cutting-edge Proofs of Concept that challenge the status quo of Quality Engineering. There is an electric culture of innovation taking hold, where every engineer is empowered to become a builder and a disruptor. We aren’t just waiting for the future of QE to arrive — we are actively coding it into existence today.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pmmoamuss6z97c0ygji.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2pmmoamuss6z97c0ygji.jpg" alt="Yaseen Mohamed" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Looking Ahead
&lt;/h2&gt;

&lt;p&gt;The overwhelming success of QE Spark 2026 was rooted in its clarity of purpose. By shifting from reactive detection to intelligent prevention, our team is actively clearing the path for a future where quality is a continuous, autonomous stream rather than a late-cycle hurdle.&lt;/p&gt;

&lt;p&gt;As the event concluded, the atmosphere was electric. We are no longer just asking, “How can we test this?” We are asking, “How can we do this smarter?” The journey toward an AI-Native future at Sysco Labs has officially begun, and the results promise to redefine the very boundaries of Quality Engineering.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fby96cutjt42v1vd2vp1y.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fby96cutjt42v1vd2vp1y.webp" alt="Group photo" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tharushika Rathnayake, Sachini Karunarathne, Nuwanga Arambawela, Tharindu Dias, Dinithra Thantilage, Pasan Somarathna, Iresha Wijesinghe, Thanya Withanachchi, Shanaz Nabeel, Chiran Govinnage, Akila Rangalla, Mohamed Yaseen, Sujith Thushera, Kasun De Silva, Ruzaik Refai, Dineth Mendis, Volta Jebaprashanth.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>syscolabs</category>
      <category>qualityengineering</category>
      <category>ai</category>
      <category>automation</category>
    </item>
    <item>
      <title>Raw XPath is Dead: How XPathy's Fluent API Solves Your Flaky Test Problem</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Fri, 17 Oct 2025 18:53:19 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/raw-xpath-is-dead-how-xpathys-fluent-api-solves-your-flaky-test-problem-4h7g</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/raw-xpath-is-dead-how-xpathys-fluent-api-solves-your-flaky-test-problem-4h7g</guid>
      <description>&lt;p&gt;One of the most frequent causes of flaky tests is when a front-end change inadvertently alters the casing or spacing of an HTML attribute or text content. A CSS class might change from &lt;code&gt;active&lt;/code&gt; to &lt;code&gt;Active&lt;/code&gt;, or an error message might gain an extra space: &lt;code&gt;" Error Message "&lt;/code&gt; vs. &lt;code&gt;"Error Message"&lt;/code&gt;. These minor variations are enough to break an exact-match XPath, leading to frustrating test failures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XPathy's Value Transformation feature&lt;/strong&gt; provides a fluent, built-in mechanism to address these issues. By applying transformations like case-folding and space-normalization &lt;em&gt;before&lt;/em&gt; the locator comparison, XPathy ensures your locators match the &lt;strong&gt;intended value&lt;/strong&gt;, regardless of the developer's formatting choices.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92pf48fq58htyy2biwg4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92pf48fq58htyy2biwg4.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Achieving Case-Insensitive Matching
&lt;/h2&gt;

&lt;p&gt;Raw XPath requires the complex use of the &lt;code&gt;translate()&lt;/code&gt; function to achieve case-insensitivity. You must provide the entire alphabet twice: once for upper-case characters to replace, and once for the lower-case equivalents to replace them with.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Raw, Error-Prone XPath&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;//div[contains(translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'menu-item')]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;XPathy simplifies this into a single, declarative method call using the &lt;code&gt;Case&lt;/code&gt; enum.&lt;/p&gt;

&lt;h3&gt;
  
  
  The &lt;code&gt;.withCase(IGNORED)&lt;/code&gt; Method
&lt;/h3&gt;

&lt;p&gt;The most common transformation is to ignore case for stable attributes like &lt;code&gt;id&lt;/code&gt; or &lt;code&gt;class&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Case&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IGNORED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active-tab"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='active-tab']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This locator will successfully match elements with &lt;code&gt;class="active-tab"&lt;/code&gt;, &lt;code&gt;class="Active-Tab"&lt;/code&gt;, or &lt;code&gt;class="ACTIVE-TAB"&lt;/code&gt;. This small addition instantly makes the locator bulletproof against casing inconsistencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Force Casing (&lt;code&gt;UPPER&lt;/code&gt; and &lt;code&gt;LOWER&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;You can also force the element's value to a specific case before comparison, which is helpful for standardizing text content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UPPER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"USERNAME"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//label[translate(text(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')='USERNAME']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This ensures the locator matches any variation of "Username," "username," or "USERNAME."&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Whitespace and Symbol Cleanup
&lt;/h2&gt;

&lt;p&gt;Inconsistent spacing in text nodes is a rampant source of locator flakiness. Similarly, needing to compare numeric values (like prices) often requires removing currency symbols.&lt;/p&gt;

&lt;h3&gt;
  
  
  Normalize Space with &lt;code&gt;.withNormalizeSpace()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;The XPath &lt;code&gt;normalize-space()&lt;/code&gt; function strips leading and trailing whitespace and replaces sequences of internal whitespace with a single space. XPathy exposes this critical utility directly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withNormalizeSpace&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid password"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[normalize-space(text())='Invalid password']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This locator will match text nodes that contain &lt;code&gt;"Invalid password"&lt;/code&gt;, &lt;code&gt;" Invalid password "&lt;/code&gt;, or even &lt;code&gt;"Invalid   password"&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Filtering Characters for Consistency
&lt;/h3&gt;

&lt;p&gt;When dealing with dynamic content like prices or order IDs, you often only care about the alphanumeric parts. XPathy allows you to explicitly &lt;strong&gt;keep&lt;/strong&gt; or &lt;strong&gt;remove&lt;/strong&gt; sets of characters.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;XPathy Code&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Numeric Value Check&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;span.byText().withRemoveOnly(SPECIAL_CHARACTERS).contains("1999")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes &lt;code&gt;$&lt;/code&gt; or &lt;code&gt;,&lt;/code&gt; to stabilize price comparison.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ID Clean-up&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;td.byText().withKeepOnly(ENGLISH_ALPHABETS, NUMBERS).equals("ORD123")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes hyphens or spaces from Order IDs like &lt;code&gt;ORD-123&lt;/code&gt; before checking.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These transformations allow you to focus on the &lt;strong&gt;meaningful data&lt;/strong&gt; rather than the superficial presentation or formatting characters, making the locator invariant to regional currency or spacing differences.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Combining Transformations for Ultimate Resilience
&lt;/h2&gt;

&lt;p&gt;The true power of XPathy lies in chaining these operations to clean up a value thoroughly before comparison.&lt;/p&gt;

&lt;p&gt;Imagine a product title that might have inconsistent spacing, numbers (which should be ignored), and mixed casing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withNormalizeSpace&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withRemoveOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NUMBERS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IGNORED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"premium feature"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[contains(translate(translate(normalize-space(text()), '0123456789', ''), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'premium feature')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By stacking &lt;code&gt;.withNormalizeSpace()&lt;/code&gt;, &lt;code&gt;.withRemoveOnly()&lt;/code&gt;, and &lt;code&gt;.withCase()&lt;/code&gt;, the XPath is comprehensively cleaned up. The final result is a locator that will reliably find the element, regardless of how many formatting inconsistencies are introduced by the UI layer.&lt;/p&gt;

&lt;p&gt;These fluent transformations are the key to building locators that are truly &lt;strong&gt;bulletproof&lt;/strong&gt; against the casing, spacing, and formatting issues that plague Selenium test stability.&lt;/p&gt;

&lt;h2&gt;
  
  
  XPathy provides a lot more: Read the Full Documentation:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753"&gt;https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Repository:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Volta-Jebaprashanth/xpathy" rel="noopener noreferrer"&gt;https://github.com/Volta-Jebaprashanth/xpathy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy Testing! 🚀&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Stop Writing Flaky Selenium Tests: meet XPathy - The Java Library That Fixes XPath Complexity</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Fri, 17 Oct 2025 18:45:45 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/stop-writing-flaky-selenium-tests-meet-xpathy-the-java-library-that-fixes-xpath-complexity-3b81</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/stop-writing-flaky-selenium-tests-meet-xpathy-the-java-library-that-fixes-xpath-complexity-3b81</guid>
      <description>&lt;p&gt;Flaky Selenium tests are the bane of every automation team. They waste time, erode trust in the test suite, and slow down deployment cycles. While numerous factors contribute to flakiness—timing issues, synchronization, and race conditions—a major, often overlooked source is the &lt;strong&gt;brittleness of hand-written XPath locators&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;When an XPath locator is a fragile string full of hardcoded values, quotes, and complex functions, it breaks every time a front-end developer makes a minor change: a class name capitalization, an extra space, or a slightly different attribute value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XPathy&lt;/strong&gt; is the Java library built to eliminate this specific complexity and brittleness. By replacing error-prone string concatenation with a &lt;strong&gt;fluent, object-oriented API&lt;/strong&gt;, XPathy lets you define resilient locators that are immediately more stable, reducing the most common causes of XPath-induced flakiness.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8o54kpeorbj5tuta771.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft8o54kpeorbj5tuta771.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Flakiness Pain Point: Brittleness in Raw Locators
&lt;/h2&gt;

&lt;p&gt;A locator is flaky if it works one moment and fails the next without a logical cause. This often stems from developers failing to account for three common, real-world variations:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flakiness Cause&lt;/th&gt;
&lt;th&gt;XPathy Solution&lt;/th&gt;
&lt;th&gt;Example of Brittleness&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Case Variation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Transformations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Class changes from &lt;code&gt;active&lt;/code&gt; to &lt;code&gt;Active&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Whitespace/Symbols&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Transformations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Text changes from &lt;code&gt;"Total $100"&lt;/code&gt; to &lt;code&gt;"Total €100.00"&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Structural Changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Logical Grouping  &amp;amp; Having&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A container element shifts position or gets a new, temporary class.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;XPathy tackles these issues by embedding &lt;strong&gt;robustness&lt;/strong&gt; and &lt;strong&gt;explicit structural validation&lt;/strong&gt; directly into the locator's construction process.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Eliminate Flakiness with Resilient Transformations
&lt;/h2&gt;

&lt;p&gt;The most immediate fix for flakiness is ensuring your locator is &lt;strong&gt;immune to minor text and attribute variations&lt;/strong&gt;. XPathy’s value transformation methods (&lt;code&gt;.with...()&lt;/code&gt;) handle this automatically, translating into complex-but-correct XPath functions (like &lt;code&gt;translate()&lt;/code&gt; and &lt;code&gt;normalize-space()&lt;/code&gt;) under the hood.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Case-Insensitivity (Flakiness Fixed)
&lt;/h3&gt;

&lt;p&gt;Instead of relying on a string matching exact capitalization, force the comparison to ignore case.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flaky Manual XPath&lt;/th&gt;
&lt;th&gt;Resilient XPathy Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;//div[@class='ActiveButton']&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;div.byAttribute(class_).withCase(IGNORED).equals("activebutton")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;em&gt;Fix:&lt;/em&gt; Matches &lt;code&gt;ActiveButton&lt;/code&gt;, &lt;code&gt;activebutton&lt;/code&gt;, or &lt;code&gt;ACTIVEBUTTON&lt;/code&gt; consistently.&lt;/td&gt;
&lt;td&gt;output : &lt;code&gt;"//div[translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='activebutton']"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  B. White Space and Symbol Neutralization
&lt;/h3&gt;

&lt;p&gt;Prices and error messages frequently contain inconsistent spacing or varying currency symbols, leading to failures.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flaky Manual XPath&lt;/th&gt;
&lt;th&gt;Resilient XPathy Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;//span[text()='$1,999']&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;span.byText().withRemoveOnly(SPECIAL_CHARACTERS).equals("1999")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;em&gt;Fix:&lt;/em&gt; Removes all symbols and allows the locator to match &lt;code&gt;$1,999&lt;/code&gt;, &lt;code&gt;€1.999&lt;/code&gt;, or &lt;code&gt;1999&lt;/code&gt; based on the stable numeric value.&lt;/td&gt;
&lt;td&gt;output : &lt;code&gt;"//span[translate(text(), concat('!@#$%^&amp;amp;*()_+-=[]{};:,./&amp;lt;&amp;gt;?~\ , '"',"'"), '') = '1999']"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  3. Reduce False Positives with Structural Validation
&lt;/h2&gt;

&lt;p&gt;Flakiness also occurs when a locator accidentally targets the &lt;em&gt;wrong&lt;/em&gt; element because its properties (like a class or ID) are reused elsewhere. XPathy’s &lt;strong&gt;Having Operations (Section H)&lt;/strong&gt; allow you to ensure the target element exists only within a validated structure.&lt;/p&gt;

&lt;p&gt;You don't just find a button; you find a button &lt;strong&gt;that is guaranteed to be inside the active form&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Validating a Target's Context
&lt;/h3&gt;

&lt;p&gt;We want to click the "Submit" button, but only if it belongs to a form that is currently visible (e.g., &lt;code&gt;section#active-form&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;ancestor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active-form"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Generated XPath: "//button[text() = 'Submit' and ( ancestor::section[@id='active-form'] )]"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Impact on Flakiness:&lt;/strong&gt; If a hidden or stale "Submit" button exists outside &lt;code&gt;#active-form&lt;/code&gt;, this test will &lt;strong&gt;not&lt;/strong&gt; find it. The locator explicitly restricts the search space, drastically reducing false positives and test flakiness caused by interacting with the wrong element.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  4. Maintenance and Clarity: The Ultimate Flakiness Prevention
&lt;/h2&gt;

&lt;p&gt;A major reason XPath locators become brittle is that they are so difficult to read and maintain. When a developer can't quickly understand a complex string, they usually won't fix it properly—they'll just write a new, equally brittle one.&lt;/p&gt;

&lt;p&gt;XPathy's fluent API makes locators self-documenting, ensuring that when the UI changes, the corresponding locator fix is obvious:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Broken Code Example&lt;/th&gt;
&lt;th&gt;XPathy Fix Example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Old:&lt;/strong&gt; &lt;code&gt;//a[@href="/profile" and @class="active"]&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Change to:&lt;/strong&gt; &lt;code&gt;a.byAttribute(href).contains("profile").and().byAttribute(class_).contains("selected")&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Benefit:&lt;/em&gt; You change &lt;code&gt;.equals("active")&lt;/code&gt; to &lt;code&gt;.contains("selected")&lt;/code&gt; directly, preserving the logical structure and preventing accidental syntax errors common in raw string edits. |&lt;/p&gt;

&lt;p&gt;By promoting clarity, XPathy enforces good practices, leading to a test suite where locators are consistently well-written, structurally sound, and inherently less prone to failure. If you want to stop writing flaky tests, the first step is to upgrade your locator language.&lt;/p&gt;

&lt;h2&gt;
  
  
  XPathy provides a lot more: Read the Full Documentation:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753"&gt;https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Repository:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Volta-Jebaprashanth/xpathy" rel="noopener noreferrer"&gt;https://github.com/Volta-Jebaprashanth/xpathy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy Testing! 🚀&lt;/p&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>java</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Meet XPathy: The Fluent Java API That Makes Raw XPath Obsolete</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Fri, 17 Oct 2025 18:31:33 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/meet-xpathy-the-fluent-java-api-that-makes-raw-xpath-obsolete-1bnm</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/meet-xpathy-the-fluent-java-api-that-makes-raw-xpath-obsolete-1bnm</guid>
      <description>&lt;p&gt;For years, UI automation engineers have struggled with the same nemesis: &lt;strong&gt;raw XPath strings&lt;/strong&gt;. They're brittle, hard to read, and prone to silent errors caused by a misplaced bracket, quote, or function. Balancing string concatenation, single quotes, and double quotes to build complex locators is a maintenance headache that slows down test development and makes debugging a nightmare.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XPathy&lt;/strong&gt; is a lightweight Java library built to solve this problem. It replaces manual string manipulation with a fluent, object-oriented API, allowing developers to build sophisticated Selenium locators using clear, chainable methods. XPathy makes raw XPath obsolete by turning the process of locator creation into a &lt;strong&gt;declarative, business-readable task&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why XPathy? The Core Problem Solved
&lt;/h3&gt;

&lt;p&gt;The goal of a locator is to express a test's intent. Instead of writing the syntax for an element with an ID that starts with &lt;code&gt;menu-&lt;/code&gt;, XPathy lets you express the intent directly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional, Brittle XPath&lt;/th&gt;
&lt;th&gt;XPathy Fluent API (Declarative Intent)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;//div[starts-with(@data-testid, 'menu-') and contains(text(), 'Item')]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;div.byAttribute(data_testid).startsWith("menu-").and().byText().contains("Item")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The result is a locator that is dramatically more &lt;strong&gt;readable&lt;/strong&gt;, &lt;strong&gt;maintainable&lt;/strong&gt;, and &lt;strong&gt;scalable&lt;/strong&gt; across different environments. The final XPathy object seamlessly converts to a Selenium &lt;code&gt;By&lt;/code&gt; object or a standard XPath string via &lt;code&gt;.getLocator()&lt;/code&gt; or &lt;code&gt;.toString()&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  The XPathy Architecture: Context and Flow
&lt;/h3&gt;

&lt;p&gt;XPathy operates on a clear, layered architecture that guarantees valid XPath generation at every step. This &lt;em&gt;context-building flow&lt;/em&gt; is what enables the fluent API.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Starting Point:&lt;/strong&gt; Begin with a generic attribute (id, class_, data_testid) or a specific HTML tag (div, button, span).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context Selection:&lt;/strong&gt; Use a &lt;code&gt;by...()&lt;/code&gt; method to define what you are filtering on:
&lt;code&gt;.byAttribute(id).byText().byNumber().byStyle(backgroundColor)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Condition Finalization:&lt;/strong&gt; Apply the final predicate to generate the XPath condition:
&lt;code&gt;.equals("value")&lt;/code&gt; &lt;code&gt;.contains("text")&lt;/code&gt; &lt;code&gt;.greaterThan(50)&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  1. Basic Operations
&lt;/h3&gt;

&lt;p&gt;All standard XPath operations are immediately accessible:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;XPathy Code&lt;/th&gt;
&lt;th&gt;Generated XPath&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id.contains("login")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;//*[contains(@id, 'login')]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target any tag with an ID containing "login".&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;h2.byText().startsWith("Chapter")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;//h2[starts-with(text(), 'Chapter')]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target an &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; tag whose text begins with "Chapter".&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;value.lessThan(50)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;//*[@value &amp;lt; 50]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Target any tag where the numeric attribute value is less than 50.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  2. Robustness Through Value Transformations
&lt;/h3&gt;

&lt;p&gt;One of XPathy's most powerful features is its ability to apply &lt;strong&gt;transformations&lt;/strong&gt; (like case-insensitivity or whitespace cleanup) before comparison. This creates locators resilient to UI variations.&lt;/p&gt;

&lt;p&gt;You use the &lt;code&gt;.with...()&lt;/code&gt; methods to apply transformations to the current context:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;XPathy Code&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Ignore Case&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;div.byAttribute(class_).withCase(IGNORED).equals("active")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Matches &lt;code&gt;class="active"&lt;/code&gt;, &lt;code&gt;class="Active"&lt;/code&gt;, or &lt;code&gt;class="ACTIVE"&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Normalize Space&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;p.byText().withNormalizeSpace().equals("Error message")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Ignores leading/trailing/extra spaces in the text node.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Remove Symbols&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;span.byText().withRemoveOnly(SPECIAL_CHARACTERS).contains("1999")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Removes &lt;code&gt;$&lt;/code&gt;, &lt;code&gt;€&lt;/code&gt;, &lt;code&gt;,&lt;/code&gt;, or &lt;code&gt;.&lt;/code&gt; from price text before comparison.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These transformations—complex in raw XPath—become simple, chainable methods in XPathy.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Logical Composition and Grouping
&lt;/h3&gt;

&lt;p&gt;XPathy eliminates the risk of incorrect operator precedence by managing parentheses automatically when grouping conditions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;XPathy Example&lt;/th&gt;
&lt;th&gt;Generated XPath&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Simple AND&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;div.byAttribute(id).equals("form").and().byText().contains("Login")&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;//div[@id='form' and contains(text(), 'Login')]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Union (OR Group)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;button.byAttribute(id).union(Or.equals("btn1"), Or.equals("btn2"))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;//button[@id='btn1' or @id='btn2']&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nested Logic&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;div.byCondition(and(text()..., or(attr()...)))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generates fully parenthesized complex logic.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This fluent logical API enables modeling of complex business rules (e.g., &lt;em&gt;Must be a featured product OR a high-rated product, BUT NOT expired&lt;/em&gt;) in clear Java code.&lt;/p&gt;




&lt;h3&gt;
  
  
  4. Advanced Relationship Testing with Having
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Having Operation&lt;/strong&gt; allows you to filter a target element based on related elements (child, ancestor, or sibling) without leaving the target context.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;XPathy Code&lt;/th&gt;
&lt;th&gt;Generated XPath&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Check Descendant&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;div.byAttribute(class_).equals("card").and().byHaving().descendant(span.byText().contains("In Stock"))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;//div[@class='card' and (.//span[contains(text(), 'In Stock')])]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Check Preceding Sibling&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;input.byAttribute(name).equals("user").and().byHaving().precedingSibling(label.byText().equals("Username"))&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;//input[@name='user' and (preceding-sibling::label[text()='Username'])]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This allows you to say: &lt;em&gt;"Find the DIV only if it has a descendant SPAN that says 'In Stock'."&lt;/em&gt; It's essential for locating containers based on dynamic content.&lt;/p&gt;




&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;XPathy is more than just a convenience wrapper; it's a &lt;strong&gt;paradigm shift&lt;/strong&gt; in how web element locators are created. By moving from fragile string-based syntax to a &lt;strong&gt;resilient, object-oriented, fluent API&lt;/strong&gt;, XPathy ensures locators are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Clear:&lt;/strong&gt; Intent is expressed explicitly.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Robust:&lt;/strong&gt; Built-in transformations neutralize common UI quirks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainable:&lt;/strong&gt; Code is self-documenting and easy to update.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your team struggles with flaky tests, complex XPath, and slow debugging cycles, &lt;strong&gt;XPathy offers a definitive Java-native solution&lt;/strong&gt; that makes raw XPath truly obsolete.&lt;/p&gt;

&lt;h2&gt;
  
  
  XPathy provides a lot more: Read the Full Documentation:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753"&gt;https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Repository:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Volta-Jebaprashanth/xpathy" rel="noopener noreferrer"&gt;https://github.com/Volta-Jebaprashanth/xpathy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy Testing! 🚀&lt;/p&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>java</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Beyond Parent/Child: Navigating the DOM Tree Fluently with XPathy</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Fri, 17 Oct 2025 18:14:11 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/beyond-parentchild-navigating-the-dom-tree-fluently-with-xpathy-5f89</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/beyond-parentchild-navigating-the-dom-tree-fluently-with-xpathy-5f89</guid>
      <description>&lt;p&gt;The challenge in robust UI automation often lies in locating an element based not just on its own attributes, but on the characteristics of its related nodes — siblings, ancestors, or descendants. Traditional XPath forces context switching (&lt;code&gt;/../following-sibling::*&lt;/code&gt;), leading to brittle, unreadable locators.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;XPathy&lt;/strong&gt; addresses this by separating &lt;strong&gt;DOM Traversal&lt;/strong&gt; from &lt;strong&gt;Relationship Predication&lt;/strong&gt;. This article explores how XPathy combines explicit DOM Navigation methods (&lt;code&gt;$up()&lt;/code&gt;, &lt;code&gt;$descendant()&lt;/code&gt;, etc.) with the powerful, context-preserving &lt;strong&gt;Having Operations (&lt;code&gt;byHaving()&lt;/code&gt;)&lt;/strong&gt; to create readable, maintainable, and precisely targeted locators.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Foundation: Explicit DOM Traversal
&lt;/h2&gt;

&lt;p&gt;XPathy simplifies the core XPath axes into intuitive, chained methods prefixed with &lt;code&gt;$&lt;/code&gt; for visual clarity. These methods are designed to change the context of the expression, moving from one node to another.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;XPathy Method&lt;/th&gt;
&lt;th&gt;XPath Axis/Shorthand&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;$parent()&lt;/code&gt; / &lt;code&gt;$up(n)&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;parent::* / ..&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Moves up one or more levels.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$ancestor(tag)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;ancestor::tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Matches any element above the current node.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$child(tag)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;child::tag / ./tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Matches immediate children.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$descendant(tag)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;descendant::tag / //tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Matches elements anywhere below the current node.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$followingSibling(tag)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;following-sibling::tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Matches elements after the current node at the same level.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$precedingSibling(tag)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;preceding-sibling::tag&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Matches elements before the current node at the same level.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Example: Chaining Traversal&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$parent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$followingSibling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$descendant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                  &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Discount"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="s"&gt;"//div[@id='main']/../following-sibling::div/descendant::span[contains(text(), 'Discount')]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Purpose:&lt;/strong&gt; Start at &lt;code&gt;div#main&lt;/code&gt;, go up one parent, then across to the next sibling &lt;code&gt;div&lt;/code&gt;, and finally find any &lt;code&gt;span&lt;/code&gt; below it.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Advanced Relationship Predication: Having Operations
&lt;/h2&gt;

&lt;p&gt;While traversal changes context, &lt;strong&gt;Having Operations&lt;/strong&gt; use traversal axes to check an attribute, text, or characteristic of a related node — &lt;strong&gt;without changing the locator's final target&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;byHaving()&lt;/code&gt; method acts as a &lt;strong&gt;predicate (a filter &lt;code&gt;[ ... ]&lt;/code&gt;)&lt;/strong&gt; that encapsulates a relationship test. This is essential when verifying a structural or data-driven relationship before acting on the target element.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Checking for a Descendant's Existence (Deep Check)
&lt;/h3&gt;

&lt;p&gt;A common case is locating a container only if it holds a specific button or status message deep within.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Find &lt;code&gt;div.card&lt;/code&gt; that contains a descendant button with text &lt;em&gt;"Add to Cart"&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
   &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;descendant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Add to Cart"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resulting XPath:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="s"&gt;"//div[@class='card' and ( .//button[text() = 'Add to Cart'] )]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; The final locator targets the outer &lt;code&gt;div&lt;/code&gt;, ensuring WebDriver interacts with the correct container.&lt;/p&gt;




&lt;h3&gt;
  
  
  B. Checking an Ancestor's Attribute (Context Check)
&lt;/h3&gt;

&lt;p&gt;Ensure that a target element is rendered within the correct structural context — a critical safeguard against false positives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Find a &lt;code&gt;span.price&lt;/code&gt; only if its ancestor &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; has &lt;code&gt;id="featured-product"&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;ancestor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featured-product"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resulting XPath:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="s"&gt;"//span[@class='price' and ( ancestor::section[@id='featured-product'] )]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; If another &lt;code&gt;span.price&lt;/code&gt; exists outside the featured section, it’s ignored, ensuring accuracy.&lt;/p&gt;




&lt;h3&gt;
  
  
  C. Checking a Sibling's Content (Relational Data Check)
&lt;/h3&gt;

&lt;p&gt;Use this when elements depend on their immediate neighbors (e.g., a label followed by an input field).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario:&lt;/strong&gt; Find an input field preceded by a label with text &lt;em&gt;"Email"&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;precedingSibling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resulting XPath:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="s"&gt;"//input[@type='text' and ( preceding-sibling::label[text() = 'Email'] )]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefit:&lt;/strong&gt; Precisely target the input using the label text — a stable reference for UI automation.&lt;/p&gt;




&lt;h3&gt;
  
  
  3. The Power of Composition: Traversal and Having Combined
&lt;/h3&gt;

&lt;p&gt;XPathy allows &lt;strong&gt;Traversal&lt;/strong&gt; and &lt;strong&gt;Having&lt;/strong&gt; to be mixed with logical operators and transformations, unlocking advanced, human-readable locators.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Case:&lt;/strong&gt; Find a &lt;code&gt;div.form-container&lt;/code&gt; that contains an enabled &lt;em&gt;Login&lt;/em&gt; button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"form-container"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;descendant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Login"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                              &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;not&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"disabled"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resulting XPath:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="s"&gt;"//div[@class='form-container' and ( .//button[contains(text(), 'Login') and not(contains(@class, 'disabled'))] )]"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt; The locator targets the main form container while verifying that it contains a &lt;em&gt;Login&lt;/em&gt; button that is &lt;strong&gt;not disabled&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;By separating &lt;strong&gt;context traversal&lt;/strong&gt; from &lt;strong&gt;relationship validation&lt;/strong&gt;, XPathy empowers testers to write &lt;strong&gt;structural assertions directly into locators&lt;/strong&gt; — reducing fragility and increasing readability.&lt;/p&gt;

&lt;p&gt;This approach turns XPath from a brittle string expression into a &lt;strong&gt;fluent, logical representation of the DOM’s relationships&lt;/strong&gt;, making UI automation both expressive and maintainable.&lt;/p&gt;

&lt;h2&gt;
  
  
  XPathy provides a lot more: Read the Full Documentation:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753"&gt;https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Repository:
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Volta-Jebaprashanth/xpathy" rel="noopener noreferrer"&gt;https://github.com/Volta-Jebaprashanth/xpathy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Happy Testing! 🚀&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>xpath</category>
      <category>java</category>
      <category>automation</category>
    </item>
    <item>
      <title>How We Eliminated 90% of Our Locator Failures Using Value Transformations with XPathy</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Thu, 16 Oct 2025 18:52:31 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/how-we-eliminated-90-of-our-locator-failures-using-value-transformations-with-xpathy-3kea</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/how-we-eliminated-90-of-our-locator-failures-using-value-transformations-with-xpathy-3kea</guid>
      <description>&lt;p&gt;The promise of automated testing is &lt;strong&gt;speed&lt;/strong&gt; and &lt;strong&gt;reliability&lt;/strong&gt;. The reality, however, often involves staring at a CI pipeline failure caused by a tiny, heartbreaking UI change — a change that turns &lt;code&gt;"Submit Button"&lt;/code&gt; into &lt;code&gt;"submit button"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This minor inconsistency is the source of endless locator flakiness. Our team realized that the problem wasn’t the XPath syntax itself — it was the fragile, exact-match relationship between our code and the messy, dynamic reality of the browser’s DOM.&lt;/p&gt;

&lt;p&gt;By implementing &lt;strong&gt;Value Transformations&lt;/strong&gt; using the Java library &lt;strong&gt;XPathy&lt;/strong&gt;, we stopped fighting minor UI changes and started building resilient locators. The result? A measured &lt;strong&gt;90% reduction in locator-related pipeline failures&lt;/strong&gt; over three months.&lt;/p&gt;

&lt;p&gt;Here’s how we achieved that transformation.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. The Real Enemy: Dynamic Noise
&lt;/h2&gt;

&lt;p&gt;Most locator failures aren't caused by a complete restructuring of the HTML; they’re caused by “noise”:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Case Inconsistency:&lt;/strong&gt; &lt;code&gt;New York&lt;/code&gt; vs &lt;code&gt;NEW YORK&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unwanted Whitespace:&lt;/strong&gt; Extra spaces, tabs, or newlines in text content — e.g., &lt;code&gt;"   Log In   "&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Formatting Characters:&lt;/strong&gt; Currency symbols, commas, or parentheses in numeric values — e.g., &lt;code&gt;($1,234.56)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Traditional XPath solutions require manually embedding complex functions like &lt;code&gt;translate()&lt;/code&gt; or &lt;code&gt;normalize-space()&lt;/code&gt;, making locators unreadable and still prone to string errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. The Transformation Toolkit
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;XPathy&lt;/strong&gt; replaces manual function transcription with declarative, fluent methods. These automatically wrap your target value in robust XPath code, normalizing the element’s content before comparison.&lt;/p&gt;

&lt;h3&gt;
  
  
  A. Case-Agnostic Comparison
&lt;/h3&gt;

&lt;p&gt;We stopped worrying about whether a button’s text was capitalized. This immediately fixed all failures related to localization and internal styling changes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Traditional, Brittle XPath&lt;/th&gt;
&lt;th&gt;XPathy, Resilient Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;//button[text()='Login']&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;button.byText().withCase(IGNORED).equals("login")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Resulting Robust XPath:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'ABCDEFGHIJKLMNOPQRSTUVWXYZ'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'abcdefghijklmnopqrstuvwxyz'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  B. Whitespace Normalization
&lt;/h3&gt;

&lt;p&gt;Whitespace is the silent killer. Dynamic content often carries unintended leading or trailing spaces. The &lt;code&gt;.withNormalizeSpace()&lt;/code&gt; transformation ensures only meaningful characters are compared.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Stops a test from failing if the text is "  Order Confirmed  "&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;withNormalizeSpace&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order Confirmed"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Resulting Robust XPath:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="ow"&gt;div&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;normalize-space&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'Order Confirmed'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  C. Filtering Out the Noise
&lt;/h3&gt;

&lt;p&gt;When validating numeric values (prices, scores, counts), you often need to ignore symbols and formatting. XPathy lets you &lt;strong&gt;remove characters&lt;/strong&gt; before comparison.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Check a price div that might contain "$1,999.00"&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"total"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;withRemoveOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;CURRENCY_SYMBOLS&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;COMMAS&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1999.00"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// The result removes '$' and ',' before comparing against the expected value.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. The Tangible Business Result
&lt;/h2&gt;

&lt;p&gt;By adopting &lt;strong&gt;Value Transformations&lt;/strong&gt;, we shifted our focus from low-value maintenance to high-value feature testing.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;✅ 90% Reduction in Locator Flakiness:&lt;/strong&gt; Tests became stable across environments (staging vs. production) because minor formatting differences no longer broke them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Faster Authoring:&lt;/strong&gt; Engineers stopped researching complex XPath functions and simply declared intent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🧩 Clearer Code:&lt;/strong&gt; Fluent syntax like &lt;code&gt;.withCase(IGNORED)&lt;/code&gt; instantly communicates intent, simplifying code reviews and onboarding.&lt;/li&gt;
&lt;/ul&gt;




&lt;h1&gt;
  
  
  XPathy provides a lot more: Read the Full Documentation:
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753"&gt;https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The Repository:
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/Volta-Jebaprashanth/xpathy" rel="noopener noreferrer"&gt;https://github.com/Volta-Jebaprashanth/xpathy&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;We discovered that resilience isn’t about avoiding change — it’s about &lt;strong&gt;embracing it&lt;/strong&gt; by making locators immune to tiny, inevitable inconsistencies of web development.&lt;/p&gt;

&lt;p&gt;If you’re tired of watching your CI pipeline go red because of a stray space or capitalization difference, it’s time to equip your team with &lt;strong&gt;XPathy’s Value Transformations&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Reliability is within reach.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>tooling</category>
      <category>java</category>
    </item>
    <item>
      <title>The Automated Tester's Secret Weapon: Build Robust XPaths in Seconds, Not Minutes (With XPathy)</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Thu, 16 Oct 2025 18:41:37 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/the-automated-testers-secret-weapon-build-robust-xpaths-in-seconds-not-minutes-with-xpathy-984</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/the-automated-testers-secret-weapon-build-robust-xpaths-in-seconds-not-minutes-with-xpathy-984</guid>
      <description>&lt;p&gt;In the world of automated testing, time is currency. Every minute spent debugging a flaky locator is a minute lost on feature development. Traditional XPath, while powerful, is a massive time sink—it forces testers into tedious, manual labor: counting parentheses, transcribing functions, and meticulously checking for case sensitivity.&lt;/p&gt;

&lt;p&gt;What if you could cut the locator creation and maintenance time by &lt;strong&gt;80%&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Introducing &lt;strong&gt;XPathy&lt;/strong&gt;, the fluent Java API that transforms XPath from a brittle, time-consuming string exercise into a rapid, declarative coding task. This library is the automated tester's secret weapon for maximizing speed and reliability.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⏱️ Killer Feature 1: The Speed of Fluency
&lt;/h2&gt;

&lt;p&gt;Imagine needing to locate an element that meets three criteria: it’s a specific product card, it's not disabled, and its price is exactly &lt;code&gt;$49.99&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Time-Consuming Approach (Raw XPath)
&lt;/h3&gt;

&lt;p&gt;Writing this manually requires chaining and operators, potentially using &lt;code&gt;translate()&lt;/code&gt; for case insensitivity, and definitely &lt;code&gt;normalize-space()&lt;/code&gt; if you expect dynamic text.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="ow"&gt;div&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;@data-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'product'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;@class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'disabled'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;normalize-space&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'49.99'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Time taken:&lt;/strong&gt; ~2 minutes (if you're careful).&lt;/p&gt;

&lt;h3&gt;
  
  
  The Efficient Approach (XPathy)
&lt;/h3&gt;

&lt;p&gt;XPathy allows you to declare your intent instantly. The compiler handles the syntax, the quotes, and the structure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"data-type"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"product"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;not&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"disabled"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;withNormalizeSpace&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"49.99"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Time taken:&lt;/strong&gt; ~30 seconds. You code, XPathy compiles.&lt;/p&gt;

&lt;p&gt;The result? Rapid authoring that doesn't sacrifice precision or structure.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛡️ Killer Feature 2: Reliability by Transformation
&lt;/h2&gt;

&lt;p&gt;Flakiness is the ultimate killer of efficiency. A locator that breaks because a value changed from &lt;code&gt;Login&lt;/code&gt; to &lt;code&gt;login&lt;/code&gt; or because a developer added a space is a waste of time.&lt;/p&gt;

&lt;p&gt;XPathy's &lt;strong&gt;Transformations&lt;/strong&gt; solve flakiness at the source, adding bulletproof resilience without any manual XPath syntax.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;The Flaky Reality&lt;/th&gt;
&lt;th&gt;The XPathy Resilience&lt;/th&gt;
&lt;th&gt;Efficiency Gain&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Element text has extra spaces (&lt;code&gt;" Submit "&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.withNormalizeSpace()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No time spent debugging or writing &lt;code&gt;normalize-space()&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Attribute value changes case (&lt;code&gt;"Enabled"&lt;/code&gt; vs &lt;code&gt;"enabled"&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.withCase(IGNORED)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;No time spent writing long &lt;code&gt;translate()&lt;/code&gt; functions.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price text includes currency symbols (&lt;code&gt;$1,000.00&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;.withRemoveOnly(SPECIAL_CHARACTERS)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Instant filtering of noise characters for clean numeric checks.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This isn't just about saving seconds on locator creation; it’s about saving hours on &lt;strong&gt;post-release maintenance&lt;/strong&gt; when the UI changes slightly.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Killer Feature 3: Precision with Having and Logic
&lt;/h2&gt;

&lt;p&gt;Complex UIs require complex logic. You need to verify not just a single element, but its relationship to others—like finding a table row that contains a cell with a specific status.&lt;/p&gt;

&lt;p&gt;In raw XPath, this means switching contexts or using cumbersome relative paths. In XPathy, the &lt;strong&gt;Having Operation&lt;/strong&gt; makes it declarative.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Find a shopping cart item (div) ONLY if it has a span child that says "Shipped".&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cart-item"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                         &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shipped"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result: //div[@class='cart-item' and ( ./span[text()='Shipped'] )]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This fluent chaining allows testers to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Model Business Logic:&lt;/strong&gt; Find X if Y exists nearby.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avoid Fragile Paths:&lt;/strong&gt; Focus on the condition, not the exact path depth.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Define Complex Groups:&lt;/strong&gt; Use &lt;code&gt;union()&lt;/code&gt; and &lt;code&gt;intersect()&lt;/code&gt; to define multiple valid conditions in one go, dramatically reducing the code needed for cross-browser or environmental differences.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🚀 Conclusion: Stop Debugging Strings, Start Automating
&lt;/h2&gt;

&lt;p&gt;The "secret weapon" is simple: use well-designed code to replace manual, error-prone tasks.&lt;/p&gt;

&lt;p&gt;XPathy's fluent syntax, intelligent transformations, and powerful logical operators ensure that you are writing robust, readable, and reliable locators in &lt;strong&gt;seconds, not minutes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By focusing on &lt;em&gt;what you need to find&lt;/em&gt; instead of &lt;em&gt;how to write the XPath function&lt;/em&gt;, you regain valuable time for developing tests, not maintaining locators.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build smarter, faster, and stronger with XPathy — your automation ally.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>productivity</category>
      <category>testing</category>
      <category>tooling</category>
      <category>java</category>
    </item>
    <item>
      <title>From String Soup to Fluent Code: Reinventing XPath in Java with XPathy 🚀</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Thu, 16 Oct 2025 18:38:14 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/from-string-soup-to-fluent-code-reinventing-xpath-in-java-with-xpathy-5eng</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/from-string-soup-to-fluent-code-reinventing-xpath-in-java-with-xpathy-5eng</guid>
      <description>&lt;p&gt;If you’ve spent any time writing UI automation with Selenium and Java, you know the frustration: XPath is brittle, hard to read, and a nightmare to maintain. You spend precious minutes balancing quotes, checking for typos, and debugging flaky locators that break every time a developer changes a single CSS class.&lt;/p&gt;

&lt;p&gt;We call this &lt;strong&gt;"String Soup"&lt;/strong&gt;—a mess of long, error-prone strings that hide your automation intent.&lt;/p&gt;

&lt;p&gt;But what if you could write locators that look like clean, fluent Java code? What if they could adapt automatically to whitespace, casing, and special characters?&lt;/p&gt;

&lt;p&gt;Meet &lt;strong&gt;XPathy&lt;/strong&gt;, a lightweight Java library designed to take the &lt;em&gt;pain&lt;/em&gt; out of XPath.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔍 The Problem: Why XPath Strings Fail Us
&lt;/h2&gt;

&lt;p&gt;Consider a common scenario: you need to find a submit button that has a specific ID but is not currently disabled, and its text should be case-insensitive.&lt;/p&gt;

&lt;h3&gt;
  
  
  ❌ The String Soup XPath
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;@id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'submit-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;@class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'ABCDEFGHIJKLMNOPQRSTUVWXYZ'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'abcdefghijklmnopqrstuvwxyz'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'disabled'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'ABCDEFGHIJKLMNOPQRSTUVWXYZ'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'abcdefghijklmnopqrstuvwxyz'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single line is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Unreadable:&lt;/strong&gt; Good luck figuring out the logic in six months.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Error-Prone:&lt;/strong&gt; Missing a quote, forgetting a bracket, or mistyping &lt;code&gt;translate&lt;/code&gt; is easy.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Untrustworthy:&lt;/strong&gt; It relies on complex functions and manual string management.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  ✅ The Solution: Fluent and Intent-Driven Code
&lt;/h2&gt;

&lt;p&gt;XPathy replaces raw strings with a &lt;strong&gt;fluent API&lt;/strong&gt; that maps human intent directly to XPath logic. Instead of manually writing &lt;code&gt;contains()&lt;/code&gt; or &lt;code&gt;translate()&lt;/code&gt;, you simply chain methods.&lt;/p&gt;

&lt;h3&gt;
  
  
  ✅ The XPathy Fluent Code
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Attribute&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Case&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"submit-"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;not&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IGNORED&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"disabled"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IGNORED&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;@id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'submit-'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;@class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'ABCDEFGHIJKLMNOPQRSTUVWXYZ'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'abcdefghijklmnopqrstuvwxyz'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'disabled'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;translate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'ABCDEFGHIJKLMNOPQRSTUVWXYZ'&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'abcdefghijklmnopqrstuvwxyz'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'login'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The code is now:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Readable:&lt;/strong&gt; It reads like an English sentence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type-Safe:&lt;/strong&gt; The compiler catches typos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maintainable:&lt;/strong&gt; Add or remove conditions without breaking anything.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🔪 Three Pillars of Robust Locators
&lt;/h2&gt;

&lt;p&gt;XPathy solves the three biggest problems with locators: brittleness, complexity, and ambiguity.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Bulletproof Locators with Transformations 🛡️
&lt;/h3&gt;

&lt;p&gt;The most common reason for flaky tests is inconsistency in UI values (whitespace, case, or formatting). XPathy’s &lt;strong&gt;Transformations&lt;/strong&gt; automatically wrap your value in the necessary XPath functions.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Flaky Scenario&lt;/th&gt;
&lt;th&gt;XPathy Solution&lt;/th&gt;
&lt;th&gt;Fluent Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Case Inconsistency: &lt;code&gt;Login&lt;/code&gt; vs &lt;code&gt;LOGIN&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;withCase(IGNORED)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;byText().withCase(IGNORED).equals("login")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whitespace Padding: &lt;code&gt;" Invalid Password "&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;withNormalizeSpace()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;byText().withNormalizeSpace().equals("Invalid Password")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Special Characters: &lt;code&gt;$1,999.00&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;withRemoveOnly(SPECIAL_CHARACTERS)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;byText().withRemoveOnly(SPECIAL_CHARACTERS).equals("199900")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accents/Diacritics: &lt;code&gt;Café&lt;/code&gt; vs &lt;code&gt;Cafe&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;withTranslate()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;byText().withTranslate("éàè", "eae").contains("Cafe")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  2. Mastering Complex Logic and Precedence 🧠
&lt;/h3&gt;

&lt;p&gt;When a simple &lt;code&gt;.and()&lt;/code&gt; isn’t enough, XPathy gives you &lt;strong&gt;explicit logical control&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  A. Grouping Conditions with Union and Intersect
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Match a button that has one of three possible dynamic IDs&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                       &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;union&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                           &lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login-btn"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                           &lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"signin-btn"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                           &lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"auth-"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                       &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;@id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'login-btn'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;@id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'signin-btn'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;@id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'auth-'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  B. Nested Logic for Precision
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byCondition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;or&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pending"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"processing"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="ow"&gt;div&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'Order'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;@class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'pending'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;@class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'processing'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3. DOM Relationships and Having Operations 🔍
&lt;/h3&gt;

&lt;p&gt;Defining relationships in XPath is powerful but hard to read. XPathy simplifies this with &lt;code&gt;byHaving()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Find a &amp;lt;div&amp;gt; product card that *has* a descendant button with "Add to Cart" text&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"product-card"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;descendant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                         &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Add to Cart"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Result:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="ow"&gt;div&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;@class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'product-card'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;and&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;.//&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;text&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;'Add to Cart'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes it effortless to filter parent elements based on the content of their descendants.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 From XPathy to Selenium
&lt;/h2&gt;

&lt;p&gt;XPathy integrates directly with Selenium—no extra setup.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Get the Selenium Locator:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;By&lt;/span&gt; &lt;span class="n"&gt;seleniumLocator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLocator&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;driver&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findElement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seleniumLocator&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Get the Raw XPath String (for logging/debugging):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;rawXPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  📊 Conclusion: Stop Writing Strings, Start Writing Code
&lt;/h2&gt;

&lt;p&gt;XPathy is more than a utility—it’s a &lt;em&gt;paradigm shift&lt;/em&gt; for locator management. It turns fragile XPath strings into expressive, fluent, and type-safe Java code.&lt;/p&gt;

&lt;p&gt;If your tests constantly break because of small UI changes, it’s time to stop debugging &lt;strong&gt;String Soup&lt;/strong&gt; and start building &lt;strong&gt;robust, readable, and maintainable locators&lt;/strong&gt; with XPathy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Ready to start? Check out the full documentation and repository.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  XPathy provides a lot more: Read the Full Documentation:
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753"&gt;https://dev.to/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  The Repository:
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://github.com/Volta-Jebaprashanth/xpathy" rel="noopener noreferrer"&gt;https://github.com/Volta-Jebaprashanth/xpathy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Happy Testing!&lt;/strong&gt; 🚀&lt;/p&gt;

</description>
      <category>testing</category>
      <category>automation</category>
      <category>tooling</category>
      <category>java</category>
    </item>
    <item>
      <title>XPathy: A Fluent API for Writing Smarter, Cleaner XPath in Selenium</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Mon, 29 Sep 2025 16:19:26 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/xpathy-a-fluent-api-for-writing-smarter-cleaner-xpath-in-selenium-5753</guid>
      <description>&lt;h1&gt;
  
  
  XPathy User Manual
&lt;/h1&gt;

&lt;p&gt;XPathy is a lightweight Java library that simplifies the creation of &lt;strong&gt;XPath expressions&lt;/strong&gt; to be used in Selenium. Instead of manually writing long, error‑prone strings, XPathy allows you to build expressions using a &lt;strong&gt;fluent API&lt;/strong&gt;. This makes your locators more &lt;strong&gt;readable&lt;/strong&gt;, &lt;strong&gt;maintainable&lt;/strong&gt;, and &lt;strong&gt;scalable&lt;/strong&gt;. XPathy takes away the frustration of balancing brackets, quotes, and functions, letting developers focus on expressing intent clearly.&lt;/p&gt;

&lt;p&gt;When you create an XPathy object, you can call &lt;strong&gt;&lt;code&gt;.getLocator()&lt;/code&gt;&lt;/strong&gt; to return a Selenium &lt;strong&gt;&lt;code&gt;By&lt;/code&gt;&lt;/strong&gt;  object, or call &lt;strong&gt;&lt;code&gt;.toString()&lt;/code&gt;&lt;/strong&gt; to get the XPath, making it directly usable in your automation scripts. XPathy is compatible with any Selenium version &lt;strong&gt;3.0 or higher&lt;/strong&gt; with any &lt;code&gt;Java&lt;/code&gt; or &lt;code&gt;Kotlin&lt;/code&gt; versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Volta-Jebaprashanth/xpathy" rel="noopener noreferrer"&gt;https://github.com/Volta-Jebaprashanth/xpathy&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  👨‍💻 Author
&lt;/h2&gt;

&lt;p&gt;Created by &lt;strong&gt;Volta Jebaprashanth&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
📧 &lt;a href="//mailto:voltajeba@gmail.com"&gt;voltajeba@gmail.com&lt;/a&gt;&lt;br&gt;&lt;br&gt;
📞 +94 77 463 7185&lt;br&gt;&lt;br&gt;
🔗 &lt;a href="https://www.linkedin.com/in/voltajeba" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  📦 Package
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.xpathy&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📦 Installation (via JitPack)
&lt;/h2&gt;

&lt;p&gt;📎 &lt;a href="https://jitpack.io/#Volta-Jebaprashanth/xpathy/3.0.0" rel="noopener noreferrer"&gt;View on JitPack&lt;/a&gt;&lt;br&gt;
for other installation modules.&lt;/p&gt;

&lt;p&gt;To use this library in your &lt;strong&gt;Maven&lt;/strong&gt; project (pom.xml):&lt;/p&gt;
&lt;h3&gt;
  
  
  Add the JitPack repository:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;repositories&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;repository&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;id&amp;gt;&lt;/span&gt;jitpack.io&lt;span class="nt"&gt;&amp;lt;/id&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;url&amp;gt;&lt;/span&gt;https://jitpack.io&lt;span class="nt"&gt;&amp;lt;/url&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/repository&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/repositories&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Add the XPathy dependency:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.github.Volta-Jebaprashanth&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;xpathy&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;3.0.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  A - Basic Operations
&lt;/h1&gt;

&lt;p&gt;This section introduces the basic operations of XPathy with corresponding XPath outputs. By the end of this manual, you will understand how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Use attributes (&lt;code&gt;id&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;data-*&lt;/code&gt;, etc.)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Work with HTML tags and combine them with attributes&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Target text content inside elements&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Handle numeric comparisons for attributes and inner text&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Query inline style attributes&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  1. Working with Attributes
&lt;/h2&gt;

&lt;p&gt;Attributes are the most common entry point for XPath locators. XPathy exposes all HTML attributes as objects, each with chainable methods.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import all attributes:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Attribute&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Contains on &lt;code&gt;id&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login-button"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //*[contains(@id, 'login-button')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Equals on &lt;code&gt;class&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //*[@class='active']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;StartsWith on &lt;code&gt;data-testid&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;data_testid&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"menu-"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //*[starts-with(@data-testid, 'menu-')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Numeric comparisons on &lt;code&gt;value&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;greaterThan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //*[@value &amp;gt; 100]&lt;/span&gt;

&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lessThan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //*[@value &amp;lt; 50]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Additional methods include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;haveIt()&lt;/code&gt; → checks whether an attribute exists&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;isEmpty()&lt;/code&gt; → confirms an attribute is present but empty&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;isNumeric()&lt;/code&gt; → ensures an attribute’s value is numeric&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Attributes within Specific Tags
&lt;/h2&gt;

&lt;p&gt;XPathy allows scoping attributes inside specific HTML tags, making locators more precise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import all tags:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Find a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; by &lt;code&gt;id&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main-container"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //div[@id='main-container']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Find a &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; by &lt;code&gt;class&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"section-title"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //h2[@class='section-title']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Find a &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; by &lt;code&gt;data-testid&lt;/code&gt; starting with text&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_testid&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"paragraph-"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //p[starts-with(@data-testid, 'paragraph-')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Note:&lt;/strong&gt; Every attribute method (&lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;contains&lt;/code&gt;, &lt;code&gt;startsWith&lt;/code&gt;, &lt;code&gt;greaterThan&lt;/code&gt;, etc.) works with every supported tag.
&lt;/h2&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  3. Working with Text Content
&lt;/h2&gt;

&lt;p&gt;XPathy provides intuitive methods for targeting visible text inside elements.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Text contains&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Welcome"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //div[contains(text(), 'Welcome')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Text starts with&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Chapter"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //h2[starts-with(text(), 'Chapter')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Global Text usage&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Error"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //*[contains(text(), 'Error')]&lt;/span&gt;

&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Success"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //*[starts-with(text(), 'Success')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is useful when attributes are dynamic but the element text is stable.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Numeric Values Inside Elements
&lt;/h2&gt;

&lt;p&gt;Some elements display numbers, such as counters or prices. XPathy lets you build conditions around them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Greater than numeric content&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byNumber&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;greaterThan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //td[number(text()) &amp;gt; 10]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Between numeric values&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byNumber&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;between&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //span[number(text()) &amp;gt;= 5 and number(text()) &amp;lt;= 15]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is especially handy for table cells or statistic widgets.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Working with Styles
&lt;/h2&gt;

&lt;p&gt;Inline styles can be targeted when attributes or text are insufficient.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Check inline style for background colour within a tag&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byStyle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backgroundColor&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"#000000"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //div[contains(translate(@style, ' ', ''), 'background-color:#000000;')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Check inline style directly&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Style&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;backgroundColor&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"#000000"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //*[contains(translate(@style, ' ', ''), 'background-color:#000000;')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h1&gt;
  
  
  B - Understanding the Architecture Flow
&lt;/h1&gt;

&lt;p&gt;XPathy follows a layered architecture for building locators. Each starting point such as &lt;code&gt;.byText()&lt;/code&gt;, &lt;code&gt;.byAttribute()&lt;/code&gt;, &lt;code&gt;.byNumber()&lt;/code&gt;, or &lt;code&gt;.byStyle()&lt;/code&gt; returns a &lt;strong&gt;builder object&lt;/strong&gt; that knows how to handle that context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.byText()&lt;/code&gt; → switches context to element text, allowing operations like &lt;code&gt;.equals()&lt;/code&gt;, &lt;code&gt;.contains()&lt;/code&gt;, &lt;code&gt;.startsWith()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.byAttribute(attribute)&lt;/code&gt; → switches context to a specific attribute, enabling methods such as &lt;code&gt;.equals()&lt;/code&gt;, &lt;code&gt;.contains()&lt;/code&gt;, &lt;code&gt;.startsWith()&lt;/code&gt;, &lt;code&gt;.greaterThan()&lt;/code&gt;, &lt;code&gt;.lessThan()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.byNumber()&lt;/code&gt; → converts inner text into a number, making numeric methods like &lt;code&gt;.greaterThan()&lt;/code&gt;, &lt;code&gt;.lessThan()&lt;/code&gt;, &lt;code&gt;.between()&lt;/code&gt; available.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.byStyle(styleAttribute)&lt;/code&gt; → inspects inline CSS properties inside the &lt;code&gt;style&lt;/code&gt; attribute, and supports &lt;code&gt;.equals()&lt;/code&gt;, &lt;code&gt;.haveIt()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How methods are chained
&lt;/h3&gt;

&lt;p&gt;When you call &lt;code&gt;.equals()&lt;/code&gt;, &lt;code&gt;.contains()&lt;/code&gt;, &lt;code&gt;.startsWith()&lt;/code&gt;, etc., you are finalizing the condition on the selected context. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"header"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;div&lt;/code&gt; sets the base tag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.byAttribute(id)&lt;/code&gt; selects the &lt;code&gt;id&lt;/code&gt; attribute.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.equals("header")&lt;/code&gt; finalizes the expression as &lt;code&gt;//div[@id='header']&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Similarly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Title"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;h2&lt;/code&gt; sets the base tag.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.byText()&lt;/code&gt; switches to the text node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.startsWith("Title")&lt;/code&gt; produces &lt;code&gt;//h2[starts-with(text(),'Title')]&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This consistent flow applies to &lt;strong&gt;all contexts&lt;/strong&gt;. You always begin with a tag or attribute, select the context with &lt;code&gt;.byText()&lt;/code&gt;, &lt;code&gt;.byAttribute()&lt;/code&gt;, &lt;code&gt;.byNumber()&lt;/code&gt;, or &lt;code&gt;.byStyle()&lt;/code&gt;, then finalize with methods like &lt;code&gt;.equals()&lt;/code&gt;, &lt;code&gt;.contains()&lt;/code&gt;, &lt;code&gt;.startsWith()&lt;/code&gt;, &lt;code&gt;.greaterThan()&lt;/code&gt;, or &lt;code&gt;.between()&lt;/code&gt;. The resulting &lt;code&gt;XPathy&lt;/code&gt; object can then be converted to a Selenium &lt;code&gt;By&lt;/code&gt; object with &lt;code&gt;.getLocator()&lt;/code&gt;.&lt;/p&gt;




&lt;h1&gt;
  
  
  C - Basic Logical Operations
&lt;/h1&gt;

&lt;p&gt;XPathy also supports combining multiple conditions with logical operators. These map directly to &lt;strong&gt;XPath &lt;code&gt;and()&lt;/code&gt;, &lt;code&gt;or()&lt;/code&gt;, and &lt;code&gt;not()&lt;/code&gt; constructs&lt;/strong&gt;, but with a fluent, chainable API that preserves readability.&lt;/p&gt;

&lt;p&gt;Logical operations can be applied between &lt;strong&gt;different contexts&lt;/strong&gt; (attributes, text, numbers, styles), making it possible to express powerful compound locators without juggling parentheses and syntax manually.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. &lt;code&gt;and()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.and()&lt;/code&gt; operator joins two conditions that must &lt;strong&gt;both be true&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main-container"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result: //div[@id='main-container' and contains(text(), 'Hello World')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;div.byAttribute(id).equals("main-container")&lt;/code&gt; → &lt;code&gt;//div[@id='main-container']&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;.and().byText().contains("Hello World")&lt;/code&gt; → adds an additional condition on the same &lt;code&gt;div&lt;/code&gt; node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Final → &lt;code&gt;//div[@id='main-container' and contains(text(), 'Hello World')]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  2. &lt;code&gt;or()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.or()&lt;/code&gt; operator joins two conditions where &lt;strong&gt;either one may be true&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main-container"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;or&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result: //div[@id='main-container' or contains(text(), 'Hello World')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Matches any &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with &lt;code&gt;id="main-container"&lt;/code&gt; &lt;strong&gt;OR&lt;/strong&gt; text containing &lt;code&gt;"Hello World"&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  3. &lt;code&gt;not()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;.not()&lt;/code&gt; operator negates the following condition. This allows you to exclude elements matching a certain attribute, text, or style.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;not&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main-container"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result: //div[contains(text(), 'Hello World') and not(@id='main-container')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;First condition: &lt;code&gt;contains(text(), 'Hello World')&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Second condition: &lt;code&gt;not(@id='main-container')&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Combined with &lt;code&gt;.and()&lt;/code&gt; → &lt;code&gt;//div[contains(text(), 'Hello World') and not(@id='main-container')]&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  4. Chaining Multiple Logical Operations
&lt;/h2&gt;

&lt;p&gt;XPathy allows chaining &lt;code&gt;and()&lt;/code&gt;, &lt;code&gt;or()&lt;/code&gt;, and &lt;code&gt;not()&lt;/code&gt; in sequence to build more complex predicates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Discount"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;not&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"expired"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;or&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byNumber&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;greaterThan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//span[contains(text(), 'Discount') and not(@class='expired') or number(text()) &amp;gt; 50]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Usage Tips:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Parentheses are automatically handled to preserve correct evaluation order.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can mix attribute, text, number, and style conditions freely.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;code&gt;.not()&lt;/code&gt; immediately before &lt;code&gt;.equals()&lt;/code&gt;, &lt;code&gt;.contains()&lt;/code&gt;, &lt;code&gt;.startsWith()&lt;/code&gt;, etc.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;✅ With logical operators, XPathy expressions scale from simple attribute checks to full-fledged business rules written in a clear, fluent style.&lt;/p&gt;

&lt;h1&gt;
  
  
  D - DOM Navigation
&lt;/h1&gt;

&lt;p&gt;XPathy provides intuitive methods for navigating the DOM tree. These methods allow you to traverse relationships between elements—moving up, down, or sideways—while still chaining into text, attributes, numbers, or styles.&lt;/p&gt;

&lt;p&gt;All navigation methods starts with &lt;code&gt;$&lt;/code&gt; made you easy to extend it with a traversal operator such as &lt;code&gt;$child()&lt;/code&gt;, &lt;code&gt;$parent()&lt;/code&gt;, &lt;code&gt;$ancestor()&lt;/code&gt;, &lt;code&gt;$descendant()&lt;/code&gt;, &lt;code&gt;$followingSibling()&lt;/code&gt;, or &lt;code&gt;$precedingSibling()&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. &lt;code&gt;$tag(tag)&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Targets a nested tag under the current element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$tag&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Submit"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[@class='container']//button[text()='Submit']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. &lt;code&gt;$child()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Restricts traversal to &lt;strong&gt;immediate child elements&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"menu"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$child&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Home"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//ul[@id='menu']/child::*[contains(text(), 'Home')]&lt;/span&gt;


&lt;span class="c1"&gt;// Specific child tag&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"menu"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Contact"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//ul[@id='menu']/child::li[contains(text(), 'Contact')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. &lt;code&gt;$ancestor()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Moves upward in the DOM to match &lt;strong&gt;ancestor elements&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;href&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"profile"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$ancestor&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"navbar"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//a[contains(@href, 'profile')]/ancestor::*[@id='navbar']&lt;/span&gt;


&lt;span class="c1"&gt;// Specific ancestor tag&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Settings"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$ancestor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dropdown"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//span[text()='Settings']/ancestor::div[@class='dropdown']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  4. &lt;code&gt;$descendant()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Matches &lt;strong&gt;descendant elements&lt;/strong&gt; nested anywhere below the current node.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"content"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$descendant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Welcome"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//section[@id='content']/descendant::p[contains(text(), 'Welcome')]&lt;/span&gt;


&lt;span class="c1"&gt;// Any descendant&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"card"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$descendant&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"price"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[@class='card']/descendant::*[@class='price']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. &lt;code&gt;$parent()&lt;/code&gt; and &lt;code&gt;$up()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Navigate &lt;strong&gt;one or more levels up&lt;/strong&gt; the DOM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$19.99"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$parent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"product"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//span[text()='$19.99']/parent::div[@class='product']&lt;/span&gt;


&lt;span class="c1"&gt;// Move up multiple levels&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$up&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"form-container"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//input[@name='email']/../..[@id='form-container']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  6. &lt;code&gt;$followingSibling()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Matches siblings that appear &lt;strong&gt;after&lt;/strong&gt; the current element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Username"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$followingSibling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//label[text()='Username']/following-sibling::input[@type='text']&lt;/span&gt;


&lt;span class="c1"&gt;// Any following sibling&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$followingSibling&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//h2[text()='Features']/following-sibling::*[@class='description']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  7. &lt;code&gt;$precedingSibling()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Matches siblings that appear &lt;strong&gt;before&lt;/strong&gt; the current element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Contact"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$precedingSibling&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"About"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//li[text()='Contact']/preceding-sibling::*[text()='About']&lt;/span&gt;


&lt;span class="c1"&gt;// Specific preceding sibling&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Canada"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$precedingSibling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;option&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"USA"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//option[text()='Canada']/preceding-sibling::option[text()='USA']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  8. Multiple Navigations
&lt;/h2&gt;

&lt;p&gt;XPathy also supports &lt;strong&gt;chaining multiple navigation steps&lt;/strong&gt; in sequence to express complex DOM relationships.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main-container"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$parent&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$followingSibling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;$descendant&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello World"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[contains(@id, 'main-container')]/../following-sibling::div/descendant::*[contains(text(), 'Hello World')]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;✅ With DOM navigation, XPathy makes parent-child, ancestor-descendant, and sibling relationships &lt;strong&gt;explicit, realistic, and chainable&lt;/strong&gt; using examples drawn from real-world UIs.&lt;/p&gt;

&lt;h1&gt;
  
  
  E - Value Transformations
&lt;/h1&gt;

&lt;p&gt;One of the most powerful features of XPathy is the ability to &lt;strong&gt;transform values&lt;/strong&gt; before applying conditions. Transformations make locators more &lt;strong&gt;robust&lt;/strong&gt; against variations in casing, whitespace, special characters, numbers, or accented characters.&lt;/p&gt;

&lt;p&gt;Transformations are chainable and can be combined in sequence. They apply to the current context (attribute, text, number, or style) before the final predicate (&lt;code&gt;equals&lt;/code&gt;, &lt;code&gt;contains&lt;/code&gt;, &lt;code&gt;startsWith&lt;/code&gt;, etc.) is applied.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Case Transformations
&lt;/h2&gt;

&lt;p&gt;Import cases:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Case&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Ignore Case
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IGNORED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login-button"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//button[contains(translate(@id, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'login-button')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Useful when the &lt;code&gt;id&lt;/code&gt; attribute can appear in different cases (&lt;code&gt;Login-Button&lt;/code&gt;, &lt;code&gt;LOGIN-BUTTON&lt;/code&gt;, etc.). Without this, you’d have to write multiple OR conditions. This makes the locator simpler and case-proof.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; A login button has dynamic casing depending on the build pipeline. Instead of maintaining different XPaths, this ensures one locator works for all.&lt;/p&gt;




&lt;h3&gt;
  
  
  Force Uppercase
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UPPER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"USERNAME"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//label[translate(text(), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')='USERNAME']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Normalizes all text to uppercase before comparing. Makes it easy when UI labels are expected to be uppercase, but sometimes come mixed-case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; A form label might appear as &lt;code&gt;Username&lt;/code&gt;, &lt;code&gt;USERNAME&lt;/code&gt;, or &lt;code&gt;UserName&lt;/code&gt;. With transformation, all variations still match.&lt;/p&gt;




&lt;h3&gt;
  
  
  Force Lowercase
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOWER&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='active']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Ensures attribute comparison works even when class values vary by case. CSS classes are often lowercase, but some dev teams mix formats.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; You’re matching &lt;code&gt;&amp;lt;div class="Active"&amp;gt;&lt;/code&gt; vs &lt;code&gt;&amp;lt;div class="active"&amp;gt;&lt;/code&gt;. This keeps the locator consistent.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Whitespace Handling
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Normalize Space in Text
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withNormalizeSpace&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Invalid password"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[normalize-space(text())='Invalid password']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Cleans up inconsistent spacing inside text content.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Error messages sometimes appear with odd padding: &lt;code&gt;"Invalid password"&lt;/code&gt;. This matches regardless of spacing.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Character Filtering (Keep or Remove)
&lt;/h2&gt;

&lt;p&gt;Import filters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Only&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Keep Only
&lt;/h3&gt;

&lt;p&gt;You can use one / many Only enums listed in the &lt;code&gt;Only&lt;/code&gt; class inside these transformations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withKeepOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENGLISH_ALPHABETS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ProductABC"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//span[contains(translate(text(), translate(text(), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', ''), ''), 'ProductABC')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Strips everything except letters, ignoring numbers or symbols.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Product titles like &lt;code&gt;Product-ABC&lt;/code&gt; should still match when you only care about the alphabetic part.&lt;/p&gt;




&lt;h3&gt;
  
  
  Keep Only with Multiple &lt;code&gt;Only&lt;/code&gt; enums
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withKeepOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;ENGLISH_ALPHABETS&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="no"&gt;NUMBERS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"ORD1234"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//td[translate(text(), translate(text(), 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', ''), '')='ORD1234']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Preserves letters and digits, removing symbols or spaces.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Order IDs sometimes render as &lt;code&gt;ORD-1234&lt;/code&gt; or &lt;code&gt;ORD 1234&lt;/code&gt;. This transformation makes them match as &lt;code&gt;ORD1234&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Remove Only
&lt;/h3&gt;

&lt;p&gt;Same as &lt;code&gt;keepOnly()&lt;/code&gt; You can use one / many Only enums listed in the &lt;code&gt;Only&lt;/code&gt; class inside these transformations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withRemoveOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SPECIAL_CHARACTERS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"1999"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//span[contains(translate(text(), concat('!@#$%^&amp;amp;*()_+-=[]{}|;:,./&amp;lt;&amp;gt;?`~' , "'", '"'), ''), '1999')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Eliminates all the symbols&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Prices appear as &lt;code&gt;$1,999&lt;/code&gt; or &lt;code&gt;€1.999&lt;/code&gt;. After removing special characters, you can reliably match &lt;code&gt;1999&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Character Translation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withTranslate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"éàè"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"eae"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cafe"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//h1[contains(translate(text(), 'éàè', 'eae'), 'Cafe')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Replaces accented letters with plain equivalents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; The UI shows &lt;code&gt;Café&lt;/code&gt;, &lt;code&gt;Cafè&lt;/code&gt;, or &lt;code&gt;Càfe&lt;/code&gt;. Translating accents ensures all match &lt;code&gt;Cafe&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Combining Multiple Transformations
&lt;/h2&gt;

&lt;p&gt;Transformations can be &lt;strong&gt;stacked&lt;/strong&gt; in sequence, and be applied in the order.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withNormalizeSpace&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withRemoveOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;NUMBERS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withTranslate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"éàè"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"eae"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IGNORED&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"premium cafe"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[contains(translate(translate(translate(normalize-space(text()), 'éàè', 'eae'), '0123456789', ''), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'premium cafe')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Cleans text thoroughly by trimming, removing numbers, translating accents, and ignoring case.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Product title in UI appears as &lt;code&gt;" Prémium Café 2024 "&lt;/code&gt; or &lt;code&gt;"PREMIUM CAFE"&lt;/code&gt;. This locator still matches it reliably.&lt;/p&gt;




&lt;p&gt;✅ Transformations are the &lt;strong&gt;critical differentiator&lt;/strong&gt; in XPathy. They eliminate brittle locators by normalizing casing, whitespace, characters, and formatting differences automatically, letting you focus on intent rather than string quirks.&lt;/p&gt;

&lt;h1&gt;
  
  
  F - Union and Intersect Logical Operations
&lt;/h1&gt;

&lt;p&gt;XPathy goes beyond simple &lt;code&gt;and()&lt;/code&gt;, &lt;code&gt;or()&lt;/code&gt;, and &lt;code&gt;not()&lt;/code&gt; chaining by introducing &lt;strong&gt;union&lt;/strong&gt; and &lt;strong&gt;intersect&lt;/strong&gt; operations. These allow you to group multiple conditions into clean, reusable blocks, improving readability and reducing parenthesis juggling in complex locators.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Union (&lt;code&gt;union(Or...)&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;union()&lt;/code&gt; method combines multiple &lt;strong&gt;OR conditions&lt;/strong&gt; into a single predicate. Instead of chaining multiple &lt;code&gt;.or()&lt;/code&gt; calls, you can list them together for clarity.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Multiple Login Button IDs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;union&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login-btn"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                           &lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"signin-btn"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                           &lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"auth"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//button[@id='login-btn' or @id='signin-btn' or contains(@id, 'auth')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Matches any &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; used for logging in, regardless of whether the app calls it &lt;code&gt;login-btn&lt;/code&gt;, &lt;code&gt;signin-btn&lt;/code&gt;, or something dynamic like &lt;code&gt;auth-123&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Different environments (dev, QA, prod) may use slightly different IDs for the login button. Instead of writing separate locators for each, the union ensures one robust locator works everywhere.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Intersect (&lt;code&gt;intersect(And...)&lt;/code&gt;)
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;intersect()&lt;/code&gt; method combines multiple &lt;strong&gt;AND conditions&lt;/strong&gt; into one predicate. This is especially useful when you want a field or text to satisfy multiple patterns simultaneously.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Order Confirmation Messages
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;intersect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;And&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Order #"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                              &lt;span class="nc"&gt;And&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Confirmed"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                              &lt;span class="nc"&gt;And&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;not&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Cancelled"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[starts-with(text(), 'Order #') and contains(text(), 'Confirmed') and not(contains(text(), 'Cancelled'))]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Matches any &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; showing an order confirmation such as &lt;code&gt;Order #1234 Confirmed&lt;/code&gt;, but excludes cancelled orders like &lt;code&gt;Order #1234 Cancelled&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; After placing an order, the app shows confirmation text in different formats. Intersect ensures your test only picks valid confirmed orders.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Using Transformations with Union and Intersect
&lt;/h2&gt;

&lt;p&gt;Just like with single conditions, you can apply &lt;strong&gt;transformations&lt;/strong&gt; inside &lt;code&gt;union()&lt;/code&gt; and &lt;code&gt;intersect()&lt;/code&gt;. This makes locators more resilient to variations in casing, spacing, or special characters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Union with Transformation for Navigation Tabs
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;union&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withRemoveOnly&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;SPECIAL_CHARACTERS&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                           &lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IGNORED&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"selected"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//li[contains(translate(@class, concat('!@#$%^&amp;amp;*()_+-=[]{}|;:,./&amp;lt;&amp;gt;?`~\' , '"',"'"), ''), 'active') or translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')='selected']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; A navigation tab might render with classes like &lt;code&gt;active!&lt;/code&gt;, &lt;code&gt;selected&lt;/code&gt;, or &lt;code&gt;SELECTED&lt;/code&gt;. With transformations, the locator still works consistently.&lt;/p&gt;




&lt;h3&gt;
  
  
  Example: Intersect with Transformation for Product Labels
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;intersect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;And&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withNormalizeSpace&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Premium"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                              &lt;span class="nc"&gt;And&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;LOWER&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"subscription"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//span[contains(normalize-space(text()), 'Premium') and contains(translate(text(), 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), 'subscription')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Product labels may appear as &lt;code&gt;Premium SUBSCRIPTION&lt;/code&gt;, &lt;code&gt;premium subscription&lt;/code&gt;, or &lt;code&gt;PREMIUM Subscription&lt;/code&gt;. Normalization and case-insensitive comparison ensure all variants match.&lt;/p&gt;




&lt;p&gt;✅ With &lt;strong&gt;union&lt;/strong&gt; and &lt;strong&gt;intersect&lt;/strong&gt;, XPathy makes real-world locators—like login buttons, order confirmations, and product labels—&lt;strong&gt;clean, robust, and transformation-friendly&lt;/strong&gt;, while still compiling to pure XPath under the hood.&lt;/p&gt;

&lt;h1&gt;
  
  
  G - Nested Logical Conditions
&lt;/h1&gt;

&lt;p&gt;XPathy also supports &lt;strong&gt;nested logical conditions&lt;/strong&gt;, allowing you to build deeply structured expressions with combinations of &lt;code&gt;and()&lt;/code&gt;, &lt;code&gt;or()&lt;/code&gt;, and &lt;code&gt;not()&lt;/code&gt;. This makes it possible to represent complex business rules in a way that is both &lt;strong&gt;clear and maintainable&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. How It Works
&lt;/h2&gt;

&lt;p&gt;Instead of chaining &lt;code&gt;and()&lt;/code&gt;, &lt;code&gt;or()&lt;/code&gt;, and &lt;code&gt;not()&lt;/code&gt; inline, you can use the &lt;strong&gt;&lt;code&gt;Condition&lt;/code&gt;&lt;/strong&gt; helper methods to group multiple conditions explicitly. This helps when certain expressions need parentheses for precedence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Import static methods:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Example: Nested Login Validation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byCondition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;startsWith&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Login"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;

                        &lt;span class="n"&gt;or&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                                &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Button"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                                &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"auth-btn"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                        &lt;span class="o"&gt;),&lt;/span&gt;

                        &lt;span class="n"&gt;not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;withCase&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;IGNORED&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"disabled"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[(starts-with(text(), 'Login')&lt;/span&gt;
&lt;span class="c1"&gt;//      and (contains(text(), 'Button') or contains(@id, 'auth-btn'))&lt;/span&gt;
&lt;span class="c1"&gt;//      and not(contains(translate(@class, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz'), 'disabled')))]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Must start with the word &lt;code&gt;Login&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Must either contain the text &lt;code&gt;Button&lt;/code&gt; &lt;strong&gt;or&lt;/strong&gt; have an ID containing &lt;code&gt;auth-btn&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Must &lt;strong&gt;not&lt;/strong&gt; contain the class &lt;code&gt;disabled&lt;/code&gt; (case-insensitive)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; On a login form, the submit button may appear as &lt;code&gt;Login Button&lt;/code&gt;, &lt;code&gt;Login&lt;/code&gt;, or even dynamically generated with ID &lt;code&gt;auth-btn-123&lt;/code&gt;. Sometimes the button is disabled with &lt;code&gt;disabled&lt;/code&gt; class. This nested locator ensures you only match the correct, enabled login button.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Example: Product Label with Nested Rules
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Nested Logical Conditions&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Condition&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byCondition&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;or&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Premium"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;

                        &lt;span class="n"&gt;and&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                                &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"highlight"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                                &lt;span class="n"&gt;attribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data_testid&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"featured"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                                &lt;span class="n"&gt;not&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Expired"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                        &lt;span class="o"&gt;)&lt;/span&gt;
                &lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//span[&lt;/span&gt;
&lt;span class="c1"&gt;//   contains(text(), 'Premium')&lt;/span&gt;
&lt;span class="c1"&gt;//   or (&lt;/span&gt;
&lt;span class="c1"&gt;//       @class='highlight'&lt;/span&gt;
&lt;span class="c1"&gt;//       and contains(@data-testid, 'featured')&lt;/span&gt;
&lt;span class="c1"&gt;//       and not(contains(text(), 'Expired'))&lt;/span&gt;
&lt;span class="c1"&gt;//   )&lt;/span&gt;
&lt;span class="c1"&gt;//]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Matches &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; elements that either contain the word &lt;code&gt;Premium&lt;/code&gt; &lt;strong&gt;OR&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Have class equal to &lt;code&gt;highlight&lt;/code&gt;, a &lt;code&gt;data-testid&lt;/code&gt; containing &lt;code&gt;featured&lt;/code&gt;, and text that does not contain &lt;code&gt;Expired&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; In an e-commerce site, premium products may be labeled with the word &lt;code&gt;Premium&lt;/code&gt; in the text, or tagged structurally with a &lt;code&gt;highlight&lt;/code&gt; class and &lt;code&gt;data-testid&lt;/code&gt; attribute like &lt;code&gt;featured-product&lt;/code&gt;. Expired promotions should be excluded. This nested locator ensures your tests target only valid premium or featured product labels.---&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Benefits of Nested Conditions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Clarity&lt;/strong&gt;: Explicit parentheses in code mirror how the XPath will evaluate.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;: Easy to add or remove conditions without breaking the structure.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: Supports mixing attributes, text, styles, and transformations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Accuracy&lt;/strong&gt;: Guarantees correct precedence when combining multiple conditions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;✅ With nested logical conditions, XPathy can model &lt;strong&gt;real-world business rules&lt;/strong&gt; like enabled login buttons or valid product labels in a fluent, readable style while generating precise XPath expressions.&lt;/p&gt;

&lt;h1&gt;
  
  
  H - Having Operations
&lt;/h1&gt;

&lt;p&gt;XPathy introduces &lt;strong&gt;Having operations&lt;/strong&gt;, which allow you to define conditions on &lt;strong&gt;related elements&lt;/strong&gt; (child, parent, ancestor, sibling, etc.) inside the same expression. This eliminates the need to manually switch context, while keeping locators expressive and precise.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;byHaving()&lt;/code&gt; method acts as a &lt;strong&gt;predicate builder&lt;/strong&gt;, where you can inject another XPathy object or specify traversal operators like &lt;code&gt;child&lt;/code&gt;, &lt;code&gt;descendant&lt;/code&gt;, &lt;code&gt;ancestor&lt;/code&gt;, etc.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Basic Having with Direct Condition
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"product-card"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                         &lt;span class="n"&gt;span&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"In Stock"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                         &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[@class='product-card' and ( span[contains(text(), 'In Stock')] )]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Selects &lt;code&gt;&amp;lt;div class="product-card"&amp;gt;&lt;/code&gt; elements &lt;strong&gt;only if they contain a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; with text containing "In Stock"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Useful when filtering product cards that explicitly show availability labels.&lt;/p&gt;




&lt;h2&gt;
  
  
  2. Having with Child
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                  &lt;span class="n"&gt;tr&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"total-row"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                  &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//table[( ./tr[@class='total-row'] )]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Matches an &lt;code&gt;&amp;lt;table&amp;gt;&lt;/code&gt; if it has a &lt;strong&gt;direct child row&lt;/strong&gt; with the class &lt;code&gt;total-row&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Ensures the order summary table contains a row summarizing the total.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Having with Descendant
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"checkout"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;descendant&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                      &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Place Order"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                      &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//section[@id='checkout' and ( .//button[contains(text(), 'Place Order')] )]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Matches a &lt;code&gt;&amp;lt;section id="checkout"&amp;gt;&lt;/code&gt; if &lt;strong&gt;any nested descendant button&lt;/strong&gt; contains the text "Place Order".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Ensures the checkout section contains the final purchase button.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Having with Ancestor
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"price-tag"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;ancestor&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                       &lt;span class="n"&gt;section&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"product-details"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                       &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//div[@class='price-tag' and ( ancestor::section[@id='product-details'] )]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Matches &lt;code&gt;&amp;lt;div class="price-tag"&amp;gt;&lt;/code&gt; only if it has a &lt;strong&gt;&lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; ancestor&lt;/strong&gt; with &lt;code&gt;id="product-details"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Useful when price elements appear in multiple contexts, but you only want those tied to the product details section.&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Having with Parent
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"menu-items"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                      &lt;span class="n"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;role&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"navigation"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                      &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//ul[@class='menu-items' and ( parent::nav[@role='navigation'] )]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Matches &lt;code&gt;&amp;lt;ul class="menu-items"&amp;gt;&lt;/code&gt; only if its &lt;strong&gt;immediate parent &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt;&lt;/strong&gt; has the role &lt;code&gt;navigation&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Ensures you are selecting menu items that belong to the main navigation bar.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Having with Following Sibling
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Features"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;followingSibling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                       &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"description"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                       &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//h2[text()='Features' and ( following-sibling::div[@class='description'] )]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Matches &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; with text "Features" only if a &lt;strong&gt;following sibling div&lt;/strong&gt; has class &lt;code&gt;description&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Useful when feature headers are always followed by a descriptive block.&lt;/p&gt;




&lt;h2&gt;
  
  
  7. Having with Preceding Sibling
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Contact"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;precedingSibling&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
                     &lt;span class="n"&gt;li&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"About"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//li[text()='Contact' and ( preceding-sibling::li[text()='About'] )]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Matches &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt; with text "Contact" only if a &lt;strong&gt;preceding sibling li&lt;/strong&gt; contains "About".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Ensures the navigation order is About → Contact.&lt;/p&gt;




&lt;h2&gt;
  
  
  8. Having with Simplified workflow
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;table&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invoice"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byHaving&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;child&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;td&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;byText&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Subtotal"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result:&lt;/span&gt;
&lt;span class="c1"&gt;//table[@id='invoice' and ./td[contains(text(), 'Subtotal')]]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description:&lt;/strong&gt; Matches &lt;code&gt;&amp;lt;table id="invoice"&amp;gt;&lt;/code&gt; if it contains a &lt;strong&gt;direct &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt; child&lt;/strong&gt; with text "Subtotal".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sample Situation:&lt;/strong&gt; Ensures invoices include a subtotal cell before calculating totals.&lt;/p&gt;




&lt;h2&gt;
  
  
  9. General Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Readability:&lt;/strong&gt; Express complex DOM relationships inline without writing full XPath manually.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexibility:&lt;/strong&gt; Mix with transformations (&lt;code&gt;withCase&lt;/code&gt;, &lt;code&gt;withRemoveOnly&lt;/code&gt;, etc.) for resilient conditions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Reusability:&lt;/strong&gt; You can insert any XPathy expression into &lt;code&gt;byHaving()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;✅ With &lt;strong&gt;Having operations&lt;/strong&gt;, XPathy allows conditions to be written on related elements — child, parent, ancestor, descendant, or siblings — while keeping expressions structured and business-readable.&lt;/p&gt;

&lt;h1&gt;
  
  
  🎯 Conclusion
&lt;/h1&gt;

&lt;p&gt;XPathy turns brittle, hand-written XPath into a fluent, readable DSL that scales with your UI and your team. From attribute/text/number/style contexts to robust value transformations, DOM navigation, logical composition (and/or/not, union/intersect), nested conditions, and &lt;strong&gt;Having&lt;/strong&gt; operations—everything is designed to express &lt;em&gt;intent&lt;/em&gt; clearly while compiling to pure XPath under the hood. The result is faster authoring, easier reviews, fewer flaky locators, and a test suite you can actually trust.&lt;/p&gt;

&lt;h3&gt;
  
  
  🤝 Contributing
&lt;/h3&gt;

&lt;p&gt;Issues, ideas, and PRs are welcome! Share real-world cases where XPathy simplified your locators, or propose new operators/transformations you’d love to see. Clear repros and before/after snippets help a ton.&lt;/p&gt;

&lt;h3&gt;
  
  
  💬 Support &amp;amp; Feedback
&lt;/h3&gt;

&lt;p&gt;If something feels clunky or you’ve got a “there must be a nicer way” moment—open an issue. XPathy grows best with practical feedback from active test suites.&lt;/p&gt;

&lt;h3&gt;
  
  
  🙌 Thanks
&lt;/h3&gt;

&lt;p&gt;Thanks to everyone building reliable UI tests and pushing for clearer, more maintainable code. Your feedback shapes XPathy’s roadmap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Made with ❤️ by  &lt;strong&gt;Volta Jebaprashanth&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;📧 &lt;a href="//mailto:voltajeba@gmail.com"&gt;voltajeba@gmail.com&lt;/a&gt;&lt;br&gt;
🔗 &lt;a href="https://www.linkedin.com/in/voltajeba" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Happy Testing with XPathy! 🚀&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>automation</category>
      <category>xpath</category>
      <category>locator</category>
    </item>
    <item>
      <title>Mastering Attribute-Based Locators in Selenium with XPathy</title>
      <dc:creator>Volta Jebaprashanth</dc:creator>
      <pubDate>Mon, 29 Sep 2025 16:12:52 +0000</pubDate>
      <link>https://forem.com/volta_jebaprashanth_ac7af/mastering-attribute-based-locators-in-selenium-with-xpathy-4gie</link>
      <guid>https://forem.com/volta_jebaprashanth_ac7af/mastering-attribute-based-locators-in-selenium-with-xpathy-4gie</guid>
      <description>&lt;p&gt;When it comes to Selenium automation, &lt;strong&gt;locators&lt;/strong&gt; are everything. If your locators are brittle, your tests will break at the slightest UI change. Among all locator strategies, &lt;strong&gt;XPath remains the most powerful—but also the most painful&lt;/strong&gt; to maintain. Writing &lt;code&gt;//div[@id='login-btn' and contains(@class,'active')]&lt;/code&gt; over and over is tedious, error‑prone, and clunky.&lt;/p&gt;

&lt;p&gt;This is where &lt;strong&gt;XPathy&lt;/strong&gt; changes the game.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why Attributes Matter
&lt;/h3&gt;

&lt;p&gt;Most real-world web elements are identified by attributes: &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;class&lt;/code&gt;, &lt;code&gt;data-testid&lt;/code&gt;, &lt;code&gt;role&lt;/code&gt;, or custom data attributes. Unfortunately, attribute handling in raw XPath can get verbose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xpath"&gt;&lt;code&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;@id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'login-btn'&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="ow"&gt;or&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;@class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="s"&gt;'primary'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now imagine doing this across hundreds of tests. It gets messy, fast.&lt;/p&gt;




&lt;h3&gt;
  
  
  Enter XPathy: Fluent Attribute Handling
&lt;/h3&gt;

&lt;p&gt;With XPathy, attributes become &lt;strong&gt;first-class citizens&lt;/strong&gt;. Instead of juggling quotes and brackets, you express intent directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Attribute&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;xpathy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Tag&lt;/span&gt;&lt;span class="o"&gt;.*;&lt;/span&gt;

&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"login-btn"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //button[@id='login-btn']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Want to match by class?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //div[contains(@class, 'active')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Need numeric comparisons on attributes like &lt;code&gt;value&lt;/code&gt;?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;greaterThan&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Result: //input[@value &amp;gt; 100]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Suddenly, the code reads like English—&lt;strong&gt;no lost time deciphering brackets.&lt;/strong&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Power Move: Attribute Chaining
&lt;/h3&gt;

&lt;p&gt;Raw XPath forces you to juggle &lt;code&gt;and&lt;/code&gt; conditions manually. XPathy makes it natural:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;div&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"main-container"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;and&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
                     &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;class_&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;contains&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"active"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Result: //div[@id='main-container' and contains(@class,'active')]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Readable. Scalable. Maintainable.&lt;/p&gt;




&lt;h3&gt;
  
  
  Real-World Scenario
&lt;/h3&gt;

&lt;p&gt;Let’s say you’re testing an e-commerce site. The “Add to Cart” button can look different depending on environment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dev: &lt;code&gt;&amp;lt;button id="add-btn"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;QA: &lt;code&gt;&amp;lt;button id="cart-add"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Prod: &lt;code&gt;&amp;lt;button id="add-to-cart"&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In plain XPath, you’d either duplicate locators or write a gnarly OR expression. With XPathy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;XPathy&lt;/span&gt; &lt;span class="n"&gt;locator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;byAttribute&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
                       &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;union&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"add-btn"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                              &lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cart-add"&lt;/span&gt;&lt;span class="o"&gt;),&lt;/span&gt;
                              &lt;span class="nc"&gt;Or&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"add-to-cart"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;

&lt;span class="c1"&gt;// Result: //button[@id='add-btn' or @id='cart-add' or @id='add-to-cart']&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One locator, robust across environments.&lt;/p&gt;




&lt;h3&gt;
  
  
  Why This Matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Less Fragile:&lt;/strong&gt; Avoids hand-crafted strings that collapse on UI tweaks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;More Expressive:&lt;/strong&gt; Reads like intent, not syntax.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Easier Onboarding:&lt;/strong&gt; New team members won’t fear XPath anymore.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-Proof:&lt;/strong&gt; Transformations (case-insensitive, whitespace normalization, symbol removal) make locators resilient to messy frontends.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Final Thoughts
&lt;/h3&gt;

&lt;p&gt;If you’re serious about automation, mastering attribute-based locators is a must. XPathy doesn’t replace XPath—it makes it &lt;strong&gt;human-friendly&lt;/strong&gt;. Attributes are no longer a pain point; they’re your strongest ally in building robust, future-proof Selenium tests.&lt;/p&gt;

&lt;p&gt;👉 Next up in this series: &lt;em&gt;“Taming Text Nodes: Smarter Content Locators with XPathy”&lt;/em&gt; 🚀&lt;/p&gt;

</description>
      <category>selenium</category>
      <category>automation</category>
      <category>xpath</category>
      <category>qualityassurance</category>
    </item>
  </channel>
</rss>
