Permalink
Browse files

feat(ngMock): allow mock $controller service to set up controller bin…

…dings

Adds a new mock for the $controller service, in order to simplify testing using the
bindToController feature.

```js
var dictionaryOfControllerBindings = {
  data: [
    { id: 0, phone: '...', name: '...' },
    { id: 1, phone: '...', name: '...' },
  ]
};

// When the MyCtrl constructor is called, `this.data ~= dictionaryOfControllerBindings.data`
$controller(MyCtrl, myLocals, dictionaryOfControllerBindings);
```

Closes #9425
Closes #11239
  • Loading branch information...
1 parent 2d0eda1 commit d02d0585a086ecd2e1de628218b5a6d85c8fc7bd @caitp caitp committed with petebacondarwin Mar 4, 2015
Showing with 94 additions and 0 deletions.
  1. +72 −0 src/ngMock/angular-mocks.js
  2. +22 −0 test/ngMock/angular-mocksSpec.js
@@ -1781,6 +1781,77 @@ angular.mock.$RootElementProvider = function() {
};
/**
+ * @ngdoc service
+ * @name $controller
+ * @description
+ * A decorator for {@link ng.$controller} with additional `bindings` parameter, useful when testing
+ * controllers of directives that use {@link $compile#-bindtocontroller- `bindToController`}.
+ *
+ *
+ * ## Example
+ *
+ * ```js
+ *
+ * // Directive definition ...
+ *
+ * myMod.directive('myDirective', {
+ * controller: 'MyDirectiveController',
+ * bindToController: {
+ * name: '@'
+ * }
+ * });
+ *
+ *
+ * // Controller definition ...
+ *
+ * myMod.controller('MyDirectiveController', ['log', function($log) {
+ * $log.info(this.name);
+ * })];
+ *
+ *
+ * // In a test ...
+ *
+ * describe('myDirectiveController', function() {
+ * it('should write the bound name to the log', inject(function($controller, $log) {
+ * var ctrl = $controller('MyDirective', { /* no locals */ }, { name: 'Clark Kent' });
+ * expect(ctrl.name).toEqual('Clark Kent');
+ * expect($log.info.logs).toEqual(['Clark Kent']);
+ * });
+ * });
+ *
+ * ```
+ *
+ * @param {Function|string} constructor If called with a function then it's considered to be the
+ * controller constructor function. Otherwise it's considered to be a string which is used
+ * to retrieve the controller constructor using the following steps:
+ *
+ * * check if a controller with given name is registered via `$controllerProvider`
+ * * check if evaluating the string on the current scope returns a constructor
+ * * if $controllerProvider#allowGlobals, check `window[constructor]` on the global
+ * `window` object (not recommended)
+ *
+ * The string can use the `controller as property` syntax, where the controller instance is published
+ * as the specified property on the `scope`; the `scope` must be injected into `locals` param for this
+ * to work correctly.
+ *
+ * @param {Object} locals Injection locals for Controller.
+ * @param {Object=} bindings Properties to add to the controller before invoking the constructor. This is used
+ * to simulate the `bindToController` feature and simplify certain kinds of tests.
+ * @return {Object} Instance of given controller.
+ */
+angular.mock.$ControllerDecorator = ['$delegate', function($delegate) {
+ return function(expression, locals, later, ident) {
+ if (later && typeof later === 'object') {
+ var create = $delegate(expression, locals, true, ident);
+ angular.extend(create.instance, later);
+ return create();
+ }
+ return $delegate(expression, locals, later, ident);
+ };
+}];
+
+
+/**
* @ngdoc module
* @name ngMock
* @packageName angular-mocks
@@ -1808,6 +1879,7 @@ angular.module('ngMock', ['ng']).provider({
$provide.decorator('$$rAF', angular.mock.$RAFDecorator);
$provide.decorator('$$asyncCallback', angular.mock.$AsyncCallbackDecorator);
$provide.decorator('$rootScope', angular.mock.$RootScopeDecorator);
+ $provide.decorator('$controller', angular.mock.$ControllerDecorator);
}]);
/**
@@ -1751,6 +1751,28 @@ describe('ngMock', function() {
}));
});
});
+
+
+ describe('$controllerDecorator', function() {
+ it('should support creating controller with bindings', function() {
+ var called = false;
+ var data = [
+ { name: 'derp1', id: 0 },
+ { name: 'testname', id: 1 },
+ { name: 'flurp', id: 2 }
+ ];
+ module(function($controllerProvider) {
+ $controllerProvider.register('testCtrl', function() {
+ called = true;
+ expect(this.data).toEqual(data);
+ });
+ });
+ inject(function($controller, $rootScope) {
+ $controller('testCtrl', { scope: $rootScope }, { data: data });
+ expect(called).toBe(true);
+ });
+ });
+ });
});

0 comments on commit d02d058

Please sign in to comment.