Shamoon
Shamoon

Reputation: 43491

Why won't the state change in a unit test for an AngularJS controller?

I'm using Angular-ui Router not $routeProvider. My controller is:

myApp.controller('LoginController', [
  '$scope', '$rootScope', '$state', 'userService', function($scope, $rootScope, $state, userService) {
    $scope.init = function() {
      $rootScope.pageTitle = 'Login';
      $scope.auth = {};
      return $scope.errors = null;
    };
    $scope.login = function() {
      return userService.login($scope.auth).then(function(response) {
        console.log(response);
        if (response.authenticated === true) {
          console.log("Movin states!");
          return $state.go('dashboard');
        } else {
          return $scope.errors = {
            message: 'Invalid credentials',
            type: 'error'
          };
        }
      });
    };
    return $scope.init();
  }
]);

In my test, I'm doing:

it('should properly authenticate a user', function() {
  $scope.init();
  $scope.auth = {
    username: '[email protected]',
    password: 'mypassword'
  };
  $scope.login();
  deferred.resolve({
    authenticated: true
  });
  $rootScope.$apply();
  expect($state.current.name).toEqual('dashboard');
});

However, the $state.current.name stays as ''. Why won't it update?

Upvotes: 0

Views: 1984

Answers (2)

Khanh TO
Khanh TO

Reputation: 48972

Try this plunker:

 beforeEach(inject(function($rootScope, $controller, userService,$q,$state) {
    $scope = $rootScope.$new();

    ctrl = $controller('LoginController', {
      $scope: $scope,
      $rootScope:$rootScope,
      userService:userService,
      $state:$state
    });

    _userService = userService;
    _$q = $q;
    _$state = $state;

  }));

  it('should properly authenticate a user', function() {
   // $scope.init();
   // $scope.auth = {
  //    username: '[email protected]',
  //    password: 'mypassword'
  //  };

    //Set up
    var deferred = _$q.defer();
    spyOn(_userService,"login").and.callFake(function(){//fake the real implementation 
      //we don't need parameters in our fake function.
      //we only care about our return value.   
         return deferred.promise;
    }); 
    spyOn(_$state,"go"); //mock the $state.go method so that we can verify whether it was called.

    $scope.login();
    deferred.resolve({
      authenticated: true
    });

    $scope.$digest();
    expect(_$state.go).toHaveBeenCalledWith('dashboard');//verify
  });

Some points to note:

  • Unit tests should be isolated from each other and from other real implementations. Therefore when you set up a test, the test should not rely on $scope.init(); to set up your test. This implementation may change which may introduce failure to your test inadvertently.

  • Instead of testing for $state.current.name, you should test whether $state.go was called with correct parameters as it's more direct.

Upvotes: 2

Rajendra Khabiya
Rajendra Khabiya

Reputation: 2030

You have messed all things in your controller try below things in structured format for a login :

  var app = angular.module('app', [...Define dependencies here..]);

    //Contant For Login Process

       app.constant(

                'AUTH_EVENTS', {
                   loginSuccess: 'auth-login-success',
                   loginFailed: 'auth-login-failed',
                   logoutSuccess: 'auth-logout-success',
                   sessionTimeout: 'auth-session-timeout',
                   notAuthenticated: 'auth-not-authenticated',
                   notAuthorized: 'auth-not-authorized'
                }
       )

//Service to maintain User Session after Login

        .service('Session', function () {

            this.setSession = function(sessionId, userId, userRole){
                this.id = sessionId;
                this.userId = userId;
                this.userRole = userRole;    
            };
            this.create = function (sessionId, userId, userRole) {

              localStorage.setItem("sessionId" , sessionId);
              localStorage.setItem("userId" , userId);
              localStorage.setItem("userRole" , userRole);
              this.setSession(sessionId, userId, userRole);

            };
            this.destroy = function () {
              localStorage.clear();
              this.id = null;
              this.userId = null;
              this.userRole = null;
            };
            return this;

            }
    )

//Auth Service Factory for login logic

       .factory('AuthService', function ($http, Session) {

               return {
                 login: function (credentials) {
                   return $http
                     .post('LOGIN_API_URL_HERE', credentials)
                     .then(function (res) {
                       Session.create('1', '1', 'Employee');
                     });

                 },
                 isAuthenticated: function () {
                   return !!Session.userId;
                 },
                 isAuthorized: function (authorizedRoles) {
                   if (!angular.isArray(authorizedRoles)) {
                     authorizedRoles = [authorizedRoles];
                   }
                   return (this.isAuthenticated() && authorizedRoles.indexOf(Session.userRole) !== -1);
                 }
               };
       })


//Controller 

     .controller('loginController', ['$scope','$location', 'AUTH_EVENTS', 'AuthService', function($scope, $location, AUTH_EVENTS, AuthService){

                if (AuthService.isAuthenticated() === false) {

                    $scope.page_heading = "App";
                    $scope.credentials = {
                        username: '',
                        password: ''
                    };
                    $scope.login = function (credentials) {

                     if(credentials.username != "" && credentials.password != ""){

                        AuthService.login(credentials).then(function () {
                            $scope.login_message = AUTH_EVENTS.loginSuccess;
                             // Path After login        
                             $location.path("/");
                              }, function () {
                            $scope.login_message = AUTH_EVENTS.loginFailed;

                              });

                     }else{
                        $scope.login_message =  credentials.username == "" ? 'Username Required' :  $scope.login_message;
                        $scope.login_message =  credentials.password == "" ? 'Password Required' :  $scope.login_message;
                        $scope.login_message =  credentials.username == "" && credentials.password == "" ? 'Username & Password Required' : $scope.login_message;
                     }
                    };
                }else{
                               // Path After login
                    $location.path("/");
                }
            }]);

Now after login you can check anywhere for session like this

if (localStorage.getItem('sessionId') != "")
       // Check for session in localstorage used in case of  single time login
    Session.setSession(localStorage.getItem('sessionId'),localStorage.getItem('userId'), localStorage.getItem('userRole'));

if (AuthService.isAuthenticated()){

   // Redirect to Dashboard
}else{

  //Redirect to Login
}

Upvotes: 0

Related Questions