Skip to content

POC: Support multi-remote take 2 - #2172

Closed
dprevost-LMI wants to merge 75 commits into
webdriverio:mainfrom
dprevost-LMI:support-multiremote-take-2
Closed

dprevost-LMI wants to merge 75 commits into
webdriverio:mainfrom
dprevost-LMI:support-multiremote-take-2

Conversation

@dprevost-LMI

@dprevost-LMI dprevost-LMI commented Aug 4, 2026 •

Copy link
Copy Markdown
Collaborator

Multi-Remote Support

Support multi-remote for Browser and element matchers.
Fixes #106

Browser Matchers

await expect(multiRemoteBrowser).toHaveTitle('WebdriverJS Testpage')
await expect(multiRemoteBrowser).toHaveTitle({
    chrome: expect.stringContaining('WebdriverJS'),
    firefox: expect.stringContaining('WebdriverJS')
})
await expect(multiRemoteBrowser.select('firefox')).toHaveTitle({
    firefox: expect.stringContaining('WebdriverJS')
})

Element(s) Matchers

toBe Matchers

await expect(multiRemoteBrowser.$('h1')).toBeDisplayed()
await expect(multiRemoteBrowser.$$('h1')).toBeDisplayed()

// Allow a subset of browsers
await expect(multiRemoteBrowser.$('h1').select('firefox')).toBeDisplayed()
await expect(multiRemoteBrowser.select('firefox').$$('h1')).toBeDisplayed() 
// Note: One day we could have select API directly on MultiRemoteElement[]

toHave Matchers

// Single element
await expect(multiRemoteBrowser.$('h1')).toHaveText('WebdriverJS Testpage')
await expect(h1).toHaveText({
     'firefox': 'WebdriverJS Testpage',
     'chrome': 'WebdriverJS Testpage'
  })

// Multiple elements
await expect(multiRemoteBrowser.$$('h1')).toHaveText(['Testpage', 'Test CSS Attributes'])
await expect(h1).toHaveText({
    'firefox': ['WebdriverJS Testpage', 'Test CSS Attributes'],
    'chrome': ['WebdriverJS Testpage', 'Test CSS Attributes']
})
await expect(h1.select('firefox')).toHaveText({
    'firefox': ['WebdriverJS Testpage', 'Test CSS Attributes']
})

Error messages

toBe multi-remote single element

Expect multi-remote<chrome, firefox>.$(`h1`) to be displayed

- Expected  - 1
+ Received  + 1

  Object {
    "chrome": "displayed",
-   "firefox": "displayed",
+   "firefox": "not displayed",
  }

toHave multi-remote single elements

Expect multi-remote<chrome, firefox>.$(`h1`) to have text

- Expected  - 1
+ Received  + 1

  Object {
    "chrome": "WebdriverJS Testpage",
-   "firefox": "ebdriverJS Testpage",
+   "firefox": "WebdriverJS Testpage",
  }

toHave multi-remote multiple elements

Expect multi-remote<chrome, firefox>.$$(`h1`) to have text

- Expected  - 2
+ Received  + 2

  Object {
    "chrome": Array [
-     "WebdriverJS Testpage",
-     "Test CSS Attributes",
+     "",
+     "Open Source and Open Governed",
    ],
    "firefox": Array [
      "WebdriverJS Testpage",
      "Test CSS Attributes",
    ],
  }

