在编写测试时,我在我们正在使用的toastr插件上遇到了错误TypeError: $.extend is not a function
。似乎jQuery没有被正确选取,即使它在浏览器中正常工作。
在我们的主模拟文件中,我们导入了jQuery并将其绑定到全局windows对象,并且它可以在整个应用程序(但是toastr插件)中访问,即使在mocha中进行测试时也是如此:
import jsdom from 'jsdom';
import $ from 'jquery';
import chai from 'chai';
import chaiImmutable from 'chai-immutable';
import React from 'react';
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>');
const win = doc.defaultView;
global.document = doc;
global.window = win;
global.expect = chai.expect;
global.$ = $(win);
global.jquery = $(win);
global.jQuery = $(win);
Object.keys(window).forEach((key) => {
if (!(key in global)) {
global[key] = window[key];
}
});
chai.use(chaiImmutable);
因此,在仔细观察toastr时,我注意到了这一点:
; (function (define) {
define(['jquery'], function ($) {
// this function is called on inizialization
function getOptions() {
return $.extend({}, getDefaults(), toastr.options);
}
直接从node_modules获取jquery
,然后在函数范围内定义对象$
,这意味着它忽略window.$
(即使在这里也能正常工作)。
因此,日志记录$
将返回函数,并且记录$ .anyMethodFromjQuery($.extend
)将返回undefined。
最后我尝试记录$ .prototype,在浏览器中它将返回jQuery对象,而在mocha中它返回空对象{}
所以最后它定义了没有在mocha环境中创建原型而且我不能在插件中添加一行代码$ = window.$;
,因为没有人应该编辑插件+我们正在使用npm。
有没有解决方案?
答案 0 :(得分:0)
您遇到了麻烦,因为您正在加载应该由JSDom加载的代码。虽然在某些情况下可能在Node中加载代码然后将其传递给JSDom创建的窗口,但这是一种脆弱的方法,正如您所发现的那样。每当我将JSDom用于除了写入和抛出案例之外的其他事情时,我会加载通常由浏览器通过JSDom加载的每个脚本。这可以避免遇到您遇到的问题。这是基于您在问题中显示的代码的概念验证。您会看到toastr.getContainer()
运行正常,因为Toastr已被JSDom加载。如果您尝试使用与import toastr from "toastr"
相同的代码,则会遇到与您遇到的问题相同的问题。
import jsdom from 'jsdom';
import $ from 'jquery';
import path from "path";
const doc = jsdom.jsdom('<!doctype html><html><body></body></html>', {
features: {
FetchExternalResources: ["script"],
ProcessExternalResources: ["script"]
}
});
const win = doc.defaultView;
global.document = doc;
global.window = win;
global.$ = $(win);
global.jquery = $(win);
global.jQuery = $(win);
window.addEventListener("error", function () {
console.log("ERROR");
});
const script = document.createElement("script");
script.src = path.join(__dirname, "./node_modules/toastr/toastr.js");
document.head.appendChild(script);
// The load will necessarily be asynchronous, so we have to wait for it.
script.addEventListener("load", function () {
console.log("LOADED");
console.log(window.toastr);
// We do this here so that `toastr` is also picked up.
Object.keys(window).forEach((key) => {
if (!(key in global)) {
global[key] = window[key];
}
});
toastr.getContainer();
});
请注意,当我尝试调用toastr.info(...)
时,代码会挂起。我看了一下Toastr的代码,但我不清楚是什么导致了这个问题。 JSDom无法模拟浏览器的功能。 Toastr可能依赖于这些功能。过去,由于其局限性,我有时不得不将测试套件从JSDom上移开。