[英]How do you return an object from the browser environment to the Node environment in Puppeteer?
[英]How to run Node packages from Puppeteer client environment for testing purposes
我正在使用Puppeteer在 react 环境中测试客户端函数 - 该函数本身不使用 React,但旨在导入 es6 react 模块并在最终用户 DOM 环境中运行。 我需要 Puppeteer,因为此函数依赖于诸如innerText
属性,而这些属性在 jsdom 中不可用。
此函数将 DOM 元素作为参数,但是我无法为其编写测试文件。 这是我的代码示例:
import path from 'path';
import puppeteer from 'puppeteer';
import {getSelectionRange, setSelectionRange} from './selection';
describe(
'getSelection should match setSelection',
() => {
let browser;
let page;
beforeAll(async done => {
try {
browser = await puppeteer.launch();
page = await browser.newPage();
await page.goto(
`file://${path.join(process.env.ROOT,
'testFiles/selection_range_test.html')}`
);
await page.exposeFunction(
'setSelectionRange',
(el, start, end) => setSelectionRange(el, start, end)
);
await page.exposeFunction(
'getSelectionRange',
el => getSelectionRange(el)
);
} catch(error) {
console.error(error);
}
done();
});
afterAll(async done => {
await browser.close();
done();
});
it('should match on a node with only one text node children', async () => {
const {selection, element, argEl} = await page.evaluate(async () => {
const stn = document.getElementById('single-text-node');
// Since console.log will output in the Puppeteer browser and not in node console,
// I added a line inside the selectionRange function to return the element it receives
// as an argument.
const argEl = await window.setSelectionRange(stn, 1, 10);
const selectionRange = await window.getSelectionRange(stn);
return {selection: selectionRange, element: stn, argEl};
});
// Outputs <div id="single-text-node">...</div>
// (the content is long so I skipped it, but it displays the correct value here)
console.log(element.outerHTML);
// Outputs {}
console.log(argEl);
});
}
);
如评论中所述,直接从page.evaluate()
返回的元素是正确的,但是当作为参数传递时,该函数接收一个空对象。 我怀疑范围问题,但我在这里完全没有解决方案。
遗憾的是,我找不到任何不会调用我的文件转译的解决方案,但希望我设法使其正常工作。
关键点是创建第二个转译配置,该配置将使用 UMD 格式生成可由 Web 浏览器直接使用的代码。 由于我使用 rollup,这里是我的rollup,config.js文件:
import babel from 'rollup-plugin-babel';
import commonjs from 'rollup-plugin-commonjs';
import resolve from 'rollup-plugin-node-resolve';
import pkg from './package.json';
// The content that is actually exported to be used within a React or Node application.
const libConfig = [
{
inlineDynamicImports: true,
input: './src/index.js',
output: [
{
file: './lib/index.js',
format: 'cjs'
},
],
external: [...Object.keys(pkg.dependencies || {})],
plugins: [
commonjs(),
resolve(),
babel({exclude: 'node_modules/**'})
]
}
];
// Used to generate a bundle that is directly executable within a browser environment, for E2E testing.
const testConfig = [
{
inlineDynamicImports: true,
input: './src/index.js',
output: [
{
file: './dist/index.js',
format: 'umd',
name: 'tachyon'
},
],
external: [...Object.keys(pkg.dependencies || {})],
plugins: [
commonjs(),
resolve(),
babel({runtimeHelpers: true})
]
}
];
const config = process.env.NODE_ENV === 'test' ? testConfig : libConfig;
export default config;
然后我稍微重写了我的脚本,以便在每次测试运行时生成我的测试包。
包.json
{
"scripts": {
"build:test": "NODE_ENV=test rollup -c && NODE_ENV=",
"build": "rollup -c",
"test": "yarn build:test && jest"
},
}
最后,我将转换后的脚本添加到我的selection_test.html文件中:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Selection range test</title>
<script src="../dist/index.js"></script>
</head>
...
这让我可以像这样编写我的测试文件:
import path from 'path';
import puppeteer from 'puppeteer';
import {describe, beforeAll, afterAll, it} from '@jest/globals';
describe(
'getSelection should match setSelection',
() => {
let browser;
let page;
beforeAll(async done => {
try {
browser = await puppeteer.launch({
headless: true,
args: ['--disable-web-security', '--disable-features=SameSiteByDefaultCookies,CookiesWithoutSameSiteMustBeSecure'],
});
page = await browser.newPage();
await page.goto(`file://${path.join(process.env.ROOT, 'tests/selection_test.html')}`, {waitUntil: 'networkidle0'});
await page.setBypassCSP(true);
} catch(error) {
console.error(error);
}
done();
});
afterAll(async done => {
await browser.close();
done();
});
it('should match on a node with only one text node children', async () => {
const data = await page.evaluate(() => {
// Fix eslint warnings.
window.tachyon = window.tachyon || null;
if (window.tachyon == null) {
return new Error(`cannot find tachyon module`);
}
const stn = document.getElementById('single-text-node');
const witnessRange = tachyon.setRange(stn, 1, 10);
const selectionRange = tachyon.getRange(stn);
return {witnessRange, selectionRange, element: stn.outerHTML};
});
console.log(data); // Outputs the correct values
/*
{
witnessRange: { start: 1, end: 10 },
selectionRange: {
absolute: { start: 1, end: 10 },
start: { container: {}, offset: 1 },
end: { container: {}, offset: 10 }
},
element: '<div id="single-text-node">Lorem ... sem.</div>'
}
*/
});
}
);
唯一剩下的问题是, start.container
和end.container
的结果中getRange
是不确定的,但它的木偶不能处理的范围似乎更容易的问题startContainer
和endContainer
性-我能够通过内容之间的DOM引用page.evaluate 和我的模块功能没有任何问题,所以它看起来不再是问题了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.