From ac7db4dc06c2567a96a680186e6a4cc5d0d3aad7 Mon Sep 17 00:00:00 2001
From: Tifenn Guillas <tifenn.guillas@gmail.com>
Date: Fri, 15 Oct 2021 14:33:38 +0200
Subject: [PATCH] WIP: tests on checkbox component

---
 .../between-date.component.spec.ts            |  98 +++++++++++++++
 .../search-type/between-date.component.ts     |  26 ++--
 .../search-type/between.component.spec.ts     | 115 +++++++++++++++++
 .../criteria/search-type/between.component.ts |  12 +-
 .../search-type/checkbox.component.spec.ts    | 108 ++++++++++++++++
 .../search-type/checkbox.component.ts         |  25 ++--
 .../search-type/datalist.component.spec.ts    | 119 ++++++++++++++++++
 .../search-type/datalist.component.ts         |  12 +-
 .../search-type/date.component.spec.ts        | 115 +++++++++++++++++
 .../criteria/search-type/date.component.ts    |  14 ++-
 .../search-type/datetime.component.spec.ts    |  15 +--
 .../select-multiple.component.spec.ts         |   2 +-
 12 files changed, 617 insertions(+), 44 deletions(-)
 create mode 100644 client/src/app/instance/search/components/criteria/search-type/between-date.component.spec.ts
 create mode 100644 client/src/app/instance/search/components/criteria/search-type/between.component.spec.ts
 create mode 100644 client/src/app/instance/search/components/criteria/search-type/checkbox.component.spec.ts
 create mode 100644 client/src/app/instance/search/components/criteria/search-type/datalist.component.spec.ts
 create mode 100644 client/src/app/instance/search/components/criteria/search-type/date.component.spec.ts

diff --git a/client/src/app/instance/search/components/criteria/search-type/between-date.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/between-date.component.spec.ts
new file mode 100644
index 00000000..a0dceaf5
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/between-date.component.spec.ts
@@ -0,0 +1,98 @@
+import { Component, Input, ViewChild } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { BetweenDateComponent } from './between-date.component';
+import { BetweenCriterion } from '../../../../store/models/criterion';
+import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
+
+describe('[Instance][Search][Component][Criteria][SearchType] BetweenDateComponent', () => {
+    @Component({
+        selector: `app-host`,
+        template: `
+            <app-between-date 
+                    [id]="id"
+                    [operator]="operator"
+                    [label]="label"
+                    [criterion]="criterion">
+            </app-between-date>`
+    })
+    class TestHostComponent {
+        @ViewChild(BetweenDateComponent, { static: false })
+        public testedComponent: BetweenDateComponent;
+        public id: number = undefined;
+        public operator: string = undefined;
+        public label: string = undefined;
+        public criterion: BetweenCriterion = undefined;
+    }
+
+    @Component({ selector: 'app-operator', template: '' })
+    class OperatorStubComponent {
+        @Input() operator: string;
+        @Input() searchType: string;
+        @Input() advancedForm: boolean;
+        @Input() disabled: boolean;
+    }
+
+    let testHostComponent: TestHostComponent;
+    let testHostFixture: ComponentFixture<TestHostComponent>;
+    let testedComponent: BetweenDateComponent;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                BetweenDateComponent,
+                TestHostComponent
+            ],
+            imports: [
+                FormsModule,
+                ReactiveFormsModule,
+                BsDatepickerModule.forRoot()
+            ]
+        });
+        testHostFixture = TestBed.createComponent(TestHostComponent);
+        testHostComponent = testHostFixture.componentInstance;
+        testHostFixture.detectChanges();
+        testedComponent = testHostComponent.testedComponent;
+    });
+
+    it('should create the component', () => {
+        expect(testedComponent).toBeTruthy();
+    });
+
+    it('should call ngOnChanges and apply changes', () => {
+        const spy = jest.spyOn(testedComponent, 'ngOnChanges');
+        testHostComponent.criterion = { id: 1, type: 'between', operator: 'eq', min: '2019-02-17', max: '2021-04-12' } as BetweenCriterion;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.dateRange.value[0].getDate()).toEqual(17);
+        expect(testedComponent.form.controls.dateRange.value[0].getMonth()).toEqual(1);
+        expect(testedComponent.form.controls.dateRange.value[0].getFullYear()).toEqual(2019);
+        expect(testedComponent.form.controls.dateRange.value[1].getDate()).toEqual(12);
+        expect(testedComponent.form.controls.dateRange.value[1].getMonth()).toEqual(3);
+        expect(testedComponent.form.controls.dateRange.value[1].getFullYear()).toEqual(2021);
+        expect(testedComponent.form.disabled).toBeTruthy();
+        testHostComponent.criterion = undefined;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.dateRange.value).toBeNull();
+        expect(testedComponent.form.enabled).toBeTruthy();
+        expect(spy).toHaveBeenCalledTimes(2);
+    });
+
+    it('raises the add criterion event when clicked', () => {
+        testedComponent.id = 1;
+        const operator = 'eq';
+        testedComponent.operator = operator;
+        const dateMin: Date = new Date('2019-02-17');
+        const dateMax: Date = new Date('2021-04-12');
+        testedComponent.form.controls.dateRange.setValue([dateMin, dateMax]);
+        const expectedCriterion = { id: testedComponent.id, type: 'between', operator, min: '2019-02-17', max: '2021-04-12' } as BetweenCriterion;
+        testedComponent.addCriterion.subscribe((event: BetweenCriterion) => expect(event).toEqual(expectedCriterion));
+        testedComponent.emitAdd();
+    });
+
+    it('#getDateString() should return a date as string', () => {
+        const dateString = '2019-02-17';
+        const date = new Date(dateString);
+        expect(testedComponent.getDateString(date)).toEqual(dateString);
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/between-date.component.ts b/client/src/app/instance/search/components/criteria/search-type/between-date.component.ts
index 9e0ab4d1..68f1dc17 100644
--- a/client/src/app/instance/search/components/criteria/search-type/between-date.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/between-date.component.ts
@@ -7,21 +7,31 @@
  * file that was distributed with this source code.
  */
 
-import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, SimpleChanges } from '@angular/core';
+import {
+    Component,
+    Input,
+    Output,
+    EventEmitter,
+    ChangeDetectionStrategy,
+    SimpleChanges,
+    OnChanges
+} from '@angular/core';
 import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, BetweenCriterion } from 'src/app/instance/store/models';
 
