Skip to main content

Expect

When you're writing tests, you often need to check that values meet certain conditions. expect gives you access to a number of "matchers" that let you validate different things on the browser, an element or mock object.

Default Options

These default options below are connected to the waitforTimeout and waitforInterval options set in the config.

Only set the options below if you want to wait for specific timeouts for your assertions.

{
wait: 2000, // ms to wait for expectation to succeed
interval: 100, // interval between attempts
}

If you like to pick different timeouts and intervals, set these options like this:

// wdio.conf.js
import { setOptions } from 'expect-webdriverio'

export const config = {
// ...
before () {
setOptions({ wait: 5000 })
},
// ...
}

Matcher Options

Every matcher can take several options that allows you to modify the assertion:

Command Options
NameTypeDetails
waitnumbertime in ms to wait for expectation to succeed. Default: 3000
intervalnumberinterval between attempts. Default: 100
beforeAssertionfunctionfunction to be called before assertion is made
afterAssertionfunctionfunction to be called after assertion is made containing assertion results
messagestringuser message to prepend before assertion error
String Options

This option can be applied in addition to the command options when strings are being asserted.

NameTypeDetails
ignoreCasebooleanapply toLowerCase to both actual and expected values
trimbooleanapply trim to actual value
replaceReplacer | Replacer[]replace parts of the actual value that match the string/RegExp. The replacer can be a string or a function.
containingbooleanexpect actual value to contain expected value, otherwise strict equal.
asStringbooleanmight be helpful to force converting property value to string
atStartbooleanexpect actual value to start with the expected value
atEndbooleanexpect actual value to end with the expected value
atIndexnumberexpect actual value to have the expected value at the given index
Number Options

This option can be applied in addition to the command options when numbers are being asserted.

NameTypeDetails
eqnumberequals
ltenumberless then equals
gtenumbergreater than or equals

Handling HTML Entities

An HTML entity is a piece of text (“string”) that begins with an ampersand (&) and ends with a semicolon (;). Entities are frequently used to display reserved characters (which would otherwise be interpreted as HTML code), and invisible characters (like non-breaking spaces, e.g.  ).

To find or interact with such element use unicode equivalent of the entity. e.g.:

<div data="Some&nbsp;Value">Some&nbsp;Text</div>
const myElem = await $('div[data="Some\u00a0Value"]')
await expect(myElem).toHaveAttribute('data', 'div[Some\u00a0Value')
await expect(myElem).toHaveText('Some\u00a0Text')

You can find all unicode references in the HTML spec.

Note: unicode is case-insensitive hence both \u00a0 and \u00A0 works. To find element in browser inspect, remove u from unicode e.g.: div[data="Some\00a0Value"]

Browser Matchers

toHaveUrl

Checks if browser is on a specific page.

Usage
await browser.url('https://webdriver.io/')
await expect(browser).toHaveUrl('https://webdriver.io')
Usage
await browser.url('https://webdriver.io/')
await expect(browser).toHaveUrl(expect.stringContaining('webdriver'))

toHaveTitle

Checks if website has a specific title.

Usage
await browser.url('https://webdriver.io/')
await expect(browser).toHaveTitle('WebdriverIO · Next-gen browser and mobile automation test framework for Node.js')
await expect(browser).toHaveTitle(expect.stringContaining('WebdriverIO'))

toHaveClipboardText

Checks if the browser has a specific text stored in its clipboard.

Usage
import { Key } from 'webdriverio'

await browser.keys([Key.Ctrl, 'a'])
await browser.keys([Key.Ctrl, 'c'])
await expect(browser).toHaveClipboardText('some clipboard text')
await expect(browser).toHaveClipboardText(expect.stringContaining('clipboard text'))

Element Matchers

toBeDisplayed

Calls isDisplayed on given element.

Usage
const elem = await $('#someElem')
await expect(elem).toBeDisplayed()

toExist

Calls isExisting on given element.

Usage
const elem = await $('#someElem')
await expect(elem).toExist()

toBePresent

Same as toExist.

Usage
const elem = await $('#someElem')
await expect(elem).toBePresent()

toBeExisting

Same as toExist.

Usage
const elem = await $('#someElem')
await expect(elem).toBeExisting()

toBeFocused

Checks if element has focus. This assertion only works in a web context.

Usage
const elem = await $('#someElem')
await expect(elem).toBeFocused()

toHaveAttribute

Checks if an element has a certain attribute with a specific value.

Usage
const myInput = await $('input')
await expect(myInput).toHaveAttribute('class', 'form-control')
await expect(myInput).toHaveAttribute('class', expect.stringContaining('control'))

toHaveAttr

Same as toHaveAttribute.

Usage
const myInput = await $('input')
await expect(myInput).toHaveAttr('class', 'form-control')
await expect(myInput).toHaveAttr('class', expect.stringContaining('control'))

