Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

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.

**Note**: Multi-remote is not yet supported. Any working scenario is coincidental and may break or change without notice until fully supported.

## Soft Assertions

Soft assertions allow you to continue test execution even when an assertion fails. This is useful when you want to check multiple conditions in a test and collect all failures rather than stopping at the first failure. Failures are collected and reported at the end of the test.
Expand Down Expand Up @@ -726,6 +728,30 @@ await expect(listItems).toBeElementsArrayOfSize({ gte: 5 })
await expect(listItems).toBeElementsArrayOfSize({ gte: 5, lte: 5 })
```

### Multiple Elements Support

Matchers starting with `toBe` support arrays of elements returned from `$$()`:
- **Standard Behavior:** Every element must pass. One failure fails the assertion.
- **Using `.not`:** Every element must *not* meet the matcher condition. One match fails the assertion.
- **Empty Arrays:** Empty element arrays will fail the assertion.
- *Note:* Only the `toExist`, `toBeExisting`, and `toBePresent` matchers succeed when using `.not` on an empty element array.

#### Usage

```ts
// Awaited selector syntax
await expect(await $$('#someElements')).toBeDisplayed()

// Non-awaited
await expect($$('#someElements')).toBeDisplayed()

// Works with filtered arrays
await expect($$('#someElements').filter((t) => t.isExisting())).toBeDisplayed()

