Skip to content

Instantly share code, notes, and snippets.

@thecodedrift
Last active February 11, 2024 22:31
Show Gist options
  • Save thecodedrift/6894733 to your computer and use it in GitHub Desktop.
Save thecodedrift/6894733 to your computer and use it in GitHub Desktop.
Shim your AMD
/*
================================================================================
ABOUT THIS FILE - amd shim https://gist.github.com/Jakobo/6894733
================================================================================
This file is a helper shim for pure AMD modules. When writing an AMD module,
you need to have access to a global "define" method. This creates a global
define(), a global require(), and a global AMD object.
define/require work like proper sinon stubs. Any define() calls that happen
before your test are automatically queued to run. You can then stub out
define() to respond how you need it to, including expectations.
AMD.run() allows you to "unpause" your loader, running through your
define() statements as if you are doing them for the first time; this time
with sinon stubs.
AMD.exports is an array [] containing the exports. It's sequentially
written to for every define() call made.
*/
var AMD = {};
var define;
var require;
(function() {
var exports = [];
var queue = [];
var oldDefine = define;
var oldRequire = require;
// replace the global define if it exists with a generic buffer
define = function() {
queue.push([].slice.call(arguments, 0));
};
define.amd = true;
var resolveDefineCall = function(args) {
var resolved = null;
var factory = null;
// swap the "factory" object if it is a function with an interceptor
// this saves all the "resolved" variables transparently to the end
// user
factory = args.pop();
if (typeof factory == 'function') {
args.push(function() {
resolved = [].slice.call(arguments, 0);
});
}
// through sinon we go!
define.apply(define, args);
// if the factory was a function, run it with our resolved things
// otherwise, return the object literal
if (typeof factory == 'function') {
exports.push(factory.apply(factory, resolved));
}
else {
exports.push(factory);
}
};
// initializes a sinon version of the AMD environment
AMD.init = function(stubber) {
if (!stubber && !sinon) {
throw new Error('AMD Shim requires at least some mock generator. We recommend sinon.js');
}
if (!stubber) {
stubber = sinon.stub;
}
define = stubber();
require = stubber();
AMD.ready = true;
};
// tun through all our define() statements now that we've stubbed them out
// exports end up in AMD.exports
AMD.run = function() {
if (!AMD.ready) {
throw new Error('You must call AMD.init() to stub define and require first');
}
// empty the buffer, and shove each item through sinon.js
// a while loop ensures more define() goes through sinon
var args = queue.shift();
while (args) {
resolveDefineCall(args);
args = queue.shift();
}
};
AMD.restore = function() {
AMD.ready = false;
define = oldDefine;
require = oldRequire;
};
// all exports from define()d items
AMD.exports = exports;
}(typeof sinon != 'undefined'));
// 1. Include amd.js
// 2. Include your AMD module
// 3. Create local mocks that are your dependencies
var myMock = {
foo: sinon.stub(),
bar: sinon.stub()
};
var myOtherMock = {
baz: sinon.stub();
quux: sinon.stub();
};
// 4. define is sinon compatible by the time you get here
// when it's called, call the callback (2) with our mocks.
// if your "exports" was just an object literal, you
// don't need this line.
AMD.init();
define.callsArgWith(2, myMock, myOtherMock);
// 5. run all AMD modules (amd.js was holding them for you)
AMD.run();
// 6. test your exports!
// yes, this is a contrived example to illustrate
describe('my module test', funciton() {
var myModule = AMD.exports[0]; // ... use normally
it('should exist', function() {
expect(myModule).to.be.ok();
});
});
// 7. restore variables
AMD.restore();
@chadhietala
Copy link

Were you going to commit AMD.js to trunk? I kinda need it. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment