Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJs ui-router. Protractor test failing

I'm creating a SPA using AngularJs and the ui-router from Angular-UI. Now I'm trying to create the authentication logic.

$rootScope.$on("$stateChangeStart", function (event, toState) {
    if(toState.authenticate && !MainService.isAuthenticated()) {
        if($cookieStore.get('authToken')) {
            MainService.loginWithToken($cookieStore.get('authToken'))
            .then(function() {
                $state.go(toState.name);
                event.preventDefault();
            });
        }

        $rootScope.requestPath = toState.name;
        $state.go('public.login');
        event.preventDefault();
    } 

    if(toState.url == '/login' && MainService.isAuthenticated()) {
        $state.go('private.main');
        event.preventDefault();
    }
});

On state change, this checks if state requires authentication and transfer to login state if necessary. Also if user is logged in it prevents from reaching the login state. Authentication is done by token stored in cookie.

This is my protractor test scenario:

describe('Routes', function() {
it('Should go to the selected path if user logged in', function() {
    browser.get('/');
    expect(browser.getLocationAbsUrl()).toMatch("/login");

    browser.manage().addCookie("authToken", "aaa");

    browser.manage().getCookie("authToken").then(function(cookie) {
        expect(cookie.name).toBe('authToken');
        expect(cookie.value).toBe('aaa');
    });

    browser.get('/');
    expect(browser.getLocationAbsUrl()).toMatch("/main");

    browser.get('/#/main');
    expect(browser.getLocationAbsUrl()).toMatch("/main");

    /* This part fails, because, when the user is logged in, 
    he should be transfered to main state, if he is trying to reach the 
    login page. In this failing case, the user is able to reach the 
    /login even if he is  logged in. */

    browser.get('/#/login');
    expect(browser.getLocationAbsUrl()).toMatch("/main");

    browser.manage().deleteCookie("authToken");

    browser.get('/#/login');
    expect(browser.getLocationAbsUrl()).toMatch("/login");

    browser.get('/#/main');
    expect(browser.getLocationAbsUrl()).toMatch("/login");
});

});

When I try to simulate the test behaviour myself, everything is ok, but when I run protractor:

Message:
 Expected 'http://localhost/#/login' to match '/main'.

Stacktrace: Error: Failed expectation

like image 841
andrius.k Avatar asked Oct 31 '22 23:10

andrius.k


1 Answers

I bumped into another question which resolved this issue.

Basically, you wait for an element in the new page to appear instead of relying on protractor to wait for state/page finish loading. At the time of writing this answer, protractor is still unreliable on waiting page fully loaded for ui-router. Protractor waits for $timeout and $http to be done.

official doc

so if you are using websocket, it might not be covered (at least according my observation).

The api you need to use is browser.wait

    browser.wait(function() {
       return $('#test321').isPresent(); // keeps waiting until this statement resolves to true
      }, 
      timeToWaitInMilliseconds, 
      'message to log to console if element is not present after that time'
    );

    expect($('#test321').isPresent()).toBe(true);  

You can find details in the following link Protractor, AngularJS, Parse -- Protractor does not wait for Angular to resolve

like image 165
Tim Hong Avatar answered Nov 11 '22 03:11

Tim Hong