如何在 Puppeteer with Jest 中使用 page.evaluate 中的导入函数?

How to use imported function inside page.evaluate in Puppeteer with Jest?

我有一个使用 Jest 进行单元测试的 TypeScript 项目,并且刚刚将 Puppeteer 添加到组合中,目的是 运行 在客户端进行一些测试。它工作正常,除非我尝试在 page.evaluate.

中使用导入函数

例如,我在HdpiCanvas.test.ts中有以下内容:

import { createHdpiCanvas } from "./HdpiCanvas";

test("createHdpiCanvas", async () => {
    await page.setViewport({ width: 800, height: 600, deviceScaleFactor: 2 });
    let size = await page.evaluate(() => {
        const canvas = createHdpiCanvas(); // document.createElement('canvas');
        return [canvas.width, canvas.height];
    });
    console.log(size); // [600, 300] for HDPI canvas and [ 300, 150 ] for a regular one
});

注释掉 document.createElement('canvas') 后,测试 运行 就可以正常记录 [ 300, 150 ]。但是对于 createHdpiCanvas()page.evaluate 函数会抛出以下错误:

Error: Evaluation failed: ReferenceError: HdpiCanvas_1 is not defined
    at __puppeteer_evaluation_script__:2:24

HdpiCanvas.ts里面的实际createHdpiCanvas定义如下:

export function createHdpiCanvas(width = 300, height = 150): HTMLCanvasElement {
    const canvas = document.createElement('canvas');
    canvas.width = width;
    canvas.height = height;
    applyHdpiOverrides(canvas);
    return canvas;
}

本身依赖于 HdpiCanvas.ts 中定义的其他函数,如 applyHdpiOverrides.

您可以在调用 evaluate 函数之前这样做:

await page.exposeFunction("applyHdpiOverrides",applyHdpiOverrides);
await page.exposeFunction("createHdpiCanvas",createHdpiCanvas);

现在您的 window 将识别这些功能

您可能已经明白了,page.evaluate 失败的原因是因为该函数是在不同的上下文和当前范围内计算的(其中 createHdpiCanvas 在函数外部可用) 丢失了。

我对 pupeteer 一点都不熟悉,但通过查看该功能的文档,您可能可以尝试一些东西。它不是很漂亮,但也许值得一试:

您可以将 createHdpiCanvas 作为参数传递,但是函数不可序列化,因此您必须自己做:

page.evaluate((createHdpiCanvasCode) => {
  const createHdpiCanvas = new Function(`return ${createHdpiCanvasCode}`)();
  const canvas = createHdpiCanvas();
  return [canvas.width, canvas.height];
}, createHdpiCanvas.toString());

但是,您必须以相同的方式注入依赖项。

Meni Roytenburd 建议的解决方案是正确的。如果您不喜欢每个函数都需要单独暴露给浏览器这一事实,那么想到的唯一想法是首先将您的项目转换为单个 JavaScript 文件,然后将其作为 <script> 标签 — 就像您在现实生活中一样。

第一步是用您的包生成一个 JavaScript 文件。有时这可以单独使用 TypeScript 编译器来完成,但也可以使用像 Webpack 这样的工具。

完成后,您可以从 Puppeteer 中将您的包移动到客户端:

await page.addScriptTag({ path: 'path/to/the/bundle' });

请记住,这仍然可能将您的函数暴露给全局范围,因此可以通过 window.

访问它们
  let size = await page.evaluate(() => {
      const canvas = window.createHdpiCanvas();
      return [canvas.width, canvas.height];
  });

  console.log(size); // [ 300, 150 ]

这种方法的另一个缺点是必须处理 TypeScript 生成的警告 — 此时它不知道 createHdpiCanvas 存在于 window,因此访问 window.createHdpiCanvas会导致错误。