mcv
mcv

Reputation: 4429

Element with directive doesn't $compile properly in unit test

My directive runs fine in the browser. It's just the unit test I can't seem to get working. The directive creates a simple slider, and sets a few values on the scope, including min.

In the unit test, $compile(element) seems to just wrap it in jqlite without doing anything else with it. Well, it also gives it a scope apparently, but there's nothing on the scope. The template also hasn't been applied.

My unit test:

describe('Given the slider directive', function () {

beforeEach(function() {
    module('app');
});

beforeEach(inject(function($httpBackend){
    // necessary because .whenGET().passThrough() doesn't seem to work
    $httpBackend.whenGET('partials/slider.html').respond('<div class="slider horizontal">'+
        '<div class="min">{{min}}</div>'+
        '<div class="max">{{max}}</div>'+
        '<a slider-handle class="handle" ng-class="{focus: focus}"'+
        'role="slider"'+
        'aria-valuemin="{{min}}" aria-valuemax="{{max}}" aria-valuenow="{{slider.value}}" aria-labelledby="{{id}}-label" aria-controls="{{id}}-value"'+
        'tabindex="0"'+
        'ng-keydown="handleKeyDown($event)" ng-keypress="handleKeyPress($event)"'+
        'ng-focus="handleFocus($event)" ng-blur="handleBlur($event)"'+
        'ng-mousedown="handleMouseDown($event)"'+
        'ng-style="{\'left\': slider.left}"></a>'+
        '<div ng-style="{\'left\': slider.left}" ng-show="showVals" id="{{id}}-value" class="slider-value" role="presentation">{{slider.value}}</div>'+
    '</div>');
}));

it('should compile and set the scope correctly', inject(function($compile, $rootScope) {
    var element = $compile('<div slider min="6" max="18" step="1" ng-model="value"></div>')($rootScope);
    $rootScope.$digest();

    var iScope = element.scope();

    iScope.$digest();

    console.log(iScope);
    console.log(iScope.min);
    console.log(element.html());
    console.log(element);

    expect(element.html()).toContain("6");
    expect(element.find('div[class=min]').html()).toBe(6);
    expect(iScope.min).toBe(6);

}));
});

The console output of this is:

Scope{$id: '00T', $$childTail: null, $$childHead: null, $$prevSibling: null, $$nextSibling: null, $$watchers: null, $parent: null, $$phase: null, $root: Scope{$id: '00T', $$childTail: null, $$childHead: null, $$prevSibling: null, $$nextSibling: null, $$watchers: null, $parent: null, $$phase: null, $root: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., safeApply: ...}, this: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., safeApply: ...}, $$destroyed: false, $$asyncQueue: [], $$postDigestQueue: [], $$listeners: Object{}, $$isolateBindings: Object{}, safeApply: function (a) { ... }}, this: Scope{$id: '00T', $$childTail: null, $$childHead: null, $$prevSibling: null, $$nextSibling: null, $$watchers: null, $parent: null, $$phase: null, $root: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., safeApply: ...}, this: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., safeApply: ...}, $$destroyed: false, $$asyncQueue: [], $$postDigestQueue: [], $$listeners: Object{}, $$isolateBindings: Object{}, safeApply: function (a) { ... }}, $$destroyed: false, $$asyncQueue: [], $$postDigestQueue: [], $$listeners: Object{}, $$isolateBindings: Object{}, safeApply: function (a) { ... }}
undefined
''
{0: <div slider="" min="6" max="18" step="1" ng-model="value" class="ng-scope"></div>, length: 1}

I've got a JSFiddle with the entire (slightly cleaned up) code, but there it mysteriously doesn't even get past the $compile, which is not the same problem I have locally. Not sure if the JSFiddle is any use, because now I've got two mysterious problems instead of one.

Upvotes: 2

Views: 801

Answers (1)

mcv
mcv

Reputation: 4429

Months of no answer or comments while I work on other stuff, and now I found the answer. It's really simple:

$httpBackend.flush();

When you use $httpBackend.whenGET() in a unit test, you always need to flush. So when you test a directive with a templateUrl, you need to flush() after you $compile().

I hope this is useful to someone out there.

Upvotes: 4

Related Questions