如何在 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
会导致错误。
我有一个使用 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
会导致错误。