# 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>
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>
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>
- 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>
- 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)
})