0
0
Cypresstesting~15 mins

cy.contains() for text matching in Cypress - Deep Dive

Choose your learning style9 modes available
Overview - cy.contains() for text matching
What is it?
cy.contains() is a command in Cypress that finds elements on a web page by matching their visible text. It helps testers locate buttons, links, or any element containing specific words or phrases. This makes it easier to interact with elements without needing complex selectors. It works by searching the page for the text you provide and returns the first matching element.
Why it matters
Without cy.contains(), testers would have to rely on complicated CSS selectors or element IDs that might change often, making tests fragile and hard to maintain. Using text to find elements mimics how a user sees the page, making tests more readable and stable. This reduces test failures caused by UI changes and speeds up writing tests.
Where it fits
Before learning cy.contains(), you should understand basic Cypress commands like cy.get() and how to write simple tests. After mastering cy.contains(), you can learn advanced querying techniques, custom commands, and how to combine text matching with other selectors for precise testing.
Mental Model
Core Idea
cy.contains() finds page elements by looking for visible text, just like a user scanning the screen for words.
Think of it like...
It's like using a highlighter to find a word in a book; you look for the text you want and mark it, ignoring the exact font or page layout.
┌───────────────────────────────┐
│ Web Page Elements             │
│ ┌─────────────┐ ┌───────────┐ │
│ │ Button 'OK' │ │ Link 'Help'│ │
│ └─────────────┘ └───────────┘ │
│                               │
│ cy.contains('Help') → selects │
│ the 'Help' link element       │
└───────────────────────────────┘
Build-Up - 6 Steps
1
FoundationBasic usage of cy.contains()
🤔
Concept: Learn how to use cy.contains() to find an element by its visible text.
In Cypress, you write cy.contains('Submit') to find the first element that shows the text 'Submit'. This command returns that element so you can interact with it, like clicking or checking if it exists. Example: cy.contains('Submit').click() // clicks the button or link with text 'Submit'
Result
The test finds the element with text 'Submit' and clicks it successfully.
Understanding that cy.contains() searches visible text helps you write tests that are closer to how users see the page.
2
FoundationText matching is case sensitive by default
🤔
Concept: By default, cy.contains() matches text exactly, including case.
If you write cy.contains('submit'), but the button text is 'Submit', the command will NOT find it because of the lowercase 's'. To match case-insensitively, you need to use a regular expression with the 'i' flag. Example: cy.contains(/submit/i).click() // matches 'Submit', 'submit', 'SUBMIT', etc.
Result
The test finds the element regardless of text case and clicks it.
Knowing the default case sensitivity prevents confusion when tests fail to find elements due to letter case differences.
3
IntermediateUsing selectors with cy.contains()
🤔Before reading on: do you think cy.contains() can be combined with CSS selectors to narrow down the search? Commit to your answer.
Concept: You can specify a CSS selector as the first argument to limit where cy.contains() looks for text.
Syntax: cy.contains(selector, text) Example: cy.contains('button', 'Submit').click() // finds a
Result
The test clicks the 'Submit' button specifically, ignoring other elements with the same text.
Combining selectors with text matching makes tests more precise and reduces accidental matches.
4
IntermediateMatching partial text with cy.contains()
🤔Before reading on: do you think cy.contains() matches only exact full text or can it find partial matches? Commit to your answer.
Concept: cy.contains() matches elements containing the given text anywhere inside, not just exact full text matches.
Example: If an element has text 'Submit your form', cy.contains('Submit').click() will find it because 'Submit' is part of the text. You can also use regular expressions for more flexible matching: cy.contains(/sub/i) // matches any text containing 'sub' ignoring case
Result
The test finds elements containing the partial text and interacts with them.
Knowing cy.contains() matches partial text helps you write simpler selectors without needing exact full text.
5
AdvancedHandling multiple matches with cy.contains()
🤔Before reading on: if multiple elements contain the same text, do you think cy.contains() selects all or just one? Commit to your answer.
Concept: cy.contains() returns the first matching element by default, not all matches.
If multiple elements have the same text, cy.contains('Delete') will select only the first one found in the DOM order. To interact with others, you can use cy.contains() combined with .eq(index) or use cy.get() with filters. Example: cy.contains('Delete').eq(1).click() // clicks the second 'Delete' element
Result
The test clicks the specific matched element, avoiding ambiguity.
Understanding that cy.contains() picks the first match prevents unexpected test behavior when multiple elements share text.
6
Expertcy.contains() with shadow DOM and dynamic content
🤔Before reading on: do you think cy.contains() can find text inside shadow DOM or dynamically loaded elements by default? Commit to your answer.
Concept: By default, cy.contains() cannot access shadow DOM elements or elements not yet in the DOM; special handling is needed.
Shadow DOM hides elements inside a separate DOM tree. To find text inside it, you must use Cypress plugins or commands that pierce shadow roots. For dynamic content, cy.contains() waits up to the default timeout for the element to appear. Example for shadow DOM: cy.get('custom-element').shadow().contains('Click me').click() For dynamic content, cy.contains('Loading complete', { timeout: 10000 }) waits longer.
Result
The test successfully finds and interacts with text inside shadow DOM or waits for dynamic text to appear.
Knowing cy.contains() limitations with shadow DOM and dynamic content helps you write robust tests for modern web apps.
Under the Hood
cy.contains() works by traversing the DOM tree and checking the visible text content of each element. It uses the browser's native text rendering to find elements whose inner text matches the given string or pattern. It ignores hidden elements and stops at the first match unless further filtered. It also supports regular expressions for flexible matching. Internally, it uses Cypress's retry mechanism to wait for elements to appear before failing.
Why designed this way?
cy.contains() was designed to mimic how users find elements by reading visible text, making tests more natural and less brittle. Using text instead of complex selectors reduces maintenance when UI structure changes. The choice to return the first match by default balances simplicity and performance. Support for regular expressions adds flexibility without complicating the API.
┌───────────────────────────────┐
│ cy.contains('text') called    │
├───────────────────────────────┤
│ ↓                             │
│ Traverse DOM elements          │
│ ↓                             │
│ Check visible text of element  │
│ ↓                             │
│ Match text or regex?           │
│ ┌───────────────┐             │
│ │ Yes           │             │
│ │ Return element│             │
│ └───────────────┘             │
│ │ No                        │
│ ↓                           │
│ Continue to next element     │
│ ↓                           │
│ If none found, retry until timeout │
│ ↓                           │
│ Fail test if no match       │
└───────────────────────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Does cy.contains() find hidden elements by default? Commit to yes or no.
Common Belief:cy.contains() finds any element with matching text, even if it's hidden or invisible.
Tap to reveal reality
Reality:cy.contains() only finds elements that are visible to the user, ignoring hidden or display:none elements.
Why it matters:Tests may fail or behave unexpectedly if you assume hidden elements are matched, leading to false positives or negatives.
Quick: Does cy.contains() match text inside HTML attributes like title or alt? Commit to yes or no.
Common Belief:cy.contains() matches text anywhere in the element, including attributes like title or alt.
Tap to reveal reality
Reality:cy.contains() matches only visible text content, not text inside attributes.
Why it matters:Relying on attribute text matching causes tests to fail because the text is not visible and thus not matched.
Quick: If multiple elements contain the same text, does cy.contains() return all of them? Commit to yes or no.
Common Belief:cy.contains() returns all elements containing the text so you can interact with any of them.
Tap to reveal reality
Reality:cy.contains() returns only the first matching element found in the DOM order.
Why it matters:Assuming multiple matches are returned can cause tests to interact with the wrong element or miss others.
Quick: Can cy.contains() find text inside shadow DOM by default? Commit to yes or no.
Common Belief:cy.contains() can find text inside shadow DOM elements just like normal DOM elements.
Tap to reveal reality
Reality:cy.contains() cannot access shadow DOM elements without special commands or plugins.
Why it matters:Tests may fail silently or miss elements inside shadow DOM if this limitation is not understood.
Expert Zone
1
cy.contains() uses Cypress's retry-ability, so it waits for the text to appear before failing, which helps with dynamic content but can mask timing issues if overused.
2
When combining cy.contains() with selectors, the selector limits the search scope, but the text match still searches all descendants, which can lead to unexpected matches if the selector is too broad.
3
Using regular expressions with cy.contains() allows powerful matching but can cause performance issues or false positives if the regex is too general or complex.
When NOT to use
Avoid cy.contains() when you need to select elements by attributes, IDs, or classes for precise targeting, especially if text can change or be localized. Use cy.get() with stable selectors instead. Also, avoid cy.contains() for elements inside shadow DOM unless you use shadow DOM support commands.
Production Patterns
In real-world tests, cy.contains() is often combined with scoped selectors to reduce flakiness, like cy.get('.form').contains('Submit').click(). It is also used with regular expressions for localization testing, matching text in different languages. Teams use it to write readable tests that describe user actions by text rather than technical selectors.
Connections
User Interface Accessibility
Builds-on
Understanding how cy.contains() finds visible text helps testers ensure that UI elements are accessible and properly labeled for screen readers and keyboard navigation.
Regular Expressions
Builds-on
Knowing how to use regular expressions with cy.contains() unlocks flexible and powerful text matching, improving test robustness across dynamic or localized content.
Natural Language Processing (NLP)
Analogous pattern
Both cy.contains() and NLP involve searching and matching text patterns within larger bodies of text, showing how pattern matching concepts apply across software testing and language understanding.
Common Pitfalls
#1Trying to find hidden elements with cy.contains() expecting it to work.
Wrong approach:cy.contains('Hidden Text').click()
Correct approach:cy.get('[data-test=hidden-element]').invoke('show').contains('Hidden Text').click()
Root cause:Misunderstanding that cy.contains() only matches visible elements, so hidden elements are ignored.
#2Using cy.contains() with exact string but text case differs causing no match.
Wrong approach:cy.contains('submit').click() // text is 'Submit'
Correct approach:cy.contains(/submit/i).click() // case-insensitive regex
Root cause:Not realizing cy.contains() is case sensitive by default.
#3Assuming cy.contains() returns all matches and trying to interact with multiple elements directly.
Wrong approach:cy.contains('Delete').click() // multiple 'Delete' buttons exist
Correct approach:cy.contains('Delete').eq(1).click() // clicks second 'Delete' button
Root cause:Believing cy.contains() returns all matches instead of only the first.
Key Takeaways
cy.contains() finds elements by visible text, making tests more user-focused and easier to write.
It matches partial text and is case sensitive by default, but you can use regular expressions for flexible matching.
Combining cy.contains() with CSS selectors narrows the search scope and improves test precision.
cy.contains() returns only the first matching element, so be careful when multiple elements share the same text.
It only finds visible elements and cannot access shadow DOM without special handling.