andrius.k
andrius.k

Reputation: 819

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

Upvotes: 5

Views: 4583

Answers (3)

Dmitri Zaitsev
Dmitri Zaitsev

Reputation: 14056

Note that in Angular version 1.3 browser.getLocationAbsUrl() returns only the relative path. See the issue in https://github.com/angular/protractor/issues/1436:

Using angular 1.3 in the app under test and protractor 1.3.1 browser.getLocationAbsUrl() returns a relative url instead of the absUrl due to using angular.getTestability().getLocation() instead of $location.absUrl(). It's probably as easy as adding a getLocationAbs() to $$testability, but that goes into architectural questions I don't have context for.

Upvotes: 0

Tim Hong
Tim Hong

Reputation: 2764

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

Upvotes: 2

Cal
Cal

Reputation: 188

You might need to wait for the page to get loaded:

browser.get('/#/main');
var ptor = protractor.getInstance();
ptor.waitForAngular();
expect(browser.getLocationAbsUrl()).toMatch("/main");

Upvotes: 0

Related Questions