Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular Jasmine test response interceptor

I'm trying to test my response interceptor but I have a hard time figuring out how to mock the $window object. Here is my interceptor code :

'use strict';

angular.module('Domain.handlers')

.config(function($httpProvider) {
  $httpProvider.responseInterceptors.push('UnauthorizedInterceptor');
})

.factory('UnauthorizedInterceptor', function($q, $injector, $window, ENV) {
  return function(promise) {
    var success = function(response) { return response; };
    var error   = function(response) {
      if (response.status === 401) {
        $window.location.href = ENV.account + '/oauth/authorize?client_id=' + ENV.clientId + '&redirect_uri=' + ENV.app + '/oauth/callback&response_type=token';
      }
      return $q.reject(response);
    };
    return promise.then(success, error);
  };
});

And here is my spec :

'use strict';

describe('Domain.handlers.response', function() {
  var UnauthorizedInterceptor,
      httpProvider,
      $httpBackend,
      $http,
      token = '123456789';

  beforeEach(module('Domain.handlers', function($httpProvider) {
    httpProvider = $httpProvider;
  }));

  beforeEach(inject(function(_UnauthorizedInterceptor_, _$httpBackend_, _$http_) {
    UnauthorizedInterceptor = _UnauthorizedInterceptor_;
    $httpBackend = _$httpBackend_;
    $http = _$http_;
  }));

  describe('UnauthorizedInterceptor', function() {
    it('should be defined', function() {
      expect(UnauthorizedInterceptor).toBeDefined();
    });

    describe('HTTP status', function() {
      describe('is 200 OK', function() {
        it('should return a 200 status', function() {
          $httpBackend.expectGET('http://api.domain.com/clients').respond(200, {});
          $http.get('http://api.domain.com/clients');
          $httpBackend.flush();
        });
      });

      describe('is 401 Unauthorized', function() {
        it('should redirect to accounts.domain.com', inject(function($window) {
          $httpBackend.expectGET('http://api.domain.com/clients').respond(401, {});
          $http.get('http://api.domain.com/clients');
          expect($window.location.href).toEqual('http://accounts.domain.com/oauth/.....');
          $httpBackend.flush();
        }));
      });
    });
  });
});

I've got a : Expected 'http://localhost:8080/context.html' to equal 'http://accounts.domain.com/oauth/.....'. Any help on how to mock properly the $window object or more generally how to test a 401 + redirection case?

like image 735
lkartono Avatar asked Nov 22 '14 02:11

lkartono


2 Answers

You should structure your interceptor definition using the more recent syntax. Your URL construction should also be in a service so that it can easily be mocked in tests.

.factory('UnauthorizedInterceptor', function($q, $window, OtherService) {
  var service = {
    responseError: handleUnauthorized
  };

  return service;

  function handleUnauthorized(rejection) {
    if (rejection.status === 401) {
      $window.location.href = OtherService.getUnauthorizedRedirectURL();
    }
    return $q.reject(rejection);
  }
});

Doing so will let you test it just like any other factory without having to worry about the internal implementations of $http interceptors, or having to mock responses with $httpBackend.

describe('Domain.handlers.response', function() {
  var $window,
      UnauthorizedInterceptor,
      OtherService,
      redirectUrl = 'someUrl';

  beforeEach(module('Domain.handlers'));

  beforeEach(function () {
    $window = { location: { href: null } };

    module(function($provide) {
      $provide.value('$window', $window);
    });
  });

  beforeEach(inject(function(_UnauthorizedInterceptor_, _OtherService_) {
    UnauthorizedInterceptor = _UnauthorizedInterceptor_;
    OtherService = _OtherService_;

    spyOn(OtherService, 'getUnauthorizedRedirectURL').andReturn(redirectUrl);
  }));

  describe('UnauthorizedInterceptor', function() {
    it('should be defined', function() {
      expect(UnauthorizedInterceptor).toBeDefined();
    });

    it('should have a handler for responseError', function () {
      expect(angular.isFunction(UnauthorizedInterceptor.responseError)).toBe(true);
    });

    describe('when HTTP 401', function () {
      beforeEach(function () {
        var rejection = { status: 401 };
        UnauthorizedInterceptor.responseError(rejection);
      });

      it('should set window location', function () {
        expect($window.location.href).toBe(redirectUrl);
      });
    });

    describe('when not HTTP 401', function () {
      beforeEach(function () {
        var rejection = { status: 500 };
        UnauthorizedInterceptor.responseError(rejection);
      });

      it('should not set window location', function () {
        expect($window.location.href).not.toBe(redirectUrl);
      });
    });
  });
});
like image 186
user2943490 Avatar answered Sep 26 '22 06:09

user2943490


Here is an example of the responseError interceptor and the corresponding jasmine spec.

angular.module('interceptorDemo').factory('redirectInterceptor', ['$q', '$window', function($q, $window) {
    'use strict';

    function handleUnauthorizedAccess(config) {
        if (401 === config.status) {
            $window.location = '/signIn/';
        }
        return $q.reject(config);
    }

    return {
        responseError: handleUnauthorizedAccess
    };
}]);

The interceptor intercepts the ajax request, if the request is failed, then if the status code is 401 then user is redirected to signIn page.

Jasmine spec for the same is:

describe('redirectInterceptor specs', function() {

    var redirectInterceptor, $q;

    beforeEach(module('interceptorDemo'));

    beforeEach(function() {
        $window = {
            location: {
                href: null
            }
        };

        module(function($provide) {
            $provide.value('$window', $window);
        });
    });

    beforeEach(inject(function(_redirectInterceptor_, _$q_) {
        redirectInterceptor = _redirectInterceptor_;
        $q = _$q_;
        spyOn($q, 'reject');
    }));

    describe('redirectInterceptor specs', function() {
        it('should redirect to signIn page for unauthorized access', function() {
            var response = {
                status: 401,
                config: {}
            };
            var promise = redirectInterceptor.responseError(response);
            expect($window.location).toBe('/singIn/');
            expect($q.reject).toHaveBeenCalled();
        });

        it('should not redirect to signIn page for error code other than unauthorized access', function() {
            var response = {
                status: 404,
                config: {}
            };
            var promise = redirectInterceptor.responseError(response);
            expect($window.location).toEqual({
                href: null
            });
            expect($q.reject).toHaveBeenCalled();
        });

    });
});

We have spied on the $q so we can also test that the reject is called for the 401 error.

like image 21
Sanjay Bharwani Avatar answered Sep 23 '22 06:09

Sanjay Bharwani