toHaveElementClass

Checks if an element has a single class name. Can also be called with an array as a parameter when the element can have multiple class names.

Usage
const myInput = await $('input')
await expect(myInput).toHaveElementClass('form-control', { message: 'Not a form control!' })
await expect(myInput).toHaveElementClass(['form-control' , 'w-full'], { message: 'not full width' })
await expect(myInput).toHaveElementClass(expect.stringContaining('form'), { message: 'Not a form control!' })

toHaveElementProperty

Checks if an element has a certain property.

Usage
const elem = await $('#elem')
await expect(elem).toHaveElementProperty('height', 23)
await expect(elem).not.toHaveElementProperty('height', 0)

toHaveValue

Checks if an input element has a certain value.

Usage
const myInput = await $('input')
await expect(myInput).toHaveValue('admin-user', { ignoreCase: true })
await expect(myInput).toHaveValue(expect.stringContaining('user'), { ignoreCase: true })

toBeClickable

Checks if an element can be clicked by calling isClickable on the element.

Usage
const elem = await $('#elem')
await expect(elem).toBeClickable()

toBeDisabled

Checks if an element is disabled by calling isEnabled on the element.

Usage
const elem = await $('#elem')
await expect(elem).toBeDisabled()
// same as
await expect(elem).not.toBeEnabled()

toBeEnabled

Checks if an element is enabled by calling isEnabled on the element.

Usage
const elem = await $('#elem')
await expect(elem).toBeEnabled()
// same as
await expect(elem).not.toBeDisabled()

toBeSelected

Checks if an element is enabled by calling isSelected on the element.

Usage
const elem = await $('#elem')
await expect(elem).toBeSelected()

toBeChecked

Same as toBeSelected.

Usage
const elem = await $('#elem')
await expect(elem).toBeChecked()

toHaveComputedLabel

Checks if element has a specific computed WAI-ARIA label. Can also be called with an array as parameter in the case where the element can have different labels.

Usage
await browser.url('https://webdriver.io/')
const elem = await $('a[href="https://github.com/webdriverio/webdriverio"]')
await expect(elem).toHaveComputedLabel('GitHub repository')
await expect(elem).toHaveComputedLabel(expect.stringContaining('repository'))
Usage
await browser.url('https://webdriver.io/')
const elem = await $('a[href="https://github.com/webdriverio/webdriverio"]')
await expect(elem).toHaveComputedLabel(['GitHub repository', 'Private repository'])
await expect(elem).toHaveComputedLabel([expect.stringContaining('GitHub'), expect.stringContaining('Private')])

toHaveComputedRole

Checks if element has a specific computed WAI-ARIA role. Can also be called with an array as parameter in the case where the element can have different labels.

Usage
await browser.url('https://webdriver.io/')
const elem = await $('[aria-label="Skip to main content"]')
await expect(elem).toHaveComputedRole('region')
await expect(elem).toHaveComputedRole(expect.stringContaining('ion'))
Usage
await browser.url('https://webdriver.io/')
const elem = await $('[aria-label="Skip to main content"]')
await expect(elem).toHaveComputedRole(['region', 'section'])
await expect(elem).toHaveComputedRole([expect.stringContaining('reg'), expect.stringContaining('sec')])

toHaveHref

Checks if link element has a specific link target.

Usage
const link = await $('a')
await expect(link).toHaveHref('https://webdriver.io')
await expect(link).toHaveHref(expect.stringContaining('webdriver.io'))

Same as toHaveHref.

Usage
const link = await $('a')
await expect(link).toHaveLink('https://webdriver.io')
await expect(link).toHaveLink(expect.stringContaining('webdriver.io'))

toHaveId

Checks if element has a specific id attribute.

Usage
const elem = await $('#elem')
await expect(elem).toHaveId('elem')

toHaveText

Checks if element has a specific text. Can also be called with an array as parameter in the case where the element can have different texts.

Usage
await browser.url('https://webdriver.io/')
const elem = await $('.container')
await expect(elem).toHaveText('Next-gen browser and mobile automation test framework for Node.js')
await expect(elem).toHaveText(expect.stringContaining('test framework for Node.js'))
await expect(elem).toHaveText(['Next-gen browser and mobile automation test framework for Node.js', 'Get Started'])
await expect(elem).toHaveText([expect.stringContaining('test framework for Node.js'), expect.stringContaining('Started')])

In case there is a list of elements in the div below:

<ul>
<li>Coffee</li>
<li>Tea</li>
<li>Milk</li>
</ul>

You can assert them using an array:

const elem = await $$('ul > li')
await expect(elem).toHaveText(['Coffee', 'Tea', 'Milk'])

toHaveHTML

Checks if element has a specific text. Can also be called with an array as parameter in the case where the element can have different texts.

