/** * Cross-protocol browser.execute() wrapper. * * WebDriverIO's devtools protocol (CDP via puppeteer-core) breaks when * browser.execute() receives a function argument because: * 1. WebDriverIO prepends a polyfill before `return (fn).apply(null, arguments)` * 2. The devtools executeScript trims the script, finds no leading `return`, * and passes it directly to eval() * 3. eval() fails with "Illegal return statement" because `return` is outside * a function body (the polyfill lines precede it) * * Fix: always pass a string expression to browser.execute(). Arguments are * JSON-serialized and inlined into the script — no reliance on the `arguments` * object which is protocol-dependent. * * Works identically with: * - WebDriver protocol (Tauri via tauri-driver) * - devtools/CDP protocol (Electrobun via CEF) */ import { browser } from '@wdio/globals'; /** * Execute a function in the browser, cross-protocol safe. * * Usage mirrors browser.execute(): * exec(() => document.title) * exec((sel) => document.querySelector(sel) !== null, '.my-class') * exec((a, b) => a + b, 1, 2) */ export async function exec(fn: (...args: any[]) => R, ...args: any[]): Promise { const fnStr = fn.toString(); const serializedArgs = args.map(a => JSON.stringify(a)).join(', '); // Wrap as an IIFE expression — no `return` at the top level const script = `return (${fnStr})(${serializedArgs})`; return browser.execute(script) as Promise; } /** * Skip a test programmatically — works with both protocols. * * Mocha's this.skip() requires a non-arrow `function()` context. In * WebDriverIO hooks (beforeTest), `this` may not carry the Mocha context * with devtools protocol. This helper uses the same mechanism but is * callable from any context that has the Mocha `this`. * * Usage inside `it('...', async function () { ... })`: * if (condition) { skipTest(this); return; } */ export function skipTest(ctx: Mocha.Context): void { ctx.skip(); }