// Using the .not modifier (Asserts NO elements are displayed)
await expect($$('#someElements')).not.toBeDisplayed()
```

## Network Matchers

### toBeRequested
Expand Down
37 changes: 34 additions & 3 deletions playgrounds/mocha/test/specs/wdio-matchers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,27 +25,58 @@ describe('WebdriverIO Custom Matchers', () => {

describe('Element existence matchers', () => {
it('should verify element exists', async () => {
const searchButton = await $('.DocSearch-Button')
const searchButton = $('.DocSearch-Button')

await expect(searchButton).toExist()
await expect(await searchButton).toExist()
await expect(await searchButton).toBeExisting()
})

it('should verify all elements are existing', async () => {
const searchButton = $$('.DocSearch-Button')

await expect(searchButton).toExist()
await expect(searchButton).toBeExisting()
await expect(await searchButton).toExist()
await expect(await searchButton).toBeExisting()
})

it('should verify element does not exist', async () => {
const nonExistent = await $('.non-existent-element')

await expect(nonExistent).not.toExist()
await expect(await nonExistent).not.toExist()
})

it('should verify elements do not exist', async () => {
const nonExistent = $$('.non-existent-elements')

await expect(nonExistent).not.toExist()
await expect(await nonExistent).not.toExist()
})

})

describe('Element visibility matchers', () => {
it('should verify element is displayed', async () => {
const nav = await $('nav')
const nav = $('nav')

await expect(nav).toBeDisplayed()
await expect(await nav).toBeDisplayed()
})

it('should verify element is displayed in viewport', async () => {
const searchButton = await $('.DocSearch-Button')

await expect(searchButton).toBeDisplayedInViewport()
})

it('should verify elements are displayed', async () => {
const nav = $$('nav')

await expect(nav).toBeDisplayed()
await expect(await nav).toBeDisplayed()
await expect(await nav.filter(n => n.isExisting())).toBeDisplayedInViewport()
})
})

describe('Element state matchers', () => {
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/matchers/element/toBeClickable.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { executeCommandBe } from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import type { WdioElementOrArrayMaybePromise } from '../../types.js'

export async function toBeClickable(
received: WdioElementMaybePromise,
received: WdioElementOrArrayMaybePromise,
options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS
) {
this.expectation = this.expectation || 'clickable'
Expand Down
4 changes: 2 additions & 2 deletions src/matchers/element/toBeDisabled.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { executeCommandBe } from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import type { WdioElementOrArrayMaybePromise } from '../../types.js'

export async function toBeDisabled(
received: WdioElementMaybePromise,
received: WdioElementOrArrayMaybePromise,
options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS
) {
this.expectation = this.expectation || 'disabled'
Expand Down
4 changes: 2 additions & 2 deletions src/matchers/element/toBeDisplayed.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { executeCommandBe } from '../../utils.js'
import type { WdioElementMaybePromise } from '../../types.js'
import type { WdioElementOrArrayMaybePromise } from '../../types.js'
import { DEFAULT_OPTIONS_TO_BE_DISPLAYED } from '../../constants.js'

export async function toBeDisplayed(
received: WdioElementMaybePromise,
received: WdioElementOrArrayMaybePromise,
options: ExpectWebdriverIO.ToBeDisplayedOptions = DEFAULT_OPTIONS_TO_BE_DISPLAYED,
) {
this.expectation = this.expectation || 'displayed'
Expand Down
4 changes: 2 additions & 2 deletions src/matchers/element/toBeDisplayedInViewport.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { executeCommandBe } from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import type { WdioElementOrArrayMaybePromise } from '../../types.js'

export async function toBeDisplayedInViewport(
received: WdioElementMaybePromise,
received: WdioElementOrArrayMaybePromise,
options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS
) {
this.expectation = this.expectation || 'displayed in viewport'
Expand Down
4 changes: 2 additions & 2 deletions src/matchers/element/toBeEnabled.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { executeCommandBe } from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import type { WdioElementOrArrayMaybePromise } from '../../types.js'

export async function toBeEnabled(
received: WdioElementMaybePromise,
received: WdioElementOrArrayMaybePromise,
options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS
) {
this.expectation = this.expectation || 'enabled'
Expand Down
9 changes: 5 additions & 4 deletions src/matchers/element/toBeExisting.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { executeCommandBe } from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import type { WdioElementOrArrayMaybePromise } from '../../types.js'

export async function toExist(
received: WdioElementMaybePromise,
received: WdioElementOrArrayMaybePromise,
options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS
) {
this.expectation = this.expectation || 'exist'
this.verb = this.verb || ''
this.allowEmptyElements = true

await options.beforeAssertion?.({
matcherName: 'toExist', // TODO use this.matcher = this.matcher || toExist in v6.0.0 to fix matcherName issue with toBeExisting and toBePresent
Expand All @@ -25,13 +26,13 @@ export async function toExist(
return result
}

export function toBeExisting(el: WdioElementMaybePromise, options?: ExpectWebdriverIO.CommandOptions) {
export function toBeExisting(el: WdioElementOrArrayMaybePromise, options?: ExpectWebdriverIO.CommandOptions) {
this.expectation = 'existing'
this.verb = 'be'

return toExist.call(this, el, options)
}
export function toBePresent(el: WdioElementMaybePromise, options?: ExpectWebdriverIO.CommandOptions) {
export function toBePresent(el: WdioElementOrArrayMaybePromise, options?: ExpectWebdriverIO.CommandOptions) {
this.expectation = 'present'
this.verb = 'be'

Expand Down
4 changes: 2 additions & 2 deletions src/matchers/element/toBeFocused.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { executeCommandBe } from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import type { WdioElementOrArrayMaybePromise } from '../../types.js'

export async function toBeFocused(
received: WdioElementMaybePromise,
received: WdioElementOrArrayMaybePromise,
options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS
) {
this.expectation = this.expectation || 'focused'
Expand Down
6 changes: 3 additions & 3 deletions src/matchers/element/toBeSelected.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { executeCommandBe } from '../../utils.js'
import { DEFAULT_OPTIONS } from '../../constants.js'
import type { WdioElementMaybePromise } from '../../types.js'
import type { WdioElementOrArrayMaybePromise } from '../../types.js'

export async function toBeSelected(
received: WdioElementMaybePromise,
received: WdioElementOrArrayMaybePromise,
options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS
) {
this.expectation = this.expectation || 'selected'
Expand All @@ -24,7 +24,7 @@ export async function toBeSelected(
return result
}

export async function toBeChecked (el: WdioElementMaybePromise, options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS) {
export async function toBeChecked (el: WdioElementOrArrayMaybePromise, options: ExpectWebdriverIO.CommandOptions = DEFAULT_OPTIONS) {
this.expectation = 'checked'

await options.beforeAssertion?.({
Expand Down
2 changes: 2 additions & 0 deletions src/matchers/element/toHaveText.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ export async function toHaveText(
const commandResult = await executeCommandWithStrategy( {
unresolvedElements: received,
singleElementCompare: (element, _index) => compareElement(element, expectedValue, options),
isNot,
strategy: 'LegacyMultipleElements',
})
subject = commandResult.subject
actualText = commandResult.actual
Expand Down
Loading