Find elements by class and text

Elements with just given class

This recipe answers the question #14281open in new window - how do I select an element having an exact class? We can use cy.getopen in new window or cy.containsopen in new window

<ul>
  <li class="day">first A</li>
  <li class="day old">first B</li>
  <li class="day disabled">first C</li>

  <li class="day">second A</li>
  <li class="day old">second B</li>
  <li class="day disabled">second C</li>

  <li class="day">third A</li>
  <li class="day old">third B</li>
  <li class="day disabled">third C</li>
</ul>

How do we select an element having class "day" with text "second" but NOT "day old" or "day disabled"?

// this selects ALL elements
cy.get('.day').should('have.length', 9)
// this selects only elements with exactly class "day"
// because we are using the attribute selector
cy.get('[class=day]')
  .should('have.length', 3)
  // now we can find the one element with text "second"
  .contains('second')
  .should('have.text', 'second A')
  // confirm it does not have other classes
  .should('have.class', 'day')
  .and('not.have.class', 'old')
  .and('not.have.class', 'disabled')

// similarly we can use cy.contains directly
cy.contains('[class=day]', 'second A')
  // we can confirm classes like above
  // or by taking the classList from the DOM element
  .then(($el) => $el[0].className)
  .should('equal', 'day')

// similarly finding "third B"
cy.contains('[class="day old"]', 'third B')
  .then(($el) => $el[0].className)
  .should('equal', 'day old')

Selecting by exact text

If you want to select using the exact text, use a regular expression

We want to select ".day" with the exact text "8" from the HTML below.

<ul>
  <li class="day">28</li>
  <li class="day old">28 B</li>
  <li class="day disabled">28 C</li>

  <li class="day">8</li>
  <li class="day old">8 B</li>
  <li class="day disabled">8 C</li>
</ul>
// using a string "8" finds the first element containing it
cy.contains('[class=day]', '8').should('have.text', '28')
// using regular expression finds the exact text match
cy.contains('[class=day]', /^8$/).should('have.text', '8')

Find multiple elements with text

The command cy.contains yields the first element. If you want to find multiple elements that contain the given text, use jQuery selector :containsopen in new window. Note that the selector text is case sensitive.

<ul>
  <li>apple</li>
  <li>pineapple</li>
  <li>grapes</li>
  <li>crab apple</li>
  <li>crab legs</li>
</ul>
// cy.contains finds the first element that contains the text "apple"
cy.contains('apple')
  .should('have.length', 1)
  .and('have.text', 'apple')
// using jQuery :contains selector we can find all elements with text "apple"
cy.get('li:contains(apple)')
  .should('have.length', 3)
  .first()
  .should('have.text', 'apple')
// quote the text with multiple words
cy.get('li:contains("crab apple")')
  .should('have.length', 1)
  .and('have.text', 'crab apple')

Note: use a selector before :contains otherwise, both UL and LI elements will be returned.

// without LI selector, the results will include the parent UL element
cy.get(':contains(apple)')
  .should('have.length', 4)
  .first()
  .should('have.prop', 'nodeName', 'UL')

We can also filter the elements to find LI elements with the text "crab"

cy.get('li').filter(':contains(crab)').should('have.length', 2)

Filter elements using cy.not

We can filter items using the cy.notopen in new window command.

<ul>
  <li class="initial active">Apples</li>
  <li class="initial current">Oranges</li>
  <li class="initial active">Grapes</li>
  <li class="initial current">Melons</li>
</ul>
cy.get('.initial')
  .not('.active')
  .should('have.length', 2)
  .first()
  .should('have.text', 'Oranges')

Complex filtering

Sometimes the filtering logic is too complex to be expressed by CSS3 selectors. For example, how to get all matching elements not inside a given <UL> list?

<div class="target">target</div>
<ul>
  <li>
    First
    <div>example</div>
  </li>
  <li>
    Second
    <div>example</div>
  </li>
</ul>
<div class="target">last target</div>

We need to find the elements, then use a callback function to check if each element has ul ancestor. Using .then callback is great for such tasks, and we can utilize the bundled Cypress.jQuery methods for checking the ancestors of the given element.

cy.get('div')
  // an assertion ensures we have elements
  // before starting filtering
  .should('have.length.gt', 1)
  .then(($els) => {
    const $filtered = $els.filter((k, el) => {
      // if the found element is inside <UL> element
      // then reject it by returning false
      const $ul = Cypress.$(el).closest('ul')
      return $ul.length === 0
    })

    return $filtered
  })
  // finds only the elements outside the <UL> element
  .should('have.length', 2)
  // check by confirming the class on each found element
  .and('have.class', 'target')

You can simplify the logic above using cy.filteropen in new window query command with callback function argument. Instead of cy.then + cy.filter, simply use the cy.filter(callback):

// filter elements using cy.filter
cy.get('div')
  .should('have.length.gt', 1)
  .filter((k, el) => {
    // if the found element is inside <UL> element
    // then reject it by returning false
    const $ul = Cypress.$(el).closest('ul')
    return $ul.length === 0
  })
  // finds only the elements outside the <UL> element
  .should('have.length', 2)
  // check by confirming the class on each found element
  .and('have.class', 'target')