SI-126 unit tests + SI-178 comments form 2nd code review

Signed-off-by: Peter Buschmann <peter.buschmann@pta.de>
diff --git a/angular.json b/angular.json
index bb09ee6..3884180 100644
--- a/angular.json
+++ b/angular.json
@@ -121,7 +121,20 @@
             "karmaConfig": "projects/grid-failure-information-app/karma.conf.js",
             "assets": ["projects/grid-failure-information-app/src/favicon.ico", "projects/grid-failure-information-app/src/assets"],
             "styles": ["projects/grid-failure-information-app/src/styles.scss", "projects/grid-failure-information-app/src/styles/variables.scss"],
-            "scripts": []
+            "scripts": [],
+            "codeCoverage": true,
+            "codeCoverageExclude": [
+              "src/environments/**",
+              "e2e/*.ts",
+              "**/*.action.*",
+              "**/*.model.*",
+              "**/*.module.*",
+              "**/*column-definition*",
+              "**/*api-client.*",
+              "**/*.animation.*",
+              "**/lib/**/*",
+              "**/testing/**/*"
+            ]
           }
         },
         "lint": {
diff --git a/i18n/grid-failure.de.json b/i18n/grid-failure.de.json
index 092c3f5..147d834 100644
--- a/i18n/grid-failure.de.json
+++ b/i18n/grid-failure.de.json
@@ -5,6 +5,10 @@
   },
   "GridFailure": {
     "Title": "Störungsmeldung",
+    "SubTitle1": "Störungsinformation",
+    "SubTitle2": "Störungsdetails",
+    "SubTitle3": "Störungsort [NS]",
+    "SubTitle4": "Stationsattribute [MS]",
     "Branch": "Sparte",
     "BranchColorCode": "Farbe der Sparte",
     "City": "Ort (betroffene Orte)",
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.html b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.html
index 988e349..46d47a2 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.html
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.html
@@ -18,7 +18,7 @@
     <form [ngrxFormState]="gridFailureSandbox.gridFailureDetailsFormState$ | async">
       <div>
         <div class="section">
-          <label class="heading">Störungsinformation</label>
+          <label class="heading">{{ 'GridFailure.SubTitle1' | translate }}</label>
 
           <!-- failureClassificationId (temporär) -->
           <div class="form-group row">
@@ -172,7 +172,7 @@
         </div>
 
         <div class="section">
-          <label class="heading">Störungsdetails</label>
+          <label class="heading">{{ 'GridFailure.SubTitle2' | translate }}</label>
 
           <!-- branchId temporär -->
           <div class="form-group row">
@@ -259,7 +259,7 @@
                 readonly
               />
               <div class="input-group-append">
-                <button class="btn btn-outline-primary calendar" (click)="reset('failureBegin')" type="button">
+                <button class="btn btn-outline-primary calendar" (click)="reset(gridFailureSandbox.datePickerResetEnum.FailureBegin)" type="button">
                   <em class="fa fa-times-circle" aria-hidden="true"></em>
                 </button>
               </div>
@@ -287,7 +287,7 @@
                 readonly
               />
               <div class="input-group-append">
-                <button class="btn btn-outline-primary calendar" (click)="reset('failureEndPlanned')" type="button">
+                <button class="btn btn-outline-primary calendar" (click)="reset(gridFailureSandbox.datePickerResetEnum.FailureEndPlanned)" type="button">
                   <em class="fa fa-times-circle" aria-hidden="true"></em>
                 </button>
               </div>
@@ -315,7 +315,7 @@
                 readonly
               />
               <div class="input-group-append">
-                <button class="btn btn-outline-primary calendar" (click)="reset('failureEndResupplied')" type="button">
+                <button class="btn btn-outline-primary calendar" (click)="reset(gridFailureSandbox.datePickerResetEnum.FailureEndResupplied)" type="button">
                   <em class="fa fa-times-circle" aria-hidden="true"></em>
                 </button>
               </div>
@@ -355,7 +355,7 @@
         </div>
 
         <div class="section">
-          <label class="heading">Störungsort [NS]</label>
+          <label class="heading">{{ 'GridFailure.SubTitle3' | translate }}</label>
           <!-- postcode -->
           <div class="form-group row">
             <label for="postcode" class="col-sm-2 col-form-label">{{ 'GridFailure.Postcode' | translate }}</label>
@@ -435,7 +435,7 @@
         </div>
 
         <div class="section">
-          <label class="heading">Stationsattribute [MS]</label>
+          <label class="heading">{{ 'GridFailure.SubTitle4' | translate }}</label>
 
           <!-- stationDescription -->
           <div class="form-group row">
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.spec.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.spec.ts
index 12a2569..8805ca4 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.spec.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-details/grid-failure-details.component.spec.ts
@@ -10,23 +10,33 @@
  *
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
-import { GridFailureSandbox } from '@pages/grid-failure/grid-failure.sandbox';
-import { GridFailureDetailsComponent } from '@pages/grid-failure/grid-failure-details/grid-failure-details.component';
+import { GridFailureDetailsComponent } from './grid-failure-details.component';
 
 describe('GridFailureDetailsComponent', () => {
   let component: GridFailureDetailsComponent;
-  let sandbox: GridFailureSandbox;
+  let gridFailureSandbox: any;
 
   beforeEach(() => {
-    sandbox = {
+    gridFailureSandbox = {
+      clearGridFailurePropertyData() {},
       registerEvents() {},
-      endSubscriptions() {},
-      clear() {},
     } as any;
-    component = new GridFailureDetailsComponent(sandbox);
+    component = new GridFailureDetailsComponent(gridFailureSandbox);
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
+
+  it('should registerEvents OnInit', () => {
+    let spy = spyOn(gridFailureSandbox, 'registerEvents');
+    component.ngOnInit();
+    expect(spy).toHaveBeenCalled();
+  });
+
+  it('should call "clearGridFailurePropertyData()" in Sandbox if "reset()" is called', () => {
+    const spy = spyOn(gridFailureSandbox, 'clearGridFailurePropertyData');
+    component.reset('test');
+    expect(spy).toHaveBeenCalled();
+  });
 });
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.html b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.html
index d12745f..da6a262 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.html
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.html
@@ -14,7 +14,7 @@
   <div header>
     <span>{{ 'GridFailures.Title' | translate }}</span>
     <div class="d-flex flex-row-reverse">
-      <button class="btn btn-primary new-button" (click)="sandbox.newGridFailure()">
+      <button class="btn btn-primary new-button" (click)="sandbox.createGridFailure()">
         {{ 'GridFailures.New' | translate }}
       </button>
     </div>
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.spec.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.spec.ts
index 7433066..9d9dbb3 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.spec.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.spec.ts
@@ -12,13 +12,20 @@
  ********************************************************************************/
 import { GridFailureListComponent } from '@pages/grid-failure/grid-failure-list/grid-failure-list.component';
 import { Router } from '@angular/router';
+import { GridFailure } from '@shared/models';
+import { GridFailureSandbox } from '@pages/grid-failure/grid-failure.sandbox';
+import { Subscription } from 'rxjs';
 
-describe('GridFailureListComponent', () => {
+describe('GridFailureListComponent ', () => {
   let component: GridFailureListComponent;
   let router: Router;
-  const sandbox: any = () => {};
+  let sandbox: GridFailureSandbox;
+  let subscription: Subscription;
 
   beforeEach(() => {
+    router = { navigate() {} } as any;
+    sandbox = { endSubscriptions() {} } as any;
+    subscription = { unsubscribe() {} } as any;
     component = new GridFailureListComponent(sandbox, router);
   });
 
@@ -30,4 +37,18 @@
     component.ngOnInit();
     expect(component.gridOptions.context.icons.edit).toBeTruthy();
   });
+
+  it('should call appropriate functions for edit event', () => {
+    const spy: any = spyOn(router, 'navigate');
+    component.ngOnInit();
+    component.gridOptions.context.eventSubject.next({ type: 'edit', data: new GridFailure() });
+    expect(spy).toHaveBeenCalled();
+  });
+
+  it('should unsubscribe OnDestroy', () => {
+    const spy: any = spyOn(sandbox, 'endSubscriptions');
+    component['_subscription'] = new Subscription();
+    component.ngOnDestroy();
+    expect(spy).toHaveBeenCalled();
+  });
 });
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.ts
index 611e8e4..194cd79 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure-list/grid-failure-list.component.ts
@@ -16,7 +16,7 @@
 import { GRID_FAILURE_COLDEF } from '@pages/grid-failure/grid-failure-list/grid-failure-list-column-definition';
 import { GridFailureSandbox } from '@pages/grid-failure/grid-failure.sandbox';
 import { Router } from '@angular/router';
-import { Subscription } from 'rxjs';
+import { takeUntil } from 'rxjs/operators';
 
 @Component({
   selector: 'app-grid-failure-list',
@@ -26,9 +26,8 @@
 export class GridFailureListComponent extends BaseList implements OnInit, OnDestroy {
   public columnDefinition: any = GRID_FAILURE_COLDEF;
   public frameworkComponents: { setFilterComponent: any };
-  private _subscription: Subscription;
 
-  constructor(public sandbox: GridFailureSandbox, private router: Router) {
+  constructor(public sandbox: GridFailureSandbox, private _router: Router) {
     super();
     this.frameworkComponents = { setFilterComponent: SetFilterComponent };
   }
@@ -39,15 +38,15 @@
       icons: { edit: true },
     };
 
-    this._subscription = this.gridOptions.context.eventSubject.subscribe(event => {
+    this.gridOptions.context.eventSubject.pipe(takeUntil(this._endSubscriptions$)).subscribe(event => {
       if (event.type === 'edit' || event.type === 'readonly') {
-        this.router.navigate(['/grid-failures', event.data.id]);
+        this._router.navigate(['/grid-failures', event.data.id]);
       }
     });
   }
 
   ngOnDestroy() {
-    this._subscription.unsubscribe();
+    this._endSubscriptions$.next(true);
     this.sandbox.endSubscriptions();
   }
 }
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.resolver.spec.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.resolver.spec.ts
index 8db2758..9ebb055 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.resolver.spec.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.resolver.spec.ts
@@ -20,6 +20,7 @@
     sandbox = {
       clear() {},
       loadGridFailures() {},
+      loadGridFailure() {},
     } as any;
   });
 
@@ -31,9 +32,17 @@
     expect(component).toBeTruthy();
   });
 
-  // it('should call loadGridFailures', () => {
-  //   const spy = spyOn(sandbox, 'loadGridFailures');
-  //   component.resolve();
-  //   expect(spy).toHaveBeenCalled();
-  // });
+  it('should call loadGridFailures', () => {
+    const spy = spyOn(sandbox, 'loadGridFailures');
+    let ar: any = { params: { gridFailureId: undefined } };
+    component.resolve(ar);
+    expect(spy).toHaveBeenCalled();
+  });
+
+  it('should call loadGridFailure', () => {
+    const spy = spyOn(sandbox, 'loadGridFailure');
+    let ar: any = { params: { gridFailureId: '6432a9c9-0384-44af-9bb8-34f2878d7b49' } };
+    component.resolve(ar);
+    expect(spy).toHaveBeenCalled();
+  });
 });
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.sandbox.spec.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.sandbox.spec.ts
index 934ac4a..7b1d777 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.sandbox.spec.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.sandbox.spec.ts
@@ -30,6 +30,9 @@
   beforeEach(() => {
     appState = { dispatch: () => {}, pipe: () => of(true), select: () => of(true) } as any;
     actionSubject = { pipe: () => of(true) } as any;
+    utilService = { displayNotification() {} } as any;
+    router = { navigateByUrl() {} } as any;
+    modalService = { open() {} } as any;
     spyOn(appState, 'dispatch').and.callFake(() => {});
 
     service = new GridFailureSandbox(appState, actionSubject, router, utilService, modalService);
@@ -43,4 +46,52 @@
     service.loadGridFailures();
     expect(appState.dispatch).toHaveBeenCalledWith(gridFailureActions.loadGridFailures());
   });
+
+  it('should dispatch loadGridFailure Action via loadGridFailure(id)', () => {
+    service.loadGridFailure('id');
+    expect(appState.dispatch).toHaveBeenCalledWith(gridFailureActions.loadGridFailureDetail({ payload: 'id' }));
+  });
+
+  it('should navigate to new route via createGridFailure()', () => {
+    let spy = spyOn(router, 'navigateByUrl');
+    service.createGridFailure();
+    expect(spy).toHaveBeenCalledWith('/grid-failures/new');
+  });
+
+  it('should dispatch the right "setValueAction"', () => {
+    let property = 'failureBegin';
+    service.clearGridFailurePropertyData(property);
+    expect(appState.dispatch).toHaveBeenCalledTimes(1);
+
+    property = 'failureEndPlanned';
+    service.clearGridFailurePropertyData(property);
+    expect(appState.dispatch).toHaveBeenCalledTimes(2);
+
+    property = 'failureEndResupplied';
+    service.clearGridFailurePropertyData(property);
+    expect(appState.dispatch).toHaveBeenCalledTimes(3);
+
+    property = 'test';
+    service.clearGridFailurePropertyData(property);
+    expect(appState.dispatch).toHaveBeenCalledTimes(3);
+  });
+
+  it('should clear form state when current change is canceled and form state is pristine', () => {
+    let spy = spyOn(service, 'clear');
+    service.currentFormState = { isPristine: true } as any;
+    service.cancel();
+    expect(spy).toHaveBeenCalled();
+  });
+
+  it('should open modal when current change is canceled and form state is not pristine', () => {
+    spyOn(service['_modalService'], 'open').and.returnValue({ componentInstance: { title: '' }, result: { then: () => of(true) } } as any);
+    service.currentFormState = { isPristine: false } as any;
+    service.cancel();
+    expect(modalService.open).toHaveBeenCalled();
+  });
+
+  it('should dispatch actions to clear the form', () => {
+    service.clear();
+    expect(appState.dispatch).toHaveBeenCalledTimes(2);
+  });
 });
