Edit: It turns out that the following is an issue caused by running the code with tsx, not an issue in playwright. To reproduce, run the code below with npx tsx <file-with-code>.ts.
You can get it to work by defining __name yourself:
declare global {
interface Window {
__name: (func: Function)=> Function
}
}
//... Once you have your page or context:
await context.addInitScript(() => {
window.__name = (func) => func;
})
Related issues:
Original question
I have a playwright Page object on which I want to evaluate some code. Since I need to do one thing several times inside the evaluation, I want to define that piece of code in a function like so:
await page.evaluate(() => {
const foo = (): string => "foo";
return foo();
});
But whenever I run anything that assigns a function to a variable, I get the following error during execution.
await page.evaluate: ReferenceError: __name is not defined
at eval (eval at evaluate (:226:30), <anonymous>:1:16)
at UtilityScript.evaluate (<anonymous>:228:17)
at UtilityScript.<anonymous> (<anonymous>:1:44)
I can fix it by assigning the function to something like window.foo = () => "foo" and then calling that. But I don't understand why this is an error in the first place.
It does not make a difference whether I use an arrow function or not.
If I just define a non-callable variable, I can use it just fine within the evaluate context.
So how do I define a function without assigning it to some global object for use withing evaluate, and why is a callable variable special in the first place?
This is all within a single call to evaluate, it does not need to persist.
If I don't assign it, but keep it anonymous it works fine, and if it's not callable, it also works fine, and assigned to some object it works:
await page.evaluate(() => {
return ((): string => "foo")();
});
await page.evaluate(() => {
const x = "foo";
return x;
});
await page.evaluate(() => {
window.foo = () => "foo";
return window.foo();
});
Just defining it in the browser console obviously also works:

This question is not a duplicate of this, because I want to define the function within the call, not outside of it. I understand that there is serialization to send it to the browser, which I don't need here since I am only defining it once already in the browser. The 2. answer on that question even does what I am trying to do.
Edit: Here is a minimal example the causes the error on my machine:
const { chromium } = await import("@playwright/test");
const server = await chromium.launchServer();
const client = await chromium.connect(server.wsEndpoint());
const browser = await client.newContext.bind(client)();
const page = await browser.newPage();
const shouldBeFoo = await page.evaluate(() => {
const foo = () => "foo";
return foo();
});
// ReferenceError: __name is not defined
This snippet reliably triggers the error on my machine, even with this contrived example where it of course does not make sense to have the function in the first place. There is no bigger picture needed than this, there is nothing else I am doing.
If I debug into the evaluate call into playwright, I land here:
async evaluate(pageFunction, arg) {
(0, _jsHandle.assertMaxArguments)(arguments.length, 2);
return await this._mainFrame.evaluate(pageFunction, arg);
}
There is already something that seems wrong at this point: the "pageFunction" argument is a function that reads like this in the debugger: ()=>{const foo=__name(()=>"foo","foo");return foo()}.
Calling it at this point still works.
Inside _mainFrame.evaluate, it gets stringified, I am assuming this has something to do with it:
const result = await this._channel.evaluateExpression({
expression: String(pageFunction),
isFunction: typeof pageFunction === 'function',
arg: (0, _jsHandle.serializeArgument)(arg)
});
This might also just be a bug in playwright.
Unable to reproduce:
const playwright = require("playwright"); // ^1.42.1
let browser;
(async () => {
browser = await playwright.firefox.launch();
const page = await browser.newPage();
const result = await page.evaluate(() => {
const foo = () => "foo";
return foo();
});
console.log(result); // => foo
// it also works using a string:
const result2 = await page.evaluate(`;(() => {
const foo = () => "foo";
return foo();
})();`);
console.log(result2); // => foo
})()
.catch(err => console.error(err))
.finally(() => browser?.close());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With