Now we are talking about how to test AngularJS directives with an
isolated scope.
Let's say that you have a directive that should help you to select items from a given long list with filtering, ordering and selecting facilities:
<my-list model="exampleList" />
where
exampleList is the following:
$scope.exampleList = [
{id: '001', label:'First item', selected:false},
{id: '002', label:'Second item', selected: false},
...
];
It sounds like a complex thing but you can implement it with Angular with few lines of Javascript code (
app/scripts/directives/mylist.js):
'use strict';
angular.module('myListApp')
.directive('myList', [function () {
return {
templateUrl: 'views/mylist.html',
restrict: 'E',
replace: true,
scope: {model: '='},
controller: ['$scope', '$element', '$attrs', function ($scope, $element, $attrs) {
... other logics
}]
};
}]);
with this directive's template (
app/views/mylist.html):
<div>
<div class="input-group filtercontrols">
<span class="input-group-addon">Filter</span>
<input type="text" class="form-control" placeholder="filter by" ng-model="filter">
<span class="input-group-addon" ng-click="filter = ''">reset</span>
</div>
<div class="input-group ordercontrols">
<label class="radio-inline">
<input type="radio" value="label" ng-model="orderby"> Order by label
</label>
<label class="radio-inline">
<input type="radio" value="id" ng-model="orderby"> Order by id
</label>
<label>
<input type="checkbox" ng-model="reverse" />
Reverse
</label>
</div>
<div class="checkboxes">
<div class="checkbox widgetcontrols" ng-repeat="elem in model | filter:{$:filter} | orderBy: orderby:reverse">
<label>
<input class="smart" type="checkbox" ng-model="elem.value" value="{{elem.value || false}}" />
<span class="itemid">[{{elem.id}}]</span> {{elem.label}}
</label>
</div>
</div>
</div>
Done!
How it works? This reusable component let you filter items and order them
by label, id or reversed depending on the user input. If the user
clicks on reversed, the list is reversed, if the user digits "Fir" as
filter the list will be reduced and so on. Selecting one or more items,
the binded $scope.exampleList will reflect the user choice.
How to test this directive
The directive has an isolate scope where
model is two-way binded to
$scope.exampleList (see
model: '=' on the directive definition). The other ng-models you see into
the template directives are just internal models that lives into the
isolated scope of the myList directive (orderby, reverse, filter) and
they don't affect the outer scope.
So when you are trying to change the reverse, orderby or filter properties, you'll have to do it on the right isolated scope!
'use strict';
describe('Directive: myList', function () {
// load the directive's module
beforeEach(module('myListApp', 'views/mylist.html'));
var element,
$rootScope,
$compile,
$element,
scope;
beforeEach(inject(function (_$rootScope_, _$compile_) {
$rootScope = _$rootScope_;
scope = $rootScope.$new();
$compile = _$compile_;
$rootScope.model = [
{id: '001', label:'First item'},
{id: '002', label:'Second item'}
];
$element = angular.element('<my-list model="model"></my-list>');
element = $compile($element)(scope);
$rootScope.$apply();
}));
it('Labels order (reverse)', function () {
var isolateScope = $element.isolateScope();
isolateScope.reverse = true;
isolateScope.orderby = 'label';
isolateScope.$apply();
expect($element.find('.itemid').eq(0).text()).toBe('[002]');
});
});
So remember: .isolateScope is your friend!
Links: