Note: Thanks for putting together this crate. This is a much needed piece of functionality.
I ran into this issue trying to use it, and asked Claude to see if it could provide a solution. Here's what it came up with.
The text below was created by Claude Opus 4.6.
Summary
The execute_sync (and likely execute_async) handler in tauri-wd does not resolve W3C WebDriver element references back to DOM nodes before passing them to the injected script. This causes every WebdriverIO command that internally uses browser.execute(script, element) to fail on macOS WKWebView.
Error
WebDriverError: Argument 1 ('other') to Node.contains must be an instance of Node
when running "execute/sync" with method "POST"
Root Cause
When WebdriverIO calls browser.execute(script, element), it serializes the element per the W3C WebDriver spec as:
{"element-6066-11e4-a52e-4f735466cecf": "<uuid>"}
Per the spec, the WebDriver server must deserialize this JSON object back into a real DOM element before passing it to the JavaScript function.
The perform_actions handler in tauri-wd does this correctly — it walks action arguments, resolves the UUID to a (selector, index) pair, and the plugin uses document.querySelectorAll(selector)[index] to get the actual DOM node.
However, execute_sync does not do this. It directly serializes args to JSON and injects them into the script:
let args_json = serde_json::to_string(&body.args).unwrap();
let script = format!(
"var __args={args_json};return (function(){{{}}}).apply(null,__args)",
body.script
);
So the function receives a plain JavaScript object {"element-6066-11e4-a52e-4f735466cecf": "some-uuid"} instead of a DOM Node.
Impact
WebdriverIO v9 uses browser.execute() with element arguments for many fundamental operations:
isDisplayed() / waitForDisplayed() — runs the isElementDisplayed script which calls document.contains(element)
checkVisibility() — passes element to elem.checkVisibility()
getComputedStyle() — passes element to window.getComputedStyle(elem)
This means virtually every element interaction fails, making E2E tests unusable.
Suggested Fix
The execute_sync handler should walk the args array before forwarding to the plugin. For each argument that is a JSON object with the key "element-6066-11e4-a52e-4f735466cecf", look up the UUID in session.elements and replace it with inline JavaScript that resolves the element in the browser, e.g.:
// Replace W3C element reference with a resolution expression
let resolution_js = format!(
"document.querySelectorAll('{}')[{}]",
elem_ref.selector, elem_ref.index
);
This matches the pattern already used in perform_actions.
Environment
tauri-webdriver-automation v0.1.3
- WebdriverIO v9.19.0
- macOS 15 (Sequoia), WKWebView
- Tauri v2.10.2
Reproduction
Any WebdriverIO test that interacts with elements will trigger this. Minimal example:
const el = await $("#some-element");
await expect(el).toBeDisplayed(); // fails with Node.contains error
Note: Thanks for putting together this crate. This is a much needed piece of functionality.
I ran into this issue trying to use it, and asked Claude to see if it could provide a solution. Here's what it came up with.
The text below was created by Claude Opus 4.6.
Summary
The
execute_sync(and likelyexecute_async) handler intauri-wddoes not resolve W3C WebDriver element references back to DOM nodes before passing them to the injected script. This causes every WebdriverIO command that internally usesbrowser.execute(script, element)to fail on macOS WKWebView.Error
Root Cause
When WebdriverIO calls
browser.execute(script, element), it serializes the element per the W3C WebDriver spec as:{"element-6066-11e4-a52e-4f735466cecf": "<uuid>"}Per the spec, the WebDriver server must deserialize this JSON object back into a real DOM element before passing it to the JavaScript function.
The
perform_actionshandler intauri-wddoes this correctly — it walks action arguments, resolves the UUID to a(selector, index)pair, and the plugin usesdocument.querySelectorAll(selector)[index]to get the actual DOM node.However,
execute_syncdoes not do this. It directly serializesargsto JSON and injects them into the script:So the function receives a plain JavaScript object
{"element-6066-11e4-a52e-4f735466cecf": "some-uuid"}instead of a DOM Node.Impact
WebdriverIO v9 uses
browser.execute()with element arguments for many fundamental operations:isDisplayed()/waitForDisplayed()— runs theisElementDisplayedscript which callsdocument.contains(element)checkVisibility()— passes element toelem.checkVisibility()getComputedStyle()— passes element towindow.getComputedStyle(elem)This means virtually every element interaction fails, making E2E tests unusable.
Suggested Fix
The
execute_synchandler should walk theargsarray before forwarding to the plugin. For each argument that is a JSON object with the key"element-6066-11e4-a52e-4f735466cecf", look up the UUID insession.elementsand replace it with inline JavaScript that resolves the element in the browser, e.g.:This matches the pattern already used in
perform_actions.Environment
tauri-webdriver-automationv0.1.3Reproduction
Any WebdriverIO test that interacts with elements will trigger this. Minimal example: