From 3ec61a8d76f93f6d106002a9f7d94bee2d79ec61 Mon Sep 17 00:00:00 2001 From: Tifenn Guillas <tifenn.guillas@gmail.com> Date: Fri, 27 Aug 2021 14:36:41 +0200 Subject: [PATCH] WIP: Tests on search effect --- .../store/effects/search.effects.spec.ts | 222 ++++++++++++++++++ .../instance/store/effects/search.effects.ts | 36 ++- .../instance/store/services/search.service.ts | 2 +- 3 files changed, 254 insertions(+), 6 deletions(-) create mode 100644 client/src/app/instance/store/effects/search.effects.spec.ts diff --git a/client/src/app/instance/store/effects/search.effects.spec.ts b/client/src/app/instance/store/effects/search.effects.spec.ts new file mode 100644 index 00000000..ac09a5c9 --- /dev/null +++ b/client/src/app/instance/store/effects/search.effects.spec.ts @@ -0,0 +1,222 @@ +import { TestBed } from '@angular/core/testing'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; +import { ToastrService } from 'ngx-toastr'; + +import { SearchEffects } from './search.effects'; +import { SearchService } from '../services/search.service'; +import * as searchActions from '../actions/search.actions'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import * as fromSearch from '../reducers/search.reducer'; + +describe('SearchEffects', () => { + let actions = new Observable(); + let effects: SearchEffects; + let metadata: EffectsMetadata<SearchEffects>; + let searchService: SearchService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { search: { ...fromSearch.initialState } }; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + SearchEffects, + { provide: SearchService, useValue: { + retrieveData: jest.fn(), + retrieveDataLength: jest.fn() + }}, + { provide: ToastrService, useValue: { error: jest.fn() }}, + provideMockActions(() => actions), + provideMockStore({ initialState }), + ] + }).compileComponents(); + effects = TestBed.inject(SearchEffects); + metadata = getEffectsMetadata(effects); + searchService = TestBed.inject(SearchService); + toastr = TestBed.inject(ToastrService); + store = TestBed.inject(MockStore); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + // describe('initSearch$ effect', () => { + // it('should dispatch the registerSuccess action on success', () => { + // const action = sampActions.register(); + // const outcome = sampActions.registerSuccess(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-a|', { a: action }); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // + // it('should dispatch the registerFail action on failure', () => { + // const action = sampActions.register(); + // const error = new Error(); + // const outcome = sampActions.registerFail(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-#|', {}, error); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // }); + + // describe('restartSearch$ effect', () => { + // it('should dispatch the registerSuccess action on success', () => { + // const action = sampActions.register(); + // const outcome = sampActions.registerSuccess(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-a|', { a: action }); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // + // it('should dispatch the registerFail action on failure', () => { + // const action = sampActions.register(); + // const error = new Error(); + // const outcome = sampActions.registerFail(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-#|', {}, error); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // }); + + // describe('loadDefaultFormParameters$ effect', () => { + // it('should dispatch the registerSuccess action on success', () => { + // const action = sampActions.register(); + // const outcome = sampActions.registerSuccess(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-a|', { a: action }); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // + // it('should dispatch the registerFail action on failure', () => { + // const action = sampActions.register(); + // const error = new Error(); + // const outcome = sampActions.registerFail(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-#|', {}, error); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // }); + + // describe('retrieveDataLength$ effect', () => { + // it('should dispatch the registerSuccess action on success', () => { + // const action = sampActions.register(); + // const outcome = sampActions.registerSuccess(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-a|', { a: action }); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // + // it('should dispatch the registerFail action on failure', () => { + // const action = sampActions.register(); + // const error = new Error(); + // const outcome = sampActions.registerFail(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-#|', {}, error); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // }); + + describe('retrieveDataLengthFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.retrieveDataLengthFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = searchActions.retrieveDataLengthFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.retrieveDataLengthFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith('Loading Failed', 'The search data length loading failed'); + }); + }); + + // describe('retrieveData$ effect', () => { + // it('should dispatch the registerSuccess action on success', () => { + // const action = sampActions.register(); + // const outcome = sampActions.registerSuccess(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-a|', { a: action }); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // + // it('should dispatch the registerFail action on failure', () => { + // const action = sampActions.register(); + // const error = new Error(); + // const outcome = sampActions.registerFail(); + // + // actions = hot('-a', { a: action }); + // const response = cold('-#|', {}, error); + // const expected = cold('--b', { b: outcome }); + // sampService.register = jest.fn(() => response); + // + // expect(effects.register$).toBeObservable(expected); + // }); + // }); + + describe('retrieveDataFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.retrieveDataFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = searchActions.retrieveDataFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.retrieveDataFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith('Loading Failed', 'The search data loading failed'); + }); + }); +}); diff --git a/client/src/app/instance/store/effects/search.effects.ts b/client/src/app/instance/store/effects/search.effects.ts index b92513c6..be0879e7 100644 --- a/client/src/app/instance/store/effects/search.effects.ts +++ b/client/src/app/instance/store/effects/search.effects.ts @@ -28,9 +28,17 @@ import * as searchSelector from '../selectors/search.selector'; import * as coneSearchActions from '../actions/cone-search.actions'; import * as coneSearchSelector from '../selectors/cone-search.selector'; +/** + * @class + * @classdesc Search effects. + */ @Injectable() export class SearchEffects { - initSearch$ = createEffect(() => + + /** + * Calls actions to initialize search. + */ + initSearch$ = createEffect((): any => this.actions$.pipe( ofType(searchActions.initSearch), concatLatestFrom(() => [ @@ -79,14 +87,20 @@ export class SearchEffects { ) ); - restartSearch$ = createEffect(() => + /** + * Calls actions to restart search. + */ + restartSearch$ = createEffect((): any => this.actions$.pipe( ofType(searchActions.restartSearch), map(() => searchActions.initSearch()) ) ); - loadDefaultFormParameters$ = createEffect(() => + /** + * Calls actions to load default form parameters. + */ + loadDefaultFormParameters$ = createEffect((): any => this.actions$.pipe( ofType(searchActions.loadDefaultFormParameters), concatLatestFrom(() => [ @@ -153,7 +167,10 @@ export class SearchEffects { ) ); - retrieveDataLength$ = createEffect(() => + /** + * Calls actions to retrieve data length. + */ + retrieveDataLength$ = createEffect((): any => this.actions$.pipe( ofType(searchActions.retrieveDataLength), concatLatestFrom(() => [ @@ -179,6 +196,9 @@ export class SearchEffects { ) ); + /** + * Displays retrieve data length error notification. + */ retrieveDataLengthFail$ = createEffect(() => this.actions$.pipe( ofType(searchActions.retrieveDataLengthFail), @@ -186,7 +206,10 @@ export class SearchEffects { ), { dispatch: false} ); - retrieveData$ = createEffect(() => + /** + * Calls actions to retrieve data. + */ + retrieveData$ = createEffect((): any => this.actions$.pipe( ofType(searchActions.retrieveData), concatLatestFrom(() => [ @@ -216,6 +239,9 @@ export class SearchEffects { ) ); + /** + * Displays retrieve data error notification. + */ retrieveDataFail$ = createEffect(() => this.actions$.pipe( ofType(searchActions.retrieveDataFail), diff --git a/client/src/app/instance/store/services/search.service.ts b/client/src/app/instance/store/services/search.service.ts index 17181432..2bdfaa2f 100644 --- a/client/src/app/instance/store/services/search.service.ts +++ b/client/src/app/instance/store/services/search.service.ts @@ -14,11 +14,11 @@ import { Observable } from 'rxjs'; import { AppConfigService } from 'src/app/app-config.service'; -@Injectable() /** * @class * @classdesc Search service. */ +@Injectable() export class SearchService { constructor(private http: HttpClient, private config: AppConfigService) { } -- GitLab