fix(dioxus): preserve undefined in execute() via __wdio_value__ envelope#412
fix(dioxus): preserve undefined in execute() via __wdio_value__ envelope#412goosewobbler wants to merge 1 commit into
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Standing release PR: #400 · 10 packages queued · open 15h 25m · ✅ ready to merge Release Preview — 13 packages
These changes will be added to the release PR (#400) when merged: ChangelogProject-wide changesFixed
@wdio/dioxus-bridge N/A → 1.0.1Fixed
@wdio/dioxus-service N/A → 1.0.1Fixed
@wdio/electron-cdp-bridge wdio-electron-cdp-bridge@v10.0.0 → 10.0.1Changed
@wdio/electron-service wdio-electron-service@v10.0.0 → 10.0.1Changed
@wdio/flutter-service v1.0.0-next.0 → 1.0.1Changed
@wdio/native-cdp-bridge v0.1.0-next.0 → 0.1.1Changed
@wdio/native-mobile-core v1.0.0 → 1.0.1Changed
@wdio/native-spy wdio-native-spy@v1.1.0 → 1.1.1Changed
@wdio/native-types wdio-native-types@v2.3.1 → 2.3.2Changed
@wdio/native-utils wdio-native-utils@v2.4.0 → 2.4.1Changed
@wdio/react-native-service v1.0.0-next.0 → 1.0.1Changed
dioxus-package-test-app v0.1.0 → 0.1.1Changed
Also bumped (sync versioning)
After merge — predicted release
Updated automatically by ReleaseKit |
|
| Filename | Overview |
|---|---|
| packages/dioxus-bridge/guest-js/index.ts | Correctly replaces result ?? null with the { __wdio_value__: result } envelope on the success path; error path still sends null as before. Logic is sound — JSON.stringify drops the key for undefined, preserves it for null. |
| packages/dioxus-service/src/commands/execute.ts | Adds unwrapEmbeddedResult and applies it to both execute and runInterceptorScript. Correct for the embedded path; the key-absent fallthrough silently returns undefined for any plain object that isn't the envelope, which could mislead callers in non-embedded contexts. |
| packages/dioxus-service/test/commands/execute.spec.ts | Tests correctly updated to use the new envelope stubs; new cases added for undefined and null return values covering both execute and runInterceptorScript. |
| packages/dioxus-service/test/mock.spec.ts | Call-data stubs correctly wrapped in the __wdio_value__ envelope to match the new runInterceptorScript return contract. |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant Test as Test (dioxus-service)
participant Exec as execute.ts
participant WD as browser.execute (WebDriver)
participant Poll as guest-js polling loop
participant Script as User Script
Test->>Exec: execute(browser, fn, ...args)
Exec->>WD: browser.execute(wrappedScript)
WD->>Poll: queue command (id, script, args)
Poll->>Script: AsyncFunction(...args)
Script-->>Poll: result (any value, incl. undefined)
Note over Poll: error ? null : { __wdio_value__: result }
Poll->>WD: invoke('__embedded_result', envelope)
WD-->>Exec: "raw = { __wdio_value__: X } or {}"
Note over Exec: unwrapEmbeddedResult(raw)
Exec-->>Test: ReturnValue
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant Test as Test (dioxus-service)
participant Exec as execute.ts
participant WD as browser.execute (WebDriver)
participant Poll as guest-js polling loop
participant Script as User Script
Test->>Exec: execute(browser, fn, ...args)
Exec->>WD: browser.execute(wrappedScript)
WD->>Poll: queue command (id, script, args)
Poll->>Script: AsyncFunction(...args)
Script-->>Poll: result (any value, incl. undefined)
Note over Poll: error ? null : { __wdio_value__: result }
Poll->>WD: invoke('__embedded_result', envelope)
WD-->>Exec: "raw = { __wdio_value__: X } or {}"
Note over Exec: unwrapEmbeddedResult(raw)
Exec-->>Test: ReturnValue
Reviews (1): Last reviewed commit: "fix(dioxus): preserve undefined in execu..." | Re-trigger Greptile
| if (raw !== null && typeof raw === 'object' && !Array.isArray(raw)) { | ||
| const envelope = raw as Record<string, unknown>; | ||
| if ('__wdio_value__' in envelope) { | ||
| return envelope['__wdio_value__'] as T; | ||
| } | ||
| // Key absent: the result was undefined (omitted by JSON.stringify) | ||
| return undefined as unknown as T; | ||
| } | ||
| return raw as T; |
There was a problem hiding this comment.
Silent
undefined for any non-envelope object in non-embedded contexts
unwrapEmbeddedResult returns undefined for ANY plain object that lacks __wdio_value__, not just the serialized-undefined empty envelope {}. This is correct when browser.execute is always routed through the embedded Dioxus polling loop (which guarantees the envelope). However, the string-script overload of execute carries no such guarantee — the file header documents it as providing "standard WDIO behaviour where the body is wrapped as function() { ${body} }", implying it can be called against a non-embedded WebDriver. If that path is ever exercised and the script returns a plain object (e.g., { x: 1 }), unwrapEmbeddedResult would fall into the key-absent branch and silently return undefined instead of the actual value. Adding an explicit guard (e.g., checking Object.keys(envelope).length === 0 for the empty-envelope case, or only applying unwrapEmbeddedResult when the embedded bridge is known to be active) would make the assumption explicit and prevent silent corruption.
Summary
browser.dioxus.execute(...)returningundefinedpreviously deliverednullto the test;nullandundefinedwere indistinguishableresult ?? null, collapsing both tonull@wdio/tauri-service's__wdio_value__envelope — wrap the result before posting it through__embedded_result; unwrap inexecute.tsandrunInterceptorScriptJSON.stringifyomitsundefinedobject properties,{ __wdio_value__: undefined }serialises to{}(key absent) while{ __wdio_value__: null }serialises to{"__wdio_value__":null}(key present), making the two distinguishable on receiptmock.spec.tscall-data stubs to match the new envelope shapeCloses #409
Test plan
pnpm --filter @wdio/dioxus-service testpasses (all 12 execute + 11 mock tests green)pnpm --filter @wdio/dioxus-bridge testpassesresetAllMocks(),execute(() => invoke('cmd'))returnsundefined, notnull(downstream example app mocking spec)🤖 Generated with Claude Code