Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js, Mocha, make globals in closures available

I am currently setting up some mocha tests using Node and in general they work. I now came across a problem I am not able to resolve.

I have a JS file containing the following: MyClass.js (General CoffeeScript output for class MyClass + constructor: ->)

EDIT: This is browser code, I just want to use Node to test it. (Is that even desirable?)

(function() {

  window.MyClass = (function() {

    function MyClass() {
      // Do something cool here
    }

    return MyClass;

  })();

}).call(this);

I now require MyClass.js in my test file. Once I run it, it directly throws an error

Testfile:

var myclass = require('MyClass.js');
...
describe('MyClass', function() { ... });

Error:

ReferenceError: window is not defined.

So far, I understand why this is happening, window does not exist in Node. But I cannot come up with a solution. I actually do not need the real window object specifically, so I thought mocking it would be enough. But it is not...

var window = {},
    myclass = require('myclass.js');
...
describe('MyClass', function() { ... });

This command is also not helping: $ mocha --globals window

I still end up with the same error. Any idea is much appreciated!

like image 840
pabera Avatar asked Dec 19 '13 13:12

pabera


4 Answers

You don't actually want the window object, what you want is the global object. Here is some code that can get it in the browser (in which case it will be the same as 'window') or in node (in which case it will be the same as 'global').

var global = Function('return this')();

Then set things on that rather than on 'window'.

Note: there are other ways of getting the global object, but this has the benefit that it will work inside strict mode code too.

like image 162
kybernetikos Avatar answered Oct 16 '22 18:10

kybernetikos


With following code you can use your class-like object in web-browser environment and Node.js without modification. (Sorry, I don't know how to translate that to CoffeeScript)

(function (exports) {

    var MyClass = (function() {

        function MyClass() {
            // Do something cool here
        }

        return MyClass;

    })();

    exports(MyClass);

})(function (exported) {
    if (typeof module !== 'undefined' && module.exports) {

        module.exports = exported;

    } else if (typeof window !== 'undefined') {

        window.MyClass = exported;

    } else {

        throw new Error('unknown environment');

    }
});

As you already have a scope which doesn't pollute global name-space, you could reduce it to:

(function (exports) {

    function MyClass() {
        // Do something cool here
    }

    exports(MyClass);

})(function (exported) {

    // see above

});

I'm not an expert in AMD, require.js and other module loaders, but I think it should be easy to extend this pattern to support other environments as well.

Edit

In a comment you said that the above solution is not working when translated back to CoffeeScript. Therefore, I suggest another solution. I haven't tried it but maybe this could be a way to solve your problem:

global.window = {}; // <-- should be visible in your myclass.js

require('myclass.js');

var MyClass = global.window.MyClass;

describe('MyClass', function() {
    var my = new MyClass();
    ...
});

It's a horrible piece of code, but if it works, maybe for testing purposes it's sufficient.

Due to the module loading behaviour of node.js this only works if your require('myclass.js') is the first require of this file in the node process. But in case of testing with Mocha this should be true.

like image 42
hgoebl Avatar answered Oct 16 '22 18:10

hgoebl


1) What you are looking for is the module.exports to expose things in Node:

http://openmymind.net/2012/2/3/Node-Require-and-Exports/

2) Also you don't need IIFE in Node, you can drop the (function() {...

3) You can alway look at some popular Node repo on Github to see examples, look at the Mocha code since you're using it, you'll learn a thing or two.

like image 20
plus- Avatar answered Oct 16 '22 18:10

plus-


Something like jsdom is lighter than PhantomJS and yet provides quite a few things you need to test code that expects to be running with a proper window. I've used it with great success to test code that navigates up and down the DOM tree.

You ask:

This is browser code, I just want to use Node to test it. (Is that even desirable?)

It is very desirable. There's a point at which a solution like jsdom won't cut it but as long as your code is within the limit of what jsdom handles, might as well use it and keep the cost of launching a test environment to the minimum needed.

like image 25
Louis Avatar answered Oct 16 '22 17:10

Louis