Matchers support an element array returned from $$():
- Strict Index-based Matching: If an array of expected values is provided, it must match the elements' count; each value is checked at its index.
- If a single value is provided, every element is compared to it.
- Asymmetric matchers (e.g.,
expect.stringContaining) work within expected value arrays. A list matcher (expect.arrayContaining(),expect.arrayOf(),jasmine.arrayWithExactContents()) is not for one element: alone, it compares the values of all the elements; in an array of expected values, or withsome(), it throws (except intoHaveElementProperty, because a property can be a list).toHaveStyle,toHaveWidth,toHaveHeightandtoHaveChildrenalways throw with a list matcher: give one value, or an array with one value for each element.toBeElementsArrayOfSizealso throws with a list matcher: it counts the elements, so give one count (a number or aNumberMatcher). See Matching a subset of element values. - Assertion fails if no elements are found, except with
toBeElementsArrayOfSizeand existing matcherstoExist,toBeExistingandtoBePresent. - Options like
StringOptionsorHTMLOptionsapply to the whole array;NumberMatcherbehaves like any expected provided value. - The assertion passes only if all elements match.
- Using
.notmeans all elements must not match. - On failures, the element array is automatically re-fetched until the matcher passes or times out, ensuring reliability against dynamic DOM changes.
- Instead of
StringOptionsfor a single expected value, use RegExp or asymmetric matchers.- For
ignoreCaseuse RegEx (/MyExample/i) - For
containinguse Asymmetric Matchers (expect.stringContaining('Example'))
- For
The Browser Runner uses standard expect, only extended with the expect-webdriverio matchers: use expect.oneOf and expect.some there, see Browser Runner. Multi-remote is not supported, see Multiple Elements & Multi-remote.
You can pass any of these element types to expect:
ChainablePromiseArray(the non-awaited case)ElementArray(the awaited case)Element[](the filtered case)MultiRemoteElementArray(multi-remote$$()), where these rules apply per browser instance, see Multi-remote Support
Results of toHaveText on $$('li') for three lists:
| Expected value | Passes when | Coffee, Tea |
Tea, Tea |
Coffee, Tea, Milk |
|---|---|---|---|---|
'Tea' |
every element has this text | fails | passes | fails |
['Coffee', 'Tea'] |
same count, each element has the text at its index | passes | fails | fails |
expect.oneOf('Tea', 'Coffee') |
each element has one of the texts; a text can be missing | passes | passes | fails |
expect.arrayContaining(['Tea', 'Coffee']) |
each text is on at least one element; elements with other texts are allowed | passes | fails | passes |
jasmine.arrayWithExactContents(['Tea', 'Coffee']) (Jasmine) |
the same texts in any order, with no other text | passes | fails | fails |
expect.arrayOf(expect.stringMatching(/^(Tea|Coffee)$/)) |
every element has one of the texts; also passes with no element | passes | passes | fails |
some(elements) with 'Tea' |
at least one element has the text | passes | passes | passes |
For more granular or explicit per-element validation, use a parameterized test of your framework. Example in Mocha:
describe('Element at index of $$()', () => {
[
{ expectedText: 'one', index: 0 },
{ expectedText: 'two', index: 2 },
{ expectedText: 'four', index: 4 },
].forEach(({ expectedText, index }) => {
it(`element ${index} of $$('label') has the text "${expectedText}"`, async () => {
await expect($$('label')[index]).toHaveText(expectedText)
})
})
})import { some } from 'expect-webdriverio/api'
const elements = $$('myElements')
// Single expected value
// Every element in the list must match this exact text.
await expect(elements).toHaveText('myValue1');
// Every element in the list must match either option.
await expect(elements).toHaveText(/optionA|optionB/);
// NOT Equivalent to:
await expect(elements).toHaveText(['optionA', 'optionB']);
// but equivalent to
await expect(elements).toHaveText(expect.oneOf('optionA', 'optionB'));
// Multiple expected values
// Elements must match index-by-index: element 0 must be 'valueForIndex0' AND element 1 must be 'valueForIndex1'.
await expect(elements).toHaveText(['valueForIndex0', 'valueForIndex1']);
// Element at index 0 must match either `index0OptionA` or `index0OptionB` AND element 1 must match the exact string `index1OptionC`.
await expect(elements).toHaveText([
/index0OptionA|index0OptionB/,
'index1OptionC'
]);
// Equivalent as
await expect(elements).toHaveText([
expect.oneOf('index0OptionA','index0OptionB'),
'index1OptionC'
]);
// Negation with `.not`
// Every element in the list must NOT match the regex pattern.
await expect(elements).not.toHaveText(/forbiddenTextA|forbiddenTextB/);
// Allow to succeed only if one element matches (or not match)
// Succeeds if at least one element matches either 'optionA' or 'optionB'
await expect(some(elements)).toHaveText(expect.oneOf('optionA', 'optionB'));
// Succeeds if at least one element does not matches either 'forbiddenTextA' or 'forbiddenTextB'
await expect(some(elements)).not.toHaveText(/forbiddenTextA|forbiddenTextB/);
// Succeeds if the first element matches 'valueForIndex0' OR the second matches 'valueForIndex1'
await expect(some(elements)).toHaveText(['valueForIndex0', 'valueForIndex1']);