diff --git a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.sandbox.ts b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.sandbox.ts
index 04e8e74..fac63ab 100644
--- a/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.sandbox.ts
+++ b/projects/grid-failure-information-app/src/app/pages/grid-failure/grid-failure.sandbox.ts
@@ -17,7 +17,7 @@
 import * as gridFailuresDetailFormReducer from '@shared/store/reducers/grid-failures/grid-failure-details-form.reducer';
 import { Observable } from 'rxjs';
 import { ILoadGridFailuresSuccess, ILoadGridFailureSuccess } from '@shared/store/actions/grid-failures.action';
-import * as gridFailureActions from '@app/shared/store/actions/grid-failures.action';
+import * as gridFailureActions from '@shared/store/actions/grid-failures.action';
 import { takeUntil, take } from 'rxjs/operators';
 import { ofType } from '@ngrx/effects';
 import { Router } from '@angular/router';
@@ -30,21 +30,23 @@
 import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
 import { SafetyQueryDialogComponent } from '@shared/components/dialogs/safety-query-dialog/safety-query-dialog.component';
 import { navigateHome } from '@shared/utility';
+import { DatePickerResetEnum } from '@shared/constants/enums';
 
 @Injectable()
 export class GridFailureSandbox extends BaseSandbox {
-  public gridFailureList$ = this.appState$.select(store.getGridFailuresData);
+  public gridFailureList$: Observable<GridFailure[]> = this.appState$.select(store.getGridFailuresData);
   public gridFailureListLoading$: Observable<boolean> = this.appState$.select(store.getGridFailuresLoading);
 
   public gridFailureDetailsFormState$: Observable<FormGroupState<GridFailure>> = this.appState$.select(store.getGridFailuresDetails);
   public currentFormState: FormGroupState<GridFailure>;
+  public datePickerResetEnum = DatePickerResetEnum;
 
   constructor(
     protected appState$: Store<store.State>,
-    protected actionsSubject: ActionsSubject,
-    protected router: Router,
-    protected utilService: UtilService,
-    protected modalService: NgbModal
+    private _actionsSubject: ActionsSubject,
+    private _router: Router,
+    private _utilService: UtilService,
+    private _modalService: NgbModal
   ) {
     super(appState$);
   }
@@ -53,29 +55,29 @@
 
   public loadGridFailures(): Observable<ILoadGridFailuresSuccess> {
     this.appState$.dispatch(gridFailureActions.loadGridFailures());
-    return this.actionsSubject.pipe(ofType(gridFailureActions.loadGridFailuresSuccess), take(1));
+    return this._actionsSubject.pipe(ofType(gridFailureActions.loadGridFailuresSuccess), take(1));
   }
 
   public loadGridFailure(gridFailureId: string): Observable<ILoadGridFailureSuccess> {
     this.appState$.dispatch(gridFailureActions.loadGridFailureDetail({ payload: gridFailureId }));
-    return this.actionsSubject.pipe(ofType(gridFailureActions.loadGridFailureDetailSuccess), take(1));
+    return this._actionsSubject.pipe(ofType(gridFailureActions.loadGridFailureDetailSuccess), take(1));
   }
 
-  public newGridFailure() {
+  public createGridFailure() {
     this.appState$.dispatch(new SetValueAction(fromGridFailuresDetailFormReducer.FORM_ID, fromGridFailuresDetailFormReducer.INITIAL_STATE.value));
     this.appState$.dispatch(new ResetAction(fromGridFailuresDetailFormReducer.FORM_ID));
-    this.router.navigateByUrl('/grid-failures/new');
+    this._router.navigateByUrl('/grid-failures/new');
   }
 
   public clearGridFailurePropertyData(property: string): void {
     switch (property) {
-      case 'failureBegin':
+      case this.datePickerResetEnum.FailureBegin:
         this.appState$.dispatch(new SetValueAction(gridFailuresDetailFormReducer.INITIAL_STATE.controls.failureBegin.id, null));
         break;
-      case 'failureEndPlanned':
+      case this.datePickerResetEnum.FailureEndPlanned:
         this.appState$.dispatch(new SetValueAction(gridFailuresDetailFormReducer.INITIAL_STATE.controls.failureEndPlanned.id, null));
         break;
-      case 'failureEndResupplied':
+      case this.datePickerResetEnum.FailureEndResupplied:
         this.appState$.dispatch(new SetValueAction(gridFailuresDetailFormReducer.INITIAL_STATE.controls.failureEndResupplied.id, null));
         break;
 
@@ -86,7 +88,7 @@
 
   public cancel(): void {
     if (!this.currentFormState.isPristine) {
-      const modalRef = this.modalService.open(SafetyQueryDialogComponent);
+      const modalRef = this._modalService.open(SafetyQueryDialogComponent);
       modalRef.componentInstance.title = 'ConfirmDialog.Action.edit';
       modalRef.componentInstance.body = 'ConfirmDialog.Content';
       modalRef.result.then(
@@ -103,7 +105,7 @@
   clear(): void {
     this.appState$.dispatch(new SetValueAction(fromGridFailuresDetailFormReducer.FORM_ID, fromGridFailuresDetailFormReducer.INITIAL_STATE.value));
     this.appState$.dispatch(new ResetAction(fromGridFailuresDetailFormReducer.FORM_ID));
-    navigateHome(this.router);
+    navigateHome(this._router);
   }
 
   public saveGridFailure(): void {
@@ -113,11 +115,11 @@
           payload: new GridFailure(this.currentFormState.value),
         })
       );
-      this.actionsSubject.pipe(ofType(gridFailureActions.saveGridFailureSuccess), takeUntil(this._endSubscriptions$)).subscribe(() => {
+      this._actionsSubject.pipe(ofType(gridFailureActions.saveGridFailureSuccess), take(1), takeUntil(this._endSubscriptions$)).subscribe(() => {
         this.clear();
       });
     } else {
-      this.utilService.displayNotification('MandatoryFieldError', 'alert');
+      this._utilService.displayNotification('MandatoryFieldError', 'alert');
     }
   }
 
diff --git a/projects/grid-failure-information-app/src/app/shared/components/base-components/base.component.spec.ts b/projects/grid-failure-information-app/src/app/shared/components/base-components/base.component.spec.ts
index 01c9c6f..f7d44ea 100644
--- a/projects/grid-failure-information-app/src/app/shared/components/base-components/base.component.spec.ts
+++ b/projects/grid-failure-information-app/src/app/shared/components/base-components/base.component.spec.ts
@@ -19,16 +19,10 @@
 
   beforeEach(() => {
     sandbox = { endSubscriptions() {} } as any;
-    component = new BaseComponent(sandbox);
+    component = new BaseComponent();
   });
 
   it('should create', () => {
     expect(component).toBeTruthy();
   });
-
-  it('should endSubscriptions via ngOnDestroy', () => {
-    let spy = spyOn(sandbox, 'endSubscriptions');
-    component.ngOnDestroy();
-    expect(spy).toHaveBeenCalled();
-  });
 });
diff --git a/projects/grid-failure-information-app/src/app/shared/components/base-components/base.component.ts b/projects/grid-failure-information-app/src/app/shared/components/base-components/base.component.ts
index 027bd37..8e59b43 100644
--- a/projects/grid-failure-information-app/src/app/shared/components/base-components/base.component.ts
+++ b/projects/grid-failure-information-app/src/app/shared/components/base-components/base.component.ts
@@ -10,12 +10,9 @@
  *
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
-import { OnDestroy } from '@angular/core';
-import { BaseSandbox } from '@shared/sandbox/base.sandbox';
+import { Subject } from 'rxjs';
 
-export class BaseComponent implements OnDestroy {
-  constructor(public baseSandebox?: BaseSandbox) {}
-  ngOnDestroy(): void {
-    !!this.baseSandebox && this.baseSandebox.endSubscriptions();
-  }
+export class BaseComponent {
+  protected _endSubscriptions$: Subject<boolean> = new Subject();
+  constructor() {}
 }
diff --git a/projects/grid-failure-information-app/src/app/shared/components/base-components/base.list.ts b/projects/grid-failure-information-app/src/app/shared/components/base-components/base.list.ts
index 70db84b..9cc4db9 100644
--- a/projects/grid-failure-information-app/src/app/shared/components/base-components/base.list.ts
+++ b/projects/grid-failure-information-app/src/app/shared/components/base-components/base.list.ts
@@ -12,8 +12,9 @@
  ********************************************************************************/
 import { GridOptions } from 'ag-grid-community';
 import { Subject } from 'rxjs';
+import { BaseComponent } from '@shared/components/base-components/base.component';
 
-export abstract class BaseList {
+export abstract class BaseList extends BaseComponent {
   public events$: Subject<any> = new Subject();
   public noRowsTemplate: string;
   public loadingTemplate: string;
@@ -28,6 +29,7 @@
   protected pageSize: number = 3;
 
   constructor() {
+    super();
     this.noRowsTemplate = `<span>Keine Einträge</span>`;
   }
 }
diff --git a/projects/grid-failure-information-app/src/app/shared/constants/enums.ts b/projects/grid-failure-information-app/src/app/shared/constants/enums.ts
new file mode 100644
index 0000000..00fd65b
--- /dev/null
+++ b/projects/grid-failure-information-app/src/app/shared/constants/enums.ts
@@ -0,0 +1,17 @@
+/********************************************************************************
+ * Copyright (c) 2020 Contributors to the Eclipse Foundation
+ *
+ * See the NOTICE file(s) distributed with this work for additional
+ * information regarding copyright ownership.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ ********************************************************************************/
+export enum DatePickerResetEnum {
+  FailureBegin = 'failureBegin',
+  FailureEndPlanned = 'failureEndPlanned',
+  FailureEndResupplied = 'failureEndResupplied',
+}
diff --git a/projects/grid-failure-information-app/src/app/shared/store/effects/grid-failures.effect.spec.ts b/projects/grid-failure-information-app/src/app/shared/store/effects/grid-failures.effect.spec.ts
index 4b62914..ee4174b 100644
--- a/projects/grid-failure-information-app/src/app/shared/store/effects/grid-failures.effect.spec.ts
+++ b/projects/grid-failure-information-app/src/app/shared/store/effects/grid-failures.effect.spec.ts
@@ -28,6 +28,9 @@
   beforeEach(() => {
     apiClient = {
       getGridFailures() {},
+      getGridFailureDetails() {},
+      putGridFailure() {},
+      postGridFailure() {},
     } as any;
     store = {
       dispatch() {},
@@ -58,4 +61,42 @@
     done();
     actions$.next(gridFailureActions.loadGridFailures());
   });
+
+  it('should equal loadGridFailureDetailSuccess in response to getGridFailureDetails', done => {
+    apiResponse = new GridFailure({ id: '1' });
+    spyOn(apiClient, 'getGridFailureDetails').and.returnValue(of(apiResponse));
+    effects.getGridFailureDetails$.pipe(take(1)).subscribe(result => {
+      expect(result).toEqual(gridFailureActions.loadGridFailureDetailSuccess({ payload: apiResponse }));
+    });
+    done();
+    actions$.next(gridFailureActions.loadGridFailureDetail({ payload: '1' }));
+  });
+
+  it('should equal loadGridFailureDetailSuccess in response to getGridFailureDetails Error', done => {
+    spyOn(apiClient, 'getGridFailureDetails').and.returnValue(throwError('x'));
+    effects.getGridFailureDetails$.pipe(take(1)).subscribe(result => {
+      expect(result).toEqual(gridFailureActions.loadGridFailureDetailFail({ payload: 'x' }));
+    });
+    done();
+    actions$.next(gridFailureActions.loadGridFailureDetail({ payload: '1' }));
+  });
+
+  it('should equal saveGridFailureSuccess in response to saveGridFailure', done => {
+    apiResponse = new GridFailure({ id: '1' });
+    spyOn(apiClient, 'putGridFailure').and.returnValue(of(apiResponse));
+    effects.saveGridFailure$.pipe(take(1)).subscribe(result => {
+      expect(result).toEqual(gridFailureActions.saveGridFailureSuccess({ payload: new GridFailure({ id: '1' }) }));
+    });
+    done();
+    actions$.next(gridFailureActions.saveGridFailure({ payload: new GridFailure({ id: '1' }) }));
+  });
+
+  it('should equal saveGridFailureFail in response to saveGridFailure Error', done => {
+    spyOn(apiClient, 'putGridFailure').and.returnValue(throwError('x'));
+    effects.saveGridFailure$.pipe(take(1)).subscribe(result => {
+      expect(result).toEqual(gridFailureActions.saveGridFailureFail({ payload: 'x' }));
+    });
+    done();
+    actions$.next(gridFailureActions.saveGridFailure({ payload: new GridFailure({ id: '1' }) }));
+  });
 });
diff --git a/projects/grid-failure-information-app/src/app/shared/store/reducers/grid-failures/grid-failure-details-form.reducer.spec.ts b/projects/grid-failure-information-app/src/app/shared/store/reducers/grid-failures/grid-failure-details-form.reducer.spec.ts
index 8d51ae1..62ba203 100644
--- a/projects/grid-failure-information-app/src/app/shared/store/reducers/grid-failures/grid-failure-details-form.reducer.spec.ts
+++ b/projects/grid-failure-information-app/src/app/shared/store/reducers/grid-failures/grid-failure-details-form.reducer.spec.ts
@@ -10,77 +10,46 @@
  *
  * SPDX-License-Identifier: EPL-2.0
  ********************************************************************************/
-import {
-  GridFailuresReducer,
-  INITIAL_STATE,
-  getData,
-  getLoading,
-  getLoaded,
-  getFailed,
-  reducer,
-} from '@app/shared/store/reducers/grid-failures/grid-failures.reducer';
+import * as fromReducer from '@app/shared/store/reducers/grid-failures/grid-failure-details-form.reducer';
 import * as gridFailureActions from '@shared/store/actions/grid-failures.action';
 import { GridFailure } from '@shared/models';
+import { Action } from '@ngrx/store';
+import { FormGroupState } from 'ngrx-forms';
 
-describe('AddressTypes reducer', () => {
-  it('should return the initial state', () => {
-    const action = { type: 'NOOP' } as any;
-    const result = reducer(undefined, action);
+describe('GridFailure Detail Form reducer', () => {
+  const { INITIAL_STATE } = fromReducer;
 
-    expect(result).toBe(INITIAL_STATE);
+  it('should return the default state when no state is given', () => {
+    const action: Action = { type: '' };
+    const state: FormGroupState<GridFailure> = fromReducer.reducer(undefined, action);
+
+    expect(state).toBe(INITIAL_STATE);
   });
 
-  it('should trigger loading state', () => {
-    const action = gridFailureActions.loadGridFailures();
-    const result = GridFailuresReducer(INITIAL_STATE, action);
+  it('should return the initial state when action is not found', () => {
+    const action: Action = { type: '' };
+    const state: FormGroupState<GridFailure> = fromReducer.reducer(INITIAL_STATE, action);
 
-    expect(result).toEqual({
-      ...INITIAL_STATE,
-      loading: true,
-    });
+    expect(state).toBe(INITIAL_STATE);
   });
 
-  it('should trigger loaded state', () => {
-    const items: gridFailureActions.ILoadGridFailuresSuccess = { payload: [new GridFailure()] };
-    items.payload[0].id = 'testme';
-    const action = gridFailureActions.loadGridFailuresSuccess(items);
-    const result = GridFailuresReducer(INITIAL_STATE, action);
+  it('should return the initial state when no action set', () => {
+    const action: Action = null;
+    const state: FormGroupState<GridFailure> = fromReducer.reducer(INITIAL_STATE, action);
 
-    expect(result.loaded).toBe(true);
+    expect(state).toBe(INITIAL_STATE);
   });
 
-  it('should trigger failed state', () => {
-    const error: gridFailureActions.ILoadGridFailuresFail = { payload: 'err_msg' };
-    const action = gridFailureActions.loadGridFailuresFail(error);
-    const result = GridFailuresReducer(INITIAL_STATE, action);
-
-    expect(result).toEqual({
-      ...INITIAL_STATE,
-      failed: true,
-    });
+  it('should return state via getFormState', () => {
+    const action: Action = null;
+    const state: FormGroupState<GridFailure> = fromReducer.reducer(INITIAL_STATE, action);
+    expect(fromReducer.getFormState(state)).toBe(state);
   });
 
-  it('getData return state.data', () => {
-    const state = { ...INITIAL_STATE };
-    const result = getData(state);
-    expect(result).toBe(state.data);
-  });
-
-  it('getLoading return state.loading', () => {
-    const state = { ...INITIAL_STATE };
-    const result = getLoading(state);
-    expect(result).toBe(state.loading);
-  });
-
-  it('getLoaded return state.loaded', () => {
-    const state = { ...INITIAL_STATE };
-    const result = getLoaded(state);
-    expect(result).toBe(state.loaded);
-  });
-
-  it('getFailed return state.failed', () => {
-    const state = { ...INITIAL_STATE };
-    const result = getFailed(state);
-    expect(result).toBe(state.failed);
+  it('should set id', () => {
+    const action: Action = { type: gridFailureActions.loadGridFailureDetailSuccess.type };
+    action['payload'] = { id: 'x' };
+    const state: FormGroupState<GridFailure> = fromReducer.reducer(INITIAL_STATE, action);
+    expect(state.controls.id).toBeDefined();
   });
 });
diff --git a/projects/grid-failure-information-app/src/app/shared/utility/utility.service.ts b/projects/grid-failure-information-app/src/app/shared/utility/utility.service.ts
index df3361e..056454e 100644
--- a/projects/grid-failure-information-app/src/app/shared/utility/utility.service.ts
+++ b/projects/grid-failure-information-app/src/app/shared/utility/utility.service.ts
@@ -1,4 +1,4 @@
- /********************************************************************************
+/********************************************************************************
  * Copyright (c) 2020 Contributors to the Eclipse Foundation
  *
  * See the NOTICE file(s) distributed with this work for additional
@@ -17,11 +17,7 @@
 
 @Injectable()
 export class UtilService {
-  constructor(
-    private translateService: TranslateService,
-    private notificationService: NotificationsService,
-    private configService: ConfigService
-  ) {}
+  constructor(private translateService: TranslateService, private notificationService: NotificationsService, private configService: ConfigService) {}
 
   /**
    * Translates given message code and title code and displays corresponding notification
@@ -30,15 +26,9 @@
    * @param type
    * @param titleTranslationCode
    */
-  public displayNotification(
-    messageTranslationCode: string,
-    type: string = 'info',
-    titleTranslationCode?: string
-  ) {
+  public displayNotification(messageTranslationCode: string, type: string = 'info', titleTranslationCode?: string) {
     const message: string = this.translateService.instant(messageTranslationCode);
-    let title: string = titleTranslationCode
-      ? this.translateService.instant(titleTranslationCode)
-      : null;
+    let title: string = titleTranslationCode ? this.translateService.instant(titleTranslationCode) : null;
 
     switch (type) {
       case 'error':
@@ -58,11 +48,7 @@
         break;
     }
 
-    this.notificationService[type](
-      title,
-      message,
-      this.configService.get('notifications').options
-    );
+    this.notificationService[type](title, message, this.configService.get('notifications').options);
   }
 
   /**
@@ -71,11 +57,8 @@
    * @param data
    */
   public translateLookupData(data: Array<any>): Array<any> {
-    // Translate quantity stock adjustment reasons
     return data.map(lookup => {
-      lookup.name = lookup.code
-        ? this.translateService.instant('Lookups')[lookup.code]
-        : lookup.name;
+      lookup.name = lookup.code ? this.translateService.instant('Lookups')[lookup.code] : lookup.name;
       return lookup;
     });
   }