pongular
Version:
AngularJS dependency injection ripped out for use on the server.
183 lines (171 loc) • 6.41 kB
JavaScript
/*global require: false, exports: false, global: false, beforeEach: false, afterEach: false, setup: false, teardown: false */
var pongular = require('./pongular').pongular,
utils = require('./utils');
if(global.jasmine || global.mocha) {
var currentSpec = null,
isSpecRunning = function() {
return !!currentSpec;
};
(beforeEach || setup)(function() {
currentSpec = this;
});
(afterEach || teardown)(function() {
var injector = currentSpec.$injector;
utils.forEach(currentSpec.$modules, function(module) {
if (module && module.$$hashKey) {
module.$$hashKey = undefined;
}
});
currentSpec.$injector = null;
currentSpec.$modules = null;
currentSpec = null;
});
/**
* @ngdoc function
* @name pongular.mock.module
* @description
*
* This function registers a module configuration code. It collects the configuration information
* which will be used when the injector is created by {@link pongular.mock.inject inject}.
*
* See {@link pongular.mock.inject inject} for usage example
*
* @param {...(string|Function|Object)} fns any number of modules which are represented as string
* aliases or as anonymous module initialization functions. The modules are used to
* configure the injector. The 'ng' and 'ngMock' modules are automatically loaded. If an
* object literal is passed they will be register as values in the module, the key being
* the module name and the value being what is returned.
*/
exports.module = function() {
var moduleFns = Array.prototype.slice.call(arguments, 0);
return isSpecRunning() ? workFn() : workFn;
/////////////////////
function workFn() {
if (currentSpec.$injector) {
throw new Error('Injector already created, can not register a module!');
} else {
var modules = currentSpec.$modules || (currentSpec.$modules = []);
utils.forEach(moduleFns, function(module) {
if (utils.isObject(module) && !utils.isArray(module)) {
modules.push(function($provide) {
utils.forEach(module, function(value, key) {
$provide.value(key, value);
});
});
} else {
modules.push(module);
}
});
}
}
};
/**
* @ngdoc function
* @name pongular.mock.inject
* @description
*
* *NOTE*: This function is also published on global for easy access.<br>
*
* The inject function wraps a function into an injectable function. The inject() creates new
* instance of {@link AUTO.$injector $injector} per test, which is then used for
* resolving references.
*
*
* ## Resolving References (Underscore Wrapping)
* Often, we would like to inject a reference once, in a `beforeEach()` block and reuse this
* in multiple `it()` clauses. To be able to do this we must assign the reference to a variable
* that is declared in the scope of the `describe()` block. Since we would, most likely, want
* the variable to have the same name of the reference we have a problem, since the parameter
* to the `inject()` function would hide the outer variable.
*
* To help with this, the injected parameters can, optionally, be enclosed with underscores.
* These are ignored by the injector when the reference name is resolved.
*
* For example, the parameter `_myService_` would be resolved as the reference `myService`.
* Since it is available in the function body as _myService_, we can then assign it to a variable
* defined in an outer scope.
*
* ```
* // Defined out reference variable outside
* var myService;
*
* // Wrap the parameter in underscores
* beforeEach( inject( function(_myService_){
* myService = _myService_;
* }));
*
* // Use myService in a series of tests.
* it('makes use of myService', function() {
* myService.doStuff();
* });
*
* ```
*
* See also {@link pongular.mock.module pongular.mock.module}
*
* ## Example
* Example of what a typical jasmine tests looks like with the inject method.
* <pre>
*
* pongular.module('myApplicationModule', [])
* .value('mode', 'app')
* .value('version', 'v1.0.1');
*
*
* describe('MyApp', function() {
*
* // You need to load modules that you want to test,
* // it loads only the "ng" module by default.
* beforeEach(module('myApplicationModule'));
*
*
* // inject() is used to inject arguments of all given functions
* it('should provide a version', inject(function(mode, version) {
* expect(version).toEqual('v1.0.1');
* expect(mode).toEqual('app');
* }));
*
*
* // The inject and module method can also be used inside of the it or beforeEach
* it('should override a version and test the new version is injected', function() {
* // module() takes functions or strings (module aliases)
* module(function($provide) {
* $provide.value('version', 'overridden'); // override version here
* });
*
* inject(function(version) {
* expect(version).toEqual('overridden');
* });
* });
* });
*
* </pre>
*
* @param {...Function} fns any number of functions which will be injected using the injector.
*/
exports.inject = function() {
var blockFns = Array.prototype.slice.call(arguments, 0);
var errorForStack = new Error('Declaration Location');
return isSpecRunning() ? workFn() : workFn;
/////////////////////
function workFn() {
var modules = currentSpec.$modules || [];
var injector = currentSpec.$injector;
if (!injector) {
injector = currentSpec.$injector = pongular.injector(modules);
}
for(var i = 0, ii = blockFns.length; i < ii; i++) {
try {
/* jshint -W040 *//* Jasmine explicitly provides a `this` object when calling functions */
injector.invoke(blockFns[i] || pongular.noop, this);
/* jshint +W040 */
} catch (e) {
if(e.stack && errorForStack) e.stack += '\n' + errorForStack.stack;
throw e;
} finally {
errorForStack = null;
}
}
}
};
}