# 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>

Example

Querying elements

This page has heading elements

cy.get
// 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>
One
Two
Three
// 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>

First paragraph

Second paragraph

Third paragraph

Fourth paragraph

cy.get('#main-bold p:has(b)').should('have.length', 2)

For another use of :has selector, see the recipe Find and Click The Accordion With A Button.

# :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>

First paragraph

Second paragraph

Third paragraph

Fourth paragraph

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 elements with the given text string and :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>
Same Same Different Same
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')
})

# 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>
Does not have children
Has a label
// 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>
34
loading...

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>
Joe
Anna

We can check the last element

cy.get('#student-names .label:last').should('have.text', 'Anna')

# 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>
Joe
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)

# 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>
5
5
15
25
5

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>
  • No line
  • No line
  • line
  • line
  • line
  • No line
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>
Chair
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>
Example
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

<ul>
  <li id="local-example-123">first</li>
  <li id="remote-example-456">second</li>
</ul>
  • first
  • second
cy.get('[id^=local-example]').should('have.text', 'first')

# Attribute suffix

Let's get the element with ID ending with "example-AF9" string

<ul>
  <li id="this-example-ZFX">first</li>
  <li id="that-example-AF9">second</li>
</ul>
  • first
  • second
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>
  • first
  • second
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>
Div with data-test-id
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>
Apples
Oranges
Grapes
Potato

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>
  • first
  • second

Another line

// 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>
Name Age Date (YYYY-MM-DD)
Dave 20 2023-12-23
Cary 30 2024-01-24
Joe 28 2022-02-25
Anna 22 2027-03-26
// 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

Read CSS case insensitive attribute selector.

🚨 Does not work, see #25304

<button class="btn-PRIMARY">Green</button>
<button class="btn-primary">Red</button>
cy.get('button[class=btn-primary i]').should('have.length', 2)

# 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>
  • apples
  • oranges
  • bananas
  • more apples
// 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')

# 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>
Some text
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>
Cypress User
// 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>
Incredible
// 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>
Cypress Examples Guide
// 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)$/,
)
// 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',
  ])

# 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>

Some text here

Simon says "hello" and then he says "world" and that's it. Nothing more.

I need more examples.

// 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>
LEGO: blocks

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>
  • Apples $4.00
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>
My name is Gleb, what's yours?
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>
My name is "Cypress examples"
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>
[INFO]: this is \\a message\\
// 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>
Garmin Instinct 2 Solar, (GRAPHITE) (010-02627-10)
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>
Name input
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>
The parent div
Child element
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>
The parent div
Child element
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')

# Temporarily escape .within

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>
Name input
Form
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>
logo
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>
  • One
  • Two
  • Buckle my shoe
// 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')
})

# 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 vs .find
Both are querying commands
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.