这篇文章只是个人学习笔记,所以结构不清,信息不全,建议阅读原版项目手册。
JSDOM 是用nodejs实现的用于测试的虚拟浏览器。
const dom = new JSDOM(``, {
url: "https://example.org/",
referrer: "https://example.com/",
contentType: "text/html",
includeNodeLocations: true,
storageQuota: 10000000
});
url
默认为 "about:blank"
.默认是规范化的,所以写了https:example.com会自动变成https://example.com/referrer
just affects the value read from document.referrer
. It defaults to no referrer (which reflects as the empty string).contentType
默认为 "text/html"
.includeNodeLocations
保存页面位置,为了性能,默认为 false
storageQuota
localStorage
和 sessionStorage 的最大存储大小。默认为
5,000,000.runScripts 设置为 "dangerously" 才能在页面上执行js
当pretendToBeVisual设置为true时,会发生以下事情:
document.hidden
返回 false
而不是 true
document.visibilityState
返回 "visible"
而不是"prerender"
window.requestAnimationFrame()
和 window.cancelAnimationFrame()
方法const window = (new JSDOM(``, { pretendToBeVisual: true })).window;
window.requestAnimationFrame(timestamp => {
console.log(timestamp > 0);
});
resources设置为 usable,才会加载以下iframe, css, js引用:
<frame>
和 <iframe>
<link rel="stylesheet">
<script>
, 不过首先你要设置 runScripts: "dangerously"
<img>
, 不过你首先要保证 已经安装了canvas
(or canvas-prebuilt
) npm 包serialize方法可以返回序列化html。例子
const dom = new JSDOM(`<!DOCTYPE html>hello`);
dom.serialize() === "<!DOCTYPE html><html><head></head><body>hello</body></html>";
// Contrast with:
dom.window.document.documentElement.outerHTML === "<html><head></head><body>hello</body></html>";
nodeLocation方法
通过nodeLocation方法获取当前元素在页面上的位置(不过要记得开启 includeNodeLocation)
const dom = new JSDOM(
`<p>Hello
<img src="foo.jpg">
</p>`,
{ includeNodeLocations: true }
);
const document = dom.window.document;
const bodyEl = document.body; // implicitly created
const pEl = document.querySelector("p");
const textNode = pEl.firstChild;
const imgEl = document.querySelector("img");
console.log(dom.nodeLocation(bodyEl)); // null; it's not in the source
console.log(dom.nodeLocation(pEl)); // { startOffset: 0, endOffset: 39, startTag: ..., endTag: ... }
console.log(dom.nodeLocation(textNode)); // { startOffset: 3, endOffset: 13 }
console.log(dom.nodeLocation(imgEl)); // { startOffset: 13, endOffset: 32 }
runVMScript(script)方法
使用runVMScript去动态的执行脚本
reconfigure(settings)方法
window的顶级属性被标记成 Unforgeable 意思是不能改的。如果你去改就会报错。比如
window.location.href = "https://example.com/"
会抛出异常 jsdomError 。你也不能新建 Window或者 Document 。
不过你可以使用 reconfigure 从jsdom外部来改变这些变量
const dom = new JSDOM();
dom.window.top === dom.window;
dom.window.location.href === "about:blank";
dom.reconfigure({ windowTop: myFakeTopForTesting, url: "https://example.com/" });
dom.window.top === myFakeTopForTesting;
dom.window.location.href === "https://example.com/";
fromURL()
使用fromURL可以快速从url中创建JSDom对象
JSDOM.fromURL("https://example.com/", options).then(dom => {
console.log(dom.serialize());
});
第二个参数,跟jsdom创建的参数一样,但是有以下限制:
不能指定 url
和 contentType
referrer
option is used as the HTTP Referer
request header of the initial request.resources
option also affects the initial request; this is useful if you want to, for example, configure a proxy (see above).Set-Cookie
方法设置的cookie会存储在 jsdom's cookie jar.里面 同样的你预先在 cookie jar 中设置的cookie会设置在request中发送过去.fromFile()
跟url类似,从html文件中创建jsdom
JSDOM.fromFile("stuff.html", options).then(dom => {
console.log(dom.serialize());
});
fragment()
有时你不需要虚拟整个页面,甚至你都不需要windows和document对象。
const frag = JSDOM.fragment(`<p>Hello</p><p><strong>Hi!</strong>`);
frag.childNodes.length === 2;
frag.querySelector("strong").textContent = "Why hello there!";
// etc.
因为没有完整的dom对象所以fragment不好序列化。但是如果fragment只包含一个元素,就很容易序列化了,比如
const frag = JSDOM.fragment(`<p>Hello</p>`);
console.log(frag.firstChild.outerHTML); // logs "<p>Hello</p>"
如果你想使用<canvas> ,记得要安装 canvas 插件
如果你在jsdom里面设置了timer,比如 window.setTimeout()
or window.setInterval() 那么jsdom会为了保证这些timer正确的被运行,而继续运行下去。如果你要关掉它,就用
window.close() 。
调试
由于jsdom是nodejs实现的,也可以用chrome 开发者工具来调试。(启动时带上
--inspect)
测试异步加载js
通过类似requirejs来异步加载脚本, 你需要通知jsdom何时js加载完毕。
<!-- Inside the HTML you supply to jsdom -->
<script>
requirejs(["entry-module"], () => {
window.onModulesLoaded();
});
</script>
然后jsdom就可以知道何时模块加载完了
// On the Node.js side:
const window = (new JSDOM(...)).window;
window.onModulesLoaded = () => {
console.log("ready to roll!");
};
共享结构和原型
为了性能考虑,多个jsdom实例共享结构和原型,比如
const dom1 = new JSDOM();
const dom2 = new JSDOM();
dom1.window.Element.prototype.expando = "blah";
console.log(dom2.window.document.createElement("frameset").expando); // logs "blah"
没实现的部分主要是两个方面:
如果你想使用这些特性你可以用PhantomJS