【问题标题】:JSDOM - Document is not definedJSDOM - 未定义文档
【发布时间】:2019-11-28 12:45:50
【问题描述】:

我创建了一个非常简单的页面,它只显示一条消息,因为我正在尝试测试 JSDOM 以使用document。但是,我收到以下错误。

首先,我在网上看过无数的例子,除了Stack Overflow上贴的问题,我连最简单的例子都解决不了。作为旁注,我是 Javascript 新手。

到目前为止我的代码如下:

根目录

--->index.html
--->module.js
--->package-lock.json
--->package.json
--->测试
--->--->messageTest.js

不同的文件如下:

index.html

<!doctype html>
<head>
  <meta charset="utf-8">
  <title>jsdom Unit Test</title>
</head>

<body>
  <p id='msg'>Hello, World!</p>
  <script src="module.js"></script>
</body>
</html>

module.js

function updateMsg(newMsg) {
  document.getElementById('msg').innerHTML = newMsg;
}

updateMsg("Changing message");

module.exports = updateMsg;

package.json

{
  "name": "message-example",
  "version": "1.0.0",
  "description": "Testing how to use the JSDOM",
  "main": "module.js",
  "scripts": {
    "test": "mocha"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "chai": "^4.2.0",
    "jsdom": "^15.1.1",
    "mocha": "^6.2.0",
    "mocha-jsdom": "^2.0.0",
    "rewire": "^4.0.1",
    "sinon": "^7.3.2"
  }
}

messageTest.js

var updateMsg = require('../module.js');
const expect = require('chai').expect
const { JSDOM } = require('jsdom');

describe('updateMsg', function () {

  before(function() {
    return JSDOM.fromFile('index.html')
      .then((dom) => {
        global.window = dom.window;
        global.document = window.document;
      });
  })

  it ('updates the innerHTML of element with id "msg"', function () {
    expect(document.getElementById('msg').innerHTML).to.equal('Hello, World!');
    updateMsg('The new msg!');
    expect(document.getElementById('msg').innerHTML).to.equal('The new msg!');
  });
});

如果我使用npm test 运行测试,我会在module.js 文件中的document.getElementByID... 步骤中收到ReferenceError: document is not defined 错误。

如果我删除updateMsg("Changing message"),我的测试显然运行良好。

【问题讨论】:

  • 您的代码似乎运行良好,它在

    标记中显示文本“正在更改消息”,检查小提琴:jsfiddle.net/7k96oeuw 而且,它不像您描述的那样document.getElementByID,是document.getElementById
  • 感谢您的收获。我的代码确实运行良好(在物理浏览器上),但是当我在需要模拟浏览器的地方运行测试时,我发现找不到文档元素。这应该用JSDOM模拟(据我所知),但我不确定为什么它不起作用。

标签: javascript html json dom jsdom


【解决方案1】:

您在该示例中有几个问题:

  1. 您正在混合使用 jsdom 窗口和全局节点上下文。避免分配给global(因为这样更容易犯这个错误),不要分配给您想在虚拟窗口中运行的require() 脚本。

  2. jsdom 默认情况下会阻止运行页面脚本,因此您的 module.js 既不会加载也不会执行。您必须提供 { resources: "usable", runScripts: "outside-only" } 参数来解决这个问题(确保您阅读了 jsdom README 中的安全隐患)。

  3. 您无需等待load 事件,因此您的测试在 jsdom 有机会加载脚本之前运行。

工作代码如下所示:

const expect = require("chai").expect;
const { JSDOM } = require("jsdom");

describe("updateMsg", function() {
  let jsdom;
  before(async function() {
    jsdom = await JSDOM.fromFile("index.html", {
      resources: "usable",
      runScripts: "dangerously"
    });
    await new Promise(resolve =>
      jsdom.window.addEventListener("load", resolve)
    );
  });

  it('updates the innerHTML of element with id "msg"', function() {
    expect(jsdom.window.document.getElementById("msg").innerHTML).to.equal(
      "Hello, World!"
    );
    jsdom.window.updateMsg("The new msg!");
    expect(jsdom.window.document.getElementById("msg").innerHTML).to.equal(
      "The new msg!"
    );
  });
});

您还需要从 module.js 中删除模块行,module 在浏览器中不存在。

【讨论】:

  • 非常感谢您的回答!它解决了错误。即使我从 module.js 中删除了模块行,您能否告诉我如何从测试文件中访问 updateMsg("") 函数?
  • @Adam jsdom 实际上就像一个真正的浏览器一样工作——它有自己的脚本加载机制来发出 HTTP 请求(或不使用节点模块解析机制的本地文件加载),所以你的 script-src标签触发正确加载并使您的功能可用。这也意味着其他节点内部(需求、模块等)不可用!它确实为您提供了一个“完美”的浏览器来测试 - 这也意味着如果您想要模块解析,您将不得不求助于 webpacking 并再次使用适当的标签加载您的脚本。
猜你喜欢
  • 2018-07-16
  • 1970-01-01
  • 2022-01-06
  • 2016-05-06
  • 2020-10-26
  • 2015-11-14
  • 1970-01-01
相关资源
最近更新 更多