# Getting Text from List of Elements

Imagine we have HTML elements.

<div>
  <div class="matching">first</div>
  <div>second</div>
  <div class="matching">third</div>
  <div class="matching">fourth</div>
  <div>fifth</div>
</div>
first
second
third
fourth
fifth

We want to get the text values of elements with the class matching

cy.get('.matching')
  .should('have.length', 3)
  .then(($els) => {
    // we get a list of jQuery elements
    // let's convert the jQuery object into a plain array
    return (
      Cypress.$.makeArray($els)
        // and extract inner text from each
        .map((el) => el.innerText)
    )
  })
  .should('deep.equal', ['first', 'third', 'fourth'])

// let's use Lodash to get property "innerText"
// from every item in the array
cy.log('**using Lodash**')
cy.get('.matching')
  .should('have.length', 3)
  .then(($els) => {
    // jQuery => Array => get "innerText" from each
    return Cypress._.map(Cypress.$.makeArray($els), 'innerText')
  })
  .should('deep.equal', ['first', 'third', 'fourth'])

cy.log('**using Lodash to convert and map**')
cy.get('.matching')
  .should('have.length', 3)
  .then(($els) => {
    expect(Cypress.dom.isJquery($els), 'jQuery input').to.be.true
    // Lodash can iterate over jQuery object
    return Cypress._.map($els, 'innerText')
  })
  .should('be.an', 'array')
  .and('deep.equal', ['first', 'third', 'fourth'])

So the final advice to extract text from the list of found elements is to use the Lodash _.map method.

cy.get('.matching').then(($els) => Cypress._.map($els, 'innerText'))

# Array vs jQuery object

Note: we cannot use cy.get(...).then(Cypress.$.makeArray).then(els => ...) to convert from jQuery object first, because the result of the $.makeArray is an array of elements, and it gets immediately wrapped back into jQuery object after returning from .then

<div>
  <div class="matching">first</div>
  <div>second</div>
  <div class="matching">third</div>
  <div class="matching">fourth</div>
  <div>fifth</div>
</div>
first
second
third
fourth
fifth
cy.get('.matching')
  .then(($els) => {
    expect(Cypress.dom.isJquery($els), 'jQuery object').to.be.true
    const elements = Cypress.$.makeArray($els)
    expect(Cypress.dom.isJquery(elements), 'converted').to.be.false
    expect(elements, 'to array').to.be.an('array')
    // we are returning an array of DOM elements
    return elements
  })
  .then((x) => {
    // but get back a jQuery object again
    expect(Cypress.dom.isJquery(x), 'back to jQuery object').to.be
      .true
    expect(x, 'an not a array').to.not.be.an('array')
    expect(x.length, '3 elements').to.equal(3)
  })

# Confirm the number of items

Let's imagine the page shows the number of items in the list, and we want to confirm the displayed number is correct.

<div>There are <span id="items-count">4</span> items</div>
<ul id="items">
  <li>Apples</li>
  <li>Oranges</li>
  <li>Pears</li>
  <li>Grapes</li>
</div>
There are 4 items
  • Apples
  • Oranges
  • Pears
  • Grapes

Because the commands are asynchronous we cannot get the number "4" and immediately use it to assert the number of <LI> items. Instead we need to use .then callback.

cy.get('#items-count')
  .invoke('text')
  .then(parseInt)
  .then((n) => {
    cy.get('#items li').should('have.length', n)
  })

# with text matching

Sometimes the number of items is a part of the text and needs to be extracted first before parsing into a number. Notice the number "4" is hiding inside the brackets and does not have its own element to query.

<div id="items-intro">There are [ 4 ] items</div>
<ul id="my-items">
  <li>Apples</li>
  <li>Oranges</li>
  <li>Pears</li>
  <li>Grapes</li>
</div>
There are [ 4 ] items
  • Apples
  • Oranges
  • Pears
  • Grapes

We need to grab the entire text

cy.get('#items-intro')
  .invoke('text')
  .then((s) => {
    // by adding an assertion here we print
    // the text in the command log for simple debugging
    expect(s).to.be.a('string')
    const matches = /\[\s*(\d+)\s*\]/.exec(s)
    return matches[1]
  })
  .then(parseInt)
  // another assertion to log the parsed number in the command log
  .should('be.a', 'number')
  // we expect between 1 and 10 items
  .and('be.within', 1, 10)
  .then((n) => {
    cy.get('#my-items li').should('have.length', n)
  })