I am trying to unit test a directive with a two-way bound property (=). The directive works in my app, but I can't get a unit test working that tests the two-way binding.
I have been trying to get this working for days. I've read MANY examples that use some but not all of the features I want to use: controllerAs, bindToController & isolateScope(). (Forget about templateURL, which I also need. I will add that if I can get this working! :)
I'm hoping someone can tell me how to show a change in the parent scope reflected in the isolate scope.
Here is a plunkr that contains the code below:
http://plnkr.co/edit/JQl9fB5kTt1CPtZymwhI
Here is my test app:
var app = angular.module('myApp', []);
angular.module('myApp').controller('greetingController', greetingController);
greetingController.$inject = ['$scope'];
function greetingController($scope) {
// this controller intentionally left blank (for testing purposes)
}
angular.module('myApp').directive('greetingDirective',
function () {
return {
scope: {testprop: '='},
restrict: 'E',
template: '<div>Greetings!</div>',
controller: 'greetingController',
controllerAs: 'greetingController',
bindToController: true
};
}
);
And here is the spec:
describe('greetingController', function () {
var ctrl, scope, rootScope, controller, data, template,
compile, isolatedScope, element;
beforeEach(module('myApp'));
beforeEach(inject(function ($injector) {
rootScope = $injector.get('$rootScope');
scope = rootScope.$new();
controller = $injector.get('$controller');
compile = $injector.get('$compile');
data = {
testprop: 1
};
ctrl = controller('greetingController', {$scope: scope}, data);
element = angular.element('<greeting-directive testprop="testprop"></greeting-directive>');
template = compile(element)(scope);
scope.$digest();
isolatedScope = element.isolateScope();
}));
// PASSES
it('testprop inital value should be 1', function () {
expect(ctrl.testprop).toBe(1);
});
// FAILS: why doesn't changing this isolateScope value
// also change the controller value for this two-way bound property?
it('testprop changed value should be 2', function () {
isolatedScope.testprop = 2;
expect(ctrl.testprop).toBe(2);
});
});
You have to correct the way you're testing your directive. You're directly changing isolatedScope
of an object and thereafter verifying the ctrl
object which unrelated DOM
which you had compiled.
Basically what you should be doing is as soon as you compiled a DOM with scope (here it is <greeting-directive testprop="testprop"></greeting-directive>
). So that scope will hold the context of compiled do. In short you can play testprop
property value. or same thing will be available inside element.scope()
. As soon as you change any value in scope
/currentScope
. You can see the value gets updated inside directive isolatedScope
. One more thing I'd like to mention is when you do controllerAs
with bindToController: true
, angular adds property with controller alias inside scope
that's we verified isolatedScope.greetingController.testprop
inside assert
Code
describe('greetingController', function() {
var ctrl, scope, rootScope, controller, data, template,
compile, isolatedScope, currentScope, element;
beforeEach(module('myApp'));
beforeEach(inject(function($injector) {
rootScope = $injector.get('$rootScope');
scope = rootScope.$new();
controller = $injector.get('$controller');
compile = $injector.get('$compile');
data = { testprop: 1 };
ctrl = controller('greetingController', { $scope: scope }, data);
element = angular.element('<greeting-directive testprop="testprop"></greeting-directive>');
template = compile(element)(scope);
scope.$digest();
currentScope = element.scope();
//OR
//currentScope = scope; //both are same
isolatedScope = element.isolateScope();
}));
// First test passes -- commented
it('testprop changed value should be 2', function() {
currentScope.testprop = 2; //change current element (outer) scope value
currentScope.$digest(); //running digest cycle to make binding effects
//assert
expect(isolatedScope.greetingController.testprop).toBe(2);
});
});