+/**
+ * @class
+ * @classdesc Between date search type component.
+ *
+ * @implements OnChanges
+ */
 @Component({
     selector: 'app-between-date',
     templateUrl: 'between-date.component.html',
     changeDetection: ChangeDetectionStrategy.OnPush
 })
-/**
- * @class
- * @classdesc Between date search type component.
- */
-export class BetweenDateComponent {
+export class BetweenDateComponent implements OnChanges {
     @Input() id: number;
     @Input() operator: string;
     @Input() label: string;
@@ -33,7 +43,7 @@ export class BetweenDateComponent {
         dateRange: new FormControl('', [Validators.required])
     });
 
-    ngOnChanges(changes: SimpleChanges) {
+    ngOnChanges(changes: SimpleChanges): void {
         if (changes.criterion && changes.criterion.currentValue) {
             const c = changes.criterion.currentValue as BetweenCriterion;
             this.form.controls.dateRange.setValue([new Date(c.min), new Date(c.max)]);
@@ -54,7 +64,7 @@ export class BetweenDateComponent {
     emitAdd(): void {
         const dateMin = this.getDateString(this.form.controls.dateRange.value[0]);
         const dateMax = this.getDateString(this.form.controls.dateRange.value[1]);
-        const fd: BetweenCriterion = {id: this.id, type: 'between', min: dateMin, max: dateMax};
+        const fd: BetweenCriterion = { id: this.id, type: 'between', min: dateMin, max: dateMax };
         this.addCriterion.emit(fd);
     }
 
diff --git a/client/src/app/instance/search/components/criteria/search-type/between.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/between.component.spec.ts
new file mode 100644
index 00000000..638ad78a
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/between.component.spec.ts
@@ -0,0 +1,115 @@
+import { Component, Input, ViewChild } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { TooltipModule } from 'ngx-bootstrap/tooltip';
+
+import { BetweenComponent } from './between.component';
+import { BetweenCriterion } from '../../../../store/models/criterion';
+
+describe('[Instance][Search][Component][Criteria][SearchType] BetweenComponent', () => {
+    @Component({
+        selector: `app-host`,
+        template: `
+            <app-between 
+                    [id]="id"
+                    [label]="label"
+                    [placeholderMin]="placeholderMin"
+                    [placeholderMax]="placeholderMax"
+                    [criterion]="criterion">
+            </app-between>`
+    })
+    class TestHostComponent {
+        @ViewChild(BetweenComponent, { static: false })
+        public testedComponent: BetweenComponent;
+        public id: number = undefined;
+        public label: string = undefined;
+        public placeholderMin: string = undefined;
+        public placeholderMax: string = undefined;
+        public criterion: BetweenCriterion = undefined;
+    }
+
+    @Component({ selector: 'app-operator', template: '' })
+    class OperatorStubComponent {
+        @Input() operator: string;
+        @Input() searchType: string;
+        @Input() advancedForm: boolean;
+        @Input() disabled: boolean;
+    }
+
+    @Component({ selector: 'app-help-like', template: '' })
+    class HelpLikeStubComponent { }
+
+    let testHostComponent: TestHostComponent;
+    let testHostFixture: ComponentFixture<TestHostComponent>;
+    let testedComponent: BetweenComponent;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                BetweenComponent,
+                TestHostComponent,
+                // OperatorStubComponent,
+                // HelpLikeStubComponent
+            ],
+            imports: [
+                FormsModule,
+                ReactiveFormsModule,
+                TooltipModule.forRoot()
+            ]
+        });
+        testHostFixture = TestBed.createComponent(TestHostComponent);
+        testHostComponent = testHostFixture.componentInstance;
+        testHostFixture.detectChanges();
+        testedComponent = testHostComponent.testedComponent;
+    });
+
+    it('should create the component', () => {
+        expect(testedComponent).toBeTruthy();
+    });
+
+    it('should call ngOnChanges and apply changes', () => {
+        const spy = jest.spyOn(testedComponent, 'ngOnChanges');
+        testHostComponent.criterion = { id: 1, type: 'between', operator: 'eq', min: 'one', max: 'two' } as BetweenCriterion;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.min.value).toEqual('one');
+        expect(testedComponent.form.controls.max.value).toEqual('two');
+        expect(testedComponent.form.disabled).toBeTruthy();
+        testHostComponent.criterion = undefined;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.min.value).toBeNull();
+        expect(testedComponent.form.controls.max.value).toBeNull();
+        expect(testedComponent.form.enabled).toBeTruthy();
+        expect(spy).toHaveBeenCalledTimes(2);
+    });
+
+    it('raises the add criterion event when clicked', () => {
+        testedComponent.id = 1;
+        const operator = 'eq';
+        testedComponent.form.controls.min.setValue('one');
+        testedComponent.form.controls.max.setValue('two');
+        const expectedCriterion = { id: testedComponent.id, type: 'between', operator, min: 'one', max: 'two' } as BetweenCriterion;
+        testedComponent.addCriterion.subscribe((event: BetweenCriterion) => expect(event).toEqual(expectedCriterion));
+        testedComponent.emitAdd();
+    });
+
+    it('#getPlaceholderMin() should fill the placeholder for the minimum value if defined', () => {
+        const placeholder = 'placeholder';
+        testedComponent.placeholderMin = placeholder;
+        expect(testedComponent.getPlaceholderMin()).toEqual(placeholder);
+    });
+
+    it('#getPlaceholderMin() should not fill the placeholder for the minimum value if not defined', () => {
+        expect(testedComponent.getPlaceholderMin()).toEqual('');
+    });
+
+    it('#getPlaceholderMax() should fill the placeholder for the maximum value if defined', () => {
+        const placeholder = 'placeholder';
+        testedComponent.placeholderMax = placeholder;
+        expect(testedComponent.getPlaceholderMax()).toEqual(placeholder);
+    });
+
+    it('#getPlaceholderMax() should not fill the placeholder for the maximum value if not defined', () => {
+        expect(testedComponent.getPlaceholderMax()).toEqual('');
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/between.component.ts b/client/src/app/instance/search/components/criteria/search-type/between.component.ts
index bc1d7b62..842d5810 100644
--- a/client/src/app/instance/search/components/criteria/search-type/between.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/between.component.ts
@@ -12,16 +12,18 @@ import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, BetweenCriterion } from 'src/app/instance/store/models';
 
+/**
+ * @class
+ * @classdesc Between search type component.
+ *
+ * @implements OnChanges
+ */
 @Component({
     selector: 'app-between',
     templateUrl: 'between.component.html',
     styleUrls: ['operator.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
-/**
- * @class
- * @classdesc Between search type component.
- */
 export class BetweenComponent implements OnChanges {
     @Input() id: number;
     @Input() label: string;
@@ -36,7 +38,7 @@ export class BetweenComponent implements OnChanges {
         max: new FormControl('', [Validators.required])
     });
 
-    ngOnChanges(changes: SimpleChanges) {
+    ngOnChanges(changes: SimpleChanges): void {
         if (changes.criterion && changes.criterion.currentValue) {
             this.form.patchValue(this.criterion);
             this.form.disable();
diff --git a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.spec.ts
new file mode 100644
index 00000000..b080f0ef
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.spec.ts
@@ -0,0 +1,108 @@
+import { Component, ViewChild } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormArray, FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { NgSelectModule } from '@ng-select/ng-select';
+
+import { CheckboxComponent } from './checkbox.component';
+import { SelectMultipleCriterion } from '../../../../store/models/criterion';
+import { Option } from '../../../../../metamodel/models';
+import { AbstractSearchComponent } from '../../../containers/abstract-search.component';
+import * as searchActions from '../../../../store/actions/search.actions';
+
+describe('[Instance][Search][Component][Criteria][SearchType] CheckboxComponent', () => {
+    @Component({
+        selector: `app-host`,
+        template: `
+            <app-checkbox 
+                    [id]="id"
+                    [label]="label"
+                    [options]="options"
+                    [criterion]="criterion">
+            </app-checkbox>`
+    })
+    class TestHostComponent {
+        @ViewChild(CheckboxComponent, { static: false })
+        public testedComponent: CheckboxComponent;
+        public id: number = undefined;
+        public label: string = undefined;
+        public options: Option[] = [];
+        public criterion: SelectMultipleCriterion = undefined;
+    }
+
+    let testHostComponent: TestHostComponent;
+    let testHostFixture: ComponentFixture<TestHostComponent>;
+    let testedComponent: CheckboxComponent;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                CheckboxComponent,
+                TestHostComponent
+            ],
+            imports: [
+                // NgSelectModule,
+                FormsModule,
+                ReactiveFormsModule
+            ]
+        });
+        testHostFixture = TestBed.createComponent(TestHostComponent);
+        testHostComponent = testHostFixture.componentInstance;
+        testHostFixture.detectChanges();
+        testedComponent = testHostComponent.testedComponent;
+    });
+
+    it('should create the component', () => {
+        expect(testedComponent).toBeTruthy();
+    });
+
+    it.only('should execute ngOnInit lifecycle', () => {
+        testedComponent.options = [
+            { label: 'One', value: 'one', display: 1 },
+            { label: 'Two', value: 'two', display: 2 },
+            { label: 'Three', value: 'three', display: 3 }
+        ];
+        testedComponent.ngOnInit();
+        console.log(testedComponent.options.length);
+        const formArray = testedComponent.form.controls.checkboxes as FormArray;
+        // console.log(formArray.controls[0]);
+        expect(testedComponent).toBeTruthy();
+    });
+
+    // it('should call ngOnChanges and apply changes', () => {
+    //     const spy = jest.spyOn(testedComponent, 'ngOnChanges');
+    //     const options: Option[] = [
+    //         { label: 'One', value: 'one', display: 1 },
+    //         { label: 'Two', value: 'two', display: 2 },
+    //         { label: 'Three', value: 'three', display: 3 }
+    //     ];
+    //     testHostComponent.criterion = { id: 1, type: 'multiple', options } as SelectMultipleCriterion;
+    //     testHostFixture.detectChanges();
+    //     expect(testedComponent.form.controls.select.value).toEqual(['one', 'two', 'three']);
+    //     expect(testedComponent.form.disabled).toBeTruthy();
+    //     testHostComponent.criterion = undefined;
+    //     testHostFixture.detectChanges();
+    //     expect(testedComponent.form.controls.select.value).toBeNull();
+    //     expect(testedComponent.form.enabled).toBeTruthy();
+    //     expect(spy).toHaveBeenCalledTimes(2);
+    // });
+
+    // it('#getCheckboxes() should return an array of checkbox', () => {
+    //     const formArray: FormArray = null;
+    //     expect(testedComponent.getCheckboxes()).toBeTruthy();
+    // });
+
+    // it('raises the add criterion event when clicked', () => {
+    //     testedComponent.id = 1;
+    //     testedComponent.options = [
+    //         { label: 'One', value: 'one', display: 1 },
+    //         { label: 'Two', value: 'two', display: 2 },
+    //         { label: 'Three', value: 'three', display: 3 }
+    //     ];
+    //     testedComponent.form.controls.select.setValue(['three']);
+    //     const expectedValue = [{ label: 'Three', value: 'three', display: 3 }];
+    //     const expectedCriterion = { id: testedComponent.id, type: 'multiple', options: expectedValue } as SelectMultipleCriterion;
+    //     testedComponent.addCriterion.subscribe((event: SelectMultipleCriterion) => expect(event).toEqual(expectedCriterion));
+    //     testedComponent.emitAdd();
+    // });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.ts b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.ts
index 67d147fa..5f01e948 100644
--- a/client/src/app/instance/search/components/criteria/search-type/checkbox.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.ts
@@ -13,16 +13,19 @@ import { FormGroup, FormArray, FormControl } from '@angular/forms';
 import { Criterion, SelectMultipleCriterion } from 'src/app/instance/store/models';
 import { Option } from 'src/app/metamodel/models';
 
+/**
+ * @class
+ * @classdesc Checkbox search type component.
+ *
+ * @implements OnInit
+ * @implements OnChanges
+ */
 @Component({
     selector: 'app-checkbox',
     templateUrl: 'checkbox.component.html',
     styleUrls: ['checkbox.component.scss', 'operator.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush
 })
-/**
- * @class
- * @classdesc Checkbox search type component.
- */
 export class CheckboxComponent implements OnInit, OnChanges {
     @Input() id: number;
     @Input() label: string;
@@ -33,16 +36,17 @@ export class CheckboxComponent implements OnInit, OnChanges {
 
     public form = new FormGroup({ });
 
-    ngOnInit() {
+    ngOnInit(): void {
         // Initialization of checkboxes (1 per option)
         const formControls: FormControl[] = [];
         for (let i = 0; i < this.options.length; i++) {
             formControls.push(new FormControl());
         }
         this.form.addControl('checkboxes', new FormArray(formControls));
+        console.log(this.form.controls.checkboxes.value.length);
     }
 
-    ngOnChanges(changes: SimpleChanges) {
+    ngOnChanges(changes: SimpleChanges): void {
         if (changes.criterion && changes.criterion.currentValue) {
             const multipleCriterion = this.criterion as SelectMultipleCriterion;
 
@@ -60,7 +64,12 @@ export class CheckboxComponent implements OnInit, OnChanges {
         }
     }
 
-    getCheckboxes() {
+    /**
+     * Returns checkboxes form.
+     *
+     * @return FormArray
+     */
+    getCheckboxes(): FormArray {
         return this.form.controls.checkboxes as FormArray;
     }
 
@@ -72,7 +81,7 @@ export class CheckboxComponent implements OnInit, OnChanges {
     emitAdd(): void {
         const selected = this.getCheckboxes().value;
         const values = [...this.options.filter((option, index) => selected[index])];
-        this.addCriterion.emit({id: this.id, type: 'multiple', options: values});
+        this.addCriterion.emit({ id: this.id, type: 'multiple', options: values });
     }
 
     /**
diff --git a/client/src/app/instance/search/components/criteria/search-type/datalist.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/datalist.component.spec.ts
new file mode 100644
index 00000000..4167d607
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/datalist.component.spec.ts
@@ -0,0 +1,119 @@
+import { Component, Input, ViewChild } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { TooltipModule } from 'ngx-bootstrap/tooltip';
+
+import { DatalistComponent } from './datalist.component';
+import { FieldCriterion } from '../../../../store/models/criterion';
+import { Option } from '../../../../../metamodel/models';
+
+describe('[Instance][Search][Component][Criteria][SearchType] DatalistComponent', () => {
+    @Component({
+        selector: `app-host`,
+        template: `
+            <app-datalist 
+                    [id]="id"
+                    [operator]="operator"
+                    [label]="label"
+                    [placeholder]="placeholder"
+                    [options]="options"
+                    [criterion]="criterion"
+                    [advancedForm]="advancedForm">
+            </app-datalist>`
+    })
+    class TestHostComponent {
+        @ViewChild(DatalistComponent, { static: false })
+        public testedComponent: DatalistComponent;
+        public id: number = undefined;
+        public operator: string = undefined;
+        public label: string = undefined;
+        public placeholder: string = undefined;
+        public options: Option[] = undefined;
+        public criterion: FieldCriterion = undefined;
+        public advancedForm: boolean = false;
+    }
+
+    @Component({ selector: 'app-operator', template: '' })
+    class OperatorStubComponent {
+        @Input() operator: string;
+        @Input() searchType: string;
+        @Input() advancedForm: boolean;
+        @Input() disabled: boolean;
+    }
+
+    @Component({ selector: 'app-help-like', template: '' })
+    class HelpLikeStubComponent { }
+
+    let testHostComponent: TestHostComponent;
+    let testHostFixture: ComponentFixture<TestHostComponent>;
+    let testedComponent: DatalistComponent;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                DatalistComponent,
+                TestHostComponent,
+                OperatorStubComponent,
+                HelpLikeStubComponent
+            ],
+            imports: [
+                FormsModule,
+                ReactiveFormsModule,
+                TooltipModule.forRoot()
+            ]
+        });
+        testHostFixture = TestBed.createComponent(TestHostComponent);
+        testHostComponent = testHostFixture.componentInstance;
+        testHostFixture.detectChanges();
+        testedComponent = testHostComponent.testedComponent;
+    });
+
+    it('should create the component', () => {
+        expect(testedComponent).toBeTruthy();
+    });
+
+    it('should call ngOnChanges and apply changes', () => {
+        const spy = jest.spyOn(testedComponent, 'ngOnChanges');
+        testHostComponent.criterion = { id: 1, type: 'field', operator: 'eq', value: 'myValue' } as FieldCriterion;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.value.value).toEqual('myValue');
+        expect(testedComponent.form.disabled).toBeTruthy();
+        testHostComponent.criterion = undefined;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.value.value).toBeNull();
+        expect(testedComponent.form.enabled).toBeTruthy();
+        expect(spy).toHaveBeenCalledTimes(2);
+    });
+
+    it('#changeOperator() should change the operator', () => {
+        expect(testedComponent.operator).toBeUndefined();
+        testedComponent.changeOperator('toto');
+        expect(testedComponent.operator).toBe('toto');
+    });
+
+    it('raises the add criterion event when clicked', () => {
+        testedComponent.id = 1;
+        const operator = 'eq';
+        testedComponent.operator = operator;
+        testedComponent.form.controls.value.setValue('myValue');
+        const expectedCriterion = { id: testedComponent.id, type: 'field', operator, value: 'myValue' } as FieldCriterion;
+        testedComponent.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion));
+        testedComponent.emitAdd();
+    });
+
+    it('#getPlaceholder() should fill the placeholder if defined', () => {
+        const placeholder = 'placeholder';
+        testedComponent.placeholder = placeholder;
+        expect(testedComponent.getPlaceholder()).toEqual(placeholder);
+    });
+
+    it('#getPlaceholder() should not fill the placeholder if not defined', () => {
+        expect(testedComponent.getPlaceholder()).toEqual('');
+    });
+
+    it('#getDatalistId() should return an id', () => {
+        testedComponent.id = 1;
+        expect(testedComponent.getDatalistId()).toEqual('datalist_' + 1);
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/datalist.component.ts b/client/src/app/instance/search/components/criteria/search-type/datalist.component.ts
index cb31dc15..dc1e1c6a 100644
--- a/client/src/app/instance/search/components/criteria/search-type/datalist.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/datalist.component.ts
@@ -13,15 +13,17 @@ import { FormGroup, FormControl, Validators } from '@angular/forms';
 import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
 import { Option } from 'src/app/metamodel/models';
 
+/**
+ * @class
+ * @classdesc Datalist search type component.
+ *
+ * @implements OnChanges
+ */
 @Component({
     selector: 'app-datalist',
     templateUrl: 'datalist.component.html',
     changeDetection: ChangeDetectionStrategy.OnPush
 })
-/**
- * @class
- * @classdesc Datalist search type component.
- */
 export class DatalistComponent implements OnChanges {
     @Input() id: number;
     @Input() operator: string;
@@ -39,7 +41,7 @@ export class DatalistComponent implements OnChanges {
 
     disabledOperator: boolean;
 
-    ngOnChanges(changes: SimpleChanges) {
+    ngOnChanges(changes: SimpleChanges): void {
         if (changes.criterion && changes.criterion.currentValue) {
             this.form.patchValue(this.criterion);
             this.form.disable();
diff --git a/client/src/app/instance/search/components/criteria/search-type/date.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/date.component.spec.ts
new file mode 100644
index 00000000..ee97c921
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/date.component.spec.ts
@@ -0,0 +1,115 @@
+import { Component, Input, ViewChild } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { DateComponent } from './date.component';
+import { FieldCriterion } from '../../../../store/models/criterion';
+import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
+
+describe('[Instance][Search][Component][Criteria][SearchType] DateComponent', () => {
+    @Component({
+        selector: `app-host`,
+        template: `
+            <app-date 
+                    [id]="id"
+                    [operator]="operator"
+                    [label]="label"
+                    [placeholder]="placeholder"
+                    [criterion]="criterion"
+                    [advancedForm]="advancedForm">
+            </app-date>`
+    })
+    class TestHostComponent {
+        @ViewChild(DateComponent, { static: false })
+        public testedComponent: DateComponent;
+        public id: number = undefined;
+        public operator: string = undefined;
+        public label: string = undefined;
+        public placeholder: string = undefined;
+        public criterion: FieldCriterion = undefined;
+        public advancedForm: boolean = false;
+    }
+
+    @Component({ selector: 'app-operator', template: '' })
+    class OperatorStubComponent {
+        @Input() operator: string;
+        @Input() searchType: string;
+        @Input() advancedForm: boolean;
+        @Input() disabled: boolean;
+    }
+
+    let testHostComponent: TestHostComponent;
+    let testHostFixture: ComponentFixture<TestHostComponent>;
+    let testedComponent: DateComponent;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                DateComponent,
+                TestHostComponent,
+                OperatorStubComponent
+            ],
+            imports: [
+                FormsModule,
+                ReactiveFormsModule,
+                BsDatepickerModule.forRoot()
+            ]
+        });
+        testHostFixture = TestBed.createComponent(TestHostComponent);
+        testHostComponent = testHostFixture.componentInstance;
+        testHostFixture.detectChanges();
+        testedComponent = testHostComponent.testedComponent;
+    });
+
+    it('should create the component', () => {
+        expect(testedComponent).toBeTruthy();
+    });
+
+    it('should call ngOnChanges and apply changes', () => {
+        const spy = jest.spyOn(testedComponent, 'ngOnChanges');
+        testHostComponent.criterion = { id: 1, type: 'field', operator: 'eq', value: '2019-02-17' } as FieldCriterion;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.date.value.getDate()).toEqual(17);
+        expect(testedComponent.form.controls.date.value.getMonth()).toEqual(1);
+        expect(testedComponent.form.controls.date.value.getFullYear()).toEqual(2019);
+        expect(testedComponent.form.disabled).toBeTruthy();
+        testHostComponent.criterion = undefined;
+        testHostFixture.detectChanges();
+        expect(testedComponent.form.controls.date.value).toBeNull();
+        expect(testedComponent.form.enabled).toBeTruthy();
+        expect(spy).toHaveBeenCalledTimes(2);
+    });
+
+    it('#changeOperator() should change the operator', () => {
+        expect(testedComponent.operator).toBeUndefined();
+        testedComponent.changeOperator('toto');
+        expect(testedComponent.operator).toBe('toto');
+    });
+
+    it('raises the add criterion event when clicked', () => {
+        testedComponent.id = 1;
+        const operator = 'eq';
+        testedComponent.operator = operator;
+        const date: Date = new Date('2019-02-17');
+        testedComponent.form.controls.date.setValue(date);
+        const expectedCriterion = { id: testedComponent.id, type: 'field', operator, value: '2019-02-17' } as FieldCriterion;
+        testedComponent.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion));
+        testedComponent.emitAdd();
+    });
+
+    it('#getPlaceholder() should fill the placeholder if defined', () => {
+        const placeholder = 'placeholder';
+        testedComponent.placeholder = placeholder;
+        expect(testedComponent.getPlaceholder()).toEqual(placeholder);
+    });
+
+    it('#getPlaceholder() should not fill the placeholder if not defined', () => {
+        expect(testedComponent.getPlaceholder()).toEqual('');
+    });
+
+    it('#getDateString() should return a date as string', () => {
+        const dateString = '2019-02-17';
+        const date = new Date(dateString);
+        expect(testedComponent.getDateString(date)).toEqual(dateString);
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/date.component.ts b/client/src/app/instance/search/components/criteria/search-type/date.component.ts
index d1f4f57c..cef1a631 100644
--- a/client/src/app/instance/search/components/criteria/search-type/date.component.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/date.component.ts
@@ -12,15 +12,17 @@ import { FormGroup, FormControl, Validators } from '@angular/forms';
 
 import { Criterion, FieldCriterion } from 'src/app/instance/store/models';
 
+/**
+ * @class
+ * @classdesc Date search type component.
+ *
+ * @implements OnChanges
+ */
 @Component({
     selector: 'app-date',
     templateUrl: 'date.component.html',
     changeDetection: ChangeDetectionStrategy.OnPush
 })
-/**
- * @class
- * @classdesc Date search type component.
- */
 export class DateComponent implements OnChanges {
     @Input() id: number;
     @Input() operator: string;
@@ -37,7 +39,7 @@ export class DateComponent implements OnChanges {
 
     disabledOperator: boolean;
 
-    ngOnChanges(changes: SimpleChanges) {
+    ngOnChanges(changes: SimpleChanges): void {
         if (changes.criterion && changes.criterion.currentValue) {
             const criterion = changes.criterion.currentValue as FieldCriterion;
 
@@ -66,7 +68,7 @@ export class DateComponent implements OnChanges {
      * @fires EventEmitter<FieldCriterion>
      */
     emitAdd(): void {
-        const fd = {id: this.id, type: 'field', operator: this.operator, value: this.getDateString(this.form.value.date)};
+        const fd = { id: this.id, type: 'field', operator: this.operator, value: this.getDateString(this.form.value.date) };
         this.addCriterion.emit(fd);
     }
 
diff --git a/client/src/app/instance/search/components/criteria/search-type/datetime.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/datetime.component.spec.ts
index b1b119a8..fa8f17a2 100644
--- a/client/src/app/instance/search/components/criteria/search-type/datetime.component.spec.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/datetime.component.spec.ts
@@ -2,13 +2,10 @@ import { Component, Input, ViewChild } from '@angular/core';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
-import { TooltipModule } from 'ngx-bootstrap/tooltip';
-
 import { DatetimeComponent } from './datetime.component';
 import { FieldCriterion } from '../../../../store/models/criterion';
 import { BsDatepickerModule } from 'ngx-bootstrap/datepicker';
 import { NgSelectModule } from '@ng-select/ng-select';
-import { combineAll } from 'rxjs/operators';
 
 describe('[Instance][Search][Component][Criteria][SearchType] DatetimeComponent', () => {
     @Component({
@@ -40,9 +37,6 @@ describe('[Instance][Search][Component][Criteria][SearchType] DatetimeComponent'
         @Input() disabled: boolean;
     }
 
-    @Component({ selector: 'app-help-like', template: '' })
-    class HelpLikeStubComponent { }
-
     let testHostComponent: TestHostComponent;
     let testHostFixture: ComponentFixture<TestHostComponent>;
     let testedComponent: DatetimeComponent;
@@ -52,14 +46,12 @@ describe('[Instance][Search][Component][Criteria][SearchType] DatetimeComponent'
             declarations: [
                 DatetimeComponent,
                 TestHostComponent,
-                OperatorStubComponent,
-                // HelpLikeStubComponent
+                OperatorStubComponent
             ],
             imports: [
                 FormsModule,
                 ReactiveFormsModule,
                 NgSelectModule,
-                // TooltipModule.forRoot(),
                 BsDatepickerModule.forRoot()
             ]
         });
@@ -75,10 +67,11 @@ describe('[Instance][Search][Component][Criteria][SearchType] DatetimeComponent'
 
     it('should call ngOnChanges and apply changes', () => {
         const spy = jest.spyOn(testedComponent, 'ngOnChanges');
-        const date: Date = new Date('2019-02-17');
         testHostComponent.criterion = { id: 1, type: 'field', operator: 'eq', value: '2019-02-17 15:47' } as FieldCriterion;
         testHostFixture.detectChanges();
-        expect(testedComponent.form.controls.date.value).toEqual(date);
+        expect(testedComponent.form.controls.date.value.getDate()).toEqual(17);
+        expect(testedComponent.form.controls.date.value.getMonth()).toEqual(1);
+        expect(testedComponent.form.controls.date.value.getFullYear()).toEqual(2019);
         expect(testedComponent.form.controls.hh.value).toEqual('15');
         expect(testedComponent.form.controls.mm.value).toEqual('47');
         expect(testedComponent.form.disabled).toBeTruthy();
diff --git a/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.spec.ts
index b89f4faf..9ad8f715 100644
--- a/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.spec.ts
+++ b/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.spec.ts
@@ -5,7 +5,7 @@ import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 import { NgSelectModule } from '@ng-select/ng-select';
 
 import { SelectMultipleComponent } from './select-multiple.component';
-import {  SelectMultipleCriterion } from '../../../../store/models/criterion';
+import { SelectMultipleCriterion } from '../../../../store/models/criterion';
 import { Option } from '../../../../../metamodel/models';
 
 describe('[Instance][Search][Component][Criteria][SearchType] SelectMultipleComponent', () => {
-- 
GitLab