From e8a4ee4c35af34cf8d09cc451e9f3093adfc3231 Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Sun, 24 Aug 2025 13:43:05 +0900 Subject: [PATCH] fix: escape scripts in addInitScript (#14717) If the script passed to addInitScript contains template literals then we'll run into problems where we try to embed it in another template literal hence we need to escape the script first. The E2E test added in this patch fails without the corresponding code change. --- e2e/wdio/headless/test.e2e.ts | 19 +++++++++++++++++++ .../src/commands/browser/addInitScript.ts | 3 ++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/e2e/wdio/headless/test.e2e.ts b/e2e/wdio/headless/test.e2e.ts index 1606a4fb4..3f54d095f 100644 --- a/e2e/wdio/headless/test.e2e.ts +++ b/e2e/wdio/headless/test.e2e.ts @@ -430,6 +430,25 @@ describe('main suite 1', () => { expect(await browser.execute(() => Math.random())).not.toBe(42) }) + it('should escape the init script', async () => { + type WindowWithHello = Window & { + sayHello?: (name: string) => string; + } + const script = await browser.addInitScript(() => { + (window as WindowWithHello).sayHello = (name) => + `Hello ${name}` + }) + + await browser.url('https://webdriver.io') + expect( + await browser.execute(() => + (window as WindowWithHello).sayHello!('there'), + ), + ).toBe('Hello there') + + await script.remove() + }) + it('passed on callback function', async () => { const script = await browser.addInitScript((num, str, bool, emit) => { setTimeout(() => emit(JSON.stringify([num, str, bool])), 500) diff --git a/packages/webdriverio/src/commands/browser/addInitScript.ts b/packages/webdriverio/src/commands/browser/addInitScript.ts index 907fd495e..cc811a0ee 100644 --- a/packages/webdriverio/src/commands/browser/addInitScript.ts +++ b/packages/webdriverio/src/commands/browser/addInitScript.ts @@ -113,8 +113,9 @@ export async function addInitScript ( const serializedParameters = (args || []).map((arg: unknown) => JSON.stringify(arg)) const context = await this.getWindowHandle() + const src = 'return ' + script.toString() const fn = `(emit) => { - const closure = new Function(\`return ${script.toString()}\`) + const closure = new Function(${JSON.stringify(src)}) return closure()(${serializedParameters.length ? `${serializedParameters.join(', ')}, emit` : 'emit'}) }` const channel = btoa(fn.toString())