From 4a65051641ea502b85eefc6e4c42192bfba5dab4 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Tue, 28 Apr 2020 10:06:34 +0200 Subject: [PATCH 1/3] Fixed issue with authentication when SSR is disabled --- src/app/core/auth/auth.effects.spec.ts | 74 +++++++++++++++++---- src/app/core/auth/auth.effects.ts | 15 ++--- src/app/shared/testing/auth-service.stub.ts | 4 ++ 3 files changed, 72 insertions(+), 21 deletions(-) diff --git a/src/app/core/auth/auth.effects.spec.ts b/src/app/core/auth/auth.effects.spec.ts index 5aaced609e..094284e679 100644 --- a/src/app/core/auth/auth.effects.spec.ts +++ b/src/app/core/auth/auth.effects.spec.ts @@ -1,9 +1,9 @@ -import { TestBed } from '@angular/core/testing'; +import { fakeAsync, flush, TestBed } from '@angular/core/testing'; import { provideMockActions } from '@ngrx/effects/testing'; -import { Store } from '@ngrx/store'; +import { Store, StoreModule } from '@ngrx/store'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; import { cold, hot } from 'jasmine-marbles'; - import { Observable, of as observableOf, throwError as observableThrow } from 'rxjs'; import { AuthEffects } from './auth.effects'; @@ -29,41 +29,53 @@ import { } from './auth.actions'; import { authMethodsMock, AuthServiceStub } from '../../shared/testing/auth-service.stub'; import { AuthService } from './auth.service'; -import { AuthState } from './auth.reducer'; - +import { authReducer } from './auth.reducer'; import { AuthStatus } from './models/auth-status.model'; import { EPersonMock } from '../../shared/testing/eperson.mock'; +import { AppState, storeModuleConfig } from '../../app.reducer'; +import { StoreActionTypes } from '../../store.actions'; +import { isAuthenticated, isAuthenticatedLoaded } from './selectors'; -describe('AuthEffects', () => { +fdescribe('AuthEffects', () => { let authEffects: AuthEffects; let actions: Observable; let authServiceStub; - const store: Store = jasmine.createSpyObj('store', { - /* tslint:disable:no-empty */ - dispatch: {}, - /* tslint:enable:no-empty */ - select: observableOf(true) - }); + let initialState; let token; + let store: MockStore; function init() { authServiceStub = new AuthServiceStub(); token = authServiceStub.getToken(); + initialState = { + core: { + auth: { + authenticated: false, + loaded: false, + loading: false, + authMethods: [] + } + } + }; } beforeEach(() => { init(); TestBed.configureTestingModule({ + imports: [ + StoreModule.forRoot({ auth: authReducer }, storeModuleConfig) + ], providers: [ AuthEffects, + provideMockStore({ initialState }), { provide: AuthService, useValue: authServiceStub }, - { provide: Store, useValue: store }, provideMockActions(() => actions), // other providers ], }); authEffects = TestBed.get(AuthEffects); + store = TestBed.get(Store); }); describe('authenticate$', () => { @@ -362,4 +374,40 @@ describe('AuthEffects', () => { }); }) }); + + describe('clearInvalidTokenOnRehydrate$', () => { + + beforeEach(() => { + store.overrideSelector(isAuthenticated, false); + }); + + describe('when auth loaded is false', () => { + it('should not call removeToken method', (done) => { + store.overrideSelector(isAuthenticatedLoaded, false); + actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } }); + spyOn(authServiceStub, 'removeToken'); + + authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => { + expect(authServiceStub.removeToken).not.toHaveBeenCalled(); + + }); + + done(); + }); + }); + + describe('when auth loaded is true', () => { + it('should call removeToken method', fakeAsync(() => { + store.overrideSelector(isAuthenticatedLoaded, true); + actions = hot('--a-|', { a: { type: StoreActionTypes.REHYDRATE } }); + spyOn(authServiceStub, 'removeToken'); + + authEffects.clearInvalidTokenOnRehydrate$.subscribe(() => { + expect(authServiceStub.removeToken).toHaveBeenCalled(); + flush(); + }); + + })); + }); + }); }); diff --git a/src/app/core/auth/auth.effects.ts b/src/app/core/auth/auth.effects.ts index 717aaff01e..c6d447961a 100644 --- a/src/app/core/auth/auth.effects.ts +++ b/src/app/core/auth/auth.effects.ts @@ -1,20 +1,18 @@ -import { Observable, of as observableOf } from 'rxjs'; - -import { catchError, debounceTime, filter, map, switchMap, take, tap } from 'rxjs/operators'; import { Injectable } from '@angular/core'; +import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; +import { catchError, debounceTime, filter, map, switchMap, take, tap } from 'rxjs/operators'; // import @ngrx import { Actions, Effect, ofType } from '@ngrx/effects'; import { Action, select, Store } from '@ngrx/store'; // import services import { AuthService } from './auth.service'; - import { EPerson } from '../eperson/models/eperson.model'; import { AuthStatus } from './models/auth-status.model'; import { AuthTokenInfo } from './models/auth-token-info.model'; import { AppState } from '../../app.reducer'; -import { isAuthenticated } from './selectors'; +import { isAuthenticated, isAuthenticatedLoaded } from './selectors'; import { StoreActionTypes } from '../../store.actions'; import { AuthMethod } from './models/auth.method'; // import actions @@ -187,10 +185,11 @@ export class AuthEffects { public clearInvalidTokenOnRehydrate$: Observable = this.actions$.pipe( ofType(StoreActionTypes.REHYDRATE), switchMap(() => { - return this.store.pipe( - select(isAuthenticated), + const isLoaded$ = this.store.pipe(select(isAuthenticatedLoaded)); + const authenticated$ = this.store.pipe(select(isAuthenticated)); + return observableCombineLatest(isLoaded$, authenticated$).pipe( take(1), - filter((authenticated) => !authenticated), + filter(([loaded, authenticated]) => loaded && !authenticated), tap(() => this.authService.removeToken()), tap(() => this.authService.resetAuthenticationError()) ); diff --git a/src/app/shared/testing/auth-service.stub.ts b/src/app/shared/testing/auth-service.stub.ts index 31143bc856..7e7e70a754 100644 --- a/src/app/shared/testing/auth-service.stub.ts +++ b/src/app/shared/testing/auth-service.stub.ts @@ -150,4 +150,8 @@ export class AuthServiceStub { getImpersonateID() { return this.impersonating; } + + resetAuthenticationError() { + return; + } } From b61490341e6f3a1e3bb98e49cd3921ed0153a7d9 Mon Sep 17 00:00:00 2001 From: Giuseppe Digilio Date: Fri, 22 May 2020 16:50:06 +0200 Subject: [PATCH 2/3] Added missing class in the authentication form to apply style properly --- .../log-in/methods/password/log-in-password.component.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/shared/log-in/methods/password/log-in-password.component.html b/src/app/shared/log-in/methods/password/log-in-password.component.html index ddd5083d44..16f42a1e16 100644 --- a/src/app/shared/log-in/methods/password/log-in-password.component.html +++ b/src/app/shared/log-in/methods/password/log-in-password.component.html @@ -1,4 +1,5 @@ -