My <custom-directive> has replace:true and template: '<img />'.
How can I write a unit test for it? I think I want to test that it actually replaces custom-directive with img.
it('should be transformed to <img>', function(){
var elm = $compile('<custom-directive></custom-directive>')(scope);
scope.$digest();
var t = elm.find('img'); // wrong! it replaces the element. it won't find another one inside
//expect(elm).toBeAnImgElement ?
});
I can't find the correct matcher.
The closest case I've seen is checking the contents (elm.html() or elm.text()) but my tag is empty.
wrap your directive in a div like:
describe('Directive: custom', function () {
beforeEach(module('App'));
var element, $scope;
it('should be transformed to <img>', inject(function ($rootScope, $compile) {
$scope = $rootScope.$new();
element = angular.element('<div><custom-directive></custom-directive></div>');
element = $compile(element)($scope);
expect(element.children('img').length).toBe(1);
}));
});
You can get the actual HTMLElement object and check its tagname. Get at the actual HTMLElement with elm[0]:
expect(elm[0].tagName).toEqual('A');
Related
I have a example AngularJS directive like this <div some-dir="5" />
How would I access this directive attribute value of 5 inside my test?
describe("some-dir", function() {
var element, scope;
beforeEach(module('app'));
beforeEach(inject(function($rootScope, $compile) {
scope = $rootScope;
element = angular.element('<div><div id="el1" some-dir="5" /></div>');
$compile(element)(scope);
scope.$digest();
}));
it('should be able to get the attribute value', function(){
// get the attr value of some-dir
});
});
You can check scope values of element using its isolateScope method. But this won't work when you pass a value right next to directive attribute, as those values are not copied into isolated scope.
In that case, it's possible to get and test that value using element.attributes method.
First compile your directive html:
var element;
beforeEach(inject(function (_$compile_, _$rootScope_) {
var $compile = _$compile_,
$scope = _$rootScope_;
element = $compile('<div my-directive="4" some-value="5"></div>')($scope);
$scope.$digest();
}));
Then you can expect element's isolateScope to return an object with someValue property.
it('should expect some-value as 5', function () {
inject(function ($injector) {
// check attribute values using isolateScope
expect(element.isolateScope().someValue).toEqual(5);
// check the value right after directive attribute
expect(element.attr('my-directive')).toEqual('4');
});
});
Here is an example plunker.
I would like to test a directive, which has a filter dependency. I would like to inject actual filter, instead of using a mock.
Here is my mocked beforeEach. How do I go about injecting actual filter? I've tried injecting as part of the inject function, but this does not seems to work.
beforeEach(function() {
// filter mock
someFilterMock = function(value) {
return value;
};
// get app
module('app');
// get html templates
module('templates');
// replace filter with a mock
module(function($provide) {
$provide.value('someFilterFilter', someFilterMock);
});
// inject & compile
inject(function($rootScope, $compile) {
// create scope
scope = $rootScope.$new();
// create element using directive
element = angular.element('<this-is-directive />');
$compile(element)(scope);
scope.$digest();
});
});
I am doing something like this in my tests:
it('uses a filter', inject(function ($filter) {
var result = $filter('filterName')(params);
expect(result).toBe('something');
}));
Perhaps it does help?
I am relatively new to jasmine tests, and I've got some problem with it. I try to test this directive :
DIRECTIVE
myApp.LoadingsDirective = function() {
return {
restrict: 'E',
replace: true,
template: '<div class="loading"><img src="http://www.nasa.gov/multimedia/videogallery/ajax-loader.gif" width="20" height="20" /></div>',
link: function (scope, element, attrs) {
scope.$watch(
function(scope) {
return scope.$eval(attrs.show);
},
function(val) {
if (val){
$(element).show();
}
else{
$(element).hide();
}
})
}
}
}
myApp.directive('loading', myApp.LoadingsDirective);
This directive just show a loading icon until the result of a asynchronious request replace it.
I try something like this :
TEST
describe('Testing directives', function() {
var $scope, $compile, element;
beforeEach(function() {
module('myApp');
inject(function($rootScope, _$compile_) {
$scope = $rootScope.$new();
$compile = _$compile_;
});
});
it('ensures directive show the loading when show attribut is true', function() {
// GIVEN
var element = $compile('<div><loading show="true"> </loading></div>')($scope);
var loadingScope = element.find('loading').scope();
// WHEN
loadingScope.$watch();
// THEN
expect(loadingScope.show).toBe('true');
});
});
What is the best way to test this type of directive ? How to get access to attributs and test it ?
I always do it this way (coffeescript, but you'll get the idea):
'use strict';
describe 'Directive: yourDirective', ->
beforeEach module('yourApp')
# angular specific stuff
$rootScope = $compile = $scope = undefined
beforeEach inject (_$rootScope_, _$compile_) ->
$rootScope = _$rootScope_
$scope = $rootScope.$new()
$compile = _$compile_
# object specific stuff
element = createElement = undefined
beforeEach inject () ->
createElement = () ->
element = angular.element("<your-directive></your-directive>")
$compile(element)($scope)
$scope.$digest()
it "should have a headline", ->
createElement()
element.find("a").click()
$scope.$apply()
expect(element.find("input").val()).toEqual("foobar")
expect($scope.inputModel).toEqual("foobar")
And this could be the directive:
<your-directive>
<a ng-click="spanModel='foobar'">set inputModel</a>
<input ng-model="inputModel">
</your-directive>
First, I extract the creation of your element into a function. This allows you to do some initial setup before the directive is created.
Then I perform some actions on my directive. If you want to apply this actions into your scope (remember in jasmine you are NOT inside angulars' digest circle), you have to call $scope.$apply() or $scope.$digest() (can't remember right now what the exact difference was).
In the example above, you click on the <a> element, and this has a ng-click attached. This sets the inputModel scope variable.
Not tested, but you'll get the idea
After giving up on ngMockE2E being able to supply proper passThrough() from the $httpBackend, I've resorted to adding the templates to the cache manually. This seems to work great, if I want to just 'test' my template. However, say I want to test some conditions on the controller that get invoked (via directive), how do a I get a reference to it?
describe('RedactedFilter ', function() {
var ctrl, filterModel,createController;
var $httpBackend,$scope,template, $templateCache,$compile,$rootScope, $compile;
beforeEach(module('RedactedApp.Services'));
beforeEach(module('RedactedApp.Controllers'));
beforeEach(module('RedactedApp.Models'));
beforeEach(inject(function($templateCache,_$compile_,_$rootScope_) {
//assign the template to the expected url called by the directive and put it in the cache
template = $templateCache.get('src/main/app/components/redactedFilter/redacted-filter.tpl.html');
$templateCache.put('src/main/app/components/redactedFilter/redactedFilter-filter.tpl.html',template);
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
beforeEach(inject(function ($controller, $rootScope,_$q_, $injector, _$compile_) {
$scope = $rootScope.$new();
$httpBackend = $injector.get('$httpBackend');
$compile = _$compile_;
filterModel = $injector.get('RedactedFilterModel');
}));
it('should default to tab 0', function() {
var formElement = angular.element('<redacted-filter></redacted-filter>');
var element = $compile(formElement)($rootScope);
$rootScope.$digest();
var ctrl = element.controller();
//ctrl is undefined
expect(ctrl.selectedTab).toEqual(0);
});
});
Note that it does not say function controller() is undefined, but the result of calling it. My directive has replace set to false so I don't think it's an issue with the transclusion hiding the element.
Here's my directive for good measure:
angular.module('RedactedApp.Directives').directive('redactedFilter', function(){
return {
restrict: 'E',
replace: false,
templateUrl: '../../main/app/components/redactedFilter/redacted-filter.tpl.html'
};
});
First, use ng-html2j or ng-templates to shove your templates into your template cache before you run tests.
Then compile the templates into elements, and shove them into your controller for testing:
beforeEach(inject(function($templateCache,_$compile_,_$rootScope_, _$controller_) {
//get the template from the cache
template = $templateCache.get('src/main/app/components/someController/widget-search.tpl.html');
$compile = _$compile_;
$rootScope = _$rootScope_;
$controller = _$controller_;
//compile it
element = $compile(template)($rootScope);
}));
//then shove your compiled element into your controller instance
it( 'should be instantiated since it does not do much yet', function(){
ctrl = $controller('SomeController',
{
$scope: $rootScope.$new(),
$element: element
});
expect( ctrl.hasInstance).toBeTruthy();
});
I have a directive as below which i want to cover as part of my jasmine unit test but not sure how to get the template value and the values inside the link in my test case. This is the first time i am trying to unit test a directive.
angular.module('newFrame', ['ngResource'])
.directive('newFrame', [
function () {
function onAdd() {
$log.info('Clicked onAdd()');
}
return {
restrict: 'E',
replace: 'true',
transclude: true,
scope: {
filter: '=',
expand: '='
},
template:
'<div class="voice ">' +
'<section class="module">' +
'<h3>All Frames (00:11) - Summary View</h3>' +
'<button class="btn" ng-disabled="isDisabled" ng-hide="isReadOnly" ng-click="onAdd()">Add a frame</button>' +
'</section>' +
'</div>',
link: function (scope) {
scope.isDisabled = false;
scope.isReadOnly = false;
scope.onAdd = onAdd();
}
};
}
]);
Here is an example with explanation:
describe('newFrame', function() {
var $compile,
$rootScope,
$scope,
$log,
getElement;
beforeEach(function() {
// Load module and wire up $log correctly
module('newFrame', function($provide) {
$provide.value('$log', console);
});
// Retrieve needed services
inject(function(_$compile_, _$rootScope_, _$log_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
$log = _$log_;
});
// Function to retrieve a compiled element linked to passed scope
getCompiledElement = function(scope) {
var element = $compile('<new-frame></new-frame>')(scope);
$rootScope.$digest();
return element;
}
// Set up spies
spyOn($log, 'info').and.callThrough();
});
it('test', function() {
// Prepare scope for the specific test
$scope.filter = 'Filter';
$scope.expand = false;
// This will be the compiled element wrapped in jqLite
// To get reference to the DOM element do: element[0]
var element = getCompiledElement($scope);
// Get a reference to the button element wrapped in jqLite
var button = element.find('button');
// Verify button is not hidden by ng-hide
expect(button.hasClass('ng-hide')).toBe(false);
// Verify button is not disabled
expect(button.attr('disabled')).toBeFalsy();
// Click the button and verify that it generated a call to $log.info
button.triggerHandler('click');
expect($log.info).toHaveBeenCalled();
});
});
Demo: http://plnkr.co/edit/tOJ0puOd6awgVvRLmfAD?p=preview
Note that I changed the code for the directive:
Injected the $log service
Changed scope.onAdd = onAdd(); to scope.onAdd = onAdd;
After reading the angular documentation for directives,i was able to solve this. Since the restrict is marked as E, the directive can only be injected through a element name. Earlier i was trying through div like below.
angular.element('<div new-frame></div>')
This will work if restrict is marked as A (attributes). Now i changed my injection in he spec file to match the directive with element name.
angular.element('<new-frame></new-frame>')
Now i was able to get the template and scope attributes in my spec. Just to be sure to accept everything, the combination of A (aatributes), E (elements) and C (class name) can be used in the restrict or any 2 as needed.