Dataset HTML attributes
You can store non-html information in the element data-*
attributes and access them via dataset
property.
Convert dataset to a plain object
📺 Watch this recipe explained at Confirm All HTML data- Attributes At Once By Using The dataset Property.
<article
id="electric-cars"
data-columns="3"
data-index-number="12314"
data-parent="cars"
>
All about electric cards
</article>
All data-*
properties are collected in a single object. Property names are camel-cased, all values are strings. We can confirm a single property
cy.get('article#electric-cars')
.should('have.prop', 'dataset')
.its('indexNumber')
.should('equal', '12314')
// equivalent to checking the "data-*" attribute
cy.get('article#electric-cars').should(
'have.attr',
'data-index-number',
'12314',
)
Let's try confirming all data-*
attributes. Directly comparing property with an object does not work
// 🚨 INCORRECT
// cannot compare dataset object with a plain object
cy.get('article#electric-cars').should('have.prop', 'dataset', {
columns: '3',
indexNumber: '12314',
parent: 'cars',
})
Instead, yield the dataset
value and convert into a plain object first before using deep.equal
assertion.
cy.get('article#electric-cars')
// yields "DOMStringMap" object
.should('have.prop', 'dataset')
// which we can convert into an object
.then(JSON.stringify)
.then(JSON.parse)
// and compare as plain object
.should('deep.equal', {
columns: '3',
indexNumber: '12314',
parent: 'cars',
})
Tip: you can use cy-spok to confirm object properties.
Query commands
Converting a browser object like DOMStringMap
to a plain object using JSON.parse(JSON.stringify(x))
is pretty common. If we use cy.then(JSON.stringify).then(JSON.parse)
commands we are breaking retries because cy.then
is not a query command. I suggest using my plugin cypress-map and its query commands to convert DOMStringMap
to a plain JavaScript object.
In the example below, one of the data attributes is set after a delay.
<article
id="electric-cars"
data-columns="3"
data-index-number="loading..."
data-parent="cars"
>
All about electric cards
</article>
<script>
// force the tests to retry by adding "data-index-number" attribute
// after some delay
setTimeout(() => {
document
.getElementById('electric-cars')
.setAttribute('data-index-number', '12314')
}, 1500)
</script>
We can keep querying the DOM and converting to a plain object until the assertion passes. Query commands cy.toPlainObject
, cy.map
, and cy.print
are from cypress-map plugin.
cy.get('article')
.should('have.prop', 'dataset')
// "cy.toPlainObject" comes from cypress-map
.toPlainObject()
// convert the property "columns" to a number
// leaving the rest of the properties unchanged
.map({
columns: Number,
})
.print('dataset %o')
.should('deep.equal', {
columns: 3,
indexNumber: '12314',
parent: 'cars',
})
We can even map some property values to convert them before the assertion. For example, we can convert the number of columns from the string "3" to the number 3. Again, cy.map
from cypress-map
is our friend here:
List of elements
Let's confirm the data attributes from a list of elements. For example, let's confirm the data-product-id
for every list element.
<ul class="a-list" data-list-type="products">
<li data-product-id="001">Product A</li>
<li data-product-id="002">Product B</li>
<li data-product-id="003">Product C</li>
</ul>
cy.get('.a-list')
.should('have.attr', 'data-list-type', 'products')
.children('li')
// cy.map comes from cypress-map plugin
.map('dataset.productId')
.should('deep.equal', ['001', '002', '003'])