$$
$$ fetches every element that matches a selector. It returns a WebdriverIO.ElementArray
immediately: a real array (Array.isArray is true) that stays thenable until the query
finishes. custom$$, react$$ and shadow$$ return the same kind of list, including when
you call them on an element ($('parent').$$('child')).
Await the list when you want it resolved. length is then a number, each index is an element,
and a normal for...of loop works:
const buttons = await $$('button')
console.log(buttons.length) // 3
for (const button of buttons) {
console.log(await button.getText())
}
The query does not run until something reads the list (await, for await, .length, an index,
or an iterator method such as map). Until then:
lengthis aPromise<number>. Useawait $$('button').length, not$$('button').length > 0.$$('button')[0]is a chainable element, soawait $$('button')[0].getText()works without awaiting the list first.for await (const button of $$('button'))yields each element.for (const button of $$('button'))throwsCannot synchronously iterate over an element list that has not resolved yet. Await the list first, or usefor await.
An index past the end of the list waits and refetches until waitforTimeout, the same way a
missing single element does. That wait still happens after the list has resolved
(const items = await $$('li'); await items[items.length]). A slice
or a filter does not: $$('li').slice(0, 2)[3] stays outside that window, and an index past a
filtered list does not resolve to an element the filter excluded. .at() uses the same index
conversion as a plain array, so .at(1.5) is element 1 and .at(NaN) is element 0.
Using the wdio testrunner this command is a global variable, see Globals
for more information. Using WebdriverIO within a standalone
script it will be located on the browser object instead (e.g. browser.$$).
You can chain $ or $$ together without wrapping individual commands into await in order
to walk down the DOM tree, e.g.:
const imageSrc = await $$('div')[1].nextElement().$$('img')[2].getAttribute('src')
Iterating over elements​
The list also provides asynchronous versions of the Array iteration methods forEach, map,
find, findIndex, some, every, filter and reduce. They accept async callbacks and can be
called on $$ directly. filter and filterSeries return a new ElementArray, so the selector and
the command that found the elements are kept:
const texts = await $$('h3').map((h3) => h3.getText())
for await (const img of $$('img')) {
console.log(await img.getAttribute('src'))
}
Every method except reduce also has a *Series variant (forEachSeries, mapSeries, and so on).
The base method runs all callbacks concurrently, while the *Series variant runs one callback at a
time, in list order. reduce always runs one callback at a time. Use a *Series variant when the order
of your interactions with the page matters.
The concurrent find and findIndex resolve with the first match to finish, which is not necessarily the
earliest match in the list, and the concurrent find, findIndex, some and every keep running the
remaining callbacks after the result is known. Use the *Series variant to get the earliest match and
stop there.
For more information on how to select specific elements, check out the Selectors guide.
Usage​
await browser.$$(selector)
Parameters​
| Name | Type | Details |
|---|---|---|
selector | String, Function | selector or JS Function to fetch multiple elements |
Examples​
loading...
loading...
loading...
loading...
Returns​
- <WebdriverIO.ElementArray>