From 52edef225b7feccaf54cc9f06fb20b193102e93e Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 3 Jan 2026 21:37:06 +0000 Subject: [PATCH] fix: tolerate trailing semicolons in browser evaluate --- CHANGELOG.md | 1 + src/browser/pw-tools-core.test.ts | 37 +++++++++++++++++++++++++++++++ src/browser/pw-tools-core.ts | 6 +++-- 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 416eeb69b1..80cecc65c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Discord: emit system events for reaction add/remove with per-guild reaction notifications (off|own|all|allowlist) (#140) — thanks @thewilloftheshadow. ### Fixes +- Browser tools: tolerate trailing semicolons in evaluate expressions (#153) — thanks @azade-c. - Auto-reply: drop final payloads when block streaming to avoid duplicate Discord sends. - Bash tool: default auto-background delay to 10s. - Telegram: chunk block-stream replies to avoid “message is too long” errors (#124) — thanks @mukhtharcm. diff --git a/src/browser/pw-tools-core.test.ts b/src/browser/pw-tools-core.test.ts index 0275a1fac3..d28e47dfb4 100644 --- a/src/browser/pw-tools-core.test.ts +++ b/src/browser/pw-tools-core.test.ts @@ -232,4 +232,41 @@ describe("pw-tools-core", () => { expect(dismiss).toHaveBeenCalled(); expect(accept).not.toHaveBeenCalled(); }); + + it("evaluates expressions with trailing semicolons", async () => { + const evaluate = vi.fn( + async (fn: (body: string) => unknown, body: string) => fn(body), + ); + currentPage = { evaluate }; + + const mod = await importModule(); + const result = await mod.evaluateViaPlaywright({ + cdpUrl: "http://127.0.0.1:18792", + fn: "() => 42;", + }); + + expect(result).toBe(42); + expect(evaluate).toHaveBeenCalled(); + }); + + it("evaluates element expressions with trailing semicolons", async () => { + const evaluate = vi.fn( + async ( + fn: (el: { tagName: string }, body: string) => unknown, + body: string, + ) => fn({ tagName: "DIV" }, body), + ); + currentRefLocator = { evaluate }; + currentPage = {}; + + const mod = await importModule(); + const result = await mod.evaluateViaPlaywright({ + cdpUrl: "http://127.0.0.1:18792", + fn: "(el) => el.tagName;", + ref: "1", + }); + + expect(result).toBe("DIV"); + expect(sessionMocks.refLocator).toHaveBeenCalledWith(currentPage, "1"); + }); }); diff --git a/src/browser/pw-tools-core.ts b/src/browser/pw-tools-core.ts index 9d6be25204..d6b37b30bb 100644 --- a/src/browser/pw-tools-core.ts +++ b/src/browser/pw-tools-core.ts @@ -207,6 +207,8 @@ export async function evaluateViaPlaywright(opts: { }): Promise { const fnText = String(opts.fn ?? "").trim(); if (!fnText) throw new Error("function is required"); + const fnBody = fnText.replace(/[;\s]+$/g, ""); + if (!fnBody) throw new Error("function is required"); const page = await getPageForTargetId(opts); ensurePageState(page); if (opts.ref) { @@ -226,7 +228,7 @@ export async function evaluateViaPlaywright(opts: { } `, ) as (el: Element, fnBody: string) => unknown; - return await locator.evaluate(elementEvaluator, fnText); + return await locator.evaluate(elementEvaluator, fnBody); } // Use Function constructor at runtime to avoid esbuild adding __name helper // which doesn't exist in the browser context @@ -242,7 +244,7 @@ export async function evaluateViaPlaywright(opts: { } `, ) as (fnBody: string) => unknown; - return await page.evaluate(browserEvaluator, fnText); + return await page.evaluate(browserEvaluator, fnBody); } export async function armFileUploadViaPlaywright(opts: {