Usage
await browser.url('https://webdriver.io/')
const elem = await $('.hero__subtitle')
await expect(elem).toHaveHTML('<p class="hero__subtitle">Next-gen browser and mobile automation test framework for Node.js</p>')
await expect(elem).toHaveHTML(expect.stringContaining('Next-gen browser and mobile automation test framework for Node.js'))
await expect(elem).toHaveHTML('Next-gen browser and mobile automation test framework for Node.js', { includeSelectorTag: false })
Usage
await browser.url('https://webdriver.io/')
const elem = await $('.hero__subtitle')
await expect(elem).toHaveHTML(['Next-gen browser and mobile automation test framework for Node.js', 'Get Started'], { includeSelectorTag: false })
await expect(elem).toHaveHTML([expect.stringContaining('automation test framework for Node.js'), expect.stringContaining('Started')], { includeSelectorTag: false })

toBeDisplayedInViewport

Checks if an element is within the viewport by calling isDisplayedInViewport on the element.

Usage
const elem = await $('#elem')
await expect(elem).toBeDisplayedInViewport()

toHaveChildren

Checks amount of the fetched element's children by calling element.$('./*') command.

Usage
const list = await $('ul')
await expect(list).toHaveChildren() // the list has at least one item
// same as
await expect(list).toHaveChildren({ gte: 1 })

await expect(list).toHaveChildren(3) // the list has 3 items
// same as
await expect(list).toHaveChildren({ eq: 3 })

toHaveWidth

Checks if element has a specific width.

Usage
await browser.url('http://github.com')
const logo = await $('.octicon-mark-github')
await expect(logo).toHaveWidth(32)

toHaveHeight

Checks if element has a specific height.

Usage
await browser.url('http://github.com')
const logo = await $('.octicon-mark-github')
await expect(logo).toHaveHeight(32)

toHaveSize

Checks if element has a specific size.

Usage
await browser.url('http://github.com')
const logo = await $('.octicon-mark-github')
await expect(logo).toHaveSize({ width: 32, height: 32 })

toBeElementsArrayOfSize

Checks amount of fetched elements using $$ command.

Note: This matcher will update the passed array with the latest elements if the assertion passes. However, if you've reassigned the variable, you'll need to fetch the elements again.

Usage
const listItems = await $$('ul>li')
await expect(listItems).toBeElementsArrayOfSize(5) // 5 items in the list

await expect(listItems).toBeElementsArrayOfSize({ lte: 10 })
// same as
assert.ok(listItems.length <= 10)

Network Matchers

toBeRequested

Checks that mock was called

Usage
const mock = browser.mock('**/api/todo*')
await expect(mock).toBeRequested()

toBeRequestedTimes

Checks that mock was called for the expected amount of times

Usage
const mock = browser.mock('**/api/todo*')
await expect(mock).toBeRequestedTimes(2) // await expect(mock).toBeRequestedTimes({ eq: 2 })

await expect(mock).toBeRequestedTimes({ gte: 5, lte: 10 }) // request called at least 5 times but less than 11

toBeRequestedWith

Checks that mock was called according to the expected options.

Most of the options supports expect/jasmine partial matchers like expect.objectContaining

Usage
const mock = browser.mock('**/api/todo*', { method: 'POST' })

await expect(mock).toBeRequestedWith({
url: 'http://localhost:8080/api/todo', // [optional] string | function | custom matcher
method: 'POST', // [optional] string | array
statusCode: 200, // [optional] number | array
requestHeaders: { Authorization: 'foo' }, // [optional] object | function | custom matcher
responseHeaders: { Authorization: 'bar' }, // [optional] object | function | custom matcher
postData: { title: 'foo', description: 'bar' }, // [optional] object | function | custom matcher
response: { success: true }, // [optional] object | function | custom matcher
})

await expect(mock).toBeRequestedWith({
url: expect.stringMatching(/.*\/api\/.*/i),
method: ['POST', 'PUT'], // either POST or PUT
statusCode: [401, 403], // either 401 or 403
requestHeaders: headers => headers.Authorization.startsWith('Bearer '),
postData: expect.objectContaining({ released: true, title: expect.stringContaining('foobar') }),
response: r => Array.isArray(r) && r.data.items.length === 20
})

Snapshot Matcher

WebdriverIO supports basic snapshot tests as well as DOM snapshot testing.

toMatchSnapshot

Checks if any arbitrary object matches a certain value. If you pass in an WebdriverIO.Element it will automatically snapshot the outerHTML state of it.

Usage
// snapshot arbitrary objects (no "await" needed here)
expect({ foo: 'bar' }).toMatchSnapshot()
// snapshot `outerHTML` of WebdriverIO.Element (DOM snapshot, requires "await")
await expect($('elem')).toMatchSnapshot()
// snapshot result of element command
await expect($('elem').getCSSProperty('background-color')).toMatchSnapshot()

