12

I am writing Mocha unit tests for Typescript code containing Jquery. I'm using jsdom for getting the document object. When I compile my TS code to JS and run the tests, it throws an error [ReferenceError: $ is not defined].

My Typescript code is here

export function hello(element) : void {
    $(element).toggleClass('abc');
};

My unit test code is as follows:

import {hello} from '../src/dummy';

var expect = require('chai').expect;
var jsdom = require('jsdom');
var document = jsdom.jsdom();
var window = document.defaultView;

var $ = require('jquery')(window);

describe('TEST NAME', () => {

    it('should run', (done) => {
        hello($('div'));
        done();
    });
});

When I run Mocha test it shows

    <failure message="$ is not defined"><![CDATA[ReferenceError: $ is not defined ...
]]></failure>

Also tried using global.$ = require("jquery"); but does not work.

Aditi
  • 123
  • 1
  • 1
  • 6
  • this is a bit unrelated but you should consider the built in functionality that angular provides for working with the dom. you shouldn't need jquery for dom manipulation. – toskv Jul 26 '16 at 13:37
  • For me, this problem surfaced as 'typescript TypeError: jQuery.Deferred is not a function' or 'typescript TypeError: jQuery.ajax is not a function', because I made the mistake of using an import * from jquery in a node.js environment, which doesn't work. If you're in that same situation, Louis' answer, or my own answer, will help you. :) – TinkerTank Jul 01 '18 at 16:15

5 Answers5

9

jQuery has to be available globally because your script gets it from the global space. If I modify your code so that var $ = require('jquery')(window); is replaced by:

global.$ = require('jquery')(window);

then it works. Note the two calls: 1st to require jquery, then to build it by passing window. You could alternatively do:

global.window = window
global.$ = require('jquery');

If window is available globally, then there is no need to perform the double call as in the first snippet: jQuery just uses the globally available window.

You probably also want to define global.jQuery since some scripts count on its presence.

Here is a full example of a test file that runs:

/// <reference path="../typings/mocha/mocha.d.ts" />
/// <reference path="../typings/chai/chai.d.ts" />
/// <reference path="../typings/jsdom/jsdom.d.ts" />
/// <reference path="./global.d.ts" />

import {hello} from './dummy';

import chai = require('chai');
var expect = chai.expect;
import jsdom = require('jsdom');
var document = jsdom.jsdom("");
var window = document.defaultView;

global.window = window
global.$ = require('jquery');

describe('TEST NAME', () => {

    it('should run', (done) => {
        hello($('div'));
        done();
    });
});

The typings files are obtained the usual way using tsd. The file ./global.d.ts fixes the issue you may get with setting new values on global. It contains:

declare namespace NodeJS {
    interface Global {
      window: any;
      $: any;
    }
}

dummy.js was also modified like this:

declare var $: any;

export function hello(element) : void {
    $(element).toggleClass('abc');
};
Louis
  • 146,715
  • 28
  • 274
  • 320
  • I tried using global.$ but it said "$ is not a property of Global". Also tried (global).$ and it said "$ is not a function" – Aditi Jul 26 '16 at 18:05
  • I've used JSDom extensively but I'm not that familiar with Typescript so I wrote from a JSDom perspective mainly. I've updated my answer to add the Typescript-related changes that I made. – Louis Jul 26 '16 at 18:19
  • Worked like a charm! Thanks Louis :) – Aditi Jul 27 '16 at 06:46
  • i used directly `(global as any).$ = require('jquery');` it worked without all the rest of the code – Katzura Nov 04 '21 at 09:54
3

You need to include jsdom and jQuery in Node first:

npm install jsdom --save-dev
npm install jquery --save-dev

and then add this lines into your .js file where you are using jQuery

const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const { window } = new JSDOM(`...`);
var jQuery = require('jquery')(window);

var example = (function($) {
 .....your jQuery code....
})(jQuery);

module.exports = example;
1

I found this question in 2020 and Louis got me pointing in the right direction. However, I found jsdom-global an easier alternative way of getting document working in Mocha. I installed jsdom-global following the instructions at jsdom-global

I used these lines to get jQuery and $ working as expected:

require('jsdom-global')();
global.window = window;
global.$ = require('jquery');
global.jQuery = $;

Then this test passed with flying colours:

const assert = require("chai").assert;
function test(){
    return "test";
}
describe('Setting up global constants...', function () {
    describe('Libraries...', function () {
        it('jQuery and $ are present', () => {
            assert(jQuery !== undefined);
            assert($ !== undefined);
            assert($ === jQuery);
            assert($.isFunction(test));
        });
    });
});
DavidHyogo
  • 2,838
  • 4
  • 31
  • 48
0

If you can't or don't want to alter the global namespace type definition, you can still include jQuery, as following:

const globalAny:any = global;
const $ = globalAny.$ = require('jquery')(window);

The full initialization of jQuery then becomes:

const jsdom = require("jsdom");
const { JSDOM } = jsdom;
const { window } = (new JSDOM());
const globalAny:any = global;
const $ = globalAny.$ = require('jquery')(window);
TinkerTank
  • 5,685
  • 2
  • 32
  • 41
-2

As said, you should import jQuery properly: import $ = require('jquery');

Yahiko
  • 188
  • 3