@dprevost-LMI
dprevost-LMI marked this pull request as ready for review August 4, 2026 10:23
@dprevost-LMI
dprevost-LMI marked this pull request as draft August 4, 2026 10:23
Comment thread src/matchers/browser/toHaveTitle.ts Outdated
Comment thread src/matchers/browser/toHaveTitle.ts Outdated
Comment thread package.json Outdated
Comment thread playgrounds/multi-remote-mocha/test/specs/wdio-matchers.test.ts Outdated
Comment thread package.json
@dprevost-LMI
dprevost-LMI force-pushed the support-multiremote-take-2 branch from cecd879 to 7ec8819 Compare August 6, 2026 00:59
@dprevost-LMI
dprevost-LMI force-pushed the support-multiremote-take-2 branch from 7ec8819 to b48142d Compare August 18, 2026 19:23
@dprevost-LMI
dprevost-LMI marked this pull request as ready for review August 21, 2026 02:41
@dprevost-LMI
dprevost-LMI marked this pull request as draft August 21, 2026 02:41
@webdriverio webdriverio deleted a comment from greptile-apps Bot Aug 21, 2026
Comment thread src/matchers/browser/toHaveTitle.ts Outdated
@dprevost-LMI dprevost-LMI reopened this Aug 25, 2026
@dprevost-LMI
dprevost-LMI force-pushed the support-multiremote-take-2 branch from f6c73cd to 91b3356 Compare August 25, 2026 11:37
@dprevost-LMI dprevost-LMI changed the title POC: Support multiremote take 2 POC: Support multi-remote take 2 Aug 26, 2026
@dprevost-LMI
dprevost-LMI force-pushed the support-multiremote-take-2 branch from 978dd57 to 3c3cb10 Compare August 27, 2026 17:17
@dprevost-LMI
dprevost-LMI force-pushed the support-multiremote-take-2 branch from aee7746 to 2455c57 Compare August 29, 2026 23:52
Comment thread src/util/elementsUtil.ts Outdated
@dprevost-LMI
dprevost-LMI force-pushed the support-multiremote-take-2 branch from 808d8ab to 4fa1b72 Compare August 30, 2026 01:21
@dprevost-LMI
dprevost-LMI marked this pull request as ready for review August 31, 2026 14:17
@dprevost-LMI
dprevost-LMI marked this pull request as draft August 31, 2026 14:17
@dprevost-LMI

dprevost-LMI commented Aug 31, 2026 •

Copy link
Copy Markdown
Collaborator Author

@greptileai, analyze this PR, all the files and the changes, in depth and report any inconsistencies, bugs, or leftover TODOs and incomplete tasks. Ensure you communicate your findings!

@webdriverio webdriverio deleted a comment from greptile-apps Bot Aug 31, 2026
@greptile-apps

greptile-apps Bot commented Aug 31, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 5/5

[Medium risk] Extends element matchers to support multi-remote browser instances.

The PR appears safe to merge; the remaining documentation inaccuracy is non-blocking.

Findings

  1. P2 Empty arrays do not fail immediately ▶

Summary

This PR adds multi-remote element assertions, per-instance expectations, element-array retry handling, types, tests, and documentation. The latest changes make multi-remote browser .not assertions strict and add tests for that behavior.

Reviews (14) · Last reviewed commit: "fix: restore part 2 review fixes lost wh..."

@dprevost-LMI
dprevost-LMI force-pushed the support-multiremote-take-2 branch from 32f6f26 to 8721bb4 Compare September 7, 2026 12:49
@dprevost-LMI
dprevost-LMI force-pushed the support-multiremote-take-2 branch 2 times, most recently from 99e80f8 to b1fe68d Compare September 23, 2026 11:21
@dprevost-LMI
dprevost-LMI marked this pull request as ready for review September 23, 2026 12:41
@dprevost-LMI
dprevost-LMI marked this pull request as draft September 23, 2026 12:41
Comment thread src/matchers/elements/toBeElementsArrayOfSize.ts
dprevost-LMI and others added 23 commits September 25, 2026 14:03
- Detect MultiRemoteElement via `selector` (not `getElement`) so it is not
  confused with MultiRemoteBrowser or multi-remote arrays
- Await per-element results in the multi-remote $$() branch instead of an
  un-awaited async forEach, and store actuals by index to keep order stable
- Treat the WDIO_ENABLE_MULTI_REMOTE_ELEMENT_ARRAY shape like plain
  MultiRemoteElement[] in the legacy and arrayContaining guards
- Fix enhanceErrorBe indexing actuals by instance name
- Mock multi-remote $$() as zipped MultiRemoteElement wrappers, matching
  WebdriverIO runtime, with optional ElementArray decoration

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ations

- Match multi-remote browser expected values by instance name, not key order
- Fail (with success: isNot + abort) on structurally invalid browser
  expectations so `.not` no longer passes silently
- Multi-remote $() / $$(): fail strictly on missing/unknown instances and
  per-instance length mismatch, while still comparing what we can
- Support MultiRemoteElementArray (incl. empty) in enhanceErrorBe
- Keep retrying an empty MultiRemoteElementArray since it can be refetched
- Remove unused MultiRemoteValuesMatcher

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rt refetch

- toBeElementsArrayOfSize: support MultiRemoteElement[] and
  MultiRemoteElementArray, counting elements per browser instance against a
  single size or one size per instance (strict on missing/unknown instances),
  with retries and synchronization of the received array