toMatchInlineSnapshot

Similarly, you can use the toMatchInlineSnapshot() to store the snapshot inline within the test file. For example, given:

await expect($('img')).toMatchInlineSnapshot()

Instead of creating a snapshot file, WebdriverIO will modify the test file directly to update the snapshot as a string:

await expect($('img')).toMatchInlineSnapshot(`"<img src="/public/apple-touch-icon-precomposed.png">"`)

Visual Snapshot Matchers

The following matcher are implemented as part of the @wdio/visual-service plugin and only available when the service is set up. Make sure you follow the set-up instructions accordingly.

toMatchElementSnapshot

Checks that if given element matches with snapshot of baseline.

Usage
await expect($('.hero__title-logo')).toMatchElementSnapshot('wdioLogo', 0, {
// options
})

The expected result is by default 0, so you can write the same assertion as:

await expect($('.hero__title-logo')).toMatchElementSnapshot('wdioLogo', {
// options
})

or not pass in any options at all:

await expect($('.hero__title-logo')).toMatchElementSnapshot()

toMatchScreenSnapshot

Checks that if current screen matches with snapshot of baseline.

Usage
await expect(browser).toMatchScreenSnapshot('partialPage', 0, {
// options
})

The expected result is by default 0, so you can write the same assertion as:

await expect(browser).toMatchScreenSnapshot('partialPage', {
// options
})

or not pass in any options at all:

await expect(browser).toMatchScreenSnapshot('partialPage')

toMatchFullPageSnapshot

Checks that if the full page screenshot matches with snapshot of baseline.

Usage
await expect(browser).toMatchFullPageSnapshot('fullPage', 0, {
// options
})

The expected result is by default 0, so you can write the same assertion as:

await expect(browser).toMatchFullPageSnapshot('fullPage', {
// options
})

or not pass in any options at all:

await expect(browser).toMatchFullPageSnapshot('fullPage')

toMatchTabbablePageSnapshot

Checks that if the full page screenshot including tab marks matches with snapshot of baseline.

Usage
await expect(browser).toMatchTabbablePageSnapshot('tabbable', 0, {
// options
})

The expected result is by default 0, so you can write the same assertion as:

await expect(browser).toMatchTabbablePageSnapshot('tabbable', {
// options
})

or not pass in any options at all:

await expect(browser).toMatchTabbablePageSnapshot('tabbable')

Using regular expressions

You can also directly use regular expressions for all matchers that do text comparison.

Usage
await browser.url('https://webdriver.io/')
const elem = await $('.container')
await expect(elem).toHaveText(/node\.js/i)
await expect(elem).toHaveText([/node\.js/i, 'Get Started'])
await expect(browser).toHaveTitle(/webdriverio/i)
await expect(browser).toHaveUrl(/webdriver\.io/)
await expect(elem).toHaveElementClass(/Container/i)

Default Matchers

In addition to the expect-webdriverio matchers you can use builtin Jest's expect assertions or expect/expectAsync for Jasmine.

Asymmetric Matchers

WebdriverIO supports usage of asymmetric matchers wherever you compare text values, e.g.:

await expect(browser).toHaveTitle(expect.stringContaining('some title'))

or

await expect(browser).toHaveTitle(expect.not.stringContaining('some title'))

TypeScript

If you are using the WDIO Testrunner everything will be automatically setup. Just follow the setup guide from the docs. However if you run WebdriverIO with a different testrunner or in a simple Node.js script you will need to add expect-webdriverio to types in the tsconfig.json.

  • "expect-webdriverio" for everyone except of Jasmine/Jest users.
  • "expect-webdriverio/jasmine" Jasmine
  • "expect-webdriverio/jest" Jest

JavaScript (VSCode)

It's required to create jsconfig.json in project root and refer to the type definitions to make autocompletion work in vanilla js.

{
"include": [
"**/*.js",
"**/*.json",
"node_modules/expect-webdriverio"
]
}

Adding your own matchers

Similar to how expect-webdriverio extends Jasmine/Jest matchers it's possible to add custom matchers.

Custom matchers should be added in wdio before hook

// wdio.conf.js
{
async before () {
const { addCustomMatchers } = await import('./myMatchers')
addCustomMatchers()
}
}
// myMatchers.js - Jest example
export function addCustomMatchers () {
if (global.expect.expect !== undefined) { // Temporary workaround. See https://github.com/webdriverio/expect-webdriverio/issues/835
global.expect = global.expect.expect;
}

expect.extend({
myMatcher (actual, expected) {
return { pass: actual === expected, message: () => 'some message' }
}
})
}

Welcome! How can I help?

WebdriverIO AI Copilot