0

While implementing the Spec file for the auth guard we write in the angular app is giving me an error I am not able to contemplate to what's happening.

Auth Guard -------------> should return FALSE if the user state is not logged in FAILED
        TypeError: actual.subscribe is not a function
            at VirtualAction.<anonymous> (node_modules/jasmine-marbles/es6/index.js:55:1)
            at VirtualAction._execute (node_modules/rxjs/_esm2015/internal/scheduler/AsyncAction.js:51:1)
            at VirtualAction._execute (node_modules/rxjs/_esm2015/internal/scheduler/VirtualTimeScheduler.js:59:1)
            at VirtualAction.execute (node_modules/rxjs/_esm2015/internal/scheduler/AsyncAction.js:39:1)
            at TestScheduler.flush (node_modules/rxjs/_esm2015/internal/scheduler/VirtualTimeScheduler.js:16:1)
            at TestScheduler.flush (node_modules/rxjs/_esm2015/internal/testing/TestScheduler.js:107:1)
            at toBeObservableComparer (node_modules/jasmine-marbles/es6/index.js:77:1)
            at <Jasmine>
            at UserContext.<anonymous> (src/app/core/guards/auth.guard.spec.ts:55:37)

Above is the issue I am facing While I pasted the code below for auth.guard.spec.ts and auth.guard.ts

import { TestBed } from '@angular/core/testing';
import { combineReducers, Store, StoreModule } from '@ngrx/store';
import { cold } from 'jasmine-marbles';
import { addMatchers, initTestScheduler } from 'jasmine-marbles';
import { RouterTestingModule } from '@angular/router/testing';
import { provideMockStore } from '@ngrx/store/testing';

import { AuthGuard } from './auth.guard';
import * as fromRoot from '../../app-state';
import * as fromAuth from '../../modules/auth/store/auth.reducer';
import * as fromAuthActions from '../../modules/auth/store/auth.actions';
import { User } from '../../modules/auth/login/login.model';
import { LogInComponent } from '../../modules/auth/login/login.component';

describe('Auth Guard', () => {
    let guard: AuthGuard;
    let store: Store<any>;
    const initialState = { loggedIn: false };
    beforeEach(() => {
        initTestScheduler();
        addMatchers();
        TestBed.configureTestingModule({
            imports: [
                RouterTestingModule.withRoutes([
                    { path: 'auth', component: LogInComponent }
                ]),
                StoreModule.forRoot({
                    ...fromRoot.reducers,
                    auth: combineReducers(fromAuth.reducer)
                })
            ],
            providers: [
                AuthGuard,
                provideMockStore({ initialState })
            ]
        });
        store = TestBed.inject(Store);
        spyOn(store, 'dispatch').and.callThrough();
        guard = TestBed.inject(AuthGuard);
    });

    it('-------------> should return FALSE if the user state is not logged in', () => {
        const expected = cold('(a|)', { a: false });
        expect(guard.canActivate()).toBeObservable(expected);
    });
});

guard.ts

import { Injectable } from '@angular/core';
import { Router, CanActivate } from '@angular/router';
import { Observable } from 'rxjs';
import { Store } from '@ngrx/store';
import { AppState } from 'src/app/app-state';
import * as fromAuth from '../../modules/auth/store/auth.reducer';

@Injectable()
export class AuthGuard implements CanActivate {
  authState: Observable<fromAuth.AuthState>;
  constructor(private store: Store<AppState>, private router: Router) { }
  canActivate(): boolean {
    // Check if user is authenticated or not i.e. only logged in users havce access to todos page
    const authState = this.store.select(fromAuth.selectAuthState);
    authState.subscribe(state => {
      if (!state.isAuthenticated) {
        this.router.navigate(['auth']);
        return false;
      }
    });

    return true;
  }
}
Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Aayushi Gupta
  • 369
  • 4
  • 18

1 Answers1

0

I think since your canActivate is returning a boolean and not an Observable<boolean>, this could be the issue.

Try changing your canActivate like so:

import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
....
canActivate(): Observable<boolean> {
    // Check if user is authenticated or not i.e. only logged in users have access to todos page
    return this.store.select(fromAuth.selectAuthState).pipe(
      map(state => {
        if (!state.isAuthenticated) {
          this.router.navigate(['auth']);
          return false;
        }
        return true;
      }),
    );
  }

Now, we will be returning an observable and then in your test expect(guard.canActivate()).toBeObservable(...) should work. Before guard.canActivate() was a boolean and not an Observable.

AliF50
  • 16,947
  • 1
  • 21
  • 37