0

I'm getting the error below

  An error was thrown in afterAll
  TypeError: this.router.navigate(...) is undefined in http://localhost:9876/_karma_webpack_/main.js (line 6552)
  59104/signOut/<@http://localhost:9876/_karma_webpack_/main.js:6552:25
  __tryOrUnsub@http://localhost:9876/_karma_webpack_/vendor.js:161782:16
  next@http://localhost:9876/_karma_webpack_/vendor.js:161721:22
  _next@http://localhost:9876/_karma_webpack_/vendor.js:161671:26
  next@http://localhost:9876/_karma_webpack_/vendor.js:161648:18
  subscribeToArray/<@http://localhost:9876/_karma_webpack_/vendor.js:166594:20
  _trySubscribe@http://localhost:9876/_karma_webpack_/vendor.js:161093:25
  subscribe@http://localhost:9876/_karma_webpack_/vendor.js:161079:22
  signOut@http://localhost:9876/_karma_webpack_/main.js:6547:38
  SignOutComponent@http://localhost:9876/_karma_webpack_/main.js:6544:14
  SignOutComponent_Factory@ng:///SignOutComponent/ɵfac.js:5:10
  hydrate@http://localhost:9876/_karma_webpack_/vendor.js:60414:35
  get@http://localhost:9876/_karma_webpack_/vendor.js:60233:33
  get@http://localhost:9876/_karma_webpack_/vendor.js:74309:33
  inject@http://localhost:9876/_karma_webpack_/vendor.js:83930:52
  inject@http://localhost:9876/_karma_webpack_/vendor.js:83826:37
  63231/</<@http://localhost:9876/_karma_webpack_/main.js:6499:80
  invoke@http://localhost:9876/_karma_webpack_/polyfills.js:600:26
  onInvoke@http://localhost:9876/_karma_webpack_/vendor.js:167198:39
  invoke@http://localhost:9876/_karma_webpack_/polyfills.js:599:52
  run@http://localhost:9876/_karma_webpack_/polyfills.js:362:43
  runInTestZone@http://localhost:9876/_karma_webpack_/vendor.js:167478:34
  wrapTestInZone/<@http://localhost:9876/_karma_webpack_/vendor.js:167493:20
  <Jasmine>
  invokeTask@http://localhost:9876/_karma_webpack_/polyfills.js:634:31
  runTask@http://localhost:9876/_karma_webpack_/polyfills.js:406:47
  drainMicroTaskQueue@http://localhost:9876/_karma_webpack_/polyfills.js:810:35

when I run the test below in my spec file

import { HttpClientModule } from '@angular/common/http';
import { TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule } from '@ngx-translate/core';
import { SessionStorageService } from '@shared/services/session-storage/session-storage.service';
import { SignOutComponent } from './signout.component';
import { SignInService } from '@shared/services/signin.service';
import { Router } from '@angular/router';
import { HomeComponent } from '../home/home.component';

describe('SignOut Component', () => {
  let component: SignOutComponent;
  let mockRouter: jasmine.SpyObj<Router>;
  let mockSignInService: jasmine.SpyObj<SignInService>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [SignOutComponent],
      imports: [
        CommonModule,
        TranslateModule,
        RouterTestingModule,
        HttpClientModule,
        RouterTestingModule.withRoutes([
          {
            path: 'home',
            component: HomeComponent
          }
        ])
      ],
      providers: [
        SignOutComponent,
        {
          provide: Router,
          useValue: {
            navigate: jasmine.createSpy('navigate')
          }
        },
        {
          provide: SignInService,
          useValue: {
            signout: jasmine.createSpy('signout').and.returnValue(of({ token: 'accessToken', user: {} }))
          }
        },
        {
          provide: SessionStorageService,
          useValue: {
            get: jasmine.createSpy('get').and.returnValue(''),
            set: jasmine.createSpy('set').and.returnValue('')
          }
        }
      ]
    });
    component = TestBed.inject(SignOutComponent);
    mockRouter = TestBed.inject(Router) as jasmine.SpyObj<Router>;
    mockSignInService = TestBed.inject(SignInService) as jasmine.SpyObj<SignInService>;
  });

  it('should create the component', () => {
    expect(component).toBeTruthy();
    expect(mockSignInService.signout).toHaveBeenCalledTimes(1);
    expect(mockRouter.navigate).toHaveBeenCalled();
    expect(mockRouter.navigate).toHaveBeenCalledWith(['/home']);
  });
});

