Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit b7889a5

Browse files
committed
fix(ngMock/$controller): respect $compileProvider.preAssignBindingsEnabled()
Fixes #15387
1 parent ce49edc commit b7889a5

File tree

2 files changed

+108
-82
lines changed

2 files changed

+108
-82
lines changed

src/ngMock/angular-mocks.js

+42-24
Original file line numberDiff line numberDiff line change
@@ -2184,6 +2184,10 @@ angular.mock.$RootElementProvider = function() {
21842184
* A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
21852185
* controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
21862186
*
2187+
* Depending on the value of
2188+
* {@link ng.$compileProvider#preAssignBindingsEnabled `preAssignBindingsEnabled()`}, the properties
2189+
* will be bound before or after invoking the constructor.
2190+
*
21872191
*
21882192
* ## Example
21892193
*
@@ -2202,18 +2206,24 @@ angular.mock.$RootElementProvider = function() {
22022206
* // Controller definition ...
22032207
*
22042208
* myMod.controller('MyDirectiveController', ['$log', function($log) {
2205-
* $log.info(this.name);
2209+
* this.log = function() {
2210+
* $log.info(this.name);
2211+
* };
22062212
* }]);
22072213
*
22082214
*
22092215
* // In a test ...
22102216
*
22112217
* describe('myDirectiveController', function() {
2212-
* it('should write the bound name to the log', inject(function($controller, $log) {
2213-
* var ctrl = $controller('MyDirectiveController', { /* no locals */ }, { name: 'Clark Kent' });
2214-
* expect(ctrl.name).toEqual('Clark Kent');
2215-
* expect($log.info.logs).toEqual(['Clark Kent']);
2216-
* }));
2218+
* describe('log()', function() {
2219+
* it('should write the bound name to the log', inject(function($controller, $log) {
2220+
* var ctrl = $controller('MyDirectiveController', { /* no locals */ }, { name: 'Clark Kent' });
2221+
* ctrl.log();
2222+
*
2223+
* expect(ctrl.name).toEqual('Clark Kent');
2224+
* expect($log.info.logs).toEqual(['Clark Kent']);
2225+
* }));
2226+
* });
22172227
* });
22182228
*
22192229
* ```
@@ -2232,26 +2242,34 @@ angular.mock.$RootElementProvider = function() {
22322242
* to work correctly.
22332243
*
22342244
* @param {Object} locals Injection locals for Controller.
2235-
* @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
2236-
* to simulate the `bindToController` feature and simplify certain kinds of tests.
2245+
* @param {Object=} bindings Properties to add to the controller instance. This is used to simulate
2246+
* the `bindToController` feature and simplify certain kinds of tests.
22372247
* @return {Object} Instance of given controller.
22382248
*/
2239-
angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
2240-
return function(expression, locals, later, ident) {
2241-
if (later && typeof later === 'object') {
2242-
var instantiate = $delegate(expression, locals, true, ident);
2243-
angular.extend(instantiate.instance, later);
2244-
2245-
var instance = instantiate();
2246-
if (instance !== instantiate.instance) {
2247-
angular.extend(instance, later);
2249+
function createControllerDecorator(compileProvider) {
2250+
angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
2251+
return function(expression, locals, later, ident) {
2252+
if (later && typeof later === 'object') {
2253+
var preAssignBindingsEnabled = compileProvider.preAssignBindingsEnabled();
2254+
2255+
var instantiate = $delegate(expression, locals, true, ident);
2256+
if (preAssignBindingsEnabled) {
2257+
angular.extend(instantiate.instance, later);
2258+
}
2259+
2260+
var instance = instantiate();
2261+
if (!preAssignBindingsEnabled || instance !== instantiate.instance) {
2262+
angular.extend(instance, later);
2263+
}
2264+
2265+
return instance;
22482266
}
2267+
return $delegate(expression, locals, later, ident);
2268+
};
2269+
}];
22492270

2250-
return instance;
2251-
}
2252-
return $delegate(expression, locals, later, ident);
2253-
};
2254-
}];
2271+
return angular.mock.$ControllerDecorator;
2272+
}
22552273

22562274
/**
22572275
* @ngdoc service
@@ -2360,11 +2378,11 @@ angular.module('ngMock', ['ng']).provider({
23602378
$httpBackend: angular.mock.$HttpBackendProvider,
23612379
$rootElement: angular.mock.$RootElementProvider,
23622380
$componentController: angular.mock.$ComponentControllerProvider
2363-
}).config(['$provide', function($provide) {
2381+
}).config(['$provide', '$compileProvider', function($provide, $compileProvider) {
23642382
$provide.decorator('$timeout', angular.mock.$TimeoutDecorator);
23652383
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
23662384
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
2367-
$provide.decorator('$controller', angular.mock.$ControllerDecorator);
2385+
$provide.decorator('$controller', createControllerDecorator($compileProvider));
23682386
}]);
23692387

23702388
/**

test/ngMock/angular-mocksSpec.js

+66-58
Original file line numberDiff line numberDiff line change
@@ -2055,69 +2055,77 @@ describe('ngMock', function() {
20552055

20562056

20572057
describe('$controllerDecorator', function() {
2058-
it('should support creating controller with bindings', function() {
2059-
var called = false;
2060-
var data = [
2061-
{ name: 'derp1', id: 0 },
2062-
{ name: 'testname', id: 1 },
2063-
{ name: 'flurp', id: 2 }
2064-
];
2065-
module(function($controllerProvider) {
2066-
$controllerProvider.register('testCtrl', function() {
2067-
called = true;
2068-
expect(this.data).toBe(data);
2058+
forEach([true, false], function(enabled) {
2059+
describe('with `preAssignBindingsEnabled(' + enabled + ')`', function() {
2060+
beforeEach(module(function($compileProvider) {
2061+
$compileProvider.preAssignBindingsEnabled(enabled);
2062+
}));
2063+
2064+
it('should support creating controller with bindings', function() {
2065+
var called = false;
2066+
var data = [
2067+
{ name: 'derp1', id: 0 },
2068+
{ name: 'testname', id: 1 },
2069+
{ name: 'flurp', id: 2 }
2070+
];
2071+
module(function($controllerProvider) {
2072+
$controllerProvider.register('testCtrl', function() {
2073+
expect(this.data).toBe(enabled ? data : undefined);
2074+
called = true;
2075+
});
2076+
});
2077+
inject(function($controller, $rootScope) {
2078+
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
2079+
expect(ctrl.data).toBe(data);
2080+
expect(called).toBe(true);
2081+
});
20692082
});
2070-
});
2071-
inject(function($controller, $rootScope) {
2072-
$controller('testCtrl', { scope: $rootScope }, { data: data });
2073-
expect(called).toBe(true);
2074-
});
2075-
});
20762083

2077-
it('should support assigning bindings when a value is returned from the constructor',
2078-
function() {
2079-
var called = false;
2080-
var data = [
2081-
{ name: 'derp1', id: 0 },
2082-
{ name: 'testname', id: 1 },
2083-
{ name: 'flurp', id: 2 }
2084-
];
2085-
module(function($controllerProvider) {
2086-
$controllerProvider.register('testCtrl', function() {
2087-
called = true;
2088-
expect(this.data).toBe(data);
2084+
it('should support assigning bindings when a value is returned from the constructor',
2085+
function() {
2086+
var called = false;
2087+
var data = [
2088+
{ name: 'derp1', id: 0 },
2089+
{ name: 'testname', id: 1 },
2090+
{ name: 'flurp', id: 2 }
2091+
];
2092+
module(function($controllerProvider) {
2093+
$controllerProvider.register('testCtrl', function() {
2094+
expect(this.data).toBe(enabled ? data : undefined);
2095+
called = true;
2096+
return {};
2097+
});
2098+
});
2099+
inject(function($controller, $rootScope) {
2100+
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
2101+
expect(ctrl.data).toBe(data);
2102+
expect(called).toBe(true);
2103+
});
2104+
}
2105+
);
20892106

2090-
return {};
2107+
if (/chrome/.test(window.navigator.userAgent)) {
2108+
it('should support assigning bindings to class-based controller', function() {
2109+
var called = false;
2110+
var data = [
2111+
{ name: 'derp1', id: 0 },
2112+
{ name: 'testname', id: 1 },
2113+
{ name: 'flurp', id: 2 }
2114+
];
2115+
module(function($controllerProvider) {
2116+
// eslint-disable-next-line no-eval
2117+
var TestCtrl = eval('(class { constructor() { called = true; } })');
2118+
$controllerProvider.register('testCtrl', TestCtrl);
2119+
});
2120+
inject(function($controller, $rootScope) {
2121+
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
2122+
expect(ctrl.data).toBe(data);
2123+
expect(called).toBe(true);
2124+
});
20912125
});
2092-
});
2093-
inject(function($controller, $rootScope) {
2094-
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
2095-
expect(ctrl.data).toBe(data);
2096-
expect(called).toBe(true);
2097-
});
2098-
}
2099-
);
2100-
2101-
if (/chrome/.test(window.navigator.userAgent)) {
2102-
it('should support assigning bindings to class-based controller', function() {
2103-
var called = false;
2104-
var data = [
2105-
{ name: 'derp1', id: 0 },
2106-
{ name: 'testname', id: 1 },
2107-
{ name: 'flurp', id: 2 }
2108-
];
2109-
module(function($controllerProvider) {
2110-
// eslint-disable-next-line no-eval
2111-
var TestCtrl = eval('(class { constructor() { called = true; } })');
2112-
$controllerProvider.register('testCtrl', TestCtrl);
2113-
});
2114-
inject(function($controller, $rootScope) {
2115-
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
2116-
expect(ctrl.data).toBe(data);
2117-
expect(called).toBe(true);
2118-
});
2126+
}
21192127
});
2120-
}
2128+
});
21212129
});
21222130

21232131

0 commit comments

Comments
 (0)