I am trying to test a simple node Module with intern.The module is an AMD module. I am running into a couple of issues
If the module is defined as below, I get the error "moduleName" has
no method 'map' "
define('moduleName',[]function(require){ var r= require('request');
})
If the module is defined as below without a moduleName, I see this
error "undefined is not a function" - I guess its not able to resolve 'require'
define([]function(require){ var r= require('request'); })
Here is how my Test Looks
define([
'intern!object',
'intern/chai!assert',
'/src/api/nameApi'
], function (registerSuite, assert,nameApi) {
registerSuite({
name: 'GetName Test',
getName: function () {
var nameFromApi = nameApi.getName();
assert( nameFromApi!= null,'name is not null');
}
});
});
Providing an explicit module ID as the first argument to define destroys module portability and shouldn’t be done. It is not currently supported by the loader used by the master branch because it is such a bad idea. It is supported by the geezer branch, but again, I strongly advise you to never use this pattern.
The second module definition you have provided is just wrong; you need to put 'require' in your dependencies array if you expect to load the special require function. You also can’t use a variable to pass a module ID to the require function if you are expecting it to be pre-loaded as a dependency. So, it should look like this:
define(['require', 'foo'], function (require) {
var foo = require('foo');
});
or, using the CommonJS compatibility wrapper syntax, where require is implicitly provided:
define(function (require) {
var r = require('foo');
});
EDIT: Also, now that you have added the test module: within your test’s define, '/src/api/nameApi' is not a valid AMD module identifier.
Related
I want to use datepickk.js inside my module: even thought this plugin supports AMD I couldn't load it inside RequireJS:
http://jsfiddle.net/numediaweb/5xbqqr0j/13/
// Config Object
requirejs.config({
// Local Directory
baseUrl: "/js",
// Script Locations
paths: {
// Common Libraries
"jquery": "//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min",
"datepickk": "//crsten.github.io/datepickk/dist/datepickk.min"
}
});
// Check Dependencies
requirejs(['jquery', 'datepickk'], function ($, Datepickk) {
var loadedMods = require.s.contexts._.defined;
console.log(loadedMods);
$('#message').text('Loaded modules => '+JSON.stringify(loadedMods));
return {};
});
If you check the console you will see that jquery is defined and the module not.
Any idea why this happens?
I tried another variation of loading this module:
require.config({
paths: {
'Datepickk': '//crsten.github.io/datepickk/dist/datepickk.min'
},
But then I get this error:
datepickk.js:1146 Uncaught TypeError: Cannot freeze
at Function.freeze (<anonymous>)
at Datepickk (datepickk.js:1146)
at Object.execCb (require.js:1693)
at Module.check (require.js:881)
at Module.enable (require.js:1173)
at Module.init (require.js:786)
at callGetModule (require.js:1200)
at Object.completeLoad (require.js:1587)
at HTMLScriptElement.onScriptLoad (require.js:1714)
Whoever wrote the AMD code for datepickk.js needs to read up on how to write AMD modules. There are two problems:
The module name is hardcoded as Datepickk because the define call is define('Datepickk', Datepickk). The first argument hardcodes the name. This is really a bad thing to do, as the RequireJS documentation is clear that developers should not hardcode names and instead let the optimizer add a name as needed, but here someone was not paying attention.
This explains why your 2nd configuration, the one with Datepickk in paths works, but your first one does not. You must refer to it as Datepickk in your paths configuration. If you want your own code to refer to it as datepickk, you can use a map configuration in addition to paths:
map: {
"*": {
datepickk: "Datepickk"
}
}
Yeah, even if you fix the above, you still get the error you ran into. Looking at the documentation for Datepickk I see that you are use it with do new Datepickk(...). If you do this, then the object to be frozen should be the new object that is assigned to this by the JavaScript virtual machine when the constructor executes. If you look at the code that makes Datepickk available to other code, this is what you see:
if ( typeof define === 'function' && define.amd ) define('Datepickk', Datepickk);
else if ( typeof exports === 'object' ) module.exports = Datepickk;
else window.Datepickk = Datepickk;
The 2nd and 3rd branch export the Datepickk constructor to the world. That's fine. The 1st branch though, which is the one that matters to you, calls define with Datepickk acting as a module factory. When RequireJS executes the define call, it immediately calls Datepickk to build the module. In this situation this is not set to any specific value, so it gets set to the current Window instance (the JavaScript virtual machine does that) and Object.freeze fails. The define call should be:
define(function () {
return Datepickk;
});
(I've also removed the hardcoded module name.) This builds a module that has for value the function Datepickk.
The code I'm trying to test relies on RequireJs loader plugins. Example with requirejs/text:
require(['text!templates/foo'], function (data) {
// handle loaded data
});
For a specific unit test, I'm trying to mock the response for text!templates/foo and override with one relevant for the test:
it('should load a template', function (done) {
// TODO: mock 'text!templates/foo' here to return 'mock_data'
// templateViewer uses the text plugin internally to do the actual loading
templateViewer.templateFor('foo', function (error, templateData) {
expect(templateData).toEqual('mock_data');
done();
});
});
I've looked at RequireJs dependency mock solutions, especially Squire.js but it seems they are all suited for mocking regular dependencies and not plugin responses.
I've also looked at stub libraries like sinon to maybe replace the actual require call but that seems problematic.
What's the recommended practice? I prefer not to replace the entire text plugin with a mock one in my requirejs configuration, just override some of its responses in specific tests.
My setup is node+mocha+requirejs
Edit
Please see this example fiddle project to see my issue with Squire:
http://runnable.com/VUBoI0ex6v9Gs-BJ/squirejs-with-plugins-for-node-js-and-hello-world
This will mock what you'd get from requiring text!foo/x.html. Plugins are not special, you just need to mock the entire path, including the plugin name.
var requirejs = require("requirejs");
var assert = require("assert");
requirejs.config({
baseUrl: __dirname,
packages: [
{
name: "squire",
location: "node_modules/squirejs",
main: "src/Squire"
}
]
});
var x;
before(function (done) {
requirejs(["squire"], function (Squire) {
var injector = new Squire();
injector.mock("text!foo/x.html", "foo").require(["text!foo/x.html"],
function (_x) {
x = _x;
done();
});
});
});
it("foo", function () {
assert.equal(x, "foo");
});
The problem you run into with the example code you added to your question is that you use the global require instead of using a require passed by your loader. You should add require as a dependency:
define(['require', ....], function (require, ....) {
The require module is special and reserved by RequireJS. It returns a reference to the require function. You must use it, for instance, when you use RequireJS's contexts so that a module loaded in a specific context uses a require function that is bound to that context. SquireJS also needs you to do this so that it can trap your calls to require. The global require bypasses SquireJS.
Is it possible to use the default node require function in a file that has been called through requirejs?
define(["require", "exports"], function(require, exports) {
//...
var Schema = require(DaoPublic._schemasDirectory + schemaFilename);
}
I always get ReferenceError: module is not defined, I also tried to load the schema using requireJs, same, because the file itself is coded as CommonJs, not AMD compatible.
Any solution?
Note that the loaded schema is in CommonJS and I need to keep this way, since it's used by several DAO, some in AMD and other in CommonJs. (Funny part)
Example of requested file (schema):
var userSchema = {
/**
* User Login, used as id to connect between all our platforms.
*/
login: {
type: String,
match: /^[a-zA-Z0-9_-]+$/,
trim: true,
required: true,
notEmpty: true,
unique: true,
check: {
minLength: 4,
maxLength: 16
}
}
};
module.exports = userSchema;
The problem is that your code is set so that RequireJS is able to find the CommonJS module by itself. However, when RequireJS is running in Node and cannot find a module, it will call Node's require function, which is what you need. So it is possible (with RequireJS) to have an AMD module use Node's require but the trick is getting RequireJS to not see the module in the first place.
Proof of Concept
Here's a proof of concept. The main file named test.js:
var requirejs = require("requirejs");
function myRequire(path) {
if (path.lastIndexOf("schemas/", 0) === 0)
path = "./" + path;
return require(path);
}
requirejs.config({
paths: {
"schemas": "BOGUS"
},
nodeRequire: myRequire
});
requirejs(['foo'], function (foo) {
console.log(foo);
});
The file foo.js:
define(["require", "exports"], function(require, exports) {
return require("./schemas/x") + " by way of foo";
});
The file schemas/x.js:
module.exports = "x";
If you run it with node test.js, you'll get on the console:
x by way of foo
Explanation
I'm calling this a "proof of concept" because I've not considered all eventualities.
The paths setting is there to throw RequireJS off track. BOGUS must be a non-existent directory. When RequireJS tries to load the module ./schemas/x, it tries to load the file ./BOGUS/x.js and does not find it. So it calls Node's require.
The nodeRequire setting tells RequireJS that Node's require function is myRequire. This is a useful lie.
The myRequire function changes the path to add the ./ at the start before calling Node's require. The issue here is that for some reason RequireJS transforms ./schemas/x to schemas/x before it gives the path to Node's require function, and Node will then be unable to find the module. Adding back the ./ at the start of the path name fixes this. I've tried a whole bunch of path variants but none of them worked. Some variants were such that RequireJS was able to find the module by itself and thus never tried calling Node's require or they prevented Node from finding the module. There may be a better way to fix this, which I've not found. (This is one reason why I'm calling this a "proof of concept".) Note that I've designed this function to only alter the paths that start with schemas/.
Other Possibilities
I've looked at other possibilities but they did not appear to me very promising. For instance, customizing NODE_PATH would eliminate myRequire but such customization is not always doable or desirable.
I have a strange behaviour in require, that I dont know how to avoid (or maybe I have my basics wrong?).
Consider the following code:
define (require) ->
potoo = require "potoo"
service = require "communication.data"
downloadIfNeeded = ->
# ...
service.download()
new potoo.App
pageContainer: potoo.UI.NGStylePage
userRequired: true
stdRoute: "overview"
onLogin: downloadIfNeeded
This is not going to work, because 'communication.data' itself requires 'app' (the code shown). so we obviously have a circular dependency. That fails with a 'Uncaught Error: Module name "app" has not been loaded yet for context: _'
Since the downloadIfNeeded function doesn't get called until after the user actually clicks something, I figured, that something like the following should work:
define (require) ->
potoo = require "potoo"
downloadIfNeeded = ->
service = require "communication.data"
service.download()
...
But that actually throws the same error as above. To make it work, i have to use a little hack. I alias the require function with some other name:
define (require) ->
potoo = require "potoo"
reqs = require
downloadIfNeeded = ->
service = reqs "communication.data"
service.download()
...
Is this the best way to do so? Or would you recommend the CommonJS Style (module.export) that is also supported by requirejs.
I've done a test here and was able to find a solution. What you have is equivalent to this JavaScript:
define(function (require) {
This is enough to be able to use the (fake) synchronous form of require. However, RequireJS will give you the error you got when you try to use a synchronous require and you have circular dependencies. What you need is this:
define(function (require, exports, module) {
This is so that your module uses exports to export its values and consequently RequireJS has an object that can be updated when the module has finished initialized.
I am currently using requirejs to manage module js/css dependencies.
I'd like to discover the possibilities of having node do this via a centralized config file.
So instead of manually doing something like
define([
'jquery'
'lib/somelib'
'views/someview']
within each module.
I'd have node inject the dependencies ie
require('moduleA').setDeps('jquery','lib/somelib','views/someview')
Anyway, I'm interested in any projects looking at dependency injection for node.
thanks
I've come up with a solution for dependency injection. It's called injectr, and it uses node's vm library and replaces the default functionality of require when including a file.
So in your tests, instead of require('libToTest'), use injectr('libToTest' { 'libToMock' : myMock });. I wanted to make the interface as straightforward as possible, with no need to alter the code being tested. I think it works quite well.
It's just worth noting that injectr files are relative to the working directory, unlike require which is relative to the current file, but that shouldn't matter because it's only used in tests.
I've previously toyed with the idea of providing an alternate require to make a form of dependency injection available in Node.js.
Module code
For example, suppose you have following statements in code.js:
fs = require('fs');
console.log(fs.readFileSync('text.txt', 'utf-8'));
If you run this code with node code.js, then it will print out the contents of text.txt.
Injector code
However, suppose you have a test module that wants to abstract away the file system.
Your test file test.js could then look like this:
var origRequire = global.require;
global.require = dependencyLookup;
require('./code.js');
function dependencyLookup (file) {
switch (file) {
case 'fs': return { readFileSync: function () { return "test contents"; } };
default: return origRequire(file);
}
}
If you now run node test.js, it will print out "test contents", even though it includes code.js.
I've also written a module to accomplish this, it's called rewire. Just use npm install rewire and then:
var rewire = require("rewire"),
myModule = rewire("./path/to/myModule.js"); // exactly like require()
// Your module will now export a special setter and getter for private variables.
myModule.__set__("myPrivateVar", 123);
myModule.__get__("myPrivateVar"); // = 123
// This allows you to mock almost everything within the module e.g. the fs-module.
// Just pass the variable name as first parameter and your mock as second.
myModule.__set__("fs", {
readFile: function (path, encoding, cb) {
cb(null, "Success!");
}
});
myModule.readSomethingFromFileSystem(function (err, data) {
console.log(data); // = Success!
});
I've been inspired by Nathan MacInnes's injectr but used a different approach. I don't use vm to eval the test-module, in fact I use node's own require. This way your module behaves exactly like using require() (except your modifications). Also debugging is fully supported.