Not sure what am I missing.

I've tried with a different approach as per the link below but it didn't work.

Jasmine testcase for angular2 router.navigate()

Below is the component code

import { Router } from '@angular/router';
import { SignInService } from '@shared/services/signin.service';
import { SessionStorageService } from '@shared/services/session-storage/session-storage.service';

@Component({
  selector: 'app-signout',
  templateUrl: './signout.component.html',
  styleUrls: ['./signout.component.scss']
})
export class SignOutComponent {
  constructor(private router: Router, private signInService: SignInService, private sessionStorageService: SessionStorageService) {
    this.signOut();
  }

  signOut() {
    this.signInService.signout().subscribe(res => {
      this.sessionStorageService.set('isUserSignedIn', 'false');
      this.sessionStorageService.set('token', '');
      this.sessionStorageService.set('athleteId', '');
      this.sessionStorageService.set('user', '');

      this.router.navigate(['/home']).then(() => {
        window.location.reload();
      });
    });
  }
}

I had to implement window.location.reload to force a page reload since it wasn't redirecting to the correct page,

2 Answers2

0

I was able to get this to work after a few changes.

  • control when you create the component as that is what calls this function
  • spy on the service and set the values to return BEFORE you create the component
  • move the window reload to a new function/service so you can spy and not actually call window.location.reload in the unit test
describe('SignOut Component', () => {
  let component: SignOutComponent;
  let mockRouter: jasmine.SpyObj<Router>;
  let mockSignInService: jasmine.SpyObj<SignInService>;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [SignOutComponent],
      imports: [
        CommonModule,
        TranslateModule,
        RouterTestingModule,
        HttpClientModule,
        RouterTestingModule.withRoutes([
          {
            path: 'home',
            component: HomeComponent
          }
        ])
      ],
      providers: [
        // usually don't need to provide the component here
        SignOutComponent,
        {
          provide: Router,
          useValue: {
            navigate: jasmine.createSpy('navigate')
          }
        },
        {
          provide: SignInService,
          useValue: {
            signout: jasmine.createSpy('signout').and.returnValue(of({ token: 'accessToken', user: {} })),
            // add this method only so we can spy on it and not call reload for real during the test
            reloadWindowLocation: jasmine.createSpy('reloadWindowLocation')
          }
        },
        {
          provide: SessionStorageService,
          useValue: {
            get: jasmine.createSpy('get').and.returnValue(''),
            set: jasmine.createSpy('set').and.returnValue('')
          }
        }
      ]
    });    
    // move creation to inside of the test - you can also move into a local function
  });

  it('should create the component', () => {
    // set up spies to return values BEFORE you create the component
    mockRouter = TestBed.inject(Router) as jasmine.SpyObj<Router>;
    mockSignInService = TestBed.inject(SignInService) as jasmine.SpyObj<SignInService>;
    // set router to resolve a promise, as you are wanting that to happen
    mockRouter.navigate.and.resolveTo(true);

    // now create the component
    component = TestBed.inject(SignOutComponent);
    // normally I do the following to create a component
    // const fixture = TestBed.createComponent(AppComponent);
    // const component = fixture.componentInstance;

    expect(component).toBeTruthy();
    expect(mockSignInService.signout).toHaveBeenCalledTimes(1);
    expect(mockRouter.navigate).toHaveBeenCalled();
    expect(mockRouter.navigate).toHaveBeenCalledWith(['/home']);
    // move your window.location.reload to somewhere we can spy on it and not call it for real during the unit test
    expect(mockSignInService.reloadWindowLocation).toHaveBeenCalled();
  });
});

Let me know if this does not work for you.

Wesley Trantham
  • 1,154
  • 6
  • 12
0

I actually solved this by removing the router and using the code below instead in the Component when I needed to navigate.

window.location.href = '/home';

Then in the test file I added this to avoid having to reload the page.

window.onbeforeunload = jasmine.createSpy();

dodekja
  • 537
  • 11
  • 24