public
Last active

requirejs (AMD) loader plugin for jstestdriver

  • Download Gist
loaderPlugin.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
(function() {
 
var loadSource = function(file, onSourceLoad) {
if (!file.fileSrc.match(/js-test/)) {
return false;
}
 
require([file.fileSrc], function() {
onSourceLoad({file: file, success: true, message: ''});
}, function(err) {
var msg = err.requireType;
if (err.requireModules) {
msg += ": " + err.requireModules.join(', ');
}
onSourceLoad({file: file, success: false, message: msg});
});
 
return true;
};
 
jstestdriver.pluginRegistrar.register({
name: 'AMDLoaderPlugin',
loadSource: loadSource
});
 
}());

This will allow jstestdriver to load anonymous AMD modules, such as those supported by requirejs. It does not require you to run the optimizer, give the modules names, monkey-patch define(), or preload the modules. The plugin works by delegating loading of test modules to requirejs.

Include this file in the 'load' section of the jstd config file (along with require.js and a config file for require.js).

load:
 - AMDLoaderPlugin.js
 - require.js
 - requirejs-config.js

test:
 - js-test/*.js

Change the pattern in the file.fileSrc.match() as necessary so it only matches test files that are requirejs modules. It would be nice if we could keep this pattern in the jstd config file, but I haven't found an easy way to pass config to the plugin.

Test modules should be declared with define(), not require().

define(['jquery', 'mymodule'], function($, mymodule) {
   TestCase('test something', /* blah blah */ );
});

Theory of operation

AMD is a problem in jstd because it starts running the test suite before the test dependencies are loaded. Jstd will make an async call to load a test script. When the script is loaded, the onSourceLoad callback fires, starting the tests. If the script makes a require() or define() call, those calls will not have completed when the testing starts.

At first glance preloading modules looks like a workable solution, because then the require() call will return immediately, instead of causing an asynchronous fetch. However it merely introduces a race condition, because jstd has no way of waiting for the preloading to finish. I you add deps: ['main'] to your requirejs config, requirejs will start loading your modules at the same time that jstd starts loading your test scripts. Whether it works depends on which loads first.

The solution is to not invoke the onSourceLoad for a test module until all of its dependencies are also loaded. This is very easy to do with requirejs.

Important safety tip: the first time jstd loads source, it does not use the loadSource plugin. I'm not sure how to coerce it to load and activate the plugin before loading other source. To activate it, always use the '--reset' flag when running tests.

Great work! Finally a solution for testing AMD modules with dependancies! One comment, shouldn´t this plugin be loaded after require.js (in the jstd config file)? I had to move it after to get it working.

acthp, I am trying to get this working and am running into problems where it doesn't seem as though the plugin gets loaded. What do you mean by, the first time jstd loads source, it doesn't use the loadSource plugin? Does the reset flag need to be on the server and client instance of jstd?

Hi acthp,

Do you have a working example that you could share? It seems like I am missing some key configurations here and there. It would be very helpful if you could help.

Thanks a lot in advance!

The easiest workaround I've found for this is to just put explicit module names inside each of my define() calls.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.