diff --git a/client/src/app/instance/store/effects/search.effects.spec.ts b/client/src/app/instance/store/effects/search.effects.spec.ts index a8883613bd739aa34849aaf4b9856da775ebb89c..d7485658f0c4b0120a08b025f1bfe7bfcd40494f 100644 --- a/client/src/app/instance/store/effects/search.effects.spec.ts +++ b/client/src/app/instance/store/effects/search.effects.spec.ts @@ -52,7 +52,7 @@ describe('[Instance][Store] SearchEffects', () => { }}, { provide: ToastrService, useValue: { error: jest.fn() }}, provideMockActions(() => actions), - provideMockStore({ initialState }), + provideMockStore({ initialState }) ] }).compileComponents(); effects = TestBed.inject(SearchEffects); diff --git a/client/src/app/instance/store/models/svom-keyword.model.ts b/client/src/app/instance/store/models/svom-keyword.model.ts index 1fabc699b4d1e4374f5b81f3120652615019ac65..d8ca382b519616ac5d2ce54ebec8a5867da8f6c5 100644 --- a/client/src/app/instance/store/models/svom-keyword.model.ts +++ b/client/src/app/instance/store/models/svom-keyword.model.ts @@ -1,6 +1,21 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +/** + * Interface for SVOM Keywords. + * + * @interface SvomKeyword + */ + export interface SvomKeyword { data_type: string default: string extension: string name: string -} \ No newline at end of file +} diff --git a/client/src/app/instance/store/services/svom-json-kw.service.ts b/client/src/app/instance/store/services/svom-json-kw.service.ts index 7a990aa7212a49ea99276349219e44b052d3f011..e86072e9d9d94687519559fb8ff638c5dcddc9d4 100644 --- a/client/src/app/instance/store/services/svom-json-kw.service.ts +++ b/client/src/app/instance/store/services/svom-json-kw.service.ts @@ -9,6 +9,8 @@ import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; + +import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { AppConfigService } from 'src/app/app-config.service'; @@ -22,7 +24,14 @@ import { SvomKeyword } from '../models'; export class SvomJsonKwService { constructor(private http: HttpClient, private config: AppConfigService) { } - loadKwSearchable(acronym: string) { + /** + * Retrieves searchable keywords for the given acronym. + * + * @param {string} acronym - The acronym. + * + * @return Observable<SvomKeyword[]> + */ + loadKwSearchable(acronym: string): Observable<SvomKeyword[]> { return this.http.get<{search_kw: SvomKeyword[]}[]>(`${this.config.apiUrl}/search/sp_cards?a=8&c=1::eq::${acronym}`).pipe( map(data => data[0].search_kw) ); diff --git a/client/src/app/metamodel/effects/attribute-distinct.effects.spec.ts b/client/src/app/metamodel/effects/attribute-distinct.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a1978b971248669d91b8c9fb424d467849451022 --- /dev/null +++ b/client/src/app/metamodel/effects/attribute-distinct.effects.spec.ts @@ -0,0 +1,88 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; +import { ToastrService } from 'ngx-toastr'; + +import { AttributeDistinctEffects } from './attribute-distinct.effects'; +import { AttributeDistinctService } from '../services/attribute-distinct.service'; +import * as attributeDistinctActions from '../actions/attribute-distinct.actions'; +import * as datasetSelector from '../selectors/dataset.selector'; +import { ATTRIBUTE } from '../../../test-data'; + +describe('[Metamodel][Effects] AttributeDistinctEffects', () => { + let actions = new Observable(); + let effects: AttributeDistinctEffects; + let metadata: EffectsMetadata<AttributeDistinctEffects>; + let service: AttributeDistinctService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { metamodel: { } }; + let mockDatasetSelectorSelectDatasetNameByRoute; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + AttributeDistinctEffects, + { provide: AttributeDistinctService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions), + provideMockStore({ initialState }) + ] + }).compileComponents(); + effects = TestBed.inject(AttributeDistinctEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(AttributeDistinctService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute,'' + ); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadAttributeDistinct$ effect', () => { + it('should dispatch the loadAttributeDistinctListSuccess action on success', () => { + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute, 'myDataset' + ); + + const action = attributeDistinctActions.loadAttributeDistinctList({ attribute: ATTRIBUTE }); + const outcome = attributeDistinctActions.loadAttributeDistinctListSuccess({ values: [] }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: [] }); + const expected = cold('--b', { b: outcome }); + service.retrieveAttributeDistinctList = jest.fn(() => response); + + expect(effects.loadAttributeDistinct$).toBeObservable(expected); + expect(service.retrieveAttributeDistinctList).toHaveBeenCalledWith('myDataset', ATTRIBUTE); + }); + + it('should dispatch the loadAttributeDistinctListFail action on HTTP failure', () => { + const action = attributeDistinctActions.loadAttributeDistinctList({ attribute: ATTRIBUTE }); + const error = new Error(); + const outcome = attributeDistinctActions.loadAttributeDistinctListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveAttributeDistinctList = jest.fn(() => response); + + expect(effects.loadAttributeDistinct$).toBeObservable(expected); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/attribute-distinct.effects.ts b/client/src/app/metamodel/effects/attribute-distinct.effects.ts index aaa51d7cdc83ba114efe06823526c7ae0395310c..6584c439e5ca4be3558606f0785413ab354a0860 100644 --- a/client/src/app/metamodel/effects/attribute-distinct.effects.ts +++ b/client/src/app/metamodel/effects/attribute-distinct.effects.ts @@ -17,10 +17,18 @@ import { map, mergeMap, catchError } from 'rxjs/operators'; import * as attributeDistinctActions from '../actions/attribute-distinct.actions'; import { AttributeDistinctService } from '../services/attribute-distinct.service'; import * as datasetSelector from '../selectors/dataset.selector'; - + +/** + * @class + * @classdesc Attribute distinct effects. + */ @Injectable() export class AttributeDistinctEffects { - loadAttributeDistinct$ = createEffect(() => + + /** + * Calls action to retrieve distinct attribute list. + */ + loadAttributeDistinct$ = createEffect((): any => this.actions$.pipe( ofType(attributeDistinctActions.loadAttributeDistinctList), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -32,7 +40,7 @@ export class AttributeDistinctEffects { ) ) ); - + constructor( private actions$: Actions, private attributeDistinctService: AttributeDistinctService, diff --git a/client/src/app/metamodel/effects/attribute.effects.spec.ts b/client/src/app/metamodel/effects/attribute.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..602d95d2b34db2e6ce7aaa4e75c2a53b065b8473 --- /dev/null +++ b/client/src/app/metamodel/effects/attribute.effects.spec.ts @@ -0,0 +1,289 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; + +import { ToastrService } from 'ngx-toastr'; +import { AttributeEffects } from './attribute.effects'; +import { AttributeService } from '../services/attribute.service'; +import * as attributeActions from '../actions/attribute.actions'; +import { ATTRIBUTE, ATTRIBUTE_LIST } from '../../../test-data'; +import * as datasetSelector from '../selectors/dataset.selector'; + +describe('[Metamodel][Effects] AttributeEffects', () => { + let actions = new Observable(); + let effects: AttributeEffects; + let metadata: EffectsMetadata<AttributeEffects>; + let service: AttributeService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { metamodel: { } }; + let mockDatasetSelectorSelectDatasetNameByRoute; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + AttributeEffects, + { provide: AttributeService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions), + provideMockStore({ initialState }) + ] + }).compileComponents(); + effects = TestBed.inject(AttributeEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(AttributeService); + toastr = TestBed.inject(ToastrService); + store = TestBed.inject(MockStore); + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute,'' + ); + router = TestBed.inject(Router); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadAttributes$ effect', () => { + it('should dispatch the loadAttributeListSuccess action on success', () => { + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute, 'myDataset' + ); + + const action = attributeActions.loadAttributeList(); + const outcome = attributeActions.loadAttributeListSuccess({ attributes: ATTRIBUTE_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: ATTRIBUTE_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveAttributeList = jest.fn(() => response); + + expect(effects.loadAttributes$).toBeObservable(expected); + expect(service.retrieveAttributeList).toHaveBeenCalledWith('myDataset'); + }); + + it('should dispatch the loadAttributeListFail action on HTTP failure', () => { + const action = attributeActions.loadAttributeList(); + const error = new Error(); + const outcome = attributeActions.loadAttributeListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveAttributeList = jest.fn(() => response); + + expect(effects.loadAttributes$).toBeObservable(expected); + }); + }); + + describe('addAttribute$ effect', () => { + it('should dispatch the addAttributeSuccess action on success', () => { + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute, 'myDataset' + ); + + const action = attributeActions.addAttribute({ attribute: ATTRIBUTE }); + const outcome = attributeActions.addAttributeSuccess({ attribute: ATTRIBUTE }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: ATTRIBUTE }); + const expected = cold('--b', { b: outcome }); + service.addAttribute = jest.fn(() => response); + + expect(effects.addAttribute$).toBeObservable(expected); + expect(service.addAttribute).toHaveBeenCalledWith('myDataset', ATTRIBUTE); + }); + + it('should dispatch the addAttributeFail action on HTTP failure', () => { + const action = attributeActions.addAttribute({ attribute: ATTRIBUTE }); + const error = new Error(); + const outcome = attributeActions.addAttributeFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addAttribute = jest.fn(() => response); + + expect(effects.addAttribute$).toBeObservable(expected); + }); + }); + + describe('addAttributeSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addAttributeSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = attributeActions.addAttributeSuccess({ attribute: ATTRIBUTE }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addAttributeSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Attribute successfully added', + 'The new attribute was added into the database' + ); + }); + }); + + describe('addAttributeFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addAttributeFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = attributeActions.addAttributeFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addAttributeFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add attribute', + 'The new attribute could not be added into the database' + ); + }); + }); + + describe('editAttribute$ effect', () => { + it('should dispatch the editAttributeSuccess action on success', () => { + const action = attributeActions.editAttribute({ attribute: ATTRIBUTE }); + const outcome = attributeActions.editAttributeSuccess({ attribute: ATTRIBUTE }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: ATTRIBUTE }); + const expected = cold('--b', { b: outcome }); + service.editAttribute = jest.fn(() => response); + + expect(effects.editAttribute$).toBeObservable(expected); + }); + + it('should dispatch the editAttributeFail action on HTTP failure', () => { + const action = attributeActions.editAttribute({ attribute: ATTRIBUTE }); + const error = new Error(); + const outcome = attributeActions.editAttributeFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editAttribute = jest.fn(() => response); + + expect(effects.editAttribute$).toBeObservable(expected); + }); + }); + + describe('editAttributeFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editAttributeFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = attributeActions.editAttributeFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editAttributeFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit attribute', + 'The existing attribute could not be edited into the database' + ); + }); + }); + + describe('deleteAttribute$ effect', () => { + it('should dispatch the deleteAttributeSuccess action on success', () => { + const action = attributeActions.deleteAttribute({ attribute: ATTRIBUTE }); + const outcome = attributeActions.deleteAttributeSuccess({ attribute: ATTRIBUTE }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: ATTRIBUTE }); + const expected = cold('--b', { b: outcome }); + service.deleteAttribute = jest.fn(() => response); + + expect(effects.deleteAttribute$).toBeObservable(expected); + }); + + it('should dispatch the deleteAttributeFail action on HTTP failure', () => { + const action = attributeActions.deleteAttribute({ attribute: ATTRIBUTE }); + const error = new Error(); + const outcome = attributeActions.deleteAttributeFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteAttribute = jest.fn(() => response); + + expect(effects.deleteAttribute$).toBeObservable(expected); + }); + }); + + describe('deleteAttributeSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteAttributeSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = attributeActions.deleteAttributeSuccess({ attribute: ATTRIBUTE }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteAttributeSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Attribute successfully deleted', + 'The existing attribute has been deleted' + ); + }); + }); + + describe('deleteAttributeFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteAttributeFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = attributeActions.deleteAttributeFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteAttributeFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete attribute', + 'The existing attribute could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/attribute.effects.ts b/client/src/app/metamodel/effects/attribute.effects.ts index 855fe3301ff2a861979aeea5d3b851e64673e473..c1198e7edf545cad34ef6da44b992b6a21a4e961 100644 --- a/client/src/app/metamodel/effects/attribute.effects.ts +++ b/client/src/app/metamodel/effects/attribute.effects.ts @@ -18,10 +18,19 @@ import { ToastrService } from 'ngx-toastr'; import * as attributeActions from '../actions/attribute.actions'; import { AttributeService } from '../services/attribute.service'; import * as datasetSelector from '../selectors/dataset.selector'; - + + +/** + * @class + * @classdesc Attribute effects. + */ @Injectable() export class AttributeEffects { - loadAttributes$ = createEffect(() => + + /** + * Calls action to retrieve attribute list. + */ + loadAttributes$ = createEffect((): any => this.actions$.pipe( ofType(attributeActions.loadAttributeList), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -33,8 +42,11 @@ export class AttributeEffects { ) ) ); - - addAttribute$ = createEffect(() => + + /** + * Calls action to add an attribute. + */ + addAttribute$ = createEffect((): any => this.actions$.pipe( ofType(attributeActions.addAttribute), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -47,23 +59,32 @@ export class AttributeEffects { ) ); - addAttributeSuccess$ = createEffect(() => + /** + * Displays add attribute success notification. + */ + addAttributeSuccess$ = createEffect(() => this.actions$.pipe( ofType(attributeActions.addAttributeSuccess), tap(() => { this.toastr.success('Attribute successfully added', 'The new attribute was added into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - addAttributeFail$ = createEffect(() => + /** + * Displays add attribute error notification. + */ + addAttributeFail$ = createEffect(() => this.actions$.pipe( ofType(attributeActions.addAttributeFail), tap(() => this.toastr.error('Failure to add attribute', 'The new attribute could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editAttribute$ = createEffect(() => + /** + * Calls action to modify an attribute. + */ + editAttribute$ = createEffect((): any => this.actions$.pipe( ofType(attributeActions.editAttribute), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -76,14 +97,20 @@ export class AttributeEffects { ) ); - editAttributeFail$ = createEffect(() => + /** + * Displays edit attribute error notification. + */ + editAttributeFail$ = createEffect(() => this.actions$.pipe( ofType(attributeActions.editAttributeFail), tap(() => this.toastr.error('Failure to edit attribute', 'The existing attribute could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteAttribute$ = createEffect(() => + /** + * Calls action to remove an attribute. + */ + deleteAttribute$ = createEffect((): any => this.actions$.pipe( ofType(attributeActions.deleteAttribute), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -96,20 +123,26 @@ export class AttributeEffects { ) ); - deleteAttributeSuccess$ = createEffect(() => + /** + * Displays delete attribute success notification. + */ + deleteAttributeSuccess$ = createEffect(() => this.actions$.pipe( ofType(attributeActions.deleteAttributeSuccess), tap(() => this.toastr.success('Attribute successfully deleted', 'The existing attribute has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteAttributeFail$ = createEffect(() => + /** + * Displays delete attribute error notification. + */ + deleteAttributeFail$ = createEffect(() => this.actions$.pipe( ofType(attributeActions.deleteAttributeFail), tap(() => this.toastr.error('Failure to delete attribute', 'The existing attribute could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private attributeService: AttributeService, diff --git a/client/src/app/metamodel/effects/column.effects.spec.ts b/client/src/app/metamodel/effects/column.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..64ef0d2deab1e4510daffd389974ebf68d60a954 --- /dev/null +++ b/client/src/app/metamodel/effects/column.effects.spec.ts @@ -0,0 +1,88 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; +import { ToastrService } from 'ngx-toastr'; + +import { ColumnEffects } from './column.effects'; +import { ColumnService } from '../services/column.service'; +import * as columnActions from '../actions/column.actions'; +import { COLUMN_LIST } from '../../../test-data'; +import * as datasetSelector from '../selectors/dataset.selector'; + +describe('[Metamodel][Effects] ColumnEffects', () => { + let actions = new Observable(); + let effects: ColumnEffects; + let metadata: EffectsMetadata<ColumnEffects>; + let service: ColumnService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { metamodel: { } }; + let mockDatasetSelectorSelectDatasetNameByRoute; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + ColumnEffects, + { provide: ColumnService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions), + provideMockStore({ initialState }) + ] + }).compileComponents(); + effects = TestBed.inject(ColumnEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(ColumnService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute,'' + ); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadColumns$ effect', () => { + it('should dispatch the loadColumnListSuccess action on success', () => { + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute, 'myDataset' + ); + + const action = columnActions.loadColumnList(); + const outcome = columnActions.loadColumnListSuccess({ columns: COLUMN_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: COLUMN_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveColumns = jest.fn(() => response); + + expect(effects.loadColumns$).toBeObservable(expected); + expect(service.retrieveColumns).toHaveBeenCalledWith('myDataset'); + }); + + it('should dispatch the loadColumnListFail action on HTTP failure', () => { + const action = columnActions.loadColumnList(); + const error = new Error(); + const outcome = columnActions.loadColumnListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveColumns = jest.fn(() => response); + + expect(effects.loadColumns$).toBeObservable(expected); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/column.effects.ts b/client/src/app/metamodel/effects/column.effects.ts index 53399092ae075c8302d67a01ed281cd5d35b6fd3..0e11b28f27add041602baf97520a2e0c021eb6fc 100644 --- a/client/src/app/metamodel/effects/column.effects.ts +++ b/client/src/app/metamodel/effects/column.effects.ts @@ -17,10 +17,18 @@ import { map, mergeMap, catchError } from 'rxjs/operators'; import * as columnActions from '../actions/column.actions'; import { ColumnService } from '../services/column.service'; import * as datasetSelector from '../selectors/dataset.selector'; - + +/** + * @class + * @classdesc Column effects. + */ @Injectable() export class ColumnEffects { - loadColumns$ = createEffect(() => + + /** + * Calls action to retrieve survey list. + */ + loadColumns$ = createEffect((): any => this.actions$.pipe( ofType(columnActions.loadColumnList), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -32,7 +40,7 @@ export class ColumnEffects { ) ) ); - + constructor( private actions$: Actions, private columnService: ColumnService, diff --git a/client/src/app/metamodel/effects/criteria-family.effects.spec.ts b/client/src/app/metamodel/effects/criteria-family.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..10ac482fdfe156d74aab60854e244b8b47628691 --- /dev/null +++ b/client/src/app/metamodel/effects/criteria-family.effects.spec.ts @@ -0,0 +1,312 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; + +import { ToastrService } from 'ngx-toastr'; +import { CriteriaFamilyEffects } from './criteria-family.effects'; +import { CriteriaFamilyService } from '../services/criteria-family.service'; +import * as criteriaFamilyActions from '../actions/criteria-family.actions'; +import { CRITERIA_FAMILY, CRITERIA_FAMILY_LIST } from '../../../test-data'; +import * as datasetSelector from '../selectors/dataset.selector'; + +describe('[Metamodel][Effects] CriteriaFamilyEffects', () => { + let actions = new Observable(); + let effects: CriteriaFamilyEffects; + let metadata: EffectsMetadata<CriteriaFamilyEffects>; + let service: CriteriaFamilyService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { metamodel: { } }; + let mockDatasetSelectorSelectDatasetNameByRoute; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + CriteriaFamilyEffects, + { provide: CriteriaFamilyService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions), + provideMockStore({ initialState }) + ] + }).compileComponents(); + effects = TestBed.inject(CriteriaFamilyEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(CriteriaFamilyService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute,'' + ); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadCriteriaFamilies$ effect', () => { + it('should dispatch the loadCriteriaFamilyListSuccess action on success', () => { + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute, 'myDataset' + ); + + const action = criteriaFamilyActions.loadCriteriaFamilyList(); + const outcome = criteriaFamilyActions.loadCriteriaFamilyListSuccess({ criteriaFamilies: CRITERIA_FAMILY_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: CRITERIA_FAMILY_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveCriteriaFamilyList = jest.fn(() => response); + + expect(effects.loadCriteriaFamilies$).toBeObservable(expected); + expect(service.retrieveCriteriaFamilyList).toHaveBeenCalledWith('myDataset'); + }); + + it('should dispatch the loadCriteriaFamilyListFail action on HTTP failure', () => { + const action = criteriaFamilyActions.loadCriteriaFamilyList(); + const error = new Error(); + const outcome = criteriaFamilyActions.loadCriteriaFamilyListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveCriteriaFamilyList = jest.fn(() => response); + + expect(effects.loadCriteriaFamilies$).toBeObservable(expected); + }); + }); + + describe('addCriteriaFamilies$ effect', () => { + it('should dispatch the addCriteriaFamilySuccess action on success', () => { + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute, 'myDataset' + ); + + const action = criteriaFamilyActions.addCriteriaFamily({ criteriaFamily: CRITERIA_FAMILY }); + const outcome = criteriaFamilyActions.addCriteriaFamilySuccess({ criteriaFamily: CRITERIA_FAMILY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: CRITERIA_FAMILY }); + const expected = cold('--b', { b: outcome }); + service.addCriteriaFamily = jest.fn(() => response); + + expect(effects.addCriteriaFamily$).toBeObservable(expected); + expect(service.addCriteriaFamily).toHaveBeenCalledWith('myDataset', CRITERIA_FAMILY); + }); + + it('should dispatch the addCriteriaFamilyFail action on HTTP failure', () => { + const action = criteriaFamilyActions.addCriteriaFamily({ criteriaFamily: CRITERIA_FAMILY }); + const error = new Error(); + const outcome = criteriaFamilyActions.addCriteriaFamilyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addCriteriaFamily = jest.fn(() => response); + + expect(effects.addCriteriaFamily$).toBeObservable(expected); + }); + }); + + describe('addCriteriaFamilySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addCriteriaFamilySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const spy = jest.spyOn(toastr, 'success'); + const action = criteriaFamilyActions.addCriteriaFamilySuccess({ criteriaFamily: CRITERIA_FAMILY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addCriteriaFamilySuccess$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Criteria family successfully added', + 'The new criteria family was added into the database' + ); + }); + }); + + describe('addCriteriaFamilyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addCriteriaFamilyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = criteriaFamilyActions.addCriteriaFamilyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addCriteriaFamilyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add criteria family', + 'The new criteria family could not be added into the database' + ); + }); + }); + + describe('editCriteriaFamily$ effect', () => { + it('should dispatch the editCriteriaFamilySuccess action on success', () => { + const action = criteriaFamilyActions.editCriteriaFamily({ criteriaFamily: CRITERIA_FAMILY }); + const outcome = criteriaFamilyActions.editCriteriaFamilySuccess({ criteriaFamily: CRITERIA_FAMILY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: CRITERIA_FAMILY }); + const expected = cold('--b', { b: outcome }); + service.editCriteriaFamily = jest.fn(() => response); + + expect(effects.editCriteriaFamily$).toBeObservable(expected); + }); + + it('should dispatch the editCriteriaFamilyFail action on HTTP failure', () => { + const action = criteriaFamilyActions.editCriteriaFamily({ criteriaFamily: CRITERIA_FAMILY }); + const error = new Error(); + const outcome = criteriaFamilyActions.editCriteriaFamilyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editCriteriaFamily = jest.fn(() => response); + + expect(effects.editCriteriaFamily$).toBeObservable(expected); + }); + }); + + describe('editCriteriaFamilySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editCriteriaFamilySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const spy = jest.spyOn(toastr, 'success'); + const action = criteriaFamilyActions.editCriteriaFamilySuccess({ criteriaFamily: CRITERIA_FAMILY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editCriteriaFamilySuccess$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Criteria family successfully edited', + 'The existing criteria family has been edited into the database' + ); + }); + }); + + describe('editCriteriaFamilyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editCriteriaFamilyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = criteriaFamilyActions.editCriteriaFamilyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editCriteriaFamilyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit criteria family', + 'The existing criteria family could not be edited into the database' + ); + }); + }); + + describe('deleteCriteriaFamily$ effect', () => { + it('should dispatch the deleteSurveySuccess action on success', () => { + const action = criteriaFamilyActions.deleteCriteriaFamily({ criteriaFamily: CRITERIA_FAMILY }); + const outcome = criteriaFamilyActions.deleteCriteriaFamilySuccess({ criteriaFamily: CRITERIA_FAMILY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: CRITERIA_FAMILY }); + const expected = cold('--b', { b: outcome }); + service.deleteCriteriaFamily = jest.fn(() => response); + + expect(effects.deleteCriteriaFamily$).toBeObservable(expected); + }); + + it('should dispatch the deleteCriteriaFamilyFail action on HTTP failure', () => { + const action = criteriaFamilyActions.deleteCriteriaFamily({ criteriaFamily: CRITERIA_FAMILY }); + const error = new Error(); + const outcome = criteriaFamilyActions.deleteCriteriaFamilyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteCriteriaFamily = jest.fn(() => response); + + expect(effects.deleteCriteriaFamily$).toBeObservable(expected); + }); + }); + + describe('deleteCriteriaFamilySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteCriteriaFamilySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const spy = jest.spyOn(toastr, 'success'); + const action = criteriaFamilyActions.deleteCriteriaFamilySuccess({ criteriaFamily: CRITERIA_FAMILY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteCriteriaFamilySuccess$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Criteria family successfully deleted', + 'The existing criteria family has been deleted' + ); + }); + }); + + describe('deleteCriteriaFamilyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteCriteriaFamilyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = criteriaFamilyActions.deleteCriteriaFamilyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteCriteriaFamilyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete criteria family', + 'The existing criteria family could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/criteria-family.effects.ts b/client/src/app/metamodel/effects/criteria-family.effects.ts index 95894f4cf924558bf1f2fb125c6eebece5d65237..e72d3178a3aa9d1ddac4032d69cfa1a741d96035 100644 --- a/client/src/app/metamodel/effects/criteria-family.effects.ts +++ b/client/src/app/metamodel/effects/criteria-family.effects.ts @@ -18,10 +18,18 @@ import { ToastrService } from 'ngx-toastr'; import * as criteriaFamilyActions from '../actions/criteria-family.actions'; import { CriteriaFamilyService } from '../services/criteria-family.service'; import * as datasetSelector from '../selectors/dataset.selector'; - + +/** + * @class + * @classdesc Criteria family effects. + */ @Injectable() export class CriteriaFamilyEffects { - loadCriteriaFamilys$ = createEffect(() => + + /** + * Calls action to retrieve criteria family list. + */ + loadCriteriaFamilies$ = createEffect((): any => this.actions$.pipe( ofType(criteriaFamilyActions.loadCriteriaFamilyList), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -34,7 +42,10 @@ export class CriteriaFamilyEffects { ) ); - addCriteriaFamily$ = createEffect(() => + /** + * Calls action to add a criteria family. + */ + addCriteriaFamily$ = createEffect((): any => this.actions$.pipe( ofType(criteriaFamilyActions.addCriteriaFamily), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -47,21 +58,30 @@ export class CriteriaFamilyEffects { ) ); - addCriteriaFamilySuccess$ = createEffect(() => + /** + * Displays add criteria family success notification. + */ + addCriteriaFamilySuccess$ = createEffect(() => this.actions$.pipe( ofType(criteriaFamilyActions.addCriteriaFamilySuccess), tap(() => this.toastr.success('Criteria family successfully added', 'The new criteria family was added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - addCriteriaFamilyFail$ = createEffect(() => + /** + * Displays add criteria family error notification. + */ + addCriteriaFamilyFail$ = createEffect(() => this.actions$.pipe( ofType(criteriaFamilyActions.addCriteriaFamilyFail), tap(() => this.toastr.error('Failure to add criteria family', 'The new criteria family could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editCriteriaFamily$ = createEffect(() => + /** + * Calls action to modify a criteria family. + */ + editCriteriaFamily$ = createEffect((): any => this.actions$.pipe( ofType(criteriaFamilyActions.editCriteriaFamily), mergeMap(action => this.criteriaFamilyService.editCriteriaFamily(action.criteriaFamily) @@ -73,21 +93,30 @@ export class CriteriaFamilyEffects { ) ); - editCriteriaFamilySuccess$ = createEffect(() => + /** + * Displays edit criteria family success notification. + */ + editCriteriaFamilySuccess$ = createEffect(() => this.actions$.pipe( ofType(criteriaFamilyActions.editCriteriaFamilySuccess), tap(() => this.toastr.success('Criteria family successfully edited', 'The existing criteria family has been edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editCriteriaFamilyFail$ = createEffect(() => + /** + * Displays edit criteria family error notification. + */ + editCriteriaFamilyFail$ = createEffect(() => this.actions$.pipe( ofType(criteriaFamilyActions.editCriteriaFamilyFail), tap(() => this.toastr.error('Failure to edit criteria family', 'The existing criteria family could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteCriteriaFamily$ = createEffect(() => + /** + * Calls action to remove a criteria family. + */ + deleteCriteriaFamily$ = createEffect((): any => this.actions$.pipe( ofType(criteriaFamilyActions.deleteCriteriaFamily), mergeMap(action => this.criteriaFamilyService.deleteCriteriaFamily(action.criteriaFamily.id) @@ -99,20 +128,26 @@ export class CriteriaFamilyEffects { ) ); - deleteCriteriaFamilySuccess$ = createEffect(() => + /** + * Displays delete criteria family success notification. + */ + deleteCriteriaFamilySuccess$ = createEffect(() => this.actions$.pipe( ofType(criteriaFamilyActions.deleteCriteriaFamilySuccess), tap(() => this.toastr.success('Criteria family successfully deleted', 'The existing criteria family has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteCriteriaFamilyFail$ = createEffect(() => + /** + * Displays delete criteria family error notification. + */ + deleteCriteriaFamilyFail$ = createEffect(() => this.actions$.pipe( ofType(criteriaFamilyActions.deleteCriteriaFamilyFail), tap(() => this.toastr.error('Failure to delete criteria family', 'The existing criteria family could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private criteriaFamilyService: CriteriaFamilyService, diff --git a/client/src/app/metamodel/effects/database.effects.spec.ts b/client/src/app/metamodel/effects/database.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..65326e24aa2d975960b63f61662f57a8254f0c90 --- /dev/null +++ b/client/src/app/metamodel/effects/database.effects.spec.ts @@ -0,0 +1,298 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +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 { DatabaseEffects } from './database.effects'; +import { DatabaseService } from '../services/database.service'; +import * as databaseActions from '../actions/database.actions'; +import { DATABASE, DATABASE_LIST } from '../../../test-data'; + +describe('[Metamodel][Effects] DatabaseEffects', () => { + let actions = new Observable(); + let effects: DatabaseEffects; + let metadata: EffectsMetadata<DatabaseEffects>; + let service: DatabaseService; + let toastr: ToastrService; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + DatabaseEffects, + { provide: DatabaseService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions) + ] + }).compileComponents(); + effects = TestBed.inject(DatabaseEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(DatabaseService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadDatabases$ effect', () => { + it('should dispatch the loadDatabaseListSuccess action on success', () => { + const action = databaseActions.loadDatabaseList(); + const outcome = databaseActions.loadDatabaseListSuccess({ databases: DATABASE_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATABASE_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveDatabaseList = jest.fn(() => response); + + expect(effects.loadDatabases$).toBeObservable(expected); + }); + + it('should dispatch the loadDatabaseListFail action on HTTP failure', () => { + const action = databaseActions.loadDatabaseList(); + const error = new Error(); + const outcome = databaseActions.loadDatabaseListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveDatabaseList = jest.fn(() => response); + + expect(effects.loadDatabases$).toBeObservable(expected); + }); + }); + + describe('addDatabase$ effect', () => { + it('should dispatch the addDatabaseSuccess action on success', () => { + const action = databaseActions.addDatabase({ database: DATABASE }); + const outcome = databaseActions.addDatabaseSuccess({ database: DATABASE }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATABASE }); + const expected = cold('--b', { b: outcome }); + service.addDatabase = jest.fn(() => response); + + expect(effects.addDatabase$).toBeObservable(expected); + }); + + it('should dispatch the addDatabaseFail action on HTTP failure', () => { + const action = databaseActions.addDatabase({ database: DATABASE }); + const error = new Error(); + const outcome = databaseActions.addDatabaseFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addDatabase = jest.fn(() => response); + + expect(effects.addDatabase$).toBeObservable(expected); + }); + }); + + describe('addDatabaseSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addDatabaseSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const routerSpy = jest.spyOn(router, 'navigate'); + const action = databaseActions.addDatabaseSuccess({ database: DATABASE }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addDatabaseSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Database successfully added', + 'The new database was added into the database' + ); + expect(routerSpy).toHaveBeenCalledTimes(1); + expect(routerSpy).toHaveBeenCalledWith(['/admin/database/database-list']); + }); + }); + + describe('addDatabaseFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addDatabaseFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = databaseActions.addDatabaseFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addDatabaseFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add database', + 'The new database could not be added into the database' + ); + }); + }); + + describe('editDatabase$ effect', () => { + it('should dispatch the editDatabaseSuccess action on success', () => { + const action = databaseActions.editDatabase({ database: DATABASE }); + const outcome = databaseActions.editDatabaseSuccess({ database: DATABASE }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATABASE }); + const expected = cold('--b', { b: outcome }); + service.editDatabase = jest.fn(() => response); + + expect(effects.editDatabase$).toBeObservable(expected); + }); + + it('should dispatch the editDatabaseFail action on HTTP failure', () => { + const action = databaseActions.editDatabase({ database: DATABASE }); + const error = new Error(); + const outcome = databaseActions.editDatabaseFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editDatabase = jest.fn(() => response); + + expect(effects.editDatabase$).toBeObservable(expected); + }); + }); + + describe('editDatabaseSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editDatabaseSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const routerSpy = jest.spyOn(router, 'navigate'); + const action = databaseActions.editDatabaseSuccess({ database: DATABASE }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editDatabaseSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Database successfully edited', + 'The existing database has been edited into the database' + ); + expect(routerSpy).toHaveBeenCalledTimes(1); + expect(routerSpy).toHaveBeenCalledWith(['/admin/database/database-list']); + }); + }); + + describe('editDatabaseFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editDatabaseFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = databaseActions.editDatabaseFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editDatabaseFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit database', + 'The existing database could not be edited into the database' + ); + }); + }); + + describe('deleteDatabase$ effect', () => { + it('should dispatch the deleteDatabaseSuccess action on success', () => { + const action = databaseActions.deleteDatabase({ database: DATABASE }); + const outcome = databaseActions.deleteDatabaseSuccess({ database: DATABASE }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATABASE }); + const expected = cold('--b', { b: outcome }); + service.deleteDatabase = jest.fn(() => response); + + expect(effects.deleteDatabase$).toBeObservable(expected); + }); + + it('should dispatch the deleteDatabaseFail action on HTTP failure', () => { + const action = databaseActions.deleteDatabase({ database: DATABASE }); + const error = new Error(); + const outcome = databaseActions.deleteDatabaseFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteDatabase = jest.fn(() => response); + + expect(effects.deleteDatabase$).toBeObservable(expected); + }); + }); + + describe('deleteDatabaseSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteDatabaseSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = databaseActions.deleteDatabaseSuccess({ database: DATABASE }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteDatabaseSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Database successfully deleted', + 'The existing database has been deleted' + ); + }); + }); + + describe('deleteDatabaseFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteDatabaseFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = databaseActions.deleteDatabaseFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteDatabaseFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete database', + 'The existing database could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/database.effects.ts b/client/src/app/metamodel/effects/database.effects.ts index 0d2b2add88347833596b69248b8edf08bd5737e7..7be7fa4755703a9518afc3007ff579f64c930aec 100644 --- a/client/src/app/metamodel/effects/database.effects.ts +++ b/client/src/app/metamodel/effects/database.effects.ts @@ -17,10 +17,18 @@ import { ToastrService } from 'ngx-toastr'; import * as databaseActions from '../actions/database.actions'; import { DatabaseService } from '../services/database.service'; - + +/** + * @class + * @classdesc Database effects. + */ @Injectable() export class DatabaseEffects { - loadDatabases$ = createEffect(() => + + /** + * Calls action to retrieve database list. + */ + loadDatabases$ = createEffect((): any => this.actions$.pipe( ofType(databaseActions.loadDatabaseList), mergeMap(() => this.databaseService.retrieveDatabaseList() @@ -32,7 +40,10 @@ export class DatabaseEffects { ) ); - addDatabase$ = createEffect(() => + /** + * Calls action to add a database. + */ + addDatabase$ = createEffect((): any => this.actions$.pipe( ofType(databaseActions.addDatabase), mergeMap(action => this.databaseService.addDatabase(action.database) @@ -44,24 +55,33 @@ export class DatabaseEffects { ) ); - addDatabaseSuccess$ = createEffect(() => + /** + * Displays add database success notification. + */ + addDatabaseSuccess$ = createEffect(() => this.actions$.pipe( ofType(databaseActions.addDatabaseSuccess), tap(() => { this.router.navigate(['/admin/database/database-list']); this.toastr.success('Database successfully added', 'The new database was added into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - addDatabaseFail$ = createEffect(() => + /** + * Displays add database error notification. + */ + addDatabaseFail$ = createEffect(() => this.actions$.pipe( ofType(databaseActions.addDatabaseFail), tap(() => this.toastr.error('Failure to add database', 'The new database could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editDatabase$ = createEffect(() => + /** + * Calls action to modify a database. + */ + editDatabase$ = createEffect((): any => this.actions$.pipe( ofType(databaseActions.editDatabase), mergeMap(action => this.databaseService.editDatabase(action.database) @@ -73,24 +93,33 @@ export class DatabaseEffects { ) ); - editDatabaseSuccess$ = createEffect(() => + /** + * Displays edit database success notification. + */ + editDatabaseSuccess$ = createEffect(() => this.actions$.pipe( ofType(databaseActions.editDatabaseSuccess), tap(() => { this.router.navigate(['/admin/database/database-list']); this.toastr.success('Database successfully edited', 'The existing database has been edited into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - editDatabaseFail$ = createEffect(() => + /** + * Displays edit database error notification. + */ + editDatabaseFail$ = createEffect(() => this.actions$.pipe( ofType(databaseActions.editDatabaseFail), tap(() => this.toastr.error('Failure to edit database', 'The existing database could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteDatabase$ = createEffect(() => + /** + * Calls action to remove a database. + */ + deleteDatabase$ = createEffect((): any => this.actions$.pipe( ofType(databaseActions.deleteDatabase), mergeMap(action => this.databaseService.deleteDatabase(action.database.id) @@ -102,20 +131,26 @@ export class DatabaseEffects { ) ); - deleteDatabaseSuccess$ = createEffect(() => + /** + * Displays delete database success notification. + */ + deleteDatabaseSuccess$ = createEffect(() => this.actions$.pipe( ofType(databaseActions.deleteDatabaseSuccess), tap(() => this.toastr.success('Database successfully deleted', 'The existing database has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteDatabaseFail$ = createEffect(() => + /** + * Displays delete database error notification. + */ + deleteDatabaseFail$ = createEffect(() => this.actions$.pipe( ofType(databaseActions.deleteDatabaseFail), tap(() => this.toastr.error('Failure to delete database', 'The existing database could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private databaseService: DatabaseService, diff --git a/client/src/app/metamodel/effects/dataset-family.effects.spec.ts b/client/src/app/metamodel/effects/dataset-family.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..f44343882971051ccca6d06e7512827c954feb60 --- /dev/null +++ b/client/src/app/metamodel/effects/dataset-family.effects.spec.ts @@ -0,0 +1,277 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; +import { ToastrService } from 'ngx-toastr'; + +import { DatasetFamilyEffects } from './dataset-family.effects'; +import { DatasetFamilyService } from '../services/dataset-family.service'; +import * as datasetFamilyActions from '../actions/dataset-family.actions'; +import * as instanceSelector from '../selectors/instance.selector'; +import { DATASET_FAMILY_LIST, DATASET_FAMILY } from '../../../test-data'; + +describe('[Metamodel][Effects] DatasetFamilyEffects', () => { + let actions = new Observable(); + let effects: DatasetFamilyEffects; + let metadata: EffectsMetadata<DatasetFamilyEffects>; + let service: DatasetFamilyService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { metamodel: { } }; + let mockInstanceSelectorSelectInstanceNameByRoute; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + DatasetFamilyEffects, + { provide: DatasetFamilyService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions), + provideMockStore({ initialState }) + ] + }).compileComponents(); + effects = TestBed.inject(DatasetFamilyEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(DatasetFamilyService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + mockInstanceSelectorSelectInstanceNameByRoute = store.overrideSelector( + instanceSelector.selectInstanceNameByRoute,'' + ); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadDatasetFamilies$ effect', () => { + it('should dispatch the loadDatasetFamilyListSuccess action on success', () => { + mockInstanceSelectorSelectInstanceNameByRoute = store.overrideSelector( + instanceSelector.selectInstanceNameByRoute,'myInstance' + ); + + const action = datasetFamilyActions.loadDatasetFamilyList(); + const outcome = datasetFamilyActions.loadDatasetFamilyListSuccess({ datasetFamilies: DATASET_FAMILY_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATASET_FAMILY_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveDatasetFamilyList = jest.fn(() => response); + + expect(effects.loadDatasetFamilies$).toBeObservable(expected); + expect(service.retrieveDatasetFamilyList).toHaveBeenCalledWith('myInstance'); + }); + + it('should dispatch the loadDatasetFamilyListFail action on HTTP failure', () => { + const action = datasetFamilyActions.loadDatasetFamilyList(); + const error = new Error(); + const outcome = datasetFamilyActions.loadDatasetFamilyListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveDatasetFamilyList = jest.fn(() => response); + + expect(effects.loadDatasetFamilies$).toBeObservable(expected); + }); + }); + + describe('addDatasetFamily$ effect', () => { + it('should dispatch the addDatasetFamilySuccess action on success', () => { + const action = datasetFamilyActions.addDatasetFamily({ datasetFamily: DATASET_FAMILY }); + const outcome = datasetFamilyActions.addDatasetFamilySuccess({ datasetFamily: DATASET_FAMILY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATASET_FAMILY }); + const expected = cold('--b', { b: outcome }); + service.addDatasetFamily = jest.fn(() => response); + + expect(effects.addDatasetFamily$).toBeObservable(expected); + }); + + it('should dispatch the addDatasetFamilyFail action on HTTP failure', () => { + const action = datasetFamilyActions.addDatasetFamily({ datasetFamily: DATASET_FAMILY }); + const error = new Error(); + const outcome = datasetFamilyActions.addDatasetFamilyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addDatasetFamily = jest.fn(() => response); + + expect(effects.addDatasetFamily$).toBeObservable(expected); + }); + }); + + describe('addDatasetFamilySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addDatasetFamilySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + }); + + describe('addDatasetFamilyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addDatasetFamilyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = datasetFamilyActions.addDatasetFamilyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addDatasetFamilyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add dataset family', + 'The new dataset family could not be added into the database' + ); + }); + }); + + describe('editDatasetFamily$ effect', () => { + it('should dispatch the editDatasetFamilySuccess action on success', () => { + const action = datasetFamilyActions.editDatasetFamily({ datasetFamily: DATASET_FAMILY }); + const outcome = datasetFamilyActions.editDatasetFamilySuccess({ datasetFamily: DATASET_FAMILY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATASET_FAMILY }); + const expected = cold('--b', { b: outcome }); + service.editDatasetFamily = jest.fn(() => response); + + expect(effects.editDatasetFamily$).toBeObservable(expected); + }); + + it('should dispatch the editDatasetFamilyFail action on HTTP failure', () => { + const action = datasetFamilyActions.editDatasetFamily({ datasetFamily: DATASET_FAMILY }); + const error = new Error(); + const outcome = datasetFamilyActions.editDatasetFamilyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editDatasetFamily = jest.fn(() => response); + + expect(effects.editDatasetFamily$).toBeObservable(expected); + }); + }); + + describe('editDatasetFamilySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editDatasetFamilySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + }); + + describe('editDatasetFamilyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editDatasetFamilyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = datasetFamilyActions.editDatasetFamilyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editDatasetFamilyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit dataset family', + 'The existing dataset family could not be edited into the database' + ); + }); + }); + + describe('deleteDatasetFamily$ effect', () => { + it('should dispatch the deleteDatasetFamilySuccess action on success', () => { + const action = datasetFamilyActions.deleteDatasetFamily({ datasetFamily: DATASET_FAMILY }); + const outcome = datasetFamilyActions.deleteDatasetFamilySuccess({ datasetFamily: DATASET_FAMILY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATASET_FAMILY }); + const expected = cold('--b', { b: outcome }); + service.deleteDatasetFamily = jest.fn(() => response); + + expect(effects.deleteDatasetFamily$).toBeObservable(expected); + }); + + it('should dispatch the deleteDatasetFamilyFail action on HTTP failure', () => { + const action = datasetFamilyActions.deleteDatasetFamily({ datasetFamily: DATASET_FAMILY }); + const error = new Error(); + const outcome = datasetFamilyActions.deleteDatasetFamilyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteDatasetFamily = jest.fn(() => response); + + expect(effects.deleteDatasetFamily$).toBeObservable(expected); + }); + }); + + describe('deleteDatasetFamilySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteDatasetFamilySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = datasetFamilyActions.deleteDatasetFamilySuccess({ datasetFamily: DATASET_FAMILY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteDatasetFamilySuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Dataset family successfully deleted', + 'The existing dataset family has been deleted' + ); + }); + }); + + describe('deleteDatasetFamilyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteDatasetFamilyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = datasetFamilyActions.deleteDatasetFamilyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteDatasetFamilyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete dataset family', + 'The existing dataset family could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/dataset-family.effects.ts b/client/src/app/metamodel/effects/dataset-family.effects.ts index c3aa0227097b011558ff28218103a39ccf42e5c4..c2a3925a8b491b96191dc568a3bf08093c49a27a 100644 --- a/client/src/app/metamodel/effects/dataset-family.effects.ts +++ b/client/src/app/metamodel/effects/dataset-family.effects.ts @@ -19,10 +19,19 @@ import { ToastrService } from 'ngx-toastr'; import * as datasetFamilyActions from '../actions/dataset-family.actions'; import { DatasetFamilyService } from '../services/dataset-family.service'; import * as instanceSelector from '../selectors/instance.selector'; - + + +/** + * @class + * @classdesc Dataset family effects. + */ @Injectable() export class DatasetFamilyEffects { - loadDatasetFamilys$ = createEffect(() => + + /** + * Calls action to retrieve dataset family list. + */ + loadDatasetFamilies$ = createEffect((): any => this.actions$.pipe( ofType(datasetFamilyActions.loadDatasetFamilyList), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -35,7 +44,10 @@ export class DatasetFamilyEffects { ) ); - addDatasetFamily$ = createEffect(() => + /** + * Calls action to add a dataset family. + */ + addDatasetFamily$ = createEffect((): any => this.actions$.pipe( ofType(datasetFamilyActions.addDatasetFamily), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -48,7 +60,10 @@ export class DatasetFamilyEffects { ) ); - addDatasetFamilySuccess$ = createEffect(() => + /** + * Displays add dataset family success notification. + */ + addDatasetFamilySuccess$ = createEffect(() => this.actions$.pipe( ofType(datasetFamilyActions.addDatasetFamilySuccess), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -56,17 +71,23 @@ export class DatasetFamilyEffects { this.router.navigate([`/admin/configure-instance/${instanceName}`]); this.toastr.success('Dataset family successfully added', 'The new dataset family was added into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - addDatasetFamilyFail$ = createEffect(() => + /** + * Displays add dataset family fail notification. + */ + addDatasetFamilyFail$ = createEffect(() => this.actions$.pipe( ofType(datasetFamilyActions.addDatasetFamilyFail), tap(() => this.toastr.error('Failure to add dataset family', 'The new dataset family could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editDatasetFamily$ = createEffect(() => + /** + * Calls action to modify a dataset family. + */ + editDatasetFamily$ = createEffect((): any => this.actions$.pipe( ofType(datasetFamilyActions.editDatasetFamily), mergeMap(action => this.datasetFamilyService.editDatasetFamily(action.datasetFamily) @@ -78,7 +99,10 @@ export class DatasetFamilyEffects { ) ); - editDatasetFamilySuccess$ = createEffect(() => + /** + * Displays edit dataset family success notification. + */ + editDatasetFamilySuccess$ = createEffect(() => this.actions$.pipe( ofType(datasetFamilyActions.editDatasetFamilySuccess), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -86,17 +110,23 @@ export class DatasetFamilyEffects { this.router.navigate([`/admin/configure-instance/${instanceName}`]); this.toastr.success('Dataset family successfully edited', 'The existing dataset family has been edited into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - editDatasetFamilyFail$ = createEffect(() => + /** + * Displays edit dataset family error notification. + */ + editDatasetFamilyFail$ = createEffect(() => this.actions$.pipe( ofType(datasetFamilyActions.editDatasetFamilyFail), tap(() => this.toastr.error('Failure to edit dataset family', 'The existing dataset family could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteDatasetFamily$ = createEffect(() => + /** + * Calls action to remove a dataset family. + */ + deleteDatasetFamily$ = createEffect((): any => this.actions$.pipe( ofType(datasetFamilyActions.deleteDatasetFamily), mergeMap(action => this.datasetFamilyService.deleteDatasetFamily(action.datasetFamily.id) @@ -108,20 +138,26 @@ export class DatasetFamilyEffects { ) ); - deleteDatasetFamilySuccess$ = createEffect(() => + /** + * Displays delete dataset family success notification. + */ + deleteDatasetFamilySuccess$ = createEffect(() => this.actions$.pipe( ofType(datasetFamilyActions.deleteDatasetFamilySuccess), tap(() => this.toastr.success('Dataset family successfully deleted', 'The existing dataset family has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteDatasetFamilyFail$ = createEffect(() => + /** + * Displays delete dataset family error notification. + */ + deleteDatasetFamilyFail$ = createEffect(() => this.actions$.pipe( ofType(datasetFamilyActions.deleteDatasetFamilyFail), tap(() => this.toastr.error('Failure to delete dataset family', 'The existing dataset family could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private datasetFamilyService: DatasetFamilyService, diff --git a/client/src/app/metamodel/effects/dataset.effects.spec.ts b/client/src/app/metamodel/effects/dataset.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..597a0aaedab3d382c1571dbfc2a4ee72a6b322cc --- /dev/null +++ b/client/src/app/metamodel/effects/dataset.effects.spec.ts @@ -0,0 +1,277 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; +import { ToastrService } from 'ngx-toastr'; + +import { DatasetEffects } from './dataset.effects'; +import { DatasetService } from '../services/dataset.service'; +import * as datasetActions from '../actions/dataset.actions'; +import * as instanceSelector from '../selectors/instance.selector'; +import { DATASET, DATASET_LIST } from '../../../test-data'; + +describe('[Metamodel][Effects] DatasetEffects', () => { + let actions = new Observable(); + let effects: DatasetEffects; + let metadata: EffectsMetadata<DatasetEffects>; + let service: DatasetService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { metamodel: { } }; + let mockInstanceSelectorSelectInstanceNameByRoute; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + DatasetEffects, + { provide: DatasetService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions), + provideMockStore({ initialState }) + ] + }).compileComponents(); + effects = TestBed.inject(DatasetEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(DatasetService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + mockInstanceSelectorSelectInstanceNameByRoute = store.overrideSelector( + instanceSelector.selectInstanceNameByRoute,'' + ); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadDatasets$ effect', () => { + it('should dispatch the loadDatasetListSuccess action on success', () => { + mockInstanceSelectorSelectInstanceNameByRoute = store.overrideSelector( + instanceSelector.selectInstanceNameByRoute,'myInstance' + ); + + const action = datasetActions.loadDatasetList(); + const outcome = datasetActions.loadDatasetListSuccess({ datasets: DATASET_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATASET_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveDatasetList = jest.fn(() => response); + + expect(effects.loadDatasets$).toBeObservable(expected); + expect(service.retrieveDatasetList).toHaveBeenCalledWith('myInstance'); + }); + + it('should dispatch the loadDatasetListFail action on HTTP failure', () => { + const action = datasetActions.loadDatasetList(); + const error = new Error(); + const outcome = datasetActions.loadDatasetListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveDatasetList = jest.fn(() => response); + + expect(effects.loadDatasets$).toBeObservable(expected); + }); + }); + + describe('addDataset$ effect', () => { + it('should dispatch the addDatasetSuccess action on success', () => { + const action = datasetActions.addDataset({ dataset: DATASET }); + const outcome = datasetActions.addDatasetSuccess({ dataset: DATASET }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATASET }); + const expected = cold('--b', { b: outcome }); + service.addDataset = jest.fn(() => response); + + expect(effects.addDataset$).toBeObservable(expected); + }); + + it('should dispatch the addDatasetFail action on HTTP failure', () => { + const action = datasetActions.addDataset({ dataset: DATASET }); + const error = new Error(); + const outcome = datasetActions.addDatasetFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addDataset = jest.fn(() => response); + + expect(effects.addDataset$).toBeObservable(expected); + }); + }); + + describe('addDatasetSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addDatasetSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + }); + + describe('addDatasetFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addDatasetFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = datasetActions.addDatasetFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addDatasetFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add dataset', + 'The new dataset could not be added into the database' + ); + }); + }); + + describe('editDataset$ effect', () => { + it('should dispatch the editDatasetSuccess action on success', () => { + const action = datasetActions.editDataset({ dataset: DATASET }); + const outcome = datasetActions.editDatasetSuccess({ dataset: DATASET }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATASET }); + const expected = cold('--b', { b: outcome }); + service.editDataset = jest.fn(() => response); + + expect(effects.editDataset$).toBeObservable(expected); + }); + + it('should dispatch the editDatasetFail action on HTTP failure', () => { + const action = datasetActions.editDataset({ dataset: DATASET }); + const error = new Error(); + const outcome = datasetActions.editDatasetFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editDataset = jest.fn(() => response); + + expect(effects.editDataset$).toBeObservable(expected); + }); + }); + + describe('editDatasetSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editDatasetSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + }); + + describe('editDatasetFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editDatasetFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = datasetActions.editDatasetFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editDatasetFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit dataset', + 'The existing dataset could not be edited into the database' + ); + }); + }); + + describe('deleteDataset$ effect', () => { + it('should dispatch the deleteDatasetSuccess action on success', () => { + const action = datasetActions.deleteDataset({ dataset: DATASET }); + const outcome = datasetActions.deleteDatasetSuccess({ dataset: DATASET }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: DATASET }); + const expected = cold('--b', { b: outcome }); + service.deleteDataset = jest.fn(() => response); + + expect(effects.deleteDataset$).toBeObservable(expected); + }); + + it('should dispatch the deleteDatasetFail action on HTTP failure', () => { + const action = datasetActions.deleteDataset({ dataset: DATASET }); + const error = new Error(); + const outcome = datasetActions.deleteDatasetFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteDataset = jest.fn(() => response); + + expect(effects.deleteDataset$).toBeObservable(expected); + }); + }); + + describe('deleteDatasetSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteDatasetSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = datasetActions.deleteDatasetSuccess({ dataset: DATASET }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteDatasetSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Dataset successfully deleted', + 'The existing dataset has been deleted' + ); + }); + }); + + describe('deleteDatasetFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteDatasetFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = datasetActions.deleteDatasetFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteDatasetFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete dataset', + 'The existing dataset could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/dataset.effects.ts b/client/src/app/metamodel/effects/dataset.effects.ts index cf0a0e3fee30c9f2b1179ee4993af68ed3e350c0..28894b6ffbe6c4b407f4ce2da36bc49897755b0c 100644 --- a/client/src/app/metamodel/effects/dataset.effects.ts +++ b/client/src/app/metamodel/effects/dataset.effects.ts @@ -19,10 +19,18 @@ import { ToastrService } from 'ngx-toastr'; import * as datasetActions from '../actions/dataset.actions'; import { DatasetService } from '../services/dataset.service'; import * as instanceSelector from '../selectors/instance.selector'; - + +/** + * @class + * @classdesc Dataset effects. + */ @Injectable() export class DatasetEffects { - loadDatasets$ = createEffect(() => + + /** + * Calls action to retrieve dataset list. + */ + loadDatasets$ = createEffect((): any => this.actions$.pipe( ofType(datasetActions.loadDatasetList), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -35,7 +43,10 @@ export class DatasetEffects { ) ); - addDataset$ = createEffect(() => + /** + * Calls action to add a dataset. + */ + addDataset$ = createEffect((): any => this.actions$.pipe( ofType(datasetActions.addDataset), mergeMap(action => this.datasetService.addDataset(action.dataset) @@ -47,7 +58,10 @@ export class DatasetEffects { ) ); - addDatasetSuccess$ = createEffect(() => + /** + * Displays add dataset success notification. + */ + addDatasetSuccess$ = createEffect(() => this.actions$.pipe( ofType(datasetActions.addDatasetSuccess), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -55,17 +69,23 @@ export class DatasetEffects { this.router.navigate([`/admin/instance/configure-instance/${instanceName}`]); this.toastr.success('Dataset successfully added', 'The new dataset was added into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - addDatasetFail$ = createEffect(() => + /** + * Displays add dataset error notification. + */ + addDatasetFail$ = createEffect(() => this.actions$.pipe( ofType(datasetActions.addDatasetFail), tap(() => this.toastr.error('Failure to add dataset', 'The new dataset could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editDataset$ = createEffect(() => + /** + * Calls action to add a dataset. + */ + editDataset$ = createEffect((): any => this.actions$.pipe( ofType(datasetActions.editDataset), mergeMap(action => this.datasetService.editDataset(action.dataset) @@ -77,7 +97,10 @@ export class DatasetEffects { ) ); - editDatasetSuccess$ = createEffect(() => + /** + * Displays modify dataset success notification. + */ + editDatasetSuccess$ = createEffect(() => this.actions$.pipe( ofType(datasetActions.editDatasetSuccess), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -85,17 +108,23 @@ export class DatasetEffects { this.router.navigate([`/admin/instance/configure-instance/${instanceName}`]); this.toastr.success('Dataset successfully edited', 'The existing dataset has been edited into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - editDatasetFail$ = createEffect(() => + /** + * Displays modify dataset error notification. + */ + editDatasetFail$ = createEffect(() => this.actions$.pipe( ofType(datasetActions.editDatasetFail), tap(() => this.toastr.error('Failure to edit dataset', 'The existing dataset could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteDataset$ = createEffect(() => + /** + * Calls action to add a dataset. + */ + deleteDataset$ = createEffect((): any => this.actions$.pipe( ofType(datasetActions.deleteDataset), mergeMap(action => this.datasetService.deleteDataset(action.dataset.name) @@ -107,20 +136,26 @@ export class DatasetEffects { ) ); - deleteDatasetSuccess$ = createEffect(() => + /** + * Displays remove dataset success notification. + */ + deleteDatasetSuccess$ = createEffect(() => this.actions$.pipe( ofType(datasetActions.deleteDatasetSuccess), tap(() => this.toastr.success('Dataset successfully deleted', 'The existing dataset has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteDatasetFail$ = createEffect(() => + /** + * Displays remove dataset error notification. + */ + deleteDatasetFail$ = createEffect(() => this.actions$.pipe( ofType(datasetActions.deleteDatasetFail), tap(() => this.toastr.error('Failure to delete dataset', 'The existing dataset could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private datasetService: DatasetService, diff --git a/client/src/app/metamodel/effects/group.effects.spec.ts b/client/src/app/metamodel/effects/group.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..56c9c8d56d97181fb3a9461a6aa14cd302bf5d07 --- /dev/null +++ b/client/src/app/metamodel/effects/group.effects.spec.ts @@ -0,0 +1,277 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable, of } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; +import { ToastrService } from 'ngx-toastr'; + +import { GroupEffects } from './group.effects'; +import { GroupService } from '../services/group.service'; +import * as groupActions from '../actions/group.actions'; +import * as instanceSelector from '../selectors/instance.selector'; +import { GROUP, GROUP_LIST } from '../../../test-data'; + +describe('[Metamodel][Effects] GroupEffects', () => { + let actions = new Observable(); + let effects: GroupEffects; + let metadata: EffectsMetadata<GroupEffects>; + let service: GroupService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { metamodel: { } }; + let mockInstanceSelectorSelectInstanceNameByRoute; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + GroupEffects, + { provide: GroupService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions), + provideMockStore({ initialState }) + ] + }).compileComponents(); + effects = TestBed.inject(GroupEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(GroupService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + mockInstanceSelectorSelectInstanceNameByRoute = store.overrideSelector( + instanceSelector.selectInstanceNameByRoute,'' + ); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadGroups$ effect', () => { + it('should dispatch the loadGroupListSuccess action on success', () => { + mockInstanceSelectorSelectInstanceNameByRoute = store.overrideSelector( + instanceSelector.selectInstanceNameByRoute,'myInstance' + ); + + const action = groupActions.loadGroupList(); + const outcome = groupActions.loadGroupListSuccess({ groups: GROUP_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: GROUP_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveGroupList = jest.fn(() => response); + + expect(effects.loadGroups$).toBeObservable(expected); + expect(service.retrieveGroupList).toHaveBeenCalledWith('myInstance'); + }); + + it('should dispatch the loadGroupListFail action on HTTP failure', () => { + const action = groupActions.loadGroupList(); + const error = new Error(); + const outcome = groupActions.loadGroupListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveGroupList = jest.fn(() => response); + + expect(effects.loadGroups$).toBeObservable(expected); + }); + }); + + describe('addGroup$ effect', () => { + it('should dispatch the addGroupSuccess action on success', () => { + const action = groupActions.addGroup({ group: GROUP }); + const outcome = groupActions.addGroupSuccess({ group: GROUP }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: GROUP }); + const expected = cold('--b', { b: outcome }); + service.addGroup = jest.fn(() => response); + + expect(effects.addGroup$).toBeObservable(expected); + }); + + it('should dispatch the addGroupFail action on HTTP failure', () => { + const action = groupActions.addGroup({ group: GROUP }); + const error = new Error(); + const outcome = groupActions.addGroupFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addGroup = jest.fn(() => response); + + expect(effects.addGroup$).toBeObservable(expected); + }); + }); + + describe('addGroupSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addGroupSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + }); + + describe('addGroupFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addGroupFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = groupActions.addGroupFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addGroupFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add group', + 'The new group could not be added into the database' + ); + }); + }); + + describe('editGroup$ effect', () => { + it('should dispatch the editGroupSuccess action on success', () => { + const action = groupActions.editGroup({ group: GROUP }); + const outcome = groupActions.editGroupSuccess({ group: GROUP }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: GROUP }); + const expected = cold('--b', { b: outcome }); + service.editGroup = jest.fn(() => response); + + expect(effects.editGroup$).toBeObservable(expected); + }); + + it('should dispatch the editGroupFail action on HTTP failure', () => { + const action = groupActions.editGroup({ group: GROUP }); + const error = new Error(); + const outcome = groupActions.editGroupFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editGroup = jest.fn(() => response); + + expect(effects.editGroup$).toBeObservable(expected); + }); + }); + + describe('editGroupSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editGroupSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + }); + + describe('editGroupFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editGroupFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = groupActions.editGroupFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editGroupFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit group', + 'The existing group could not be edited into the database' + ); + }); + }); + + describe('deleteGroup$ effect', () => { + it('should dispatch the deleteGroupSuccess action on success', () => { + const action = groupActions.deleteGroup({ group: GROUP }); + const outcome = groupActions.deleteGroupSuccess({ group: GROUP }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: GROUP }); + const expected = cold('--b', { b: outcome }); + service.deleteGroup = jest.fn(() => response); + + expect(effects.deleteGroup$).toBeObservable(expected); + }); + + it('should dispatch the deleteGroupFail action on HTTP failure', () => { + const action = groupActions.deleteGroup({ group: GROUP }); + const error = new Error(); + const outcome = groupActions.deleteGroupFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteGroup = jest.fn(() => response); + + expect(effects.deleteGroup$).toBeObservable(expected); + }); + }); + + describe('deleteGroupSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteGroupSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = groupActions.deleteGroupSuccess({ group: GROUP }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteGroupSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Group successfully deleted', + 'The existing group has been deleted' + ); + }); + }); + + describe('deleteGroupFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteGroupFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = groupActions.deleteGroupFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteGroupFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete group', + 'The existing group could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/group.effects.ts b/client/src/app/metamodel/effects/group.effects.ts index 6f3567afd9e0a71550dfba4713df71b5cf69a80d..357186527289ceda7293d1f23f9d7dce80bc50b4 100644 --- a/client/src/app/metamodel/effects/group.effects.ts +++ b/client/src/app/metamodel/effects/group.effects.ts @@ -13,16 +13,24 @@ import { Router } from '@angular/router'; import { Actions, createEffect, ofType, concatLatestFrom } from '@ngrx/effects'; import { Store } from '@ngrx/store'; import { of } from 'rxjs'; -import { map, tap, mergeMap, catchError } from 'rxjs/operators'; +import { map, tap, mergeMap, catchError, finalize, take } from 'rxjs/operators'; import { ToastrService } from 'ngx-toastr'; import * as groupActions from '../actions/group.actions'; import { GroupService } from '../services/group.service'; import * as instanceSelector from '../selectors/instance.selector'; - + +/** + * @class + * @classdesc Survey effects. + */ @Injectable() export class GroupEffects { - loadGroups$ = createEffect(() => + + /** + * Calls action to retrieve group list. + */ + loadGroups$ = createEffect((): any => this.actions$.pipe( ofType(groupActions.loadGroupList), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -35,7 +43,10 @@ export class GroupEffects { ) ); - addGroup$ = createEffect(() => + /** + * Calls action to add a group. + */ + addGroup$ = createEffect((): any => this.actions$.pipe( ofType(groupActions.addGroup), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -48,25 +59,35 @@ export class GroupEffects { ) ); - addGroupSuccess$ = createEffect(() => + /** + * Displays add group success notification. + */ + addGroupSuccess$ = createEffect(() => this.actions$.pipe( ofType(groupActions.addGroupSuccess), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), - tap(([, instanceName]) => { - this.router.navigateByUrl(`/admin/instance/configure-instance/${instanceName}/group`); + tap(instanceName => { + console.log(instanceName); + // this.router.navigateByUrl(`/admin/instance/configure-instance/${instanceName}/group`); this.toastr.success('Group successfully added', 'The new group was added into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - addGroupFail$ = createEffect(() => + /** + * Displays add group error notification. + */ + addGroupFail$ = createEffect(() => this.actions$.pipe( ofType(groupActions.addGroupFail), tap(() => this.toastr.error('Failure to add group', 'The new group could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editGroup$ = createEffect(() => + /** + * Calls action to modify a group. + */ + editGroup$ = createEffect((): any => this.actions$.pipe( ofType(groupActions.editGroup), mergeMap(action => this.groupService.editGroup(action.group) @@ -78,7 +99,10 @@ export class GroupEffects { ) ); - editGroupSuccess$ = createEffect(() => + /** + * Displays edit group success notification. + */ + editGroupSuccess$ = createEffect(() => this.actions$.pipe( ofType(groupActions.editGroupSuccess), concatLatestFrom(() => this.store.select(instanceSelector.selectInstanceNameByRoute)), @@ -86,17 +110,23 @@ export class GroupEffects { this.router.navigateByUrl(`/admin/instance/configure-instance/${instanceName}/group`); this.toastr.success('Group successfully edited', 'The existing group has been edited into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - editGroupFail$ = createEffect(() => + /** + * Displays edit group error notification. + */ + editGroupFail$ = createEffect(() => this.actions$.pipe( ofType(groupActions.editGroupFail), tap(() => this.toastr.error('Failure to edit group', 'The existing group could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteGroup$ = createEffect(() => + /** + * Calls action to remove a group. + */ + deleteGroup$ = createEffect((): any => this.actions$.pipe( ofType(groupActions.deleteGroup), mergeMap(action => this.groupService.deleteGroup(action.group.id) @@ -108,20 +138,26 @@ export class GroupEffects { ) ); - deleteGroupSuccess$ = createEffect(() => + /** + * Displays delete group success notification. + */ + deleteGroupSuccess$ = createEffect(() => this.actions$.pipe( ofType(groupActions.deleteGroupSuccess), tap(() => this.toastr.success('Group successfully deleted', 'The existing group has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteGroupFail$ = createEffect(() => + /** + * Displays delete group error notification. + */ + deleteGroupFail$ = createEffect(() => this.actions$.pipe( ofType(groupActions.deleteGroupFail), tap(() => this.toastr.error('Failure to delete group', 'The existing group could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private groupService: GroupService, diff --git a/client/src/app/metamodel/effects/instance.effects.spec.ts b/client/src/app/metamodel/effects/instance.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..2e30df89b7d24eb2225f7f6cdcbb317050e9587b --- /dev/null +++ b/client/src/app/metamodel/effects/instance.effects.spec.ts @@ -0,0 +1,298 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +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 { InstanceEffects } from './instance.effects'; +import { InstanceService } from '../services/instance.service'; +import * as instanceActions from '../actions/instance.actions'; +import { INSTANCE, INSTANCE_LIST } from '../../../test-data'; + +describe('[Metamodel][Effects] InstanceEffects', () => { + let actions = new Observable(); + let effects: InstanceEffects; + let metadata: EffectsMetadata<InstanceEffects>; + let service: InstanceService; + let toastr: ToastrService; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + InstanceEffects, + { provide: InstanceService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions) + ] + }).compileComponents(); + effects = TestBed.inject(InstanceEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(InstanceService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadInstances$ effect', () => { + it('should dispatch the loadInstanceListSuccess action on success', () => { + const action = instanceActions.loadInstanceList(); + const outcome = instanceActions.loadInstanceListSuccess({ instances: INSTANCE_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: INSTANCE_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveInstanceList = jest.fn(() => response); + + expect(effects.loadInstances$).toBeObservable(expected); + }); + + it('should dispatch the loadSurveyListFail action on HTTP failure', () => { + const action = instanceActions.loadInstanceList(); + const error = new Error(); + const outcome = instanceActions.loadInstanceListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveInstanceList = jest.fn(() => response); + + expect(effects.loadInstances$).toBeObservable(expected); + }); + }); + + describe('addInstance$ effect', () => { + it('should dispatch the addSurveySuccess action on success', () => { + const action = instanceActions.addInstance({ instance: INSTANCE }); + const outcome = instanceActions.addInstanceSuccess({ instance: INSTANCE }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: INSTANCE }); + const expected = cold('--b', { b: outcome }); + service.addInstance = jest.fn(() => response); + + expect(effects.addInstance$).toBeObservable(expected); + }); + + it('should dispatch the addSurveyFail action on HTTP failure', () => { + const action = instanceActions.addInstance({ instance: INSTANCE }); + const error = new Error(); + const outcome = instanceActions.addInstanceFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addInstance = jest.fn(() => response); + + expect(effects.addInstance$).toBeObservable(expected); + }); + }); + + describe('addInstanceSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addInstanceSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const routerSpy = jest.spyOn(router, 'navigate'); + const action = instanceActions.addInstanceSuccess({ instance: INSTANCE }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addInstanceSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Instance successfully added', + 'The new instance was added into the database' + ); + expect(routerSpy).toHaveBeenCalledTimes(1); + expect(routerSpy).toHaveBeenCalledWith(['/admin/instance/instance-list']); + }); + }); + + describe('addInstanceFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addInstanceFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = instanceActions.addInstanceFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addInstanceFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add instance', + 'The new instance could not be added into the database' + ); + }); + }); + + describe('editInstance$ effect', () => { + it('should dispatch the editInstanceSuccess action on success', () => { + const action = instanceActions.editInstance({ instance: INSTANCE }); + const outcome = instanceActions.editInstanceSuccess({ instance: INSTANCE }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: INSTANCE }); + const expected = cold('--b', { b: outcome }); + service.editInstance = jest.fn(() => response); + + expect(effects.editInstance$).toBeObservable(expected); + }); + + it('should dispatch the editSurveyFail action on HTTP failure', () => { + const action = instanceActions.editInstance({ instance: INSTANCE }); + const error = new Error(); + const outcome = instanceActions.editInstanceFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editInstance = jest.fn(() => response); + + expect(effects.editInstance$).toBeObservable(expected); + }); + }); + + describe('editInstanceSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editInstanceSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const routerSpy = jest.spyOn(router, 'navigate'); + const action = instanceActions.editInstanceSuccess({ instance: INSTANCE }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editInstanceSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Instance successfully edited', + 'The existing instance has been edited into the database' + ); + expect(routerSpy).toHaveBeenCalledTimes(1); + expect(routerSpy).toHaveBeenCalledWith(['/admin/instance/instance-list']); + }); + }); + + describe('editInstanceFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editInstanceFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = instanceActions.editInstanceFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editInstanceFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit instance', + 'The existing instance could not be edited into the database' + ); + }); + }); + + describe('deleteInstance$ effect', () => { + it('should dispatch the deleteInstanceSuccess action on success', () => { + const action = instanceActions.deleteInstance({ instance: INSTANCE }); + const outcome = instanceActions.deleteInstanceSuccess({ instance: INSTANCE }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: INSTANCE }); + const expected = cold('--b', { b: outcome }); + service.deleteInstance = jest.fn(() => response); + + expect(effects.deleteInstance$).toBeObservable(expected); + }); + + it('should dispatch the deleteSurveyFail action on HTTP failure', () => { + const action = instanceActions.deleteInstance({ instance: INSTANCE }); + const error = new Error(); + const outcome = instanceActions.deleteInstanceFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteInstance = jest.fn(() => response); + + expect(effects.deleteInstance$).toBeObservable(expected); + }); + }); + + describe('deleteInstanceSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteInstanceSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = instanceActions.deleteInstanceSuccess({ instance: INSTANCE }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteInstanceSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Instance successfully deleted', + 'The existing instance has been deleted' + ); + }); + }); + + describe('deleteInstanceFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteInstanceFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = instanceActions.deleteInstanceFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteInstanceFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete instance', + 'The existing instance could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/instance.effects.ts b/client/src/app/metamodel/effects/instance.effects.ts index e83fb1e3c4f9a20154ea09706061d30893876905..bbd8dcc3945e8624d47c73c8a5226fb2a7b242cd 100644 --- a/client/src/app/metamodel/effects/instance.effects.ts +++ b/client/src/app/metamodel/effects/instance.effects.ts @@ -17,10 +17,18 @@ import { ToastrService } from 'ngx-toastr'; import * as instanceActions from '../actions/instance.actions'; import { InstanceService } from '../services/instance.service'; - + +/** + * @class + * @classdesc Instance effects. + */ @Injectable() export class InstanceEffects { - loadInstances$ = createEffect(() => + + /** + * Calls action to retrieve instance list. + */ + loadInstances$ = createEffect((): any => this.actions$.pipe( ofType(instanceActions.loadInstanceList), mergeMap(() => this.instanceService.retrieveInstanceList() @@ -32,7 +40,10 @@ export class InstanceEffects { ) ); - addInstance$ = createEffect(() => + /** + * Calls action to add an instance. + */ + addInstance$ = createEffect((): any => this.actions$.pipe( ofType(instanceActions.addInstance), mergeMap(action => this.instanceService.addInstance(action.instance) @@ -44,24 +55,33 @@ export class InstanceEffects { ) ); - addInstanceSuccess$ = createEffect(() => + /** + * Displays add instance success notification. + */ + addInstanceSuccess$ = createEffect(() => this.actions$.pipe( ofType(instanceActions.addInstanceSuccess), tap(() => { this.router.navigate(['/admin/instance/instance-list']); this.toastr.success('Instance successfully added', 'The new instance was added into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - addInstanceFail$ = createEffect(() => + /** + * Displays add instance fail notification. + */ + addInstanceFail$ = createEffect(() => this.actions$.pipe( ofType(instanceActions.addInstanceFail), tap(() => this.toastr.error('Failure to add instance', 'The new instance could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editInstance$ = createEffect(() => + /** + * Calls action to modify an instance. + */ + editInstance$ = createEffect((): any => this.actions$.pipe( ofType(instanceActions.editInstance), mergeMap(action => this.instanceService.editInstance(action.instance) @@ -73,24 +93,33 @@ export class InstanceEffects { ) ); - editInstanceSuccess$ = createEffect(() => + /** + * Displays edit instance success notification. + */ + editInstanceSuccess$ = createEffect(() => this.actions$.pipe( ofType(instanceActions.editInstanceSuccess), tap(() => { this.router.navigate(['/admin/instance/instance-list']); this.toastr.success('Instance successfully edited', 'The existing instance has been edited into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - editInstanceFail$ = createEffect(() => + /** + * Displays edit instance fail notification. + */ + editInstanceFail$ = createEffect(() => this.actions$.pipe( ofType(instanceActions.editInstanceFail), tap(() => this.toastr.error('Failure to edit instance', 'The existing instance could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteInstance$ = createEffect(() => + /** + * Calls action to remove an instance. + */ + deleteInstance$ = createEffect((): any => this.actions$.pipe( ofType(instanceActions.deleteInstance), mergeMap(action => this.instanceService.deleteInstance(action.instance.name) @@ -102,20 +131,26 @@ export class InstanceEffects { ) ); - deleteInstanceSuccess$ = createEffect(() => + /** + * Displays remove instance success notification. + */ + deleteInstanceSuccess$ = createEffect(() => this.actions$.pipe( ofType(instanceActions.deleteInstanceSuccess), tap(() => this.toastr.success('Instance successfully deleted', 'The existing instance has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteInstanceFail$ = createEffect(() => + /** + * Displays remove instance fail notification. + */ + deleteInstanceFail$ = createEffect(() => this.actions$.pipe( ofType(instanceActions.deleteInstanceFail), tap(() => this.toastr.error('Failure to delete instance', 'The existing instance could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private instanceService: InstanceService, diff --git a/client/src/app/metamodel/effects/output-category.effects.spec.ts b/client/src/app/metamodel/effects/output-category.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..770166a623856441a5c7b0f17b073cdbe55c91c3 --- /dev/null +++ b/client/src/app/metamodel/effects/output-category.effects.spec.ts @@ -0,0 +1,307 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; + +import { ToastrService } from 'ngx-toastr'; +import { OutputCategoryEffects } from './output-category.effects'; +import { OutputCategoryService } from '../services/output-category.service'; +import * as outputCategoryActions from '../actions/output-category.actions'; +import * as datasetSelector from '../selectors/dataset.selector'; +import { CATEGORY, CATEGORY_LIST } from '../../../test-data'; + +describe('[Metamodel][Effects] OutputCategoryEffects', () => { + let actions = new Observable(); + let effects: OutputCategoryEffects; + let metadata: EffectsMetadata<OutputCategoryEffects>; + let service: OutputCategoryService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { metamodel: { } }; + let mockDatasetSelectorSelectDatasetNameByRoute; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + OutputCategoryEffects, + { provide: OutputCategoryService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions), + provideMockStore({ initialState }) + ] + }).compileComponents(); + effects = TestBed.inject(OutputCategoryEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(OutputCategoryService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute,'' + ); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadOutputCategories$ effect', () => { + it('should dispatch the loadOutputCategoryListSuccess action on success', () => { + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute, 'myDataset' + ); + + const action = outputCategoryActions.loadOutputCategoryList(); + const outcome = outputCategoryActions.loadOutputCategoryListSuccess({ outputCategories: CATEGORY_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: CATEGORY_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveOutputCategoryList = jest.fn(() => response); + + expect(effects.loadOutputCategories$).toBeObservable(expected); + expect(service.retrieveOutputCategoryList).toHaveBeenCalledWith('myDataset'); + }); + + it('should dispatch the loadOutputCategoryListFail action on HTTP failure', () => { + const action = outputCategoryActions.loadOutputCategoryList(); + const error = new Error(); + const outcome = outputCategoryActions.loadOutputCategoryListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveOutputCategoryList = jest.fn(() => response); + + expect(effects.loadOutputCategories$).toBeObservable(expected); + }); + }); + + describe('addOutputCategory$ effect', () => { + it('should dispatch the addOutputCategorySuccess action on success', () => { + const action = outputCategoryActions.addOutputCategory({ outputCategory: CATEGORY }); + const outcome = outputCategoryActions.addOutputCategorySuccess({ outputCategory: CATEGORY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: CATEGORY }); + const expected = cold('--b', { b: outcome }); + service.addOutputCategory = jest.fn(() => response); + + expect(effects.addOutputCategory$).toBeObservable(expected); + }); + + it('should dispatch the addOutputCategoryFail action on HTTP failure', () => { + const action = outputCategoryActions.addOutputCategory({ outputCategory: CATEGORY }); + const error = new Error(); + const outcome = outputCategoryActions.addOutputCategoryFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addOutputCategory = jest.fn(() => response); + + expect(effects.addOutputCategory$).toBeObservable(expected); + }); + }); + + describe('addOutputCategorySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addOutputCategorySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const spy = jest.spyOn(toastr, 'success'); + const action = outputCategoryActions.addOutputCategorySuccess({ outputCategory: CATEGORY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addOutputCategorySuccess$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Output category successfully added', + 'The new output category was added into the database' + ); + }); + }); + + describe('addOutputCategoryFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addOutputCategoryFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = outputCategoryActions.addOutputCategoryFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addOutputCategoryFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add output category', + 'The new output category could not be added into the database' + ); + }); + }); + + describe('editOutputCategory$ effect', () => { + it('should dispatch the editOutputCategorySuccess action on success', () => { + const action = outputCategoryActions.editOutputCategory({ outputCategory: CATEGORY }); + const outcome = outputCategoryActions.editOutputCategorySuccess({ outputCategory: CATEGORY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: CATEGORY }); + const expected = cold('--b', { b: outcome }); + service.editOutputCategory = jest.fn(() => response); + + expect(effects.editOutputCategory$).toBeObservable(expected); + }); + + it('should dispatch the editOutputCategoryFail action on HTTP failure', () => { + const action = outputCategoryActions.editOutputCategory({ outputCategory: CATEGORY }); + const error = new Error(); + const outcome = outputCategoryActions.editOutputCategoryFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editOutputCategory = jest.fn(() => response); + + expect(effects.editOutputCategory$).toBeObservable(expected); + }); + }); + + describe('editOutputCategorySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editOutputCategorySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const spy = jest.spyOn(toastr, 'success'); + const action = outputCategoryActions.editOutputCategorySuccess({ outputCategory: CATEGORY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editOutputCategorySuccess$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Output category successfully edited', + 'The existing output category has been edited into the database' + ); + }); + }); + + describe('editOutputCategoryFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editOutputCategoryFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = outputCategoryActions.editOutputCategoryFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editOutputCategoryFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit output category', + 'The existing output category could not be edited into the database' + ); + }); + }); + + describe('deleteOutputCategory$ effect', () => { + it('should dispatch the deleteOutputCategorySuccess action on success', () => { + const action = outputCategoryActions.deleteOutputCategory({ outputCategory: CATEGORY }); + const outcome = outputCategoryActions.deleteOutputCategorySuccess({ outputCategory: CATEGORY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: CATEGORY }); + const expected = cold('--b', { b: outcome }); + service.deleteOutputCategory = jest.fn(() => response); + + expect(effects.deleteOutputCategory$).toBeObservable(expected); + }); + + it('should dispatch the deleteOutputFamilyFail action on HTTP failure', () => { + const action = outputCategoryActions.deleteOutputCategory({ outputCategory: CATEGORY }); + const error = new Error(); + const outcome = outputCategoryActions.deleteOutputCategoryFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteOutputCategory = jest.fn(() => response); + + expect(effects.deleteOutputCategory$).toBeObservable(expected); + }); + }); + + describe('deleteOutputCategorySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteOutputCategorySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const spy = jest.spyOn(toastr, 'success'); + const action = outputCategoryActions.deleteOutputCategorySuccess({ outputCategory: CATEGORY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteOutputCategorySuccess$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Output category successfully deleted', + 'The existing output category has been deleted' + ); + }); + }); + + describe('deleteOutputCategoryFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteOutputCategoryFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = outputCategoryActions.deleteOutputCategoryFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteOutputCategoryFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete output category', + 'The existing output category could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/output-category.effects.ts b/client/src/app/metamodel/effects/output-category.effects.ts index a785cc0879b1958996de1cca8d3427d8b31afd4f..eb1278ad006de76d73460134f76bc7bd985daf96 100644 --- a/client/src/app/metamodel/effects/output-category.effects.ts +++ b/client/src/app/metamodel/effects/output-category.effects.ts @@ -18,10 +18,18 @@ import { ToastrService } from 'ngx-toastr'; import * as outputCategoryActions from '../actions/output-category.actions'; import { OutputCategoryService } from '../services/output-category.service'; import * as datasetSelector from '../selectors/dataset.selector'; - + +/** + * @class + * @classdesc Output category effects. + */ @Injectable() export class OutputCategoryEffects { - loadOutputCategorys$ = createEffect(() => + + /** + * Calls action to retrieve output categories list. + */ + loadOutputCategories$ = createEffect((): any => this.actions$.pipe( ofType(outputCategoryActions.loadOutputCategoryList), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -34,7 +42,10 @@ export class OutputCategoryEffects { ) ); - addOutputCategory$ = createEffect(() => + /** + * Calls action to add an output category. + */ + addOutputCategory$ = createEffect((): any => this.actions$.pipe( ofType(outputCategoryActions.addOutputCategory), mergeMap(action => this.outputCategoryService.addOutputCategory(action.outputCategory) @@ -46,21 +57,30 @@ export class OutputCategoryEffects { ) ); - addOutputCategorySuccess$ = createEffect(() => + /** + * Displays add output category success notification. + */ + addOutputCategorySuccess$ = createEffect(() => this.actions$.pipe( ofType(outputCategoryActions.addOutputCategorySuccess), tap(() => this.toastr.success('Output category successfully added', 'The new output category was added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - addOutputCategoryFail$ = createEffect(() => + /** + * Displays add output category fail notification. + */ + addOutputCategoryFail$ = createEffect(() => this.actions$.pipe( ofType(outputCategoryActions.addOutputCategoryFail), tap(() => this.toastr.error('Failure to add output category', 'The new output category could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editOutputCategory$ = createEffect(() => + /** + * Calls action to modify an output category. + */ + editOutputCategory$ = createEffect((): any => this.actions$.pipe( ofType(outputCategoryActions.editOutputCategory), mergeMap(action => this.outputCategoryService.editOutputCategory(action.outputCategory) @@ -72,21 +92,30 @@ export class OutputCategoryEffects { ) ); - editOutputCategorySuccess$ = createEffect(() => + /** + * Displays modify output category success notification. + */ + editOutputCategorySuccess$ = createEffect(() => this.actions$.pipe( ofType(outputCategoryActions.editOutputCategorySuccess), tap(() => this.toastr.success('Output category successfully edited', 'The existing output category has been edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editOutputCategoryFail$ = createEffect(() => + /** + * Displays modify output category fail notification. + */ + editOutputCategoryFail$ = createEffect(() => this.actions$.pipe( ofType(outputCategoryActions.editOutputCategoryFail), tap(() => this.toastr.error('Failure to edit output category', 'The existing output category could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteOutputCategory$ = createEffect(() => + /** + * Calls action to remove an output category. + */ + deleteOutputCategory$ = createEffect((): any => this.actions$.pipe( ofType(outputCategoryActions.deleteOutputCategory), mergeMap(action => this.outputCategoryService.deleteOutputCategory(action.outputCategory.id) @@ -98,20 +127,26 @@ export class OutputCategoryEffects { ) ); - deleteOutputCategorySuccess$ = createEffect(() => + /** + * Displays remove output category success notification. + */ + deleteOutputCategorySuccess$ = createEffect(() => this.actions$.pipe( ofType(outputCategoryActions.deleteOutputCategorySuccess), tap(() => this.toastr.success('Output category successfully deleted', 'The existing output category has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteOutputCategoryFail$ = createEffect(() => + /** + * Displays remove output category fail notification. + */ + deleteOutputCategoryFail$ = createEffect(() => this.actions$.pipe( ofType(outputCategoryActions.deleteOutputCategoryFail), tap(() => this.toastr.error('Failure to delete output category', 'The existing output category could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private outputCategoryService: OutputCategoryService, diff --git a/client/src/app/metamodel/effects/output-family.effects.spec.ts b/client/src/app/metamodel/effects/output-family.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..267056d81f2d00f9ef42926d741237c1fd140f9a --- /dev/null +++ b/client/src/app/metamodel/effects/output-family.effects.spec.ts @@ -0,0 +1,312 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +import { provideMockActions } from '@ngrx/effects/testing'; +import { MockStore, provideMockStore } from '@ngrx/store/testing'; +import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects'; +import { Observable } from 'rxjs'; +import { cold, hot } from 'jasmine-marbles'; +import { ToastrService } from 'ngx-toastr'; + +import { OutputFamilyEffects } from './output-family.effects'; +import { OutputFamilyService } from '../services/output-family.service'; +import * as outputFamilyActions from '../actions/output-family.actions'; +import { OUTPUT_FAMILY, OUTPUT_FAMILY_LIST } from '../../../test-data'; +import * as datasetSelector from '../selectors/dataset.selector'; + +describe('[Metamodel][Effects] OutputFamilyEffects', () => { + let actions = new Observable(); + let effects: OutputFamilyEffects; + let metadata: EffectsMetadata<OutputFamilyEffects>; + let service: OutputFamilyService; + let toastr: ToastrService; + let store: MockStore; + const initialState = { metamodel: { } }; + let mockDatasetSelectorSelectDatasetNameByRoute; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + OutputFamilyEffects, + { provide: OutputFamilyService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions), + provideMockStore({ initialState }) + ] + }).compileComponents(); + effects = TestBed.inject(OutputFamilyEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(OutputFamilyService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + store = TestBed.inject(MockStore); + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute,'' + ); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadOutputFamilies$ effect', () => { + it('should dispatch the loadOutputFamilyListSuccess action on success', () => { + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute, 'myDataset' + ); + + const action = outputFamilyActions.loadOutputFamilyList(); + const outcome = outputFamilyActions.loadOutputFamilyListSuccess({ outputFamilies: OUTPUT_FAMILY_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: OUTPUT_FAMILY_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveOutputFamilyList = jest.fn(() => response); + + expect(effects.loadOutputFamilies$).toBeObservable(expected); + expect(service.retrieveOutputFamilyList).toHaveBeenCalledWith('myDataset'); + }); + + it('should dispatch the loadOutputFamilyListFail action on HTTP failure', () => { + const action = outputFamilyActions.loadOutputFamilyList(); + const error = new Error(); + const outcome = outputFamilyActions.loadOutputFamilyListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveOutputFamilyList = jest.fn(() => response); + + expect(effects.loadOutputFamilies$).toBeObservable(expected); + }); + }); + + describe('addOutputFamily$ effect', () => { + it('should dispatch the addOutputFamilySuccess action on success', () => { + mockDatasetSelectorSelectDatasetNameByRoute = store.overrideSelector( + datasetSelector.selectDatasetNameByRoute, 'myDataset' + ); + + const action = outputFamilyActions.addOutputFamily({ outputFamily: OUTPUT_FAMILY }); + const outcome = outputFamilyActions.addOutputFamilySuccess({ outputFamily: OUTPUT_FAMILY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: OUTPUT_FAMILY }); + const expected = cold('--b', { b: outcome }); + service.addOutputFamily = jest.fn(() => response); + + expect(effects.addOutputFamily$).toBeObservable(expected); + expect(service.addOutputFamily).toHaveBeenCalledWith('myDataset', OUTPUT_FAMILY); + }); + + it('should dispatch the addOutputFamilyFail action on HTTP failure', () => { + const action = outputFamilyActions.addOutputFamily({ outputFamily: OUTPUT_FAMILY }); + const error = new Error(); + const outcome = outputFamilyActions.addOutputFamilyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addOutputFamily = jest.fn(() => response); + + expect(effects.addOutputFamily$).toBeObservable(expected); + }); + }); + + describe('addOutputFamilySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addOutputFamilySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const spy = jest.spyOn(toastr, 'success'); + const action = outputFamilyActions.addOutputFamilySuccess({ outputFamily: OUTPUT_FAMILY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addOutputFamilySuccess$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Output family successfully added', + 'The new output family was added into the database' + ); + }); + }); + + describe('addOutputFamilyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addOutputFamilyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = outputFamilyActions.addOutputFamilyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addOutputFamilyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add output family', + 'The new output family could not be added into the database' + ); + }); + }); + + describe('editOutputFamily$ effect', () => { + it('should dispatch the editOutputFamilySuccess action on success', () => { + const action = outputFamilyActions.editOutputFamily({ outputFamily: OUTPUT_FAMILY }); + const outcome = outputFamilyActions.editOutputFamilySuccess({ outputFamily: OUTPUT_FAMILY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: OUTPUT_FAMILY }); + const expected = cold('--b', { b: outcome }); + service.editOutputFamily = jest.fn(() => response); + + expect(effects.editOutputFamily$).toBeObservable(expected); + }); + + it('should dispatch the editOutputFamilyFail action on HTTP failure', () => { + const action = outputFamilyActions.editOutputFamily({ outputFamily: OUTPUT_FAMILY }); + const error = new Error(); + const outcome = outputFamilyActions.editOutputFamilyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editOutputFamily = jest.fn(() => response); + + expect(effects.editOutputFamily$).toBeObservable(expected); + }); + }); + + describe('editOutputFamilySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editOutputFamilySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const spy = jest.spyOn(toastr, 'success'); + const action = outputFamilyActions.editOutputFamilySuccess({ outputFamily: OUTPUT_FAMILY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editOutputFamilySuccess$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Output family successfully edited', + 'The existing output family has been edited into the database' + ); + }); + }); + + describe('editOutputFamilyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editOutputFamilyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = outputFamilyActions.editOutputFamilyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editOutputFamilyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit output family', + 'The existing output family could not be edited into the database' + ); + }); + }); + + describe('deleteOutputFamily$ effect', () => { + it('should dispatch the deleteSurveySuccess action on success', () => { + const action = outputFamilyActions.deleteOutputFamily({ outputFamily: OUTPUT_FAMILY }); + const outcome = outputFamilyActions.deleteOutputFamilySuccess({ outputFamily: OUTPUT_FAMILY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: OUTPUT_FAMILY }); + const expected = cold('--b', { b: outcome }); + service.deleteOutputFamily = jest.fn(() => response); + + expect(effects.deleteOutputFamily$).toBeObservable(expected); + }); + + it('should dispatch the deleteOutputFamilyFail action on HTTP failure', () => { + const action = outputFamilyActions.deleteOutputFamily({ outputFamily: OUTPUT_FAMILY }); + const error = new Error(); + const outcome = outputFamilyActions.deleteOutputFamilyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteOutputFamily = jest.fn(() => response); + + expect(effects.deleteOutputFamily$).toBeObservable(expected); + }); + }); + + describe('deleteOutputFamilySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteOutputFamilySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const spy = jest.spyOn(toastr, 'success'); + const action = outputFamilyActions.deleteOutputFamilySuccess({ outputFamily: OUTPUT_FAMILY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteOutputFamilySuccess$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Output family successfully deleted', + 'The existing output family has been deleted' + ); + }); + }); + + describe('deleteOutputFamilyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteOutputFamilyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = outputFamilyActions.deleteOutputFamilyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteOutputFamilyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete output family', + 'The existing output family could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/output-family.effects.ts b/client/src/app/metamodel/effects/output-family.effects.ts index fe65f5d7c92808a43c095dbbbf3fb468050d4813..dfe82c0babd7a66cc87862c7678ec14bf5e4ea03 100644 --- a/client/src/app/metamodel/effects/output-family.effects.ts +++ b/client/src/app/metamodel/effects/output-family.effects.ts @@ -18,10 +18,18 @@ import { ToastrService } from 'ngx-toastr'; import * as outputFamilyActions from '../actions/output-family.actions'; import { OutputFamilyService } from '../services/output-family.service'; import * as datasetSelector from '../selectors/dataset.selector'; - + +/** + * @class + * @classdesc Output family effects. + */ @Injectable() export class OutputFamilyEffects { - loadOutputFamilies$ = createEffect(() => + + /** + * Calls action to retrieve output family list for the given dataset. + */ + loadOutputFamilies$ = createEffect((): any => this.actions$.pipe( ofType(outputFamilyActions.loadOutputFamilyList), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -34,7 +42,10 @@ export class OutputFamilyEffects { ) ); - addOutputFamilies$ = createEffect(() => + /** + * Calls action to add an output family to the given dataset. + */ + addOutputFamily$ = createEffect((): any => this.actions$.pipe( ofType(outputFamilyActions.addOutputFamily), concatLatestFrom(() => this.store.select(datasetSelector.selectDatasetNameByRoute)), @@ -47,21 +58,30 @@ export class OutputFamilyEffects { ) ); - addOutputFamilySuccess$ = createEffect(() => + /** + * Displays add output family success notification. + */ + addOutputFamilySuccess$ = createEffect(() => this.actions$.pipe( ofType(outputFamilyActions.addOutputFamilySuccess), tap(() => this.toastr.success('Output family successfully added', 'The new output family was added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - addOutputFamilyFail$ = createEffect(() => + /** + * Displays add output family error notification. + */ + addOutputFamilyFail$ = createEffect(() => this.actions$.pipe( ofType(outputFamilyActions.addOutputFamilyFail), tap(() => this.toastr.error('Failure to add output family', 'The new output family could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editOutputFamily$ = createEffect(() => + /** + * Calls action to modify an output family. + */ + editOutputFamily$ = createEffect((): any => this.actions$.pipe( ofType(outputFamilyActions.editOutputFamily), mergeMap(action => this.outputFamilyService.editOutputFamily(action.outputFamily) @@ -73,21 +93,31 @@ export class OutputFamilyEffects { ) ); - editOutputFamilySuccess$ = createEffect(() => + /** + * Displays edit output family success notification. + */ + editOutputFamilySuccess$ = createEffect(() => this.actions$.pipe( ofType(outputFamilyActions.editOutputFamilySuccess), tap(() => this.toastr.success('Output family successfully edited', 'The existing output family has been edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editOutputFamilyFail$ = createEffect(() => + /** + * Displays edit output family error notification. + */ + editOutputFamilyFail$ = createEffect(() => this.actions$.pipe( ofType(outputFamilyActions.editOutputFamilyFail), tap(() => this.toastr.error('Failure to edit output family', 'The existing output family could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteOutputFamily$ = createEffect(() => + + /** + * Calls action to remove an output family. + */ + deleteOutputFamily$ = createEffect((): any => this.actions$.pipe( ofType(outputFamilyActions.deleteOutputFamily), mergeMap(action => this.outputFamilyService.deleteOutputFamily(action.outputFamily.id) @@ -99,20 +129,26 @@ export class OutputFamilyEffects { ) ); - deleteOutputFamilySuccess$ = createEffect(() => + /** + * Displays delete output family success notification. + */ + deleteOutputFamilySuccess$ = createEffect(() => this.actions$.pipe( ofType(outputFamilyActions.deleteOutputFamilySuccess), tap(() => this.toastr.success('Output family successfully deleted', 'The existing output family has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteOutputFamilyFail$ = createEffect(() => + /** + * Displays delete output family error notification. + */ + deleteOutputFamilyFail$ = createEffect(() => this.actions$.pipe( ofType(outputFamilyActions.deleteOutputFamilyFail), tap(() => this.toastr.error('Failure to delete output family', 'The existing output family could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private outputFamilyService: OutputFamilyService, diff --git a/client/src/app/metamodel/effects/root-directory.effects.spec.ts b/client/src/app/metamodel/effects/root-directory.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c2704c84e1b90a55c0b32d8aedfbdad65ab5230e --- /dev/null +++ b/client/src/app/metamodel/effects/root-directory.effects.spec.ts @@ -0,0 +1,62 @@ +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 { RootDirectoryEffects } from './root-directory.effects'; +import { RootDirectoryService } from '../services/root-directory.service'; +import * as rootDirectoryActions from '../actions/root-directory.actions'; +import { FILES } from '../../../test-data'; + +describe('[Metamodel][Effects] RootDirectoryEffects', () => { + let actions = new Observable(); + let effects: RootDirectoryEffects; + let metadata: EffectsMetadata<RootDirectoryEffects>; + let service: RootDirectoryService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + RootDirectoryEffects, + { provide: RootDirectoryService, useValue: { }}, + provideMockActions(() => actions) + ] + }).compileComponents(); + effects = TestBed.inject(RootDirectoryEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(RootDirectoryService); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadRootDirectory$ effect', () => { + it('should dispatch the loadRootDirectorySuccess action on success', () => { + const action = rootDirectoryActions.loadRootDirectory({ path: 'path' }); + const outcome = rootDirectoryActions.loadRootDirectorySuccess({ files: FILES }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: FILES }); + const expected = cold('--b', { b: outcome }); + service.retrieveRootDirectory = jest.fn(() => response); + + expect(effects.loadRootDirectory$).toBeObservable(expected); + }); + + it('should dispatch the loadRootDirectoryFail action on HTTP failure', () => { + const action = rootDirectoryActions.loadRootDirectory({ path: 'path' }); + const error = new Error(); + const outcome = rootDirectoryActions.loadRootDirectoryFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveRootDirectory = jest.fn(() => response); + + expect(effects.loadRootDirectory$).toBeObservable(expected); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/root-directory.effects.ts b/client/src/app/metamodel/effects/root-directory.effects.ts index 4c7ed37d6b12f83180bf65a978e8e7d01cae1ae4..f8ff1ff7f5167b651e47b1cea970ed6634808432 100644 --- a/client/src/app/metamodel/effects/root-directory.effects.ts +++ b/client/src/app/metamodel/effects/root-directory.effects.ts @@ -15,10 +15,18 @@ import { map, mergeMap, catchError } from 'rxjs/operators'; import * as rootDirectoryActions from '../actions/root-directory.actions'; import { RootDirectoryService } from '../services/root-directory.service'; - + +/** + * @class + * @classdesc Root directory effects. + */ @Injectable() export class RootDirectoryEffects { - loadRootDirectory$ = createEffect(() => + + /** + * Calls action to retrieve file list for the given root directory. + */ + loadRootDirectory$ = createEffect((): any => this.actions$.pipe( ofType(rootDirectoryActions.loadRootDirectory), mergeMap(action => this.rootDirectoryService.retrieveRootDirectory(action.path) @@ -29,7 +37,7 @@ export class RootDirectoryEffects { ) ) ); - + constructor( private actions$: Actions, private rootDirectoryService: RootDirectoryService diff --git a/client/src/app/metamodel/effects/select-option.effects.spec.ts b/client/src/app/metamodel/effects/select-option.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..33eced9420105607bab0334566560ecd2c18cb5c --- /dev/null +++ b/client/src/app/metamodel/effects/select-option.effects.spec.ts @@ -0,0 +1,288 @@ +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 { SelectOptionEffects } from './select-option.effects'; +import { SelectOptionService } from '../services/select-option.service'; +import * as selectOptionActions from '../actions/select-option.actions'; +import { SELECT_OPTION, SELECT_OPTION_LIST } from '../../../test-data'; + +describe('[Metamodel][Effects] SelectOptionEffects', () => { + let actions = new Observable(); + let effects: SelectOptionEffects; + let metadata: EffectsMetadata<SelectOptionEffects>; + let service: SelectOptionService; + let toastr: ToastrService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + SelectOptionEffects, + { provide: SelectOptionService, useValue: { }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions) + ] + }).compileComponents(); + effects = TestBed.inject(SelectOptionEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(SelectOptionService); + toastr = TestBed.inject(ToastrService); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadSelectOptions$ effect', () => { + it('should dispatch the loadSelectOptionListSuccess action on success', () => { + const action = selectOptionActions.loadSelectOptionList(); + const outcome = selectOptionActions.loadSelectOptionListSuccess({ selectOptions: SELECT_OPTION_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SELECT_OPTION_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveSelectOptionList = jest.fn(() => response); + + expect(effects.loadSelectOptions$).toBeObservable(expected); + }); + + it('should dispatch the loadSelectOptionListFail action on HTTP failure', () => { + const action = selectOptionActions.loadSelectOptionList(); + const error = new Error(); + const outcome = selectOptionActions.loadSelectOptionListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveSelectOptionList = jest.fn(() => response); + + expect(effects.loadSelectOptions$).toBeObservable(expected); + }); + }); + + describe('addSelectOption$ effect', () => { + it('should dispatch the addSelectOptionSuccess action on success', () => { + const action = selectOptionActions.addSelectOption({ selectOption: SELECT_OPTION }); + const outcome = selectOptionActions.addSelectOptionSuccess({ selectOption: SELECT_OPTION}); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SELECT_OPTION }); + const expected = cold('--b', { b: outcome }); + service.addSelectOption = jest.fn(() => response); + + expect(effects.addSelectOption$).toBeObservable(expected); + }); + + it('should dispatch the addSelectOptionFail action on HTTP failure', () => { + const action = selectOptionActions.addSelectOption({ selectOption: SELECT_OPTION }); + const error = new Error(); + const outcome = selectOptionActions.addSelectOptionFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addSelectOption = jest.fn(() => response); + + expect(effects.addSelectOption$).toBeObservable(expected); + }); + }); + + describe('addSelectOptionSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addSelectOptionSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = selectOptionActions.addSelectOptionSuccess({ selectOption: SELECT_OPTION }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addSelectOptionSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Select option successfully added', + 'The new select option was added into the database' + ); + }); + }); + + describe('addSelectOptionFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addSelectOptionFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = selectOptionActions.addSelectOptionFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addSelectOptionFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add select option', + 'The new select option could not be added into the database' + ); + }); + }); + + describe('editSelectOption$ effect', () => { + it('should dispatch the editSelectOptionSuccess action on success', () => { + const action = selectOptionActions.editSelectOption({ selectOption: SELECT_OPTION }); + const outcome = selectOptionActions.editSelectOptionSuccess({ selectOption: SELECT_OPTION}); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SELECT_OPTION }); + const expected = cold('--b', { b: outcome }); + service.editSelectOption = jest.fn(() => response); + + expect(effects.editSelectOption$).toBeObservable(expected); + }); + + it('should dispatch the editSelectOptionFail action on HTTP failure', () => { + const action = selectOptionActions.editSelectOption({ selectOption: SELECT_OPTION }); + const error = new Error(); + const outcome = selectOptionActions.editSelectOptionFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editSelectOption = jest.fn(() => response); + + expect(effects.editSelectOption$).toBeObservable(expected); + }); + }); + + describe('editSelectOptionSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editSelectOptionSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = selectOptionActions.editSelectOptionSuccess({ selectOption: SELECT_OPTION }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editSelectOptionSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Select option successfully edited', + 'The existing select option has been edited into the database' + ); + }); + }); + + describe('editSelectOptionFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editSelectOptionFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = selectOptionActions.editSelectOptionFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editSelectOptionFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit select option', + 'The existing select option could not be edited into the database' + ); + }); + }); + + describe('deleteSelectOption$ effect', () => { + it('should dispatch the deleteSelectOptionSuccess action on success', () => { + const action = selectOptionActions.deleteSelectOption({ selectOption: SELECT_OPTION }); + const outcome = selectOptionActions.deleteSelectOptionSuccess({ selectOption: SELECT_OPTION}); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SELECT_OPTION }); + const expected = cold('--b', { b: outcome }); + service.deleteSelectOption = jest.fn(() => response); + + expect(effects.deleteSelectOption$).toBeObservable(expected); + }); + + it('should dispatch the deleteSelectOptionFail action on HTTP failure', () => { + const action = selectOptionActions.deleteSelectOption({ selectOption: SELECT_OPTION }); + const error = new Error(); + const outcome = selectOptionActions.deleteSelectOptionFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteSelectOption = jest.fn(() => response); + + expect(effects.deleteSelectOption$).toBeObservable(expected); + }); + }); + + describe('deleteSelectOptionSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteSelectOptionSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = selectOptionActions.deleteSelectOptionSuccess({ selectOption: SELECT_OPTION }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteSelectOptionSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Select option successfully deleted', + 'The existing select option has been deleted' + ); + }); + }); + + describe('deleteSelectOptionFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteSelectOptionFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = selectOptionActions.deleteSelectOptionFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteSelectOptionFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete select option', + 'The existing select option could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/select-option.effects.ts b/client/src/app/metamodel/effects/select-option.effects.ts index eb98f8546f0da591b95a8f7827ba69492926a885..2beaf5ae6a83a22aa6e589f4f79a1081c6c3ae7e 100644 --- a/client/src/app/metamodel/effects/select-option.effects.ts +++ b/client/src/app/metamodel/effects/select-option.effects.ts @@ -16,11 +16,18 @@ import { ToastrService } from 'ngx-toastr'; import * as selectOptionActions from '../actions/select-option.actions'; import { SelectOptionService } from '../services/select-option.service'; - + +/** + * @class + * @classdesc Select option effects. + */ @Injectable() export class SelectOptionEffects { - - loadSelectOptions$ = createEffect(() => + + /** + * Calls action to retrieve select option list. + */ + loadSelectOptions$ = createEffect((): any => this.actions$.pipe( ofType(selectOptionActions.loadSelectOptionList), mergeMap(() => this.selectOptionService.retrieveSelectOptionList() @@ -31,8 +38,11 @@ export class SelectOptionEffects { ) ) ); - - addSelectOption$ = createEffect(() => + + /** + * Calls action to add a select option. + */ + addSelectOption$ = createEffect((): any => this.actions$.pipe( ofType(selectOptionActions.addSelectOption), mergeMap(action => this.selectOptionService.addSelectOption(action.selectOption) @@ -44,21 +54,30 @@ export class SelectOptionEffects { ) ); - addSelectOptionSuccess$ = createEffect(() => + /** + * Displays add select option success notification. + */ + addSelectOptionSuccess$ = createEffect(() => this.actions$.pipe( ofType(selectOptionActions.addSelectOptionSuccess), tap(() => this.toastr.success('Select option successfully added', 'The new select option was added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - addSelectOptionFail$ = createEffect(() => + /** + * Displays add select option error notification. + */ + addSelectOptionFail$ = createEffect(() => this.actions$.pipe( ofType(selectOptionActions.addSelectOptionFail), tap(() => this.toastr.error('Failure to add select option', 'The new select option could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editSelectOption$ = createEffect(() => + /** + * Calls action to modify a select option. + */ + editSelectOption$ = createEffect((): any => this.actions$.pipe( ofType(selectOptionActions.editSelectOption), mergeMap(action => this.selectOptionService.editSelectOption(action.selectOption) @@ -70,21 +89,30 @@ export class SelectOptionEffects { ) ); - editSelectOptionSuccess$ = createEffect(() => + /** + * Displays edit select option success notification. + */ + editSelectOptionSuccess$ = createEffect(() => this.actions$.pipe( ofType(selectOptionActions.editSelectOptionSuccess), tap(() => this.toastr.success('Select option successfully edited', 'The existing select option has been edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editSelectOptionFail$ = createEffect(() => + /** + * Displays edit select option error notification. + */ + editSelectOptionFail$ = createEffect(() => this.actions$.pipe( ofType(selectOptionActions.editSelectOptionFail), tap(() => this.toastr.error('Failure to edit select option', 'The existing select option could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteSelectOption$ = createEffect(() => + /** + * Calls action to remove a select option. + */ + deleteSelectOption$ = createEffect(():any => this.actions$.pipe( ofType(selectOptionActions.deleteSelectOption), mergeMap(action => this.selectOptionService.deleteSelectOption(action.selectOption.id) @@ -96,18 +124,24 @@ export class SelectOptionEffects { ) ); - deleteSelectOptionSuccess$ = createEffect(() => + /** + * Displays delete select option success notification. + */ + deleteSelectOptionSuccess$ = createEffect(() => this.actions$.pipe( ofType(selectOptionActions.deleteSelectOptionSuccess), tap(() => this.toastr.success('Select option successfully deleted', 'The existing select option has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteSelectOptionFail$ = createEffect(() => + /** + * Displays delete select option error notification. + */ + deleteSelectOptionFail$ = createEffect(() => this.actions$.pipe( ofType(selectOptionActions.deleteSelectOptionFail), tap(() => this.toastr.error('Failure to delete select option', 'The existing select option could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); constructor( diff --git a/client/src/app/metamodel/effects/select.effects.spec.ts b/client/src/app/metamodel/effects/select.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9ccc7d77fadc12a0bd57c97708da484eba6a4cca --- /dev/null +++ b/client/src/app/metamodel/effects/select.effects.spec.ts @@ -0,0 +1,288 @@ +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 { SelectEffects } from './select.effects'; +import { SelectService } from '../services/select.service'; +import * as selectActions from '../actions/select.actions'; +import { SELECT, SELECT_LIST } from '../../../test-data'; + +describe('[Metamodel][Effects] SelectEffects', () => { + let actions = new Observable(); + let effects: SelectEffects; + let metadata: EffectsMetadata<SelectEffects>; + let service: SelectService; + let toastr: ToastrService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + SelectEffects, + { provide: SelectService, useValue: { }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions) + ] + }).compileComponents(); + effects = TestBed.inject(SelectEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(SelectService); + toastr = TestBed.inject(ToastrService); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadSelects$ effect', () => { + it('should dispatch the loadSelectListSuccess action on success', () => { + const action = selectActions.loadSelectList(); + const outcome = selectActions.loadSelectListSuccess({ selects: SELECT_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SELECT_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveSelectList = jest.fn(() => response); + + expect(effects.loadSelects$).toBeObservable(expected); + }); + + it('should dispatch the loadSelectListFail action on HTTP failure', () => { + const action = selectActions.loadSelectList(); + const error = new Error(); + const outcome = selectActions.loadSelectListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveSelectList = jest.fn(() => response); + + expect(effects.loadSelects$).toBeObservable(expected); + }); + }); + + describe('addSelect$ effect', () => { + it('should dispatch the addSelectSuccess action on success', () => { + const action = selectActions.addSelect({ select: SELECT }); + const outcome = selectActions.addSelectSuccess({ select: SELECT}); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SELECT }); + const expected = cold('--b', { b: outcome }); + service.addSelect = jest.fn(() => response); + + expect(effects.addSelect$).toBeObservable(expected); + }); + + it('should dispatch the addSelectFail action on HTTP failure', () => { + const action = selectActions.addSelect({ select: SELECT }); + const error = new Error(); + const outcome = selectActions.addSelectFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addSelect = jest.fn(() => response); + + expect(effects.addSelect$).toBeObservable(expected); + }); + }); + + describe('addSelectSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addSelectSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = selectActions.addSelectSuccess({ select: SELECT }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addSelectSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Select successfully added', + 'The new select was added into the database' + ); + }); + }); + + describe('addSelectFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addSelectFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = selectActions.addSelectFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addSelectFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add select', + 'The new select could not be added into the database' + ); + }); + }); + + describe('editSelect$ effect', () => { + it('should dispatch the editSelectSuccess action on success', () => { + const action = selectActions.editSelect({ select: SELECT }); + const outcome = selectActions.editSelectSuccess({ select: SELECT}); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SELECT }); + const expected = cold('--b', { b: outcome }); + service.editSelect = jest.fn(() => response); + + expect(effects.editSelect$).toBeObservable(expected); + }); + + it('should dispatch the editSelectFail action on HTTP failure', () => { + const action = selectActions.editSelect({ select: SELECT }); + const error = new Error(); + const outcome = selectActions.editSelectFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editSelect = jest.fn(() => response); + + expect(effects.editSelect$).toBeObservable(expected); + }); + }); + + describe('editSelectSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editSelectSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = selectActions.editSelectSuccess({ select: SELECT }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editSelectSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Select successfully edited', + 'The existing select has been edited into the database' + ); + }); + }); + + describe('editSelectFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editSelectFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = selectActions.editSelectFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editSelectFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit select', + 'The existing select could not be edited into the database' + ); + }); + }); + + describe('deleteSelect$ effect', () => { + it('should dispatch the deleteSelectSuccess action on success', () => { + const action = selectActions.deleteSelect({ select: SELECT }); + const outcome = selectActions.deleteSelectSuccess({ select: SELECT}); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SELECT }); + const expected = cold('--b', { b: outcome }); + service.deleteSelect = jest.fn(() => response); + + expect(effects.deleteSelect$).toBeObservable(expected); + }); + + it('should dispatch the deleteSelectFail action on HTTP failure', () => { + const action = selectActions.deleteSelect({ select: SELECT }); + const error = new Error(); + const outcome = selectActions.deleteSelectFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteSelect = jest.fn(() => response); + + expect(effects.deleteSelect$).toBeObservable(expected); + }); + }); + + describe('deleteSelectSuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteSelectSuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = selectActions.deleteSelectSuccess({ select: SELECT }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteSelectSuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Select successfully deleted', + 'The existing select has been deleted' + ); + }); + }); + + describe('deleteSelectFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteSelectFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = selectActions.deleteSelectFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteSelectFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete select', + 'The existing select could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/select.effects.ts b/client/src/app/metamodel/effects/select.effects.ts index b3f53c9151a0cc5aa412113297b53150c800183f..c4a21e361d5b0bc916e862217c1604c05048eae0 100644 --- a/client/src/app/metamodel/effects/select.effects.ts +++ b/client/src/app/metamodel/effects/select.effects.ts @@ -16,10 +16,18 @@ import { ToastrService } from 'ngx-toastr'; import * as selectActions from '../actions/select.actions'; import { SelectService } from '../services/select.service'; - + +/** + * @class + * @classdesc Select effects. + */ @Injectable() export class SelectEffects { - loadSelects$ = createEffect(() => + + /** + * Calls action to retrieve select list. + */ + loadSelects$ = createEffect((): any => this.actions$.pipe( ofType(selectActions.loadSelectList), mergeMap(() => this.selectService.retrieveSelectList() @@ -31,7 +39,10 @@ export class SelectEffects { ) ); - addSelect$ = createEffect(() => + /** + * Calls action to add a select. + */ + addSelect$ = createEffect((): any => this.actions$.pipe( ofType(selectActions.addSelect), mergeMap(action => this.selectService.addSelect(action.select) @@ -43,21 +54,30 @@ export class SelectEffects { ) ); - addSelectSuccess$ = createEffect(() => + /** + * Displays add select success notification. + */ + addSelectSuccess$ = createEffect(() => this.actions$.pipe( ofType(selectActions.addSelectSuccess), tap(() => this.toastr.success('Select successfully added', 'The new select was added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - addSelectFail$ = createEffect(() => + /** + * Displays add select error notification. + */ + addSelectFail$ = createEffect(() => this.actions$.pipe( ofType(selectActions.addSelectFail), tap(() => this.toastr.error('Failure to add select', 'The new select could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editSelect$ = createEffect(() => + /** + * Calls action to modify a select. + */ + editSelect$ = createEffect((): any => this.actions$.pipe( ofType(selectActions.editSelect), mergeMap(action => this.selectService.editSelect(action.select) @@ -69,21 +89,30 @@ export class SelectEffects { ) ); - editSelectSuccess$ = createEffect(() => + /** + * Displays edit select success notification. + */ + editSelectSuccess$ = createEffect(() => this.actions$.pipe( ofType(selectActions.editSelectSuccess), tap(() => this.toastr.success('Select successfully edited', 'The existing select has been edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editSelectFail$ = createEffect(() => + /** + * Displays edit select success notification. + */ + editSelectFail$ = createEffect(() => this.actions$.pipe( ofType(selectActions.editSelectFail), tap(() => this.toastr.error('Failure to edit select', 'The existing select could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteSelect$ = createEffect(() => + /** + * Calls action to remove a select. + */ + deleteSelect$ = createEffect((): any => this.actions$.pipe( ofType(selectActions.deleteSelect), mergeMap(action => this.selectService.deleteSelect(action.select.name) @@ -95,20 +124,26 @@ export class SelectEffects { ) ); - deleteSelectSuccess$ = createEffect(() => + /** + * Displays delete select success notification. + */ + deleteSelectSuccess$ = createEffect(() => this.actions$.pipe( ofType(selectActions.deleteSelectSuccess), tap(() => this.toastr.success('Select successfully deleted', 'The existing select has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteSelectFail$ = createEffect(() => + /** + * Displays delete select error notification. + */ + deleteSelectFail$ = createEffect(() => this.actions$.pipe( ofType(selectActions.deleteSelectFail), tap(() => this.toastr.error('Failure to delete select', 'The existing select could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private selectService: SelectService, diff --git a/client/src/app/metamodel/effects/survey.effects.spec.ts b/client/src/app/metamodel/effects/survey.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..13ac0bbbc52ec976db97c5330d8c6fa3bc46b9c2 --- /dev/null +++ b/client/src/app/metamodel/effects/survey.effects.spec.ts @@ -0,0 +1,298 @@ +import { TestBed } from '@angular/core/testing'; +import { Router } from '@angular/router'; + +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 { SurveyEffects } from './survey.effects'; +import { SurveyService } from '../services/survey.service'; +import * as surveyActions from '../actions/survey.actions'; +import { SURVEY, SURVEY_LIST } from '../../../test-data'; + +describe('[Metamodel][Effects] SurveyEffects', () => { + let actions = new Observable(); + let effects: SurveyEffects; + let metadata: EffectsMetadata<SurveyEffects>; + let service: SurveyService; + let toastr: ToastrService; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + SurveyEffects, + { provide: SurveyService, useValue: { }}, + { provide: Router, useValue: { navigate: jest.fn() }}, + { provide: ToastrService, useValue: { + success: jest.fn(), + error: jest.fn() + }}, + provideMockActions(() => actions) + ] + }).compileComponents(); + effects = TestBed.inject(SurveyEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(SurveyService); + toastr = TestBed.inject(ToastrService); + router = TestBed.inject(Router); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadSurveys$ effect', () => { + it('should dispatch the loadSurveyListSuccess action on success', () => { + const action = surveyActions.loadSurveyList(); + const outcome = surveyActions.loadSurveyListSuccess({ surveys: SURVEY_LIST }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SURVEY_LIST }); + const expected = cold('--b', { b: outcome }); + service.retrieveSurveyList = jest.fn(() => response); + + expect(effects.loadSurveys$).toBeObservable(expected); + }); + + it('should dispatch the loadSurveyListFail action on HTTP failure', () => { + const action = surveyActions.loadSurveyList(); + const error = new Error(); + const outcome = surveyActions.loadSurveyListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveSurveyList = jest.fn(() => response); + + expect(effects.loadSurveys$).toBeObservable(expected); + }); + }); + + describe('addSurvey$ effect', () => { + it('should dispatch the addSurveySuccess action on success', () => { + const action = surveyActions.addSurvey({ survey: SURVEY }); + const outcome = surveyActions.addSurveySuccess({ survey: SURVEY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SURVEY }); + const expected = cold('--b', { b: outcome }); + service.addSurvey = jest.fn(() => response); + + expect(effects.addSurvey$).toBeObservable(expected); + }); + + it('should dispatch the addSurveyFail action on HTTP failure', () => { + const action = surveyActions.addSurvey({ survey: SURVEY }); + const error = new Error(); + const outcome = surveyActions.addSurveyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.addSurvey = jest.fn(() => response); + + expect(effects.addSurvey$).toBeObservable(expected); + }); + }); + + describe('addSurveySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addSurveySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const routerSpy = jest.spyOn(router, 'navigate'); + const action = surveyActions.addSurveySuccess({ survey: SURVEY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addSurveySuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Survey successfully added', + 'The new survey was added into the database' + ); + expect(routerSpy).toHaveBeenCalledTimes(1); + expect(routerSpy).toHaveBeenCalledWith(['/admin/survey/survey-list']); + }); + }); + + describe('addSurveyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.addSurveyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = surveyActions.addSurveyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.addSurveyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to add survey', + 'The new survey could not be added into the database' + ); + }); + }); + + describe('editSurvey$ effect', () => { + it('should dispatch the editSurveySuccess action on success', () => { + const action = surveyActions.editSurvey({ survey: SURVEY }); + const outcome = surveyActions.editSurveySuccess({ survey: SURVEY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SURVEY }); + const expected = cold('--b', { b: outcome }); + service.editSurvey = jest.fn(() => response); + + expect(effects.editSurvey$).toBeObservable(expected); + }); + + it('should dispatch the editSurveyFail action on HTTP failure', () => { + const action = surveyActions.editSurvey({ survey: SURVEY }); + const error = new Error(); + const outcome = surveyActions.editSurveyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.editSurvey = jest.fn(() => response); + + expect(effects.editSurvey$).toBeObservable(expected); + }); + }); + + describe('editSurveySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editSurveySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const routerSpy = jest.spyOn(router, 'navigate'); + const action = surveyActions.editSurveySuccess({ survey: SURVEY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editSurveySuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Survey successfully edited', + 'The existing survey has been edited into the database' + ); + expect(routerSpy).toHaveBeenCalledTimes(1); + expect(routerSpy).toHaveBeenCalledWith(['/admin/survey/survey-list']); + }); + }); + + describe('editSurveyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.editSurveyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = surveyActions.editSurveyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.editSurveyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to edit survey', + 'The existing survey could not be edited into the database' + ); + }); + }); + + describe('deleteSurvey$ effect', () => { + it('should dispatch the deleteSurveySuccess action on success', () => { + const action = surveyActions.deleteSurvey({ survey: SURVEY }); + const outcome = surveyActions.deleteSurveySuccess({ survey: SURVEY }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: SURVEY }); + const expected = cold('--b', { b: outcome }); + service.deleteSurvey = jest.fn(() => response); + + expect(effects.deleteSurvey$).toBeObservable(expected); + }); + + it('should dispatch the deleteSurveyFail action on HTTP failure', () => { + const action = surveyActions.deleteSurvey({ survey: SURVEY }); + const error = new Error(); + const outcome = surveyActions.deleteSurveyFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.deleteSurvey = jest.fn(() => response); + + expect(effects.deleteSurvey$).toBeObservable(expected); + }); + }); + + describe('deleteSurveySuccess$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteSurveySuccess$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display a success notification', () => { + const toastrSpy = jest.spyOn(toastr, 'success'); + const action = surveyActions.deleteSurveySuccess({ survey: SURVEY }); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteSurveySuccess$).toBeObservable(expected); + expect(toastrSpy).toHaveBeenCalledTimes(1); + expect(toastrSpy).toHaveBeenCalledWith( + 'Survey successfully deleted', + 'The existing survey has been deleted' + ); + }); + }); + + describe('deleteSurveyFail$ effect', () => { + it('should not dispatch', () => { + expect(metadata.deleteSurveyFail$).toEqual( + expect.objectContaining({ dispatch: false }) + ); + }); + + it('should display an error notification', () => { + const spy = jest.spyOn(toastr, 'error'); + const action = surveyActions.deleteSurveyFail(); + + actions = hot('a', { a: action }); + const expected = cold('a', { a: action }); + + expect(effects.deleteSurveyFail$).toBeObservable(expected); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith( + 'Failure to delete survey', + 'The existing survey could not be deleted from the database' + ); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/survey.effects.ts b/client/src/app/metamodel/effects/survey.effects.ts index 7d876b5ac2fa0d0f8d87b9b13483e732daddbd76..7d6cc6530d0fd701bf2b82c2fa16d056637fdd5c 100644 --- a/client/src/app/metamodel/effects/survey.effects.ts +++ b/client/src/app/metamodel/effects/survey.effects.ts @@ -17,10 +17,18 @@ import { ToastrService } from 'ngx-toastr'; import * as surveyActions from '../actions/survey.actions'; import { SurveyService } from '../services/survey.service'; - + +/** + * @class + * @classdesc Survey effects. + */ @Injectable() export class SurveyEffects { - loadSurveys$ = createEffect(() => + + /** + * Calls action to retrieve survey list. + */ + loadSurveys$ = createEffect((): any => this.actions$.pipe( ofType(surveyActions.loadSurveyList), mergeMap(() => this.surveyService.retrieveSurveyList() @@ -31,8 +39,11 @@ export class SurveyEffects { ) ) ); - - addSurvey$ = createEffect(() => + + /** + * Calls action to add survey. + */ + addSurvey$ = createEffect((): any => this.actions$.pipe( ofType(surveyActions.addSurvey), mergeMap(action => this.surveyService.addSurvey(action.survey) @@ -44,24 +55,33 @@ export class SurveyEffects { ) ); - addSurveySuccess$ = createEffect(() => + /** + * Displays add survey success notification. + */ + addSurveySuccess$ = createEffect(() => this.actions$.pipe( ofType(surveyActions.addSurveySuccess), tap(() => { this.router.navigate(['/admin/survey/survey-list']); this.toastr.success('Survey successfully added', 'The new survey was added into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - addSurveyFail$ = createEffect(() => + /** + * Displays add survey error notification. + */ + addSurveyFail$ = createEffect(() => this.actions$.pipe( ofType(surveyActions.addSurveyFail), tap(() => this.toastr.error('Failure to add survey', 'The new survey could not be added into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - editSurvey$ = createEffect(() => + /** + * Calls action to modify a survey. + */ + editSurvey$ = createEffect((): any => this.actions$.pipe( ofType(surveyActions.editSurvey), mergeMap(action => this.surveyService.editSurvey(action.survey) @@ -73,24 +93,33 @@ export class SurveyEffects { ) ); - editSurveySuccess$ = createEffect(() => + /** + * Displays edit survey success notification. + */ + editSurveySuccess$ = createEffect(() => this.actions$.pipe( ofType(surveyActions.editSurveySuccess), tap(() => { this.router.navigate(['/admin/survey/survey-list']); this.toastr.success('Survey successfully edited', 'The existing survey has been edited into the database') }) - ), { dispatch: false} + ), { dispatch: false } ); - editSurveyFail$ = createEffect(() => + /** + * Displays edit survey error notification. + */ + editSurveyFail$ = createEffect(() => this.actions$.pipe( ofType(surveyActions.editSurveyFail), tap(() => this.toastr.error('Failure to edit survey', 'The existing survey could not be edited into the database')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteSurvey$ = createEffect(() => + /** + * Calls action to remove a survey. + */ + deleteSurvey$ = createEffect((): any => this.actions$.pipe( ofType(surveyActions.deleteSurvey), mergeMap(action => this.surveyService.deleteSurvey(action.survey.name) @@ -102,24 +131,30 @@ export class SurveyEffects { ) ); - deleteSurveySuccess$ = createEffect(() => + /** + * Displays delete survey success notification. + */ + deleteSurveySuccess$ = createEffect(() => this.actions$.pipe( ofType(surveyActions.deleteSurveySuccess), tap(() => this.toastr.success('Survey successfully deleted', 'The existing survey has been deleted')) - ), { dispatch: false} + ), { dispatch: false } ); - deleteSurveyFail$ = createEffect(() => + /** + * Displays edit survey error notification. + */ + deleteSurveyFail$ = createEffect(() => this.actions$.pipe( ofType(surveyActions.deleteSurveyFail), tap(() => this.toastr.error('Failure to delete survey', 'The existing survey could not be deleted from the database')) - ), { dispatch: false} + ), { dispatch: false } ); - + constructor( private actions$: Actions, private surveyService: SurveyService, private router: Router, private toastr: ToastrService - ) {} + ) { } } diff --git a/client/src/app/metamodel/effects/table.effects.spec.ts b/client/src/app/metamodel/effects/table.effects.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..72e7b2ccab9e795032636f9c05c0db49d1bfb481 --- /dev/null +++ b/client/src/app/metamodel/effects/table.effects.spec.ts @@ -0,0 +1,61 @@ +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 { TableEffects } from './table.effects'; +import { TableService } from '../services/table.service'; +import * as tableActions from '../actions/table.actions'; + +describe('[Metamodel][Effects] TableEffects', () => { + let actions = new Observable(); + let effects: TableEffects; + let metadata: EffectsMetadata<TableEffects>; + let service: TableService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + TableEffects, + { provide: TableService, useValue: { }}, + provideMockActions(() => actions) + ] + }).compileComponents(); + effects = TestBed.inject(TableEffects); + metadata = getEffectsMetadata(effects); + service = TestBed.inject(TableService); + }); + + it('should be created', () => { + expect(effects).toBeTruthy(); + }); + + describe('loadTables$ effect', () => { + it('should dispatch the loadTableListSuccess action on success', () => { + const action = tableActions.loadTableList({ idDatabase: 1 }); + const outcome = tableActions.loadTableListSuccess({ tables: ['table1', 'table2'] }); + + actions = hot('-a', { a: action }); + const response = cold('-a|', { a: ['table1', 'table2'] }); + const expected = cold('--b', { b: outcome }); + service.retrieveTableList = jest.fn(() => response); + + expect(effects.loadTables$).toBeObservable(expected); + }); + + it('should dispatch the loadTableListFail action on HTTP failure', () => { + const action = tableActions.loadTableList({ idDatabase: 1 }); + const error = new Error(); + const outcome = tableActions.loadTableListFail(); + + actions = hot('-a', { a: action }); + const response = cold('-#|', { }, error); + const expected = cold('--b', { b: outcome }); + service.retrieveTableList = jest.fn(() => response); + + expect(effects.loadTables$).toBeObservable(expected); + }); + }); +}); diff --git a/client/src/app/metamodel/effects/table.effects.ts b/client/src/app/metamodel/effects/table.effects.ts index 0a32c24095f5a89886a58ec123fed5e4fb0fec05..9a507ce2be574c5d480e6ee67fc286a82b82cc16 100644 --- a/client/src/app/metamodel/effects/table.effects.ts +++ b/client/src/app/metamodel/effects/table.effects.ts @@ -15,10 +15,18 @@ import { map, mergeMap, catchError } from 'rxjs/operators'; import * as tableActions from '../actions/table.actions'; import { TableService } from '../services/table.service'; - + +/** + * @class + * @classdesc Table effects. + */ @Injectable() export class TableEffects { - loadTables$ = createEffect(() => + + /** + * Calls action to retrieve table list. + */ + loadTables$ = createEffect((): any => this.actions$.pipe( ofType(tableActions.loadTableList), mergeMap(action => this.tableService.retrieveTableList(action.idDatabase) @@ -29,9 +37,9 @@ export class TableEffects { ) ) ); - + constructor( private actions$: Actions, private tableService: TableService - ) {} + ) { } } diff --git a/client/src/app/metamodel/metamodel.module.ts b/client/src/app/metamodel/metamodel.module.ts index 9713c0a547f397d0373c60bb134397685f12ad33..a5d06d880ee034d114796b043240cfe3597db362 100644 --- a/client/src/app/metamodel/metamodel.module.ts +++ b/client/src/app/metamodel/metamodel.module.ts @@ -17,6 +17,10 @@ import { metamodelReducer } from './metamodel.reducer'; import { metamodelEffects } from './effects'; import { metamodelServices} from './services'; +/** + * @class + * @classdesc Metamodel module. + */ @NgModule({ imports: [ CommonModule, diff --git a/client/src/app/metamodel/metamodel.reducer.ts b/client/src/app/metamodel/metamodel.reducer.ts index 6df176d13fce0b8849defbc9de0d4cb327036f93..d7da50592c6ce08bd76d511891f220b6c8c38182 100644 --- a/client/src/app/metamodel/metamodel.reducer.ts +++ b/client/src/app/metamodel/metamodel.reducer.ts @@ -27,6 +27,11 @@ import * as select from './reducers/select.reducer'; import * as selectOption from './reducers/select-option.reducer'; import * as attributeDistinct from './reducers/attribute-distinct.reducer'; +/** + * Interface for metamodel state. + * + * @interface State + */ export interface State { database: database.State; table: table.State; @@ -68,4 +73,3 @@ const reducers = { export const metamodelReducer = combineReducers(reducers); export const getMetamodelState = createFeatureSelector<State>('metamodel'); export const selectRouterState = createFeatureSelector<RouterReducerState>('router'); - \ No newline at end of file diff --git a/client/src/app/metamodel/models/attribute.model.ts b/client/src/app/metamodel/models/attribute.model.ts index d5397bb427a5f482e707b2868aa484c3917e7f25..f58acd2c159e59532c9bf8e085b840b170a9b145 100644 --- a/client/src/app/metamodel/models/attribute.model.ts +++ b/client/src/app/metamodel/models/attribute.model.ts @@ -8,8 +8,13 @@ */ import { Option } from './option.model'; -import { RendererConfig } from './renderers/renderer-config.model'; +import { RendererConfig } from './renderers'; +/** + * Interface for attribute. + * + * @interface Attribute + */ export interface Attribute { id: number; name: string; diff --git a/client/src/app/metamodel/models/column.model.ts b/client/src/app/metamodel/models/column.model.ts index c06bebf009ba8cab65cc21ed544e5be68ba23cfd..83e1ff2799679a2461e6165a74487344f3b6310b 100644 --- a/client/src/app/metamodel/models/column.model.ts +++ b/client/src/app/metamodel/models/column.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for column. + * + * @interface Column + */ export interface Column { name: string; type: string; diff --git a/client/src/app/metamodel/models/criteria-family.model.ts b/client/src/app/metamodel/models/criteria-family.model.ts index aeb7f90aa539d50d73eadad7016bbf1986c4fba1..4870fc29908f90f0011f2391ecd9c528cc4136dd 100644 --- a/client/src/app/metamodel/models/criteria-family.model.ts +++ b/client/src/app/metamodel/models/criteria-family.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for criteria family. + * + * @interface CriteriaFamily + */ export interface CriteriaFamily { id: number; label: string; diff --git a/client/src/app/metamodel/models/database.model.ts b/client/src/app/metamodel/models/database.model.ts index e2889a40f945641d8928cbb54000b30ff44cf055..96ce8db984564381c9362c3bc6f7ef09c26cdbf3 100644 --- a/client/src/app/metamodel/models/database.model.ts +++ b/client/src/app/metamodel/models/database.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for database. + * + * @interface Database + */ export interface Database { id: number; label: string; diff --git a/client/src/app/metamodel/models/dataset-family.model.ts b/client/src/app/metamodel/models/dataset-family.model.ts index 391f90392ee3e08741ef0b34ff73ad65cdb81569..98ff77f2a92fda884f097227880d648b3b11c5f3 100644 --- a/client/src/app/metamodel/models/dataset-family.model.ts +++ b/client/src/app/metamodel/models/dataset-family.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for dataset family. + * + * @interface DatasetFamily + */ export interface DatasetFamily { id: number; label: string; diff --git a/client/src/app/metamodel/models/dataset.model.ts b/client/src/app/metamodel/models/dataset.model.ts index 22e73074318ab24c91fdb32836dd9c3834c8a1ea..7c7baa5d11a922ea9bb4353133a9e0c0a883ca92 100644 --- a/client/src/app/metamodel/models/dataset.model.ts +++ b/client/src/app/metamodel/models/dataset.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for dataset. + * + * @interface Dataset + */ export interface Dataset { name: string; table_ref: string; diff --git a/client/src/app/metamodel/models/file-info.model.ts b/client/src/app/metamodel/models/file-info.model.ts index 675c092cfd0cb0b163701fcd37234034f705cce8..f966093e908d5f5a1ce1c72732f3829c9b9d7757 100644 --- a/client/src/app/metamodel/models/file-info.model.ts +++ b/client/src/app/metamodel/models/file-info.model.ts @@ -7,6 +7,12 @@ * file that was distributed with this source code. */ + +/** + * Interface for file info. + * + * @interface FileInfo + */ export interface FileInfo { name: string; size: number; diff --git a/client/src/app/metamodel/models/group.model.ts b/client/src/app/metamodel/models/group.model.ts index 788f669b99d768c8edfdc416eb3e2966011d5526..64ed71fea6cdc94abb8f633ff238b16f0b053819 100644 --- a/client/src/app/metamodel/models/group.model.ts +++ b/client/src/app/metamodel/models/group.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for group. + * + * @interface Group + */ export interface Group { id: number; role: string; diff --git a/client/src/app/metamodel/models/instance.model.ts b/client/src/app/metamodel/models/instance.model.ts index c50e7bc234b97659b9b950b118669774f02ee582..bbb5436413cfed03eb5fe121d83183fab8d11246 100644 --- a/client/src/app/metamodel/models/instance.model.ts +++ b/client/src/app/metamodel/models/instance.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for instance. + * + * @interface Instance + */ export interface Instance { name: string; label: string; diff --git a/client/src/app/metamodel/models/option.model.ts b/client/src/app/metamodel/models/option.model.ts index 3b934e2551fe4e1bcd30278ed5da1201dcf6da26..91ab5ee69236517db59a12fea55da73f3e0ac202 100644 --- a/client/src/app/metamodel/models/option.model.ts +++ b/client/src/app/metamodel/models/option.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for option. + * + * @interface Option + */ export interface Option { label: string; value: string; diff --git a/client/src/app/metamodel/models/output-category.model.ts b/client/src/app/metamodel/models/output-category.model.ts index 408d7c8d54b113cb2b19058b7162132109714ab1..637b562ff24d3d1696f4a695d0ef1bb5a519a8f9 100644 --- a/client/src/app/metamodel/models/output-category.model.ts +++ b/client/src/app/metamodel/models/output-category.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for output category. + * + * @interface OutputCategory + */ export interface OutputCategory { id: number; label: string; diff --git a/client/src/app/metamodel/models/output-family.model.ts b/client/src/app/metamodel/models/output-family.model.ts index d1d1e076bba1a1bdba4a83c4f48705b8eed3b334..f0970ab3b9155dcbd9c1bb0afecfbec650393633 100644 --- a/client/src/app/metamodel/models/output-family.model.ts +++ b/client/src/app/metamodel/models/output-family.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for output family. + * + * @interface OutputFamily + */ export interface OutputFamily { id: number; label: string; diff --git a/client/src/app/metamodel/models/renderers/detail-renderer-config.model.ts b/client/src/app/metamodel/models/renderers/detail-renderer-config.model.ts index 4012f3c47917e6d1624da3be0ab9e98e52c0fc8a..921abadd662842ed0555c99fe5c9ea00ae1818a0 100644 --- a/client/src/app/metamodel/models/renderers/detail-renderer-config.model.ts +++ b/client/src/app/metamodel/models/renderers/detail-renderer-config.model.ts @@ -1,5 +1,20 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + import { RendererConfig } from './renderer-config.model'; +/** + * Interface for detail renderer config. + * + * @interface DetailRendererConfig + * @extends RendererConfig + */ export interface DetailRendererConfig extends RendererConfig { display: string; icon_button: string; diff --git a/client/src/app/metamodel/models/renderers/download-renderer-config.model.ts b/client/src/app/metamodel/models/renderers/download-renderer-config.model.ts index 93747e6e08d8c1375560448e493f51120005f5a0..c9291889176a2a6997a020295576198094c441a9 100644 --- a/client/src/app/metamodel/models/renderers/download-renderer-config.model.ts +++ b/client/src/app/metamodel/models/renderers/download-renderer-config.model.ts @@ -1,5 +1,20 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + import { RendererConfig } from './renderer-config.model'; +/** + * Interface for download renderer config. + * + * @interface DownloadRendererConfig + * @extends RendererConfig + */ export interface DownloadRendererConfig extends RendererConfig { display: string; text: string; diff --git a/client/src/app/metamodel/models/renderers/image-renderer-config.model.ts b/client/src/app/metamodel/models/renderers/image-renderer-config.model.ts index a93270e3bcec7f71509cec94a56714ef7058d670..2e6e89efc8495344dd08f04f268d171525d51ad6 100644 --- a/client/src/app/metamodel/models/renderers/image-renderer-config.model.ts +++ b/client/src/app/metamodel/models/renderers/image-renderer-config.model.ts @@ -1,5 +1,20 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + import { RendererConfig } from './renderer-config.model'; +/** + * Interface for image renderer config. + * + * @interface ImageRendererConfig + * @extends RendererConfig + */ export interface ImageRendererConfig extends RendererConfig { type: string; display: string; diff --git a/client/src/app/metamodel/models/renderers/link-renderer-config.model.ts b/client/src/app/metamodel/models/renderers/link-renderer-config.model.ts index 9c2c296561ae411cc7288e5c7e57d28b46e3bb49..2cb4fc07c1eb2217a32d11f9e74d8ea33f999e18 100644 --- a/client/src/app/metamodel/models/renderers/link-renderer-config.model.ts +++ b/client/src/app/metamodel/models/renderers/link-renderer-config.model.ts @@ -1,5 +1,20 @@ +/** + * This file is part of Anis Client. + * + * @copyright Laboratoire d'Astrophysique de Marseille / CNRS + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + import { RendererConfig } from './renderer-config.model'; +/** + * Interface for link renderer config. + * + * @interface LinkRendererConfig + * @extends RendererConfig + */ export interface LinkRendererConfig extends RendererConfig { href: string; display: string; diff --git a/client/src/app/metamodel/models/renderers/renderer-config.model.ts b/client/src/app/metamodel/models/renderers/renderer-config.model.ts index 00fafcd4a0df22cf3b9efdde9803ed1e5d190c25..cd8d137570dd14e95be1ab28cf25f5707e3d3416 100644 --- a/client/src/app/metamodel/models/renderers/renderer-config.model.ts +++ b/client/src/app/metamodel/models/renderers/renderer-config.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for renderer config. + * + * @interface RendererConfig + */ export interface RendererConfig { id: 'renderer-config'; } diff --git a/client/src/app/metamodel/models/select-option.model.ts b/client/src/app/metamodel/models/select-option.model.ts index 014a43f253ec46c4f71433f272e77dd7eca42395..bb3473f08f3b095e7b8985fe355acab0587b5799 100644 --- a/client/src/app/metamodel/models/select-option.model.ts +++ b/client/src/app/metamodel/models/select-option.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for select option. + * + * @interface SelectOption + */ export interface SelectOption { id: number; label: string; diff --git a/client/src/app/metamodel/models/select.model.ts b/client/src/app/metamodel/models/select.model.ts index c969f18c16d0d5447d0ab3857f88233eee1e3bfe..4bdb3d403ae6b2c5238f4240f11bccba687da673 100644 --- a/client/src/app/metamodel/models/select.model.ts +++ b/client/src/app/metamodel/models/select.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for select. + * + * @interface Select + */ export interface Select { name: string; label: string; diff --git a/client/src/app/metamodel/models/survey.model.ts b/client/src/app/metamodel/models/survey.model.ts index bba021fddcc2f97f94b5b25104fb639c27c60fef..c23ab90afca9832f448b6f69ddc457218803d680 100644 --- a/client/src/app/metamodel/models/survey.model.ts +++ b/client/src/app/metamodel/models/survey.model.ts @@ -7,6 +7,11 @@ * file that was distributed with this source code. */ +/** + * Interface for survey. + * + * @interface Survey + */ export interface Survey { name: string; label: string; diff --git a/client/src/app/metamodel/reducers/attribute-distinct.reducer.spec.ts b/client/src/app/metamodel/reducers/attribute-distinct.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e94ea987ae68546de0e171b54972a78a845874d5 --- /dev/null +++ b/client/src/app/metamodel/reducers/attribute-distinct.reducer.spec.ts @@ -0,0 +1,63 @@ +import { Action } from '@ngrx/store'; + +import * as fromAttributeDistinct from './attribute-distinct.reducer'; +import * as attributeDistinctActions from '../actions/attribute-distinct.actions'; +import { ATTRIBUTE, SURVEY, SURVEY_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] AttributeDistinct reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromAttributeDistinct; + const action = { type: 'Unknown' }; + const state = fromAttributeDistinct.attributeDistinctReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadAttributeDistinctList action should set attributeDistinctListIsLoading to true', () => { + const { initialState } = fromAttributeDistinct; + const action = attributeDistinctActions.loadAttributeDistinctList({ attribute: ATTRIBUTE }); + const state = fromAttributeDistinct.attributeDistinctReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.attributeDistinctListIsLoading).toEqual(true); + expect(state.attributeDistinctListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadAttributeDistinctListSuccess action should add attributeDistinct list, set attributeDistinctListIsLoading to false and set attributeDistinctListIsLoaded to true', () => { + const { initialState } = fromAttributeDistinct; + const action = attributeDistinctActions.loadAttributeDistinctListSuccess({ values: ['val-one', 'val-two'] }); + const state = fromAttributeDistinct.attributeDistinctReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain('val-one'); + expect(state.ids).toContain('val-two'); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.attributeDistinctListIsLoading).toEqual(false); + expect(state.attributeDistinctListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadAttributeDistinctListFail action should set attributeDistinctListIsLoading to false', () => { + const { initialState } = fromAttributeDistinct; + const action = attributeDistinctActions.loadAttributeDistinctListFail(); + const state = fromAttributeDistinct.attributeDistinctReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.attributeDistinctListIsLoading).toEqual(false); + expect(state.attributeDistinctListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get attributeDistinctListIsLoading', () => { + const action = {} as Action; + const state = fromAttributeDistinct.attributeDistinctReducer(undefined, action); + + expect(fromAttributeDistinct.selectAttributeDistinctListIsLoading(state)).toEqual(false); + }); + + it('should get attributeDistinctListIsLoaded', () => { + const action = {} as Action; + const state = fromAttributeDistinct.attributeDistinctReducer(undefined, action); + + expect(fromAttributeDistinct.selectAttributeDistinctListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/attribute-distinct.reducer.ts b/client/src/app/metamodel/reducers/attribute-distinct.reducer.ts index 657d050033e7cb11b6ad3c23023731930c57f393..533d4c31ad6431a8d9906b7999decfde8a8f5755 100644 --- a/client/src/app/metamodel/reducers/attribute-distinct.reducer.ts +++ b/client/src/app/metamodel/reducers/attribute-distinct.reducer.ts @@ -12,6 +12,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import * as attributeDistinctActions from '../actions/attribute-distinct.actions'; +/** + * Interface for attribute distinct state. + * + * @interface State + */ export interface State extends EntityState<string> { attributeDistinctListIsLoading: boolean; attributeDistinctListIsLoaded: boolean; @@ -37,8 +42,8 @@ export const attributeDistinctReducer = createReducer( }), on(attributeDistinctActions.loadAttributeDistinctListSuccess, (state, { values }) => { return adapter.setAll( - values, - { + values, + { ...state, attributeDistinctListIsLoading: false, attributeDistinctListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/attribute.reducer.spec.ts b/client/src/app/metamodel/reducers/attribute.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..b1abaad67d767c1afd7f6a8a025a863fb5ba070f --- /dev/null +++ b/client/src/app/metamodel/reducers/attribute.reducer.spec.ts @@ -0,0 +1,109 @@ +import { Action } from '@ngrx/store'; + +import * as fromAttribute from './attribute.reducer'; +import * as attributeActions from '../actions/attribute.actions'; +import { ATTRIBUTE, ATTRIBUTE_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] Attribute reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromAttribute; + const action = { type: 'Unknown' }; + const state = fromAttribute.attributeReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadAttributeList action should set attributeListIsLoading to true', () => { + const { initialState } = fromAttribute; + const action = attributeActions.loadAttributeList(); + const state = fromAttribute.attributeReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.attributeListIsLoading).toEqual(true); + expect(state.attributeListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadAttributeListSuccess action should add attribute list, set attributeListIsLoading to false and set attributeListIsLoaded to true', () => { + const { initialState } = fromAttribute; + const action = attributeActions.loadAttributeListSuccess({ attributes: ATTRIBUTE_LIST }); + const state = fromAttribute.attributeReducer(initialState, action); + expect(state.ids.length).toEqual(4); + expect(state.ids).toContain(1); + expect(state.ids).toContain(2); + expect(state.ids).toContain(3); + expect(state.ids).toContain(4); + expect(Object.keys(state.entities).length).toEqual(4); + expect(state.attributeListIsLoading).toEqual(false); + expect(state.attributeListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadAttributeListFail action should set attributeListIsLoading to false', () => { + const { initialState } = fromAttribute; + const action = attributeActions.loadAttributeListFail(); + const state = fromAttribute.attributeReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.attributeListIsLoading).toEqual(false); + expect(state.attributeListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addAttributeSuccess action should add a attribute', () => { + const { initialState } = fromAttribute; + const action = attributeActions.addAttributeSuccess({ attribute: ATTRIBUTE }); + const state = fromAttribute.attributeReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.attributeListIsLoading).toEqual(false); + expect(state.attributeListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editAttributeSuccess action should modify a attribute', () => { + const initialState = { + ...fromAttribute.initialState, + ids: [1], + entities: { 1: { ...ATTRIBUTE, label: 'label' }} + }; + const action = attributeActions.editAttributeSuccess({ attribute: ATTRIBUTE }); + const state = fromAttribute.attributeReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities[1]).toEqual(ATTRIBUTE); + expect(state.attributeListIsLoading).toEqual(false); + expect(state.attributeListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteAttributeSuccess action should modify a attribute', () => { + const initialState = { + ...fromAttribute.initialState, + ids: [1], + entities: { 1: ATTRIBUTE } + }; + const action = attributeActions.deleteAttributeSuccess({ attribute: ATTRIBUTE }); + const state = fromAttribute.attributeReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.attributeListIsLoading).toEqual(false); + expect(state.attributeListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get attributeListIsLoading', () => { + const action = {} as Action; + const state = fromAttribute.attributeReducer(undefined, action); + + expect(fromAttribute.selectAttributeListIsLoading(state)).toEqual(false); + }); + + it('should get attributeListIsLoaded', () => { + const action = {} as Action; + const state = fromAttribute.attributeReducer(undefined, action); + + expect(fromAttribute.selectAttributeListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/attribute.reducer.ts b/client/src/app/metamodel/reducers/attribute.reducer.ts index 769d7e329f6b32c2cc12e9699f11cb0ad62c8965..3def9fef90535dcb6dcf5875d2b84dd7dbe78ebc 100644 --- a/client/src/app/metamodel/reducers/attribute.reducer.ts +++ b/client/src/app/metamodel/reducers/attribute.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { Attribute } from '../models'; import * as attributeActions from '../actions/attribute.actions'; +/** + * Interface for attribute state. + * + * @interface State + */ export interface State extends EntityState<Attribute> { attributeListIsLoading: boolean; attributeListIsLoaded: boolean; @@ -36,8 +41,8 @@ export const attributeReducer = createReducer( }), on(attributeActions.loadAttributeListSuccess, (state, { attributes }) => { return adapter.setAll( - attributes, - { + attributes, + { ...state, attributeListIsLoading: false, attributeListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/column.reducer.spec.ts b/client/src/app/metamodel/reducers/column.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..69efb358fa86cc72d48707c818a3e3fde47b60fa --- /dev/null +++ b/client/src/app/metamodel/reducers/column.reducer.spec.ts @@ -0,0 +1,63 @@ +import { Action } from '@ngrx/store'; + +import * as fromColumn from './column.reducer'; +import * as columnActions from '../actions/column.actions'; +import { COLUMN_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] Column reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromColumn; + const action = { type: 'Unknown' }; + const state = fromColumn.columnReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadColumnList action should set columnListIsLoading to true', () => { + const { initialState } = fromColumn; + const action = columnActions.loadColumnList(); + const state = fromColumn.columnReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.columnListIsLoading).toEqual(true); + expect(state.columnListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadColumnListSuccess action should add column list, set columnListIsLoading to false and set columnListIsLoaded to true', () => { + const { initialState } = fromColumn; + const action = columnActions.loadColumnListSuccess({ columns: COLUMN_LIST }); + const state = fromColumn.columnReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain('myCol'); + expect(state.ids).toContain('anotherCol'); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.columnListIsLoading).toEqual(false); + expect(state.columnListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadColumnListFail action should set columnListIsLoading to false', () => { + const { initialState } = fromColumn; + const action = columnActions.loadColumnListFail(); + const state = fromColumn.columnReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.columnListIsLoading).toEqual(false); + expect(state.columnListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get columnListIsLoading', () => { + const action = {} as Action; + const state = fromColumn.columnReducer(undefined, action); + + expect(fromColumn.selectColumnListIsLoading(state)).toEqual(false); + }); + + it('should get columnListIsLoaded', () => { + const action = {} as Action; + const state = fromColumn.columnReducer(undefined, action); + + expect(fromColumn.selectColumnListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/column.reducer.ts b/client/src/app/metamodel/reducers/column.reducer.ts index ad36e21591ca9513ec60f6a1664ebdf47cb84f50..faee0a628bff04249e65c8e3edf36a8fbe20e010 100644 --- a/client/src/app/metamodel/reducers/column.reducer.ts +++ b/client/src/app/metamodel/reducers/column.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { Column } from '../models'; import * as columnActions from '../actions/column.actions'; +/** + * Interface for column state. + * + * @interface State + */ export interface State extends EntityState<Column> { columnListIsLoading: boolean; columnListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const columnReducer = createReducer( }), on(columnActions.loadColumnListSuccess, (state, { columns }) => { return adapter.setAll( - columns, - { + columns, + { ...state, columnListIsLoading: false, columnListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/criteria-family.reducer.spec.ts b/client/src/app/metamodel/reducers/criteria-family.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..249bd1c78ba8250d081513fd2b93b6c26de65772 --- /dev/null +++ b/client/src/app/metamodel/reducers/criteria-family.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromCriteriaFamily from './criteria-family.reducer'; +import * as criteriaFamilyActions from '../actions/criteria-family.actions'; +import { CRITERIA_FAMILY, CRITERIA_FAMILY_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] CriteriaFamily reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromCriteriaFamily; + const action = { type: 'Unknown' }; + const state = fromCriteriaFamily.criteriaFamilyReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadCriteriaFamilyList action should set criteriaFamilyListIsLoading to true', () => { + const { initialState } = fromCriteriaFamily; + const action = criteriaFamilyActions.loadCriteriaFamilyList(); + const state = fromCriteriaFamily.criteriaFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.criteriaFamilyListIsLoading).toEqual(true); + expect(state.criteriaFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadCriteriaFamilyListSuccess action should add criteriaFamily list, set criteriaFamilyListIsLoading to false and set criteriaFamilyListIsLoaded to true', () => { + const { initialState } = fromCriteriaFamily; + const action = criteriaFamilyActions.loadCriteriaFamilyListSuccess({ criteriaFamilies: CRITERIA_FAMILY_LIST }); + const state = fromCriteriaFamily.criteriaFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain(1); + expect(state.ids).toContain(2); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.criteriaFamilyListIsLoading).toEqual(false); + expect(state.criteriaFamilyListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadCriteriaFamilyListFail action should set criteriaFamilyListIsLoading to false', () => { + const { initialState } = fromCriteriaFamily; + const action = criteriaFamilyActions.loadCriteriaFamilyListFail(); + const state = fromCriteriaFamily.criteriaFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.criteriaFamilyListIsLoading).toEqual(false); + expect(state.criteriaFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addCriteriaFamilySuccess action should add a criteriaFamily', () => { + const { initialState } = fromCriteriaFamily; + const action = criteriaFamilyActions.addCriteriaFamilySuccess({ criteriaFamily: CRITERIA_FAMILY }); + const state = fromCriteriaFamily.criteriaFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.criteriaFamilyListIsLoading).toEqual(false); + expect(state.criteriaFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editCriteriaFamilySuccess action should modify a criteriaFamily', () => { + const initialState = { + ...fromCriteriaFamily.initialState, + ids: [1], + entities: { 1: { ...CRITERIA_FAMILY, label: 'label' }} + }; + const action = criteriaFamilyActions.editCriteriaFamilySuccess({ criteriaFamily: CRITERIA_FAMILY }); + const state = fromCriteriaFamily.criteriaFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities[1]).toEqual(CRITERIA_FAMILY); + expect(state.criteriaFamilyListIsLoading).toEqual(false); + expect(state.criteriaFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteCriteriaFamilySuccess action should modify a criteriaFamily', () => { + const initialState = { + ...fromCriteriaFamily.initialState, + ids: [1], + entities: { 1: CRITERIA_FAMILY } + }; + const action = criteriaFamilyActions.deleteCriteriaFamilySuccess({ criteriaFamily: CRITERIA_FAMILY }); + const state = fromCriteriaFamily.criteriaFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.criteriaFamilyListIsLoading).toEqual(false); + expect(state.criteriaFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get criteriaFamilyListIsLoading', () => { + const action = {} as Action; + const state = fromCriteriaFamily.criteriaFamilyReducer(undefined, action); + + expect(fromCriteriaFamily.selectCriteriaFamilyListIsLoading(state)).toEqual(false); + }); + + it('should get criteriaFamilyListIsLoaded', () => { + const action = {} as Action; + const state = fromCriteriaFamily.criteriaFamilyReducer(undefined, action); + + expect(fromCriteriaFamily.selectCriteriaFamilyListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/criteria-family.reducer.ts b/client/src/app/metamodel/reducers/criteria-family.reducer.ts index 76ccaae48cbaa1e8d6b28da6f878b495756734d6..9e072db237fd32c014340643282e4194b8f7c9fd 100644 --- a/client/src/app/metamodel/reducers/criteria-family.reducer.ts +++ b/client/src/app/metamodel/reducers/criteria-family.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { CriteriaFamily } from '../models'; import * as criteriaFamilyActions from '../actions/criteria-family.actions'; +/** + * Interface for criteria family state. + * + * @interface State + */ export interface State extends EntityState<CriteriaFamily> { criteriaFamilyListIsLoading: boolean; criteriaFamilyListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const criteriaFamilyReducer = createReducer( }), on(criteriaFamilyActions.loadCriteriaFamilyListSuccess, (state, { criteriaFamilies }) => { return adapter.setAll( - criteriaFamilies, - { + criteriaFamilies, + { ...state, criteriaFamilyListIsLoading: false, criteriaFamilyListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/database.reducer.spec.ts b/client/src/app/metamodel/reducers/database.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..dd92d42f2ec01f3d5c4a5e498cb6cd9ecf70181d --- /dev/null +++ b/client/src/app/metamodel/reducers/database.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromDatabase from './database.reducer'; +import * as databaseActions from '../actions/database.actions'; +import { DATABASE, DATABASE_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] Database reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromDatabase; + const action = { type: 'Unknown' }; + const state = fromDatabase.databaseReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadDatabaseList action should set databaseListIsLoading to true', () => { + const { initialState } = fromDatabase; + const action = databaseActions.loadDatabaseList(); + const state = fromDatabase.databaseReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.databaseListIsLoading).toEqual(true); + expect(state.databaseListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadDatabaseListSuccess action should add database list, set databaseListIsLoading to false and set databaseListIsLoaded to true', () => { + const { initialState } = fromDatabase; + const action = databaseActions.loadDatabaseListSuccess({ databases: DATABASE_LIST }); + const state = fromDatabase.databaseReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain(1); + expect(state.ids).toContain(2); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.databaseListIsLoading).toEqual(false); + expect(state.databaseListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadDatabaseListFail action should set databaseListIsLoading to false', () => { + const { initialState } = fromDatabase; + const action = databaseActions.loadDatabaseListFail(); + const state = fromDatabase.databaseReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.databaseListIsLoading).toEqual(false); + expect(state.databaseListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addDatabaseSuccess action should add a database', () => { + const { initialState } = fromDatabase; + const action = databaseActions.addDatabaseSuccess({ database: DATABASE }); + const state = fromDatabase.databaseReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.databaseListIsLoading).toEqual(false); + expect(state.databaseListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editDatabaseSuccess action should modify a database', () => { + const initialState = { + ...fromDatabase.initialState, + ids: [1], + entities: { 1: { ...DATABASE, label: 'label' }} + }; + const action = databaseActions.editDatabaseSuccess({ database: DATABASE }); + const state = fromDatabase.databaseReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities[1]).toEqual(DATABASE); + expect(state.databaseListIsLoading).toEqual(false); + expect(state.databaseListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteDatabaseSuccess action should modify a database', () => { + const initialState = { + ...fromDatabase.initialState, + ids: [1], + entities: { 1: DATABASE } + }; + const action = databaseActions.deleteDatabaseSuccess({ database: DATABASE }); + const state = fromDatabase.databaseReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.databaseListIsLoading).toEqual(false); + expect(state.databaseListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get databaseListIsLoading', () => { + const action = {} as Action; + const state = fromDatabase.databaseReducer(undefined, action); + + expect(fromDatabase.selectDatabaseListIsLoading(state)).toEqual(false); + }); + + it('should get databaseListIsLoaded', () => { + const action = {} as Action; + const state = fromDatabase.databaseReducer(undefined, action); + + expect(fromDatabase.selectDatabaseListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/database.reducer.ts b/client/src/app/metamodel/reducers/database.reducer.ts index 045397b12d64d1e7d5c768783fe7e033064e60e6..d361230babec00ec9b86a676e39156de9f018a70 100644 --- a/client/src/app/metamodel/reducers/database.reducer.ts +++ b/client/src/app/metamodel/reducers/database.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { Database } from '../models'; import * as databaseActions from '../actions/database.actions'; +/** + * Interface for database state. + * + * @interface State + */ export interface State extends EntityState<Database> { databaseListIsLoading: boolean; databaseListIsLoaded: boolean; @@ -35,8 +40,8 @@ export const databaseReducer = createReducer( }), on(databaseActions.loadDatabaseListSuccess, (state, { databases }) => { return adapter.setAll( - databases, - { + databases, + { ...state, databaseListIsLoading: false, databaseListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/dataset-family.reducer.spec.ts b/client/src/app/metamodel/reducers/dataset-family.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..d0175fd1b444cd9f18996a1ae53ac0867be3fa94 --- /dev/null +++ b/client/src/app/metamodel/reducers/dataset-family.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromDatasetFamily from './dataset-family.reducer'; +import * as datasetFamilyActions from '../actions/dataset-family.actions'; +import { DATASET_FAMILY, DATASET_FAMILY_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] DatasetFamily reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromDatasetFamily; + const action = { type: 'Unknown' }; + const state = fromDatasetFamily.datasetFamilyReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadDatasetFamilyList action should set datasetFamilyListIsLoading to true', () => { + const { initialState } = fromDatasetFamily; + const action = datasetFamilyActions.loadDatasetFamilyList(); + const state = fromDatasetFamily.datasetFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.datasetFamilyListIsLoading).toEqual(true); + expect(state.datasetFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadDatasetFamilyListSuccess action should add datasetFamily list, set datasetFamilyListIsLoading to false and set datasetFamilyListIsLoaded to true', () => { + const { initialState } = fromDatasetFamily; + const action = datasetFamilyActions.loadDatasetFamilyListSuccess({ datasetFamilies: DATASET_FAMILY_LIST }); + const state = fromDatasetFamily.datasetFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain(1); + expect(state.ids).toContain(2); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.datasetFamilyListIsLoading).toEqual(false); + expect(state.datasetFamilyListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadDatasetFamilyListFail action should set datasetFamilyListIsLoading to false', () => { + const { initialState } = fromDatasetFamily; + const action = datasetFamilyActions.loadDatasetFamilyListFail(); + const state = fromDatasetFamily.datasetFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.datasetFamilyListIsLoading).toEqual(false); + expect(state.datasetFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addDatasetFamilySuccess action should add a datasetFamily', () => { + const { initialState } = fromDatasetFamily; + const action = datasetFamilyActions.addDatasetFamilySuccess({ datasetFamily: DATASET_FAMILY }); + const state = fromDatasetFamily.datasetFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.datasetFamilyListIsLoading).toEqual(false); + expect(state.datasetFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editDatasetFamilySuccess action should modify a datasetFamily', () => { + const initialState = { + ...fromDatasetFamily.initialState, + ids: [1], + entities: { 1: { ...DATASET_FAMILY, label: 'label' }} + }; + const action = datasetFamilyActions.editDatasetFamilySuccess({ datasetFamily: DATASET_FAMILY }); + const state = fromDatasetFamily.datasetFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities[1]).toEqual(DATASET_FAMILY); + expect(state.datasetFamilyListIsLoading).toEqual(false); + expect(state.datasetFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteDatasetFamilySuccess action should modify a datasetFamily', () => { + const initialState = { + ...fromDatasetFamily.initialState, + ids: [1], + entities: { 1: DATASET_FAMILY } + }; + const action = datasetFamilyActions.deleteDatasetFamilySuccess({ datasetFamily: DATASET_FAMILY }); + const state = fromDatasetFamily.datasetFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.datasetFamilyListIsLoading).toEqual(false); + expect(state.datasetFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get datasetFamilyListIsLoading', () => { + const action = {} as Action; + const state = fromDatasetFamily.datasetFamilyReducer(undefined, action); + + expect(fromDatasetFamily.selectDatasetFamilyListIsLoading(state)).toEqual(false); + }); + + it('should get datasetFamilyListIsLoaded', () => { + const action = {} as Action; + const state = fromDatasetFamily.datasetFamilyReducer(undefined, action); + + expect(fromDatasetFamily.selectDatasetFamilyListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/dataset-family.reducer.ts b/client/src/app/metamodel/reducers/dataset-family.reducer.ts index 923f88374e7b4211d4dd156554d7fa239bce8aa8..df8f9452ab6e0de8e384a7ab8724dde030b402f5 100644 --- a/client/src/app/metamodel/reducers/dataset-family.reducer.ts +++ b/client/src/app/metamodel/reducers/dataset-family.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { DatasetFamily } from '../models'; import * as datasetFamilyActions from '../actions/dataset-family.actions'; +/** + * Interface for dataset family state. + * + * @interface State + */ export interface State extends EntityState<DatasetFamily> { datasetFamilyListIsLoading: boolean; datasetFamilyListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const datasetFamilyReducer = createReducer( }), on(datasetFamilyActions.loadDatasetFamilyListSuccess, (state, { datasetFamilies }) => { return adapter.setAll( - datasetFamilies, - { + datasetFamilies, + { ...state, datasetFamilyListIsLoading: false, datasetFamilyListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/dataset.reducer.spec.ts b/client/src/app/metamodel/reducers/dataset.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9284176096f040ebaac4ecb99f735029de90c820 --- /dev/null +++ b/client/src/app/metamodel/reducers/dataset.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromDataset from './dataset.reducer'; +import * as datasetActions from '../actions/dataset.actions'; +import { DATASET, DATASET_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] Dataset reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromDataset; + const action = { type: 'Unknown' }; + const state = fromDataset.datasetReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadDatasetList action should set datasetListIsLoading to true', () => { + const { initialState } = fromDataset; + const action = datasetActions.loadDatasetList(); + const state = fromDataset.datasetReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.datasetListIsLoading).toEqual(true); + expect(state.datasetListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadDatasetListSuccess action should add dataset list, set datasetListIsLoading to false and set datasetListIsLoaded to true', () => { + const { initialState } = fromDataset; + const action = datasetActions.loadDatasetListSuccess({ datasets: DATASET_LIST }); + const state = fromDataset.datasetReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain('myDataset'); + expect(state.ids).toContain('anotherDataset'); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.datasetListIsLoading).toEqual(false); + expect(state.datasetListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadDatasetListFail action should set datasetListIsLoading to false', () => { + const { initialState } = fromDataset; + const action = datasetActions.loadDatasetListFail(); + const state = fromDataset.datasetReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.datasetListIsLoading).toEqual(false); + expect(state.datasetListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addDatasetSuccess action should add a dataset', () => { + const { initialState } = fromDataset; + const action = datasetActions.addDatasetSuccess({ dataset: DATASET }); + const state = fromDataset.datasetReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain('myDataset'); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.datasetListIsLoading).toEqual(false); + expect(state.datasetListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editDatasetSuccess action should modify a dataset', () => { + const initialState = { + ...fromDataset.initialState, + ids: ['myDataset'], + entities: { 'myDataset': { ...DATASET, label: 'label' }} + }; + const action = datasetActions.editDatasetSuccess({ dataset: DATASET }); + const state = fromDataset.datasetReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain('myDataset'); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities['myDataset']).toEqual(DATASET); + expect(state.datasetListIsLoading).toEqual(false); + expect(state.datasetListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteDatasetSuccess action should modify a dataset', () => { + const initialState = { + ...fromDataset.initialState, + ids: ['myDataset'], + entities: { 'myDataset': DATASET } + }; + const action = datasetActions.deleteDatasetSuccess({ dataset: DATASET }); + const state = fromDataset.datasetReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.datasetListIsLoading).toEqual(false); + expect(state.datasetListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get datasetListIsLoading', () => { + const action = {} as Action; + const state = fromDataset.datasetReducer(undefined, action); + + expect(fromDataset.selectDatasetListIsLoading(state)).toEqual(false); + }); + + it('should get datasetListIsLoaded', () => { + const action = {} as Action; + const state = fromDataset.datasetReducer(undefined, action); + + expect(fromDataset.selectDatasetListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/dataset.reducer.ts b/client/src/app/metamodel/reducers/dataset.reducer.ts index becb9691e69c08353c027083fad1b096dcc8f522..013feea1abd912d6a2a9aac6c9fe3c4dbe78c450 100644 --- a/client/src/app/metamodel/reducers/dataset.reducer.ts +++ b/client/src/app/metamodel/reducers/dataset.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { Dataset } from '../models'; import * as datasetActions from '../actions/dataset.actions'; +/** + * Interface for dataset state. + * + * @interface State + */ export interface State extends EntityState<Dataset> { datasetListIsLoading: boolean; datasetListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const datasetReducer = createReducer( }), on(datasetActions.loadDatasetListSuccess, (state, { datasets }) => { return adapter.setAll( - datasets, - { + datasets, + { ...state, datasetListIsLoading: false, datasetListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/group.reducer.spec.ts b/client/src/app/metamodel/reducers/group.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..354425566c71e03b08fbf0c9b65491842526ef4a --- /dev/null +++ b/client/src/app/metamodel/reducers/group.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromGroup from './group.reducer'; +import * as groupActions from '../actions/group.actions'; +import { GROUP, GROUP_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] Group reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromGroup; + const action = { type: 'Unknown' }; + const state = fromGroup.groupReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadGroupList action should set groupListIsLoading to true', () => { + const { initialState } = fromGroup; + const action = groupActions.loadGroupList(); + const state = fromGroup.groupReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.groupListIsLoading).toEqual(true); + expect(state.groupListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadGroupListSuccess action should add group list, set groupListIsLoading to false and set groupListIsLoaded to true', () => { + const { initialState } = fromGroup; + const action = groupActions.loadGroupListSuccess({ groups: GROUP_LIST }); + const state = fromGroup.groupReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain(1); + expect(state.ids).toContain(2); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.groupListIsLoading).toEqual(false); + expect(state.groupListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadGroupListFail action should set groupListIsLoading to false', () => { + const { initialState } = fromGroup; + const action = groupActions.loadGroupListFail(); + const state = fromGroup.groupReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.groupListIsLoading).toEqual(false); + expect(state.groupListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addGroupSuccess action should add a group', () => { + const { initialState } = fromGroup; + const action = groupActions.addGroupSuccess({ group: GROUP }); + const state = fromGroup.groupReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.groupListIsLoading).toEqual(false); + expect(state.groupListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editGroupSuccess action should modify a group', () => { + const initialState = { + ...fromGroup.initialState, + ids: [1], + entities: { 1: { ...GROUP, role: 'role' }} + }; + const action = groupActions.editGroupSuccess({ group: GROUP }); + const state = fromGroup.groupReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities[1]).toEqual(GROUP); + expect(state.groupListIsLoading).toEqual(false); + expect(state.groupListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteGroupSuccess action should modify a group', () => { + const initialState = { + ...fromGroup.initialState, + ids: [1], + entities: { 1: GROUP } + }; + const action = groupActions.deleteGroupSuccess({ group: GROUP }); + const state = fromGroup.groupReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.groupListIsLoading).toEqual(false); + expect(state.groupListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get groupListIsLoading', () => { + const action = {} as Action; + const state = fromGroup.groupReducer(undefined, action); + + expect(fromGroup.selectGroupListIsLoading(state)).toEqual(false); + }); + + it('should get groupListIsLoaded', () => { + const action = {} as Action; + const state = fromGroup.groupReducer(undefined, action); + + expect(fromGroup.selectGroupListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/group.reducer.ts b/client/src/app/metamodel/reducers/group.reducer.ts index ad3c3f4343c53db72b4880b56662315308c6af97..3037765737375444edf3397c598de886dec72b4c 100644 --- a/client/src/app/metamodel/reducers/group.reducer.ts +++ b/client/src/app/metamodel/reducers/group.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { Group } from '../models'; import * as groupActions from '../actions/group.actions'; +/** + * Interface for group state. + * + * @interface State + */ export interface State extends EntityState<Group> { groupListIsLoading: boolean; groupListIsLoaded: boolean; @@ -35,8 +40,8 @@ export const groupReducer = createReducer( }), on(groupActions.loadGroupListSuccess, (state, { groups }) => { return adapter.setAll( - groups, - { + groups, + { ...state, groupListIsLoading: false, groupListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/instance.reducer.spec.ts b/client/src/app/metamodel/reducers/instance.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..4e13bdc69f4257b10f48bd884136dd75991e6bb9 --- /dev/null +++ b/client/src/app/metamodel/reducers/instance.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromInstance from './instance.reducer'; +import * as instanceActions from '../actions/instance.actions'; +import { INSTANCE, INSTANCE_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] Instance reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromInstance; + const action = { type: 'Unknown' }; + const state = fromInstance.instanceReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadInstanceList action should set instanceListIsLoading to true', () => { + const { initialState } = fromInstance; + const action = instanceActions.loadInstanceList(); + const state = fromInstance.instanceReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.instanceListIsLoading).toEqual(true); + expect(state.instanceListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadInstanceListSuccess action should add instance list, set instanceListIsLoading to false and set instanceListIsLoaded to true', () => { + const { initialState } = fromInstance; + const action = instanceActions.loadInstanceListSuccess({ instances: INSTANCE_LIST }); + const state = fromInstance.instanceReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain('myInstance'); + expect(state.ids).toContain('myOtherInstance'); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.instanceListIsLoading).toEqual(false); + expect(state.instanceListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadInstanceListFail action should set instanceListIsLoading to false', () => { + const { initialState } = fromInstance; + const action = instanceActions.loadInstanceListFail(); + const state = fromInstance.instanceReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.instanceListIsLoading).toEqual(false); + expect(state.instanceListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addInstanceSuccess action should add a instance', () => { + const { initialState } = fromInstance; + const action = instanceActions.addInstanceSuccess({ instance: INSTANCE }); + const state = fromInstance.instanceReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain('myInstance'); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.instanceListIsLoading).toEqual(false); + expect(state.instanceListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editInstanceSuccess action should modify a instance', () => { + const initialState = { + ...fromInstance.initialState, + ids: ['myInstance'], + entities: { 'myInstance': { ...INSTANCE, label: 'label' }} + }; + const action = instanceActions.editInstanceSuccess({ instance: INSTANCE }); + const state = fromInstance.instanceReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain('myInstance'); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities['myInstance']).toEqual(INSTANCE); + expect(state.instanceListIsLoading).toEqual(false); + expect(state.instanceListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteInstanceSuccess action should modify a instance', () => { + const initialState = { + ...fromInstance.initialState, + ids: ['myInstance'], + entities: { 'myInstance': INSTANCE } + }; + const action = instanceActions.deleteInstanceSuccess({ instance: INSTANCE }); + const state = fromInstance.instanceReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.instanceListIsLoading).toEqual(false); + expect(state.instanceListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get instanceListIsLoading', () => { + const action = {} as Action; + const state = fromInstance.instanceReducer(undefined, action); + + expect(fromInstance.selectInstanceListIsLoading(state)).toEqual(false); + }); + + it('should get instanceListIsLoaded', () => { + const action = {} as Action; + const state = fromInstance.instanceReducer(undefined, action); + + expect(fromInstance.selectInstanceListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/instance.reducer.ts b/client/src/app/metamodel/reducers/instance.reducer.ts index 33a282126917158df332a246bae615276f47dc17..e9f454ef2dae9be6d92023c64e4a30ef7ac6baf0 100644 --- a/client/src/app/metamodel/reducers/instance.reducer.ts +++ b/client/src/app/metamodel/reducers/instance.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { Instance } from '../models'; import * as instanceActions from '../actions/instance.actions'; +/** + * Interface for instance state. + * + * @interface State + */ export interface State extends EntityState<Instance> { instanceListIsLoading: boolean; instanceListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const instanceReducer = createReducer( }), on(instanceActions.loadInstanceListSuccess, (state, { instances }) => { return adapter.setAll( - instances, - { + instances, + { ...state, instanceListIsLoading: false, instanceListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/output-category.reducer.spec.ts b/client/src/app/metamodel/reducers/output-category.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..5af1ebc777ffbcf3b7278efd7793b2370e7d8262 --- /dev/null +++ b/client/src/app/metamodel/reducers/output-category.reducer.spec.ts @@ -0,0 +1,108 @@ +import { Action } from '@ngrx/store'; + +import * as fromOutputCategory from './output-category.reducer'; +import * as outputCategoryActions from '../actions/output-category.actions'; +import { CATEGORY, CATEGORY_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] OutputCategory reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromOutputCategory; + const action = { type: 'Unknown' }; + const state = fromOutputCategory.outputCategoryReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadOutputCategoryList action should set outputCategoryListIsLoading to true', () => { + const { initialState } = fromOutputCategory; + const action = outputCategoryActions.loadOutputCategoryList(); + const state = fromOutputCategory.outputCategoryReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.outputCategoryListIsLoading).toEqual(true); + expect(state.outputCategoryListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadOutputCategoryListSuccess action should add outputCategory list, set outputCategoryListIsLoading to false and set outputCategoryListIsLoaded to true', () => { + const { initialState } = fromOutputCategory; + const action = outputCategoryActions.loadOutputCategoryListSuccess({ outputCategories: CATEGORY_LIST }); + const state = fromOutputCategory.outputCategoryReducer(initialState, action); + expect(state.ids.length).toEqual(3); + expect(state.ids).toContain(1); + expect(state.ids).toContain(2); + expect(state.ids).toContain(3); + expect(Object.keys(state.entities).length).toEqual(3); + expect(state.outputCategoryListIsLoading).toEqual(false); + expect(state.outputCategoryListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadOutputCategoryListFail action should set outputCategoryListIsLoading to false', () => { + const { initialState } = fromOutputCategory; + const action = outputCategoryActions.loadOutputCategoryListFail(); + const state = fromOutputCategory.outputCategoryReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.outputCategoryListIsLoading).toEqual(false); + expect(state.outputCategoryListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addOutputCategorySuccess action should add a outputCategory', () => { + const { initialState } = fromOutputCategory; + const action = outputCategoryActions.addOutputCategorySuccess({ outputCategory: CATEGORY }); + const state = fromOutputCategory.outputCategoryReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.outputCategoryListIsLoading).toEqual(false); + expect(state.outputCategoryListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editOutputCategorySuccess action should modify a outputCategory', () => { + const initialState = { + ...fromOutputCategory.initialState, + ids: [1], + entities: { 1: { ...CATEGORY, label: 'label' }} + }; + const action = outputCategoryActions.editOutputCategorySuccess({ outputCategory: CATEGORY }); + const state = fromOutputCategory.outputCategoryReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities[1]).toEqual(CATEGORY); + expect(state.outputCategoryListIsLoading).toEqual(false); + expect(state.outputCategoryListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteOutputCategorySuccess action should modify a outputCategory', () => { + const initialState = { + ...fromOutputCategory.initialState, + ids: [1], + entities: { 1: CATEGORY } + }; + const action = outputCategoryActions.deleteOutputCategorySuccess({ outputCategory: CATEGORY }); + const state = fromOutputCategory.outputCategoryReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.outputCategoryListIsLoading).toEqual(false); + expect(state.outputCategoryListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get outputCategoryListIsLoading', () => { + const action = {} as Action; + const state = fromOutputCategory.outputCategoryReducer(undefined, action); + + expect(fromOutputCategory.selectOutputCategoryListIsLoading(state)).toEqual(false); + }); + + it('should get outputCategoryListIsLoaded', () => { + const action = {} as Action; + const state = fromOutputCategory.outputCategoryReducer(undefined, action); + + expect(fromOutputCategory.selectOutputCategoryListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/output-category.reducer.ts b/client/src/app/metamodel/reducers/output-category.reducer.ts index ddbed47415434fee7336a5e45078c8557d1b956e..437058cbeca194d5dcbf83789d8913ba0417ecfe 100644 --- a/client/src/app/metamodel/reducers/output-category.reducer.ts +++ b/client/src/app/metamodel/reducers/output-category.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { OutputCategory } from '../models'; import * as outputCategoryActions from '../actions/output-category.actions'; +/** + * Interface for output category state. + * + * @interface State + */ export interface State extends EntityState<OutputCategory> { outputCategoryListIsLoading: boolean; outputCategoryListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const outputCategoryReducer = createReducer( }), on(outputCategoryActions.loadOutputCategoryListSuccess, (state, { outputCategories }) => { return adapter.setAll( - outputCategories, - { + outputCategories, + { ...state, outputCategoryListIsLoading: false, outputCategoryListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/output-family.reducer.spec.ts b/client/src/app/metamodel/reducers/output-family.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..7e2397d9aa30d2a6b848b4104ba339e906ca0c39 --- /dev/null +++ b/client/src/app/metamodel/reducers/output-family.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromOutputFamily from './output-family.reducer'; +import * as outputFamilyActions from '../actions/output-family.actions'; +import { OUTPUT_FAMILY, OUTPUT_FAMILY_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] OutputFamily reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromOutputFamily; + const action = { type: 'Unknown' }; + const state = fromOutputFamily.outputFamilyReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadOutputFamilyList action should set outputFamilyListIsLoading to true', () => { + const { initialState } = fromOutputFamily; + const action = outputFamilyActions.loadOutputFamilyList(); + const state = fromOutputFamily.outputFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.outputFamilyListIsLoading).toEqual(true); + expect(state.outputFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadOutputFamilyListSuccess action should add outputFamily list, set outputFamilyListIsLoading to false and set outputFamilyListIsLoaded to true', () => { + const { initialState } = fromOutputFamily; + const action = outputFamilyActions.loadOutputFamilyListSuccess({ outputFamilies: OUTPUT_FAMILY_LIST }); + const state = fromOutputFamily.outputFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain(1); + expect(state.ids).toContain(2); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.outputFamilyListIsLoading).toEqual(false); + expect(state.outputFamilyListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadOutputFamilyListFail action should set outputFamilyListIsLoading to false', () => { + const { initialState } = fromOutputFamily; + const action = outputFamilyActions.loadOutputFamilyListFail(); + const state = fromOutputFamily.outputFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.outputFamilyListIsLoading).toEqual(false); + expect(state.outputFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addOutputFamilySuccess action should add a outputFamily', () => { + const { initialState } = fromOutputFamily; + const action = outputFamilyActions.addOutputFamilySuccess({ outputFamily: OUTPUT_FAMILY }); + const state = fromOutputFamily.outputFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.outputFamilyListIsLoading).toEqual(false); + expect(state.outputFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editOutputFamilySuccess action should modify a outputFamily', () => { + const initialState = { + ...fromOutputFamily.initialState, + ids: [1], + entities: { 1: { ...OUTPUT_FAMILY, label: 'label' }} + }; + const action = outputFamilyActions.editOutputFamilySuccess({ outputFamily: OUTPUT_FAMILY }); + const state = fromOutputFamily.outputFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities[1]).toEqual(OUTPUT_FAMILY); + expect(state.outputFamilyListIsLoading).toEqual(false); + expect(state.outputFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteOutputFamilySuccess action should modify a outputFamily', () => { + const initialState = { + ...fromOutputFamily.initialState, + ids: [1], + entities: { 1: OUTPUT_FAMILY } + }; + const action = outputFamilyActions.deleteOutputFamilySuccess({ outputFamily: OUTPUT_FAMILY }); + const state = fromOutputFamily.outputFamilyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.outputFamilyListIsLoading).toEqual(false); + expect(state.outputFamilyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get outputFamilyListIsLoading', () => { + const action = {} as Action; + const state = fromOutputFamily.outputFamilyReducer(undefined, action); + + expect(fromOutputFamily.selectOutputFamilyListIsLoading(state)).toEqual(false); + }); + + it('should get outputFamilyListIsLoaded', () => { + const action = {} as Action; + const state = fromOutputFamily.outputFamilyReducer(undefined, action); + + expect(fromOutputFamily.selectOutputFamilyListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/output-family.reducer.ts b/client/src/app/metamodel/reducers/output-family.reducer.ts index cb7c5f6b38320c69a2542149313eaae47467d41a..93fffd5cad3a09c6a52bced1a3885c59ab7f8245 100644 --- a/client/src/app/metamodel/reducers/output-family.reducer.ts +++ b/client/src/app/metamodel/reducers/output-family.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { OutputFamily } from '../models'; import * as outputFamilyActions from '../actions/output-family.actions'; +/** + * Interface for output family state. + * + * @interface State + */ export interface State extends EntityState<OutputFamily> { outputFamilyListIsLoading: boolean; outputFamilyListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const outputFamilyReducer = createReducer( }), on(outputFamilyActions.loadOutputFamilyListSuccess, (state, { outputFamilies }) => { return adapter.setAll( - outputFamilies, - { + outputFamilies, + { ...state, outputFamilyListIsLoading: false, outputFamilyListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/root-directory.reducer.spec.ts b/client/src/app/metamodel/reducers/root-directory.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..942dcd439222fccc6ad85e3725e9cebe70b442c4 --- /dev/null +++ b/client/src/app/metamodel/reducers/root-directory.reducer.spec.ts @@ -0,0 +1,63 @@ +import { Action } from '@ngrx/store'; + +import * as fromRootDirectory from './root-directory.reducer'; +import * as rootDirectoryActions from '../actions/root-directory.actions'; +import { FILES } from '../../../test-data'; + +describe('[Metamodel][Reducers] RootDirectory reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromRootDirectory; + const action = { type: 'Unknown' }; + const state = fromRootDirectory.rootDirectoryReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadRootDirectory action should set rootDirectoryIsLoading to true', () => { + const { initialState } = fromRootDirectory; + const action = rootDirectoryActions.loadRootDirectory({ path: 'path' }); + const state = fromRootDirectory.rootDirectoryReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.rootDirectoryIsLoading).toEqual(true); + expect(state.rootDirectoryIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadRootDirectorySuccess action should add files, set rootDirectoryIsLoading to false and set rootDirectoryIsLoaded to true', () => { + const { initialState } = fromRootDirectory; + const action = rootDirectoryActions.loadRootDirectorySuccess({ files: FILES }); + const state = fromRootDirectory.rootDirectoryReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain('file-one'); + expect(state.ids).toContain('file-two'); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.rootDirectoryIsLoading).toEqual(false); + expect(state.rootDirectoryIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadRootDirectoryFail action should set rootDirectoryIsLoading to false', () => { + const { initialState } = fromRootDirectory; + const action = rootDirectoryActions.loadRootDirectoryFail(); + const state = fromRootDirectory.rootDirectoryReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.rootDirectoryIsLoading).toEqual(false); + expect(state.rootDirectoryIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get rootDirectoryIsLoading', () => { + const action = {} as Action; + const state = fromRootDirectory.rootDirectoryReducer(undefined, action); + + expect(fromRootDirectory.selectRootDirectoryIsLoading(state)).toEqual(false); + }); + + it('should get rootDirectoryIsLoaded', () => { + const action = {} as Action; + const state = fromRootDirectory.rootDirectoryReducer(undefined, action); + + expect(fromRootDirectory.selectRootDirectoryIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/root-directory.reducer.ts b/client/src/app/metamodel/reducers/root-directory.reducer.ts index d8d7082f7943a134b33a7c0f64a4cd2ba01b9f3e..efab0b9d840fc4bb35267e4a32b820987b221bea 100644 --- a/client/src/app/metamodel/reducers/root-directory.reducer.ts +++ b/client/src/app/metamodel/reducers/root-directory.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import * as rootDirectoryActions from '../actions/root-directory.actions'; import { FileInfo } from '../models'; +/** + * Interface for root directory state. + * + * @interface State + */ export interface State extends EntityState<FileInfo> { rootDirectoryIsLoading: boolean; rootDirectoryIsLoaded: boolean; @@ -39,8 +44,8 @@ export const rootDirectoryReducer = createReducer( }), on(rootDirectoryActions.loadRootDirectorySuccess, (state, { files }) => { return adapter.setAll( - files, - { + files, + { ...state, rootDirectoryIsLoading: false, rootDirectoryIsLoaded: true diff --git a/client/src/app/metamodel/reducers/select-option.reducer.spec.ts b/client/src/app/metamodel/reducers/select-option.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..000a74966ba13f3292949a801719dc165431b6b8 --- /dev/null +++ b/client/src/app/metamodel/reducers/select-option.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromSelectOption from './select-option.reducer'; +import * as selectOptionActions from '../actions/select-option.actions'; +import { SELECT_OPTION, SELECT_OPTION_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] SelectOption reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromSelectOption; + const action = { type: 'Unknown' }; + const state = fromSelectOption.selectOptionReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadSelectOptionList action should set selectOptionListIsLoading to true', () => { + const { initialState } = fromSelectOption; + const action = selectOptionActions.loadSelectOptionList(); + const state = fromSelectOption.selectOptionReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.selectOptionListIsLoading).toEqual(true); + expect(state.selectOptionListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadSelectOptionListSuccess action should add selectOption list, set selectOptionListIsLoading to false and set selectOptionListIsLoaded to true', () => { + const { initialState } = fromSelectOption; + const action = selectOptionActions.loadSelectOptionListSuccess({ selectOptions: SELECT_OPTION_LIST }); + const state = fromSelectOption.selectOptionReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain(1); + expect(state.ids).toContain(2); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.selectOptionListIsLoading).toEqual(false); + expect(state.selectOptionListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadSelectOptionListFail action should set selectOptionListIsLoading to false', () => { + const { initialState } = fromSelectOption; + const action = selectOptionActions.loadSelectOptionListFail(); + const state = fromSelectOption.selectOptionReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.selectOptionListIsLoading).toEqual(false); + expect(state.selectOptionListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addSelectOptionSuccess action should add a selectOption', () => { + const { initialState } = fromSelectOption; + const action = selectOptionActions.addSelectOptionSuccess({ selectOption: SELECT_OPTION }); + const state = fromSelectOption.selectOptionReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.selectOptionListIsLoading).toEqual(false); + expect(state.selectOptionListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editSelectOptionSuccess action should modify a selectOption', () => { + const initialState = { + ...fromSelectOption.initialState, + ids: [1], + entities: { 1: { ...SELECT_OPTION, label: 'label' }} + }; + const action = selectOptionActions.editSelectOptionSuccess({ selectOption: SELECT_OPTION }); + const state = fromSelectOption.selectOptionReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain(1); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities[1]).toEqual(SELECT_OPTION); + expect(state.selectOptionListIsLoading).toEqual(false); + expect(state.selectOptionListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteSelectOptionSuccess action should modify a selectOption', () => { + const initialState = { + ...fromSelectOption.initialState, + ids: [1], + entities: { 1: SELECT_OPTION } + }; + const action = selectOptionActions.deleteSelectOptionSuccess({ selectOption: SELECT_OPTION }); + const state = fromSelectOption.selectOptionReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.selectOptionListIsLoading).toEqual(false); + expect(state.selectOptionListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get selectOptionListIsLoading', () => { + const action = {} as Action; + const state = fromSelectOption.selectOptionReducer(undefined, action); + + expect(fromSelectOption.selectSelectOptionListIsLoading(state)).toEqual(false); + }); + + it('should get selectOptionListIsLoaded', () => { + const action = {} as Action; + const state = fromSelectOption.selectOptionReducer(undefined, action); + + expect(fromSelectOption.selectSelectOptionListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/select-option.reducer.ts b/client/src/app/metamodel/reducers/select-option.reducer.ts index f63ffbd5c6f5c2cdc84386301784731bd29fb681..f6468561d435d3f003482acd019e685171be1f65 100644 --- a/client/src/app/metamodel/reducers/select-option.reducer.ts +++ b/client/src/app/metamodel/reducers/select-option.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { SelectOption } from '../models'; import * as selectOptionActions from '../actions/select-option.actions'; +/** + * Interface for select option state. + * + * @interface State + */ export interface State extends EntityState<SelectOption> { selectOptionListIsLoading: boolean; selectOptionListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const selectOptionReducer = createReducer( }), on(selectOptionActions.loadSelectOptionListSuccess, (state, { selectOptions }) => { return adapter.setAll( - selectOptions, - { + selectOptions, + { ...state, selectOptionListIsLoading: false, selectOptionListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/select.reducer.spec.ts b/client/src/app/metamodel/reducers/select.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..2e21a6f55ffd4e830d4a118dd2ec666340400457 --- /dev/null +++ b/client/src/app/metamodel/reducers/select.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromSelect from './select.reducer'; +import * as selectActions from '../actions/select.actions'; +import { SELECT, SELECT_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] Select reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromSelect; + const action = { type: 'Unknown' }; + const state = fromSelect.selectReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadSelectList action should set selectListIsLoading to true', () => { + const { initialState } = fromSelect; + const action = selectActions.loadSelectList(); + const state = fromSelect.selectReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.selectListIsLoading).toEqual(true); + expect(state.selectListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadSelectListSuccess action should add select list, set selectListIsLoading to false and set selectListIsLoaded to true', () => { + const { initialState } = fromSelect; + const action = selectActions.loadSelectListSuccess({ selects: SELECT_LIST }); + const state = fromSelect.selectReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain('select-one'); + expect(state.ids).toContain('select-two'); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.selectListIsLoading).toEqual(false); + expect(state.selectListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadSelectListFail action should set selectListIsLoading to false', () => { + const { initialState } = fromSelect; + const action = selectActions.loadSelectListFail(); + const state = fromSelect.selectReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.selectListIsLoading).toEqual(false); + expect(state.selectListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addSelectSuccess action should add a select', () => { + const { initialState } = fromSelect; + const action = selectActions.addSelectSuccess({ select: SELECT }); + const state = fromSelect.selectReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain('mySelect'); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.selectListIsLoading).toEqual(false); + expect(state.selectListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editSelectSuccess action should modify a select', () => { + const initialState = { + ...fromSelect.initialState, + ids: ['mySelect'], + entities: { 'mySelect': { ...SELECT, label: 'label' }} + }; + const action = selectActions.editSelectSuccess({ select: SELECT }); + const state = fromSelect.selectReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain('mySelect'); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities['mySelect']).toEqual(SELECT); + expect(state.selectListIsLoading).toEqual(false); + expect(state.selectListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteSelectSuccess action should modify a select', () => { + const initialState = { + ...fromSelect.initialState, + ids: ['mySelect'], + entities: { 'mySelect': SELECT } + }; + const action = selectActions.deleteSelectSuccess({ select: SELECT }); + const state = fromSelect.selectReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.selectListIsLoading).toEqual(false); + expect(state.selectListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get selectListIsLoading', () => { + const action = {} as Action; + const state = fromSelect.selectReducer(undefined, action); + + expect(fromSelect.selectSelectListIsLoading(state)).toEqual(false); + }); + + it('should get selectListIsLoaded', () => { + const action = {} as Action; + const state = fromSelect.selectReducer(undefined, action); + + expect(fromSelect.selectSelectListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/select.reducer.ts b/client/src/app/metamodel/reducers/select.reducer.ts index f12df3a564e146151601885be5cc0e117d8641c6..942f69e83a343c42f3d9bd1538f88921e447db47 100644 --- a/client/src/app/metamodel/reducers/select.reducer.ts +++ b/client/src/app/metamodel/reducers/select.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { Select } from '../models'; import * as selectActions from '../actions/select.actions'; +/** + * Interface for select state. + * + * @interface State + */ export interface State extends EntityState<Select> { selectListIsLoading: boolean; selectListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const selectReducer = createReducer( }), on(selectActions.loadSelectListSuccess, (state, { selects }) => { return adapter.setAll( - selects, - { + selects, + { ...state, selectListIsLoading: false, selectListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/survey.reducer.spec.ts b/client/src/app/metamodel/reducers/survey.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..2e54b0f7716c60d2736bdfaf3d68e4043832fd9a --- /dev/null +++ b/client/src/app/metamodel/reducers/survey.reducer.spec.ts @@ -0,0 +1,107 @@ +import { Action } from '@ngrx/store'; + +import * as fromSurvey from './survey.reducer'; +import * as surveyActions from '../actions/survey.actions'; +import { SURVEY, SURVEY_LIST } from '../../../test-data'; + +describe('[Metamodel][Reducers] Survey reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromSurvey; + const action = { type: 'Unknown' }; + const state = fromSurvey.surveyReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadSurveyList action should set surveyListIsLoading to true', () => { + const { initialState } = fromSurvey; + const action = surveyActions.loadSurveyList(); + const state = fromSurvey.surveyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.surveyListIsLoading).toEqual(true); + expect(state.surveyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadSurveyListSuccess action should add survey list, set surveyListIsLoading to false and set surveyListIsLoaded to true', () => { + const { initialState } = fromSurvey; + const action = surveyActions.loadSurveyListSuccess({ surveys: SURVEY_LIST }); + const state = fromSurvey.surveyReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain('survey-one'); + expect(state.ids).toContain('survey-two'); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.surveyListIsLoading).toEqual(false); + expect(state.surveyListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadSurveyListFail action should set surveyListIsLoading to false', () => { + const { initialState } = fromSurvey; + const action = surveyActions.loadSurveyListFail(); + const state = fromSurvey.surveyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.surveyListIsLoading).toEqual(false); + expect(state.surveyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('addSurveySuccess action should add a survey', () => { + const { initialState } = fromSurvey; + const action = surveyActions.addSurveySuccess({ survey: SURVEY }); + const state = fromSurvey.surveyReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain('mySurvey'); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.surveyListIsLoading).toEqual(false); + expect(state.surveyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('editSurveySuccess action should modify a survey', () => { + const initialState = { + ...fromSurvey.initialState, + ids: ['mySurvey'], + entities: { 'mySurvey': { ...SURVEY, label: 'label' }} + }; + const action = surveyActions.editSurveySuccess({ survey: SURVEY }); + const state = fromSurvey.surveyReducer(initialState, action); + expect(state.ids.length).toEqual(1); + expect(state.ids).toContain('mySurvey'); + expect(Object.keys(state.entities).length).toEqual(1); + expect(state.entities['mySurvey']).toEqual(SURVEY); + expect(state.surveyListIsLoading).toEqual(false); + expect(state.surveyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('deleteSurveySuccess action should modify a survey', () => { + const initialState = { + ...fromSurvey.initialState, + ids: ['mySurvey'], + entities: { 'mySurvey': SURVEY } + }; + const action = surveyActions.deleteSurveySuccess({ survey: SURVEY }); + const state = fromSurvey.surveyReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual( { }); + expect(state.surveyListIsLoading).toEqual(false); + expect(state.surveyListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get surveyListIsLoading', () => { + const action = {} as Action; + const state = fromSurvey.surveyReducer(undefined, action); + + expect(fromSurvey.selectSurveyListIsLoading(state)).toEqual(false); + }); + + it('should get surveyListIsLoaded', () => { + const action = {} as Action; + const state = fromSurvey.surveyReducer(undefined, action); + + expect(fromSurvey.selectSurveyListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/survey.reducer.ts b/client/src/app/metamodel/reducers/survey.reducer.ts index 610cd25423491752c6f23b5f8b5866a659bdc578..7514b7b3948785c2e5edb04553f0e817a0e2cedb 100644 --- a/client/src/app/metamodel/reducers/survey.reducer.ts +++ b/client/src/app/metamodel/reducers/survey.reducer.ts @@ -13,6 +13,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import { Survey } from '../models'; import * as surveyActions from '../actions/survey.actions'; +/** + * Interface for survey state. + * + * @interface State + */ export interface State extends EntityState<Survey> { surveyListIsLoading: boolean; surveyListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const surveyReducer = createReducer( }), on(surveyActions.loadSurveyListSuccess, (state, { surveys }) => { return adapter.setAll( - surveys, - { + surveys, + { ...state, surveyListIsLoading: false, surveyListIsLoaded: true diff --git a/client/src/app/metamodel/reducers/table.reducer.spec.ts b/client/src/app/metamodel/reducers/table.reducer.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9aff817fa8e807d7cff40cda6d4e6e9947958b86 --- /dev/null +++ b/client/src/app/metamodel/reducers/table.reducer.spec.ts @@ -0,0 +1,62 @@ +import { Action } from '@ngrx/store'; + +import * as fromTable from './table.reducer'; +import * as tableActions from '../actions/table.actions'; + +describe('[Metamodel][Reducers] Table reducer', () => { + it('unknown action should return the default state', () => { + const { initialState } = fromTable; + const action = { type: 'Unknown' }; + const state = fromTable.tableReducer(initialState, action); + expect(state).toBe(initialState); + }); + + it('loadTableList action should set tableListIsLoading to true', () => { + const { initialState } = fromTable; + const action = tableActions.loadTableList({ idDatabase: 1 }); + const state = fromTable.tableReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.tableListIsLoading).toEqual(true); + expect(state.tableListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('loadTableListSuccess action should add table list, set tableListIsLoading to false and set tableListIsLoaded to true', () => { + const { initialState } = fromTable; + const action = tableActions.loadTableListSuccess({ tables: ['table-one', 'table-two'] }); + const state = fromTable.tableReducer(initialState, action); + expect(state.ids.length).toEqual(2); + expect(state.ids).toContain('table-one'); + expect(state.ids).toContain('table-two'); + expect(Object.keys(state.entities).length).toEqual(2); + expect(state.tableListIsLoading).toEqual(false); + expect(state.tableListIsLoaded).toEqual(true); + expect(state).not.toBe(initialState); + }); + + it('loadTableListFail action should set tableListIsLoading to false', () => { + const { initialState } = fromTable; + const action = tableActions.loadTableListFail(); + const state = fromTable.tableReducer(initialState, action); + expect(state.ids.length).toEqual(0); + expect(state.entities).toEqual({ }); + expect(state.tableListIsLoading).toEqual(false); + expect(state.tableListIsLoaded).toEqual(false); + expect(state).not.toBe(initialState); + }); + + it('should get tableListIsLoading', () => { + const action = {} as Action; + const state = fromTable.tableReducer(undefined, action); + + expect(fromTable.selectTableListIsLoading(state)).toEqual(false); + }); + + it('should get tableListIsLoaded', () => { + const action = {} as Action; + const state = fromTable.tableReducer(undefined, action); + + expect(fromTable.selectTableListIsLoaded(state)).toEqual(false); + }); +}); diff --git a/client/src/app/metamodel/reducers/table.reducer.ts b/client/src/app/metamodel/reducers/table.reducer.ts index 24acb6e6c7aedfdd20c187a1e9ef784adec331cb..5b5f369bbc4cfff2e739f8a5363ffc4d2f41fc51 100644 --- a/client/src/app/metamodel/reducers/table.reducer.ts +++ b/client/src/app/metamodel/reducers/table.reducer.ts @@ -12,6 +12,11 @@ import { EntityState, EntityAdapter, createEntityAdapter } from '@ngrx/entity'; import * as tableActions from '../actions/table.actions'; +/** + * Interface for table state. + * + * @interface State + */ export interface State extends EntityState<string> { tableListIsLoading: boolean; tableListIsLoaded: boolean; @@ -38,8 +43,8 @@ export const tableReducer = createReducer( }), on(tableActions.loadTableListSuccess, (state, { tables }) => { return adapter.setAll( - tables, - { + tables, + { ...state, tableListIsLoading: false, tableListIsLoaded: true diff --git a/client/src/app/metamodel/selectors/attribute-distinct.selector.spec.ts b/client/src/app/metamodel/selectors/attribute-distinct.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..27fd525451e783a1c17893b5ac8e57e81fbe73c8 --- /dev/null +++ b/client/src/app/metamodel/selectors/attribute-distinct.selector.spec.ts @@ -0,0 +1,39 @@ +import * as attributeDistinctSelector from './attribute-distinct.selector'; +import * as fromAttributeDistinct from '../reducers/attribute-distinct.reducer'; + +describe('[Metamodel][Selector] AttributeDistinct selector', () => { + it('should get attributeDistinct state', () => { + const state = { metamodel: { attributeDistinct: { ...fromAttributeDistinct.initialState }}}; + expect(attributeDistinctSelector.selectAttributeDistinctState(state)).toEqual(state.metamodel.attributeDistinct); + }); + + it('should get attributeDistinct IDs', () => { + const state = { metamodel: { attributeDistinct: { ...fromAttributeDistinct.initialState }}}; + expect(attributeDistinctSelector.selectAttributeDistinctIds(state).length).toEqual(0); + }); + + it('should get attributeDistinct entities', () => { + const state = { metamodel: { attributeDistinct: { ...fromAttributeDistinct.initialState }}}; + expect(attributeDistinctSelector.selectAttributeDistinctEntities(state)).toEqual({ }); + }); + + it('should get all attributeDistincts', () => { + const state = { metamodel: { attributeDistinct: { ...fromAttributeDistinct.initialState }}}; + expect(attributeDistinctSelector.selectAllAttributeDistincts(state).length).toEqual(0); + }); + + it('should get attributeDistinct count', () => { + const state = { metamodel: { attributeDistinct: { ...fromAttributeDistinct.initialState }}}; + expect(attributeDistinctSelector.selectAttributeDistinctTotal(state)).toEqual(0); + }); + + it('should get attributeDistinctListIsLoading', () => { + const state = { metamodel: { attributeDistinct: { ...fromAttributeDistinct.initialState }}}; + expect(attributeDistinctSelector.selectAttributeDistinctListIsLoading(state)).toBe(false); + }); + + it('should get attributeDistinctListIsLoaded', () => { + const state = { metamodel: { attributeDistinct: { ...fromAttributeDistinct.initialState }}}; + expect(attributeDistinctSelector.selectAttributeDistinctListIsLoaded(state)).toBe(false); + }); +}); diff --git a/client/src/app/metamodel/selectors/attribute.selector.spec.ts b/client/src/app/metamodel/selectors/attribute.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..28e634af25859de467cc826bc4db56098b4820ed --- /dev/null +++ b/client/src/app/metamodel/selectors/attribute.selector.spec.ts @@ -0,0 +1,39 @@ +import * as attributeSelector from './attribute.selector'; +import * as fromAttribute from '../reducers/attribute.reducer'; + +describe('[Metamodel][Selector] Attribute selector', () => { + it('should get attribute state', () => { + const state = { metamodel: { attribute: { ...fromAttribute.initialState }}}; + expect(attributeSelector.selectAttributeState(state)).toEqual(state.metamodel.attribute); + }); + + it('should get attribute IDs', () => { + const state = { metamodel: { attribute: { ...fromAttribute.initialState }}}; + expect(attributeSelector.selectAttributeIds(state).length).toEqual(0); + }); + + it('should get attribute entities', () => { + const state = { metamodel: { attribute: { ...fromAttribute.initialState }}}; + expect(attributeSelector.selectAttributeEntities(state)).toEqual({ }); + }); + + it('should get all attributes', () => { + const state = { metamodel: { attribute: { ...fromAttribute.initialState }}}; + expect(attributeSelector.selectAllAttributes(state).length).toEqual(0); + }); + + it('should get attribute count', () => { + const state = { metamodel: { attribute: { ...fromAttribute.initialState }}}; + expect(attributeSelector.selectAttributeTotal(state)).toEqual(0); + }); + + it('should get attributeListIsLoading', () => { + const state = { metamodel: { attribute: { ...fromAttribute.initialState }}}; + expect(attributeSelector.selectAttributeListIsLoading(state)).toBe(false); + }); + + it('should get attributeListIsLoaded', () => { + const state = { metamodel: { attribute: { ...fromAttribute.initialState }}}; + expect(attributeSelector.selectAttributeListIsLoaded(state)).toBe(false); + }); +}); diff --git a/client/src/app/metamodel/selectors/column.selector.spec.ts b/client/src/app/metamodel/selectors/column.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..16e1076a498895aa301a43144e6dd360a0ed971b --- /dev/null +++ b/client/src/app/metamodel/selectors/column.selector.spec.ts @@ -0,0 +1,54 @@ +import * as columnSelector from './column.selector'; +import * as fromColumn from '../reducers/column.reducer'; +import { COLUMN } from '../../../test-data'; + +describe('[Metamodel][Selector] Column selector', () => { + it('should get column state', () => { + const state = { metamodel: { column: { ...fromColumn.initialState }}}; + expect(columnSelector.selectColumnState(state)).toEqual(state.metamodel.column); + }); + + it('should get column IDs', () => { + const state = { metamodel: { column: { ...fromColumn.initialState }}}; + expect(columnSelector.selectColumnIds(state).length).toEqual(0); + }); + + it('should get column entities', () => { + const state = { metamodel: { column: { ...fromColumn.initialState }}}; + expect(columnSelector.selectColumnEntities(state)).toEqual({ }); + }); + + it('should get all columns', () => { + const state = { metamodel: { column: { ...fromColumn.initialState }}}; + expect(columnSelector.selectAllColumns(state).length).toEqual(0); + }); + + it('should get column count', () => { + const state = { metamodel: { column: { ...fromColumn.initialState }}}; + expect(columnSelector.selectColumnTotal(state)).toEqual(0); + }); + + it('should get columnListIsLoading', () => { + const state = { metamodel: { column: { ...fromColumn.initialState }}}; + expect(columnSelector.selectColumnListIsLoading(state)).toBe(false); + }); + + it('should get columnListIsLoaded', () => { + const state = { metamodel: { column: { ...fromColumn.initialState }}}; + expect(columnSelector.selectColumnListIsLoaded(state)).toBe(false); + }); + + it('should get column by route', () => { + const state = { + router: { state: { params: { name: 'myCol' }}}, + metamodel: { + column: { + ...fromColumn.initialState, + ids: ['myCol'], + entities: { 'myCol': COLUMN } + } + } + }; + expect(columnSelector.selectColumnByRouteName(state)).toEqual(COLUMN); + }); +}); diff --git a/client/src/app/metamodel/selectors/criteria-family.selector.spec.ts b/client/src/app/metamodel/selectors/criteria-family.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..43dadd8477fc0a1b334d074f367ed404a43f3f34 --- /dev/null +++ b/client/src/app/metamodel/selectors/criteria-family.selector.spec.ts @@ -0,0 +1,39 @@ +import * as criteriaFamilySelector from './criteria-family.selector'; +import * as fromCriteriaFamily from '../reducers/criteria-family.reducer'; + +describe('[Metamodel][Selector] CriteriaFamily selector', () => { + it('should get criteriaFamily state', () => { + const state = { metamodel: { criteriaFamily: { ...fromCriteriaFamily.initialState }}}; + expect(criteriaFamilySelector.selectCriteriaFamilyState(state)).toEqual(state.metamodel.criteriaFamily); + }); + + it('should get criteriaFamily IDs', () => { + const state = { metamodel: { criteriaFamily: { ...fromCriteriaFamily.initialState }}}; + expect(criteriaFamilySelector.selectCriteriaFamilyIds(state).length).toEqual(0); + }); + + it('should get criteriaFamily entities', () => { + const state = { metamodel: { criteriaFamily: { ...fromCriteriaFamily.initialState }}}; + expect(criteriaFamilySelector.selectCriteriaFamilyEntities(state)).toEqual({ }); + }); + + it('should get all criteriaFamilies', () => { + const state = { metamodel: { criteriaFamily: { ...fromCriteriaFamily.initialState }}}; + expect(criteriaFamilySelector.selectAllCriteriaFamilies(state).length).toEqual(0); + }); + + it('should get criteriaFamily count', () => { + const state = { metamodel: { criteriaFamily: { ...fromCriteriaFamily.initialState }}}; + expect(criteriaFamilySelector.selectCriteriaFamilyTotal(state)).toEqual(0); + }); + + it('should get criteriaFamilyListIsLoading', () => { + const state = { metamodel: { criteriaFamily: { ...fromCriteriaFamily.initialState }}}; + expect(criteriaFamilySelector.selectCriteriaFamilyListIsLoading(state)).toBe(false); + }); + + it('should get criteriaFamilyListIsLoaded', () => { + const state = { metamodel: { criteriaFamily: { ...fromCriteriaFamily.initialState }}}; + expect(criteriaFamilySelector.selectCriteriaFamilyListIsLoaded(state)).toBe(false); + }); +}); diff --git a/client/src/app/metamodel/selectors/database.selector.spec.ts b/client/src/app/metamodel/selectors/database.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..149b4fd41d712d6c690c8819d08e8dbc0544aa02 --- /dev/null +++ b/client/src/app/metamodel/selectors/database.selector.spec.ts @@ -0,0 +1,54 @@ +import * as databaseSelector from './database.selector'; +import * as fromDatabase from '../reducers/database.reducer'; +import { DATABASE } from '../../../test-data'; + +describe('[Metamodel][Selector] Database selector', () => { + it('should get database state', () => { + const state = { metamodel: { database: { ...fromDatabase.initialState }}}; + expect(databaseSelector.selectDatabaseState(state)).toEqual(state.metamodel.database); + }); + + it('should get database IDs', () => { + const state = { metamodel: { database: { ...fromDatabase.initialState }}}; + expect(databaseSelector.selectDatabaseIds(state).length).toEqual(0); + }); + + it('should get database entities', () => { + const state = { metamodel: { database: { ...fromDatabase.initialState }}}; + expect(databaseSelector.selectDatabaseEntities(state)).toEqual({ }); + }); + + it('should get all databases', () => { + const state = { metamodel: { database: { ...fromDatabase.initialState }}}; + expect(databaseSelector.selectAllDatabases(state).length).toEqual(0); + }); + + it('should get database count', () => { + const state = { metamodel: { database: { ...fromDatabase.initialState }}}; + expect(databaseSelector.selectDatabaseTotal(state)).toEqual(0); + }); + + it('should get databaseListIsLoading', () => { + const state = { metamodel: { database: { ...fromDatabase.initialState }}}; + expect(databaseSelector.selectDatabaseListIsLoading(state)).toBe(false); + }); + + it('should get databaseListIsLoaded', () => { + const state = { metamodel: { database: { ...fromDatabase.initialState }}}; + expect(databaseSelector.selectDatabaseListIsLoaded(state)).toBe(false); + }); + + it('should get database by route', () => { + const state = { + router: { state: { params: { id: 1 }}}, + metamodel: { + database: { + ...fromDatabase.initialState, + ids: [1], + entities: { 1: DATABASE } + } + } + }; + expect(databaseSelector.selectDatabaseByRouteId(state)).toEqual(DATABASE); + }); +}); diff --git a/client/src/app/metamodel/selectors/dataset-family.selector.spec.ts b/client/src/app/metamodel/selectors/dataset-family.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3c7d7e69bcbb3dfd00401c3a470c935519d3081f --- /dev/null +++ b/client/src/app/metamodel/selectors/dataset-family.selector.spec.ts @@ -0,0 +1,39 @@ +import * as datasetFamilySelector from './dataset-family.selector'; +import * as fromDatasetFamily from '../reducers/dataset-family.reducer'; + +describe('[Metamodel][Selector] DatasetFamily selector', () => { + it('should get datasetFamily state', () => { + const state = { metamodel: { datasetFamily: { ...fromDatasetFamily.initialState }}}; + expect(datasetFamilySelector.selectDatasetFamilyState(state)).toEqual(state.metamodel.datasetFamily); + }); + + it('should get datasetFamily IDs', () => { + const state = { metamodel: { datasetFamily: { ...fromDatasetFamily.initialState }}}; + expect(datasetFamilySelector.selectDatasetFamilyIds(state).length).toEqual(0); + }); + + it('should get datasetFamily entities', () => { + const state = { metamodel: { datasetFamily: { ...fromDatasetFamily.initialState }}}; + expect(datasetFamilySelector.selectDatasetFamilyEntities(state)).toEqual({ }); + }); + + it('should get all datasetFamilies', () => { + const state = { metamodel: { datasetFamily: { ...fromDatasetFamily.initialState }}}; + expect(datasetFamilySelector.selectAllDatasetFamilies(state).length).toEqual(0); + }); + + it('should get datasetFamily count', () => { + const state = { metamodel: { datasetFamily: { ...fromDatasetFamily.initialState }}}; + expect(datasetFamilySelector.selectDatasetFamilyTotal(state)).toEqual(0); + }); + + it('should get datasetFamilyListIsLoading', () => { + const state = { metamodel: { datasetFamily: { ...fromDatasetFamily.initialState }}}; + expect(datasetFamilySelector.selectDatasetFamilyListIsLoading(state)).toBe(false); + }); + + it('should get datasetFamilyListIsLoaded', () => { + const state = { metamodel: { datasetFamily: { ...fromDatasetFamily.initialState }}}; + expect(datasetFamilySelector.selectDatasetFamilyListIsLoaded(state)).toBe(false); + }); +}); diff --git a/client/src/app/metamodel/selectors/dataset.selector.spec.ts b/client/src/app/metamodel/selectors/dataset.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9551f9d199823569e8aac551348f3037fd37a80a --- /dev/null +++ b/client/src/app/metamodel/selectors/dataset.selector.spec.ts @@ -0,0 +1,76 @@ +import * as datasetSelector from './dataset.selector'; +import * as fromDataset from '../reducers/dataset.reducer'; +import { DATASET, DATASET_LIST } from '../../../test-data'; + +describe('[Metamodel][Selector] Dataset selector', () => { + it('should get dataset state', () => { + const state = { metamodel: { dataset: { ...fromDataset.initialState }}}; + expect(datasetSelector.selectDatasetState(state)).toEqual(state.metamodel.dataset); + }); + + it('should get dataset IDs', () => { + const state = { metamodel: { dataset: { ...fromDataset.initialState }}}; + expect(datasetSelector.selectDatasetIds(state).length).toEqual(0); + }); + + it('should get dataset entities', () => { + const state = { metamodel: { dataset: { ...fromDataset.initialState }}}; + expect(datasetSelector.selectDatasetEntities(state)).toEqual({ }); + }); + + it('should get all datasets', () => { + const state = { metamodel: { dataset: { ...fromDataset.initialState }}}; + expect(datasetSelector.selectAllDatasets(state).length).toEqual(0); + }); + + it('should get dataset count', () => { + const state = { metamodel: { dataset: { ...fromDataset.initialState }}}; + expect(datasetSelector.selectDatasetTotal(state)).toEqual(0); + }); + + it('should get datasetListIsLoading', () => { + const state = { metamodel: { dataset: { ...fromDataset.initialState }}}; + expect(datasetSelector.selectDatasetListIsLoading(state)).toBe(false); + }); + + it('should get datasetListIsLoaded', () => { + const state = { metamodel: { dataset: { ...fromDataset.initialState }}}; + expect(datasetSelector.selectDatasetListIsLoaded(state)).toBe(false); + }); + + it('should get dataset by route', () => { + const state = { + router: { state: { params: { dname: 'myDataset' }}}, + metamodel: { + dataset: { + ...fromDataset.initialState, + ids: ['myDataset'], + entities: { 'myDataset': DATASET } + } + } + }; + expect(datasetSelector.selectDatasetByRouteName(state)).toEqual(DATASET); + }); + + it('should get dataset name by route', () => { + const state = {router: { state: { params: { dname: 'myDataset' }}}}; + expect(datasetSelector.selectDatasetNameByRoute(state)).toEqual('myDataset'); + }); + + it('should get datasets with cone search', () => { + const state = { + metamodel: { + dataset: { + ...fromDataset.initialState, + ids: ['myDataset', 'anotherDataset'], + entities: { + 'myDataset': DATASET_LIST[0], + 'anotherDataset': DATASET_LIST[1] + } + } + } + }; + expect(datasetSelector.selectAllConeSearchDatasets(state).length).toEqual(1); + expect(datasetSelector.selectAllConeSearchDatasets(state)).toContain(DATASET_LIST[0]); + }); +}); diff --git a/client/src/app/metamodel/selectors/group.selector.spec.ts b/client/src/app/metamodel/selectors/group.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..cc8366f5bb25e7e9fb097c7efb3d63de7fbd27a7 --- /dev/null +++ b/client/src/app/metamodel/selectors/group.selector.spec.ts @@ -0,0 +1,54 @@ +import * as groupSelector from './group.selector'; +import * as fromGroup from '../reducers/group.reducer'; +import { GROUP } from '../../../test-data'; + +describe('[Metamodel][Selector] Group selector', () => { + it('should get group state', () => { + const state = { metamodel: { group: { ...fromGroup.initialState }}}; + expect(groupSelector.selectGroupState(state)).toEqual(state.metamodel.group); + }); + + it('should get group IDs', () => { + const state = { metamodel: { group: { ...fromGroup.initialState }}}; + expect(groupSelector.selectGroupIds(state).length).toEqual(0); + }); + + it('should get group entities', () => { + const state = { metamodel: { group: { ...fromGroup.initialState }}}; + expect(groupSelector.selectGroupEntities(state)).toEqual({ }); + }); + + it('should get all groups', () => { + const state = { metamodel: { group: { ...fromGroup.initialState }}}; + expect(groupSelector.selectAllGroups(state).length).toEqual(0); + }); + + it('should get group count', () => { + const state = { metamodel: { group: { ...fromGroup.initialState }}}; + expect(groupSelector.selectGroupTotal(state)).toEqual(0); + }); + + it('should get groupListIsLoading', () => { + const state = { metamodel: { group: { ...fromGroup.initialState }}}; + expect(groupSelector.selectGroupListIsLoading(state)).toBe(false); + }); + + it('should get groupListIsLoaded', () => { + const state = { metamodel: { group: { ...fromGroup.initialState }}}; + expect(groupSelector.selectGroupListIsLoaded(state)).toBe(false); + }); + + it('should get group ID by route', () => { + const state = { + router: { state: { params: { id: 1 }}}, + metamodel: { + group: { + ...fromGroup.initialState, + ids: [1], + entities: { 1: GROUP } + } + } + }; + expect(groupSelector.selectGroupByRouteId(state)).toEqual(GROUP); + }); +}); diff --git a/client/src/app/metamodel/selectors/instance.selector.spec.ts b/client/src/app/metamodel/selectors/instance.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..137da338d11f07ac3016e8c96cdbfc2dd1fd36af --- /dev/null +++ b/client/src/app/metamodel/selectors/instance.selector.spec.ts @@ -0,0 +1,59 @@ +import * as instanceSelector from './instance.selector'; +import * as fromInstance from '../reducers/instance.reducer'; +import { INSTANCE } from '../../../test-data'; + +describe('[Metamodel][Selector] Instance selector', () => { + it('should get instance state', () => { + const state = { metamodel: { instance: { ...fromInstance.initialState }}}; + expect(instanceSelector.selectInstanceState(state)).toEqual(state.metamodel.instance); + }); + + it('should get instance IDs', () => { + const state = { metamodel: { instance: { ...fromInstance.initialState }}}; + expect(instanceSelector.selectInstanceIds(state).length).toEqual(0); + }); + + it('should get instance entities', () => { + const state = { metamodel: { instance: { ...fromInstance.initialState }}}; + expect(instanceSelector.selectInstanceEntities(state)).toEqual({ }); + }); + + it('should get all instances', () => { + const state = { metamodel: { instance: { ...fromInstance.initialState }}}; + expect(instanceSelector.selectAllInstances(state).length).toEqual(0); + }); + + it('should get instance count', () => { + const state = { metamodel: { instance: { ...fromInstance.initialState }}}; + expect(instanceSelector.selectInstanceTotal(state)).toEqual(0); + }); + + it('should get instanceListIsLoading', () => { + const state = { metamodel: { instance: { ...fromInstance.initialState }}}; + expect(instanceSelector.selectInstanceListIsLoading(state)).toBe(false); + }); + + it('should get instanceListIsLoaded', () => { + const state = { metamodel: { instance: { ...fromInstance.initialState }}}; + expect(instanceSelector.selectInstanceListIsLoaded(state)).toBe(false); + }); + + it('should get instance by route', () => { + const state = { + router: { state: { params: { iname: 'myInstance' }}}, + metamodel: { + instance: { + ...fromInstance.initialState, + ids: ['myInstance'], + entities: { 'myInstance': INSTANCE } + } + } + }; + expect(instanceSelector.selectInstanceByRouteName(state)).toEqual(INSTANCE); + }); + + it('should get instance name by route', () => { + const state = { router: { state: { params: { iname: 'myInstance' }}}}; + expect(instanceSelector.selectInstanceNameByRoute(state)).toEqual('myInstance'); + }); +}); diff --git a/client/src/app/metamodel/selectors/output-category.selector.spec.ts b/client/src/app/metamodel/selectors/output-category.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..eeb4e606b21e835b7bc8b60c8fcf626893cfd99c --- /dev/null +++ b/client/src/app/metamodel/selectors/output-category.selector.spec.ts @@ -0,0 +1,39 @@ +import * as outputCategorySelector from './output-category.selector'; +import * as fromOutputCategory from '../reducers/output-category.reducer'; + +describe('[Metamodel][Selector] OutputCategory selector', () => { + it('should get outputCategory state', () => { + const state = { metamodel: { outputCategory: { ...fromOutputCategory.initialState }}}; + expect(outputCategorySelector.selectOutputCategoryState(state)).toEqual(state.metamodel.outputCategory); + }); + + it('should get outputCategory IDs', () => { + const state = { metamodel: { outputCategory: { ...fromOutputCategory.initialState }}}; + expect(outputCategorySelector.selectOutputCategoryIds(state).length).toEqual(0); + }); + + it('should get outputCategory entities', () => { + const state = { metamodel: { outputCategory: { ...fromOutputCategory.initialState }}}; + expect(outputCategorySelector.selectOutputCategoryEntities(state)).toEqual({ }); + }); + + it('should get all outputCategories', () => { + const state = { metamodel: { outputCategory: { ...fromOutputCategory.initialState }}}; + expect(outputCategorySelector.selectAllOutputCategories(state).length).toEqual(0); + }); + + it('should get outputCategory count', () => { + const state = { metamodel: { outputCategory: { ...fromOutputCategory.initialState }}}; + expect(outputCategorySelector.selectOutputCategoryTotal(state)).toEqual(0); + }); + + it('should get outputCategoryListIsLoading', () => { + const state = { metamodel: { outputCategory: { ...fromOutputCategory.initialState }}}; + expect(outputCategorySelector.selectOutputCategoryListIsLoading(state)).toBe(false); + }); + + it('should get outputCategoryListIsLoaded', () => { + const state = { metamodel: { outputCategory: { ...fromOutputCategory.initialState }}}; + expect(outputCategorySelector.selectOutputCategoryListIsLoaded(state)).toBe(false); + }); +}); diff --git a/client/src/app/metamodel/selectors/output-family.selector.spec.ts b/client/src/app/metamodel/selectors/output-family.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..58e18fd61ff05c9715e587eb9df8bbf75e3496e9 --- /dev/null +++ b/client/src/app/metamodel/selectors/output-family.selector.spec.ts @@ -0,0 +1,39 @@ +import * as outputFamilySelector from './output-family.selector'; +import * as fromOutputFamily from '../reducers/output-family.reducer'; + +describe('[Metamodel][Selector] Output family selector', () => { + it('should get outputFamily state', () => { + const state = { metamodel: { outputFamily: { ...fromOutputFamily.initialState }}}; + expect(outputFamilySelector.selectOutputFamilyState(state)).toEqual(state.metamodel.outputFamily); + }); + + it('should get outputFamily IDs', () => { + const state = { metamodel: { outputFamily: { ...fromOutputFamily.initialState }}}; + expect(outputFamilySelector.selectOutputFamilyIds(state).length).toEqual(0); + }); + + it('should get outputFamily entities', () => { + const state = { metamodel: { outputFamily: { ...fromOutputFamily.initialState }}}; + expect(outputFamilySelector.selectOutputFamilyEntities(state)).toEqual({ }); + }); + + it('should get all outputFamilies', () => { + const state = { metamodel: { outputFamily: { ...fromOutputFamily.initialState }}}; + expect(outputFamilySelector.selectAllOutputFamilies(state).length).toEqual(0); + }); + + it('should get outputFamily count', () => { + const state = { metamodel: { outputFamily: { ...fromOutputFamily.initialState }}}; + expect(outputFamilySelector.selectOutputFamilyTotal(state)).toEqual(0); + }); + + it('should get outputFamilyListIsLoading', () => { + const state = { metamodel: { outputFamily: { ...fromOutputFamily.initialState }}}; + expect(outputFamilySelector.selectOutputFamilyListIsLoading(state)).toBe(false); + }); + + it('should get outputFamilyListIsLoaded', () => { + const state = { metamodel: { outputFamily: { ...fromOutputFamily.initialState }}}; + expect(outputFamilySelector.selectOutputFamilyListIsLoaded(state)).toBe(false); + }); +}); diff --git a/client/src/app/metamodel/selectors/root-directory.selector.spec.ts b/client/src/app/metamodel/selectors/root-directory.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab145eb08a0668c0575959ccedeca1561832f3ea --- /dev/null +++ b/client/src/app/metamodel/selectors/root-directory.selector.spec.ts @@ -0,0 +1,39 @@ +import * as rootDirectorySelector from './root-directory.selector'; +import * as fromRootDirectory from '../reducers/root-directory.reducer'; + +describe('[Metamodel][Selector] Root Directory selector', () => { + it('should get rootDirectory state', () => { + const state = { metamodel: { rootDirectory: { ...fromRootDirectory.initialState }}}; + expect(rootDirectorySelector.selectRootDirectoryState(state)).toEqual(state.metamodel.rootDirectory); + }); + + it('should get file info IDs', () => { + const state = { metamodel: { rootDirectory: { ...fromRootDirectory.initialState }}}; + expect(rootDirectorySelector.selectFileInfoIds(state).length).toEqual(0); + }); + + it('should get file info entities', () => { + const state = { metamodel: { rootDirectory: { ...fromRootDirectory.initialState }}}; + expect(rootDirectorySelector.selectFileInfoEntities(state)).toEqual({ }); + }); + + it('should get all file info', () => { + const state = { metamodel: { rootDirectory: { ...fromRootDirectory.initialState }}}; + expect(rootDirectorySelector.selectAllFileInfo(state).length).toEqual(0); + }); + + it('should get file info count', () => { + const state = { metamodel: { rootDirectory: { ...fromRootDirectory.initialState }}}; + expect(rootDirectorySelector.selectFileInfoTotal(state)).toEqual(0); + }); + + it('should get rootDirectoryIsLoading', () => { + const state = { metamodel: { rootDirectory: { ...fromRootDirectory.initialState }}}; + expect(rootDirectorySelector.selectRootDirectoryIsLoading(state)).toBe(false); + }); + + it('should get rootDirectoryIsLoading', () => { + const state = { metamodel: { rootDirectory: { ...fromRootDirectory.initialState }}}; + expect(rootDirectorySelector.selectRootDirectoryIsLoaded(state)).toBe(false); + }); +}); diff --git a/client/src/app/metamodel/selectors/select-option.selector.spec.ts b/client/src/app/metamodel/selectors/select-option.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e9933201627e0a6d2d3e918d5e99e68b1f6f1142 --- /dev/null +++ b/client/src/app/metamodel/selectors/select-option.selector.spec.ts @@ -0,0 +1,54 @@ +import * as selectOptionSelector from './select-option.selector'; +import * as fromSelectOption from '../reducers/select-option.reducer'; +import { SELECT_OPTION } from '../../../test-data'; + +describe('[Metamodel][Selector] Select option selector', () => { + it('should get selectOption state', () => { + const state = { metamodel: { selectOption: { ...fromSelectOption.initialState }}}; + expect(selectOptionSelector.selectSelectOptionState(state)).toEqual(state.metamodel.selectOption); + }); + + it('should get selectOption IDs', () => { + const state = { metamodel: { selectOption: { ...fromSelectOption.initialState }}}; + expect(selectOptionSelector.selectSelectOptionIds(state).length).toEqual(0); + }); + + it('should get selectOption entities', () => { + const state = { metamodel: { selectOption: { ...fromSelectOption.initialState }}}; + expect(selectOptionSelector.selectSelectOptionEntities(state)).toEqual({ }); + }); + + it('should get all selectOption', () => { + const state = { metamodel: { selectOption: { ...fromSelectOption.initialState }}}; + expect(selectOptionSelector.selectAllSelectOptions(state).length).toEqual(0); + }); + + it('should get selectOption count', () => { + const state = { metamodel: { selectOption: { ...fromSelectOption.initialState }}}; + expect(selectOptionSelector.selectSelectOptionTotal(state)).toEqual(0); + }); + + it('should get selectOptionListIsLoading', () => { + const state = { metamodel: { selectOption: { ...fromSelectOption.initialState }}}; + expect(selectOptionSelector.selectSelectOptionListIsLoading(state)).toBe(false); + }); + + it('should get selectOptionListIsLoaded', () => { + const state = { metamodel: { selectOption: { ...fromSelectOption.initialState }}}; + expect(selectOptionSelector.selectSelectOptionListIsLoaded(state)).toBe(false); + }); + + it('should get select option by select name', () => { + const state = { + router: { state: { params: { select: 'name_one' }}}, + metamodel: { + selectOption: { + ...fromSelectOption.initialState, + ids: [1], + entities: { 1: SELECT_OPTION } + } + } + }; + expect(selectOptionSelector.getOptionBySelectName(state)).toEqual([SELECT_OPTION]); + }); +}); diff --git a/client/src/app/metamodel/selectors/select.selector.spec.ts b/client/src/app/metamodel/selectors/select.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..227e38e6f8475fa96b6319b7defea9309d896b1c --- /dev/null +++ b/client/src/app/metamodel/selectors/select.selector.spec.ts @@ -0,0 +1,54 @@ +import * as selectSelector from './select.selector'; +import * as fromSelect from '../reducers/select.reducer'; +import { SELECT } from '../../../test-data'; + +describe('[Metamodel][Selector] Select selector', () => { + it('should get select state', () => { + const state = { metamodel: { select: { ...fromSelect.initialState }}}; + expect(selectSelector.selectSelectState(state)).toEqual(state.metamodel.select); + }); + + it('should get select IDs', () => { + const state = { metamodel: { select: { ...fromSelect.initialState }}}; + expect(selectSelector.selectSelectIds(state).length).toEqual(0); + }); + + it('should get select entities', () => { + const state = { metamodel: { select: { ...fromSelect.initialState }}}; + expect(selectSelector.selectSelectEntities(state)).toEqual({ }); + }); + + it('should get all selects', () => { + const state = { metamodel: { select: { ...fromSelect.initialState }}}; + expect(selectSelector.selectAllSelects(state).length).toEqual(0); + }); + + it('should get select count', () => { + const state = { metamodel: { select: { ...fromSelect.initialState }}}; + expect(selectSelector.selectSelectTotal(state)).toEqual(0); + }); + + it('should get selectListIsLoading', () => { + const state = { metamodel: { select: { ...fromSelect.initialState }}}; + expect(selectSelector.selectSelectListIsLoading(state)).toBe(false); + }); + + it('should get selectListIsLoaded', () => { + const state = { metamodel: { select: { ...fromSelect.initialState }}}; + expect(selectSelector.selectSelectListIsLoaded(state)).toBe(false); + }); + + it('should get select by route', () => { + const state = { + router: { state: { params: { select: 'mySelect' }}}, + metamodel: { + select: { + ...fromSelect.initialState, + ids: ['mySelect'], + entities: { 'mySelect': SELECT } + } + } + }; + expect(selectSelector.getSelectByRouteName(state)).toEqual(SELECT); + }); +}); diff --git a/client/src/app/metamodel/selectors/survey.selector.spec.ts b/client/src/app/metamodel/selectors/survey.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..21885ff6498d766169396f4650abdc7efca80aa0 --- /dev/null +++ b/client/src/app/metamodel/selectors/survey.selector.spec.ts @@ -0,0 +1,54 @@ +import * as surveySelector from './survey.selector'; +import * as fromSurvey from '../reducers/survey.reducer'; +import { SURVEY } from '../../../test-data'; + +describe('[Metamodel][Selector] Survey selector', () => { + it('should get survey state', () => { + const state = { metamodel: { survey: { ...fromSurvey.initialState }}}; + expect(surveySelector.selectSurveyState(state)).toEqual(state.metamodel.survey); + }); + + it('should get survey IDs', () => { + const state = { metamodel: { survey: { ...fromSurvey.initialState }}}; + expect(surveySelector.selectSurveyIds(state).length).toEqual(0); + }); + + it('should get survey entities', () => { + const state = { metamodel: { survey: { ...fromSurvey.initialState }}}; + expect(surveySelector.selectSurveyEntities(state)).toEqual({ }); + }); + + it('should get all surveys', () => { + const state = { metamodel: { survey: { ...fromSurvey.initialState }}}; + expect(surveySelector.selectAllSurveys(state).length).toEqual(0); + }); + + it('should get survey count', () => { + const state = { metamodel: { survey: { ...fromSurvey.initialState }}}; + expect(surveySelector.selectSurveyTotal(state)).toEqual(0); + }); + + it('should get surveyListIsLoading', () => { + const state = { metamodel: { survey: { ...fromSurvey.initialState }}}; + expect(surveySelector.selectSurveyListIsLoading(state)).toBe(false); + }); + + it('should get surveyListIsLoaded', () => { + const state = { metamodel: { survey: { ...fromSurvey.initialState }}}; + expect(surveySelector.selectSurveyListIsLoaded(state)).toBe(false); + }); + + it('should get survey by route', () => { + const state = { + router: { state: { params: { name: 'survey-one' }}}, + metamodel: { + survey: { + ...fromSurvey.initialState, + ids: ['survey-one'], + entities: { 'survey-one': SURVEY } + } + } + }; + expect(surveySelector.selectSurveyByRouteName(state)).toEqual(SURVEY); + }); +}); diff --git a/client/src/app/metamodel/selectors/table.selector.spec.ts b/client/src/app/metamodel/selectors/table.selector.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3cb5f073b8e212286f01b724fb3dfd4036afc7de --- /dev/null +++ b/client/src/app/metamodel/selectors/table.selector.spec.ts @@ -0,0 +1,39 @@ +import * as tableSelector from './table.selector'; +import * as fromTable from '../reducers/table.reducer'; + +describe('[Metamodel][Selector] Table selector', () => { + it('should get table state', () => { + const state = { metamodel: { table: { ...fromTable.initialState }}}; + expect(tableSelector.selectTableState(state)).toEqual(state.metamodel.table); + }); + + it('should get table IDs', () => { + const state = { metamodel: { table: { ...fromTable.initialState }}}; + expect(tableSelector.selectTableIds(state).length).toEqual(0); + }); + + it('should get table entities', () => { + const state = { metamodel: { table: { ...fromTable.initialState }}}; + expect(tableSelector.selectTableEntities(state)).toEqual({ }); + }); + + it('should get all tables', () => { + const state = { metamodel: { table: { ...fromTable.initialState }}}; + expect(tableSelector.selectAllTables(state).length).toEqual(0); + }); + + it('should get table count', () => { + const state = { metamodel: { table: { ...fromTable.initialState }}}; + expect(tableSelector.selectTableTotal(state)).toEqual(0); + }); + + it('should get tableListIsLoading', () => { + const state = { metamodel: { table: { ...fromTable.initialState }}}; + expect(tableSelector.selectTableListIsLoading(state)).toBe(false); + }); + + it('should get tableListIsLoaded', () => { + const state = { metamodel: { table: { ...fromTable.initialState }}}; + expect(tableSelector.selectTableListIsLoaded(state)).toBe(false); + }); +}); diff --git a/client/src/app/metamodel/services/attribute-distinct.service.spec.ts b/client/src/app/metamodel/services/attribute-distinct.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a9380a030fdbefd16bc068096767ea426a6d0bd7 --- /dev/null +++ b/client/src/app/metamodel/services/attribute-distinct.service.spec.ts @@ -0,0 +1,41 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { AttributeDistinctService } from './attribute-distinct.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { ATTRIBUTE } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] AttributeDistinctService', () => { + let service: AttributeDistinctService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + AttributeDistinctService + ] + }); + service = TestBed.inject(AttributeDistinctService); + }); + + it('#retrieveAttributeDistinctList() should return an Observable<string[]>', + inject([HttpTestingController, AttributeDistinctService],(httpMock: HttpTestingController, service: AttributeDistinctService) => { + const mockResponse = []; + + service.retrieveAttributeDistinctList('myDataset', ATTRIBUTE).subscribe((event: string[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/attribute/1/distinct'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/attribute-distinct.service.ts b/client/src/app/metamodel/services/attribute-distinct.service.ts index 8051c13d5d362582e287787db0130146779d9a82..1dc4ba7ea000fc908b4500cd7383f66a7be2028a 100644 --- a/client/src/app/metamodel/services/attribute-distinct.service.ts +++ b/client/src/app/metamodel/services/attribute-distinct.service.ts @@ -15,10 +15,22 @@ import { Observable } from 'rxjs'; import { Attribute } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Attribute distinct service. + */ @Injectable() export class AttributeDistinctService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves distinct attribute list for the given dataset. + * + * @param {string} datasetName - The dataset name. + * @param {Attribute} attribute - The attribute. + * + * @return Observable<string[]> + */ retrieveAttributeDistinctList(datasetName: string, attribute: Attribute): Observable<string[]> { return this.http.get<string[]>(`${this.config.apiUrl}/dataset/${datasetName}/attribute/${attribute.id}/distinct`); } diff --git a/client/src/app/metamodel/services/attribute.service.spec.ts b/client/src/app/metamodel/services/attribute.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..1f1882248def816806b542e38e8f7ccc318a86cf --- /dev/null +++ b/client/src/app/metamodel/services/attribute.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { AttributeService } from './attribute.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { Attribute } from '../models'; +import { ATTRIBUTE } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] AttributeService', () => { + let service: AttributeService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + AttributeService + ] + }); + service = TestBed.inject(AttributeService); + }); + + it('#retrieveAttributeList() should return an Observable<Attribute[]>', + inject([HttpTestingController, AttributeService],(httpMock: HttpTestingController, service: AttributeService) => { + const mockResponse = []; + + service.retrieveAttributeList('myDataset').subscribe((event: Attribute[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/attribute'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addAttribute() should return an Observable<Attribute>', + inject([HttpTestingController, AttributeService],(httpMock: HttpTestingController, service: AttributeService) => { + const mockResponse = ATTRIBUTE; + + service.addAttribute('myDataset', ATTRIBUTE).subscribe((event: Attribute) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/attribute'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editAttribute() should return an Observable<Attribute>', + inject([HttpTestingController, AttributeService],(httpMock: HttpTestingController, service: AttributeService) => { + const mockResponse = ATTRIBUTE; + + service.editAttribute('myDataset', ATTRIBUTE).subscribe((event: Attribute) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/attribute/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteAttribute() should return an Observable<object>', + inject([HttpTestingController, AttributeService],(httpMock: HttpTestingController, service: AttributeService) => { + const mockResponse = {}; + + service.deleteAttribute('myDataset', ATTRIBUTE).subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/attribute/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/attribute.service.ts b/client/src/app/metamodel/services/attribute.service.ts index 50fa14d9126f081f891caae53ad5b02da1ea0827..34f056b1dbb01da3662b21fa92b476049d6a233d 100644 --- a/client/src/app/metamodel/services/attribute.service.ts +++ b/client/src/app/metamodel/services/attribute.service.ts @@ -15,23 +15,58 @@ import { Observable } from 'rxjs'; import { Attribute } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Attribute service. + */ @Injectable() export class AttributeService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves attribute list for the given dataset. + * + * @param {string} datasetName - The dataset name. + * + * @return Observable<Attribute[]> + */ retrieveAttributeList(datasetName: string): Observable<Attribute[]> { return this.http.get<Attribute[]>(`${this.config.apiUrl}/dataset/${datasetName}/attribute`); } + /** + * Adds a new attribute to the given dataset. + * + * @param {string} datasetName - The dataset name. + * @param {Attribute} attribute - The attribute. + * + * @return Observable<Attribute> + */ addAttribute(datasetName: string, attribute: Attribute): Observable<Attribute> { return this.http.post<Attribute>(`${this.config.apiUrl}/dataset/${datasetName}/attribute`, attribute); } + /** + * Modifies an attribute to the given dataset. + * + * @param {string} datasetName - The dataset name. + * @param {Attribute} attribute - The attribute. + * + * @return Observable<Attribute> + */ editAttribute(datasetName: string, attribute: Attribute): Observable<Attribute> { return this.http.put<Attribute>(`${this.config.apiUrl}/dataset/${datasetName}/attribute/${attribute.id}`, attribute); } - deleteAttribute(datasetName: string, attribute: Attribute) { - return this.http.delete(`${this.config.apiUrl}/dataset/${datasetName}/attribute/${attribute.id}`); + /** + * Removes an attribute to the given dataset. + * + * @param {string} datasetName - The dataset name. + * @param {Attribute} attribute - The attribute. + * + * @return Observable<object> + */ + deleteAttribute(datasetName: string, attribute: Attribute): Observable<object> { + return this.http.delete(`${this.config.apiUrl}/dataset/${datasetName}/attribute/${attribute.id}`); } } diff --git a/client/src/app/metamodel/services/column.service.spec.ts b/client/src/app/metamodel/services/column.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..c13e511743d17a5aa19f259c3738daa8db670acd --- /dev/null +++ b/client/src/app/metamodel/services/column.service.spec.ts @@ -0,0 +1,41 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { ColumnService } from './column.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { Column } from '../models'; + +describe('[Instance][Metamodel][Services] ColumnService', () => { + let service: ColumnService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + ColumnService + ] + }); + service = TestBed.inject(ColumnService); + }); + + it('#retrieveColumns() should return an Observable<Column[]>', + inject([HttpTestingController, ColumnService],(httpMock: HttpTestingController, service: ColumnService) => { + const mockResponse = []; + + service.retrieveColumns('myDataset').subscribe((event: Column[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/column'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/column.service.ts b/client/src/app/metamodel/services/column.service.ts index 16b51ddc6e00e112ebf754ee128deea6abfdb300..6f97e64d4f2fde6456fc0315f477cda2ef3886b4 100644 --- a/client/src/app/metamodel/services/column.service.ts +++ b/client/src/app/metamodel/services/column.service.ts @@ -15,10 +15,21 @@ import { Observable } from 'rxjs'; import { Column } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Column service. + */ @Injectable() export class ColumnService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves column list for the given dataset. + * + * @param {string} datasetName - The dataset name. + * + * @return Observable<Column[]> + */ retrieveColumns(datasetName: string): Observable<Column[]> { return this.http.get<Column[]>(`${this.config.apiUrl}/dataset/${datasetName}/column`); } diff --git a/client/src/app/metamodel/services/criteria-family.service.spec.ts b/client/src/app/metamodel/services/criteria-family.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ee25ed5e899ac73431c99c1311a070334270a77a --- /dev/null +++ b/client/src/app/metamodel/services/criteria-family.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { CriteriaFamilyService } from './criteria-family.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { CriteriaFamily } from '../models'; +import { CRITERIA_FAMILY } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] CriteriaFamilyService', () => { + let service: CriteriaFamilyService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + CriteriaFamilyService + ] + }); + service = TestBed.inject(CriteriaFamilyService); + }); + + it('#retrieveCriteriaFamilyList() should return an Observable<CriteriaFamily[]>', + inject([HttpTestingController, CriteriaFamilyService],(httpMock: HttpTestingController, service: CriteriaFamilyService) => { + const mockResponse = []; + + service.retrieveCriteriaFamilyList('myDataset').subscribe((event: CriteriaFamily[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/criteria-family'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addCriteriaFamily() should return an Observable<CriteriaFamily>', + inject([HttpTestingController, CriteriaFamilyService],(httpMock: HttpTestingController, service: CriteriaFamilyService) => { + const mockResponse = CRITERIA_FAMILY; + + service.addCriteriaFamily('myDataset', CRITERIA_FAMILY).subscribe((event: CriteriaFamily) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/criteria-family'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editCriteriaFamily() should return an Observable<CriteriaFamily>', + inject([HttpTestingController, CriteriaFamilyService],(httpMock: HttpTestingController, service: CriteriaFamilyService) => { + const mockResponse = CRITERIA_FAMILY; + + service.editCriteriaFamily(CRITERIA_FAMILY).subscribe((event: CriteriaFamily) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/criteria-family/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteCriteriaFamily() should return an Observable<object>', + inject([HttpTestingController, CriteriaFamilyService],(httpMock: HttpTestingController, service: CriteriaFamilyService) => { + const mockResponse = {}; + + service.deleteCriteriaFamily(1).subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/criteria-family/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/criteria-family.service.ts b/client/src/app/metamodel/services/criteria-family.service.ts index ece1eea2fad401567e6250b4f4a522478b7946bf..5b1d533b6863a0494332a42f3e5407379f950e81 100644 --- a/client/src/app/metamodel/services/criteria-family.service.ts +++ b/client/src/app/metamodel/services/criteria-family.service.ts @@ -15,23 +15,56 @@ import { Observable } from 'rxjs'; import { CriteriaFamily } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Criteria family service. + */ @Injectable() export class CriteriaFamilyService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves criteria family list for the given dataset. + * + * @param {string} datasetName - The dataset name. + * + * @return Observable<CriteriaFamily[]> + */ retrieveCriteriaFamilyList(datasetName: string): Observable<CriteriaFamily[]> { return this.http.get<CriteriaFamily[]>(`${this.config.apiUrl}/dataset/${datasetName}/criteria-family`); } + /** + * Adds a new criteria family for the given dataset. + * + * @param {string} datasetName - The dataset name. + * @param {CriteriaFamily} newCriteriaFamily - The criteria family. + * + * @return Observable<CriteriaFamily> + */ addCriteriaFamily(datasetName: string, newCriteriaFamily: CriteriaFamily): Observable<CriteriaFamily> { return this.http.post<CriteriaFamily>(`${this.config.apiUrl}/dataset/${datasetName}/criteria-family`, newCriteriaFamily); } + /** + * Modifies a criteria family. + * + * @param {CriteriaFamily} criteriaFamily - The criteria family. + * + * @return Observable<CriteriaFamily> + */ editCriteriaFamily(criteriaFamily: CriteriaFamily): Observable<CriteriaFamily> { return this.http.put<CriteriaFamily>(`${this.config.apiUrl}/criteria-family/${criteriaFamily.id}`, criteriaFamily); } - deleteCriteriaFamily(criteriaFamilyId: number) { + /** + * Removes a criteria family. + * + * @param {number} criteriaFamilyId - The criteria family ID. + * + * @return Observable<object> + */ + deleteCriteriaFamily(criteriaFamilyId: number): Observable<object> { return this.http.delete(`${this.config.apiUrl}/criteria-family/${criteriaFamilyId}`); } } diff --git a/client/src/app/metamodel/services/database.service.spec.ts b/client/src/app/metamodel/services/database.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3c34b8d64c60e2d64cbf0031fa3411cbf70269f --- /dev/null +++ b/client/src/app/metamodel/services/database.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { DatabaseService } from './database.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { Database } from '../models'; +import { DATABASE } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] DatabaseService', () => { + let service: DatabaseService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + DatabaseService + ] + }); + service = TestBed.inject(DatabaseService); + }); + + it('#retrieveDatabaseList() should return an Observable<Database[]>', + inject([HttpTestingController, DatabaseService],(httpMock: HttpTestingController, service: DatabaseService) => { + const mockResponse = []; + + service.retrieveDatabaseList().subscribe((event: Database[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/database'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addDatabase() should return an Observable<Database>', + inject([HttpTestingController, DatabaseService],(httpMock: HttpTestingController, service: DatabaseService) => { + const mockResponse = DATABASE; + + service.addDatabase(DATABASE).subscribe((event: Database) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/database'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editDatabase() should return an Observable<Database>', + inject([HttpTestingController, DatabaseService],(httpMock: HttpTestingController, service: DatabaseService) => { + const mockResponse = DATABASE; + + service.editDatabase(DATABASE).subscribe((event: Database) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/database/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteDatabase() should return an Observable<object>', + inject([HttpTestingController, DatabaseService],(httpMock: HttpTestingController, service: DatabaseService) => { + const mockResponse = {}; + + service.deleteDatabase(1).subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/database/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/database.service.ts b/client/src/app/metamodel/services/database.service.ts index b24141715e62930397ffd247013af15c712bda86..a17665a551b297c2e6f7f2a33ff20a150383f09b 100644 --- a/client/src/app/metamodel/services/database.service.ts +++ b/client/src/app/metamodel/services/database.service.ts @@ -15,23 +15,53 @@ import { Observable } from 'rxjs'; import { Database } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Database service. + */ @Injectable() export class DatabaseService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves database list. + * + * @return Observable<Database[]> + */ retrieveDatabaseList(): Observable<Database[]> { return this.http.get<Database[]>(`${this.config.apiUrl}/database`); } + /** + * Adds a new database. + * + * @param {Database} newDatabase - The database. + * + * @return Observable<Database> + */ addDatabase(newDatabase: Database): Observable<Database> { return this.http.post<Database>(`${this.config.apiUrl}/database`, newDatabase); } + /** + * Modifies a database. + * + * @param {Database} database - The database. + * + * @return Observable<Database> + */ editDatabase(database: Database): Observable<Database> { return this.http.put<Database>(`${this.config.apiUrl}/database/${database.id}`, database); } - deleteDatabase(databaseId: number) { + /** + * Removes a database. + * + * @param {number} databaseId - The database ID. + * + * @return Observable<object> + */ + deleteDatabase(databaseId: number): Observable<object> { return this.http.delete(`${this.config.apiUrl}/database/${databaseId}`); } } diff --git a/client/src/app/metamodel/services/dataset-family.service.spec.ts b/client/src/app/metamodel/services/dataset-family.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..59a3ad499185445508fabd3a115efc710881f39f --- /dev/null +++ b/client/src/app/metamodel/services/dataset-family.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { DatasetFamilyService } from './dataset-family.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { DatasetFamily } from '../models'; +import { DATASET_FAMILY, GROUP } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] DatasetFamilyService', () => { + let service: DatasetFamilyService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + DatasetFamilyService + ] + }); + service = TestBed.inject(DatasetFamilyService); + }); + + it('#retrieveDatasetFamilyList() should return an Observable<DatasetFamily[]>', + inject([HttpTestingController, DatasetFamilyService],(httpMock: HttpTestingController, service: DatasetFamilyService) => { + const mockResponse = []; + + service.retrieveDatasetFamilyList('myInstance').subscribe((event: DatasetFamily[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/instance/myInstance/dataset-family'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addDatasetFamily() should return an Observable<DatasetFamily>', + inject([HttpTestingController, DatasetFamilyService],(httpMock: HttpTestingController, service: DatasetFamilyService) => { + const mockResponse = DATASET_FAMILY; + + service.addDatasetFamily('myInstance', DATASET_FAMILY).subscribe((event: DatasetFamily) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/instance/myInstance/dataset-family'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editDatasetFamily() should return an Observable<DatasetFamily>', + inject([HttpTestingController, DatasetFamilyService],(httpMock: HttpTestingController, service: DatasetFamilyService) => { + const mockResponse = DATASET_FAMILY; + + service.editDatasetFamily(DATASET_FAMILY).subscribe((event: DatasetFamily) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset-family/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteDatasetFamily() should return an Observable<object>', + inject([HttpTestingController, DatasetFamilyService],(httpMock: HttpTestingController, service: DatasetFamilyService) => { + const mockResponse = {}; + + service.deleteDatasetFamily(1).subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset-family/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/dataset-family.service.ts b/client/src/app/metamodel/services/dataset-family.service.ts index c7385f9c19b0723aba38d22f2dd42a9fa6356d36..099f2315dfee134c4600ec1917590362288bcb21 100644 --- a/client/src/app/metamodel/services/dataset-family.service.ts +++ b/client/src/app/metamodel/services/dataset-family.service.ts @@ -15,23 +15,56 @@ import { Observable } from 'rxjs'; import { DatasetFamily } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Dataset family service. + */ @Injectable() export class DatasetFamilyService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves dataset families for the given instance. + * + * @param {string} instanceName - The instance. + * + * @return Observable<DatasetFamily> + */ retrieveDatasetFamilyList(instanceName: string): Observable<DatasetFamily[]> { return this.http.get<DatasetFamily[]>(`${this.config.apiUrl}/instance/${instanceName}/dataset-family`); } + /** + * Adds a new dataset family for the given instance. + * + * @param {string} instanceName - The instance. + * @param {DatasetFamily} newDatasetFamily - The dataset family. + * + * @return Observable<DatasetFamily> + */ addDatasetFamily(instanceName: string, newDatasetFamily: DatasetFamily): Observable<DatasetFamily> { return this.http.post<DatasetFamily>(`${this.config.apiUrl}/instance/${instanceName}/dataset-family`, newDatasetFamily); } + /** + * Modifies a dataset family. + * + * @param {DatasetFamily} datasetFamily - The dataset family. + * + * @return Observable<DatasetFamily> + */ editDatasetFamily(datasetFamily: DatasetFamily): Observable<DatasetFamily> { return this.http.put<DatasetFamily>(`${this.config.apiUrl}/dataset-family/${datasetFamily.id}`, datasetFamily); } - deleteDatasetFamily(datasetFamilyId: number) { + /** + * Removes a dataset family. + * + * @param {number} datasetFamilyId - The dataset family ID. + * + * @return Observable<object> + */ + deleteDatasetFamily(datasetFamilyId: number): Observable<object> { return this.http.delete(`${this.config.apiUrl}/dataset-family/${datasetFamilyId}`); } } diff --git a/client/src/app/metamodel/services/dataset.service.spec.ts b/client/src/app/metamodel/services/dataset.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..fce30bc1715d7c91ed2225047a0af124cfa6ce58 --- /dev/null +++ b/client/src/app/metamodel/services/dataset.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { DatasetService } from './dataset.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { Dataset } from '../models'; +import { DATASET } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] DatasetService', () => { + let service: DatasetService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + DatasetService + ] + }); + service = TestBed.inject(DatasetService); + }); + + it('#retrieveDatasetList() should return an Observable<Dataset[]>', + inject([HttpTestingController, DatasetService],(httpMock: HttpTestingController, service: DatasetService) => { + const mockResponse = []; + + service.retrieveDatasetList('myInstance').subscribe((event: Dataset[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/instance/myInstance/dataset'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addDataset() should return an Observable<Dataset>', + inject([HttpTestingController, DatasetService],(httpMock: HttpTestingController, service: DatasetService) => { + const mockResponse = DATASET; + + service.addDataset(DATASET).subscribe((event: Dataset) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset-family/1/dataset'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editDataset() should return an Observable<Dataset>', + inject([HttpTestingController, DatasetService],(httpMock: HttpTestingController, service: DatasetService) => { + const mockResponse = DATASET; + + service.editDataset(DATASET).subscribe((event: Dataset) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteDataset() should return an Observable<object>', + inject([HttpTestingController, DatasetService],(httpMock: HttpTestingController, service: DatasetService) => { + const mockResponse = {}; + + service.deleteDataset('myDataset').subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/dataset.service.ts b/client/src/app/metamodel/services/dataset.service.ts index 312749da555701cecd5cdc0ff085b87fcec47f02..f82b88be174f85a4c75b638a8aebaef8995e1356 100644 --- a/client/src/app/metamodel/services/dataset.service.ts +++ b/client/src/app/metamodel/services/dataset.service.ts @@ -15,23 +15,55 @@ import { Observable } from 'rxjs'; import { Dataset } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Dataset service. + */ @Injectable() export class DatasetService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves dataset list for the given instance. + * + * @param {string} instanceName - The instance name. + * + * @return Observable<Dataset[]> + */ retrieveDatasetList(instanceName: string): Observable<Dataset[]> { return this.http.get<Dataset[]>(`${this.config.apiUrl}/instance/${instanceName}/dataset`); } + /** + * Adds a new dataset. + * + * @param {Dataset} newDataset - The dataset. + * + * @return Observable<Dataset> + */ addDataset(newDataset: Dataset): Observable<Dataset> { return this.http.post<Dataset>(`${this.config.apiUrl}/dataset-family/${newDataset.id_dataset_family}/dataset`, newDataset); } + /** + * Modifies a dataset. + * + * @param {Dataset} dataset - The dataset. + * + * @return Observable<Dataset> + */ editDataset(dataset: Dataset): Observable<Dataset> { return this.http.put<Dataset>(`${this.config.apiUrl}/dataset/${dataset.name}`, dataset); } - deleteDataset(datasetName: string) { + /** + * Removes a dataset. + * + * @param {string} datasetName - The dataset name. + * + * @return Observable<object> + */ + deleteDataset(datasetName: string): Observable<object> { return this.http.delete(`${this.config.apiUrl}/dataset/${datasetName}`); } } diff --git a/client/src/app/metamodel/services/group.service.spec.ts b/client/src/app/metamodel/services/group.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e5af8d3806eb3df0c0939552f3b1bc9bf73d387c --- /dev/null +++ b/client/src/app/metamodel/services/group.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { GroupService } from './group.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { Group } from '../models'; +import { GROUP } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] GroupService', () => { + let service: GroupService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + GroupService + ] + }); + service = TestBed.inject(GroupService); + }); + + it('#retrieveGroupList() should return an Observable<Group[]>', + inject([HttpTestingController, GroupService],(httpMock: HttpTestingController, service: GroupService) => { + const mockResponse = []; + + service.retrieveGroupList('myInstance').subscribe((event: Group[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/instance/myInstance/group'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addGroup() should return an Observable<Group>', + inject([HttpTestingController, GroupService],(httpMock: HttpTestingController, service: GroupService) => { + const mockResponse = GROUP; + + service.addGroup('myInstance', GROUP).subscribe((event: Group) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/instance/myInstance/group'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editGroup() should return an Observable<Group>', + inject([HttpTestingController, GroupService],(httpMock: HttpTestingController, service: GroupService) => { + const mockResponse = GROUP; + + service.editGroup(GROUP).subscribe((event: Group) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/group/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteGroup() should return an Observable<object>', + inject([HttpTestingController, GroupService],(httpMock: HttpTestingController, service: GroupService) => { + const mockResponse = {}; + + service.deleteGroup(1).subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/group/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/group.service.ts b/client/src/app/metamodel/services/group.service.ts index a96ed07797d2d97642a3bf2d4f063e9574d4646d..a563414f0b4c247c56c6771cd8d11b9dfaf4c9b3 100644 --- a/client/src/app/metamodel/services/group.service.ts +++ b/client/src/app/metamodel/services/group.service.ts @@ -15,23 +15,56 @@ import { Observable } from 'rxjs'; import { Group } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Group service. + */ @Injectable() export class GroupService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves group list for the given instance. + * + * @param {string} instanceName - The instance. + * + * @return Observable<Group[]> + */ retrieveGroupList(instanceName: string): Observable<Group[]> { return this.http.get<Group[]>(`${this.config.apiUrl}/instance/${instanceName}/group`); } + /** + * Adds a new group for the given instance. + * + * @param {string} instanceName - The instance. + * @param {Group} newGroup - The group. + * + * @return Observable<Group> + */ addGroup(instanceName: string, newGroup: Group): Observable<Group> { return this.http.post<Group>(`${this.config.apiUrl}/instance/${instanceName}/group`, newGroup); } + /** + * Modifies a group. + * + * @param {Group} group - The group. + * + * @return Observable<Group> + */ editGroup(group: Group): Observable<Group> { return this.http.put<Group>(`${this.config.apiUrl}/group/${group.id}`, group); } - deleteGroup(groupId: number) { + /** + * Removes a group. + * + * @param {number} groupId - The group ID. + * + * @return Observable<object> + */ + deleteGroup(groupId: number): Observable<object> { return this.http.delete(`${this.config.apiUrl}/group/${groupId}`); } } diff --git a/client/src/app/metamodel/services/output-category.service.spec.ts b/client/src/app/metamodel/services/output-category.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..3e20ec1ad3631869f4ac51f47111bf6d53464e14 --- /dev/null +++ b/client/src/app/metamodel/services/output-category.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { OutputCategoryService } from './output-category.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { OutputCategory, OutputFamily } from '../models'; +import { CATEGORY, OUTPUT_FAMILY } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] OutputCategoryService', () => { + let service: OutputCategoryService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + OutputCategoryService + ] + }); + service = TestBed.inject(OutputCategoryService); + }); + + it('#retrieveOutputCategoryList() should return an Observable<OutputCategory[]>', + inject([HttpTestingController, OutputCategoryService],(httpMock: HttpTestingController, service: OutputCategoryService) => { + const mockResponse = []; + + service.retrieveOutputCategoryList('myDataset').subscribe((event: OutputCategory[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/output-category'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addOutputCategory() should return an Observable<OutputCategory>', + inject([HttpTestingController, OutputCategoryService],(httpMock: HttpTestingController, service: OutputCategoryService) => { + const mockResponse = CATEGORY; + + service.addOutputCategory(CATEGORY).subscribe((event: OutputCategory) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/output-family/1/output-category'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editOutputCategory() should return an Observable<OutputCategory>', + inject([HttpTestingController, OutputCategoryService],(httpMock: HttpTestingController, service: OutputCategoryService) => { + const mockResponse = CATEGORY; + + service.editOutputCategory(CATEGORY).subscribe((event: OutputCategory) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/output-category/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteOutputCategory() should return an Observable<object>', + inject([HttpTestingController, OutputCategoryService],(httpMock: HttpTestingController, service: OutputCategoryService) => { + const mockResponse = {}; + + service.deleteOutputCategory(1).subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/output-category/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/output-category.service.ts b/client/src/app/metamodel/services/output-category.service.ts index b13ef00fdb66caecacd3a7d25c428dd263164227..ae9cc7bdff1e1daad2dcda8ff2bedc84591398e3 100644 --- a/client/src/app/metamodel/services/output-category.service.ts +++ b/client/src/app/metamodel/services/output-category.service.ts @@ -15,23 +15,55 @@ import { Observable } from 'rxjs'; import { OutputCategory } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Output category service. + */ @Injectable() export class OutputCategoryService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves output category list for the given dataset. + * + * @param {string} datasetName - The dataset name. + * + * @return Observable<OutputCategory[]> + */ retrieveOutputCategoryList(datasetName: string): Observable<OutputCategory[]> { return this.http.get<OutputCategory[]>(`${this.config.apiUrl}/dataset/${datasetName}/output-category`); } + /** + * Adds a new output category. + * + * @param {OutputCategory} newOutputCategory - The output category. + * + * @return Observable<OutputCategory> + */ addOutputCategory(newOutputCategory: OutputCategory): Observable<OutputCategory> { return this.http.post<OutputCategory>(`${this.config.apiUrl}/output-family/${newOutputCategory.id_output_family}/output-category`, newOutputCategory); } + /** + * Modifies an output category. + * + * @param {OutputCategory} outputCategory - The output category. + * + * @return Observable<OutputCategory> + */ editOutputCategory(outputCategory: OutputCategory): Observable<OutputCategory> { return this.http.put<OutputCategory>(`${this.config.apiUrl}/output-category/${outputCategory.id}`, outputCategory); } - deleteOutputCategory(outputCategoryId: number) { + /** + * Removes an output category. + * + * @param {number} outputCategoryId - The output category ID. + * + * @return Observable<object> + */ + deleteOutputCategory(outputCategoryId: number): Observable<object> { return this.http.delete(`${this.config.apiUrl}/output-category/${outputCategoryId}`); } } diff --git a/client/src/app/metamodel/services/output-family.service.spec.ts b/client/src/app/metamodel/services/output-family.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..9455a0a8f5ff10aaba49b44ba76fcc6edbe8d741 --- /dev/null +++ b/client/src/app/metamodel/services/output-family.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { OutputFamilyService } from './output-family.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { OutputFamily } from '../models'; +import { OUTPUT_FAMILY } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] OutputFamilyService', () => { + let service: OutputFamilyService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + OutputFamilyService + ] + }); + service = TestBed.inject(OutputFamilyService); + }); + + it('#retrieveOutputFamilyList() should return an Observable<OutputFamily[]>', + inject([HttpTestingController, OutputFamilyService],(httpMock: HttpTestingController, service: OutputFamilyService) => { + const mockResponse = []; + + service.retrieveOutputFamilyList('myDataset').subscribe((event: OutputFamily[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/output-family'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addOutputFamily() should return an Observable<OutputFamily>', + inject([HttpTestingController, OutputFamilyService],(httpMock: HttpTestingController, service: OutputFamilyService) => { + const mockResponse = OUTPUT_FAMILY; + + service.addOutputFamily('myDataset', OUTPUT_FAMILY).subscribe((event: OutputFamily) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/dataset/myDataset/output-family'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editOutputFamily() should return an Observable<OutputFamily>', + inject([HttpTestingController, OutputFamilyService],(httpMock: HttpTestingController, service: OutputFamilyService) => { + const mockResponse = OUTPUT_FAMILY; + + service.editOutputFamily(OUTPUT_FAMILY).subscribe((event: OutputFamily) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/output-family/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteOutputFamily() should return an Observable<object>', + inject([HttpTestingController, OutputFamilyService],(httpMock: HttpTestingController, service: OutputFamilyService) => { + const mockResponse = {}; + + service.deleteOutputFamily(1).subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/output-family/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/output-family.service.ts b/client/src/app/metamodel/services/output-family.service.ts index 5c73604aa1b9706f3beb798620ef69a2c95c73c8..58c05651adb19e619a8ab041a4c782677f4b1879 100644 --- a/client/src/app/metamodel/services/output-family.service.ts +++ b/client/src/app/metamodel/services/output-family.service.ts @@ -15,23 +15,56 @@ import { Observable } from 'rxjs'; import { OutputFamily } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Output family service. + */ @Injectable() export class OutputFamilyService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves output family list for the given dataset. + * + * @param {string} datasetName - The dataset name. + * + * @return Observable<OutputFamily[]> + */ retrieveOutputFamilyList(datasetName: string): Observable<OutputFamily[]> { return this.http.get<OutputFamily[]>(`${this.config.apiUrl}/dataset/${datasetName}/output-family`); } + /** + * Adds a new output family for the given dataset. + * + * @param {string} datasetName - The dataset name. + * @param {OutputFamily} newOutputFamily - The output family. + * + * @return Observable<OutputFamily> + */ addOutputFamily(datasetName: string, newOutputFamily: OutputFamily): Observable<OutputFamily> { return this.http.post<OutputFamily>(`${this.config.apiUrl}/dataset/${datasetName}/output-family`, newOutputFamily); } - editOutputFamily(criteriaFamily: OutputFamily): Observable<OutputFamily> { - return this.http.put<OutputFamily>(`${this.config.apiUrl}/output-family/${criteriaFamily.id}`, criteriaFamily); + /** + * Modifies an output family. + * + * @param {OutputFamily} outputFamily - The output family. + * + * @return Observable<OutputFamily> + */ + editOutputFamily(outputFamily: OutputFamily): Observable<OutputFamily> { + return this.http.put<OutputFamily>(`${this.config.apiUrl}/output-family/${outputFamily.id}`, outputFamily); } - deleteOutputFamily(outputFamilyId: number) { + /** + * Removes an output family. + * + * @param {number} outputFamilyId - The output family ID. + * + * @return Observable<object> + */ + deleteOutputFamily(outputFamilyId: number): Observable<object> { return this.http.delete(`${this.config.apiUrl}/output-family/${outputFamilyId}`); } } diff --git a/client/src/app/metamodel/services/root-directory.service.spec.ts b/client/src/app/metamodel/services/root-directory.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..adf40e820e48fd7c0217d4c03afece1a9df2048a --- /dev/null +++ b/client/src/app/metamodel/services/root-directory.service.spec.ts @@ -0,0 +1,41 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { RootDirectoryService } from './root-directory.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { FileInfo } from '../models'; + +describe('[Instance][Metamodel][Services] RootDirectoryService', () => { + let service: RootDirectoryService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + RootDirectoryService + ] + }); + service = TestBed.inject(RootDirectoryService); + }); + + it('#retrieveRootDirectory() should return an Observable<FileInfo[]>', + inject([HttpTestingController, RootDirectoryService],(httpMock: HttpTestingController, service: RootDirectoryService) => { + const mockResponse = []; + + service.retrieveRootDirectory('/path').subscribe((event: FileInfo[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/file-explorer/path'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/root-directory.service.ts b/client/src/app/metamodel/services/root-directory.service.ts index 7935a76d4e87f55e97e5400f86e067b86dc81b47..ed8c19c37fe555106b5d84b8e0ea5041026b2056 100644 --- a/client/src/app/metamodel/services/root-directory.service.ts +++ b/client/src/app/metamodel/services/root-directory.service.ts @@ -15,10 +15,21 @@ import { Observable } from 'rxjs'; import { FileInfo } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Root directory service. + */ @Injectable() export class RootDirectoryService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves root directory with the given path. + * + * @param {string} path - The path. + * + * @return Observable<FileInfo[]> + */ retrieveRootDirectory(path: string): Observable<FileInfo[]> { return this.http.get<FileInfo[]>(`${this.config.apiUrl}/file-explorer${path}`); } diff --git a/client/src/app/metamodel/services/select-option.service.spec.ts b/client/src/app/metamodel/services/select-option.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..e079e3b0583462fb612e2917cca884937bd8df07 --- /dev/null +++ b/client/src/app/metamodel/services/select-option.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { SelectOptionService } from './select-option.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { SelectOption } from '../models'; +import { SELECT_OPTION } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] SelectOptionService', () => { + let service: SelectOptionService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + SelectOptionService + ] + }); + service = TestBed.inject(SelectOptionService); + }); + + it('#retrieveSelectOptionList() should return an Observable<SelectOption[]>', + inject([HttpTestingController, SelectOptionService],(httpMock: HttpTestingController, service: SelectOptionService) => { + const mockResponse = []; + + service.retrieveSelectOptionList().subscribe((event: SelectOption[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/option'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addSelectOption() should return an Observable<SelectOption>', + inject([HttpTestingController, SelectOptionService],(httpMock: HttpTestingController, service: SelectOptionService) => { + const mockResponse = SELECT_OPTION; + + service.addSelectOption(SELECT_OPTION).subscribe((event: SelectOption) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/option'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editSelectOption() should return an Observable<SelectOption>', + inject([HttpTestingController, SelectOptionService],(httpMock: HttpTestingController, service: SelectOptionService) => { + const mockResponse = SELECT_OPTION; + + service.editSelectOption(SELECT_OPTION).subscribe((event: SelectOption) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/option/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteSelectOption() should return an Observable<object>', + inject([HttpTestingController, SelectOptionService],(httpMock: HttpTestingController, service: SelectOptionService) => { + const mockResponse = {}; + + service.deleteSelectOption(1).subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/option/1'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/select-option.service.ts b/client/src/app/metamodel/services/select-option.service.ts index 719b902128db274dd66729fd83e8efeaa52238fb..904a627fff4c326583cb1aa7cc3356ec211999a0 100644 --- a/client/src/app/metamodel/services/select-option.service.ts +++ b/client/src/app/metamodel/services/select-option.service.ts @@ -15,23 +15,51 @@ import { Observable } from 'rxjs'; import { SelectOption } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Select option service. + */ @Injectable() export class SelectOptionService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves select option list. + * + * @return Observable<SelectOption[]> + */ retrieveSelectOptionList(): Observable<SelectOption[]> { return this.http.get<SelectOption[]>(`${this.config.apiUrl}/option`); } + /** + * Adds a new select option. + * + * @param {SelectOption} settingsSelectOption - The select option. + * + * @return Observable<SelectOption> + */ addSelectOption(settingsSelectOption: SelectOption): Observable<SelectOption> { return this.http.post<SelectOption>(`${this.config.apiUrl}/option`, settingsSelectOption); } + /** + * Modifies a new select option. + * + * @param {SelectOption} settingsSelectOption - The select option. + * + * @return Observable<SelectOption> + */ editSelectOption(settingsSelectOption: SelectOption): Observable<SelectOption> { return this.http.put<SelectOption>(`${this.config.apiUrl}/option/${settingsSelectOption.id}`, settingsSelectOption); } - deleteSelectOption(id: number) { + /** + * Removes a select option. + * + * @param {number} id - The select option ID. + */ + deleteSelectOption(id: number): Observable<object> { return this.http.delete(`${this.config.apiUrl}/option/${id}`); } } diff --git a/client/src/app/metamodel/services/select.service.spec.ts b/client/src/app/metamodel/services/select.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab449586c7eae1da626aedcd58503db57c4997b5 --- /dev/null +++ b/client/src/app/metamodel/services/select.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { SelectService } from './select.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { Select } from '../models'; +import { SELECT } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] SelectService', () => { + let service: SelectService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + SelectService + ] + }); + service = TestBed.inject(SelectService); + }); + + it('#retrieveSelectList() should return an Observable<Select[]>', + inject([HttpTestingController, SelectService],(httpMock: HttpTestingController, service: SelectService) => { + const mockResponse = []; + + service.retrieveSelectList().subscribe((event: Select[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/select'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addSelect() should return an Observable<Select>', + inject([HttpTestingController, SelectService],(httpMock: HttpTestingController, service: SelectService) => { + const mockResponse = SELECT; + + service.addSelect(SELECT).subscribe((event: Select) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/select'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editSelect() should return an Observable<Select>', + inject([HttpTestingController, SelectService],(httpMock: HttpTestingController, service: SelectService) => { + const mockResponse = SELECT; + + service.editSelect(SELECT).subscribe((event: Select) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/select/mySelect'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteSelect() should return an Observable<object>', + inject([HttpTestingController, SelectService],(httpMock: HttpTestingController, service: SelectService) => { + const mockResponse = {}; + + service.deleteSelect('mySelect').subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/select/mySelect'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/select.service.ts b/client/src/app/metamodel/services/select.service.ts index f1e440f8f6c87cfd6acb41c370b2407d3ecbac4e..337ed1b2bd66f581ed85987f11b373c09cf0a641 100644 --- a/client/src/app/metamodel/services/select.service.ts +++ b/client/src/app/metamodel/services/select.service.ts @@ -15,23 +15,53 @@ import { Observable } from 'rxjs'; import { Select } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Select service. + */ @Injectable() export class SelectService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves select list. + * + * @return Observable<Select[]> + */ retrieveSelectList(): Observable<Select[]> { return this.http.get<Select[]>(`${this.config.apiUrl}/select`); } + /** + * Adds a new select. + * + * @param {Select} select - The select. + * + * @return Observable<Select> + */ addSelect(select: Select): Observable<Select> { return this.http.post<Select>(`${this.config.apiUrl}/select`, select); } + /** + * Modifies a select. + * + * @param {Select} select - The select. + * + * @return Observable<Select> + */ editSelect(select: Select): Observable<Select> { return this.http.put<Select>(`${this.config.apiUrl}/select/${select.name}`, select); } - deleteSelect(name: string) { + /** + * Removes a select. + * + * @param {string} name - The select name. + * + * @return Observable<object> + */ + deleteSelect(name: string): Observable<object> { return this.http.delete(`${this.config.apiUrl}/select/${name}`); } } diff --git a/client/src/app/metamodel/services/survey.service.spec.ts b/client/src/app/metamodel/services/survey.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..de82c5e0db5b8ba39a51a53ac7609b2a6eb479cf --- /dev/null +++ b/client/src/app/metamodel/services/survey.service.spec.ts @@ -0,0 +1,102 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { SurveyService } from './survey.service'; +import { AppConfigService } from 'src/app/app-config.service'; +import { Survey } from '../models'; +import { SURVEY } from '../../../test-data'; + +describe('[Instance][Metamodel][Services] SurveyService', () => { + let service: SurveyService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + SurveyService + ] + }); + service = TestBed.inject(SurveyService); + }); + + it('#retrieveSurveyList() should return an Observable<Survey[]>', + inject([HttpTestingController, SurveyService],(httpMock: HttpTestingController, service: SurveyService) => { + const mockResponse = []; + + service.retrieveSurveyList().subscribe((event: Survey[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/survey'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('GET'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#addSurvey() should return an Observable<Survey>', + inject([HttpTestingController, SurveyService],(httpMock: HttpTestingController, service: SurveyService) => { + const mockResponse = SURVEY; + + service.addSurvey(SURVEY).subscribe((event: Survey) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/survey'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('POST'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#editSurvey() should return an Observable<Survey>', + inject([HttpTestingController, SurveyService],(httpMock: HttpTestingController, service: SurveyService) => { + const mockResponse = SURVEY; + + service.editSurvey(SURVEY).subscribe((event: Survey) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/survey/mySurvey'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('PUT'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); + + it('#deleteSurvey() should return an Observable<object>', + inject([HttpTestingController, SurveyService],(httpMock: HttpTestingController, service: SurveyService) => { + const mockResponse = {}; + + service.deleteSurvey('mySurvey').subscribe((event: object) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/survey/mySurvey'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.method).toEqual('DELETE'); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/survey.service.ts b/client/src/app/metamodel/services/survey.service.ts index d4fdd6e3d294f76786a64dd9a3be5e01e6153131..926e6618528e1bca3a9a2242f68a9bbecaf2cc45 100644 --- a/client/src/app/metamodel/services/survey.service.ts +++ b/client/src/app/metamodel/services/survey.service.ts @@ -15,23 +15,53 @@ import { Observable } from 'rxjs'; import { Survey } from '../models'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Survey service. + */ @Injectable() export class SurveyService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves survey list. + * + * @return Observable<Survey[]> + */ retrieveSurveyList(): Observable<Survey[]> { return this.http.get<Survey[]>(`${this.config.apiUrl}/survey`); } + /** + * Adds a new survey. + * + * @param {Survey} newSurvey - The survey. + * + * @return Observable<Survey> + */ addSurvey(newSurvey: Survey): Observable<Survey> { return this.http.post<Survey>(`${this.config.apiUrl}/survey`, newSurvey); } + /** + * Modifies a survey. + * + * @param {Survey} survey - The survey. + * + * @return Observable<Survey> + */ editSurvey(survey: Survey): Observable<Survey> { return this.http.put<Survey>(`${this.config.apiUrl}/survey/${survey.name}`, survey); } - deleteSurvey(surveyName: string) { + /** + * Removes a survey. + * + * @param {string} surveyName - The survey name. + * + * @return Observable<object> + */ + deleteSurvey(surveyName: string): Observable<object> { return this.http.delete(`${this.config.apiUrl}/survey/${surveyName}`); } } diff --git a/client/src/app/metamodel/services/table.service.spec.ts b/client/src/app/metamodel/services/table.service.spec.ts new file mode 100644 index 0000000000000000000000000000000000000000..525cf0841d73a6db25c05cb0a7333b6a3bd80848 --- /dev/null +++ b/client/src/app/metamodel/services/table.service.spec.ts @@ -0,0 +1,39 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { TableService } from './table.service'; +import { AppConfigService } from 'src/app/app-config.service'; + +describe('[Instance][Metamodel][Services] TableService', () => { + let service: TableService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + providers: [ + { provide: AppConfigService, useValue: { apiUrl: 'http://testing.com' } }, + TableService + ] + }); + service = TestBed.inject(TableService); + }); + + it('#retrieveTableList() should return an Observable<string[]>', + inject([HttpTestingController, TableService],(httpMock: HttpTestingController, service: TableService) => { + const mockResponse = ['Table']; + + service.retrieveTableList(1).subscribe((event: string[]) => { + expect(event).toEqual(mockResponse); + }); + + const mockRequest = httpMock.expectOne('http://testing.com/database/1/table'); + + expect(mockRequest.cancelled).toBeFalsy(); + expect(mockRequest.request.responseType).toEqual('json'); + mockRequest.flush(mockResponse); + + httpMock.verify(); + } + ) + ); +}); diff --git a/client/src/app/metamodel/services/table.service.ts b/client/src/app/metamodel/services/table.service.ts index 2d67e4a1860d21975117bcbcdb99f52401925e4a..c9fe534dca73eeaf4872c377d6615a13062c2c9f 100644 --- a/client/src/app/metamodel/services/table.service.ts +++ b/client/src/app/metamodel/services/table.service.ts @@ -14,10 +14,21 @@ import { Observable } from 'rxjs'; import { AppConfigService } from 'src/app/app-config.service'; +/** + * @class + * @classdesc Table service. + */ @Injectable() export class TableService { constructor(private http: HttpClient, private config: AppConfigService) { } + /** + * Retrieves results for the given parameters. + * + * @param {number} idDatabase - The database ID. + * + * @return Observable<string[]> + */ retrieveTableList(idDatabase: number): Observable<string[]> { return this.http.get<string[]>(`${this.config.apiUrl}/database/${idDatabase}/table`); } diff --git a/client/src/test-data.ts b/client/src/test-data.ts index 69575072bb5b538deaf44ebc1f551865421e38e0..fbde4ef15bbaa951120e5135cb4f1561f9263573 100644 --- a/client/src/test-data.ts +++ b/client/src/test-data.ts @@ -1,13 +1,47 @@ import { - Attribute, + Attribute, Column, CriteriaFamily, Database, Dataset, - DatasetFamily, + DatasetFamily, FileInfo, Group, Instance, OutputCategory, - OutputFamily, SelectOption, + OutputFamily, Select, SelectOption, Survey } from './app/metamodel/models'; +export const DATABASE_LIST: Database[] = [ + { + id: 1, + label: 'database one', + dbname: 'database-one', + dbtype: 'type', + dbhost: 'host', + dbport: 1234, + dblogin: 'login', + dbpassword: 'pwd' + }, + { + id: 2, + label: 'database two', + dbname: 'database-two', + dbtype: 'type', + dbhost: 'host', + dbport: 1234, + dblogin: 'login', + dbpassword: 'pwd' + } +]; + +export const DATABASE: Database = { + id: 1, + label: 'my database', + dbname: 'myDatabase', + dbtype: 'type', + dbhost: 'host', + dbport: 1234, + dblogin: 'login', + dbpassword: 'pwd' +}; + export const SURVEY_LIST: Survey[] = [ { name: 'survey-one', @@ -29,6 +63,16 @@ export const SURVEY_LIST: Survey[] = [ } ]; +export const SURVEY: Survey = { + name: 'mySurvey', + label: 'My Survey', + description: 'This is my survey', + link: 'http://my-survey.com', + manager: 'Orelsan', + id_database: 1, + nb_datasets: 2 +}; + export const INSTANCE_LIST: Instance[] = [ { name: 'myOtherInstance', @@ -132,11 +176,35 @@ export const INSTANCE: Instance = { nb_datasets: 2 }; +export const GROUP_LIST: Group[] = [ + { + id: 1, + role: 'admin', + instance_name: 'myInstance', + datasets: ['myDataset', 'otherDataset'] + }, + { + id: 2, + role: 'guest', + instance_name: 'myInstance', + datasets: ['myDataset', 'otherDataset'] + } +]; + +export const GROUP: Group = { + id: 1, + role: 'admin', + instance_name: 'myInstance', + datasets: ['myDataset', 'otherDataset'] +}; + export const DATASET_FAMILY_LIST: DatasetFamily[] = [ { id: 2, label: 'My second dataset family', display: 2, opened: false }, { id: 1, label: 'My first dataset family', display: 1, opened: true } ]; +export const DATASET_FAMILY: DatasetFamily = { id: 1, label: 'My first dataset family', display: 1, opened: true }; + export const DATASET_LIST: Dataset[] = [ { name: 'myDataset', @@ -210,7 +278,7 @@ export const DATASET_LIST: Dataset[] = [ survey_label: 'More about this survey' }, cone_search: { - cone_search_enabled: true, + cone_search_enabled: false, cone_search_opened: true, cone_search_column_ra: 1, cone_search_column_dec: 2, @@ -389,6 +457,36 @@ export const ATTRIBUTE_LIST: Attribute[] = [ } ]; +export const ATTRIBUTE: Attribute = { + id: 1, + name: 'name_one', + label: 'label_one', + form_label: 'form_label_one', + description: 'description_one', + output_display: 2, + criteria_display: 2, + search_flag: 'ID', + search_type: 'field', + operator: '=', + type: 'integer', + order_by: true, + detail: true, + display_detail: 2, + options: [ + { label: 'Three', value: 'three', display: 3 }, + { label: 'One', value: 'one', display: 1 }, + { label: 'Two', value: 'two', display: 2 } + ], + id_output_category: 2 +}; + +export const COLUMN_LIST: Column[] = [ + { name: 'myCol', type: 'type' }, + { name: 'anotherCol', type: 'type' } +]; + +export const COLUMN: Column = { name: 'myCol', type: 'type' }; + export const CATEGORY_LIST: OutputCategory[] = [ { id: 1, @@ -410,17 +508,68 @@ export const CATEGORY_LIST: OutputCategory[] = [ } ]; +export const CATEGORY: OutputCategory = { + id: 1, + label: 'Another output category', + display: 20, + id_output_family: 1 +}; + export const OUTPUT_FAMILY_LIST: OutputFamily[] = [ { id: 2, label: 'Output family Two', display: 2, opened: true }, { id: 1, label: 'Output family One', display: 1, opened: false } ]; +export const OUTPUT_FAMILY: OutputFamily = { id: 1, label: 'Output family One', display: 1, opened: false }; + +export const CRITERIA_FAMILY_LIST: CriteriaFamily[] = [ + { + id: 1, + label: 'myCriteriaFamily', + display: 1, + opened: true + }, + { + id: 2, + label: 'anotherCriteriaFamily', + display: 1, + opened: true + } +]; + +export const CRITERIA_FAMILY: CriteriaFamily = { + id: 1, + label: 'myCriteriaFamily', + display: 1, + opened: true +}; + +export const SELECT_LIST: Select[] = [ + { name: 'select-one', label: 'Select one' }, + { name: 'select-two', label: 'Select two' } +]; + +export const SELECT: Select = { name: 'mySelect', label: 'My select' }; + export const SELECT_OPTION_LIST: SelectOption[] = [ { id: 2, label: 'select_option_label_two', value: 'select_option_label_two', display: 2, select_name: 'name_two' }, { id: 1, label: 'select_option_label_one', value: 'select_option_label_one', display: 1, select_name: 'name_one' } ]; +export const SELECT_OPTION: SelectOption = { + id: 1, + label: 'select_option_label_one', + value: 'select_option_label_one', + display: 1, + select_name: 'name_one' +}; + export const OBJECT_DETAIL: any = { label_four: 'spec1d', label_five: 5 }; + +export const FILES: FileInfo[] = [ + { name: 'file-one', size: 1, type: 'type', mimetype: 'mimetype' }, + { name: 'file-two', size: 2, type: 'type', mimetype: 'mimetype' }, +];