I have a directive that $emit's an event, but in the parent scope of a unit test doesn't get the event.
test $broadcasts an event to the directive, directive gets the event, and then directives $emits its own event, that the parent should get.
unit test:
beforeEach(inject(function ($compile, $rootScope) {
scope = $rootScope.$new();
compile = $compile;
}));
element = compile(angular.element('<div myDirective></div>'))(scope);
scope.$digest();
var _scope = element.isolateScope();
scope.$broadcast('directive-change');
scope.$on('directive-successfully-changed', function (e) {});
directive:
scope.$on('directive-change', function (e) {
scope.$emit('directive-successfully-changed');
});
Related
I wrote a test for my directive using jasmine testcase framework with karma testcase runner.
In my project ,I am already having one directive called
<parent-directive></parent-directive>
and i tried to include that parent directive into another one called
<child-directive></child-directive>.
Parent directive elements are converted as components called SampleComponents and included in the child directive
Sample.js
'use strict'
angular.module('Sample')
.directive('SampleHeader', SampleHeader)
function SampleHeader () {
return {
restrict: 'A',
templateUrl: 'header/header.html',
scope: {},
controller: function ($scope) {
$scope.logoutHeader = function () {
console.log('Logout call back')
require('electron').remote.app.quit()
}
}
}
}
SampleSpec.js
describe('SampleHeader', function () {
var $compile, $rootScope, elements, scope, controller
beforeEach(module('Sample'))
beforeEach(module('SampleComponenets'))
beforeEach(module('ngAnimate'))
beforeEach(module('ngRoute'))
beforeEach(module('ngMaterial'))
beforeEach(module('ngCookies'))
beforeEach(module('datatables'))
beforeEach(inject(function (_$compile_, _$rootScope_, _$q_,_$controller_) {
deferred = _$q_.defer()
$compile = _$compile_
$rootScope = _$rootScope_
controller = _$controller_
scope = $rootScope.$new()
elements = angular.element('<sample-header></sample-header>')
$compile(elements)($rootScope.$new())
$rootScope.$digest()
controller = elements.controller('SampleHeader')
scope = elements.isolateScope() || elements.scope()
scope.$digest()
}))
it('should check logoutHeader is called', function () {scope.logoutHeader()
})
})
restrict: 'A' -> it seems you created a attribute directive so you should compile the directive as a attribute (like, elements = angular.element('<div sample-header></div>')).
describe('SampleHeader', function () {
var $compile, $rootScope, elements, scope, controller
beforeEach(module('Sample'))
beforeEach(module('SampleComponenets'))
beforeEach(module('ngAnimate'))
beforeEach(module('ngRoute'))
beforeEach(module('ngMaterial'))
beforeEach(module('ngCookies'))
beforeEach(module('datatables'))
beforeEach(inject(function (_$compile_, _$rootScope_, _$q_,_$controller_) {
deferred = _$q_.defer()
$compile = _$compile_
$rootScope = _$rootScope_
controller = _$controller_
scope = $rootScope.$new()
elements = angular.element('<div sample-header></div>')
$compile(elements)($rootScope.$new())
$rootScope.$digest()
controller = elements.controller('SampleHeader')
scope = elements.isolateScope() || elements.scope()
scope.$digest()
}))
it('should check logoutHeader is called', function () {scope.logoutHeader()
})
})
I'm trying to test one of my directives and I can't work out why my scope object is undefined.
define(["require", "exports", 'angular', 'directives', "angularMocks", "chartApp"], function (require, exports, angular, Directives) {
"use strict";
describe('board controls', function () {
describe('task filter', function () {
var $compile;
var $rootScope;
var scope;
var element;
beforeEach(angular.mock.module('chartApp'));
beforeEach(angular.mock.module('partials/directives/board-controls.html'));
beforeEach(inject(function (_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
scope = $rootScope.$new();
expect(scope).toBeDefined();
element = $compile('<board-controls></board-controls>')(scope);
scope.$digest();
}));
it('displays modal', function () {
scope.showChildFilters();
});
});
});
});
In the it('displays modal')... part Karma outputs:
TypeError: undefined is not an object (evaluating 'scope.showChildFilters')
But in the beforeEach(...) part it seems to be working. I just can't see why this doesn't work.
You need to change to this
it('displays modal', function () {
//scope.showChildFilters();
var isolateScope = element.isolateScope(); //I prefer to name isolateScope
isolateScope.$apply() //cause scope to digest and watch and all that
isolateScope.showChildFilters();
});
This is a very detailed answer to your question as well.
Testing element directive - can't access isolated scope methods during tests
I am trying to test the reciever from a $broadcast (from a controller) in my directive via .on.
Directive:
describe('<-- myDirective Spec ------>', function () {
var scope, $compile, element, $httpBackend, rootScope;
beforeEach(angular.mock.module('myApp'));
beforeEach(inject(function (_$rootScope_, _$compile_, _$httpBackend_) {
scope = _$rootScope_.$new();
$compile = _$compile_;
$httpBackend = _$httpBackend_;
rootScope = _$rootScope_;
var html = '<my-directive></my-directive>';
element = $compile(angular.element(html))(scope);
spyOn(scope, '$broadcast').and.callThrough();
scope.$digest();
}));
it('should be defined', function () {
expect(element).toBeDefined();
});
it('should broadcast ', function () {
scope.$broadcast('sendEmail');
expect(scope.$on).toHaveBeenCalledWith('sendEmail', jasmine.any(Function));
});
});
With the above, i get error:
Expected a spy, but got Function.
Update:
You can either test simply if your $broadcast is getting called with
expect(scope.$broadcast).toHaveBeenCalled()
or to actually test $on do something like
scope.$on('sendEmail', spyFunction)
expect(spyFunction).toHaveBeenCalledWith('sendEmail')
Reason: $broadcast doesn't actually calls $on function. $on is a listener which calls the callback function (passed as second argument) when it listens the event (first argument).
You are currently spying on $broadcast function of the scope, and have put a test on $on function. You need to replace
spyOn(scope, '$broadcast').and.callThrough();
by
spyOn(scope, '$on').and.callThrough();
I have a nested controller as below:
<div ng-controller="ParentController">{{ data.value }}
<div ng-controller="ChildController">{{ data.value }}</div>
</div>
app.controller('ParentController', function ($scope) {
$scope.data = {
value: "A"
}
});
My child controller sets the parent scope as below:
app.controller('ChildController', function ($scope) {
$scope.data.value = "B";
});
My Jasmine unit test is:
describe('ChildController', function () {
var scope, $rootScope;
beforeEach(inject(function ($controller, _$rootScope_) {
$rootScope = _$rootScope_;
scope = $rootScope.$new();
$controller('ChildController', { $scope: scope});
scope.$digest();
}));
it('should change parent scope', function () {
expect(scope.data.value).toEqual("B");
});
});
The test results in "Cannot read property 'value' of undefined".
How do I unit test a child controller that uses a parent scope?
It really depends on what you want to test. If you want to assert that the child controller changes the value during its initialization then just setup the value for the test to change. You don't need to test Angular's scope hierarchy.
So, I'd suggest just to do this:
describe('ChildController', function () {
var scope, $rootScope;
beforeEach(inject(function ($controller, _$rootScope_) {
$rootScope = _$rootScope_;
scope = $rootScope.$new();
//setup
scope.data = { value: "A" };
$controller('ChildController', { $scope: scope});
scope.$digest();
}));
it('should change parent scope', function () {
expect(scope.data.value).toEqual("B");
});
});
First initialize $scope.data={};
app.controller('ChildController', function ($scope) {
$scope.data={};
$scope.data.value = "B";
});
I'm trying to test an angular controller that queries the DOM for a select box value.
$scope.getValue = function(){
document.getElementById('select_box_value').value;
}
How can I stub this function so it returns a simple integer value in a controller test, like so? Every time I try it this way, the original function runs instead of my stub.
describe('MyCtrl', function () {
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller('MyCtrl', {
'$scope': scope,
'$scope.getValue':sinon.stub().returns(20)
});
}));
it('gets the select box value', function () {
expect(scope.getValue).toBe(20);
});
});