2

I'm working with Angular6 and I'm trying to make my Jasmine test reach the callback inside mergeMap bellow, but after days of work I just can't do it.

callback inside mergeMap highlighted

My test returns correctly the refresh method, but it just doesn't seem to follow through the pipe.

This is the full method where I'm testing:

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<any> {
    if (
      this.jwtInterceptor.isWhitelistedDomain(req) &&
      !this.jwtInterceptor.isBlacklistedRoute(req)
    ) {
      const newRequest = req.clone();
      return next.handle(newRequest).pipe(
        tap(() => {
          const token = this.jwtHelper.tokenGetter();
          if (token) {
            const expirationTime = this.jwtHelper
              .getTokenExpirationDate()
              .getTime();
            const expirationLeft = expirationTime - Date.now();
            const expired =
              expirationLeft > 0 && expirationLeft < 5 * 60 * 1000;
            if (expired && !this.isUpdating) {
              this.isUpdating = true;
              return this.refresh().pipe(
                mergeMap(() => { // this is the unreachable part
                  return this.jwtInterceptor.intercept(req, next);
                })
              );
            }
          }
        }),
        catchError(err => {
          if (err instanceof HttpErrorResponse && err.status === 401) {
            this.router.navigate([this.config.routeToGoIfNotLoggedIn]);
          }
          return throwError(err);
        })
      );
    } else {
      return next.handle(req);
    }
  }

Here my this.refresh() method returned:

refresh(): Observable<any> {
    const refreshObservable = this.authAPI.refreshToken();
    const refreshSubject = new ReplaySubject<any>(1);
    refreshSubject.subscribe(
      data => {
        localStorage.setItem('token', data.jwt);
        this.isUpdating = false;
      },
      err => {
        this.isUpdating = false;
        this.userLoggedService.removeUserLoggedAndToken();
      }
    );

    refreshObservable.subscribe(refreshSubject);
    return refreshSubject;
  }
}

and this is my this.authAPI.refreshToken() fired inside refresh():

  refreshToken() {
    return this.http.get(`${this.config.baseAPIUrl}refreshToken`);
  }

The http call is correctly catch within my httpTestingController and the final expectation is to have this.jwtInterceptor.intercept(req, next) fired.

This is my spec for the part I'm failing:

it('should call Refresh method if token is expiring in 5 minutes', () => {
    const nowInMillisec = Number(new Date());
    const FourMinutesFromNow = nowInMillisec + 4 * 60000;
    spyOn(
      jwtService,
      'getTokenExpirationDate'
    ).and.returnValue(new Date(FourMinutesFromNow));

    const jwtInterceptor = TestBed.get(JwtInterceptor);
    const spyOnJwtInterceptor = spyOn(jwtInterceptor, 'intercept');

    dummyService.fakeHttpCall().subscribe();
    httpTestingController.expectOne(
      `${MockAuthConfig.baseAPIUrl}user/fake-call`
    );

    const callbackHttpFired = httpTestingController.expectOne(
      `${MockAuthConfig.baseAPIUrl}refreshToken`
    );
    expect(callbackHttpFired.request.url).toEqual(
      `${MockAuthConfig.baseAPIUrl}refreshToken`
    );
    expect(spyOnJwtInterceptor).toHaveBeenCalled(); // this spy is never fired... :(
  });

Any ideas?? Thank you very much!!

Анна
  • 1,248
  • 15
  • 26

1 Answers1

1

The usage of done() is vital here since the call of subscribe is asynchronous, the rest of your test is runned before the subscribe exits. Also, you should test the refresh method separatly. First test that the refresh was called in the intercept

The signature should then be changed with

it('should call Refresh method if token is expiring in 5 minutes', (done) => {    
  ...
  jwtInterceptor.intecept.subscribe(() => { 
    expect(spyOnJwtInterceptor).toHaveBeenCalled();
    done();
  });
}

So the test method will wait until done() is called. And the expect will go as planed.

JFPicard
  • 5,029
  • 3
  • 19
  • 43
  • That makes really sense. Thanks! But I'm having some trouble with my spies. The spy on `refresh` isn't triggered, even though trough console.logs I can see that the code clearly fires `refresh`. And `jwtInterceptor.intercept().subscribe(() ...` returns `Cannot read property 'subscribe' of undefined` I'll keep on investigating it and come back... :/ – Анна Dec 13 '18 at 11:40
  • You must remove the spy: const spyOnJwtInterceptor = spyOn(jwtInterceptor, 'intercept'); – JFPicard Dec 13 '18 at 13:20