Why can’t I access ‘window’ in an exposeFunction() function with Puppeteer?

evaluate the function

You can pass the dynamic script using evaluate.

(async function(){
    var puppeteer = require('puppeteer');
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    var functionToInject = function(){
        return window.navigator.appName;
    }

    var data = await page.evaluate(functionToInject); // <-- Just pass the function
    console.log(data); // outputs: Netscape
    
    await browser.close();
})()

addScriptTag and readFileSync

You can save the function to a seperate file and use the function using addScriptTag.

await page.addScriptTag({path: 'my-script.js'});

or evaluate with readFileSync.

await page.evaluate(fs.readFileSync(filePath, 'utf8'));

or, pass a parameterized funciton as a string to page.evaluate.

await page.evaluate(new Function('foo', 'console.log(foo);'), {foo: 'bar'});

Make a new function dynamically

How about making it into a runnable function 😀 ?

function runnable(fn) {
  return new Function("arguments", `return ${fn.toString()}(arguments)`);
}

The above will create a new function with provided arguments. We can pass any function we want.

Such as the following function with window, along with arguments,

function functionToInject() {
  return window.location.href;
};

works flawlessly with promises too,

function functionToInject() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(window.location.href);
    }, 5000);
  });
}

and with arguments,

async function functionToInject(someargs) {
  return someargs; // {bar: 'foo'}
};

Call the desired function with evaluate,

var data = await page.evaluate(runnable(functionToInject), {bar: "foo"});
console.log(data); // shows the location

Leave a Comment