我使用木偶师来测试function环境中的客户端函数--该函数本身并不使用function,而是要导入es6 function模块并在最终用户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()返回的元素是正确的,但是当作为参数传递时,函数会接收一个空对象。我怀疑这是一个范围问题,但我完全没有解决办法。
发布于 2020-10-10 19:56:39
遗憾的是,我找不到任何解决方案不能调用转换我的文件,但希望我设法使它正确工作。
关键是创建第二个transpile配置,该配置将生成一个代码,由web浏览器使用UMD格式直接使用。由于我使用了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;然后,我稍微重写了我的脚本,以便在每次测试运行时生成我的测试包。
package.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属性的问题--我能够在page.evaluate的内容和我的模块函数之间传递DOM引用,而没有任何问题,所以看起来不再像问题了。
https://stackoverflow.com/questions/64234975
复制相似问题