2016-06-30 22 views
1

Beim Schreiben von Tests habe ich Bug TypeError: $.extend is not a function auf Toastr Plugin, das wir verwenden. Es scheint, dass jQuery nicht richtig aufgenommen wird, selbst wenn es normal im Browser funktioniert.Probleme beim Definieren von Modulen mit AMD in Mocha

In unserem Haupt Mock-Datei importiert wir jQuery und binden an globalen Windows-Objekt und es ist zugänglich über ganze Anwendung (aber toastr Plugin), auch während des Tests in Mokka:

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); 

So, während näherem Hinsehen auf toastr ich dies bemerkt:

; (function (define) { 
    define(['jquery'], function ($) { 

    // this function is called on inizialization 
    function getOptions() { 
     return $.extend({}, getDefaults(), toastr.options); 
    } 

es dauert jquery von node_modules direkt und definiert dann Objekt $ in Funktionsumfang, das bedeutet, dass es window.$ ist zu ignorieren (welche arbeitet normalerweise sogar hier).

Daher Logging $ wird Funktion zurückgeben, und die Protokollierung $ .anyMethodFromjQuery ($.extend) wird undefined zurückgeben.

Am Ende habe ich versucht, $ .prototype anmelden, im Browser wird es mir jQuery-Objekt zurück, während in Mokka es leeres Objekt {}

Also am Ende gibt es nicht erstellt Prototyp in Mokka-Umgebung definiert haben und Ich kann nicht eine Zeile Code $ = window.$; in Plugin hinzufügen, da niemand ein Plugin bearbeiten sollte + wir npm verwenden.

Gibt es dafür eine Lösung?

+0

Wie wird 'toastr' geladen? – Louis

+0

Mit npm, in der Anwendung verwende ich nur 'Import toastr von 'toastr''; – Arcagully

Antwort

0

Sie stoßen auf Probleme, weil Sie Code laden, der von JSDom außerhalb davon geladen werden sollte. Es ist zwar möglich in einigen Fällen, Code in Node zu laden und dann an das Fenster zu übergeben, das JSDom erstellt, das ist ein spröder Ansatz, wie Sie entdeckt haben. Wann immer ich JSDom für andere Dinge als Schreib-und-Toss-Fälle verwende, lade ich jedes Skript, das normalerweise von einem Browser über JSDom geladen würde. Dies verhindert, dass Sie auf das Problem stoßen, in das Sie geraten sind. Hier ist ein Proof-of-Concept, basierend auf dem Code, den Sie in der Frage gezeigt haben. Sie werden sehen, dass toastr.getContainer() läuft, weil Toastr von JSDom geladen wurde. Wenn Sie versuchen, den gleichen Code mit einer import toastr from "toastr" zu verwenden, erhalten Sie das gleiche Problem, das Sie kennengelernt haben.

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(); 
}); 

Beachten Sie, dass der Code aufgehängt, als ich versuchte, toastr.info(...) anrufen. Ich habe mir den Code von Toastr angeschaut, aber mir ist nicht klar, was das Problem verursacht. Es gibt Funktionen von Browsern, die JSDom nicht emulieren kann. Es ist möglich, dass Toastr von solchen Features abhängig ist. In der Vergangenheit musste ich manchmal Testsuiten von JSDom wegen seiner Einschränkungen wechseln.

+0

Das sieht gut aus, das einzige Problem ist, dass das Script nicht geladen wird. 'Script.addEventListener (" load "' feuert nie, deshalb habe ich immer noch das selbe Problem. Ich versuche jetzt einen effektiven Weg zu finden toastr mit jsdom – Arcagully

+0

Wann immer Ich poste Code dieser Art, ich teste es vor dem Posten, also ja, es funktioniert. Ich habe es getestet. Natürlich, wenn Sie 'npm toastr 'vorher nicht installiert haben, wird es nicht laden. – Louis

+0

Ja, ich bemerkte, dass es das Problem mit 'path.join ('../../ node_modules/toastr/toastr.js') sein konnte;' Ich überprüfe das jetzt – Arcagully