Contains text in a list

Let's take a list and check if it contains a given text string. We can write this test in several ways.

📺 Watch the video Find Text Item Without Flake Using cy.contains Commandopen in new window to see the explanation behind this recipe.

Query the items first

Confirm that the list includes the item with the text "three"

<ul id="list">
  <li>one</li>
  <li>two</li>
  <li>three</li>
  <li>four</li>
  <li>five</li>
</ul>
// uncomment the threshold to see the full array
// chai.config.truncateThreshold = 300
cy.get('#list')
  .find('li')
  .then((items) => {
    const list = Array.from(items, (item) => item.innerText)
    expect(list).to.include('three')
  })

Dynamic list

What if the list is dynamic? The added items will NOT be seen by the .then command unfortunately, since Cypress retries the last command only, see the retry-ability guideopen in new window.

<ul id="list">
  <li>one</li>
  <li>two</li>
</ul>
<script>
  setTimeout(function () {
    const list = document.getElementById('list')
    list.innerHTML += `
      <li>three</li>
      <li>four</li>
      <li>five</li>
    `
  }, 1000)
</script>
// 🔥 WILL NOT WORK
// the ".then(cb)" will never retry checking the page
// and won't see the added items
cy.get('#list')
  .find('li')
  .then((items) => {
    const list = Array.from(items, (item) => item.innerText)
    expect(list).to.include('three')
  })
// ✅ a single cy.contains command retries searching the entire page
// until the list item "three" is added and the test passes
cy.contains('#list li', 'three')

What if you want to check multiple items?

const items = ['three', 'four', 'one']
items.forEach((item) => {
  cy.contains('#list li', item)
})

Using cypress-map

If we are using cypress-mapopen in new window plugin, we have additional queries that make writing the test much simpler. Let's find the string "three" on the page.

<ul id="list">
  <li>one</li>
  <li>two</li>
</ul>
<script>
  setTimeout(function () {
    const list = document.getElementById('list')
    list.innerHTML += `
      <li>three</li>
      <li>four</li>
      <li>five</li>
    `
  }, 1000)
</script>

We can query the DOM, then map each element to its text contents using the query cy.map. This produces a "live" list of strings, which updates if the DOM changes.

// a chain of query commands
// which retries until the assertion passes
cy.get('#list li').map('innerText').should('include', 'three')