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

Commit 7d9a791

Browse files
committed
fix(ngMock/$controller): respect $compileProvider.preAssignBindingsEnabled()
Fixes #15387 Closes #15395
1 parent 17ddba8 commit 7d9a791

File tree

2 files changed

+158
-54
lines changed

2 files changed

+158
-54
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

+116-30
Original file line numberDiff line numberDiff line change
@@ -2055,27 +2055,15 @@ 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);
2069-
});
2070-
});
2071-
inject(function($controller, $rootScope) {
2072-
$controller('testCtrl', { scope: $rootScope }, { data: data });
2073-
expect(called).toBe(true);
2074-
});
2075-
});
20762058

2077-
it('should support assigning bindings when a value is returned from the constructor',
2078-
function() {
2059+
describe('with `preAssignBindingsEnabled(true)`', function() {
2060+
2061+
beforeEach(module(function($compileProvider) {
2062+
$compileProvider.preAssignBindingsEnabled(true);
2063+
}));
2064+
2065+
2066+
it('should support creating controller with bindings', function() {
20792067
var called = false;
20802068
var data = [
20812069
{ name: 'derp1', id: 0 },
@@ -2084,40 +2072,138 @@ describe('ngMock', function() {
20842072
];
20852073
module(function($controllerProvider) {
20862074
$controllerProvider.register('testCtrl', function() {
2087-
called = true;
20882075
expect(this.data).toBe(data);
2089-
2090-
return {};
2076+
called = true;
20912077
});
20922078
});
20932079
inject(function($controller, $rootScope) {
20942080
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
20952081
expect(ctrl.data).toBe(data);
20962082
expect(called).toBe(true);
20972083
});
2084+
});
2085+
2086+
2087+
it('should support assigning bindings when a value is returned from the constructor',
2088+
function() {
2089+
var called = false;
2090+
var data = [
2091+
{ name: 'derp1', id: 0 },
2092+
{ name: 'testname', id: 1 },
2093+
{ name: 'flurp', id: 2 }
2094+
];
2095+
module(function($controllerProvider) {
2096+
$controllerProvider.register('testCtrl', function() {
2097+
expect(this.data).toBe(data);
2098+
called = true;
2099+
return {};
2100+
});
2101+
});
2102+
inject(function($controller, $rootScope) {
2103+
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
2104+
expect(ctrl.data).toBe(data);
2105+
expect(called).toBe(true);
2106+
});
2107+
}
2108+
);
2109+
2110+
2111+
if (/chrome/.test(window.navigator.userAgent)) {
2112+
it('should support assigning bindings to class-based controller', function() {
2113+
var called = false;
2114+
var data = [
2115+
{ name: 'derp1', id: 0 },
2116+
{ name: 'testname', id: 1 },
2117+
{ name: 'flurp', id: 2 }
2118+
];
2119+
module(function($controllerProvider) {
2120+
// eslint-disable-next-line no-eval
2121+
var TestCtrl = eval('(class { constructor() { called = true; } })');
2122+
$controllerProvider.register('testCtrl', TestCtrl);
2123+
});
2124+
inject(function($controller, $rootScope) {
2125+
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
2126+
expect(ctrl.data).toBe(data);
2127+
expect(called).toBe(true);
2128+
});
2129+
});
20982130
}
2099-
);
2131+
});
2132+
21002133

2101-
if (/chrome/.test(window.navigator.userAgent)) {
2102-
it('should support assigning bindings to class-based controller', function() {
2134+
describe('with `preAssignBindingsEnabled(false)`', function() {
2135+
2136+
beforeEach(module(function($compileProvider) {
2137+
$compileProvider.preAssignBindingsEnabled(false);
2138+
}));
2139+
2140+
2141+
it('should support creating controller with bindings', function() {
21032142
var called = false;
21042143
var data = [
21052144
{ name: 'derp1', id: 0 },
21062145
{ name: 'testname', id: 1 },
21072146
{ name: 'flurp', id: 2 }
21082147
];
21092148
module(function($controllerProvider) {
2110-
// eslint-disable-next-line no-eval
2111-
var TestCtrl = eval('(class { constructor() { called = true; } })');
2112-
$controllerProvider.register('testCtrl', TestCtrl);
2149+
$controllerProvider.register('testCtrl', function() {
2150+
expect(this.data).toBeUndefined();
2151+
called = true;
2152+
});
21132153
});
21142154
inject(function($controller, $rootScope) {
21152155
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
21162156
expect(ctrl.data).toBe(data);
21172157
expect(called).toBe(true);
21182158
});
21192159
});
2120-
}
2160+
2161+
2162+
it('should support assigning bindings when a value is returned from the constructor',
2163+
function() {
2164+
var called = false;
2165+
var data = [
2166+
{ name: 'derp1', id: 0 },
2167+
{ name: 'testname', id: 1 },
2168+
{ name: 'flurp', id: 2 }
2169+
];
2170+
module(function($controllerProvider) {
2171+
$controllerProvider.register('testCtrl', function() {
2172+
expect(this.data).toBeUndefined();
2173+
called = true;
2174+
return {};
2175+
});
2176+
});
2177+
inject(function($controller, $rootScope) {
2178+
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
2179+
expect(ctrl.data).toBe(data);
2180+
expect(called).toBe(true);
2181+
});
2182+
}
2183+
);
2184+
2185+
2186+
if (/chrome/.test(window.navigator.userAgent)) {
2187+
it('should support assigning bindings to class-based controller', function() {
2188+
var called = false;
2189+
var data = [
2190+
{ name: 'derp1', id: 0 },
2191+
{ name: 'testname', id: 1 },
2192+
{ name: 'flurp', id: 2 }
2193+
];
2194+
module(function($controllerProvider) {
2195+
// eslint-disable-next-line no-eval
2196+
var TestCtrl = eval('(class { constructor() { called = true; } })');
2197+
$controllerProvider.register('testCtrl', TestCtrl);
2198+
});
2199+
inject(function($controller, $rootScope) {
2200+
var ctrl = $controller('testCtrl', { scope: $rootScope }, { data: data });
2201+
expect(ctrl.data).toBe(data);
2202+
expect(called).toBe(true);
2203+
});
2204+
});
2205+
}
2206+
});
21212207
});
21222208

21232209

0 commit comments

Comments
 (0)