I have the following directive to autofocus a field:
.directive('ngAutofocus', function ($timeout) {
return {
restrict: 'A',
link: function (scope, elm) {
$timeout(function () {
elm[0].focus();
});
}
};
}
How would I unit test this? I tried several things like the following selector but they all return errors or false:
console.log($(elm[0]).is(':focus'));
My unit test is set up like this:
elm = angular.element('<input type="text" name="textfield1" ng-autofocus>');
$scope.$digest();
$compile(elm)($scope);
I figured it out, and it was pretty obvious actually;
it('should set the focus on timeout', function () {
spyOn(elm[0],'focus');
$timeout.flush();
expect(elm[0].focus).toHaveBeenCalled();
})
My problem was two-fold:
- I wasn't calling the timeout flush function so timeout wasn't occuring, and
- I was trying to look at the focus attribute of the element whereas just looking at the call of the focus() function is much more like unit-testing. The focus attribute is something that really belongs to the e2e testing territory.
You can use document.activeElement
to check focus. The only downside being that the HTML needs to be added to the document body for this to work.
https://developer.mozilla.org/en-US/docs/Web/API/Document/activeElement
A more verbose solution below, which allows testing (spying on) the focus that runs immediately (i.e. no $timeout
or other event). The key is to first render a DOM element
before $compile
runs:
'use strict';
describe('Testing the focus call from the link function', function () {
var $compile;
var $rootScope;
beforeEach(angular.mock.module('auto-focus-module'));
beforeEach(inject(function (_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('should automatically focus when calling the link function', function () {
var $scope = $rootScope.$new();
// create an uncompiled DOM element so we can bind the focus spy
var rawEl = angular.element('<input auto-focus-directive>');
// set the spy
spyOn(rawEl[0], 'focus');
// compile the rawEl so that compile and link functions run
$compile(rawEl)($scope);
expect(rawEl[0].focus).toHaveBeenCalled();
});
});
With a directive
and link
function that could look like:
(function () {
'use strict';
angular.module('auto-focus-module')
.directive('autoFocusDirective', autoFocusDirective);
function autoFocusDirective () {
return {
link: link
};
function link (scope, elem) {
elem[0].focus();
}
}
})();
You should use the angular.element api - jQuery lite - and use the method triggerHandler().
it('should have focus', function() {
elm.triggerHandler('focus');
expect(elm).toBeInFocus() //PSEUDO CODE - you will need to see how this can be tested
}
http://docs.angularjs.org/api/ng/function/angular.element
http://api.jquery.com/triggerhandler/
Potential Area for some testing focus knowledge:
https://shanetomlinson.com/2014/test-element-focus-javascript
Also you concerning your unit test - you don't need to append the element to the body, it's possible to test without that.