- Warn once that refetching a plain MultiRemoteElement[] from the global
  multiRemoteBrowser is best effort, recommending
  WDIO_ENABLE_MULTI_REMOTE_ELEMENT_ARRAY=true; skip it when the global is absent
- Share hasSameInstanceNames in multiRemoteUtils
- Add multi-remote typings, type tests and docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, types

- isBrowser: handle the @wdio/globals proxy `bound Browser` constructor name
  (single browser messages printed [object Object])
- Any plain object expected value is per-instance values, so unknown or
  misspelled instance names fail strictly; toHaveStyle/toHaveSize/
  toHaveElementProperty opt out as their expected value can be an object
- Compare each instance on its own elements (zipped $$() results may differ
  in length), with per-instance expected arrays and failure message diffs
- Array expected on multi-remote $(): compared when the matcher supports it,
  else strict failure; structural failures abort instead of retrying
- some(): at least one matching element in every instance
- arrayContaining: supported on multi-remote $$(), per instance
- Browser matchers: apply string options to oneOf (also nested per
  instance), never compare against an unsupported array
- Number matchers (toHaveChildren/Width/Height): support per-instance values
  instead of throwing or silently passing with the default { gte: 1 }
- Per-instance values on non multi-remote elements fail strictly
- Best-effort refetch: an empty refetch keeps the received elements so the
  selector remains available; empty plain array accepts per-instance sizes
- Bypass async `every` of MultiRemoteElementArray in isElementArrayLike
- Types: $$() array signatures and per-instance values for multi-remote,
  array-only matchers reject a single MultiRemoteElement, optional
  toHaveLocalStorageItem value on multi-remote
- Tests: fix e2e message regexes and flaky refetch test, mocks for
  multi-remote $() and getUrl/getTitle spies, re-enable coverage thresholds

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Add docs/MultiRemote.md: configuration (feature flag and WebdriverIO
  environment variables), instance names, strict expected values, browser
  and element matchers, retries, error messages, limitations and
  alternatives (ported from the previous multi-remote draft)
- API.md: feature flags & environment variables section, multi-remote notes
  and links instead of "not yet supported"
- README: multi-remote feature, WebdriverIO v9.31.5 requirement, doc links,
  fix default options link
- MultipleElements.md: multi-remote element arrays, drop implemented
  "coming soon" item; link multi-remote playground

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e flag comment

- API.md: default `wait` is 2000ms, use `setDefaultOptions` instead of the
  deprecated `setOptions`
- useToHaveTextStrictMultiElementsCompareStrategy: drop the outdated
  "removed in v6.0.0" note, list what requires it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An empty plain MultiRemoteElement[] (without
WDIO_ENABLE_MULTI_REMOTE_ELEMENT_ARRAY) holds no instance names, so they were
derived from the expected keys and validated against themselves: incomplete,
misspelled or unknown instance names passed. Take them from the global
multiRemoteBrowser instead, falling back on the expected keys only without
injected globals.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Per-instance values were detected from plain objects, guessing for matchers
whose expected value is itself an object (toHaveStyle, toHaveSize,
toHaveElementProperty) by checking whether a key is an instance name, so an
instance named `width` or `height` turned a literal size into per-instance
values.

- Add expect.multiRemote() (also `multiRemote` from expect-webdriverio/api),
  an asymmetric matcher holding one expected value per instance, forwarding
  string options to nested values and printing per instance
- A plain object stays the per-instance shorthand where an object cannot be
  a valid expected value; for toHaveStyle, toHaveSize and
  toHaveElementProperty it is always a literal and per-instance values
  require expect.multiRemote(); drop the instance-name key heuristic
- Support it in the strategy, browser, number and size matchers, with the
  same failure messages as the plain object shorthand
- compareStyle: a non-string expected value is a mismatch instead of a crash
- Types, tests (incl. full failure message assertions) and docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Grant Chrome clipboard access only to the guinea-pig fixture origin and
localhost instead of every HTTPS origin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…EMENT_ARRAY

Add a dedicated section listing the best-effort behaviors of the plain
MultiRemoteElement[]: re-fetching from the global multiRemoteBrowser ignoring
parent and select() scope, no re-fetch without injected globals, no retry of
an initially empty result, and per-instance sizes on an empty result. Describe
the WebdriverIO environment variables as opt-in, without assuming a default
change in a future version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Validate against real Chrome and Firefox: different values per browser with
expect.multiRemote() and its plain object shorthand, per-browser failure
messages, strict missing/unknown/misspelled browser names (also with .not),
string options on nested expect.oneOf(), some() and arrayContaining per
browser, element counts differing per browser, per-browser NumberMatchers,
and toHaveStyle treating a plain object as a literal style.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…merge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…erge

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dprevost-LMI
dprevost-LMI force-pushed the support-multiremote-take-2 branch from aa78153 to c74f68d Compare September 25, 2026 18:25
dprevost-LMI added a commit that referenced this pull request Sep 25, 2026
* feat: Multi-remote element matchers ($() and $$())

