【问题标题】:How to use imported function inside page.evaluate in Puppeteer with Jest?如何在 Puppeteer with Jest 中使用 page.evaluate 中的导入函数?
【发布时间】:2018-12-07 20:30:56
【问题描述】:

我有一个使用 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

【问题讨论】:

    标签: typescript import module jestjs puppeteer


    【解决方案1】:

    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 生成的警告——此时它不知道window 上存在createHdpiCanvas,因此访问window.createHdpiCanvas 会导致错误。

    【讨论】:

    • 虽然这可能行得通,但就像你说的那样有一些缺点,我想知道为什么在开发人员之间如此普遍的需求下,它没有得到很好的支持?另外我想知道,如果page 发生变化(即发生导航),我们需要一次又一次地注入脚本文件,对吗?恕我直言,这看起来不太好。
    【解决方案2】:

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

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

    现在您的窗口将识别这些功能

    【讨论】:

    • 我试过了,不想走这条路,原因有几个:你必须一个一个地暴露它们很乏味,函数暴露在页面的window对象上,以及我仍然遇到这种方法的错误。
    【解决方案3】:

    您可能已经知道,page.evaluate 失败的原因是函数在不同的上下文中进行评估,并且当前范围(createHdpiCanvas 在函数之外可用)丢失了。

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2019-10-20
      • 2020-05-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多