[JSDOM脚本元素在Mocha中被覆盖

问题描述 投票:1回答:1

我有两个几乎相同的JS文件,我无法更改它们要为其添加测试。

文件1:

const url = "https://file-1.js";

(function () {
  "use strict";
  window.onload = () => {
    const script = document.createElement("script");
    script.src = url;
    document.head.appendChild(script);
  };
})();

文件2:

const url = "https://file-2.js";

(function () {
  "use strict";
  window.onload = () => {
    const script = document.createElement("script");
    script.src = url;
    document.head.appendChild(script);
  };
})();

然后测试1:

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

const { window } = new JSDOM(`<!DOCTYPE html><head></head><p>Fake document</p>`, {
  resources: "usable",
});

global.document = window.document;
global.window = window;

const myFile = require("../src/myFile");

describe("Test 1", function () {
    it("Loads a file from an external source", function (done) {
      console.log(window.document.head.children); // See what's going on
      expect(window.document.head.children[0].src).to.equal("https://file-1.js");
    });
});

测试2:

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

const myFile2 = require("../src/myFile2");

describe("Test 2", function () {
    it("Loads a file from an external source", function (done) {
      console.log(window.document.head.children); // See what's going on
      expect(window.document.head.children[0].src).to.equal("https://file-2.js");
    });
});

测试2通过,但测试1失败。两个console.logs的值是:

HTMLCollection { '0': HTMLScriptElement {} }

[console.log(window.document.head.children[0].src)产生:

https://file-2.js

我希望window.document.head中有两个孩子,但上面只有1个。看来Mocha首先要在所有测试中加载所有必需的文件,而第二个文件中的appendChild会覆盖第一个文件中的值。

有没有解决的办法?我尝试了done()或在需要被调用的地方四处移动,但结果相同。

javascript testing mocha global jsdom
1个回答
0
投票

I created a repo for you to look at.一些注意事项:

  1. 如果要加载外部脚本,请为JSDOM设置runScripts: "dangerously"标志(请参见this issue)。

  2. 我们需要自己手动重新触发load事件-基本上,在执行脚本时,document.readyState的状态为"complete",即load事件已经触发。

    这里发生的是,一旦JSDOM编译完HTML脚本,我们就在初始化时将其传递给window。我们可以导入所需的内容,然后手动触发load事件-只要我们不将任何脚本传递给初始JSDOM调用,就可以确保不会触发任何东西。

  3. 加载事件触发后,实际上已正确设置了生成的<script>标签,但是由于它们包含没有实际加载内容的伪URL,因此该过程将抛出:Error: Could not load script: "https://file-1.js/"。为了测试,我将这些URL更改为jQuery库和Hammer.js,您将需要添加逻辑以确保URL安全。

  4. 因为两个脚本都设置了window.onload = function() {...},所以如果我们都运行它们,然后触发load事件(通常会这样做),则window.onload在运行时将被test2.js覆盖。我们可以解决这个问题,但这仅仅是因为我们知道脚本包含的内容。

过去几天我实际上一直在从事与此类似的工作,最近又提出了两个软件包来帮助解决类似的情况:enable-window-document(公开了windowdocument全局)和enable-browser-mode(旨在完全模拟浏览器运行时,将global对象设置为window,并公开window.include函数以在全局上下文中评估导入的脚本,即: window.include('jquery.min.js'),没有错误)。

对于这种情况(以及测试脚本的低复杂性),enable-window-document就足够了。当以完全浏览器兼容性模式运行时,由于两个脚本中的const url = ...声明,我们实际上会失败-在启用完全浏览器兼容性的情况下,会在全局上下文中对这些脚本进行评估,从而导致尝试重新设置window.url变量声明为const。只需将windowdocument全局变量设置为[[will就可以用在此用例中,但是如果您开始加载复杂的脚本,则可能会遇到问题。

如果您的脚本可以在浏览器中运行(即,没有冲突的全局enable-browser-mode变量),然后替换对浏览器JS的所有const调用(require和[C0 ])和test1.js。这将确保该窗口引用全局对象,并且您的平均野生浏览器JS将按预期执行。

加载所有脚本并解决test2.js冲突后,我们运行测试:

include()

onload
而且很奇怪,我们无法在运行时访问// inside index.js
...
console.log(document.head.outerHTML);
console.log("jQuery:", window.$);
。但是,在控制台中:

$ node . RUNNING TESTS... <head><script src="https://code.jquery.com/jquery-3.5.1.min.js"></script><script src="https://hammerjs.github.io/dist/hammer.min.js"></script></head> jQuery: undefined

因此,我建议您四处逛逛,看看您可以做什么,而不能上班。

[脚注:笑话是Facebook的热门垃圾,我不会在调试它时担心自己(声称window.jQuery中不存在$ node Welcome to Node.js v14.4.0. Type ".help" for more information. > require('.') RUNNING TESTS... <head><script src="https://code.jquery.com/jquery-3.5.1.min.js"></script><script src="https://hammerjs.github.io/dist/hammer.min.js"></script></head> jQuery: undefined {} > window.jQuery <ref *1> [Function: S] { fn: S { jquery: '3.5.1', constructor: [Circular *1], length: 0, toArray: [Function: toArray] ... 全局变量,依此类推)。尽管我可能会遗漏某些东西,但我们在此所做的工作还是很棘手,似乎超出了套件的范围。如果您想花时间调试它,请成为我的客人:我离开了项目结构,以便您可以运行window并查看它在抱怨什么。

无论如何,希望对您有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.