Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stop ng2-idle for protractor tests

Tags:

I'm using ng2-idle to auto logout users after a period of time. I initialise it in my appComponent constructor:

import {Idle, DEFAULT_INTERRUPTSOURCES} from '@ng-idle/core';

export class AppComponent {

    constructor(
      private idle:Idle) {

      idle.setIdle(21600);
      idle.setTimeout(1);
      idle.setInterrupts(DEFAULT_INTERRUPTSOURCES);

      idle.onTimeout.subscribe(() => { this.logout(); });
    };

Upon successful login I start it up in my LoginComponent using this.idle.watch() (Idle is injected into the constructor).

This all works fine but when I go to run my protractor tests they timeout, I think because protractor is left waiting for the timeout to end which will take a while since I set ng2-idle to 6 hours!

Error: Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.

If I don't initialise using Idle.watch() then the tests run.

What I want to do is to set Idle.stop(); in the onPrepare block in my protractor config file and reset it to Idle.watch(); after the tests are complete in my onComplete block.

I have tried var idle = require('@ng-idle/core'); in the protractor conf file but it throws me back the following:

ReferenceError: document is not defined

So how can I require the ng2-idle module in protractors config file?

like image 326
Mark Kenny Avatar asked Jan 12 '17 11:01

Mark Kenny


1 Answers

I cannot provide you a solution for the question you asked. But I will provide you a different approach to solve this problem. While setting up Protractor for my team, I encountered the same issue after implementing a session timeout strategy using ng-idle. After some heartburn on how to solve this, I realized Angular zone API can be used to resolve this. Here is my appcomponent with a solution. See runOutsideAngular and run methods in the zone.

Notice that all state changes are wrapped inside the run method. If not, the change detection will not work and none of the state fields including count down timer will not update UI.

This solution involves modifying the application which uses ng-idle as this cannot be done within Protractor in my current understanding.

this.ngZone.runOutsideAngular

@Component( {
selector   : 'app-root',
templateUrl: './app.component.html',
styleUrls  : [ './app.component.scss' ]
} )

export class AppComponent implements OnInit, OnDestroy{

idleState = 'Not started.';
idleStateTitle = '';
idleStateBtnText = '';
timedOut = false;

@ViewChild( DialogComponent ) dialog: DialogComponent;

constructor( private idle: Idle,
             private locationStrategy: LocationStrategy,
             private authUtils: AuthUtils,
             private ngZone: NgZone ){

    this.ngZone.runOutsideAngular(() => {
        //Idle timer is set to 28 minutes and timeout count down timer will run for 2 minutes.
        idle.setIdle( 28 * 60 );
        idle.setTimeout( 120 );
    });


    //When idling starts display the dialog with count down timer
    idle.onIdleStart.subscribe( () =>{
        this.idleState = '';
        this.idleStateTitle = 'Your session is about to expire.';
        this.idleStateBtnText = 'Stay Logged In';
        this.ngZone.run(() => this.dialog.show());
    } );

    //User stopped idling
    idle.onIdleEnd.subscribe( () =>{
        this.idleState = 'No longer idle.';
    } );

    //Show timeout warning two minutes before expiry
    idle.onTimeoutWarning.subscribe( ( countdown ) =>{
        this.ngZone.run(() => { this.idleState = 'Your session will time out in ' + countdown + ' seconds! '});
    } );

    //Session Timed out
    idle.onTimeout.subscribe( () =>{
        this.ngZone.run( () =>{
            this.idleStateTitle = "Your session has expired.";
            this.idleState = 'To Continue, log back in';
            this.timedOut = true;
            this.idleStateBtnText = 'Log in Again';
        } );
    });
}

reset(){
    this.ngZone.runOutsideAngular(() => {
        this.idle.watch();
    });
    this.ngZone.run( () =>{
        this.idleState = 'Started.';
        this.idleStateTitle = "";
        this.idleStateBtnText = '';
        this.timedOut = false;
    });
}

title = 'Projects';

/**
 * <p>Refresh Token by getting user token from the user service. Also reset the idle state timer here.</p>
 */
refreshToken(){
    this.authUtils.refreshToken();
    this.reset();
}

/**
 * Handle timeout
 */
processTimeout(){
    this.dialog.hide( null );
    if( this.timedOut ){
        AuthUtils.invalidateSession();
        let origin = window.location.origin;
        window.location.href = origin + this.locationStrategy.getBaseHref() + "?_="+ (Math.floor(Math.random() * 10000));
    }
    else{
        this.refreshToken();
    }
}

ngOnInit(): void{
    this.reset();
}

ngOnDestroy(): void{
    this.ngZone.runOutsideAngular(() =>{
        this.idle.stop();
    });
}
}

I had some partial success using browser.waitForAngularEnabled(false) and disabling and enabling them within beforeEach and afterEach jasmine routines. but they were flaky and did not work for all tests.

Edit: I checked ng2-idle issue tracker and found there is an open defect for this here

like image 143
santon Avatar answered Oct 14 '22 04:10

santon