The scriptExecRuntime wrapper is a self-contained JavaScript execution runtime designed to sit inside a browser (or WebView) and provide structured, Selenium/Appium-friendly execution semantics. Its features line up closely with the needs of executeScript / executeAsyncScript when Java is on the other side.
Below is a structured description of what it provides and why each piece exists.
Feature
-
Guards against multiple installations:
if (root.__wdRuntime) return;
Why it matters
- Selenium/Appium often injects helper scripts repeatedly.
- This ensures:
- No redefinition warnings
- No loss of state
- Deterministic behavior across executions
Feature
- Minimal global footprint:
__wdRuntime.runSync__wdRuntime.runAsync__wd.fail
Implementation
- Custom
namespace()helper safely creates nested objects. - Functions are registered by name rather than assigned anonymously.
Why it matters
- Avoids collisions with page JavaScript
- Keeps all Selenium infrastructure under a predictable prefix
- Compatible with older browsers (no
Object.assign, no modules)
__wdRuntime.runSync(fn, ...args)Behavior
-
Executes user code immediately
-
Returns a structured envelope:
{ "status": "ok", "value": ... }or
{ "status": "error", "exception": {...} }
Why
- Mirrors
executeScript - Avoids uncaught JS exceptions leaking into WebDriver internals
__wdRuntime.runAsync(fn, callback, timeoutMs, ...args)Behavior
- Supports:
- Promise-returning functions
- Explicit async logic
- Enforces single completion
- Enforces timeout
Why
- Matches Selenium’s
executeAsyncScript - Prevents:
- Double-callback bugs
- Hung scripts
- Silent promise rejections
Feature
createCompletion()guarantees:- Callback invoked once
- Timeout auto-failure
Script did not complete within 30000 msWhy
- Browsers do not guarantee async script completion
- Selenium requires exactly one callback
- Prevents grid/node deadlocks
Feature
-
Detects Promises via duck typing:
value && typeof value.then === 'function'
Behavior
then → okcatch → error envelope
Why
- Enables modern async JavaScript without requiring callbacks
- Works across:
- Native Promises
- WebKit / Safari
- Legacy Selenium nodes
Feature
-
All failures are normalized into a Java-style exception structure:
{ "status": "error", "exception": { "className": "java.lang.RuntimeException", "message": "...", "stack": "..." } }
Why
- Java clients expect:
classNamemessage- stack traces
- Enables lossless propagation from JS → Java
To guard against bad behavior, scripts wrapped and executed via scriptExecRuntime are constrained in the types of exceptions that they are allowed to return. By default, scripts are allowed to return AssertionError failures and any exception that's a subclass of RuntimeException. The ALLOWED_EXCEPTIONS setting enables you to specify additional exceptions that scripts are allowed to return.
The value of ALLOWED_EXCEPTIONS is a comma-delimited list of exception specifications. There are three supported formats:
- Fully qualified exception class names
- Ex:
com.mycompany.FooBarExceptionexplicitly allows scripts to return FooBarException
- Ex:
- Package wildcards using
*(package only)- Ex:
com.mycompany.exceptions.*allows scripts to return exceptions defined in package com.mycompany.exceptions
- Ex:
- Package subtree wildcards using
**- Ex:
com.mycompany.widget.**allows scripts to return exceptions defined in package com.mycompany.widget or its subpackages
- Ex:
Feature
__wd.fail("java.lang.IllegalStateException", "Something went wrong");Behavior
- Throws a tagged internal exception
- Caught and translated into a Java exception envelope
- Preserves JS stack trace
Why
- Allows test authors to:
- Fail intentionally
- Map JS failures to specific Java exception types
- Avoids abusing
throw new Error(...)
Feature
-
Tagged exception marker:
__wdUserFailure: true
Behavior
- User-triggered failures are handled differently from runtime errors
- Prevents user errors from being wrapped as generic JS runtime failures
Why
- Enables semantic failures:
- Assertion-like behavior
- Controlled test aborts
- Keeps infrastructure errors distinct
Feature
-
Manual stack capture via:
throw new Error();
Why
- Some WebDriver environments:
- Drop stack traces
- Normalize exceptions poorly
- This guarantees stack availability when possible
Notable constraints
- No ES6 syntax
- No modules
- No arrow functions
- No
async/await
Why
- Runs in:
- Safari iOS
- Embedded WebViews
- Older Selenium Grid 3 nodes
- Matches the lowest common denominator
Feature
-
Both sync and async variants forward extra arguments:
fn.apply(null, args)
Why
- Aligns with Selenium’s ability to pass arguments into scripts
- Avoids closure hacks
This runtime wrapper provides:
- ✅ Idempotent injection
- ✅ Safe global namespacing
- ✅ Unified sync/async execution
- ✅ Promise support
- ✅ Timeout enforcement
- ✅ Java-compatible error envelopes
- ✅ Explicit, typed user failures
- ✅ Stack trace preservation
- ✅ Selenium 3 / Grid / Safari compatibility
In short, it acts as a mini execution kernel that bridges JavaScript and Java cleanly, predictably, and defensively—exactly what Selenium/Appium scripting needs in hostile browser environments.
Written with StackEdit.