Find elements with subelements

Imagine we have a list of elements. Some elements can have <span class="label">...</span> inside. We want to filter the top level list to only have elements that have a label inside.

<ol id="list">
  <li>first</li>
  <li>second</li>
  <li>
    <p>third <span class="label">New!</span></p>
  </li>
  <li>
    <p>fourth <span class="label">New!</span></p>
  </li>
  <li>
    <p>fifth <span class="label">Advanced!</span></p>
  </li>
</ol>
<style>
  .label {
    background-color: #8b8bee;
    padding: 0.2rem 0.5rem;
    border-radius: 0.5rem;
  }
</style>
// select all list elements
cy.get('#list li')
  .should('have.length', 5)
  // filter the list by inspecting the children elements
  .then(($elements) => {
    // we can use the Lodash _.filter method to iterate
    // over the DOM elements and filter by a predicate
    const filtered = Cypress._.filter($elements, (el) => {
      return el.querySelector('.label')
    })

    return filtered
  })
  .should('have.length', 3)
  // confirm one of the list items
  .last()
  .should('include.text', 'fifth')
  .find('.label')
  .should('have.text', 'Advanced!')
// we can perform the opposite: only take the elements
// WITHOUT child element with class "badge" inside
cy.get('#list li')
  .then(($elements) =>
    // Lodash _.reject method is the opposite of _.filter
    Cypress._.reject($elements, (el) =>
      el.querySelector('.label'),
    ),
  )
  .should('have.length', 2)
  .first()
  .should('have.text', 'first')

What if we want to use Cypress commands inside the element filter callback? Like cy.contains? Let's find all LI elements that have labels with the text "Advanced" in them.

cy.get('#list li')
  .should('have.length', 5)
  .then(($elements) => {
    // all found LI elements
    const filtered = []

    Cypress._.each($elements, (li) => {
      // to run a Cypress command against the DOM element
      // wrap it inside Cypress chain
      cy.wrap(li)
        .contains('.label', 'Advanced')
        .should(($label) => {
          // there might NOT be such element
          if ($label.length) {
            // only if there is such label,
            // put the original "li" element
            // into the filtered list
            filtered.push(li)
          }
        })
    })

    // yield the filtered list of DOM elements
    // to the next Cypress assertion / command
    cy.wrap(filtered)
  })
  .should('have.length', 1) // yields [li] array
  .its(0) // yields the first LI DOM element from the array
  .should('include.text', 'fifth') // confirm we found the right element

We can also use the jQuery :has selector to find LI elements with .label elements inside.

cy.get('li:has(.label)')
  .should('have.length', 3)
  .last()
  .contains('Advanced')

We can even use the :has combined with :contains to find all LI elements with .label elements that have the text "New" in them.

cy.get('li:has(.label:contains("New"))').should('have.length', 2)

Watch the video "jQuery :has and :contains Selectors in Cypress Testsopen in new window".

Find labels OR warnings

Imagine that some list elements have plain labels and some have warnings. Can we find the list elements with labels OR warnings using :has selector?

<ol id="list">
  <li>first</li>
  <li>second</li>
  <li>
    <p>third <span class="badge label">New!</span></p>
  </li>
  <li>
    <p>fourth <span class="badge label">New!</span></p>
  </li>
  <li>
    <p>fifth <span class="badge warning">Deprecated</span></p>
  </li>
</ol>
<style>
  .badge {
    padding: 0.2rem 0.5rem;
    border-radius: 0.5rem;
  }
  .label {
    background-color: #8b8bee;
  }
  .warning {
    background-color: #f99650;
  }
</style>
// use jQuery CSS selector :has to find
// all list items with an element with class label or warning inside
cy.get('li:has(.label, .warning)').should('have.length', 3)
// alternative: split into two commands
// using the https://on.cypress.io/filter command
cy.get('li')
  .should('have.length', 5)
  .filter(':has(.label, .warning)')
  .should('have.length', 3)

Watch the video Find Elements With Specific Child Elements Using Cypress jQuery :has Selectoropen in new window.