Part 3/4 of the multi-remote support split from #2172.

- Multi-remote strict strategy for `$()` and `$$()`: each instance is compared on its own
  elements, with a single expected value, an index-based array, or one value per instance
  (`expect.multiRemote()` or plain object shorthand); `.not`, `some()` and
  `expect.arrayContaining()` apply per instance
- Re-fetching of multi-remote `$$()` between retries, reliable with
  `WDIO_ENABLE_MULTI_REMOTE_ELEMENT_ARRAY`, best effort without it
- `toBe*` and string matchers (text, attribute, HTML, class, id, href, value, computed
  label/role) support multi-remote elements; `toHaveText` requires the strict strategy flag
- Failure messages per instance, e.g. multi-remote<chrome, firefox>.$(`h1`)
- `toHaveSize`, `toHaveStyle` and `toHaveElementProperty` keep a plain object as a literal
- Types, type tests, docs and multi-remote playground specs

Number, size and object matchers and `toBeElementsArrayOfSize` come in part 4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: reject multi-remote elements under the legacy toHaveText strategy, also non-awaited and with .not

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: support the multi-remote per-instance shorthand on toHaveValue

toHaveValue delegates to toHaveElementProperty, which keeps a plain object as a literal
property value. A value is a string, so for toHaveValue a plain object is the per-instance
shorthand.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: remove leftover debug log from multi-remote playground spec

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
dprevost-LMI added a commit that referenced this pull request Sep 26, 2026
…rrayOfSize (#2231)

* feat: Multi-remote number, size and object matchers and toBeElementsArrayOfSize

Part 4/4 of the multi-remote support split from #2172.

- `toHaveWidth`, `toHaveHeight` and `toHaveChildren` support multi-remote elements, with one
  number or NumberMatcher for every instance or one per instance (`expect.multiRemote()` or
  plain object shorthand)
- `toHaveSize`, `toHaveStyle` and `toHaveElementProperty` keep a plain object as a literal
  value: per-instance values require `expect.multiRemote()`
- `toBeElementsArrayOfSize` counts the elements per instance, against a single size or one size
  per instance
- A plain object passed as per-instance styles is a mismatch instead of a crash
- Types, type tests, docs and multi-remote playground specs

Fixes #106

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: call matchers through a typed this context instead of .call({})

Calling through the context resolves the matcher overloads, so the tests are type-checked
against the real signatures, and invalid inputs are flagged with @ts-expect-error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: require every multi-remote instance to differ with .not toBeElementsArrayOfSize

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: never treat an empty array outside multi-remote as a multi-remote $$() in toBeElementsArrayOfSize

An empty regular ElementArray, or an empty array in a regular (non multi-remote) session,
now rejects per-instance sizes like a non-empty one, instead of passing against the
instance names taken from the expected value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix: reject number expected values mixing NumberOptions keys and multi-remote instance names

An object such as { eq: 2, firefox: 3 } was read as NumberOptions, silently dropping the
other keys. It now throws, pointing to expect.multiRemote(), which is also required when
instances are named like an option key. featureFlags is added to the NumberOptions keys.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor: require expect.multiRemote() for per-instance values of number matchers

A plain object is a legacy NumberOptions for toHaveWidth, toHaveHeight, toHaveChildren and
toBeElementsArrayOfSize, so telling it apart from per-instance values relied on guessing from
the key names (instance names colliding with option keys, mixed objects, missing keys).
Per-instance numbers now require expect.multiRemote(), which is never ambiguous. The plain
object shorthand is kept for the other matchers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test: validate the entire failure message of the multi-remote failing assertions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* feat: label multi-remote per-instance values in failure messages

The diff of a multi-remote failure now shows `Multi-remote values {` instead of `Object {`
for the per-instance values, so they are not mistaken for an object expected value.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@dprevost-LMI

dprevost-LMI commented Sep 26, 2026 •

Copy link
Copy Markdown
Collaborator Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for Multiremote

1 participant