如何使用 Jest 测试 Puppeteer `page` 中的任意 es6 模块?

How to test an arbitrary es6 module in Puppeteer `page` with Jest?

我有一个像这样的 es6 模块

// MyModulue.js
import MySubModule from '../my-sub-module';

const MyFunction => {
    // accesses various DOM elements
    const element = document.querySelector("#whatever");
    
    // accesses various DOM api's
    const styles = window.getComputedStyle(element)

    // uses some other imported module
    const result = MySubModule.someMethod(styles)

    // a ton of other business logic
    // ...
    
    return someResult;
}

export default MyFunction;

我想在 Puppeteer page(即浏览器选项卡)的上下文中测试 MyModule.js

这是我的:

await page.setContent(input);
await page.addScriptTag({
      path: 'path/to/function/MyModule.js',
      type: 'module',
});
const actualFieldLabel = await page.evaluate(async (selector) => {
     const field = document.querySelector(selector);
     const result = await MyFunction(field); // <=== How can I do this??
     return result;
}
expect(actualFieldType).toBe(output);

我怎样才能让它工作?无论我尝试过什么,结果都是 undefined 或抛出异常。

您遇到了一些问题,只是不想让任何其他人遭受这些问题。对我来说,这是其中之一。下面是我的情况的解决方法希望对你也有帮助:

Puppeteer 不是在开玩笑 in their documentation 当他们说 pageFunction 回调是在页面上下文 中执行时 。但是,更有用的说法是 pageFunction 在当前 HTML 文档中执行,并且在 window / 全局范围内包含您添加的任何内容 - 因此缺少您未提供的任何内容.

需要进行两个修复:

// MyModulue.js
import MySubModule from '../my-sub-module';

const MyFunction => {
    // accesses various DOM elements
    const element = document.querySelector("#whatever");

    // accesses various DOM api's
    const styles = window.getComputedStyle(element)

    // uses some other imported module
    const result = MySubModule.someMethod(styles)

    // a ton of other business logic
    // ...

    return someResult;
}

export default MyFunction;

// Problem #1: export default didn't expose MyFunction to the global 
// scope so it was not accessible from the page.evaluate(() => {...})
// You can fix this by exposing it on the window global like so:
window.MyFunction = MyFunction;

第二个问题是 MyModule.js 依赖于另一个模块,MySubModule.js,它在 page 中根本不存在,因为我有只叫

await page.addScriptTag({
  path: 'path/to/function/MyModule.js',
  type: 'module',
});

而不是像这样:

await page.addScriptTag({
  path: 'path/to/function/MyModule.js',
  type: 'module',
});
await page.addScriptTag({
  path: 'path/to/function/my-sub-module.js',
  type: 'module',
});

但是,我必须说在我的例子中 MyModule.js 有一个巨大的依赖树,其中包含许多导入的模块,并且其中许多模块导入其他模块。结果,我不得不借助现有的 webpack.config 来添加另一个入口点和输出块。所以我最终的解决方案是为 MyModule 的整个依赖树导入一个 webpack 包,类似于 my-module.bundle.js,见下文:

await page.setContent(input);
await page.addScriptTag({
    type: 'module',

    // this is the output of webpack creating a bundle of the dependency tree 
    // starting at MyBundle.js
    path: 'build/my-module.bundle.js', 
});
const actualFieldLabel = await page.evaluate(async (selector) => {
     const field = document.querySelector(selector);
     const result = await MyFunction(field); // <=== How can I do this??
     return result;
}
expect(actualFieldType).toBe(output);

这很有效,因为捆绑包具有所有依赖项并且 page.addScriptTag 将它们直接插入 page!

非常感谢 jest-puppeteerjest 开箱即用!