Assertions

Examples of asserting the state or behavior of your application in Cypress, for a full reference of commands, go to docs.cypress.ioopen in new window

Implicit Assertions

.should()open in new window

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 answeropen in new window.

.and()open in new window

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 objects
  • have.attr with 1 argument for DOM elements
  • have.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 Assertionopen in new window.

<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 functionopen in new window

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 deterministicopen in new window. 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 commandopen in new window, 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()open in new window 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 matchopen in new window 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()open in new window 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.containsopen in new window 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

<span id="y-value">&radic;y</span>
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 &radic; into the browser text. 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
}
cy.get('#y-value').should('have.text', encode('&radic;y'))

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-abilityopen in new window.

// 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)$/)

OR text match using oneOf assertion

📺 Watch this example in the video Compare Text In An Element Against Several Possible Values Using oneOf Assertionopen in new window.

<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 Explainedopen in new window.

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 Itemopen in new window.

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)

See Chai assertion libraryopen in new window

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 Assertionsopen in new window.

See also