From 72449d5d837d5023c234349d9b43463dcace87a7 Mon Sep 17 00:00:00 2001
From: Tifenn Guillas <tifenn.guillas@lam.fr>
Date: Mon, 20 Dec 2021 12:15:03 +0100
Subject: [PATCH] WIP: Tests and comments on metamodel reducers

---
 .../effects/select-option.effects.spec.ts     | 288 +++++++++++++++++
 .../effects/select-option.effects.ts          |  72 +++--
 .../metamodel/effects/select.effects.spec.ts  | 290 ++++++++++++++++++
 .../app/metamodel/effects/select.effects.ts   |  71 +++--
 .../metamodel/effects/table.effects.spec.ts   |  61 ++++
 .../app/metamodel/effects/table.effects.ts    |   1 +
 6 files changed, 746 insertions(+), 37 deletions(-)
 create mode 100644 client/src/app/metamodel/effects/select-option.effects.spec.ts
 create mode 100644 client/src/app/metamodel/effects/select.effects.spec.ts
 create mode 100644 client/src/app/metamodel/effects/table.effects.spec.ts

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 00000000..cc5c965c
--- /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: { retrieveCoordinates: jest.fn() }},
+                { 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 eb98f854..2beaf5ae 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 00000000..3032179c
--- /dev/null
+++ b/client/src/app/metamodel/effects/select.effects.spec.ts
@@ -0,0 +1,290 @@
+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: {retrieveCoordinates: jest.fn()}},
+                {
+                    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 b3f53c91..c4a21e36 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/table.effects.spec.ts b/client/src/app/metamodel/effects/table.effects.spec.ts
new file mode 100644
index 00000000..ce9ea659
--- /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: { retrieveCoordinates: jest.fn() }},
+                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 669c08fe..9a507ce2 100644
--- a/client/src/app/metamodel/effects/table.effects.ts
+++ b/client/src/app/metamodel/effects/table.effects.ts
@@ -22,6 +22,7 @@ import { TableService } from '../services/table.service';
  */
 @Injectable()
 export class TableEffects {
+
     /**
      * Calls action to retrieve table list.
      */
-- 
GitLab