Assertions
Examples of asserting the state or behavior of your application in Cypress, for a full reference of commands, go to docs.cypress.io
Implicit Assertions
.should()
To make an assertion about the current subject, use the .should()
command.
<table class="table table-bordered assertion-table">
<thead>
<tr>
<th>#</th>
<th>Column heading</th>
<th>Column heading</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">1</th>
<td>Column content</td>
<td>Column content</td>
</tr>
<tr>
<th scope="row">2</th>
<td>Column content</td>
<td>Column content</td>
</tr>
<tr class="success">
<th scope="row">3</th>
<td>Column content</td>
<td>Column content</td>
</tr>
</tbody>
</table>
cy.get('.assertion-table')
.find('tbody tr:last')
.should('have.class', 'success')
.find('td')
.first()
// checking the text of the element in various ways
.should('have.text', 'Column content')
.should('contain', 'Column content')
.should('have.html', 'Column content')
// chai-jquery uses "is()" to check if element matches selector
.should('match', 'td')
// to match text content against a regular expression
// first need to invoke jQuery method text()
// and then match using regular expression
.invoke('text')
.should('match', /column content/i)
// a better way to check element's text content against a regular expression
// is to use "cy.contains"
// https://on.cypress.io/contains
cy.get('.assertion-table')
.find('tbody tr:last')
// finds first element with text content matching regular expression
.contains('td', /column content/i)
.should('be.visible')
Typically the assertions have 1, 2, or 3 arguments.
// 1 argument - the assertion name only like "be.visible"
cy.contains('th', '1').should('be.visible')
// 2 arguments - assertion plus its argument
// for example, the yielded element have the prop "scope" with any value
cy.contains('th', '1').should('have.prop', 'scope')
// 3 arguments - assertion, plus 2 arguments
// for example, the yielded element have the prop "scope" with value "row"
cy.contains('th', '1').should('have.prop', 'scope', 'row')
The number of arguments an assertion allows depends on the assertion itself.
For more examples of using the match
assertion against jQuery elements and text, see the recipe Match Assertion.
Note: find even more examples of matching element's text content in this FAQ answer.
.and()
To chain multiple assertions together, use the .and()
command.
<a
class="assertions-link active"
href="https://on.cypress.io"
target="_blank"
>Cypress Docs</a
>
// https://on.cypress.io/and
cy.get('.assertions-link')
.should('have.class', 'active')
.and('have.attr', 'href')
.and('include', 'cypress.io')
Note that all assertions attached to the same command must pass at the same time for the command to succeed.
Subject
The implicit assertions keep the original subject and pass it to the next command.
const employee = {
person: {
name: {
first: 'Joe',
last: 'Smith',
},
},
}
cy.wrap(employee)
.should('have.key', 'person')
.then((x) => {
// we are still working with the entire object
expect(x).to.equal(employee)
})
Except for several assertions that DO change the subject:
have.property
for objectshave.attr
with 1 argument for DOM elementshave.prop
with 1 argument for DOM elements
as the next tests demonstrate
have.property
assertion
const employee = {
person: {
name: {
first: 'Joe',
last: 'Smith',
},
},
}
cy.wrap(employee)
.should('have.property', 'person')
.then((x) => {
// the current subject has been changed to employee.person
expect(x).to.equal(employee.person)
expect(x).to.have.key('name')
})
// Tip: you can use another implicit assertion to check the yielded property
cy.wrap(employee) // full object
.should('have.property', 'person') // employee.person
.should('equal', employee.person) // still employee.person
.and('have.key', 'name') // still employee.person
// still employee.person because have.key does not change the subject
.should('equal', employee.person)
Multiple properties
If you want to check multiple properties at once we can use deep.include
assertion. Note we cannot use deep.equals
in this case, since the object has extra properties we are not interested in.
const person = {
firstName: 'Joe',
lastName: 'Smith',
age: 29,
}
cy.wrap(person).should('deep.include', {
firstName: 'Joe',
lastName: 'Smith',
})
have.prop
assertion
<input id="my-age" value="20" />
cy.get('#my-age')
.should('have.prop', 'value', '20')
// yields the original element
.and('have.attr', 'id', 'my-age')
// if we don't know the exact value, we can
// yield its value using the "have.prop" assertion
// and convert it to a number before checking
// if the value falls in the given range
cy.get('#my-age')
.should('have.prop', 'value')
// yields the value prop
.then(parseInt)
.should('be.within', 10, 30)
Note: the assertion have.prop <name>
yields the value of the prop "name". Assertion have.prop <name> <value>
yields the original element.
Input should have text value matching a regular expression
You can use the have.prop
assertion to grab the text value and yield it to the next assertion.
<input id="user-ssn" value="123-45-6789" />
// it looks best if we know the exact value to check
cy.get('#user-ssn').should('have.value', '123-45-6789')
If we don't know the exact value to expect, we can grab the value
property and check if it follows a regular expression.
cy.get('#user-ssn')
.should('have.prop', 'value')
// yields the string value
.should('match', /^\d\d\d-\d\d-\d\d\d\d$/)
have.attr
assertion
<div
data-cy="subject-example"
style="color: orange; background-color:green;"
>
Test div
</div>
cy.get('[data-cy=subject-example]')
.should('have.attr', 'style')
.then((x) => {
// x is the complete style attribute
const withoutWhiteSpace = x.replace(/\s/g, '')
expect(withoutWhiteSpace).to.equal(
'color:orange;background-color:green;',
)
})
// we can remove the whitespace by invoking the method
// on the yielded subject
cy.get('[data-cy=subject-example]') // jQuery element
.should('have.attr', 'style') // string attribute
.invoke('replace', /\s/g, '') // string without whitespace
.should('equal', 'color:orange;background-color:green;')
Escape special characters
Sometimes an attribute can have a special character like .
or :
in it. If you are just checking the attribute name, you do not need to escape them.
<div id="escape-attribute" attr.aria-label="Attribute example">
Example
</div>
// confirm the element has the attribute
cy.get('#escape-attribute').should(
'have.attr',
'attr.aria-label',
)
// confirm the element has the attribute and that attribute
// has the specific value
cy.get('#escape-attribute').should(
'have.attr',
'attr.aria-label',
'Attribute example',
)
have.attr
assertion chain
This recipe is available as a video Use Cypress Have.Attr Assertion.
<a id="my-link" href="/some/complex/link-123" title="home page"
>My link</a
>
If we want to just check presence of an attribute, we can use the "have.attr" assertion with the attribute name
cy.get('#my-link').should('have.attr', 'id')
// yields the value of the attribute "id"
Note: the "have.attr" assertion with one argument changes the subject yielded to the next command or assertion, unlike most assertions. When confirming the attributes it is useful if we want to confirm something about the attribute value. For example, if we want to check if the element has the attribute and the attribute has specific length, we could chain the assertions:
// the anchor title should be at least 5 characters
cy.get('#my-link')
.should('have.attr', 'title')
// yields the value of the attribute "title"
.should('be.a', 'string')
.its('length')
.should('be.greaterThan', 5)
If we know the expected attribute value, we can add it as the third argument to the should(...)
assertion. In that case, the original element is yielded.
cy.get('#my-link')
.should('have.attr', 'id', 'my-link')
// yields the original DOM element
.should('satisfy', Cypress.dom.isElement)
.and('have.attr', 'title', 'home page')
// yields the original DOM element again
.should('satisfy', Cypress.dom.isElement)
If we only know a part of the expected attribute, we can first assert the attribute is present, then use an assertion to match its value.
cy.get('#my-link')
.should('have.attr', 'href')
// yields the attribute value
// check if the href attribute includes given string
.and('include', 'link-')
// we can also use a regular expression
cy.get('#my-link')
.should('have.attr', 'href')
// yields the attribute value
.and('match', /\/link\-\d+/)
<div class="first second">Test div</div>
cy.get('.first')
.should('have.prop', 'class')
.then((x) => {
// x is the class prop
expect(x).to.equal('first second')
})
should be a NaN
cy.wrap(NaN).should('be.a.NaN')
cy.wrap(42).should('not.be.a.NaN')
Explicit Assertions
expect
To make a BDD assertion about a specified subject, use expect
.
expect(true, 'it is true').to.be.true
const o = { foo: 'bar' }
expect(o, 'object reference').to.equal(o)
// "deep.equal" assertion compares the properties inside the object
expect(o, 'deep equality').to.deep.equal({ foo: 'bar' })
// matching text using regular expression
expect('FooBar').to.match(/bar$/i)
// check if the variable is defined, is a string, and has characters
const orgId = '4AB001C'
expect(orgId, 'org id').to.be.a('string').and.not.be.empty
// check if the response status code is successful
const statusCode = 204
expect(statusCode, 'status code').to.be.within(200, 399)
// check if a value is one of the three allowed choices
const fruit = 'Grapes'
expect(fruit, 'the fruit').to.be.oneOf([
'Apples',
'Oranges',
'Grapes',
])
// generic predicate function check using the "satisfy" assertion
expect('Hello').to.satisfy(
(s) => typeof s === 'string' && s.length === 5,
)
// you can use built-in Lodash predicates
expect(window.NaN, 'NaN').to.satisfy(Cypress._.isNaN)
cy.wrap(2, 'Two').should('satisfy', Cypress._.isFinite)
string assertion
<table id="substrings">
<tbody>
<tr>
<td>Column 1</td>
</tr>
</tbody>
</table>
You can use the Chai assertion include
to check if one string contains another string.
expect('Hello, World')
.to.include('Hello')
// "contains" assertion is an alias to "include"
.and.to.contain('World')
There is also a Chai assertion string
that also checks if a string includes another string.
// chai assertion "string" checks the presence of substring
cy.get('#substrings tbody tr:first td:first')
.invoke('text')
.then((s) => {
// the string "s" contains the substring "Column"
expect(s).to.string('Column')
// the string "s" does not include the substring "Row"
expect(s).to.not.string('Row')
})
Compare two lists of elements
Let's compare the text content of two lists. We would like to assert that the second list is a subset of the first one. First, we need to get the text from each list, then compare them.
Tip: see recipe "Getting Text from List of Elements" to see how to iterate over the list of elements and get their text content.
<ol id="first">
<li>Apples</li>
<li>Oranges</li>
<li>Melons</li>
<li>Grapes</li>
</ol>
<ol id="second">
<li>Grapes</li>
<li>Oranges</li>
</ol>
const firstList = []
const secondList = []
// let's get the first list of strings
cy.get('#first li').each(($li) => {
firstList.push($li.text())
})
cy.get('#second li')
.each(($li) => {
secondList.push($li.text())
})
.then(() => {
// when this callback runs, both lists will be populated
expect(firstList).to.include.members(secondList)
})
Tip: check out more list comparison examples in Compare Two Lists recipe.
expect a NaN
expect(NaN, 'not a number').to.be.a.NaN
expect(42).not.to.be.a.NaN
assert
To make a TDD assertion about a specified subject, use assert
.
const person = {
name: 'Joe',
age: 20,
}
assert.isObject(person, 'value is object')
Should with callback function
You can write your own complicated checks using .should(cb)
function if included assertions are not enough. Pass a function to should()
with any number of explicit assertions within it. The callback function will be retried until it passes all your explicit assertions or times out.
<div class="assertions-p">
<p>Some text from first p</p>
<p>More text from second p</p>
<p>And even more text from third p</p>
</div>
// Pass a function to should that can have any number
// of explicit assertions within it.
// The ".should(cb)" function will be retried
// automatically until it passes all your explicit assertions or times out.
cy.get('.assertions-p')
.find('p')
.should(($p) => {
// https://on.cypress.io/$
// return an array of texts from all of the p's
// @ts-ignore TS6133 unused variable
const texts = $p.map((i, el) => Cypress.$(el).text())
// jquery map returns jquery object
// and .get() convert this to simple array
const paragraphs = texts.get()
// array should have length of 3
expect(paragraphs, 'has 3 paragraphs').to.have.length(3)
// use second argument to expect(...) to provide clear
// message with each assertion
expect(
paragraphs,
'has expected text in each paragraph',
).to.deep.eq([
'Some text from first p',
'More text from second p',
'And even more text from third p',
])
})
Partial class string match example
Assert that element's class includes heading-
.
<div class="docs-header">
<div class="main-abc123 heading-xyz987">Introduction</div>
</div>
cy.get('.docs-header')
.find('div')
// .should(cb) callback function will be retried
.should(($div) => {
expect($div).to.have.length(1)
const className = $div[0].className
expect(className).to.match(/heading-/)
})
// .then(cb) callback is not retried,
// it either passes or fails
.then(($div) => {
expect($div, 'text content').to.have.text('Introduction')
})
Throwing own errors
You can throw any error from the callback function. The callback will be retried, but the assertions will not be shown as nicely in the Command Log UI as Chai assertions.
<div class="docs-header-example">
<div class="heading-top">Top content</div>
</div>
cy.get('.docs-header-example')
.find('div')
.should(($div) => {
if ($div.length !== 1) {
// you can throw your own errors
throw new Error('Did not find 1 element')
}
const className = $div[0].className
if (!className.match(/heading-/)) {
throw new Error(
`Could not find class "heading-" in ${className}`,
)
}
})
Dynamic text example
We strongly recommend that your tests are deterministic. But sometimes you might need to match text between two elements, and you do not know what that text should be. Save the value from the first element, then compare it from a should(cb)
callback.
<div class="two-elements">
<div class="first">Foo Bar</div>
<div class="second">foo b a r</div>
</div>
/**
* Text from the first element.
* @type {string}
*/
let text
/**
* Normalizes passed text,
* useful before comparing text with spaces and different capitalization.
* @param {string} s Text to normalize
*/
const normalizeText = (s) => s.replace(/\s/g, '').toLowerCase()
cy.get('.two-elements')
.find('.first')
.then(($first) => {
// save text from the first element
text = normalizeText($first.text())
})
cy.get('.two-elements')
.find('.second')
.should(($div) => {
// we can massage text before comparing
const secondText = normalizeText($div.text())
expect(secondText, 'second text').to.equal(text)
})
Retrying should callback
Remember that Cypress only retries the very last command, if it allows retrying. If you need to perform additional steps before running an assertion, you can use .should(callbackFn)
to retry multiple operations.
<div class="random-number-example">
Random number: <span id="random-number">🎁</span>
</div>
<script>
const el = document.getElementById('random-number')
setTimeout(function () {
el.innerText = Math.floor(Math.random() * 10 + 1)
}, 1500)
</script>
cy.get('#random-number').should(($div) => {
const n = parseFloat($div.text())
expect(n).to.be.gte(1).and.be.lte(10)
})
Should vs Then
You can write assertions inside .should(callback)
or using the .then(callback)
functions. The .should(callback)
will retry if the assertions fail and the previous command can be retried. If an assertion inside .then(callback)
fails, then the test fails immediately. Thus I suggest using .then(cb)
if the previous command is never going to be retried like cy.request
or cy.wrap
// works, but is NOT recommended
// because it will retry the assertion even if the object never changes
// until the command timeout passes
cy.wrap({ name: 'Joe' }).should((o) => {
expect(o).to.have.property('name', 'Joe')
})
// recommended: using .then to immediately fail
// if the assertion fails
cy.wrap({ name: 'Joe' }).then((o) => {
expect(o).to.have.property('name', 'Joe')
})
Truthy assertion
The most "forgiving" assertion - as long as there is something truthy, be.ok
assertion will pass.
// explicit assertions
expect(42, 'a number').to.be.ok
expect({}, 'an object').to.be.ok
// implicit assertions
cy.wrap(console.log).should('be.ok')
cy.wrap([1, 2, 3]).should('be.ok')
cy.wrap('kids').should('be.ok')
Undefined, null, zero, empty strings, and false values are not ok.
cy.wrap(undefined).should('not.be.ok')
cy.wrap(null).should('not.be.ok')
cy.wrap(0).should('not.be.ok')
cy.wrap(false).should('not.be.ok')
cy.wrap('').should('not.be.ok')
Satisfy a predicate function
Chai assertion satisfy
checks the value against a predicate function that returns a boolean value.
<div id="my-greeting">Hello</div>
// my predicate functions
const isEven = (n) => n % 2 === 0
const hasHello = ($el) => $el.text() === 'Hello'
// explicit assertions
expect(42).to.satisfy(isEven)
// implicit assertions
cy.wrap(101).should('not.satisfy', isEven)
cy.get('#my-greeting')
.should('satisfy', Cypress.dom.isVisible)
.and('satisfy', hasHello)
You can add a string message to the assertion after the predicate function.
cy.wrap(102).should('satisfy', isEven, 'id is even')
You might notice that should('satisfy', callback)
is very similar to the powerful should(callback)
assertion. Thus I usually prefer just the should(callback)
. Only when the predicate is simple and has a good name, then I write should('satisfy', named callback function)
. I can also use should('not.satisfy', predicate)
to confirm the subject does not pass the predicate.
Existence
In most cases, you do not need to explicitly check if the element exists - if the cy.get
, cy.contains
, etc. command finds an element, it exists. If you want to check that the element with a specific selector or text does not exist, then attach the assertion.
<div class="animal">fox</div>
.animal {
text-transform: uppercase;
}
// the element text is still "fox",
// the CSS uppercase transformation
// does not change the cy.contains result
cy.contains('.animal', 'FOX').should('not.exist')
// find the element correctly
// and use the built-in existence assertion
cy.contains('.animal', 'fox')
Multiple assertions
If you attach multiple assertions to the same command, all assertions must pass at once. For example, here is a test that shows how to correctly check the disappearing element.
<div style="display: none" id="loading">Loading ...</div>
<button id="load-something">Load</button>
<script>
document
.getElementById('load-something')
.addEventListener('click', function () {
const loadingElement = document.getElementById('loading')
// first show the loading element
setTimeout(function showLoading() {
loadingElement.style.display = 'block'
}, 1500)
// then hide the loading element
setTimeout(function hideLoading() {
loadingElement.style.display = 'none'
}, 2500)
})
</script>
The command below fails because the element cannot be visible AND invisible at the same time:
// ⛔️ DOES NOT WORK
// cy.get('#load-something').click()
// cy.get('#loading').should('be.visible').and('not.be.visible')
Instead split the assertions to have separate command to re-query the element and pass one by one The first command asserts the loading element is visible, the second command gets the element again and asserts the element is invisible:
// ✅ THE CORRECT WAY
cy.get('#load-something').click()
cy.get('#loading').should('be.visible')
cy.get('#loading').should('not.be.visible')
It is ok to add multiple assertions that can be true at the same time:
cy.get('#loading')
.should('exist')
.and('have.text', 'Loading ...')
Input elements
When using HTML input elements, use have.value
assertion.
<input
id="rent"
type="text"
pattern="[0-9]+\.*[0-9]*"
placeholder="e.g 000.00"
required
/>
cy.get('#rent').type('630.00').should('have.value', '630.00')
Even if you have a numeric input, its value is still a string.
<input id="count" type="number" value="12" required />
cy.get('#count').should('have.value', '12')
// if you want to convert the value to a number
cy.get('#count').invoke('val').then(Number).should('equal', 12)
// a good practice is to have a single "should"
// to make sure the retry-ability works
cy.get('#count').should(($el) => {
const count = Number($el.val())
expect(count, 'count').to.equal(12)
})
Element has focus
Let's confirm that the element still has the focus 2 seconds after typing into it.
<input id="your-name" /> <input id="your-occupation" />
cy.get('input#your-name')
.type('Joe')
.wait(2000)
.should('have.focus')
// the other input element should not have focus
cy.get('#your-occupation').should('not.have.focus')
See how to confirm the cursor position in Text Area Cursor recipe.
Checkboxes
<ul id="checkboxes">
<li>
<input type="checkbox" name="bike" checked="checked" />
<label for="bike">I have a bike</label>
</li>
<li>
<input type="checkbox" name="car" disabled="disabled" />
<label for="car">I have a car</label>
</li>
</ul>
li {
list-style-type: none;
}
cy.get('[name=bike]').should('be.checked')
cy.get('[name=car]')
.should('not.be.checked')
// we can also confirm this input checkbox is disabled
.and('be.disabled')
Non-input elements
With non-input HTML elements, you can use the contain
assertion.
<p id="text-example">A brown fox ...</p>
cy.get('#text-example').should('contain', 'brown fox')
HTML element tag
To confirm the HTML element's tag name, use have.prop
assertion with property nodeName
. Remember that node names are all capitalized like DIV
, P
, etc.
<marquee id="tag-example">A brown fox ...</marquee>
cy.get('#tag-example').should('have.prop', 'nodeName', 'MARQUEE')
You can also use match
assertion with HTML element, which invokes jQuery is() method.
cy.get('#tag-example')
// let's see if different selectors match the element
.should('match', 'marquee')
.and('match', '#tag-example')
.and('match', 'marquee#tag-example')
// you can even pass your own predicate callback function
.and('match', (k, el) => {
// return a boolean to pass the "match" assertion
return el.innerText.includes('fox')
})
Text assertions
Include or have text
Let's check if the element's text is exactly the string "Hello, World!"
<div id="greeting">Hello, World!</div>
cy.get('#greeting').should('have.text', 'Hello, World!')
We can also check if the element's text includes a string
cy.get('#greeting').should('include.text', 'Hello')
Multiple elements
If you query has multiple elements, then the text is the concatenation of all texts, because under the hood the text assertions call jQuery.text()
method.
<ul id="greetings">
<li>Hello</li>
<li>Hi</li>
<li>Aloha</li>
</ul>
cy.get('#greetings li')
.should('include.text', 'Hello')
.and('include.text', 'Hi')
.and('include.text', 'Aloha')
.and('have.text', 'HelloHiAloha')
// let's check the jQuery text() method
.invoke('text')
.should('equal', 'HelloHiAloha')
Compare text between two elements
Let's say we want to confirm that the text is the same in two elements A and B. We don't know the text, so we need to get it from one of the elements.
<div class="A">Brown fox</div>
<div class="B">Brown fox</div>
// cy.get yields a jQuery object
cy.get('.A')
// get the text from jQuery object
.invoke('text')
// to use the yielded text
// use the cy.then command
.then((text) => {
cy.contains('.B', text)
})
Match a regular expression
To confirm an element has text matching a regular expression, grab its text and use the Chai match assertion. For example, let's confirm the button shows one of the arithmetic operations +
, -
, /
, or *
:
<button id="math-op">+</button>
cy.get('#math-op')
.invoke('text')
.should('match', /[+-/*]/)
// if we get the text from the element and know the exact variants
// we can use "oneOf" assertion
.and('be.oneOf', ['+', '-', '/', '*'])
// alternative: use cy.contains command for more robust matching
cy.contains('#math-op', /[+-/*]/)
You can use jQuery match
assertion that maps to using jQuery is() function.
cy.get('#math-op').should('match', (k, el) => {
return (
el.innerText.includes('+') ||
el.innerText.includes('-') ||
el.innerText.includes('/') ||
el.innerText.includes('*')
)
})
contain.text
You can check if the merged text from the found elements includes a given string using contain.text
assertion.
<div id="labels">
<div class="label">34</div>
<div class="label">loading...</div>
</div>
There are two labels, thus the text it checks is "34loading.."
cy.get('#labels .label').should('contain.text', '34loading')
It will find each string separately.
cy.get('#labels .label')
.should('contain.text', '34')
.and('contain.text', 'load')
Tip: if you want to consider just the first element, you can use cy.contains
. With this command, you can use a regular expression.
cy.contains('#labels .label', /^\d+$/)
Spaces
Before comparing an element text with a number, trim the spaces (if any), and convert a string into a number.
<!-- Notice the newlines and spaces around the number -->
<div id="employee-number">1209</div>
cy.get('#employee-number')
.invoke('text')
.invoke('trim')
.then(Number)
.should('equal', 1209)
Newlines
If the text contains newline characters, you can trim it before asserting the text contents or use cy.contains or include.text
assertion.
To better show how assertions wait for the application to be ready, this element adds "there!" after a delay.
<div id="newlines-example">hello</div>
<script>
setTimeout(function () {
document.getElementById('newlines-example').innerText +=
', there!\n'
}, 1000)
</script>
cy.get('#newlines-example')
// cannot use "have.text" because it requires
// and exact match, and the element has "\n...\n"
// .should('have.text', 'hello, there!')
// if you want to perform specific text transforms
// before checking it, do it inside a should(cb) function
.should(($el) => {
// yget and trim the text before comparing
const text = $el.text().trim()
expect(text).to.equal('hello, there!')
})
// the "include.text" assertion only checks part of the text
.and('include.text', 'hello, there!')
// cy.contains uses partial text match too
cy.contains('#newlines-example', 'hello, there!')
HTML entities
Plus apostrophes, single quotes, and back ticks.
<span id="y-value">√y</span>
<div id="instrument">Tom's fiddle</div>
<div id="restaurant">Joe`s Pizza</div>
cy.get('#y-value')
// use the text value of the HTML entity
.should('have.html', '√y')
// "have.html", "have.text", and "contain"
// assertions work the same with text
.and('have.text', '√y')
.and('contain', '√y')
Unfortunately, there is no standard built-in JavaScript method for converting HTML entities like √
into the browser text.
// 🚨 WILL NOT WORK
cy.contains('#y-valid', '√y')
Thus the test can encode it itself to use in the assertion.
// a utility for converting HTML entities into text
const encode = (s) => {
const p = document.createElement('p')
p.innerHTML = s // encodes
return p.innerText
}
// ✅ WORKS
cy.get('#y-value').should('have.text', encode('√y'))
// apostrophe ' encoded as a single quote "'"
cy.contains('#instrument', "Tom's fiddle")
// backtick
cy.contains('#restaurant', 'Joe`s Pizza').should(
'include.text',
'Joe`s',
)
Visible element with text
Let's confirm that the page contains a visible element with some text.
<div id="greeting">Hello, there!</div>
// if we know the precise text we are looking for
cy.get('#greeting')
.should('be.visible')
.and('have.text', 'Hello, there!')
// if we do not know the text
cy.get('#greeting')
.should('be.visible')
.invoke('text')
.should('be.a', 'string')
.and('be.not.empty')
Partial text match
<div id="parent-element">
some text at the start
<span class="inner">main content</span>
and some text afterwards
</div>
cy.get('#parent-element')
// we only know a part of the text somewhere
// inside the element
.should('include.text', 'at the start')
// "include.text" and "contain" are synonym assertions
// to find partial text match
.and('contain', 'some text afterwards')
// the text inside the child element also counts
.and('contain', 'main content')
cy.get('#parent-element')
// if we use cy.contains command
// we find the child <span> element
.contains('main')
.should('have.class', 'inner')
have.text vs contain
<div id="blurb">A quick brown fox jumped</div>
cy.get('#blurb')
// assertions "include.text" and "contain"
// are equivalent and match the entire text
// or just part of the text
.should('include.text', 'brown fox')
.and('contain', 'brown fox')
// can match the entire text
.and('contain', 'A quick brown fox jumped')
// "have.text" requires the full text match
.and('have.text', 'A quick brown fox jumped')
// but "have.text" does not allow partial text match
.and('not.have.text', 'brown fox')
Text matching the regular expression
We can use regular expressions with "match" assertions to confirm part of the text.
<div id="a-greeting">Hello, there!</div>
cy.get('#a-greeting')
.invoke('text')
.should('match', /^Hello/)
// tip: use cy.contains to find element with text
// matching a regular expression
cy.contains('#a-greeting', /^Hello/)
// you can ignore the case and perform case-insensitive match
cy.get('#a-greeting')
.invoke('text')
.should('match', /hello, there!/i)
// a better version would use cy.contains command
// to perform case-insensitive query
// you can use either a regular expression
cy.contains('#a-greeting', /hello, there!/i)
// or use the contains option to ignore the case
cy.contains('#a-greeting', 'hello, there!', { matchCase: false })
Match numbers using a regular expression
Imagine we want to validate the date inside an element. The date should have the day number between 1 and 31.
<div id="date-string">Feb 14, 2022</div>
// \b means the word boundary
// (?<day>...) is a named capture group
// \d{1,2}, is a one or two digit number followed by a comma
const dayRegex = /\b(?<day>\d{1,2}),/
cy.get('#date-string')
.invoke('text')
.should('match', dayRegex)
// extract the day number
.invoke('match', dayRegex)
// grab the named group, convert to integer
.its('groups.day')
.then(parseInt)
// and confirm its value
.should('be.within', 1, 31)
// and in our case we know when Valentine's day is
.and('equal', 14)
Converting text
Sometimes you need to extract the text and convert it into a number before running an assertion.
<div id="num-example">
Messages <span class="messages">4</span>
</div>
Without converting, the text from an element is a string
// get the element and assert it has text "4"
cy.get('#num-example .messages').should('have.text', '4')
// equivalent command using cy.contains command
cy.contains('#num-example .messages', '4')
If we want to compare the element's text like a number, we need to extract the text using jQuery text
method, then parse it into an integer before the assertion.
cy.get('#num-example .messages')
.invoke('text')
.then(parseInt)
.should('equal', 4)
// if you do not know the exact expected number
// use range assertions, like "greater than", "within"
.and('be.gt', 0)
.and('be.within', 0, 10)
You can also combine multiple steps into a single "should" callback for greater retry-ability.
// use command + single assertion callback
cy.get('#num-example .messages').should(($el) => {
const n = parseInt($el.text())
expect(n, 'number of messages')
.to.be.a('number')
.and.be.within(0, 10)
})
Number within range
Very useful approach to confirm a number is within certain range or between limits A and B.
<div id="shipping">Ground shipping for $11.79, up to 2 kg.</div>
// named capture group that matches "$" + dollar + cents text
const priceRe = /\$(?<price>\d+\.\d{2})/
cy.contains('#shipping', priceRe) // yields jQuery
.invoke('text') // yields text
.invoke('match', priceRe) // yields match
.its('groups.price') // yields text
.then(Number) // yields a number
.should('be.within', 10, 15) // number is between X and Y
See Dollar range recipe.
OR match using a regular expression
If you want to confirm the text matches one string or another, use a regular expression
<div id="or-match">Joe</div>
cy.get('#or-match')
.invoke('text')
.should('match', /^(Joe|Mary)$/)
// the same can be done using cy.contains command
cy.contains('#or-match', /^(Joe|Mary)$/)
oneOf
assertion
OR text match using 📺 Watch this example in the video Compare Text In An Element Against Several Possible Values Using oneOf Assertion.
<div id="guest-name">Joe</div>
cy.get('#guest-name')
.invoke('text')
.should('be.oneOf', ['Mary', 'Joe'])
One of the elements has the text
Imagine one of several elements should have the text we are looking for. We just don't know precisely which element does. I would recommend using the combined CSS selector selector 1, selector 2, selector 3
which returns the combined list of all those selectors.
<div id="nav-1">Joe</div>
<div id="profile">
<span data-cy="username">Joseph<span>
</div>
// finds the first element with the text "Joe"
cy.contains('#nav-1, #profile', 'Joe').should('have.id', 'nav-1')
// same selector finds the second element with the text "Joseph"
cy.contains('#nav-1, #profile', 'Joseph').should(
'have.id',
'profile',
)
Text should not present
<div id="text-lines">
<span>some text at the start</span>
<span class="inner">main content</span>
<span>and some text afterwards</span>
</div>
cy.get('#text-lines').should('include.text', 'main content')
cy.get('#text-lines .inner').should(
'not.include.text',
'some text',
)
// you can also use :contains and :not selectors
cy.get('#text-lines span:not(:contains("main content"))').should(
'have.length',
2,
)
Visibility of multiple elements
Only some elements should be visible for the assertion should('be.visible')
to pass.
<ul id="few-elements">
<li>first</li>
<li style="display:none">second</li>
<li>third</li>
</ul>
The test passes, even if some elements are invisible.
cy.get('#few-elements li')
.should('be.visible')
.and('have.length', 3)
// while the second element is still invisible
cy.contains('#few-elements li', 'second').should(
'not.be.visible',
)
// workarounds for visibility checks
// 1. we can use jQuery selector :visible to get just the visible elements
cy.get('#few-elements li:visible').should('have.length', 2)
// 2. we can filter visible elements using jQuery selector
cy.get('#few-elements li')
.should('have.length', 3)
.filter(':visible')
.should('have.length', 2)
// 3. we can filter elements to get just the invisible elements
cy.get('#few-elements li')
.not(':visible')
.should('have.length', 1)
.and('have.text', 'second')
For more, see my video Visibility Of Multiple Elements Explained.
Elements becoming invisible
Let's checks if a list of elements becomes invisible after some time.
<ul id="multiple-elements">
<li>first</li>
<li>second</li>
<li>third</li>
</ul>
<button id="hide-multiple-elements">Hide items</button>
<script>
document
.getElementById('hide-multiple-elements')
.addEventListener('click', function () {
// we hide the elements after some unknown delay
setTimeout(
function () {
document
.querySelectorAll('#multiple-elements li')
.forEach(function (el) {
el.style.display = 'none'
})
},
// elements disappear after 1 - 2 seconds
Math.random() * 1000 + 1000,
)
})
</script>
At first, all elements are visible
cy.get('#multiple-elements li')
.should('have.length', 3)
.and('be.visible')
The elements become invisible after clicking on the button
cy.get('#hide-multiple-elements').click()
cy.get('#multiple-elements li')
// the elements still exist in the DOM
.should('exist')
.and('have.length', 3)
// but should not be visible to the user
.and('not.be.visible')
Placeholder attribute
Let's validate the input element's placeholder attribute.
<input
type="text"
id="inputEmail"
class="form-control"
placeholder="Email"
/>
cy.get('#inputEmail').should('have.attr', 'placeholder', 'Email')
Disabled elements
<input type="text" id="example-input" disabled />
cy.get('#example-input')
.should('be.disabled')
// let's enable this element from the test
.invoke('prop', 'disabled', false)
cy.get('#example-input')
// we can use "enabled" assertion
.should('be.enabled')
// or negate the "disabled" assertion
.and('not.be.disabled')
Data attributes
<ul id="data-attributes">
<li data-test-id="first">first</li>
<li data-e2e="second">second</li>
<li data-e2e="one" data-cy="two">third</li>
</ul>
We can check if an element has a particular "data-x" attribute present, and its value using a "have.data" assertion.
cy.contains('#data-attributes li', 'first')
// "data" object converts names to camel case
.should('have.data', 'testId', 'first')
// the assertion yields the original element
.should('match', 'li')
.and('have.text', 'first')
cy.contains('#data-attributes li', 'second')
// first assertion confirms there is such "data-x" property
.should('have.data', 'e2e')
// and yields the jQuery object to the next assertion
.should('have.text', 'second')
// multiple "data-" properties need multiple commands
cy.contains('#data-attributes li', 'third').should(
'have.data',
'e2e',
'one',
)
cy.contains('#data-attributes li', 'third').should(
'have.data',
'cy',
'two',
)
// or a single "data()" call to get an object of values
cy.contains('#data-attributes li', 'third')
.invoke('data')
.should('deep.equal', {
e2e: 'one',
cy: 'two',
})
Tip: you can simplify checking for "data-" attributes by adding your own custom Chai assertion, see the recipe.
Array assertions
Confirm it is an array
cy.wrap([1, 2, 3]).should('be.an', 'array').and('have.length', 3)
Confirm array has a given primitive value
expect([1, 2, 3, 99]).to.include(99).and.not.include(42)
expect(['hello', 'world']).to.include('world')
Be careful if the array contains objects. By default, JavaScript checks the references, and not values.
const todos = [{ title: 'write code', completed: true }]
expect(todos)
// the array includes the reference to the same object
.to.include(todos[0])
// and does not compare by value
.and.not.include({ title: 'write code', completed: true })
If you want to find an object in an array and you know the EXACT full object, use the "deep.include" assertion.
const p1 = { name: 'p1', id: 1 }
const p2 = { name: 'p2', id: 2 }
const p3 = { name: 'p3', id: 3 }
// if we have a reference to the object we want to find
// it is the same as finding a primitive value
expect([p1, p2, p3], 'object references').to.include(p2)
// check array by value if you know the ENTIRE object
// for finding objects by value need to use "deep.include" assertion
expect([p1, p2, p3], 'object value')
.to.deep.include({
name: 'p3',
id: 3,
})
// and it does not work with partial objects
.and.not.deep.include({
name: 'p3',
})
If you want to find an object in an array and you know only some of its properties, use Cypress._.find
Lodash method.
const p1 = { name: 'p1', id: 1 }
const p2 = { name: 'p2', id: 2 }
const p3 = { name: 'p3', id: 3 }
// if you know a part of the object, use Lodash _.find helper method
// that can search by a property or several properties
// Tip: Lodash is bundled with Cypress under Cypress._
expect(Cypress._.find([p1, p2, p3], { name: 'p3' }), 'found p3')
// check the found object
.to.be.an('object')
.and.have.property('id', 3)
Confirm items in an array
cy.wrap([1, 2, 3])
.should('be.an', 'array')
.each((k) => {
expect(k).to.be.a('number').and.be.within(1, 3)
})
Watch the video Validate Each Array Item.
Comparing arrays
Whenever you assert arrays and other objects, you probably mean to assert the values inside, and not the references. Thus you need to use the deep.equal
assertion.
const arr = ['Apples', 'Bananas', 'Grapes']
// assert that cy.wrap yields the same array reference
// as we passed into it
cy.wrap(arr).should('equal', arr)
// if you have another array, even if the values are the same
// the "equal" assertion compares just the reference
// thus an array is "not.equal" to its copy
cy.wrap(arr).should('not.equal', Cypress._.clone(arr))
// but it is "deep.equal" to another array if the values are the same
cy.wrap(arr).should('deep.equal', Cypress._.clone(arr))
// assert the yielded array has the expected items inside
cy.wrap(arr)
.invoke('reverse')
// because we are comparing arrays, we need to use "deep.equal" assertion
.should('deep.equal', ['Grapes', 'Bananas', 'Apples'])
Objects
Checking a property
Check if an object has a property
const person = {
name: 'Joe',
}
// check if the property is present, but do not check the value
cy.wrap(person).should('have.property', 'name')
// check if the property is present and has a specific value
cy.wrap(person).should('have.property', 'name', 'Joe')
// check if the value is a string and matches a regular expression
cy.wrap(person)
.should('have.property', 'name')
// now we are working with the value from person.name
.should('be.a', 'string')
.and('match', /joe/i)
Assertions have.property
and have.key
are similar.
// the assertion "have.property" yields the value
cy.wrap(person)
.should('have.property', 'name')
.should('equal', 'Joe')
// the assertion "have.key" yields the same object
cy.wrap(person)
.should('have.key', 'name')
// we are still working with the object
.should('deep.equal', { name: 'Joe' })
Multiple properties
If the object has multiple properties, use have.keys
if you want to precisely assert them.
const address = {
city: 'Boston',
state: 'MA',
zip: '90210',
}
// you can specify keys as an array argument
cy.wrap(address).should('have.keys', ['city', 'state', 'zip'])
// or as separate arguments
cy.wrap(address).should('have.keys', 'city', 'state', 'zip')
If you are only interested in some properties, use the include.keys
assertion
// the object has city and state keys (and possible others)
cy.wrap(address).should('include.keys', ['city', 'state'])
// can pass keys as separate arguments
cy.wrap(address).should('include.keys', 'city', 'state')
Partial match
If we want to check a part of the an object, we can use deep.include
assertion.
const address = {
city: 'Boston',
state: 'MA',
zip: '90210',
}
cy.wrap(address)
.should('deep.include', {
city: 'Boston',
zip: '90210',
})
// continue working with the original object
.and('deep.include', {
state: 'MA',
})
Multiple variants
expect('hello').to.be.oneOf(['hi', 'good morning', 'hello'])
cy.wrap(42).should('be.oneOf', [10, 42, 30])
Has a method
You can check if a particular object has a method with a given name.
const person = {
greeting() {
return 'Hello'
},
}
expect(person).to.respondTo('greeting')
cy.wrap(person).should('respondTo', 'greeting')
Approximate value using closeTo
One way to compare a floating point number is to round it using Math.round
to the nearest integer.
// compare the given number to the closest integer
cy.wrap(52.9).then(Math.round).should('equal', 53)
Another way is to use the assertion closeTo
// using closeTo assertion
// expected 32.7 to be close to 32 +/- 1
expect(32.7).to.be.closeTo(32, 1.0)
cy.wrap(52.9).should('be.closeTo', 50, 3)
This is especially useful when dealing with dimensions of elements on the page, since no real-world measurement is exact. It always slightly different between the browsers and operating systems.
<div id="font-size-example">This is my message</div>
cy.get('#font-size-example')
.invoke('css', 'fontSize')
.should('be.a', 'string')
// the font size returned is in pixels, like "16px"
.and('match', /^\d+px$/)
.invoke('replace', 'px', '')
.then(Number)
.should('be.closeTo', 16, 1)
Adding assertions
Multiple attributes
The built-in Chai assertion have.attr
only confirms a single attribute. What if we want to confirm multiple attributes? We could write it like this:
<a id="about-page" href="/about" target="_blank">About</a>
cy.contains('a', 'About').should((el) => {
// we do not care about ID attribute
expect(el).to.have.attr('href', '/about')
expect(el).to.have.attr('target', '_blank')
})
Custom Chai assertion
Alternatively, we can add a custom Chai assertion to our global chai
object.
<a id="about-page" href="/about" target="_blank">About</a>
// add custom Chai assertion to confirm multiple attributes
chai.use((_chai, utils) => {
// use "function" syntax to make sure when Chai
// calls it, the "this" object points at Chai
function assertAttributes(attributes) {
Object.keys(attributes).forEach((attr) => {
const value = this._obj.attr(attr)
const expectedValue = attributes[attr]
this.assert(
value === expectedValue,
`expected to find attribute **${attr}: ${expectedValue}**, found **${value}**`,
)
})
}
_chai.Assertion.addMethod('attributes', assertAttributes)
})
// now let's use our custom Chai assertion
// to confirm multiple element attributes at once
cy.contains('a', 'About').should('have.attributes', {
href: '/about',
target: '_blank',
// but we do not care about "id" attribute
})
Check against undefined
Checking against an undefined
value:
expect(undefined, 'undefined value').to.be.undefined
expect(undefined, 'undefined is not null').to.not.be.null
expect(null, 'null value').to.not.be.undefined
expect(null, 'null is null').to.be.null
You can also compare the values using to.equal
assertion
expect(undefined, 'undefined equals undefined').to.equal(
undefined,
)
expect(undefined, 'undefined does not equal null').to.not.equal(
null,
)
expect(null, 'null equals null').to.equal(null)
Similarly, the wrapped undefined value can be confirmed using BDD assertions
cy.wrap(undefined, 'wrapped undefined')
.should('be.undefined')
.and('equal', undefined)
.and('not.be.null')
.and('not.equal', null)
cy.wrap(42, 'wrapped number')
.should('not.be.undefined')
.and('not.be.null')
.and('be.a', 'number')
Assertion message
See how to avoid message truncation
Use the strongest assertion
You can validate the data shown or used by the web application in many ways. I would advise to pick the strongest assertion possible to catch any unexpected changes that might happen.
<strong>Phone: </strong>
<span class="phone">(123) 456-7890</span>
// the weakest assertion
cy.get('.phone')
.invoke('text')
.should('be.a', 'string')
.and('have.length', 14)
// a much stronger assertion
const phoneFormat = /^\(\d{3}\) \d{3}-\d{4}$/
cy.contains('.phone', phoneFormat)
// the strongest is to confirm the exact value
cy.get('.phone').should('have.text', '(123) 456-7890')
Watch the video Use Stronger Assertions.
See also
- Lesser known Chai assertions recipe