Querying
Examples of querying for DOM elements in Cypress, for a full reference of commands, go to docs.cypress.io and read Selecting Elements: Best Practices Guide. All Cypress querying commands automatically retry until the elements are found, see the retry-ability examples. Cypress supports both CSS and jQuery selectors.
// checks the page until it finds an element with class "title"
cy.get('.title')
// all querying commands have a built-in existence assertion
// the above command is equivalent to:
cy.get('.title').should('exist')
cy.get()
To query for the button, use the cy.get()
command.
<div id="querying-example">
<div class="well">
<button id="query-btn" class="query-btn btn btn-primary">
Button
</button>
</div>
</div>
// selects the button using ID
cy.get('#query-btn').should('contain', 'Button')
// selects the button using class
cy.get('.query-btn').should('contain', 'Button')
// Use CSS selectors just like jQuery
cy.get('#querying-example .well>button:first').should(
'contain',
'Button',
)
Number of items
You can attach an assertion to confirm the number of elements.
<section>
<h4>Example</h4>
<h5>Querying elements</h5>
<p>This page has heading elements</p>
<h6>cy.get</h6>
</section>
// find all H4 + H5 + H6 elements
// and confirm the minimum number of elements
// using the "greater" assertion
cy.get('h4,h5,h6').should('have.length.gt', 1)
Tip: use the .should('have.length', N)
assertion to confirm the exact number of elements. For example, see Assertions page.
jQuery selectors
Cypress querying commands use jQuery selectors that go beyond the standard CSS selectors. Here are a couple of examples.
:checkbox selector
The jQuery :checkbox
selector is equivalent to [type=checkbox]
attribute CSS selector. The jQuery docs advise to use at least the element type like input:checkbox
to avoid the default *:checkbox
wildcard.
<div id="checkbox-example">
<input type="checkbox" id="typeA" checked />
</div>
cy.get('#checkbox-example input:checkbox')
.should('be.checked')
.and('have.id', 'typeA')
.then(($checkbox) => {
// the jQuery :checkbox selector returns the same elements
// as the [type=checkbox] attribute selector
cy.get('#checkbox-example input[type=checkbox]').then(
($el) => {
// let's confirm it
expect($checkbox[0], 'same DOM element').to.equal($el[0])
},
)
})
Disabled elements
<div id="some-buttons">
<button id="one">One</button>
<button id="two" disabled>Two</button>
<button id="three" disabled>Three</button>
</div>
// get all disabled buttons
cy.get('#some-buttons button:disabled').should('have.length', 2)
// use combination of jQuery :not and :disabled selectors
cy.get('#some-buttons button:not(:disabled)')
.should('have.length', 1)
.and('have.text', 'One')
Checked elements
<div id="checks">
<div>One <input type="checkbox" id="one" /></div>
<div>Two <input type="checkbox" id="two" checked /></div>
<div>Three <input type="checkbox" id="three" /></div>
</div>
// get all checked boxes
cy.get('#checks input:checked')
.should('have.length', 1)
.and('have.id', 'two')
// get all unchecked boxes through jQuery :not and :checked selectors
cy.get('#checks input:not(:checked)').should('have.length', 2)
:has selector
We can find elements that contain some other element using the :has
selector. Let's find all paragraphs with bold text inside.
<div id="main-bold">
<p>First paragraph</p>
<p>Second <b>paragraph</b></p>
<p>Third paragraph</p>
<p>Fourth <b>paragraph</b></p>
</div>
cy.get('#main-bold p:has(b)').should('have.length', 2)
For another use of :has
selector, see the recipes Find and Click The Accordion With A Button and List item with text tag.
:has with :not selector
We can also combine :has
with :not
selectors to find all paragraphs without <b>
elements.
<div id="main-bold">
<p>First paragraph</p>
<p>Second <b>paragraph</b></p>
<p>Third paragraph</p>
<p>Fourth <b>paragraph</b></p>
</div>
cy.get('#main-bold p:not(:has(b))')
.should('have.length', 2)
.each(($p) => {
// should be the first or the first paragraphs
expect($p.text()).to.match(/^(First|Third)/)
})
Find text with :contains selector
cy.get
uses jQuery selectors, thus you can immediately use them to find elements by text (or without given text). Use :contains(text)
to find multiple elements with the given text string and use :not(:contains(text))
to get elements without the text.
<table id="text-example">
<tbody>
<tr>
<td>Same</td>
<td>Same</td>
<td>Different</td>
<td>Same</td>
</tr>
</tbody>
</table>
cy.get('table#text-example').within(() => {
// selects all table cells with text "Same"
cy.get('td:contains("Same")').should('have.length', 3)
// if the text does not have white spaces, no need to quote it
cy.get('td:contains(Same)').should('have.length', 3)
// you can find elements NOT having the given text
cy.get('td:not(:contains(Same))')
.should('have.length', 1)
.and('have.text', 'Different')
})
jQuery :contains vs cy.contains
Cypress command cy.contains
returns the first element with the matching text or regular expression. The command cy.get(':contains("text...")')
returns multiple elements with the text.
<div id="uploads">
<button id="up1">Select File</button>
<button id="up2">Select File</button>
<button id="up3">Select File</button>
<button id="up4">Select File</button>
</div>
With cy.contains
we get the first matching button
cy.contains('#uploads button', 'Select File')
.should('have.length', 1)
.and('have.id', 'up1')
With cy.get
+ jQuery :contains(text)
we can get all the buttons.
cy.get('#uploads button:contains("Select File")')
.should('have.length', 4)
.last()
.should('have.id', 'up4')
When you yield a list of elements, it is simple to get a particular one by its zero-based index with cy.eq command.
cy.get('#uploads button:contains("Select File")')
.should('have.length', 4)
.eq(1)
// finds the 2nd button
.should('have.id', 'up2')
Note: cy.contains
supports regular expressions, while jQuery :contains(text)
does not.
Combine :has and :contains selectors
📺 Watch this example in the video A Cypress Example With Disabled Button And Has Text jQuery Selectors.
<div>
<label>My button</label>
<button disabled>Click</button>
</div>
Let's get the button by finding the DIV
element that has a LABEL
element inside with the text "My button".
// the DIV has LABEL with the text "My button"
// and then get the child "BUTTON" element
cy.get('div:has( label:contains("My button") ) button')
// and confirm we found the right button element
.should('have.text', 'Click')
.and('be.disabled')
Multiple :has clauses
Let's pretend we want to find all DIV
elements having a LABEL
and BUTTON
elements inside.
📺 Watch this example explained in the video Multiple :has Selector Clauses.
<div>Does not have children</div>
<div>Has a label <label>Champ</label></div>
<div>
<label>My button</label>
<button disabled>Click</button>
</div>
<div>
<label>My other button</label>
<button disabled>Press</button>
</div>
<div>
<button>Finish</button>
</div>
// find all DIV elements with a LABEL element inside
cy.get('div:has(label)').should('have.length', 3)
// find all DIV elements with a LABEL and a BUTTON inside
cy.get('div:has(label):has(button)').should('have.length', 2)
// find all DIV elements with a LABEL or a BUTTON inside
cy.get('div:has(label, button)')
.should('have.length', 4)
// confirm the last element in the found list
.last()
.contains('button', 'Finish')
:first and :last
<div id="labels">
<div class="label">34</div>
<div class="label">loading...</div>
</div>
Let's check if the first label has the text "34"
cy.contains('#labels .label', '34')
Equivalent cy.get
command can use the :first
jQuery selector
cy.get('#labels .label:first').should('have.text', '34')
You can get the second or other elements by index (zero-based) using the jQuery :eq
selector. For example, to confirm part of the second element's text:
cy.get('#labels .label:eq(1)').should('include.text', 'load')
:last
<div id="student-names">
<div class="label">Joe</div>
<div class="label">Anna</div>
</div>
We can check the last element
cy.get('#student-names .label:last').should('have.text', 'Anna')
Wildcard selector
You can grab all elements using the wildcard *
selector
<ul id="the-names">
<li class="label">Joe</li>
<li class="label">Anna</li>
</ul>
Let's confirm all HTML elements by checking their nodeName
properties
cy.get('*')
.should('have.length', 3)
// cy.map comes from the cypress-map plugin
// https://github.com/bahmutov/cypress-map
.map('nodeName')
.should('deep.equal', ['UL', 'LI', 'LI'])
Escaping special characters
If the element's selector has special characters like .
or :
escape the using \\
character
<div id="user:1234" class="admin.user">Joe</div>
cy.get('#user\\:1234').should('have.text', 'Joe')
cy.get('.admin\\.user')
// no need to escape the non-selector text
.should('have.id', 'user:1234')
See also the recipe Escape Selector.
Find elements without a given class
In the HTML below all links have the class "help", but some links have the class "external". We want to find all links having the class "help", but without the class "external".
<a href="article1.html" class="help external">Article 1</a>
<a href="article2.html" class="help">Article 2</a>
<a href="article3.html" class="help external">Article 3</a>
<a href="index.html" class="help">index</a>
cy.get('a.help:not(.external)')
.should('have.length', 2)
// confirm the two found elements
.then(($el) => Cypress._.map($el, 'innerText'))
.should('deep.equal', ['Article 2', 'index'])
Find elements without an attribute
Let's find all anchor links without HREF
attribute
Some of the anchor links below are missing an href
attribute. Let's find them
<div id="links">
<a href="article1.html">Article 1</a>
<a href="article2.html">Article 2</a>
<a>Article 3</a>
<a>Article 4</a>
<a href="index.html">index</a>
</div>
// get all "A" elements
// without the attribute "href"
cy.get('#links a:not([href])').should('have.length', 2)
See also recipe Find All Buttons Without data-cy
attribute
Find elements with an attribute having a different value
Let's find all anchor links without HREF
attribute
Let's find all anchor links that have the attribute data-level
other than primary
or do not have this attribute
<div id="help-links">
<a href="article1.html" data-level="primary">Article 1</a>
<a href="article2.html" data-level="secondary">Article 2</a>
<a data-level="primary">Article 3</a>
<a>Article 4</a>
<a href="index.html" data-level="index">index</a>
</div>
// get all "A" elements where the attribute "data-level"
// either does not exist or has a value other than "primary"
cy.get('#help-links a:not([data-level=primary])').should(
'have.length',
3,
)
Find elements without two given classes
Imagine in a calendar date picker we have previous month, this month, and the next month. We want to select the current month's first day. For simplicity, I will only include three elements.
<div class="datepicker">
<!-- 5th of the last month -->
<div class="date lastMonth">5</div>
<div class="date" data-note="current">5</div>
<div class="date">15</div>
<div class="date">25</div>
<div class="date nextMonth">5</div>
</div>
<style>
.lastMonth:after {
content: ' last month';
}
.nextMonth:after {
content: ' next month';
}
</style>
We only want to select the "5" day of the current month, and do not include the other days with the digit "5" like "15" and "25".
cy.get('.date:not(.lastMonth, .nextMonth)')
// filter by text to find exactly "5"
.contains(/^5$/)
// confirm the got the correct element
.should('have.attr', 'data-note', 'current')
Using attribute selector
You can grab all elements that have an attribute present. For example, to find all <LI>
rows with the attribute "line" present:
<ul id="row-attributes">
<li>No line</li>
<li>No line</li>
<!-- this attribute "line" has no value at all -->
<li line>line</li>
<li line="up">line</li>
<li line="down">line</li>
<li>No line</li>
</ul>
cy.get('#row-attributes li[line]').should('have.length', 3)
You can grab elements with a given attribute. For example, let's make sure there is only a single <a>
element pointing at "index.html":
<div id="specific-href">
<a href="article1.html">Article 1</a>
<a href="article2.html">Article 2</a>
<a href="article3.html">Article 3</a>
<a href="index.html">index</a>
</div>
cy.get('#specific-href a[href="index.html"]')
.should('have.length', 1)
.and('have.text', 'index')
If you want to combine multiple attributes, see the recipe # Get By Attributes.
Attribute from a variable
<div id="attribute-value">
<div data-product-id="123-04-5678">Chair</div>
</div>
const productId = '123-04-5678'
cy.get('#attribute-value')
// use the "productId" variable value
// to form the full selector string
// via JavaScript template string
.find(`[data-product-id="${productId}"]`)
.should('have.text', 'Chair')
Alternative: concatenate the variable into a string using JavaScript +
operator
cy.get(
'#attribute-value [data-product-id="' + productId + '"]',
).should('have.text', 'Chair')
Get input elements with the given value
See the recipe Get input elements with the given value.
Escape the attribute
Sometimes an attribute can have a special character like .
or :
in it. Please escape the attribute using the \\
character.
<div id="escape-attribute" attr.aria-label="Attribute example">
Example
</div>
cy.get('[attr\\.aria-label="Attribute example"]')
.should('have.id', 'escape-attribute')
// ignore the newline characters by using the assertion "include.text"
// rather than the assertion "have.text"
.and('include.text', 'Example')
Attribute prefix
Let's get the element with ID starting with "local-example" prefix. The id
is just an attribute, so we can use id^=value
"id starts with the value" CSS selector:
<ul>
<li id="local-example-123">first</li>
<li id="remote-example-456">second</li>
</ul>
cy.get('[id^=local-example]').should('have.text', 'first')
Attribute suffix
Let's get the element with ID ending with "example-AF9" string. Again, we treat id
field as a plain attribute and use the CSS selector id$=value
"id ends with the value".
<ul>
<li id="this-example-ZFX">first</li>
<li id="that-example-AF9">second</li>
</ul>
cy.get('[id$=example-AF9]').should('have.text', 'second')
Attribute contains text
Let's find an anchor element with HREF attribute that includes the text "help"
<a href="/some/link.html">Link 1</a>
<a href="/another/link">Link 2</a>
<a href="/link/to/help/article.html">Link 3</a>
// quotes around the text without spaces are optional
cy.get('a[href*="help"]')
.should('have.length', 1)
.and('have.text', 'Link 3')
Having attribute disabled
Let's find all buttons with the attribute "disabled" present. While we are at it, let's find all the elements without such attribute.
<div id="few-buttons">
<button>First</button>
<button disabled>Second</button>
<button disabled>Third</button>
<button>Fourth</button>
</div>
// finds both button that have the attribute "disabled"
cy.get('#few-buttons button[disabled]')
.should('have.length', 2)
.first()
.should('have.text', 'Second')
// finds the two buttons without the attribute "disabled"
cy.get('#few-buttons button:not([disabled])')
.should('have.length', 2)
.last()
.should('have.text', 'Fourth')
Combining attribute selectors
Let's get the element with ID that starts with "my-" prefix and ending with "-yours" suffix
<ul id="combine-attributes">
<li id="my-first-123">first</li>
<li id="my-second-yours">second</li>
</ul>
cy.get('#combine-attributes').within(() => {
cy.get('[id^=my-][id$=-yours]').should('have.text', 'second')
})
Using data attribute
To find elements by data attribute, query using the attribute selector.
<div data-test-id="test-example" class="example">
Div with <code>data-test-id</code>
</div>
cy.get('[data-test-id="test-example"]').should(
'have.class',
'example',
)
cy.get()
yields a jQuery object, you can get its attribute by invoking the .attr()
method.
// find the element, confirm its attribute
cy.get('[data-test-id="test-example"]')
.invoke('attr', 'data-test-id')
.should('equal', 'test-example')
// or you can get an element's CSS property
cy.get('[data-test-id="test-example"]')
.invoke('css', 'position')
.should('equal', 'static')
Alternatively, chain assertions directly to the cy.get()
call. See assertions documentation.
cy.get('[data-test-id="test-example"]')
.should('have.attr', 'data-test-id', 'test-example')
.and('have.css', 'position', 'static')
Using partial data attribute
<div id="partial-data">
<div data-test-id="fruit one">Apples</div>
<div data-test-id="fruit two">Oranges</div>
<div data-test-id="fruit one">Grapes</div>
<div data-test-id="not a fruit">Potato</div>
</div>
Let's find all items with "data-test-id" that includes the text "fruit" anywhere in the string.
cy.get('#partial-data [data-test-id*=fruit]').should(
'have.length',
4,
)
Let's find all items that start with "fruit" in that attribute.
cy.get('#partial-data [data-test-id^=fruit]').should(
'have.length',
3,
)
Let's find the items that end the data attribute with the string "a fruit"
cy.get('#partial-data [data-test-id$="a fruit"]')
.should('have.length', 1)
.and('have.text', 'Potato')
Let's find the elements with text "one" anywhere in their text
cy.get('#partial-data [data-test-id*=one]')
.should('have.length', 2)
.first()
.should('have.text', 'Apples')
AND selector
Let's find all P
and LI
elements. You can combine multiple selectors using comma operator.
<div id="and-selector-example">
<ul>
<li id="first">first</li>
<li id="second">second</li>
</ul>
<p>Another line</p>
</div>
// find all P elements inside the element with id "and-selector-example"
// and all LI elements inside the element with id "and-selector-example"
cy.get(
'#and-selector-example p, #and-selector-example li',
).should('have.length', 3)
// alternative: first find the element with id "and-selector-example"
// then find P and LI elements
cy.get('#and-selector-example').within(() => {
cy.get('p, li').should('have.length', 3)
})
Table column
You can get the table column using CSS selectors
<style>
table td {
border: 3px solid black;
padding: 3px 5px;
}
#sort-by-date {
margin: 10px 0px;
}
</style>
<table id="people">
<thead>
<tr>
<td>Name</td>
<td>Age</td>
<td>Date (YYYY-MM-DD)</td>
</tr>
</thead>
<tbody id="people-data">
<tr>
<td>Dave</td>
<td>20</td>
<td>2023-12-23</td>
</tr>
<tr>
<td>Cary</td>
<td>30</td>
<td>2024-01-24</td>
</tr>
<tr>
<td>Joe</td>
<td>28</td>
<td>2022-02-25</td>
</tr>
<tr>
<td>Anna</td>
<td>22</td>
<td>2027-03-26</td>
</tr>
</tbody>
</table>
// let's get the first column
cy.get('table#people tbody td:nth-child(1)').should(($cells) => {
expect($cells[0]).to.have.text('Dave')
expect($cells[1]).to.have.text('Cary')
expect($cells[2]).to.have.text('Joe')
expect($cells[3]).to.have.text('Anna')
})
// let's get the second column
cy.get('table#people tbody td:nth-child(2)').should(($cells) => {
expect($cells[0]).to.have.text('20')
expect($cells[1]).to.have.text('30')
expect($cells[2]).to.have.text('28')
expect($cells[3]).to.have.text('22')
})
You can even map each element to a number before confirming the entire array.
// get the second column of cells
cy.get('table#people tbody td:nth-child(2)').should(($cells) => {
const values = Cypress._.map($cells, 'innerText').map(Number)
expect(values).to.deep.equal([20, 30, 28, 22])
})
Tip: you can extract multiple values and create assertions using cypress-should-really or cypress-map helpers.
With the given computed style
See the recipe Computed style.
Index of the found element
Imagine we want to find the list item with the class "active", and then see what is the index of the element among its siblings.
You can watch this example in the video Get The Index Of An Element Using jQuery Method.
<ol id="carousel">
<li>apples</li>
<li>grapes</li>
<li class="active">kiwi</li>
</ol>
li.active {
font-weight: bold;
}
cy.get('#carousel li.active')
// call jQuery index() method
// to yield the index of the active LI item
.invoke('index')
.should('equal', 2)
// if you want to work with the index,
// yield it to the next command
.then((index) => {
cy.log(`index is **${index}**`)
})
Let's find the LI
element by text and confirm its index:
cy.contains('#carousel li', 'kiwi')
.should('have.class', 'active')
.invoke('index')
.should('equal', 2)
Case-insensitive attribute selectors
See the Case-insensitive query recipe
cy.contains()
We can find elements by their content using cy.contains()
<div id="querying">
<ul class="query-list">
<li class="first">apples</li>
<li class="second">oranges</li>
<li class="third">bananas</li>
<li class="fourth">more apples</li>
</ul>
<div class="query-button">
<button class="btn btn-default">
<span>Save Form</span>
</button>
</div>
</div>
// finds the first element with the given text
cy.get('.query-list')
.contains('apples')
.should('have.class', 'first')
// ignore text when matching
cy.get('.query-list')
.contains('APPLE', { matchCase: false })
.should('have.class', 'first')
.and('have.text', 'apples')
cy.get('.query-list')
.contains('bananas')
.should('have.class', 'third')
// we can pass a regexp to `.contains()`
cy.get('.query-list')
.contains(/^b\w+/)
.should('have.class', 'third')
// passing a selector to contains will
// yield the selector containing the text
cy.get('div#querying')
.contains('ul', 'oranges')
.should('have.class', 'query-list')
cy.get('.query-button')
.contains('Save Form')
.should('have.class', 'btn')
See also: Contains text in a list, Debug cy.get and cy.contains commands, cy.contains and regular expressions.
cy.contains with selector and text
You can give the element selector to match. The text can be anywhere in the element or its children.
<div id="contains-example">
<div
data-cy="parent"
style="font-weight: heavy; text-decoration: underline"
>
<span>Some text</span>
</div>
</div>
cy.get('#contains-example').within(() => {
// finds the immediate element
cy.contains('Some text').should(
'have.prop',
'nodeName',
'SPAN',
)
// find the parent element with "Some text" somewhere inside
cy.contains('[data-cy=parent]', 'Some text')
.should('have.prop', 'nodeName', 'DIV') // we found the parent div
.and('have.css', 'text-decoration')
// the text-decoration style string includes color and line type
// we are only interested in the presence of the "underline" keyword
.should('include', 'underline')
})
cy.contains with regular expression
You can use a regular expression instead of text
<div id="user-name">Cypress User</div>
// ignore case
cy.contains(/cypress user/i)
// match text exactly
cy.contains(/^Cypress User$/)
Even if there are optional white space characters around the text, you can still use ^
and $
to require no other text in the element.
Note the whitespace around the word "Incredible"
<div class="nickname"> Incredible </div>
// find the nickname "Incredible" that can have whitespace around it
// but cannot have any other characters
cy.contains('.nickname', /^\s*Incredible\s*$/)
cy.contains with regular expression OR
Let's confirm that the title text is one of the three possible titles
<div class="my-title">Cypress Examples Guide</div>
<style>
.my-title {
font-size: x-large;
}
</style>
// we do not know the precise expected title
// we know it can be one of three possible titles
cy.contains(
'.my-title',
/^(Testing Examples|Cypress Examples Guide|Short Feature Tests)$/,
)
Perhaps a simpler test extracts the text from the element by invoking the jQuery text
method and checks if it equals one of the allowed strings. With Cypress v12 queries, the entire chain cy.get().invoke().should()
is retried.
// you can also get the text and confirm it
cy.get('.my-title')
.invoke('text')
.should('be.oneOf', [
'Testing Examples',
'Cypress Examples Guide',
'Short Feature Tests',
])
We can also use the .should('match', regular expression)
to check the text
// you can also get the text and confirm it
cy.get('.my-title')
.invoke('text')
.should('match', /example/i)
Tip: be careful with "OR" assertions, as they might relax the tests too much. For example, when adding a new item, the text might be "New item added". When editing an existing test the text might be "Item updated". It is tempting to match one of two variants:
.should('be.oneOf', ['New item added', 'Item updated'])
What if the application code accidentally shows "Item updated" when adding a new item? The test will still pass just fine. This is why I would argue that the test should know exactly what to expect. For example, the test code might have a variable "updated" to control which test it is:
function checkText(updated) {
if (updated) {
cy.contains('.my-title', 'Item updated')
} else {
cy.contains('.my-title', 'New item added')
}
}
The test is much stricter and simpler to read.
cy.contains with regular expression AND
Let's find a paragraph with words "hello", "world", and "more" in it.
<section id="paragraphs">
<p class="first">Some text here</p>
<p class="second">
Simon says "hello" and then he says "world" and that's it.
Nothing more.
</p>
<p class="third">I need more examples.</p>
</section>
// look for a paragraph with the world "hello", "world", and "more"
// separated by some other characters.
cy.contains('#paragraphs p', /hello.+world.+more/)
// confirm we have found the right paragraph
.should('have.class', 'second')
cy.contains with duplicate white spaces
If the HTML element contains duplicate white spaces, using cy.contains
becomes trickier. The example below has a double space between the :
and b
characters.
<div id="spaces">LEGO: blocks</div>
If you inspect this element in the browser's console, you will see that the browser returns different strings for innerHTML
and innerText
properties - and the browser collapses multiple spaces into one.
> $0.innerText.length
> 12
> $0.innerHTML.length
> 13
If you are using the literal string for matching, the cy.contains
will fail, since it uses the innerText
property.
// FAILS, cannot find the element while having " " double space
// cy.contains('#spaces', 'LEGO: blocks')
// solution: find the element and assert its text yourself
cy.get('#spaces').should($el => {
expect($el).to.have.html('LEGO: blocks')
})
// you can also remove duplicate white spaces before calling cy.contains
// and for good measure trimp the text we are looking for
cy.contains('#spaces', 'LEGO: blocks'.replace(/\s+/g, ' ').trim())
cy.contains and children elements
The cy.contains
command can find the element even if the text is split across its children elements.
<ul id="sell-fruits">
<li>
<span class="name">Apples</span>
<span class="price">$4.00</span>
</li>
</ul>
cy.contains('#sell-fruits li', 'Apples $4.00')
Extract part of the text
Once you found an element with some text, you can extract a specific part using a regular expression with named groups, which are supported by the modern browsers.
<div id="my-name">My name is Gleb, what's yours?</div>
cy.contains('#my-name', 'My name is')
// let's extract the name
.invoke('text')
// match a group using a regex
.invoke('match', /name is (?<name>\w+),/)
// grab just the specific group "name"
.its('groups.name')
.should('equal', 'Gleb')
Text with double quotes
Double quotes in the text you are looking for present no problem for the cy.contains
command.
<div id="with-quotes">My name is "Cypress examples"</div>
cy.contains('div', 'name is "Cypress examples"')
.should('exist')
.scrollIntoView()
.should('have.id', 'with-quotes')
Escape text
When using the cy.contains
command, you need to escape the backslashes \
characters.
<div id="escape-text-example">
<div id="message">[INFO]: this is \\a message\\</div>
</div>
// notice how we need to escape the JavaScript string
// because it needs to have double back slashes
const msg = '[INFO]: this is \\\\a message\\\\'
cy.get('#escape-text-example')
.contains(msg)
.should('have.id', 'message')
cy.get('#escape-text-example')
// almost equivalent jQuery :contains(text) selector
// but unfortunately it breaks on back slashes
// https://api.jquery.com/contains-selector/
// .find(`:contains("${msg}")`)
// we need to escape each escaped backslash!
.find(`:contains("[INFO]: this is \\\\\\\\a message\\\\\\\\")`)
.should('have.id', 'message')
Imagine the HTML text has double white space. Then cy.contains
struggles.
<div data-testid="product-name">
Garmin Instinct 2 Solar, (GRAPHITE) (010-02627-10)
</div>
const selector = '[data-testid=product-name]'
const text =
'Garmin Instinct 2 Solar, (GRAPHITE) (010-02627-10)'
cy.get(selector).should('contain.text', text)
// the entire text does not match
cy.contains(selector, text).should('not.exist')
// but pieces of the long text can be found correctly
cy.contains(selector, 'Garmin Instinct 2 Solar')
cy.contains(selector, '(GRAPHITE)')
cy.contains(selector, '(010-02627-10)')
// it is the double spaces that pose a problem
cy.contains(selector, ') (').should('not.exist')
// if we remove the double space, it works
cy.contains(selector, ') (')
Suggestion: clean up the text before searching by replacing multiple spaces with a single " " character.
cy.contains(selector, text.replaceAll(/\s+/g, ' '))
.within
We can find elements within a specific DOM element .within()
<h6>Name input</h6>
<input
type="text"
id="inputName"
class="form-control"
placeholder="Name"
/>
<h6>Form</h6>
<form class="query-form">
<input
type="text"
id="inputEmail"
class="form-control"
placeholder="Email"
/>
<input
type="text"
id="inputPassword"
class="form-control"
placeholder="Password"
/>
</form>
// validate placeholder attributes
cy.get('.query-form').within(() => {
cy.get('input:first').should(
'have.attr',
'placeholder',
'Email',
)
cy.get('input:last').should(
'have.attr',
'placeholder',
'Password',
)
})
Yields the original element
The cy.within
yields the same DOM element it received as the parent.
<div id="within-yields">
The parent div
<div class="some-child">Child element</div>
</div>
cy.get('#within-yields')
.within(() => {
// we are trying to return something
// from the .within callback,
// but it won't have any effect
return cy
.contains('Child element')
.should('have.class', 'some-child')
})
.should('have.id', 'within-yields')
You can attempt to cy.wrap a different value - still the original parent element is going to be yielded.
<div id="wrap-inside-within">
The parent div
<div class="some-child">Child element</div>
</div>
cy.get('#wrap-inside-within')
.within(() => {
// returning cy.wrap(...) has no effect on the yielded value
// it will still be the original parent DOM element
return cy.wrap('a new value')
})
.should('have.id', 'wrap-inside-within')
.within
Temporarily escape You can temporarily escape the .within
scope by using cy.root + cy.closest commands.
<section id="escape-example">
<h6>Name input</h6>
<input
type="text"
id="inputName"
class="form-control"
placeholder="Name"
/>
<h6>Form</h6>
<form class="the-form">
<input
type="text"
id="inputEmail"
class="form-control"
placeholder="Email"
/>
<input
type="text"
id="inputPassword"
class="form-control"
placeholder="Password"
/>
</form>
</section>
cy.get('.the-form').within(() => {
// escape back find H6
cy.root()
.closest('#escape-example')
.contains('h6', 'Name input')
// escape and enter text into the input field
cy.root()
.closest('#escape-example')
.find('input#inputName')
.type('Batman')
})
Note: you need the cy.root()
command first because cy.closest
is a child command and cannot be used to start the new command chain.
Number of elements
Using .within
followed by cy.get
is convenient for finding multiple matching elements inside another element. For example, let's confirm that the given picture element has at least 2 source
elements and 1 img
child element.
<picture>
<source srcset="logo-768.png 768w, logo-768-1.5x.png 1.5x" />
<source srcset="logo-480.png, logo-480-2x.png 2x" />
<img src="logo-320.png" alt="logo" />
</picture>
cy.get('picture').within(() => {
// at least 2 source elements
cy.get('source').should('have.length.gt', 1)
// single img element
cy.get('img').should('have.length', 1)
})
Within works with multiple elements
The command cy.within
requires the parent subject to be a single element.
<ul id="fruits">
<li id="item-apples"><a href="/apples">Apples</a></li>
<li id="item-oranges"><a href="/oranges">Oranges</a></li>
</ul>
cy.get('#fruits li')
.should('have.length', 2) // there are 2 LI items
// 🚨 NOT GOING TO WORK
// "Your subject contained 2 elements"
.within(() => {
// Nope, not going to get here
})
cy.root()
We can find the root DOM element cy.root()
<ul class="query-ul">
<li>One</li>
<li>Two</li>
<li>Buckle my shoe</li>
</ul>
// By default, root is the document
cy.root().should('match', 'html')
cy.get('.query-ul').within(() => {
// In this within, the root is now the ul DOM element
cy.root().should('have.class', 'query-ul')
})
Check the recipe Root Element Attributes
Best Practices: Selecting elements
Prefer dedicated data-cy
or data-test
attributes to CSS class names and element IDs. See detailed discussion at Best Practices: Selecting elements
<div id="best-practices">
<button
id="main"
class="btn btn-large"
name="submission"
role="button"
data-cy="submit"
>
Submit
</button>
</div>
cy.get('#best-practices').within(() => {
// Worst - too generic, no context
cy.get('button').click()
// Bad. Coupled to styling. Highly subject to change.
cy.get('.btn.btn-large').click()
// Average. Coupled to the `name` attribute which has HTML semantics.
cy.get('[name=submission]').click()
// Better. But still coupled to styling or JS event listeners.
cy.get('#main').click()
// Slightly better. Uses an ID but also ensures the element
// has an ARIA role attribute
cy.get('#main[role=button]').click()
// Much better. But still coupled to text content that may change.
cy.contains('Submit').click()
// Best. Insulated from all changes.
cy.get('[data-cy=submit]').click()
})
cy.get vs .find
The cy.get command always starts its search from the document
element, or, if used inside .within
, from the cy.root element. The .find command starts the search from the current subject.
<div class="test-title">cy.get vs .find</div>
<section id="comparison">
<div class="feature">Both are querying commands</div>
</section>
cy.get('#comparison')
.get('div')
// finds the DIV .test-title outside the #parent
// and the DIV .feature inside
.should('have.class', 'test-title')
.and('have.class', 'feature')
cy.get('#comparison')
.find('div')
// the search is limited to the tree at #comparison element
.should('have.length', 1)
.and('have.class', 'feature')
Pseudo class selectors
See the Pseudo CSS selectors recipe.