Mocking the event object in AngularJS event unit testing

Nicolás Straub picture Nicolás Straub · Jan 13, 2014 · Viewed 10.6k times · Source

I have the following test:

    it('Should keep location when user rejects confirmation', inject(function ($controller, $rootScope) {
        var confirmStub = sinon.stub(),
            eventStub = {
                preventDefault: sinon.spy()
            };

        miscServiceStub = function () {
            this.confirm = confirmStub;
        };

        confirmStub.returns(false);

        initializeController($controller, 'Builder', $rootScope);

        $rs.$broadcast('$locationChangeStart', eventStub);
        expect(confirmStub).toHaveBeenCalledOnce();
        expect(confirmStub).toHaveBeenCalledWith('Are you sure you want to leave? you will loose any unsaved changes.');
        expect(eventStub.stopPropagation).toHaveBeenCalledOnce();

        miscServiceStub = function () {};
    }));

which tests this code:

$rootScope.$on('$locationChangeStart', function (event) {
    dump(arguments);
    if (!$miscUtils.confirm('Are you sure you want to leave? you will loose any unsaved changes.')){
        event.preventDefault();
    }
});

event.$stopPropagation doesn't call the mock event, and dump(arguments) shows that it is being passed into the event right after the real event object:

Chromium 31.0.1650 (Ubuntu) DUMP: Object{
    0: Object{name: '$locationChangeStart', targetScope: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., activeTab: ..., routeParams: ...}, preventDefault: function () { ... }, defaultPrevented: false, currentScope: Scope{$id: ..., $$childTail: ..., $$childHead: ..., $$prevSibling: ..., $$nextSibling: ..., $$watchers: ..., $parent: ..., $$phase: ..., $root: ..., this: ..., $$destroyed: ..., $$asyncQueue: ..., $$postDigestQueue: ..., $$listeners: ..., $$isolateBindings: ..., activeTab: ..., routeParams: ...}}, 
    1: Object{stopPropagation: spy}
}

how can I make it so the event object is the mock event and not the real event object itself? Am I approaching this the right way? I'm quite new to Angular and any comments on the code/test would be greatly appreciated.

If you need any more related code please tell me.

Answer

Jono picture Jono · Apr 19, 2014

$scope.$broadcast returns the Event object, so you can do this:

var event = $scope.$broadcast("someEvent");
expect(event.defaultPrevented).toBeTruthy();