Skip to content

Latest commit

 

History

History
68 lines (47 loc) · 4.18 KB

select-element-by-exact-innerText-regex-match.md

File metadata and controls

68 lines (47 loc) · 4.18 KB

Select element by exact innerText regExp match

User Story

Given this HTML:

<p>Target String</p>
<ul>
  <li>Target String but not really</li>
  <li>Target Strings?</li>
  <li>Not Target String</li>
  <li>Target String(nah)</li>
  <li>Target String</li> <!-- Select ONLY this -->
  <li>So not Target String</li>
  <li>No Target String here</li>
</ul>
  • I want to use Cypress to select one element from the HTML below. Specifically, the <li>Target String</li> element. Note the constraints:
    • There may be other elements of different types with the same innerText.
    • There may be elements on the same type that contain the same innerText plus more text either before, after the Target String, or both.
    • It's not possible to assign the innerText as an attribute for any of the <li> elements because they were generated by a library.
    • It's not possible to manually add a unique id or other attribute to the desired element, since it was generated by a library.

Solution

We need to select the <li> element with an exact match for our Target String. This is the most elegant way I can think of. We need a little help from our unsightly friend, regExp:

const regExp = /(?<!.)(Target String)(?!.)/;

cy.contains("li", regExp); // 🎉SUCCESS!

Initially, I didn't think the .contains() method was appropriate because the Cypress docs say it matches any string that includes the target string.

DOM elements can contain more than the desired text and still match.

Since they expressed this so explicitly even though they indicated that selectors could be regExp, I thought it meant that even a regExp selector would still return elements that included the target string.

👉 Click to read why I didn't choose other options

I tried using other strategies, but they were not successful.

  • .select would have been great, but it only works on <option> elements nested in <select>.
  • .get('li[innerText="Target String"]') - though you can use HTML attributes to build a more complex selector, you're limited by what is visible in the DOM. Every element has an innerText attribute under the hood, but it's not visible in the DOM so it can't be used.
  • .should did not seem to have a useful assertion or chainer.
  • I also thought of getting all <li> elements that contain Target String, but then I'd have to iterate through them, which does not seem elegant. I thought about using the .filter and .children in this case.
  • .find wouldn't work because it only accepts string selectors, not regExp.
  • .within, .spread, and .siblings seemed inelegant.
  • I probably considered other options but they were not quite suitable or elegant for other reasons.

RegExplanation

It matches the Target String ONLY if there are no characters before or after it. 😎

  • . - The dot/period matches any character.
  • (?<!.) - Negative Lookbehind - Don't match if there are any characters before the Target String.
  • (?!.) - Negative Lookahead - Don't match if there are any characters after the Target String.

Resources

My favorite regExp tool for years has been RegExr.com. It's excellent! It's fast, quick, free, open source, doesn't require sign-in, shows syntax errors, and explains your regExp to you, piece by piece! Of course, you can add your own text to test your regExp. I still come back to it when I'm dealing with a challenging regExp or need to check my work. Here is the GitHub repo.