在Mocha中使用AMD定义模块的问题

时间:2016-06-30 09:13:23

标签: mocha amd

在编写测试时,我在我们正在使用的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。

有没有解决方案?

1 个答案:

答案 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上移开。