From 51816c4b197a766a7a8a0021eaeb81573924276d Mon Sep 17 00:00:00 2001
From: Tifenn Guillas <tifenn.guillas@gmail.com>
Date: Thu, 5 Aug 2021 15:56:14 +0200
Subject: [PATCH] WIP: Tests on detail effects

---
 .../store/effects/cone-search.effects.spec.ts |   4 +-
 .../store/effects/detail.effects.spec.ts      | 188 ++++++++++++++++++
 .../instance/store/effects/detail.effects.ts  |  23 ++-
 .../instance/store/services/detail.service.ts |   1 +
 4 files changed, 211 insertions(+), 5 deletions(-)
 create mode 100644 client/src/app/instance/store/effects/detail.effects.spec.ts

diff --git a/client/src/app/instance/store/effects/cone-search.effects.spec.ts b/client/src/app/instance/store/effects/cone-search.effects.spec.ts
index 3befaed4..172bacf7 100644
--- a/client/src/app/instance/store/effects/cone-search.effects.spec.ts
+++ b/client/src/app/instance/store/effects/cone-search.effects.spec.ts
@@ -37,7 +37,7 @@ describe('ConeSearchEffects', () => {
         expect(effects).toBeTruthy();
     });
 
-    describe('retrieveCoordinates effect', () => {
+    describe('retrieveCoordinates$ effect', () => {
         it('should dispatch the retrieveCoordinatesSuccess action on success', () => {
             const name: string = 'myObjectName';
             const apiResponse = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
@@ -107,7 +107,7 @@ describe('ConeSearchEffects', () => {
         });
     });
 
-    describe('retrieveCoordinatesFail effect', () => {
+    describe('retrieveCoordinatesFail$ effect', () => {
         it('should not dispatch', () => {
             expect(metadata.retrieveCoordinatesFail$).toEqual(
                 expect.objectContaining({ dispatch: false })
diff --git a/client/src/app/instance/store/effects/detail.effects.spec.ts b/client/src/app/instance/store/effects/detail.effects.spec.ts
new file mode 100644
index 00000000..ec713df9
--- /dev/null
+++ b/client/src/app/instance/store/effects/detail.effects.spec.ts
@@ -0,0 +1,188 @@
+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 { DetailEffects } from './detail.effects';
+import { DetailService } from '../services/detail.service';
+import * as detailActions from '../actions/detail.actions';
+import { Resolver } from '../models';
+import { MockStore, provideMockStore } from '@ngrx/store/testing';
+import * as detailSelector from '../selectors/detail.selector';
+import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector';
+import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
+import * as fromDetail from '../reducers/detail.reducer';
+
+describe('DetailEffects', () => {
+    let actions = new Observable();
+    let effects: DetailEffects;
+    let metadata: EffectsMetadata<DetailEffects>;
+    let coneSearchService: DetailService;
+    let store: MockStore;
+    let toastr: ToastrService;
+    const initialState = { detail: { ...fromDetail.initialState } };
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            providers: [
+                DetailEffects,
+                { provide: DetailService, useValue: { retrieveCoordinates: jest.fn() }},
+                { provide: ToastrService, useValue: { error: jest.fn() } },
+                provideMockActions(() => actions),
+                provideMockStore({ initialState }),
+            ]
+        }).compileComponents();
+        effects = TestBed.inject(DetailEffects);
+        metadata = getEffectsMetadata(effects);
+        coneSearchService = TestBed.inject(DetailService);
+        store = TestBed.inject(MockStore);
+        toastr = TestBed.inject(ToastrService);
+    });
+
+    it('should be created', () => {
+        expect(effects).toBeTruthy();
+    });
+
+    describe('retrieveObject$ effect', () => {
+        // it('should dispatch the retrieveObjectSuccess action on success', () => {
+        //     const name: string = 'myObjectName';
+        //     const apiResponse = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+        //         "<Sesame xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+        //         " xsi:noNamespaceSchemaLocation=\"http://vizier.u-strasbg.fr/xml/sesame_4x.xsd\">\n" +
+        //         "<Target option=\"NSV\">\n" +
+        //         "  <name>myObjectName</name>\n" +
+        //         "  <!-- Q1525761 #1 -->\n" +
+        //         "  <Resolver name=\"N=NED\"><!--delay: 989ms [2] -->\n" +
+        //         "    <otype>!*</otype>\n" +
+        //         "    <jpos>06:45:09.24 -16:42:47.3</jpos>\n" +
+        //         "    <jradeg>1</jradeg>\n" +
+        //         "    <jdedeg>2</jdedeg>\n" +
+        //         "    <refPos>2007A&amp;A...474..653V</refPos>\n" +
+        //         "    <errRAmas>50</errRAmas><errDEmas>500</errDEmas>\n" +
+        //         "    <oname>Sirius</oname>\n" +
+        //         "  </Resolver>\n" +
+        //         "</Target>\n" +
+        //         "</Sesame>\n" +
+        //         "<!--- ====Done (2021-Aug-04,15:00:56z)==== -->\n";
+        //     const resolver: Resolver = { name: 'myObjectName', ra: 1, dec: 2 };
+        //     const action = coneSearchActions.retrieveCoordinates({ name });
+        //     const outcome = coneSearchActions.retrieveCoordinatesSuccess({ resolver });
+        //
+        //     actions = hot('-a', { a: action });
+        //     const response = cold('-a|', { a: apiResponse });
+        //     const expected = cold('--b', { b: outcome });
+        //     coneSearchService.retrieveCoordinates = jest.fn(() => response);
+        //
+        //     expect(effects.retrieveCoordinates$).toBeObservable(expected);
+        //     console.log(store.select())
+        //     expect(false).toBeTruthy();
+        // });
+
+        // it('should dispatch the retrieveObjectFail action on failure', () => {
+        //     const name: string = 'myObjectName';
+        //     const action = coneSearchActions.retrieveCoordinates({ name });
+        //     const error = new Error();
+        //     const outcome = coneSearchActions.retrieveCoordinatesFail();
+        //
+        //     actions = hot('-a', { a: action });
+        //     const response = cold('-#|', {}, error);
+        //     const expected = cold('--b', { b: outcome });
+        //     coneSearchService.retrieveCoordinates = jest.fn(() => response);
+        //
+        //     expect(effects.retrieveCoordinates$).toBeObservable(expected);
+        // });
+    });
+
+    describe('retrieveObjectFail$ effect', () => {
+        it('should not dispatch', () => {
+            expect(metadata.retrieveObjectFail$).toEqual(
+                expect.objectContaining({ dispatch: false })
+            );
+        });
+
+        it('should display an error notification', () => {
+            const spy = jest.spyOn(toastr, 'error');
+            const action = detailActions.retrieveObjectFail();
+
+            actions = hot('a', { a: action });
+            const expected = cold('a', { a: action });
+
+            expect(effects.retrieveObjectFail$).toBeObservable(expected);
+            expect(spy).toHaveBeenCalledTimes(1);
+            expect(spy).toHaveBeenCalledWith('Loading Failed!', 'Unable to load the object');
+        });
+    });
+
+    describe('retrieveSpectra$ effect', () => {
+        // it('should dispatch the retrieveSpectraSuccess action on success', () => {
+        //     const name: string = 'myObjectName';
+        //     const apiResponse = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
+        //         "<Sesame xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
+        //         " xsi:noNamespaceSchemaLocation=\"http://vizier.u-strasbg.fr/xml/sesame_4x.xsd\">\n" +
+        //         "<Target option=\"NSV\">\n" +
+        //         "  <name>myObjectName</name>\n" +
+        //         "  <!-- Q1525761 #1 -->\n" +
+        //         "  <Resolver name=\"N=NED\"><!--delay: 989ms [2] -->\n" +
+        //         "    <otype>!*</otype>\n" +
+        //         "    <jpos>06:45:09.24 -16:42:47.3</jpos>\n" +
+        //         "    <jradeg>1</jradeg>\n" +
+        //         "    <jdedeg>2</jdedeg>\n" +
+        //         "    <refPos>2007A&amp;A...474..653V</refPos>\n" +
+        //         "    <errRAmas>50</errRAmas><errDEmas>500</errDEmas>\n" +
+        //         "    <oname>Sirius</oname>\n" +
+        //         "  </Resolver>\n" +
+        //         "</Target>\n" +
+        //         "</Sesame>\n" +
+        //         "<!--- ====Done (2021-Aug-04,15:00:56z)==== -->\n";
+        //     const resolver: Resolver = { name: 'myObjectName', ra: 1, dec: 2 };
+        //     const action = coneSearchActions.retrieveCoordinates({ name });
+        //     const outcome = coneSearchActions.retrieveCoordinatesSuccess({ resolver });
+        //
+        //     actions = hot('-a', { a: action });
+        //     const response = cold('-a|', { a: apiResponse });
+        //     const expected = cold('--b', { b: outcome });
+        //     coneSearchService.retrieveCoordinates = jest.fn(() => response);
+        //
+        //     expect(effects.retrieveCoordinates$).toBeObservable(expected);
+        //     console.log(store.select())
+        //     expect(false).toBeTruthy();
+        // });
+
+        // it('should dispatch the retrieveSpectraFail action on failure', () => {
+        //     const name: string = 'myObjectName';
+        //     const action = coneSearchActions.retrieveCoordinates({ name });
+        //     const error = new Error();
+        //     const outcome = coneSearchActions.retrieveCoordinatesFail();
+        //
+        //     actions = hot('-a', { a: action });
+        //     const response = cold('-#|', {}, error);
+        //     const expected = cold('--b', { b: outcome });
+        //     coneSearchService.retrieveCoordinates = jest.fn(() => response);
+        //
+        //     expect(effects.retrieveCoordinates$).toBeObservable(expected);
+        // });
+    });
+
+    describe('retrieveSpectraFail$ effect', () => {
+        it('should not dispatch', () => {
+            expect(metadata.retrieveSpectraFail$).toEqual(
+                expect.objectContaining({ dispatch: false })
+            );
+        });
+
+        it('should display an error notification', () => {
+            const spy = jest.spyOn(toastr, 'error');
+            const action = detailActions.retrieveSpectraFail();
+
+            actions = hot('a', { a: action });
+            const expected = cold('a', { a: action });
+
+            expect(effects.retrieveSpectraFail$).toBeObservable(expected);
+            expect(spy).toHaveBeenCalledTimes(1);
+            expect(spy).toHaveBeenCalledWith('Loading Failed!', 'Unable to load spectra');
+        });
+    });
+});
diff --git a/client/src/app/instance/store/effects/detail.effects.ts b/client/src/app/instance/store/effects/detail.effects.ts
index 256412ae..2a18c7ff 100644
--- a/client/src/app/instance/store/effects/detail.effects.ts
+++ b/client/src/app/instance/store/effects/detail.effects.ts
@@ -20,10 +20,18 @@ import * as detailActions from '../actions/detail.actions';
 import * as detailSelector from '../selectors/detail.selector';
 import * as attributeSelector from 'src/app/metamodel/selectors/attribute.selector';
 import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
- 
+
+/**
+ * @class
+ * @classdesc Detail effects.
+ */
 @Injectable()
 export class DetailEffects {
-    retrieveObject$ = createEffect(() =>
+
+    /**
+     * Calls actions to retrieve object.
+     */
+    retrieveObject$ = createEffect((): any =>
         this.actions$.pipe(
             ofType(detailActions.retrieveObject),
             concatLatestFrom(() => [
@@ -43,6 +51,9 @@ export class DetailEffects {
         )
     );
 
+    /**
+     * Displays retrieve object error notification.
+     */
     retrieveObjectFail$ = createEffect(() => 
         this.actions$.pipe(
             ofType(detailActions.retrieveObjectFail),
@@ -50,7 +61,10 @@ export class DetailEffects {
         ), { dispatch: false}
     );
 
-    retrieveSpectra$ = createEffect(() =>
+    /**
+     * Calls actions to retrieve spectra.
+     */
+    retrieveSpectra$ = createEffect((): any =>
         this.actions$.pipe(
             ofType(detailActions.retrieveSpectra),
             concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)),
@@ -64,6 +78,9 @@ export class DetailEffects {
         )
     );
 
+    /**
+     * Displays retrieve spectra error notification.
+     */
     retrieveSpectraFail$ = createEffect(() => 
         this.actions$.pipe(
             ofType(detailActions.retrieveSpectraFail),
diff --git a/client/src/app/instance/store/services/detail.service.ts b/client/src/app/instance/store/services/detail.service.ts
index 3b946c5b..978c046a 100644
--- a/client/src/app/instance/store/services/detail.service.ts
+++ b/client/src/app/instance/store/services/detail.service.ts
@@ -33,6 +33,7 @@ export class DetailService {
      * @return Observable<any[]>
      */
     retrieveObject(dname: string, criterionId: number, objectSelected: string, outputList: number[]): Observable<any[]> {
+        console.log(dname, criterionId, objectSelected, outputList);
         const query = dname + '?c=' + criterionId + '::eq::' + objectSelected + '&a=' + outputList.join(';');
         return this.http.get<any[]>(this.config.apiUrl + '/search/' + query);
     }
-- 
GitLab