如何动态注入函数以使用 Puppeteer 进行评估?

How can I dynamically inject functions to evaluate using Puppeteer?

我正在使用 Puppeteer 无头 Chrome。我希望评估页面内的一个函数,该函数使用其他函数的一部分,在别处动态定义。

下面的代码是一个最小的示例/证明。实际上 functionToInject()otherFunctionToInject() 更复杂并且需要页面 DOM.

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(someURL);       

var functionToInject = function(){
    return 1+1;
}

var otherFunctionToInject = function(input){
    return 6
}

var data = await page.evaluate(function(functionToInject, otherFunctionToInject){
    console.log('woo I run inside a browser')
    return functionToInject() + otherFunctionToInject();
});

return data

当我 运行 代码时,我得到:

Error: Evaluation failed: TypeError: functionToInject is not a function

据我了解:functionToInject 未传递到页面的 JS 上下文中。但是如何将它传递到页面的 JS 上下文中呢?

您可以使用 addScriptTag:

将函数添加到页面上下文
const browser = await puppeteer.launch();
const page = await browser.newPage();

function functionToInject (){
    return 1+1;
}

function otherFunctionToInject(input){
    return 6
}

await page.addScriptTag({ content: `${functionToInject} ${otherFunctionToInject}`});

var data = await page.evaluate(function(){
    console.log('woo I run inside a browser')
    return functionToInject() + otherFunctionToInject();
});

console.log(data);

await browser.close();

这个例子是用字符串连接解决这个问题的肮脏方法。更干净的方法是在 addScriptTag 方法中使用 urlpath


或使用exposeFunction(但现在函​​数被包装在Promise中):

const browser = await puppeteer.launch();
const page = await browser.newPage();

var functionToInject = function(){
    return 1+1;
}

var otherFunctionToInject = function(input){
    return 6
}

await page.exposeFunction('functionToInject', functionToInject);
await page.exposeFunction('otherFunctionToInject', otherFunctionToInject);

var data = await page.evaluate(async function(){
    console.log('woo I run inside a browser')
    return await functionToInject() + await otherFunctionToInject();
});

console.log(data);

await browser.close();

工作示例accessible by link,在同一个 repo 中,您可以看到经过测试的组件。

it("click should return option value", async () => {
    const optionToReturn = "ClickedOption";

    const page = await newE2EPage();
    const mockCallBack = jest.fn();

    await page.setContent(
      `<list-option option='${optionToReturn}'></list-option>`
    );

    await page.exposeFunction("functionToInject", mockCallBack); // Inject function
    await page.$eval("list-option", (elm: any) => {
      elm.onOptionSelected = this.functionToInject;  // Assign function
    });
    await page.waitForChanges();

    const element = await page.find("list-option");
    await element.click();
    expect(mockCallBack.mock.calls.length).toEqual(1); // Check calls
    expect(mockCallBack.mock.calls[0][0]).toBe(optionToReturn); // Check argument
  });

您还可以使用 page.exposeFunction(),这将使您的函数 return 成为 Promise(需要使用 asyncawait)。发生这种情况是因为您的函数不会 running inside your browser,而是在您的 nodejs 应用程序内部,其结果正在 into/to 浏览器代码中来回发送。

const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto(someURL);       

var functionToInject = function(){
    return 1+1;
}

var otherFunctionToInject = function(input){
    return 6
}

await page.exposeFunction("functionToInject", functionToInject)
await page.exposeFunction("otherFunctionToInject", otherFunctionToInject)

var data = await page.evaluate(async function(){
    console.log('woo I run inside a browser')
    return await functionToInject() + await otherFunctionToInject();
});

return data

相关问题:

  1. exposeFunction() does not work after goto()
  2. exposed function queryseldtcor not working in puppeteer
  3. How to use evaluateOnNewDocument and exposeFunction?
  4. exposeFunction remains in memory?
  5. Puppeteer evaluate function
  6. allow to pass a parameterized funciton as a string to page.evaluate
  7. Functions bound with page.exposeFunction() produce unhandled promise rejections
  8. How to pass a function in Puppeteers .evaluate() method?
  9. Why can't I access 'window' in an exposeFunction() function with Puppeteer?