0

I have angular 8 application and I am using the OidcSecurityService for identity server.

And I am bussy to write some unit tests for it. But I am stuck on the following code part:

 ngOnInit() {
    this.oidcSecurityService
      .checkAuth()
 
      .subscribe(isAuthenticated => {
        if (!isAuthenticated) {
          Eif ('/autologin' !== window.location.pathname) {
            this.write('redirect', window.location.pathname);
            this.router.navigate(['/autologin']);
          }
        }
        if (isAuthenticated) {
          this.navigateToStoredEndpoint();
        }
      });
    //console.log('windowPath',  window.location.pathname);
  }

and the complete ts file looks like this:


export class AppComponent implements OnInit {
  title = 'cityflows-client';
  constructor(public oidcSecurityService: OidcSecurityService, public router: Router) {}

  ngOnInit() {
    this.oidcSecurityService
      .checkAuth()

      .subscribe(isAuthenticated => {
        if (!isAuthenticated) {
          if ('/autologin' !== window.location.pathname) {
            this.write('redirect', window.location.pathname);
            this.router.navigate(['/autologin']);
          }
        }
        if (isAuthenticated) {
          this.navigateToStoredEndpoint();
        }
      });
    //console.log('windowPath',  window.location.pathname);
  }


  /**
   * Generate new set of access tokens for a http call
   */
  refreshSession() {
    this.oidcSecurityService.authorize();
  }

  /**
   * Redirect function for redirecting the user to the login page when application starts.
   */
  private navigateToStoredEndpoint() {
    const path = this.read('redirect');

    if (this.router.url === path) {
      return;
    }
    if (path.toString().includes('/unauthorized')) {
      this.router.navigate(['/']);
    } else {
      this.router.navigate([path]);
    }
  }

  /**
   *
   * @param key
   * For checking if user is authenticated for the URL
   * And if not will go back to root directory
   */
  private read(key: string): any {
    const data = localStorage.getItem(key);
    if (data != null) {
      return JSON.parse(data);
    }
    return;
  }

  /**
   *
   * @param key
   * @param value
   * for checking if url is the correct one
   */
  private write(key: string, value: any): void {
    localStorage.setItem(key, JSON.stringify(value));
  }
}

and my unit test looks like this:

import { CommonModule } from '@angular/common';
import { TestBed, async, ComponentFixture } from '@angular/core/testing';
import { Router } from '@angular/router';
import { RouterTestingModule } from '@angular/router/testing';
import { OidcSecurityService } from 'angular-auth-oidc-client';
import { of } from 'rxjs';
import { AppComponent } from './app.component';
import { OidcSecurityServiceStub } from './shared/mocks/oidcSecurityServiceStub';
import { routes } from './app-routing.module';describe('AppComponent', () => {


  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;
  let authenticatedService: OidcSecurityServiceStub;
  const routerSpy = { navigate: jasmine.createSpy('/autologin') };
  const routerNavigateSpy = { navigate: jasmine.createSpy('navigate') };

  let router: Router;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [RouterTestingModule],
      providers: [
        { provide: OidcSecurityService, useClass: OidcSecurityServiceStub },
        { provide: Router, useValue: routerSpy },
        { provide: Router, useValue: routerNavigateSpy }
      ],

      declarations: [AppComponent]
    }).compileComponents();
  }));
  beforeEach(() => {
    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    authenticatedService = new OidcSecurityServiceStub();

    fixture.detectChanges();
  });

  it('should create the app', () => {
    expect(component).toBeTruthy();
  });

  it('Navigate if not authenticated', () => {
    spyOn(component.oidcSecurityService, 'checkAuth').and.returnValue(of(false));
    component.ngOnInit();
    expect(routerSpy.navigate).not.toHaveBeenCalledWith(['/']);
    
  });

  it(' Should Navigate if not is root path ', () => {
    spyOn(component.oidcSecurityService, 'checkAuth').and.returnValue(of(false));   
    component.ngOnInit();
    expect(routerSpy.navigate).not.toHaveBeenCalledWith('/autologin');
   
  }); 
});

But in the coverage rapport I see a E on this line:

   if ('/autologin' !== window.location.pathname) {

Else path not taken.

So what I have to change?

Thank you

I just can do:

public href: string = '/';

and then:

  ngOnInit() {
    this.oidcSecurityService
      .checkAuth()

      .subscribe(isAuthenticated => {
        if (!isAuthenticated) {
          if ('/autologin' !== this.href) { 
            this.write('redirect', this.href);
            this.router.navigate(['/autologin']);
          }
        }
        if (isAuthenticated) {
          this.navigateToStoredEndpoint();
        }
      });
   
  }
mightycode Newton
  • 3,229
  • 3
  • 28
  • 54

2 Answers2

0

I had a similar problem in writing unit tests for my service file and I searched around on the web, found 1 answer that solved the problem for me.

Link I referred: https://cheriecodes.wordpress.com/2013/02/03/jasmine-how-to-mock-window-location/

The basic idea is to use a function to return the value of "window.location.pathname" so that we can "spyOn" that function.

So your code will change to:

getPathname(): string {
  return window.location.pathname;
}

ngOnInit() {
  this.oidcSecurityService
    .checkAuth()

    .subscribe(isAuthenticated => {
      if (!isAuthenticated) {
        Eif('/autologin' !== this.getPathname()) {
          this.write('redirect', this.getPathname());
          this.router.navigate(['/autologin']);
        }
      }
      if (isAuthenticated) {
        this.navigateToStoredEndpoint();
      }
    });
  //console.log('windowPath',  this.getPathname());
}

and add another unit test like:

it('Navigate if not authenticated', () => {
  spyOn(component.oidcSecurityService, 'checkAuth').and.returnValue( of (false));
  // Add this line to change the value of "window.location.pathname" as per your requirement.
  spyOn(component, 'getPathname').and.returnValue('/autologin');
  component.ngOnInit();
  expect(routerSpy.navigate).not.toHaveBeenCalledWith('/autologin');

});
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
GK01
  • 1
  • 2
-1

Try adding something like this:

it('should navigate to autologin', () => {
  const oldPathName = window.location.pathname;
  spyOn(component.oidcSecurityService, 'checkAuth').and.returnValue(of(false));
  window.location.pathname = '/somethingElse'; // I am not sure if this will change the URL or not. 
// If it changes the URL in the browser, it can be bad.
  component.ngOnInit();
  expect(routerSpy.navigate).toHaveBeenCalledWith('/autologin');
  window.location.pathname = oldPathName; // restore it to the old version
});

Instead of using location.pathname, you should be using ActivatedRoute. Then you can mock it easily in your unit tests.

https://angular.io/api/router/ActivatedRoute How to use ActivatedRoute in Angular 5?

Or maybe even the router to get the current URL. Get current url in Angular

AliF50
  • 16,947
  • 1
  • 21
  • 37