2012-01-06 18:10:47 -08:00
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
describe('module loader', function() {
|
|
|
|
|
var window;
|
|
|
|
|
|
2014-10-22 17:31:27 -04:00
|
|
|
beforeEach(function() {
|
2012-01-06 18:10:47 -08:00
|
|
|
window = {};
|
|
|
|
|
setupModuleLoader(window);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should set up namespace', function() {
|
|
|
|
|
expect(window.angular).toBeDefined();
|
|
|
|
|
expect(window.angular.module).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should not override existing namespace', function() {
|
|
|
|
|
var angular = window.angular;
|
|
|
|
|
var module = angular.module;
|
|
|
|
|
|
|
|
|
|
setupModuleLoader(window);
|
|
|
|
|
expect(window.angular).toBe(angular);
|
|
|
|
|
expect(window.angular.module).toBe(module);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should record calls', function() {
|
|
|
|
|
var otherModule = window.angular.module('other', []);
|
2012-01-13 14:19:10 -08:00
|
|
|
otherModule.config('otherInit');
|
2012-01-06 18:10:47 -08:00
|
|
|
|
2012-01-13 14:19:10 -08:00
|
|
|
var myModule = window.angular.module('my', ['other'], 'config');
|
2012-01-06 18:10:47 -08:00
|
|
|
|
2012-01-13 14:19:10 -08:00
|
|
|
expect(myModule.
|
2015-03-13 00:01:48 -05:00
|
|
|
decorator('dk', 'dv').
|
2012-03-08 15:59:32 -08:00
|
|
|
provider('sk', 'sv').
|
2012-01-06 18:10:47 -08:00
|
|
|
factory('fk', 'fv').
|
2012-03-08 16:53:15 -08:00
|
|
|
service('a', 'aa').
|
2012-01-06 18:10:47 -08:00
|
|
|
value('k', 'v').
|
|
|
|
|
filter('f', 'ff').
|
2011-12-14 02:55:31 +01:00
|
|
|
directive('d', 'dd').
|
2016-01-06 15:52:45 +01:00
|
|
|
component('c', 'cc').
|
2012-03-26 13:01:24 -07:00
|
|
|
controller('ctrl', 'ccc').
|
2012-01-13 14:19:10 -08:00
|
|
|
config('init2').
|
2012-02-22 13:28:42 -08:00
|
|
|
constant('abc', 123).
|
2012-01-13 14:19:10 -08:00
|
|
|
run('runBlock')).toBe(myModule);
|
2012-01-06 18:10:47 -08:00
|
|
|
|
|
|
|
|
expect(myModule.requires).toEqual(['other']);
|
2012-01-13 14:19:10 -08:00
|
|
|
expect(myModule._invokeQueue).toEqual([
|
2016-03-13 13:20:30 +01:00
|
|
|
['$provide', 'constant', jasmine.objectContaining(['abc', 123])],
|
|
|
|
|
['$provide', 'provider', jasmine.objectContaining(['sk', 'sv'])],
|
|
|
|
|
['$provide', 'factory', jasmine.objectContaining(['fk', 'fv'])],
|
|
|
|
|
['$provide', 'service', jasmine.objectContaining(['a', 'aa'])],
|
|
|
|
|
['$provide', 'value', jasmine.objectContaining(['k', 'v'])],
|
|
|
|
|
['$filterProvider', 'register', jasmine.objectContaining(['f', 'ff'])],
|
|
|
|
|
['$compileProvider', 'directive', jasmine.objectContaining(['d', 'dd'])],
|
|
|
|
|
['$compileProvider', 'component', jasmine.objectContaining(['c', 'cc'])],
|
|
|
|
|
['$controllerProvider', 'register', jasmine.objectContaining(['ctrl', 'ccc'])]
|
2014-04-17 10:11:28 -04:00
|
|
|
]);
|
|
|
|
|
expect(myModule._configBlocks).toEqual([
|
2016-03-13 13:20:30 +01:00
|
|
|
['$injector', 'invoke', jasmine.objectContaining(['config'])],
|
fix(loader): module.decorator order of operations is now irrelevant
`module.decorator` is now processed via the configBlocks order of operations and:
1. no longer throws error if declared before the provider being decorated.
2. guarantees the correct provider will be decorated when multiple, same-name
providers are defined.
(1) Prior to this fix, declaring `module.decorator` before the provider that it
decorates results in throwing an error:
```js
angular
.module('theApp', [])
.decorator('theFactory', moduleDecoratorFn)
.factory('theFactory', theFactoryFn);
// Error: [$injector:modulerr] Failed to instantiate module theApp due to:
// Error: [$injector:unpr] Unknown provider: theFactoryProvider
```
The result of this fix now allows for the declaration order above.
(2) Prior to this fix, declaring `module.decorator` before the final, same-named
provider results in that provider **not** being decorated as expected:
**NOTE:** Angular does not use provider name spacing, so the final declared
provider is selected if multiple, same-named providers are declared.
```js
angular
.module('theApp', [])
.factory('theFactory', theFactoryFn)
.decorator('theFactory', moduleDecoratorFn)
.factory('theFactory', theOtherFactoryFn);
```
`theOtherFactoryFn` is selected as 'theFactory' provider, but prior to this fix it is **not**
decorated via `moduleDecoratorFn`. This fix ensures that `theOtherFactoryFn` will be decorated as
expected when using the declaration order above.
Closes #12382
Closes #14348
BREAKING CHANGE:
`module.decorator` declarations are now processed as part of the `module.config`
queue and may result in providers being decorated in a different order if
`module.config` blocks are also used to decorate providers via
`$provide.decorator`.
For example, consider the following declaration order in which 'theFactory' is
decorated by both a `module.decorator` and a `$provide.decorator`:
```js
angular
.module('theApp', [])
.factory('theFactory', theFactoryFn)
.config(function($provide) {
$provide.decorator('theFactory', provideDecoratorFn);
})
.decorator('theFactory', moduleDecoratorFn);
```
Prior to this fix, 'theFactory' provider would be decorated in the following
order:
1. moduleDecoratorFn
2. provideDecoratorFn
The result of this fix changes the order in which 'theFactory' is decorated
because now `module.decorator` declarations are processed in the same order as
`module.config` declarations:
1. provideDecoratorFn
2. moduleDecoratorFn
2016-03-30 17:30:42 -04:00
|
|
|
['$provide', 'decorator', jasmine.objectContaining(['dk', 'dv'])],
|
2016-03-13 13:20:30 +01:00
|
|
|
['$injector', 'invoke', jasmine.objectContaining(['init2'])]
|
2012-01-06 18:10:47 -08:00
|
|
|
]);
|
2012-01-13 14:19:10 -08:00
|
|
|
expect(myModule._runBlocks).toEqual(['runBlock']);
|
2012-01-06 18:10:47 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2016-08-10 12:13:14 +02:00
|
|
|
it('should not throw error when `module.decorator` is declared before provider that it decorates', function() {
|
fix(loader): module.decorator order of operations is now irrelevant
`module.decorator` is now processed via the configBlocks order of operations and:
1. no longer throws error if declared before the provider being decorated.
2. guarantees the correct provider will be decorated when multiple, same-name
providers are defined.
(1) Prior to this fix, declaring `module.decorator` before the provider that it
decorates results in throwing an error:
```js
angular
.module('theApp', [])
.decorator('theFactory', moduleDecoratorFn)
.factory('theFactory', theFactoryFn);
// Error: [$injector:modulerr] Failed to instantiate module theApp due to:
// Error: [$injector:unpr] Unknown provider: theFactoryProvider
```
The result of this fix now allows for the declaration order above.
(2) Prior to this fix, declaring `module.decorator` before the final, same-named
provider results in that provider **not** being decorated as expected:
**NOTE:** Angular does not use provider name spacing, so the final declared
provider is selected if multiple, same-named providers are declared.
```js
angular
.module('theApp', [])
.factory('theFactory', theFactoryFn)
.decorator('theFactory', moduleDecoratorFn)
.factory('theFactory', theOtherFactoryFn);
```
`theOtherFactoryFn` is selected as 'theFactory' provider, but prior to this fix it is **not**
decorated via `moduleDecoratorFn`. This fix ensures that `theOtherFactoryFn` will be decorated as
expected when using the declaration order above.
Closes #12382
Closes #14348
BREAKING CHANGE:
`module.decorator` declarations are now processed as part of the `module.config`
queue and may result in providers being decorated in a different order if
`module.config` blocks are also used to decorate providers via
`$provide.decorator`.
For example, consider the following declaration order in which 'theFactory' is
decorated by both a `module.decorator` and a `$provide.decorator`:
```js
angular
.module('theApp', [])
.factory('theFactory', theFactoryFn)
.config(function($provide) {
$provide.decorator('theFactory', provideDecoratorFn);
})
.decorator('theFactory', moduleDecoratorFn);
```
Prior to this fix, 'theFactory' provider would be decorated in the following
order:
1. moduleDecoratorFn
2. provideDecoratorFn
The result of this fix changes the order in which 'theFactory' is decorated
because now `module.decorator` declarations are processed in the same order as
`module.config` declarations:
1. provideDecoratorFn
2. moduleDecoratorFn
2016-03-30 17:30:42 -04:00
|
|
|
angular.module('theModule', []).
|
|
|
|
|
decorator('theProvider', function($delegate) { return $delegate; }).
|
|
|
|
|
factory('theProvider', function() { return {}; });
|
|
|
|
|
|
|
|
|
|
expect(function() {
|
|
|
|
|
createInjector(['theModule']);
|
|
|
|
|
}).not.toThrow();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2016-08-10 12:13:14 +02:00
|
|
|
it('should run decorators in order of declaration, even when mixed with provider.decorator', function() {
|
2016-05-24 17:34:08 +02:00
|
|
|
var log = '';
|
|
|
|
|
|
|
|
|
|
angular.module('theModule', [])
|
|
|
|
|
.factory('theProvider', function() {
|
|
|
|
|
return {api: 'provider'};
|
|
|
|
|
})
|
|
|
|
|
.decorator('theProvider', function($delegate) {
|
|
|
|
|
$delegate.api = $delegate.api + '-first';
|
|
|
|
|
return $delegate;
|
|
|
|
|
})
|
|
|
|
|
.config(function($provide) {
|
|
|
|
|
$provide.decorator('theProvider', function($delegate) {
|
|
|
|
|
$delegate.api = $delegate.api + '-second';
|
|
|
|
|
return $delegate;
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
.decorator('theProvider', function($delegate) {
|
|
|
|
|
$delegate.api = $delegate.api + '-third';
|
|
|
|
|
return $delegate;
|
|
|
|
|
})
|
|
|
|
|
.run(function(theProvider) {
|
|
|
|
|
log = theProvider.api;
|
2016-05-24 18:12:12 +02:00
|
|
|
});
|
2016-05-24 17:34:08 +02:00
|
|
|
|
|
|
|
|
createInjector(['theModule']);
|
|
|
|
|
expect(log).toBe('provider-first-second-third');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2016-08-10 12:13:14 +02:00
|
|
|
it('should decorate the last declared provider if multiple have been declared', function() {
|
2016-05-24 17:34:08 +02:00
|
|
|
var log = '';
|
|
|
|
|
|
|
|
|
|
angular.module('theModule', []).
|
2016-07-20 15:45:04 +02:00
|
|
|
factory('theProvider', function() {
|
|
|
|
|
return {
|
|
|
|
|
api: 'firstProvider'
|
|
|
|
|
};
|
|
|
|
|
}).
|
2016-05-24 17:34:08 +02:00
|
|
|
decorator('theProvider', function($delegate) {
|
|
|
|
|
$delegate.api = $delegate.api + '-decorator';
|
2016-07-20 15:45:04 +02:00
|
|
|
return $delegate;
|
|
|
|
|
}).
|
|
|
|
|
factory('theProvider', function() {
|
|
|
|
|
return {
|
|
|
|
|
api: 'secondProvider'
|
|
|
|
|
};
|
|
|
|
|
}).
|
2016-05-24 17:34:08 +02:00
|
|
|
run(function(theProvider) {
|
|
|
|
|
log = theProvider.api;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
createInjector(['theModule']);
|
2016-05-24 18:12:12 +02:00
|
|
|
expect(log).toBe('secondProvider-decorator');
|
2016-05-24 17:34:08 +02:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
2012-01-06 18:10:47 -08:00
|
|
|
it('should allow module redefinition', function() {
|
|
|
|
|
expect(window.angular.module('a', [])).not.toBe(window.angular.module('a', []));
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
it('should complain of no module', function() {
|
|
|
|
|
expect(function() {
|
|
|
|
|
window.angular.module('dontExist');
|
2016-08-10 12:13:14 +02:00
|
|
|
}).toThrowMinErr('$injector', 'nomod', 'Module \'dontExist\' is not available! You either misspelled the module name ' +
|
|
|
|
|
'or forgot to load it. If registering a module ensure that you specify the dependencies as the second ' +
|
|
|
|
|
'argument.');
|
2012-01-06 18:10:47 -08:00
|
|
|
});
|
2013-10-05 10:49:09 +01:00
|
|
|
|
|
|
|
|
it('should complain if a module is called "hasOwnProperty', function() {
|
|
|
|
|
expect(function() {
|
|
|
|
|
window.angular.module('hasOwnProperty', []);
|
2016-08-10 12:13:14 +02:00
|
|
|
}).toThrowMinErr('ng','badname', 'hasOwnProperty is not a valid module name');
|
2013-10-05 10:49:09 +01:00
|
|
|
});
|
2013-11-20 10:35:27 +00:00
|
|
|
|
|
|
|
|
it('should expose `$$minErr` on the `angular` object', function() {
|
|
|
|
|
expect(window.angular.$$minErr).toEqual(jasmine.any(Function));
|
2014-04-26 23:07:28 +03:00
|
|
|
});
|
2016-10-08 15:17:37 +01:00
|
|
|
|
|
|
|
|
describe('Module', function() {
|
|
|
|
|
describe('info()', function() {
|
|
|
|
|
var theModule;
|
|
|
|
|
|
|
|
|
|
beforeEach(function() {
|
|
|
|
|
theModule = angular.module('theModule', []);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should default to an empty object', function() {
|
|
|
|
|
expect(theModule.info()).toEqual({});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should store the object passed as a param', function() {
|
|
|
|
|
theModule.info({ version: '1.2' });
|
|
|
|
|
expect(theModule.info()).toEqual({ version: '1.2' });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should throw if the parameter is not an object', function() {
|
|
|
|
|
expect(function() {
|
|
|
|
|
theModule.info('some text');
|
|
|
|
|
}).toThrowMinErr('ng', 'aobj');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should completely replace the previous info object', function() {
|
|
|
|
|
theModule.info({ value: 'X' });
|
|
|
|
|
theModule.info({ newValue: 'Y' });
|
|
|
|
|
expect(theModule.info()).toEqual({ newValue: 'Y' });
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|
2012-01-06 18:10:47 -08:00
|
|
|
});
|