lfr-amd-loader
Version:
AMD Loader with support for combo URL and conditional loading
553 lines (426 loc) • 17.7 kB
JavaScript
;
require('./fixture/common.js');
require('./fixture/script.js');
var assert = require('chai').assert;
var sinon = require('sinon');
describe('Loader', function() {
beforeEach(function() {
Object.keys(require.cache).forEach(function(cache) {
delete require.cache[cache];
});
global.__CONFIG__ = {
url: __dirname + '/fixture',
basePath: '/modules',
maps: {
'liferay': 'liferay@1.0.0',
'liferay2': 'liferay@2.0.0'
},
modules: {
module1: {
dependencies: ['module2', 'module3']
},
module2: {
dependencies: []
},
module3: {
dependencies: []
},
module5: {
dependencies: ['module6', 'module7', 'exports']
},
module6: {
dependencies: ['module7', 'exports']
},
module7: {
dependencies: ['exports']
},
moduleMissing: {
dependencies: []
},
moduleCyclic1: {
dependencies: ['moduleCyclic2']
},
moduleCyclic2: {
dependencies: ['moduleCyclic1']
},
'liferay@1.0.0/relative1': {
dependencies: ['exports', 'module', './relative2'],
path: 'relative1.js'
},
'liferay@1.0.0/relative2': {
dependencies: ['exports', 'module', './sub-relative/sub-relative1'],
path: 'relative2.js'
},
'liferay@1.0.0/relative3': {
dependencies: ['exports', 'module'],
path: 'relative3.js'
},
'liferay@1.0.0/sub-relative/sub-relative1': {
dependencies: ['exports', 'module', '../relative3'],
'path': 'sub-relative/sub-relative1.js'
},
'liferay@1.0.0': {
dependencies: ['exports'],
path: 'liferay.js'
},
'liferay@2.0.0': {
dependencies: ['exports', 'liferay'],
path: 'liferay2.js'
},
'exports-dep': {
dependencies: ['exports'],
path: 'exports-dep.js'
},
'module-dep': {
dependencies: ['exports', 'module'],
path: 'module-dep.js'
},
'liferay@1.0.0/empty': {
dependencies: [],
path: 'empty.js'
},
'liferay@1.0.0/mappeddeps': {
dependencies: ['liferay', 'liferay2'],
path: 'mappeddeps.js'
}
}
};
global.__CONFIG__.waitTimeout = 0;
global.__CONFIG__.paths = {};
require('../umd/config-parser.js');
require('../umd/event-emitter.js');
require('../umd/script-loader.js');
});
it('should add module to the configuration', function() {
var moduleName = '_' + Math.random().toString();
var modules = Loader.getModules();
assert.notProperty(modules, moduleName);
var module = Loader.addModule({
name: moduleName,
dependencies: []
});
assert.property(modules, moduleName);
assert.isObject(module);
assert.propertyVal(module, 'name', moduleName);
});
it('should define a module without dependencies (except exports)', function (done) {
var impl = sinon.spy(function (exports) {
exports.pejJung = {};
});
Loader.define('pej-jung', ['exports'], impl);
setTimeout(function() {
assert(impl.notCalled);
var modules = Loader.getModules();
var module = modules['pej-jung'];
assert.ok(module);
assert.strictEqual('pej-jung', module.name);
assert.strictEqual(module.pendingImplementation, impl);
assert.strictEqual(module.requested, undefined);
done();
}, 50);
});
it('should discover missing dependencies of already defined modules', function() {
var module1 = Math.random().toString();
var dep1 = Math.random().toString();
var module2 = Math.random().toString();
var dep2 = Math.random().toString();
Loader.define(module1, [dep1], function() {return 1;});
Loader.define(module2, [dep2], function() {return 1;});
var missingDeps = Loader._getMissingDepenencies([module1, module2]);
assert.isArray(missingDeps);
assert.strictEqual(2, missingDeps.length);
assert.sameMembers([dep1, dep2], missingDeps);
});
it('should resolve relative dependencies path in define', function() {
var module = 'test/sub1/' + Math.random().toString();
var depName = Math.random().toString();
var dep = '../' + depName;
Loader.define(module, [dep], function() {});
var modules = Loader.getModules();
assert.property(modules, module);
assert.isArray(modules[module].dependencies);
assert.strictEqual('test/' + depName, modules[module].dependencies[0]);
});
it('should not accept define with only one parameter', function() {
assert.isUndefined(Loader.define(function() {}));
});
it('should not accept define with two parameters', function() {
assert.isUndefined(Loader.define(['exports'], function() {}));
});
it('should define a module with name and without dependencies', function() {
var module = Math.random().toString();
var impl = function() {};
Loader.define(module, impl);
var modules = Loader.getModules();
assert.property(modules, module);
assert.isArray(modules[module].dependencies);
assert.strictEqual(modules[module].pendingImplementation, impl);
assert.sameMembers(['exports', 'module'], modules[module].dependencies);
});
it('should define itself as AMD compatibe loader', function() {
assert.property(global.define, 'amd');
assert.isObject(global.define.amd);
});
it('should register unregistered modules in require', function() {
var module = Math.random().toString();
Loader.require(module);
var modules = Loader.getModules();
assert.property(modules, module);
assert.isObject(modules[module]);
assert.property(modules[module], 'dependencies');
assert.isArray(modules[module].dependencies);
});
it('should map modules in require', function() {
var module = Math.random().toString();
var alias = Math.random().toString();
global.__CONFIG__.maps[module] = alias;
Loader.require(module, [], function() {return 1;});
var modules = Loader.getModules();
assert.property(modules, alias);
});
it('should load already defined (manually) modules', function (done) {
var failure = sinon.stub();
var success = sinon.stub();
Loader.require(['aui-123', 'pej-jung'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled);
assert.isTrue(success.calledOnce);
done();
}, 50);
});
it('should load unregistered modules', function (done) {
var one;
var failure = sinon.stub();
var success = sinon.spy(function(_one) {
one = _one;
});
global.__CONFIG__.paths['one'] = '/modules2/one';
global.__CONFIG__.paths['two'] = '/modules2/two';
global.__CONFIG__.paths['three'] = '/modules2/three';
Loader.require(['one'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled);
assert.isTrue(success.calledOnce);
assert.isFunction(one);
done();
}, 50);
});
it('should load previously registered modules', function (done) {
var failure = sinon.stub();
var success = sinon.stub();
Loader.require('module1', success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled);
assert.isTrue(success.calledOnce);
done();
}, 50);
});
it('should load module which implementation is an object', function(done) {
var moduleName = Math.random().toString();
Loader.addModule({
name: 'impl_as_object',
dependencies: ['exports'],
path: '/modules2/impl_as_object.js'
});
var failure = sinon.stub();
var implementation;
var success = sinon.spy(function(impl) {
implementation = impl;
});
Loader.require(['impl_as_object'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled, 'Failure should not be called');
assert.isTrue(success.calledOnce, 'Success should be called');
assert.isObject(implementation, 'The implementation should be an object');
assert.property(implementation, 'pesho');
done();
}, 50);
});
it('should fail on registered but not existing file', function (done) {
var failure = sinon.stub();
var success = sinon.stub();
Loader.require('moduleMissing', success, failure);
setTimeout(function() {
assert.isTrue(failure.calledOnce);
assert.isTrue(success.notCalled);
done();
}, 50);
});
it('should fail if there are cyclic dependencies', function (done) {
var failure = sinon.stub();
var success = sinon.stub();
Loader.require('moduleCyclic1', 'moduleCyclic2', success, failure);
setTimeout(function() {
assert.isTrue(failure.calledOnce);
assert.isTrue(success.notCalled);
done();
}, 50);
});
it('should succeed when requiring modules multiple times', function (done) {
var failure = sinon.stub();
var success = sinon.stub();
Loader.require(['module5'], success, failure);
Loader.require(['module6'], success, failure);
Loader.require(['module7'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled);
assert.isTrue(success.calledThrice);
done();
}, 50);
});
it('should ignore success and callback if not functions', function() {
Loader.require(['module1'], null, null);
});
it('should return conditional modules', function() {
var conditionalModules = Loader.getConditionalModules();
assert.deepEqual({}, conditionalModules);
});
it('should load aliased modules', function(done) {
var failure = sinon.stub();
var success = sinon.stub();
Loader.require(['liferay'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled);
assert.isTrue(success.calledOnce);
done();
}, 50);
});
it('should load aliased modules with aliased dependencies', function(done) {
var failure = sinon.stub();
var success = sinon.stub();
Loader.require(['liferay2'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled, 'Failure should be not called');
assert.isTrue(success.calledOnce, 'Success should be called');
done();
}, 50);
});
it('should load module with "exports" dependency', function(done) {
var failure = sinon.stub();
var successValue;
var success = sinon.spy(function(val) {
successValue = val;
});
Loader.require(['exports-dep'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled, 'Failure should be not called');
assert.isTrue(success.calledOnce, 'Success should be called');
assert.isObject(successValue);
assert.property(successValue, 'default');
assert.isFunction(successValue.default);
assert.strictEqual('alabala', successValue.default.name);
done();
}, 50);
});
it('should load module with "module" dependency', function(done) {
var failure = sinon.stub();
var successValue;
var success = sinon.spy(function(val) {
successValue = val;
});
Loader.require(['module-dep'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled, 'Failure should be not called');
assert.isTrue(success.calledOnce, 'Success should be called');
assert.isFunction(successValue);
assert.strictEqual('alabala', successValue.name);
done();
}, 50);
});
it('should load module with relative path', function(done) {
var failure = sinon.stub();
var successValue;
var success = sinon.spy(function(val) {
successValue = val;
});
Loader.require(['liferay/relative1'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled, 'Failure should be not called');
assert.isTrue(success.calledOnce, 'Success should be called');
assert.isObject(successValue);
done();
}, 50);
});
it('should resolve the missing dependencies without multiple require calls', function(done) {
Loader.require = sinon.spy(Loader.require);
var failure = sinon.stub();
var success = sinon.stub();
Loader.require.call(Loader, ['liferay@1.0.0/relative1'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled, 'Failure should be not called');
assert.isTrue(success.calledOnce, 'Success should be called');
assert.isTrue(Loader.require.calledOnce, 'Require should be called once');
done();
}, 50);
});
it('should load module with mapped dependencies without multiple require calls', function(done) {
Loader.require = sinon.spy(Loader.require);
var failure = sinon.stub();
var success = sinon.stub();
Loader.require.call(Loader, ['liferay/mappeddeps'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled, 'Failure should be not called');
assert.isTrue(success.calledOnce, 'Success should be called');
assert.isTrue(Loader.require.calledOnce, 'Require should be called once');
done();
}, 50);
});
it('should cancel a require in case of failure because of an empty module', function(done) {
global.__CONFIG__.waitTimeout = 20;
var failureError;
var failure = sinon.spy(function(error) {
failureError = error;
});
var success = sinon.stub();
Loader.require(['liferay@1.0.0/empty'], success, failure);
setTimeout(function() {
assert.isTrue(failure.calledOnce, 'Failure should be called once');
assert.isTrue(success.notCalled, 'Success should not be called');
assert.instanceOf(failureError, Error);
assert.property(failureError, 'dependecies');
assert.property(failureError, 'mappedModules');
assert.property(failureError, 'missingDependencies');
assert.property(failureError, 'modules');
done();
}, 50);
});
it('should failure after require timeout, but a valid module should still load', function(done) {
global.__CONFIG__.waitTimeout = 1;
global.__CONFIG__.paths['delay'] = '/modules2/delay';
var failureError;
var failure = sinon.spy(function(error) {
failureError = error;
});
var success = sinon.stub();
Loader.require(['delay'], success, failure);
setTimeout(function() {
assert.isTrue(failure.calledOnce, 'Failure should be called');
assert.property(failureError, 'modules');
assert.isTrue(success.notCalled, 'Success should be not called');
// Delay module adds "delay" property to global
assert.strictEqual(1, global.delay);
done();
}, 50);
});
it('should load modules which do not expose a define function', function(done) {
Loader.addModule({
dependencies: [],
exports: '_',
name: 'underscore',
path: '/modules2/underscore.js'
});
var failure = sinon.spy(function(error) {
console.error(error);
});
var success = sinon.stub();
Loader.require(['underscore'], success, failure);
setTimeout(function() {
assert.isTrue(failure.notCalled, 'Failure should not be called');
assert.isTrue(success.calledOnce, 'Success should be called');
assert.property(global, '_');
var modules = Loader.getModules();
assert.property(modules['underscore'], 'implementation');
done();
}, 50);
});
});