Cypress: Get all elements containing a given string / regex

Viewed 1457

I'm trying to test a pagination bar with cypress.

I want to assert the number of buttons containing a number only in this bar, and ignore the other buttons (previous page, next page...)

The buttons are looking like this:

<button class="...">33</button>

I first tried this test:

cy.get('.pagination')
  .find('button')
  .contains(/\d+/)
  .should('have.length.gte', 2) 

But this gave me a warning about the fact that contains will only return one element, making the "length" test useless.

I also tried many combinations based on filter, the ":contains" jquery keyword, but none worked:

.filter(`:contains('/\d+\')`) 
// >> finds nothing

.filter((elt) => { return elt.contains(rx) })  
// >> throws 'elt.contains is not a function'

.filter((elt) => { return rx.test(elt.text()) }) 
// >> throws 'elt.text is not a function'

.filter(() => { return rx.test(Cypress.$(this).text()) }) 
// filter everything and return nothing, even the buttons containing the text '1'
4 Answers

.filter() with a callback has parameters (index, elt) => {} which means you can use it like this

cy.get('.pagination')
  .find('button')
  .filter((index, elt) => { return elt.innerText.match(/\d+/) }) 
  .should('have.length.gte', 2)

nextAll() might work in this situation:

cy
  .get('.pagination')
  .find('button')
  .contains(/\d+/)
  .nextAll()
  .should('have.length.gte', 2);

Another solution might be to distinguish the pagination buttons by something else, like a class, or some html attribute that is unique to them.

You can use an loop through the elements and match the element text and then increment a count variable and then later validate it, something like:

var count =0
  cy.get('.pagination').find('button').each(($ele) => {
  if(/\d+/.test($ele.text()){
    count++
  }
})
expect(count).to.be.greaterThan(2)

You can do other things as well like:

  1. Assertions
cy.get('.pagination').find('button').each(($ele) => {
  if(/\d+/.test($ele.text()){
    expect(+$ele.text().trim()).to.be.a('number')
  }
})
  1. Perform Click
cy.get('.pagination').find('button').each(($ele) => {
  if(/\d+/.test($ele.text()){
    cy.wrap($ele).click()
  }
})
  1. Validate Inner text
cy.get('.pagination').find('button').each(($ele) => {
  if(/\d+/.test($ele.text()){
    cy.wrap($ele).should('have.text', 'sometext')
  }
})

nextAll() fails if there's element wrapping the buttons, but you can count the wrappers.

cy.get('.pagination')
  .find('button')     // presume this is 'Prev' button
  .parent()
  .nextAll(':not(:contains(Next))')
  .should('have.length.gte', 2) 

or .nextUntil()

cy.get('.pagination')
  .find('button')     // presume this is 'Prev' button
  .parent()
  .nextUntil(':contains(Next)')
  .should('have.length.gte', 2) 

or .children()

cy.get('.pagination')
  .children(':not(:contains(Prev)):not(:contains(Next))')
  .should('have.length.gte', 2) 

Overall, .filter() is better as it does not assume the HTML structure.

Related