diff --git a/client/src/app/instance/search/components/attribute-label.component.spec.ts b/client/src/app/instance/search/components/attribute-label.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..705ac3aac9b69f721ae6bc1140625131cda6200d
--- /dev/null
+++ b/client/src/app/instance/search/components/attribute-label.component.spec.ts
@@ -0,0 +1,29 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { AttributeLabelComponent } from "./attribute-label.component"
+
+describe('[instance][search][components] AttributeLabelComponent', () => {
+    let component: AttributeLabelComponent;
+    let fixture: ComponentFixture<AttributeLabelComponent>;
+    TestBed.configureTestingModule({
+        declarations: [
+            AttributeLabelComponent
+        ]
+    });
+    beforeEach(() => {
+        fixture = TestBed.createComponent(AttributeLabelComponent);
+        component = fixture.componentInstance;
+    });
+
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    })
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria-list-parameters.component.spec.ts b/client/src/app/instance/search/components/criteria-list-parameters.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..93a4356f366dad92a1ac75c8ae22e514e422e0e4
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria-list-parameters.component.spec.ts
@@ -0,0 +1,45 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { Attribute } from "src/app/metamodel/models";
+import { Criterion } from "../../store/models";
+import * as fromStoreModels from "../../store/models";
+import { CriteriaListParametersComponent } from "./criteria-list-parameters.component"
+
+describe('[instance][search][components] CriteriaListParametersComponent', () => {
+    let component: CriteriaListParametersComponent;
+    let fixture: ComponentFixture<CriteriaListParametersComponent>;
+    TestBed.configureTestingModule({
+        declarations: [
+            CriteriaListParametersComponent
+        ]
+    });
+    beforeEach(() => {
+        fixture = TestBed.createComponent(CriteriaListParametersComponent);
+        component = fixture.componentInstance;
+    });
+    it('component should be created', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getAttribute(2) should return attribute with id  2', () => {
+        let attribute: Attribute;
+        component.attributeList = [{ ...attribute, id: 1 }, { ...attribute, id: 2 }];
+        let result = component.getAttribute(2);
+        expect(result.id).toEqual(2);
+    });
+    it('printCriterion(criterion: Criterion) should call getPrettyCriterion(criterion)', () => {
+        let criterion: Criterion = { id: 1, type: 'test' };
+        let spy = jest.spyOn(fromStoreModels, 'getPrettyCriterion');
+        component.printCriterion(criterion);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(criterion);
+    });
+
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria/cone-search-tab.component.spec.ts b/client/src/app/instance/search/components/criteria/cone-search-tab.component.spec.ts
index 7299cc4bff92495fb4402b28f0653b0d62433632..9c10b8de69be760cf38eb678adfc743536d3d92f 100644
--- a/client/src/app/instance/search/components/criteria/cone-search-tab.component.spec.ts
+++ b/client/src/app/instance/search/components/criteria/cone-search-tab.component.spec.ts
@@ -10,14 +10,12 @@
 import { Component, Input } from '@angular/core';
 import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
-
 import { AccordionModule } from 'ngx-bootstrap/accordion';
-
 import { ConeSearchTabComponent } from './cone-search-tab.component';
 import { ConeSearch } from '../../../store/models';
 import { DatasetByNamePipe } from '../../../../shared/pipes/dataset-by-name.pipe';
 
-describe('[Instance][Search][Component][Criteria] ConeSearchTabComponent', () => {
+describe('[Instance][Search][Components][Criteria] ConeSearchTabComponent', () => {
     @Component({ selector: 'app-cone-search', template: '' })
     class ConeSearchStubComponent {
         @Input() coneSearch: ConeSearch;
@@ -27,7 +25,6 @@ describe('[Instance][Search][Component][Criteria] ConeSearchTabComponent', () =>
 
     let component: ConeSearchTabComponent;
     let fixture: ComponentFixture<ConeSearchTabComponent>;
-
     beforeEach(() => {
         TestBed.configureTestingModule({
             declarations: [
@@ -47,4 +44,19 @@ describe('[Instance][Search][Component][Criteria] ConeSearchTabComponent', () =>
     it('should create the component', () => {
         expect(component).toBeTruthy();
     });
+    it('should raises addConeSearch event with the newConeSearch', () => {
+        let newConeSearch: ConeSearch = { dec: 10, ra: 5, radius: 10 };
+        const spy = jest.spyOn(component.addConeSearch, 'emit');
+        component.emitAdd(newConeSearch);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(newConeSearch);
+    });
+    it('should raises  updateConeSearch with the newConeSearch', () => {
+        let newConeSearch: ConeSearch = { dec: 10, ra: 5, radius: 10 };
+        component.coneSearch = { dec: 5, ra: 2, radius: 5 }
+        const spy = jest.spyOn(component.updateConeSearch, 'emit');
+        component.emitAdd(newConeSearch);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(newConeSearch);
+    })
 });
diff --git a/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts b/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts
index c2863548af82d3e378fee2881678fe7ab749e9e4..969089687cff68398b2c1c1eaf3d8b5a3a9b6f03 100644
--- a/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts
+++ b/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts
@@ -235,6 +235,13 @@ describe('[Instance][Search][Component][Criteria] CriteriaByFamilyComponent', ()
         component.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(criterion));
         component.emitAdd(criterion);
     });
+    it('raises the emitUpdate criterion event when clicked', () => {
+        const criterion = { id: 1, type: 'field', operator: 'eq', value: 'test' } as FieldCriterion;
+        let spy = jest.spyOn(component.updateCriterion, 'emit');
+        component.emitUpdate(criterion);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(criterion);
+    });
 
     it('raises the delete criterion event when clicked', () => {
         const criterionId = 1;
diff --git a/client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts b/client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts
index 10fe3aa6f636103021b46f76548d2a9da4f4aa96..85c8027ad28d3c6046a2c8cbccdece8294f08708 100644
--- a/client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts
+++ b/client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts
@@ -14,7 +14,7 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
 import { AccordionModule } from 'ngx-bootstrap/accordion';
 
 import { CriteriaTabsComponent } from './criteria-tabs.component';
-import { Attribute } from '../../../../metamodel/models';
+import { Attribute, CriteriaFamily } from '../../../../metamodel/models';
 import { Criterion, FieldCriterion, SvomKeyword } from '../../../store/models';
 import { AttributeListByFamilyPipe } from '../../../../shared/pipes/attribute-list-by-family.pipe';
 
@@ -28,6 +28,7 @@ describe('[Instance][Search][Component][Criteria] CriteriaTabsComponent', () =>
 
     let component: CriteriaTabsComponent;
     let fixture: ComponentFixture<CriteriaTabsComponent>;
+    let attribute: Attribute;
 
     beforeEach(() => {
         TestBed.configureTestingModule({
@@ -43,21 +44,50 @@ describe('[Instance][Search][Component][Criteria] CriteriaTabsComponent', () =>
         });
         fixture = TestBed.createComponent(CriteriaTabsComponent);
         component = fixture.componentInstance;
+        component.attributeList = [
+            { ...attribute, id_criteria_family: 1 },
+            { ...attribute, id_criteria_family: 2 },
+            { ...attribute, id_criteria_family: 1 }
+        ];
     });
 
     it('should create the component', () => {
         expect(component).toBeTruthy();
     });
+    it('getCriteriaFamilyList() should return an attribute array one criteriaFamily', () => {
+        component.criteriaFamilyList = [ 
+            { display: 10, id: 1, label: 'test', opened: true },
+            { display: 10, id: 5, label: 'test2', opened: true },
+            { display: 10, id: 3, label: 'test3', opened: true },
+        ]
+        let result = component.getCriteriaFamilyList();
+        expect(result.length).toEqual(1);
+    });
+    it('getAttributeListByCriteriaFamily(criteriaFamily: CriteriaFamily) should return an attribute array two attributes', () => {
+        const criteriaFamily: CriteriaFamily = { display: 10, id: 1, label: 'test', opened: true };
+        let result = component.getAttributeListByCriteriaFamily(criteriaFamily);
+        expect(result.length).toEqual(2);
 
+    });
     it('raises the add criterion event when clicked', () => {
         const criterion = { id: 1, type: 'field', operator: 'eq', value: 'test' } as FieldCriterion;
         component.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(criterion));
         component.emitAdd(criterion);
     });
+    it('raises the emitUpdate criterion event', () => {
+        const criterion = { id: 1, type: 'field', operator: 'eq', value: 'test' } as FieldCriterion;
+        const spy = jest.spyOn(component.updateCriterion, 'emit');
+        component.emitUpdate(criterion);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(criterion);
+    });
 
     it('raises the delete criterion event when clicked', () => {
         const criterionId = 1;
-        component.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1));
+        component.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1
+        ));
         component.emitDelete(criterionId);
     });
+
 });
+
diff --git a/client/src/app/instance/search/components/criteria/criterion.component.spec.ts b/client/src/app/instance/search/components/criteria/criterion.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..92c8722411d8ebc0b60406b6c9d9b5c71cd61b99
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/criterion.component.spec.ts
@@ -0,0 +1,178 @@
+/**
+ * component file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with component source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing"
+import { MockStore, provideMockStore } from "@ngrx/store/testing";
+import { CriterionComponent } from "./criterion.component";
+import { ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentRef, ElementRef, EmbeddedViewRef, EnvironmentInjector, EventEmitter, Injector, NgModuleRef, SimpleChange, TemplateRef, Type, ViewContainerRef, ViewRef } from '@angular/core';
+import { Attribute } from "src/app/metamodel/models";
+import { AbstractSearchTypeComponent } from "./search-type";
+import { Criterion, FieldCriterion } from "src/app/instance/store/models";
+import { FormGroup } from "@angular/forms";
+import { of } from "rxjs";
+
+class MockAbstractSearchTypeComponent extends AbstractSearchTypeComponent {
+    getCriterion = jest.fn().mockImplementation(() => ({
+        id: 1,
+        type: 'field',
+        operator: "test"
+    } as FieldCriterion))
+    setCriterion = jest.fn()
+
+}
+class MockComponentRef extends ComponentRef<AbstractSearchTypeComponent> {
+
+    form: FormGroup = new FormGroup({})
+    setInput(name: string, value: unknown): void {
+        return null;
+    }
+    get location(): ElementRef<any> {
+        return null;
+    }
+    get injector(): Injector {
+        return null;
+    }
+    get instance(): AbstractSearchTypeComponent {
+        return new MockAbstractSearchTypeComponent()
+    }
+    get hostView(): ViewRef {
+        return null;
+    }
+    get changeDetectorRef(): ChangeDetectorRef {
+        return null;
+    }
+    get componentType(): Type<any> {
+        return null;
+    }
+    destroy(): void {
+        return null;
+    }
+    onDestroy(callback: Function): void {
+        return null;
+    }
+
+
+}
+class MockViewContainerRef extends ViewContainerRef {
+    get element(): ElementRef<any> {
+        return null;
+    }
+    get injector(): Injector {
+        return null;
+    }
+    get parentInjector(): Injector {
+        return null;
+    }
+    clear(): void {
+        return null;
+    }
+    get(index: number): ViewRef {
+        return null;
+    }
+    get length(): number {
+        return null;
+    }
+    createEmbeddedView(templateRef: unknown, context?: unknown, index?: unknown): EmbeddedViewRef<any> {
+        return null;
+    }
+    createComponent(componentFactory: unknown, index?: unknown, injector?: unknown, projectableNodes?: unknown, environmentInjector?: unknown): ComponentRef<any> | ComponentRef<any> {
+
+        return new MockComponentRef();
+    }
+    insert(viewRef: ViewRef, index?: number): ViewRef {
+        return null;
+    }
+    move(viewRef: ViewRef, currentIndex: number): ViewRef {
+        return null;
+    }
+    indexOf(viewRef: ViewRef): number {
+        return null;
+    }
+    remove(index?: number): void {
+        return null;
+    }
+    detach(index?: number): ViewRef {
+        return null;
+    }
+
+}
+describe('[instance][search][component][criteria] CriterionComponent', () => {
+    let component: CriterionComponent;
+    let fixture: ComponentFixture<CriterionComponent>;
+    let store: MockStore;
+    let viewContainerRef: MockViewContainerRef = new MockViewContainerRef();
+    let attribute: Attribute;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                CriterionComponent
+            ],
+            providers: [
+                provideMockStore({}),
+            ],
+        })
+            .overrideComponent(CriterionComponent, {
+                set: { changeDetection: ChangeDetectionStrategy.Default }
+            }).compileComponents()
+        fixture = TestBed.createComponent(CriterionComponent);
+        component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
+        component.SearchTypeLoaderDirective = {
+            viewContainerRef: viewContainerRef
+        }
+        component.attribute = { ...attribute, search_type: 'test' }
+        component.criteriaList = [{ id: 1, type: 'testCriterion' }, { id: 2, type: 'test2' }];
+        component.searchTypeComponent = new MockAbstractSearchTypeComponent();
+    });
+    it('should create component', async () => {
+        fixture.detectChanges();
+        expect(component).toBeTruthy();
+    });
+    it('should call ngOnChanges and apply changes', () => {
+        fixture.detectChanges();
+        const spy = jest.spyOn(component, 'ngOnChanges');
+        expect(spy).toHaveBeenCalledTimes(0);
+        component.ngOnChanges(
+            {
+                criterion: new SimpleChange(component.criterion, { id: 1, type: 'test' }, false),
+                criteriaList: new SimpleChange(component.criteriaList, [{ id: 1, type: 'test' }, { id: 2, type: 'test2' }], false),
+            }
+        );
+        fixture.detectChanges();
+        expect(spy).toHaveBeenCalledTimes(1);
+
+    });
+    it('emitAdd() raises addCriterion event', () => {
+        let spy = jest.spyOn(component.addCriterion, 'emit');
+        component.searchTypeComponent.nullOrNotNull = 'test';
+        let criterion: Criterion = {
+            id: component.attribute.id,
+            type: 'field',
+            operator: "test"
+        } as FieldCriterion;
+        component.emitAdd();
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(criterion);
+
+    });
+    it('emitAdd() raises addCriterion event', () => {
+        component.searchTypeComponent.nullOrNotNull = '';
+        let spy = jest.spyOn(component.updateCriterion, 'emit');
+        let criterion: Criterion = {
+            id: 1,
+            type: 'field',
+            operator: "test"
+        } as FieldCriterion;
+        component.emitAdd();
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(criterion);
+
+    });
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria/search-type/abstract-search-type.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/abstract-search-type.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..30a1a154264e68253b1d308ac3274ee887b86b0c
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/abstract-search-type.component.spec.ts
@@ -0,0 +1,74 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { TestBed } from "@angular/core/testing";
+import { ReactiveFormsModule, UntypedFormControl, UntypedFormGroup } from "@angular/forms";
+import { Criterion } from "src/app/instance/store/models";
+import { Attribute } from "src/app/metamodel/models";
+import { AbstractSearchTypeComponent } from "./abstract-search-type.component";
+class TestAbstractSearchTypeComponent extends AbstractSearchTypeComponent {
+    getCriterion(): Criterion {
+        return { id: 1, type: 'test' }
+    }
+}
+describe('[instance][search][components][criteria][search-type] AbstractSearchTypeComponent', () => {
+    let component: TestAbstractSearchTypeComponent;
+    let attribute: Attribute;
+    TestBed.configureTestingModule({
+        declarations: [
+            TestAbstractSearchTypeComponent
+        ],
+        imports: [
+            ReactiveFormsModule
+        ]
+    });
+    beforeEach(() => {
+        component = new TestAbstractSearchTypeComponent();
+        component.form = new UntypedFormGroup({
+            test: new UntypedFormControl('test')
+        });
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should call form.patchValue', () => {
+        let spy = jest.spyOn(component.form, 'patchValue');
+        let criterion: Criterion = { id: 1, type: 'test' };
+        component.setCriterion(criterion);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(criterion);
+    });
+    it('setCriterion(criterion: Criterion) should call form.reset', () => {
+        let spy = jest.spyOn(component.form, 'reset');
+        component.setCriterion(null);
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+    it('isValid() should return true', () => {
+        expect(component.isValid()).toBe(true);
+    });
+    it('disable() should disable the form', () => {
+        expect(component.form.disabled).toBe(false);
+        component.disable();
+        expect(component.form.disabled).toBe(true);
+    });
+    it('getType() should return text', () => {
+        component.attribute = { ...attribute, operator: 'in' };
+        expect(component.getType()).toEqual('text');
+    });
+    it('getType() should return number', () => {
+        component.attribute = { ...attribute, type: 'integer' };
+        expect(component.getType()).toEqual('number');
+    });
+    it('ngOnDestroy() should unsubscribe from formValueChangesSubscription', () => {
+        component.formValueChangesSubscription = component.form.valueChanges.pipe().subscribe();
+        expect(component.formValueChangesSubscription.closed).toBe(false);
+        component.ngOnDestroy();
+        expect(component.formValueChangesSubscription.closed).toBe(true);
+    });
+});
\ No newline at end of file
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 0000000000000000000000000000000000000000..7f26a773873b65d7b0a56472aa224512718e59f5
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/between-date.component.spec.ts
@@ -0,0 +1,75 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { Criterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { BetweenDateComponent } from './between-date.component';
+
+describe('[Instance][search][components][criteria][search-type] BetweenDateComponent', () => {
+    let component: BetweenDateComponent;
+    let fixture: ComponentFixture<BetweenDateComponent>;
+    let spyOnLabel;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [BetweenDateComponent],
+            imports: [ReactiveFormsModule]
+        });
+        fixture = TestBed.createComponent(BetweenDateComponent);
+        component = fixture.componentInstance;
+        fixture.detectChanges();
+        spyOnLabel = jest.spyOn(component.form.controls.label, 'setValue');
+
+    });
+
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set values in form on dateRange and  label with bw', () => {
+        let criterion: Criterion = { id: 1, type: 'between' };
+        let spyOnDateRange = jest.spyOn(component.form.controls.dateRange, 'setValue');
+
+        component.setCriterion(criterion);
+        expect(spyOnDateRange).toHaveBeenCalledTimes(1);
+        expect(spyOnLabel).toHaveBeenCalledTimes(1);
+        expect(spyOnLabel).toHaveBeenCalledWith('bw');
+
+    });
+    it('setCriterion(criterion: Criterion) should set value on label when the  criterion type is not between', () => {
+        let criterion: Criterion = { id: 1, type: 'test' };
+        component.setCriterion(criterion);
+        expect(spyOnLabel).toHaveBeenCalledTimes(1);
+    });
+    it('setCriterion(criterion: Criterion) should set value on label when criterion param is undefined', () => {
+        component.setCriterion(null);
+        expect(spyOnLabel).toHaveBeenCalledWith('bw');
+    });
+    it('getCriterion() should return an criterion of type between', () => {
+        component.form.controls.dateRange.setValue([
+            new Date(),
+            new Date()
+        ]);
+        component.attribute = { ...attribute, id: 1 };
+        expect(component.getCriterion().type).toEqual('between');
+    });
+    it('labelOnChange() should disable dateRange', () => {
+        component.form.controls.label.setValue('nl');
+        expect(component.form.controls.dateRange.disabled).toBe(false);
+        component.labelOnChange();
+        expect(component.form.controls.dateRange.disabled).toBe(true);
+    });
+    it('labelOnChange() should enable dateRange', () => {
+        component.form.controls.dateRange.disable();
+        expect(component.form.controls.dateRange.enabled).toBe(false);
+        component.labelOnChange();
+        expect(component.form.controls.dateRange.enabled).toBe(true);
+    });
+});
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 0000000000000000000000000000000000000000..163434c862d984251e561bee2aad4ac595304d9a
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/between.component.spec.ts
@@ -0,0 +1,92 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { Criterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { BetweenComponent } from './between.component';
+
+describe('[Instance][search][components][criteria][search-type] BetweenComponent', () => {
+    let component: BetweenComponent;
+    let fixture: ComponentFixture<BetweenComponent>;
+    let spyOnLabel;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [BetweenComponent],
+            imports: [ReactiveFormsModule]
+        });
+        fixture = TestBed.createComponent(BetweenComponent);
+        component = fixture.componentInstance;
+        component.attribute = { ...attribute, id: 1, placeholder_min: 'min', placeholder_max: 'max' };
+        fixture.detectChanges();
+        spyOnLabel = jest.spyOn(component.form.controls.label, 'setValue');
+
+    });
+
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set values in form on dateRange and  label with bw', () => {
+        let criterion: Criterion = { id: 1, type: 'between' };
+
+        component.setCriterion(criterion);
+        expect(spyOnLabel).toHaveBeenCalledTimes(1);
+        expect(spyOnLabel).toHaveBeenCalledWith('bw');
+
+    });
+    it('setCriterion(criterion: Criterion) should set value on label when the  criterion type is not between', () => {
+        let criterion: Criterion = { id: 1, type: 'test' };
+        component.setCriterion(criterion);
+        expect(spyOnLabel).toHaveBeenCalledTimes(1);
+    });
+    it('setCriterion(criterion: Criterion) should set value on label when criterion param is undefined', () => {
+        component.setCriterion(null);
+        expect(spyOnLabel).toHaveBeenCalledWith('bw');
+    });
+    it('getCriterion() should return an criterion of type between', () => {
+        expect(component.getCriterion().type).toEqual('between');
+    });
+    it('getPlaceholderMin() should return "min"', () => {
+        expect(component.getPlaceholderMin()).toEqual('min');
+    });
+    it('getPlaceholderMin() should return ""', () => {
+        component.attribute = { ...component.attribute, placeholder_min: null };
+        expect(component.getPlaceholderMin()).toEqual('');
+    });
+    it('getPlaceholderMax() should return "max"', () => {
+        component.attribute = { ...component.attribute, placeholder_max: null };
+        expect(component.getPlaceholderMax()).toEqual('');
+    });
+    it('getPlaceholderMax() should return ""', () => {
+        expect(component.getPlaceholderMax()).toEqual('max');
+    });
+    it('isValid() should return true', () => {
+        component.form.controls.min.setValue(1);
+        expect(component.isValid()).toBe(1);
+    });
+    it('labelOnChange() should disable max and min', () => {
+        component.form.controls.label.setValue('nl');
+        expect(component.form.controls.min.disabled).toBe(false);
+        expect(component.form.controls.max.disabled).toBe(false);
+        component.labelOnChange();
+        expect(component.form.controls.min.disabled).toBe(true);
+        expect(component.form.controls.max.disabled).toBe(true);
+    });
+    it('labelOnChange() should enable max and min', () => {
+        component.form.controls.max.disable();
+        component.form.controls.min.disable();
+        expect(component.form.controls.max.enabled).toBe(false);
+        expect(component.form.controls.min.enabled).toBe(false);
+        component.labelOnChange();
+        expect(component.form.controls.max.enabled).toBe(true);
+        expect(component.form.controls.min.enabled).toBe(true);
+    });
+});
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 0000000000000000000000000000000000000000..4c8878fd3ec0c3ac456a344951832c80585aa436
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/checkbox.component.spec.ts
@@ -0,0 +1,133 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule, UntypedFormArray, UntypedFormControl } from '@angular/forms';
+import { FieldCriterion, SelectMultipleCriterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { CheckboxComponent } from './checkbox.component';
+
+describe('[Instance][search][components][criteria][search-type] CheckboxComponent', () => {
+    let component: CheckboxComponent;
+    let fixture: ComponentFixture<CheckboxComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [CheckboxComponent],
+            imports: [ReactiveFormsModule]
+        });
+        fixture = TestBed.createComponent(CheckboxComponent);
+        component = fixture.componentInstance;
+        component.form.addControl('checkboxes', new UntypedFormArray([]));
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ]
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setAttribute(attribute: Attribute) should add a checkboxes with attributes options ', () => {
+        let attributeParam = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ]
+        }
+        component.form.removeControl('checkboxes');
+        component.setAttribute(attributeParam);
+        fixture.detectChanges()
+        expect(component.form.controls.checkboxes.value.length).toEqual(2);
+    });
+    it('setCriterion(criterion: Criterion) should set label value to in and test1 option  to true', () => {
+        let spy = jest.spyOn(component.form.controls.label, 'setValue');
+        let criterion: SelectMultipleCriterion = { id: 1, type: 'multiple', options: [{ label: 'test1', display: 1, value: 'test1' }] };
+
+        component.form.removeControl('checkboxes');
+        component.form.addControl('checkboxes', new UntypedFormArray([
+            new UntypedFormControl(),
+        ]));
+        component.setCriterion(criterion);
+        expect(spy).toHaveBeenCalledWith('in');
+        expect((component.form.controls.checkboxes as UntypedFormArray).controls[0].value).toBe(true);
+    });
+    it('setCriterion(criterion: Criterion) should set label value to test', () => {
+        let spy = jest.spyOn(component.form.controls.label, 'setValue');
+        let criterion: FieldCriterion = { id: 1, type: 'test', operator: 'test', value: null };
+        component.setCriterion(criterion);
+        expect(spy).toHaveBeenCalledWith('test');
+    });
+    it('setCriterion(criterion: Criterion) should set label value to in when criterion param is undefined', () => {
+        let spy = jest.spyOn(component.form.controls.label, 'setValue');
+        component.setCriterion(undefined);
+        expect(spy).toHaveBeenCalledWith('in');
+    });
+    it('getCriterion() should return a criterion of type multiple', () => {
+
+        expect(component.getCriterion().type).toEqual('multiple');
+    });
+    it('isValid() should return true when values length is more then 0', () => {
+        component.form.removeControl('checkboxes');
+        component.form.addControl('checkboxes', new UntypedFormArray([
+            new UntypedFormControl('test'),
+            new UntypedFormControl('test2')
+        ]));
+        expect(component.isValid()).toBe(true);
+    });
+    it('isValid() should return true when  label value is nl or nnl', () => {
+        component.form.controls.label.setValue('nl')
+        expect(component.isValid()).toBe(true);
+        component.form.controls.label.setValue('nnl')
+        expect(component.isValid()).toBe(true);
+    });
+    it('isChecked() should return true when at least one option is checked ', () => {
+
+        component.form.removeControl('checkboxes');
+        component.form.addControl('checkboxes', new UntypedFormArray([
+            new UntypedFormControl('test'),
+        ]));
+        expect(component.isChecked()).toBe(true);
+    });
+    it('labelOnChange() should disable checkbox when label value is nl or nnl', () => {
+
+        component.form.removeControl('checkboxes');
+        component.form.addControl('checkboxes', new UntypedFormArray([
+            new UntypedFormControl('test'),
+        ]));
+        let checkboxes = component.form.controls.checkboxes as UntypedFormArray
+        expect(checkboxes.controls.find(value => value.disabled)).toBeUndefined();
+        component.form.controls.label.setValue('nl');
+        component.labelOnChange();
+        expect(checkboxes.controls.find(value => value.disabled)).toBeDefined();
+        component.form.controls.label.setValue('nnl');
+        component.labelOnChange();
+        expect(checkboxes.controls.find(value => value.disabled)).toBeDefined();
+
+
+    });
+
+    it('labelOnChange() should enable  checkboxes options when label value is not nl or nnl', () => {
+        component.form.removeControl('checkboxes');
+        component.form.addControl('checkboxes', new UntypedFormArray([
+            new UntypedFormControl('test'),
+        ]));
+        let checkboxes = component.form.controls.checkboxes as UntypedFormArray
+        checkboxes.controls.forEach(value => value.disable());
+        expect(checkboxes.controls.find(value => value.enabled)).toBeUndefined();
+        component.labelOnChange();
+        expect(checkboxes.controls.find(value => value.enabled)).toBeDefined();
+
+
+    });
+
+});
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 0000000000000000000000000000000000000000..337682c7fd3fa94e7d431c31622b81572f67c2a1
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/datalist.component.spec.ts
@@ -0,0 +1,85 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { Attribute } from 'src/app/metamodel/models';
+import { DatalistComponent } from './datalist.component';
+
+describe('[Instance][search][components][criteria][search-type] DatalistComponent', () => {
+    let component: DatalistComponent;
+    let fixture: ComponentFixture<DatalistComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [DatalistComponent],
+            imports: [ReactiveFormsModule]
+        });
+        fixture = TestBed.createComponent(DatalistComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should call operatorOnchange method when criterion param is defined', () => {
+        const spy = jest.spyOn(component, 'operatorOnChange');
+        component.setCriterion({ id: 1, type: 'test' });
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+    it('setCriterion(criterion: Criterion) should set test to form.operator value when criterion param is undefined', () => {
+        expect(component.form.controls.operator.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.operator.value).toEqual('test');
+    });
+    it('getCriterion() should return a criterion of type FieldCriterion', () => {
+        expect(component.getCriterion().type).toEqual('field');
+    });
+    it('isValid() should true when form is valid or when operator value is nl or nnl', () => {
+        expect(component.isValid()).toBe(false);
+        component.form.controls.operator.setValue('test');
+        component.form.controls.value.setValue('test');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nnl');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nl');
+        expect(component.isValid()).toBe(true);
+    });
+    it('getPlaceholder() should return "" when attribute.placeholder is undefined', () => {
+        component.attribute.placeholder_min = null;
+        expect(component.getPlaceholder()).toEqual('');
+    })
+    it('getPlaceholder() should return min ', () => {
+        expect(component.getPlaceholder()).toEqual('min');
+    });
+    it('operatorOnChange() should disable value formcontrol when operator value is nl or nnl', () => {
+        expect(component.form.controls.value.disabled).toBe(false);
+        component.form.controls.operator.setValue('nl');
+        component.operatorOnChange();
+        expect(component.form.controls.value.disabled).toBe(true);
+
+    });
+    it('operatorOnChange() should enable value formcontrol when operator value is not  nl or nnl', () => {
+        component.form.controls.value.disable();
+        expect(component.form.controls.value.enabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.value.enabled).toBe(true);
+
+    });
+
+});
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 0000000000000000000000000000000000000000..184783b5d8cbf3becd37f38385b1c57f3a604872
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/date.component.spec.ts
@@ -0,0 +1,89 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { FieldCriterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { DateComponent } from './date.component';
+
+describe('[Instance][search][components][criteria][search-type] DateComponent', () => {
+    let component: DateComponent;
+    let fixture: ComponentFixture<DateComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [DateComponent],
+            imports: [ReactiveFormsModule]
+        });
+        fixture = TestBed.createComponent(DateComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set date value when criterion param is defined', () => {
+        expect(component.form.controls.date.value).toEqual('');
+        component.setCriterion({ id: 1, type: 'test', value: new Date().toDateString() } as FieldCriterion);
+        expect(component.form.controls.date.value).not.toEqual('');
+    });
+    it('setCriterion(criterion: Criterion) should set test to form.operator value when criterion param is undefined', () => {
+        expect(component.form.controls.operator.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.operator.value).toEqual('test');
+    });
+    it('getCriterion() should return a criterion of type FieldCriterion', () => {
+        component.form.controls.date.setValue(new Date());
+        expect(component.getCriterion().type).toEqual('field');
+    });
+    it('isValid() should true when form is valid or when operator value is nl or nnl', () => {
+        expect(component.isValid()).toBe(false);
+        component.form.controls.operator.setValue('test');
+        component.form.controls.date.setValue(new Date());
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nnl');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nl');
+        expect(component.isValid()).toBe(true);
+    });
+    it('getPlaceholder() should return "" when attribute.placeholder is undefined', () => {
+        component.attribute.placeholder_min = null;
+        expect(component.getPlaceholder()).toEqual('');
+    })
+    it('getPlaceholder() should return min ', () => {
+        expect(component.getPlaceholder()).toEqual('min');
+    });
+    it('getDateString(date: Date) should return 2022-12-03', () => {
+        expect(component.getDateString(new Date('12-03-2022'))).toEqual('2022-12-03');
+    });
+    it('operatorOnChange() should disable date formcontrol when operator value is nl or nnl', () => {
+        expect(component.form.controls.date.disabled).toBe(false);
+        component.form.controls.operator.setValue('nl');
+        component.operatorOnChange();
+        expect(component.form.controls.date.disabled).toBe(true);
+    });
+    it('operatorOnChange() should enable date formcontrol when operator value is not  nl or nnl', () => {
+        component.form.controls.date.disable();
+        expect(component.form.controls.date.enabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.date.enabled).toBe(true);
+
+    });
+
+});
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
new file mode 100644
index 0000000000000000000000000000000000000000..a9dbd13822854ce8ab59f09135e77fad1d427704
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/datetime.component.spec.ts
@@ -0,0 +1,98 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { Attribute } from 'src/app/metamodel/models';
+import { DateTimeComponent } from './datetime.component';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { FieldCriterion } from 'src/app/instance/store/models';
+
+
+describe('[Instance][search][components][criteria][search-type] DateTimeComponent', () => {
+    let component: DateTimeComponent;
+    let fixture: ComponentFixture<DateTimeComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [DateTimeComponent],
+            imports: [
+                ReactiveFormsModule,
+                NgSelectModule
+            ],
+        });
+        fixture = TestBed.createComponent(DateTimeComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set operator value, date, hh, mm when a criterion is given in param', () => {
+
+        let spyOnDate = jest.spyOn(component.form.controls.date, 'setValue');
+        let spyOnHh = jest.spyOn(component.form.controls.hh, 'setValue');
+        let spyOnMm = jest.spyOn(component.form.controls.mm, 'setValue');
+        component.setCriterion({ id: 1, type: 'test', operator: 'test', value: '2022-12-03 1240' } as FieldCriterion);
+        expect(spyOnDate).toHaveBeenCalledTimes(1);
+        expect(spyOnHh).toHaveBeenCalledTimes(1);
+        expect(spyOnMm).toHaveBeenCalledTimes(1);
+    });
+    it('setCriterion(criterion: Criterion) should set operator value to test', () => {
+        expect(component.form.controls.operator.value).toEqual('')
+        component.setCriterion(null);
+        expect(component.form.controls.operator.value).toEqual('test');
+
+    });
+    it('getCriterion() should return a criterion of type FieldCriterion', () => {
+        component.form.controls.date.setValue(new Date());
+        expect(component.getCriterion().type).toEqual('field');
+    });
+    it('isValid() should true when form is valid or when operator value is nl or nnl', () => {
+        expect(component.isValid()).toBe(false);
+        component.form.controls.operator.setValue('test');
+        component.form.controls.date.setValue(new Date());
+        component.form.controls.hh.setValue('10');
+        component.form.controls.mm.setValue('10');
+
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nnl');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nl');
+        expect(component.isValid()).toBe(true);
+    });
+    it('operatorOnChange() should disable date, hh, mm formcontrol when operator value is nl or nnl', () => {
+        component.form.controls.operator.setValue('nl');
+        expect(component.form.controls.date.disabled).toBe(false);
+        expect(component.form.controls.hh.disabled).toBe(false);
+        expect(component.form.controls.hh.disabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.date.disabled).toBe(true);
+        expect(component.form.controls.hh.disabled).toBe(true);
+        expect(component.form.controls.hh.disabled).toBe(true);
+
+    });
+    it('operatorOnChange() should enable date, hh, mm formcontrol when operator value is not  nl or nnl', () => {
+        component.form.controls.date.disable();
+        expect(component.form.controls.date.enabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.date.enabled).toBe(true);
+
+    });
+
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/field.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/field.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..487eb679809aec338ecd1c476bf6ef9a0f04133d
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/field.component.spec.ts
@@ -0,0 +1,79 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { Attribute } from 'src/app/metamodel/models';
+import { FieldComponent } from './field.component';
+
+describe('[Instance][search][components][criteria][search-type] FieldComponent', () => {
+    let component: FieldComponent;
+    let fixture: ComponentFixture<FieldComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [FieldComponent],
+            imports: [
+                ReactiveFormsModule,
+            ],
+        });
+        fixture = TestBed.createComponent(FieldComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should call operatorOnChange', () => {
+        let spy = jest.spyOn(component, 'operatorOnChange');
+        component.setCriterion({ id: 1, type: 'test' });
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+    it('setCriterion(criterion: Criterion) should set operator value to test', () => {
+        expect(component.form.controls.operator.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.operator.value).toEqual('test');
+
+    });
+    it('getCriterion() should return a criterion with type field', () => {
+        expect(component.getCriterion().type).toEqual('field');
+    });
+    it('isValid() should true when form is valid or when operator value is nl or nnl', () => {
+        expect(component.isValid()).toBe(false);
+        component.form.controls.operator.setValue('test');
+        component.form.controls.value.setValue('test');
+        expect(component.isValid()).toBe(true);
+    });
+    it('operatorOnChange() should disable value formcontrol when operator value is nl or nnl', () => {
+        component.form.controls.operator.setValue('nl');
+        expect(component.form.controls.value.disabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.value.disabled).toBe(true);
+    });
+    it('operatorOnChange() should enable value formcontrol when operator value is not  nl or nnl', () => {
+        component.form.controls.value.disable();
+        expect(component.form.controls.value.enabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.value.enabled).toBe(true);
+
+    });
+    it('getPlaceholder() should return ""', () => {
+        component.attribute.placeholder_min = '';
+        expect(component.getPlaceholder()).toEqual('');
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/index.spec.ts b/client/src/app/instance/search/components/criteria/search-type/index.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5a98c205793f16369005c682d4cabcfd233177a0
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/index.spec.ts
@@ -0,0 +1,79 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { getSearchTypeComponent } from "."
+import { BetweenDateComponent } from "./between-date.component";
+import { BetweenComponent } from "./between.component";
+import { CheckboxComponent } from "./checkbox.component";
+import { DatalistComponent } from "./datalist.component";
+import { DateComponent } from "./date.component";
+import { DateTimeComponent } from "./datetime.component";
+import { FieldComponent } from "./field.component";
+import { JsonComponent } from "./json.component";
+import { ListComponent } from "./list.component";
+import { RadioComponent } from "./radio.component";
+import { SelectMultipleComponent } from "./select-multiple.component";
+import { SelectComponent } from "./select.component";
+import { SvomJsonKwComponent } from "./svom-json-kw.component";
+import { TimeComponent } from "./time.component";
+
+describe('[Instance][search][components][criteria][search-type] index', () => {
+
+    let theFunction = getSearchTypeComponent;
+
+    it('it should return FieldComponent', () => {
+        expect(theFunction('field')).toEqual(FieldComponent);
+    });
+    it('it should return BetweenComponent', () => {
+        expect(theFunction('between')).toEqual(BetweenComponent);
+    });
+    it('it should return SelectComponent', () => {
+        expect(theFunction('select')).toEqual(SelectComponent);
+    });
+    it('it should return SelectMultipleComponent', () => {
+        expect(theFunction('select-multiple')).toEqual(SelectMultipleComponent);
+    });
+    it('it should return DatalistComponent', () => {
+        expect(theFunction('field')).toEqual(FieldComponent);
+    });
+    it('it should return DatalistComponent', () => {
+        expect(theFunction('datalist')).toEqual(DatalistComponent);
+    });
+    it('it should return ListComponent', () => {
+        expect(theFunction('list')).toEqual(ListComponent);
+    });
+    it('it should return RadioComponent', () => {
+        expect(theFunction('radio')).toEqual(RadioComponent);
+    });
+    it('it should return CheckboxComponent', () => {
+        expect(theFunction('checkbox')).toEqual(CheckboxComponent);
+    });
+    it('it should return BetweenDateComponent', () => {
+        expect(theFunction('between-date')).toEqual(BetweenDateComponent);
+    });
+    it('it should return DateComponent', () => {
+        expect(theFunction('date')).toEqual(DateComponent);
+    });
+    it('it should return TimeComponent', () => {
+        expect(theFunction('time')).toEqual(TimeComponent);
+    });
+    it('it should return FieldComponent', () => {
+        expect(theFunction('field')).toEqual(FieldComponent);
+    });
+    it('it should return DateTimeComponent', () => {
+        expect(theFunction('date-time')).toEqual(DateTimeComponent);
+    });
+    it('it should return JsonComponent', () => {
+        expect(theFunction('json')).toEqual(JsonComponent);
+    });
+    it('it should return SvomJsonKwComponent ', () => {
+        expect(theFunction('svom_json_kw')).toEqual(SvomJsonKwComponent);
+    });
+
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..db21fa28a1bf6f98df95c10fd397164a34d697b0
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/json.component.spec.ts
@@ -0,0 +1,87 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { FieldCriterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { JsonComponent } from './json.component';
+
+describe('[Instance][search][components][criteria][search-type] JsonComponent', () => {
+    let component: JsonComponent;
+    let fixture: ComponentFixture<JsonComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [JsonComponent],
+            imports: [
+                ReactiveFormsModule,
+                NgSelectModule
+            ],
+        });
+        fixture = TestBed.createComponent(JsonComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set label value with js if criterion type is json', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion({ id: 1, type: 'json' });
+        expect(component.form.controls.label.value).toEqual('js');
+
+    });
+    it('setCriterion(criterion: Criterion) should set label value with test if criterion type is not json', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion({ id: 1, type: '', operator: 'test' } as FieldCriterion);
+        expect(component.form.controls.label.value).toEqual('test');
+
+    });
+    it('setCriterion(criterion: Criterion) should set label value with js when criterion param is not defined', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.label.value).toEqual('js');
+
+    });
+    it('getCriterion() should return a criterion with type json', () => {
+        expect(component.getCriterion().type).toEqual('json');
+    });
+    it('labelOnChange() should disable operator, path, value when label value is nl or nnl', () => {
+        component.form.controls.label.setValue('nl');
+        expect(component.form.controls.operator.enabled).toBe(true);
+        expect(component.form.controls.path.enabled).toBe(true);
+        expect(component.form.controls.value.enabled).toBe(true);
+        component.labelOnChange();
+        expect(component.form.controls.operator.enabled).toBe(false);
+        expect(component.form.controls.path.enabled).toBe(false);
+        expect(component.form.controls.value.enabled).toBe(false);
+
+    });
+    it('labelOnChange() should enable operator, path, value when label value is not  nl or nnl', () => {
+        component.form.controls.operator.disable()
+        component.form.controls.path.disable()
+        component.form.controls.value.disable()
+        component.labelOnChange();
+        expect(component.form.controls.operator.enabled).toBe(true);
+        expect(component.form.controls.path.enabled).toBe(true);
+        expect(component.form.controls.value.enabled).toBe(true);
+
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/list.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/list.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b06424b06802350009d7e4bda497d845af448470
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/list.component.spec.ts
@@ -0,0 +1,95 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { FieldCriterion, ListCriterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { ListComponent } from './list.component';
+
+describe('[Instance][search][components][criteria][search-type] ListComponent', () => {
+    let component: ListComponent;
+    let fixture: ComponentFixture<ListComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [ListComponent],
+            imports: [
+                ReactiveFormsModule,
+
+            ],
+        });
+        fixture = TestBed.createComponent(ListComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set list and label value when criterion param is defined', () => {
+        let spyonLabel = jest.spyOn(component.form.controls.label, 'setValue');
+        let spyOnList = jest.spyOn(component.form.controls.list, 'setValue');
+        component.setCriterion({ id: 1, type: 'list', values: [] } as ListCriterion);
+        expect(spyOnList).toHaveBeenCalledTimes(1);
+        expect(spyonLabel).toHaveBeenCalledTimes(1);
+
+    });
+    it('setCriterion(criterion: Criterion) should set label value to test when criterion.type value is not list', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion({ id: 1, type: '', operator: 'test', } as FieldCriterion);
+        expect(component.form.controls.label.value).toEqual('test');
+
+    });
+    it('setCriterion(criterion: Criterion) should set label value to in when criterion param is not defined', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.label.value).toEqual('in');
+
+    });
+    it('setCriterion(criterion: Criterion) should set label value to in when criterion param is not defined', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.label.value).toEqual('in');
+
+    });
+    it('getCriterion() should return a criterion with type list', () => {
+        expect(component.getCriterion().type).toEqual('list');
+    });
+    it('getPlaceholder() should return "" when attribute.placeholder is undefined', () => {
+        component.attribute.placeholder_min = null;
+        expect(component.getPlaceholder()).toEqual('');
+    })
+    it('getPlaceholder() should return min ', () => {
+        expect(component.getPlaceholder()).toEqual('min');
+    });
+    it('labelOnChange() should disable list when label value is nl or nnl', () => {
+        component.form.controls.label.setValue('nl');
+        expect(component.form.controls.list.enabled).toBe(true);
+        component.labelOnChange();
+        expect(component.form.controls.list.enabled).toBe(false);
+
+    });
+    it('labelOnChange() should enable list when label value is not  nl or nnl', () => {
+        component.form.controls.list.disable()
+
+        component.labelOnChange();
+        expect(component.form.controls.list.enabled).toBe(true);
+
+
+    });
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria/search-type/radio.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/radio.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c2ad9fe47f4abb86b271319d07aba11084fb3edb
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/radio.component.spec.ts
@@ -0,0 +1,80 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { FieldCriterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { RadioComponent } from './radio.component';
+
+describe('[Instance][search][components][criteria][search-type] RadioComponent', () => {
+    let component: RadioComponent;
+    let fixture: ComponentFixture<RadioComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [RadioComponent],
+            imports: [ReactiveFormsModule]
+        });
+        fixture = TestBed.createComponent(RadioComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set radio value and call operatorOnChange when criterion param  defined', () => {
+        let spy = jest.spyOn(component, 'operatorOnChange');
+        expect(component.form.controls.radio.value).toEqual('');
+        component.setCriterion({ id: 1, value: 'test', type: 'test' } as FieldCriterion);
+        expect(component.form.controls.radio.value).toEqual('test');
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+    it('setCriterion(criterion: Criterion) should set operator value to test value when criterion param is not defined', () => {
+        expect(component.form.controls.operator.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.operator.value).toEqual('test');
+    });
+    it('getCriterion() should return a criterion of type FieldCriterion', () => {
+        component.form.controls.radio.setValue('test1');
+        expect(component.getCriterion().type).toEqual('field');
+    });
+    it('isValid() should true when form is valid or when operator value is nl or nnl', () => {
+        expect(component.isValid()).toBe(false);
+        component.form.controls.operator.setValue('test');
+        component.form.controls.radio.setValue('test');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nnl');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nl');
+        expect(component.isValid()).toBe(true);
+    });
+    it('operatorOnChange() should disable radio formcontrol when operator value is nl or nnl', () => {
+        expect(component.form.controls.radio.disabled).toBe(false);
+        component.form.controls.operator.setValue('nl');
+        component.operatorOnChange();
+        expect(component.form.controls.radio.disabled).toBe(true);
+    });
+    it('operatorOnChange() should enable radio formcontrol when operator value is not  nl or nnl', () => {
+        component.form.controls.radio.disable();
+        expect(component.form.controls.radio.enabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.radio.enabled).toBe(true);
+
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/search-type-loader.directive.spec.ts b/client/src/app/instance/search/components/criteria/search-type/search-type-loader.directive.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a41b21501efd28b88bfbcdb22290dce4c297d042
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/search-type-loader.directive.spec.ts
@@ -0,0 +1,24 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { SearchTypeLoaderDirective } from "./search-type-loader.directive";
+
+class MockSearchTypeLoaderDirective extends SearchTypeLoaderDirective {
+    constructor() {
+        super(null)
+    }
+
+}
+describe('[Instance][search][components][criteria][search-type] SearchTypeLoaderDirective', () => {
+    it('should create the directive', () => {
+        let searchTypeLoaderDirective = new MockSearchTypeLoaderDirective();
+        expect(searchTypeLoaderDirective).toBeTruthy();
+    })
+
+})
\ No newline at end of file
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
new file mode 100644
index 0000000000000000000000000000000000000000..4d8696eabafdcc28de7afbe7c598c3826701ac28
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/select-multiple.component.spec.ts
@@ -0,0 +1,93 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { FieldCriterion, SelectMultipleCriterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { SelectMultipleComponent } from './select-multiple.component';
+
+describe('[Instance][search][components][criteria][search-type] SelectMultipleComponent', () => {
+    let component: SelectMultipleComponent;
+    let fixture: ComponentFixture<SelectMultipleComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [SelectMultipleComponent],
+            imports: [
+                ReactiveFormsModule,
+                NgSelectModule
+            ]
+        });
+        fixture = TestBed.createComponent(SelectMultipleComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set label and select values when criterion type is muliple', () => {
+        expect(component.form.controls.select.value).toEqual('');
+        expect(component.form.controls.label.value).toEqual('');
+        let criterion: SelectMultipleCriterion = {
+            id: 1, options: [{ display: 10, label: 'test1', value: 'test1' },
+            { display: 10, label: 'test2', value: 'test2' }
+            ], type: 'multiple'
+        };
+        component.setCriterion(criterion);
+        expect(component.form.controls.select.value).toEqual(['test1', 'test2']);
+        expect(component.form.controls.label.value).toEqual('in');
+
+    });
+    it('setCriterion(criterion: Criterion) should set label and select values when criterion type is not multiple', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        let criterion: FieldCriterion = {
+            id: 1,
+            operator: 'test',
+            type: 'test',
+            value: ''
+        }
+        component.setCriterion(criterion);
+        expect(component.form.controls.label.value).toEqual('test');
+
+    });
+    it('setCriterion(criterion: Criterion) should set label and select values when criterion param is not defined', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.label.value).toEqual('in');
+
+    });
+    it('getCriterion() should return a criterion with type list', () => {
+        expect(component.getCriterion().type).toEqual('multiple');
+    });
+    it('labelOnChange() should disable select when label value is nl or nnl', () => {
+        component.form.controls.label.setValue('nl');
+        expect(component.form.controls.select.enabled).toBe(true);
+        component.labelOnChange();
+        expect(component.form.controls.select.enabled).toBe(false);
+
+    });
+    it('labelOnChange() should enable list when label value is not  nl or nnl', () => {
+        component.form.controls.select.disable()
+        component.labelOnChange();
+        expect(component.form.controls.select.enabled).toBe(true);
+
+
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/select.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/select.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e992272a79d681a7d6a6376e094d1d695cda37e5
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/select.component.spec.ts
@@ -0,0 +1,84 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { FieldCriterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { SelectComponent } from './select.component';
+
+describe('[Instance][search][components][criteria][search-type] SelectComponent', () => {
+    let component: SelectComponent;
+    let fixture: ComponentFixture<SelectComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [SelectComponent],
+            imports: [
+                ReactiveFormsModule,
+                NgSelectModule
+            ]
+        });
+        fixture = TestBed.createComponent(SelectComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set select value and call operatorOnChange when criterion param  defined', () => {
+        let spy = jest.spyOn(component, 'operatorOnChange');
+        expect(component.form.controls.select.value).toEqual('');
+        component.setCriterion({ id: 1, value: 'test', type: 'test' } as FieldCriterion);
+        expect(component.form.controls.select.value).toEqual('test');
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+    it('setCriterion(criterion: Criterion) should set operator value to test value when criterion param is not defined', () => {
+        expect(component.form.controls.operator.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.operator.value).toEqual('test');
+    });
+    it('getCriterion() should return a criterion with type list', () => {
+        component.form.controls.select.setValue('test1');
+        expect(component.getCriterion().type).toEqual('field');
+    });
+    it('isValid() should true when form is valid or when operator value is nl or nnl', () => {
+        expect(component.isValid()).toBe(false);
+        component.form.controls.operator.setValue('test');
+        component.form.controls.select.setValue('test');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nnl');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nl');
+        expect(component.isValid()).toBe(true);
+    });
+    it('operatorOnChange() should disable select formcontrol when operator value is nl or nnl', () => {
+        expect(component.form.controls.select.disabled).toBe(false);
+        component.form.controls.operator.setValue('nl');
+        component.operatorOnChange();
+        expect(component.form.controls.select.disabled).toBe(true);
+    });
+    it('operatorOnChange() should enable select formcontrol when operator value is not  nl or nnl', () => {
+        component.form.controls.select.disable();
+        expect(component.form.controls.select.enabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.select.enabled).toBe(true);
+
+    });
+});
diff --git a/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fdd5112ae91d3f1a359f59c090f088faa9b77169
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/svom-json-kw.component.spec.ts
@@ -0,0 +1,121 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { HttpClient, HttpClientModule } from '@angular/common/http';
+import { ChangeDetectorRef } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { of } from 'rxjs';
+import { AppConfigService } from 'src/app/app-config.service';
+import { FieldCriterion, SvomKeyword } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { SvomJsonKwComponent } from './svom-json-kw.component';
+class MockHttpClient extends HttpClient {
+    get = jest.fn().mockImplementation(() => of([{ search_kw: { data_type: 'test', default: '', extension: 'test', name: '' } }]))
+}
+
+describe('[Instance][search][components][criteria][search-type] SvomJsonKwComponent', () => {
+    let component: SvomJsonKwComponent;
+    let fixture: ComponentFixture<SvomJsonKwComponent>;
+    let attribute: Attribute;
+    let httpClient: MockHttpClient = new MockHttpClient(null);
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [SvomJsonKwComponent],
+            imports: [
+                ReactiveFormsModule,
+                HttpClientModule,
+                NgSelectModule
+
+            ],
+            providers: [
+                { provide: AppConfigService, useValue: { authenticationEnabled: false } },
+                ChangeDetectorRef,
+                { provide: HttpClient, useValue: httpClient }
+            ]
+        });
+        fixture = TestBed.createComponent(SvomJsonKwComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion)  should set label value to js when criterion type is json', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion({ id: 1, type: 'json' });
+        expect(component.form.controls.label.value).toEqual('js');
+    });
+    it('setCriterion(criterion: Criterion)  should set label value to js when criterion type is json', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion({ id: 1, type: 'json' });
+        expect(component.form.controls.label.value).toEqual('js');
+    });
+    it('setCriterion(criterion: Criterion)  should set label value to test and operator value to ""  when criterion type is not  json', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.form.controls.operator.setValue('test');
+        component.setCriterion({ id: 1, type: 'test', operator: 'test' } as FieldCriterion);
+        expect(component.form.controls.label.value).toEqual('test');
+        expect(component.form.controls.operator.value).toEqual('');
+    });
+    it('setCriterion(criterion: Criterion)  should set label value to js when criterion param is type is not defined', () => {
+        expect(component.form.controls.label.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.label.value).toEqual('js');
+    });
+    it('setCriteriaList(criteriaList: Criterion[]) should ', () => {
+        let spyOnHttp = jest.spyOn(httpClient, 'get');
+        component.setCriteriaList([{ id: 3, type: 'test' }]);
+        expect(spyOnHttp).toHaveBeenCalledTimes(1);
+    });
+    it('setCriteriaList(criteriaList: Criterion[]) should ', () => {
+        component.svomKeywords = [{ data_type: 'test', default: 'test', extension: 'test', name: 'test' }]
+        component.setCriteriaList([{ id: 2, type: 'test' }]);
+        expect(component.svomKeywords.length).toEqual(0);
+    });
+    it('getCriterion() should return a criterion of type FieldCriterion', () => {
+        expect(component.getCriterion().type).toEqual('json');
+    });
+    it('getKeywordValue() should return test_extension,test_name', () => {
+        let svomKeyword: SvomKeyword = { data_type: 'test', default: 'test', name: 'test_name', extension: 'test_extension' }
+        expect(component.getKeywordValue(svomKeyword)).toEqual('test_extension,test_name');
+    });
+    it('labelOnChange() should disable operator, path, value when label value is nl or nnl', () => {
+        component.form.controls.label.setValue('nl');
+        expect(component.form.controls.operator.enabled).toBe(true);
+        expect(component.form.controls.path.enabled).toBe(true);
+        expect(component.form.controls.value.enabled).toBe(true);
+        component.labelOnChange();
+        expect(component.form.controls.operator.enabled).toBe(false);
+        expect(component.form.controls.path.enabled).toBe(false);
+        expect(component.form.controls.value.enabled).toBe(false);
+
+    });
+    it('labelOnChange() should enable operator, path, value when label value is not  nl or nnl', () => {
+        component.form.controls.operator.disable()
+        component.form.controls.path.disable()
+        component.form.controls.value.disable()
+        component.labelOnChange();
+        expect(component.form.controls.operator.enabled).toBe(true);
+        expect(component.form.controls.path.enabled).toBe(true);
+        expect(component.form.controls.value.enabled).toBe(true);
+
+    });
+
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/criteria/search-type/time.component.spec.ts b/client/src/app/instance/search/components/criteria/search-type/time.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0a6daa04847caec392c1f860c055c797e1f2086a
--- /dev/null
+++ b/client/src/app/instance/search/components/criteria/search-type/time.component.spec.ts
@@ -0,0 +1,94 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { NgSelectModule } from '@ng-select/ng-select';
+import { FieldCriterion } from 'src/app/instance/store/models';
+import { Attribute } from 'src/app/metamodel/models';
+import { TimeComponent } from './time.component';
+
+describe('[Instance][search][components][criteria][search-type] TimeComponent', () => {
+    let component: TimeComponent;
+    let fixture: ComponentFixture<TimeComponent>;
+    let attribute: Attribute;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [TimeComponent],
+            imports: [
+                ReactiveFormsModule,
+                NgSelectModule
+            ],
+        });
+        fixture = TestBed.createComponent(TimeComponent);
+        component = fixture.componentInstance;
+        component.attribute = {
+            ...attribute, options: [
+                { label: 'test1', display: 1, value: 'test1' },
+                { label: 'test2', display: 2, value: 'test2' }
+            ],
+            operator: 'test',
+            placeholder_min: 'min'
+        }
+        fixture.detectChanges();
+
+    });
+    it('should create the component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('setCriterion(criterion: Criterion) should set select value and call operatorOnChange when criterion param is defined', () => {
+        let spy = jest.spyOn(component, 'operatorOnChange');
+        expect(component.form.controls.hh.value).toEqual('');
+        expect(component.form.controls.mm.value).toEqual('');
+
+        component.setCriterion({ id: 1, value: '12:30', type: 'test' } as FieldCriterion);
+        expect(component.form.controls.hh.value).toEqual('12');
+        expect(component.form.controls.mm.value).toEqual('30');
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+    it('setCriterion(criterion: Criterion) should set operator value to test value when criterion param is not defined', () => {
+        expect(component.form.controls.operator.value).toEqual('');
+        component.setCriterion(null);
+        expect(component.form.controls.operator.value).toEqual('test');
+    });
+    it('getCriterion() should return a criterion of type FieldCriterion', () => {
+        expect(component.getCriterion().type).toEqual('field');
+    });
+    it('isValid() should true when form is valid or when operator value is nl or nnl', () => {
+        expect(component.isValid()).toBe(false);
+        component.form.controls.operator.setValue('test');
+        component.form.controls.hh.setValue('10');
+        component.form.controls.mm.setValue('10');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nnl');
+        expect(component.isValid()).toBe(true);
+        component.form.controls.operator.setValue('nl');
+        expect(component.isValid()).toBe(true);
+    });
+    it('operatorOnChange() should disable  hh, mm formcontrol when operator value is nl or nnl', () => {
+        component.form.controls.operator.setValue('nl');
+        expect(component.form.controls.hh.disabled).toBe(false);
+        expect(component.form.controls.hh.disabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.hh.disabled).toBe(true);
+        expect(component.form.controls.hh.disabled).toBe(true);
+
+    });
+    it('operatorOnChange() should enable  hh, mm formcontrol when operator value is not  nl or nnl', () => {
+        component.form.controls.hh.disable();
+        component.form.controls.mm.disable();
+        expect(component.form.controls.hh.enabled).toBe(false);
+        expect(component.form.controls.mm.enabled).toBe(false);
+        component.operatorOnChange();
+        expect(component.form.controls.hh.enabled).toBe(true);
+        expect(component.form.controls.mm.enabled).toBe(true);
+
+    });
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/dataset/dataset-card.component.spec.ts b/client/src/app/instance/search/components/dataset/dataset-card.component.spec.ts
index 2fa5236ee9f3b00283bf9ccb26e0dcff765955ed..7e5e1fb4dbd7833a7fd817ae9e11534e85488c0f 100644
--- a/client/src/app/instance/search/components/dataset/dataset-card.component.spec.ts
+++ b/client/src/app/instance/search/components/dataset/dataset-card.component.spec.ts
@@ -13,17 +13,18 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 import { PopoverModule } from 'ngx-bootstrap/popover';
 
 import { DatasetCardComponent } from './dataset-card.component';
+import { Dataset } from 'src/app/metamodel/models';
 
 describe('[Instance][Search][Component][Dataset] DatasetCardComponent', () => {
     let component: DatasetCardComponent;
     let fixture: ComponentFixture<DatasetCardComponent>;
     let router: Router;
-
+    let dataset: Dataset;
     beforeEach(() => {
         TestBed.configureTestingModule({
             declarations: [DatasetCardComponent],
             imports: [PopoverModule.forRoot()],
-            providers: [{ provide: Router, useValue: { navigate: jest.fn() }}]
+            providers: [{ provide: Router, useValue: { navigate: jest.fn() } }]
         });
         fixture = TestBed.createComponent(DatasetCardComponent);
         component = fixture.componentInstance;
@@ -41,4 +42,32 @@ describe('[Instance][Search][Component][Dataset] DatasetCardComponent', () => {
         expect(spy).toHaveBeenCalledTimes(1);
         expect(spy).toHaveBeenCalledWith(["/instance/myInstance/search/criteria/myDataset"]);
     });
+    it('should return false when authentication is enabled  and dataset is not public and user is not admin', () => {
+        component.authenticationEnabled = true;
+        component.dataset = { ...dataset, public: false };
+        component.isAdmin = jest.fn().mockImplementation(() => false);
+        expect(component.isDatasetAccessible()).toBe(false);
+    })
+    it('should return true when authentication is Enabled, dataset is not public and user is not admin and is autheticated', () => {
+        component.authenticationEnabled = true;
+        component.isAuthenticated = true;
+        component.dataset = { ...dataset, name: 'name_test' };
+        component.userRoles = ['role_test'];
+        component.datasetGroupList = [
+            {
+                datasets: ['name_test'],
+                id: 1,
+                instance_name: '',
+                role: 'role_test'
+            }
+        ]
+        component.isAdmin = jest.fn().mockImplementation(() => false);
+        expect(component.isDatasetAccessible()).toBe(true);
+    });
+    it('should return true', () => {
+
+        component.userRoles = ['test'];
+        component.adminRoles = ['test'];
+        expect(component.isAdmin()).toBe(true);
+    })
 });
diff --git a/client/src/app/instance/search/components/dataset/dataset-tabs.component.spec.ts b/client/src/app/instance/search/components/dataset/dataset-tabs.component.spec.ts
index 005b0c8ef6c8dd8c55a83cf3c491e665c3d65f59..39bdada6fa95493775a51cfe04319bd88891a253 100644
--- a/client/src/app/instance/search/components/dataset/dataset-tabs.component.spec.ts
+++ b/client/src/app/instance/search/components/dataset/dataset-tabs.component.spec.ts
@@ -32,7 +32,7 @@ describe('[Instance][Search][Component][Dataset] DatasetTabsComponent', () => {
 
     let component: DatasetTabsComponent;
     let fixture: ComponentFixture<DatasetTabsComponent>;
-
+    let dataset: Dataset;
     beforeEach(() => {
         TestBed.configureTestingModule({
             declarations: [
@@ -52,4 +52,34 @@ describe('[Instance][Search][Component][Dataset] DatasetTabsComponent', () => {
     it('should create the component', () => {
         expect(component).toBeTruthy();
     });
+    it('getDatasetFamilyList() should return an array with one datasetFamily with id 2', () => {
+        let dataset: Dataset;
+        component.datasetFamilyList = [
+            { display: 5, id: 1, label: 'test1', opened: true },
+            { display: 10, id: 2, label: 'test2', opened: false }
+        ]
+        component.datasetList = [{ ...dataset, id_dataset_family: 2 }];
+        let result = component.getDatasetFamilyList();
+        expect(result.length).toEqual(1);
+        expect(result[0].id).toEqual(2);
+
+    });
+    it('getDatasetListByDatasetFamily(datasetFamily: DatasetFamily) should return an array with two  dataset with', () => {
+
+        component.datasetList = [{ ...dataset, id_dataset_family: 1 }, { ...dataset, id_dataset_family: 2 }, { ...dataset, id_dataset_family: 2 }];
+        let result = component.getDatasetListByDatasetFamily({ display: 10, id: 2, label: 'test', opened: false });
+        expect(result.length).toEqual(2);
+    });
+    it('should return true when datasetFamily opened property is set to true', () => {
+        expect(component.checkIfDasetFamillyShouldBeOpened({ display: 10, id: 1, label: 'test', opened: true })).toBe(true);
+    });
+    it('should return true when the dataset is selected', () => {
+        component.datasetList = [
+            { ...dataset, id_dataset_family: 1, name: 'test' },
+            { ...dataset, id_dataset_family: 2 },
+            { ...dataset, id_dataset_family: 2 }
+        ];
+        component.datasetSelected = 'test';
+        expect(component.checkIfDasetFamillyShouldBeOpened({ display: 10, id: 1, label: 'test', opened: false })).toBe(true);
+    });
 });
diff --git a/client/src/app/instance/search/components/index.spec.ts b/client/src/app/instance/search/components/index.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8fadad533079595d17c63eb4d5317b0a8faef3e9
--- /dev/null
+++ b/client/src/app/instance/search/components/index.spec.ts
@@ -0,0 +1,16 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { dummiesComponents } from "./index"
+
+describe('[instance][search][components] index', () => {
+    it('test index', () => {
+        expect(dummiesComponents.length).toEqual(8);
+    })
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/output/output-tabs.component.spec.ts b/client/src/app/instance/search/components/output/output-tabs.component.spec.ts
index ba33a0759e0d6e5677af2e2938b35de891f4ed90..c13a7d7c5f2c377f6636247f820c716728793cc0 100644
--- a/client/src/app/instance/search/components/output/output-tabs.component.spec.ts
+++ b/client/src/app/instance/search/components/output/output-tabs.component.spec.ts
@@ -47,4 +47,26 @@ describe('[Instance][Search][Component][Output] OutputTabsComponent', () => {
     it('should create the component', () => {
         expect(component).toBeTruthy();
     });
+    it('getOutputFamilyList() should return an array with one outputFamilyList with id 2', () => {
+        let outputFamily: OutputFamily;
+        component.outputFamilyList = [
+            { display: 5, id: 1, label: 'test1', opened: true },
+            { display: 10, id: 2, label: 'test2', opened: false }
+        ]
+        component.outputCategoryList = [{ display: 5, id: 2, id_output_family: 2, label: 'test1' }];
+        let result = component.getOutputFamilyList();
+        expect(result.length).toEqual(1);
+        expect(result[0].id).toEqual(2);
+
+    });
+    it('should return an array with two  category', () => {
+
+        component.outputCategoryList = [
+            { display: 5, id: 2, id_output_family: 2, label: 'test1' },
+            { display: 10, id: 1, id_output_family: 1, label: 'test2' },
+            { display: 15, id: 3, id_output_family: 2, label: 'test3' }
+        ];
+        let result = component.getOutputCategoryListByFamily({ display: 10, id: 2, label: 'test', opened: false });
+        expect(result.length).toEqual(2);
+    });
 });
diff --git a/client/src/app/instance/search/components/result/abstract-download.component.spec.ts b/client/src/app/instance/search/components/result/abstract-download.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e84e9a14d389fa688a9b6b0110b2cb359508dd5f
--- /dev/null
+++ b/client/src/app/instance/search/components/result/abstract-download.component.spec.ts
@@ -0,0 +1,94 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { TestBed } from "@angular/core/testing";
+import { AppConfigService } from "src/app/app-config.service";
+import { Dataset } from "src/app/metamodel/models";
+import { AbstractDownloadComponent } from "./abstract-download.component";
+class TestAbstractDownloadComponent extends AbstractDownloadComponent {
+
+}
+describe('[instance][search][components][result] AbstractDownloadComponent', () => {
+    let component: TestAbstractDownloadComponent;
+    let dataset: Dataset;
+    TestBed.configureTestingModule({
+        declarations: [
+            TestAbstractDownloadComponent
+        ],
+    });
+    beforeEach(() => {
+        let appService: AppConfigService = {
+            apiUrl: "http://test.fr",
+            servicesUrl: "",
+            baseHref: "",
+            authenticationEnabled: false,
+            ssoAuthUrl: "",
+            ssoRealm: "",
+            ssoClientId: "",
+            adminRoles: [],
+            matomoEnabled: false,
+            matomoSiteId: 0,
+            matomoTrackerUrl: ""
+        }
+        component = new TestAbstractDownloadComponent(appService);
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getUrl(format: string, selectedData: string = null) should return http://test.fr/search/test', () => {
+        component.getQuery = jest.fn().mockImplementationOnce(() => 'test');
+        expect(component.getUrl('format_test')).toEqual('http://test.fr/search/test&f=format_test');
+    });
+    it('should return test_name?a=1;2&c=1;3;test when criterialist as at least one element', () => {
+        component.outputList = [1, 2]
+        component.criteriaList = [
+            { id: 1, type: 'test1' },
+            { id: 3, type: 'test2' }
+        ];
+        component.dataset = { ...dataset, name: 'test_name' }
+        expect(component.getQuery('test')).toEqual('test_name?a=1;2&c=1;3;test');
+    });
+    it('should return test_name?a=1;2&c=1;3;test when criteria list as 0 element ', () => {
+        component.outputList = [1, 2]
+        component.criteriaList = [];
+        component.dataset = { ...dataset, name: 'test_name' }
+        expect(component.getQuery('test')).toEqual('test_name?a=1;2&c=test');
+    });
+
+    it('should return test_name?a=1;2&c=1;3;test&cs=3:5:1 when criteria list as 0 element and there is a conesearch', () => {
+        component.outputList = [1, 2]
+        component.criteriaList = [];
+        component.dataset = { ...dataset, name: 'test_name' }
+        component.coneSearch = {
+            dec: 5,
+            ra: 3,
+            radius: 1
+        }
+        expect(component.getQuery('test')).toEqual('test_name?a=1;2&c=test&cs=3:5:1');
+    });
+    it('should raises download file event', () => {
+        let spy = jest.spyOn(component.downloadFile, 'emit');
+        component.dataset = { ...dataset, name: 'test_name' }
+        const e = { preventDefault: jest.fn() };
+        component.download(e, 'test.fr', '')
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+    it('should return json', () => {
+        expect(component.formatToExtension('json')).toEqual('json');
+    });
+    it('should return csv', () => {
+        expect(component.formatToExtension('csv')).toEqual('csv');
+    });
+    it('should return txt', () => {
+        expect(component.formatToExtension('ascii')).toEqual('txt');
+    });
+    it('should return xml', () => {
+        expect(component.formatToExtension('votable')).toEqual('xml');
+    });
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/cone-search-image.component.spec.ts b/client/src/app/instance/search/components/result/cone-search-image.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..30b4149cb45e7a7493d7da317433581a62e0b698
--- /dev/null
+++ b/client/src/app/instance/search/components/result/cone-search-image.component.spec.ts
@@ -0,0 +1,56 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing"
+import { Attribute, ConeSearchConfig } from "src/app/metamodel/models";
+import { ConeSearchImageComponent } from "./cone-search-image.component";
+
+describe('[instance][search][components][result] ConeSearchImageComponent', () => {
+    let component: ConeSearchImageComponent;
+    let fixture: ComponentFixture<ConeSearchImageComponent>;
+    let attribute: Attribute;
+    let coneSearchConfig: ConeSearchConfig;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                ConeSearchImageComponent
+            ]
+        })
+        fixture = TestBed.createComponent(ConeSearchImageComponent);
+        component = fixture.componentInstance;
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+
+    it('getData should return a array with the objet id = 1, x = ra_label, y = dec_label', () => {
+        component.coneSearchConfig = { ...coneSearchConfig, column_ra: 5, column_dec: 3 };
+        component.attributeList = [
+            { ...attribute, primary_key: true, label: 'id_label' },
+            { ...attribute, id: 5, label: 'ra_label' },
+            { ...attribute, id: 3, label: 'dec_label' }
+        ]
+        component.data = [
+            { id_label: 1, ra_label: 1, dec_label: 3 },
+
+        ]
+        let expected = [{ "id": 1, "x": 1, "y": 3 }];
+
+        expect(component.getData()).toEqual(expected);
+    });
+    it('should raises closeConeSearchPlotImageOutPut and selectId events', () => {
+        let spyOncloseConeSearchPlotImageOutPut = jest.spyOn(component.closeConeSearchPlotImageOutPut, 'emit');
+        let spyOnSelectId = jest.spyOn(component.selectId, 'emit');
+        component.closeConeSearchPlotImage();
+        expect(spyOncloseConeSearchPlotImageOutPut).toHaveBeenCalledWith(false);
+        expect(spyOnSelectId).toHaveBeenCalledWith(null);
+
+    });
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/cone-search-plot.component.spec.ts b/client/src/app/instance/search/components/result/cone-search-plot.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e99b903f7025f2109a4cdd57da0f1cbdd1c51600
--- /dev/null
+++ b/client/src/app/instance/search/components/result/cone-search-plot.component.spec.ts
@@ -0,0 +1,28 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { ConeSearchPlotComponent } from "./cone-search-plot.component";
+
+describe('[instance][search][components][result] ConeSearchPlotComponent', () => {
+    let component: ConeSearchPlotComponent;
+    let fixture: ComponentFixture<ConeSearchPlotComponent>;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [ConeSearchPlotComponent],
+        });
+        fixture = TestBed.createComponent(ConeSearchPlotComponent);
+        component = fixture.componentInstance;
+        component.coneSearchPlot = jest.fn().mockImplementationOnce(() => { })
+        fixture.detectChanges();
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/datatable-actions.component.spec.ts b/client/src/app/instance/search/components/result/datatable-actions.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c4ab8a6c6700e214e1e1272e8e913482150db675
--- /dev/null
+++ b/client/src/app/instance/search/components/result/datatable-actions.component.spec.ts
@@ -0,0 +1,63 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { AppConfigService } from "src/app/app-config.service";
+import { Attribute } from "src/app/metamodel/models";
+import { DatatableActionsComponent } from "./datatable-actions.component"
+
+describe('[instance][search][components][result] DatatableActionsComponent', () => {
+    let component: DatatableActionsComponent;
+    let fixture: ComponentFixture<DatatableActionsComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                DatatableActionsComponent
+            ],
+            providers: [
+                AppConfigService
+            ]
+        });
+        fixture = TestBed.createComponent(DatatableActionsComponent);
+        component = fixture.componentInstance;
+        component.attributeList = [
+            { ...attribute, id: 1, archive: true, primary_key: true }
+        ]
+
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('isArchiveIsAvailable should return true', () => {
+
+        component.outputList = [1];
+        expect(component.isArchiveIsAvailable()).toBe(true);
+    });
+    it('getDatatableUrl(format: string) should call getUrl', () => {
+        let spy = jest.spyOn(component, 'getUrl').mockImplementationOnce(() =>'');
+        component.getDatatableUrl('');
+        expect(spy).toHaveBeenCalledTimes(1);
+    });
+    it('should raises broadcastvotable event', () => {
+        let spy = jest.spyOn(component.broadcastVotable, 'emit');
+        component.getDatatableUrl = jest.fn().mockImplementationOnce(() => 'test.fr')
+        component.broadcastResult();
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith('test.fr');
+    });
+    it('should raises startTaskCreateArchive event', () => {
+        let spy = jest.spyOn(component.startTaskCreateArchive, 'emit');
+        component.getQuery = jest.fn().mockImplementationOnce(() => 'test/test')
+        component.downloadArchive();
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith('test/test');
+    });
+
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/datatable.component.spec.ts b/client/src/app/instance/search/components/result/datatable.component.spec.ts
index cae274206a1409c6f36d03bfb8d44726c581ca2d..fac890eb78ad78f051fc0f8d0a665405ef8306a1 100644
--- a/client/src/app/instance/search/components/result/datatable.component.spec.ts
+++ b/client/src/app/instance/search/components/result/datatable.component.spec.ts
@@ -111,7 +111,7 @@ describe('[Instance][Search][Component][Result] DatatableComponent', () => {
             order: PaginationOrder.a
         }
         component.ngOnInit();
-        Promise.resolve(null).then(function() {
+        Promise.resolve(null).then(function () {
             expect(component.sortedCol).toEqual(1);
             expect(spy).toHaveBeenCalledTimes(1);
             expect(spy).toHaveBeenCalledWith(expectedPagination);
@@ -301,7 +301,7 @@ describe('[Instance][Search][Component][Result] DatatableComponent', () => {
         };
         component.sort(1);
         expect(spy).toHaveBeenCalledTimes(2);
-        expect(spy).toHaveBeenLastCalledWith(expectedPagination);
+        expect(spy).toHaveBeenCalledWith(expectedPagination);
         component.sortedCol = 1;
         component.sortedOrder = PaginationOrder.d;
         expectedPagination = {
@@ -313,6 +313,16 @@ describe('[Instance][Search][Component][Result] DatatableComponent', () => {
         };
         component.sort(1);
         expect(spy).toHaveBeenCalledTimes(3);
-        expect(spy).toHaveBeenLastCalledWith(expectedPagination);
+        expect(spy).toHaveBeenCalledWith(expectedPagination);
     });
+    it('datumSelectedInPlot should return true', () => {
+        let attribute: Attribute;
+        component.selectId = 1;
+        component.attributeList = [
+            { ...attribute, primary_key: true, label: 'id_test' }
+        ];
+        let datum = { id_test: 1 };
+        expect(component.datumSelectedInPlot(datum)).toBe(true);
+
+    })
 });
diff --git a/client/src/app/instance/search/components/result/download-result.component.spec.ts b/client/src/app/instance/search/components/result/download-result.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..f6bd071f16075a7754eab212a488894ae28f08dc
--- /dev/null
+++ b/client/src/app/instance/search/components/result/download-result.component.spec.ts
@@ -0,0 +1,67 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { AppConfigService } from "src/app/app-config.service";
+import { Attribute, Dataset } from "src/app/metamodel/models";
+import { DownloadResultComponent } from "./download-result.component"
+
+describe('[Instance][Search][Component][Result] DownloadResultComponent', () => {
+    let component: DownloadResultComponent;
+    let fixture: ComponentFixture<DownloadResultComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                DownloadResultComponent
+            ],
+            providers: [
+                AppConfigService
+            ]
+        });
+        fixture = TestBed.createComponent(DownloadResultComponent);
+        component = fixture.componentInstance;
+        component.attributeList = [
+            { ...attribute, id: 1, archive: true, primary_key: true }
+        ]
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('isArchiveIsAvailable should return true', () => {
+        component.outputList = [1];
+        expect(component.isArchiveIsAvailable()).toBe(true);
+    });
+    it('should raises broadcastvotable event', () => {
+        let spy = jest.spyOn(component.broadcastVotable, 'emit');
+        component.getUrl = jest.fn().mockImplementationOnce(() => 'test.fr')
+        component.broadcastResult();
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith('test.fr');
+    });
+    it('should raises startTaskCreateArchive event', () => {
+        let spy = jest.spyOn(component.startTaskCreateArchive, 'emit');
+        component.getQuery = jest.fn().mockImplementationOnce(() => 'test/test')
+        component.downloadArchive();
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith('test/test');
+    });
+    it('should return true', () => {
+        let dataset: Dataset;
+        component.dataset = { ...dataset, download_json: true };
+        expect(component.isDownloadEnabled()).toBe(true);
+        component.dataset = { ...dataset, download_csv: true };
+        expect(component.isDownloadEnabled()).toBe(true);
+        component.dataset = { ...dataset, download_ascii: true };
+        expect(component.isDownloadEnabled()).toBe(true);
+        component.dataset = { ...dataset, download_vo: true };
+        expect(component.isDownloadEnabled()).toBe(true);
+    });
+
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/image-list-result.component.spec.ts b/client/src/app/instance/search/components/result/image-list-result.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ead9b5a5ba0784c91dcdad6dc87fae1f5308ba96
--- /dev/null
+++ b/client/src/app/instance/search/components/result/image-list-result.component.spec.ts
@@ -0,0 +1,90 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { AppConfigService } from "src/app/app-config.service";
+import { Attribute, ConeSearchConfig, Dataset, Image } from "src/app/metamodel/models";
+import { ImageListResultComponent } from "./image-list-result.component";
+
+describe('[Instance][Search][Component][Result] ImageListResultComponent', () => {
+    let component: ImageListResultComponent;
+    let fixture: ComponentFixture<ImageListResultComponent>;
+    let attribute: Attribute;
+    let dataset: Dataset;
+    let image: Image;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                ImageListResultComponent
+            ],
+            providers: [
+                { provide: AppConfigService, useValue: { servicesUrl: 'test' } }
+            ]
+        });
+        fixture = TestBed.createComponent(ImageListResultComponent);
+        component = fixture.componentInstance;
+        component.attributeList = [
+            { ...attribute, id: 1, archive: true, primary_key: true }
+        ]
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getHref() should return the href', () => {
+        let expected = 'test/fits-cut-to-png/test_dataset_name?filename=test_image_file_path&ra=5&dec=10&radius=3&stretch=test&pmin=5&pmax=10&axes=false';
+        component.dataset = { ...dataset, name: 'test_dataset_name' };
+        component.coneSearch = { dec: 10, ra: 5, radius: 3 }
+        expect(component.getHref({ ...image, file_path: 'test_image_file_path', stretch: 'test', pmax: 10, pmin: 5 })).toEqual(expected);
+
+    });
+    it('getFitsCutUrl() should return test/fits-cut/test?filename=test&ra=5&dec=10&radius=3', () => {
+        let expected = 'test/fits-cut/test?filename=test&ra=5&dec=10&radius=3';
+        component.dataset = { ...dataset, name: 'test' };
+        component.coneSearch = { dec: 10, ra: 5, radius: 3 }
+        expect(component.getFitsCutUrl({ ...image, file_path: 'test', stretch: 'test', pmax: 10, pmin: 5 })).toEqual(expected);
+    });
+    it('should raises download file event', () => {
+        let event = { preventDefault: jest.fn() };
+        component.getFitsCutUrl = jest.fn().mockImplementationOnce(() => 'test');
+        let spy = jest.spyOn(component.downloadFile, 'emit');
+        component.saveFitsCutFile(event, { ...image, file_path: 'test/test' });
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith({ url: 'test', filename: 'test' })
+    });
+    it('should raises download file event', () => {
+
+        component.getFitsCutUrl = jest.fn().mockImplementationOnce(() => 'test');
+        let spy = jest.spyOn(component.broadcastImage, 'emit');
+        component.broadcast({ ...image, file_path: 'test/test' });
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith('test');
+    });
+    it('getData() shoud return [{ "x": 1, "y": 2 }]', () => {
+        component.attributeList = [
+            { ...attribute, id: 1, archive: true, primary_key: true, label: 'test1' },
+            { ...attribute, id: 2, archive: true, primary_key: true, label: 'test2' }
+        ]
+        let coneSearchConfig: ConeSearchConfig;
+        component.coneSearchConfig = { ...coneSearchConfig, column_ra: 1, column_dec: 2 };
+        component.data = [
+            { test1: 1, test2: 2 }
+        ]
+        let expected = [{ "x": 1, "y": 2 }];
+        expect(component.getData()).toEqual(expected);
+
+    });
+    it('should raises emitBackGroundHref and openPlotImage events', () => {
+        let spyOnemitBackGroundHref = jest.spyOn(component.emitBackGroundHref, 'emit');
+        let spyOnopenPlotImage = jest.spyOn(component.openPlotImage, 'emit');
+        component.openConeSearch('test');
+        expect(spyOnemitBackGroundHref).toHaveBeenCalledWith('test');
+        expect(spyOnopenPlotImage).toHaveBeenCalledWith(true);
+    })
+
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/renderer/detail-link-renderer.component.spec.ts b/client/src/app/instance/search/components/result/renderer/detail-link-renderer.component.spec.ts
index c33e903fd29b7379fff1ffe2dd902515d0c7c166..33be4e4d916765f2c019c71cd3a40fb9ddec5606 100644
--- a/client/src/app/instance/search/components/result/renderer/detail-link-renderer.component.spec.ts
+++ b/client/src/app/instance/search/components/result/renderer/detail-link-renderer.component.spec.ts
@@ -11,6 +11,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
 
 import { DetailLinkRendererComponent } from './detail-link-renderer.component';
 import { RouterTestingModule } from '@angular/router/testing';
+import { Attribute, DetailLinkRendererConfig } from 'src/app/metamodel/models';
 
 describe('[Instance][Search][Component][Result][Renderer] DetailRendererComponent', () => {
     let component: DetailLinkRendererComponent;
@@ -28,4 +29,15 @@ describe('[Instance][Search][Component][Result][Renderer] DetailRendererComponen
     it('should create the component', () => {
         expect(component).toBeTruthy();
     });
+    it('getConfig() should  return the attribute render_config property', () => {
+
+        let detailLinkRendererConfig: DetailLinkRendererConfig = {
+            display: '',
+            component: '',
+            id: 'renderer-config'
+        }
+        let attribute: Attribute;
+        component.attribute = { ...attribute, renderer_config: { ...detailLinkRendererConfig } }
+        expect(component.getConfig()).toEqual(detailLinkRendererConfig);
+    });
 });
diff --git a/client/src/app/instance/search/components/result/renderer/index.spec.ts b/client/src/app/instance/search/components/result/renderer/index.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0cc1e248e6e062907f256121cb8ca5453e63f135
--- /dev/null
+++ b/client/src/app/instance/search/components/result/renderer/index.spec.ts
@@ -0,0 +1,19 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { getResultRendererComponent } from "."
+import { DetailLinkRendererComponent } from "./detail-link-renderer.component";
+
+describe('[Instance][Search][Component][Result][Renderer] index', () => {
+
+    it('should return DetailLinkRendererComponent', () => {
+        let mockgetResultRendererComponent = getResultRendererComponent;
+        expect(mockgetResultRendererComponent(('detail-link'))).toEqual(DetailLinkRendererComponent)
+    })
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/components/result/result-info.component.spec.ts b/client/src/app/instance/search/components/result/result-info.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..14dd0845212a3464f484b66b4807bbd5beaefc31
--- /dev/null
+++ b/client/src/app/instance/search/components/result/result-info.component.spec.ts
@@ -0,0 +1,37 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { ResultInfoComponent } from "./result-info.component";
+
+describe('[Instance][Search][Component][Result] ResultInfoComponent', () => {
+    let component: ResultInfoComponent;
+    let fixture: ComponentFixture<ResultInfoComponent>;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                ResultInfoComponent
+            ],
+        });
+        fixture = TestBed.createComponent(ResultInfoComponent);
+        component = fixture.componentInstance;
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getCriteriaLength() should return 3', () => {
+        component.criteriaList = [
+            { id: 1, type: 'test1' },
+            { id: 1, type: 'test1' }
+        ];
+        component.coneSearch = { dec: 10, ra: 10, radius: 5 };
+        expect(component.getCriteriaLength()).toEqual(3);
+    });
+
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/containers/criteria.component.spec.ts b/client/src/app/instance/search/containers/criteria.component.spec.ts
index cbc3aeaed785b3a3259ac8ae33307453a4e3995c..c5bc7cd0d5a1e8da41ae7c2fd7817e8c068c5a90 100644
--- a/client/src/app/instance/search/containers/criteria.component.spec.ts
+++ b/client/src/app/instance/search/containers/criteria.component.spec.ts
@@ -73,7 +73,7 @@ describe('[Instance][Search][Container] CriteriaComponent', () => {
                 SummaryStubComponent,
                 SortByCriteriaDisplayPipe
             ],
-            providers: [provideMockStore({ })]
+            providers: [provideMockStore({})]
         });
         fixture = TestBed.createComponent(CriteriaComponent);
         component = fixture.componentInstance;
@@ -88,7 +88,7 @@ describe('[Instance][Search][Container] CriteriaComponent', () => {
         const spy = jest.spyOn(store, 'dispatch');
         jest.spyOn(AbstractSearchComponent.prototype, 'ngOnInit').mockReturnThis();
         component.ngOnInit();
-        Promise.resolve(null).then(function() {
+        Promise.resolve(null).then(function () {
             expect(spy).toHaveBeenCalledTimes(2);
             expect(spy).toHaveBeenCalledWith(searchActions.changeStep({ step: 'criteria' }));
             expect(spy).toHaveBeenCalledWith(searchActions.checkCriteria());
@@ -103,6 +103,13 @@ describe('[Instance][Search][Container] CriteriaComponent', () => {
         expect(spy).toHaveBeenCalledTimes(1);
         expect(spy).toHaveBeenCalledWith(searchActions.addCriterion({ criterion }));
     });
+    it('#updateCriterion() should dispatch updateCriterion action', () => {
+        const updatedCriterion: Criterion = { id: 1, type: 'field' };
+        const spy = jest.spyOn(store, 'dispatch');
+        component.updateCriterion(updatedCriterion);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(searchActions.updateCriterion({ updatedCriterion }));
+    });
 
     it('#deleteCriterion() should dispatch deleteCriterion action', () => {
         const spy = jest.spyOn(store, 'dispatch');
@@ -111,7 +118,14 @@ describe('[Instance][Search][Container] CriteriaComponent', () => {
         expect(spy).toHaveBeenCalledWith(searchActions.deleteCriterion({ idCriterion: 1 }));
     });
 
-    it('#addConeSearch() should dispatch addConeSearch action', () => {
+    it('#addConeSearch() should dispatch updateConeSearch action', () => {
+        const coneSearch: ConeSearch = { ra: 1, dec: 2, radius: 3 };
+        const spy = jest.spyOn(store, 'dispatch');
+        component.updateConeSearch(coneSearch);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(coneSearchActions.updateConeSearch({ coneSearch }));
+    });
+    it('#updateConeSearch() should dispatch addConeSearch action', () => {
         const coneSearch: ConeSearch = { ra: 1, dec: 2, radius: 3 };
         const spy = jest.spyOn(store, 'dispatch');
         component.addConeSearch(coneSearch);
diff --git a/client/src/app/instance/search/containers/dataset.component.spec.ts b/client/src/app/instance/search/containers/dataset.component.spec.ts
index 68c0e8df07802abdff23d21a0a84535ff8b98c27..c57c55aa19ddc4d45d8ecc46308ed3fd16b04131 100644
--- a/client/src/app/instance/search/containers/dataset.component.spec.ts
+++ b/client/src/app/instance/search/containers/dataset.component.spec.ts
@@ -60,6 +60,8 @@ describe('[Instance][Search][Container] DatasetComponent', () => {
     let fixture: ComponentFixture<DatasetComponent>;
     let store: MockStore;
     let appConfigServiceStub = new AppConfigService();
+    appConfigServiceStub.authenticationEnabled = true;
+    appConfigServiceStub.adminRoles = ['test']
 
     beforeEach(waitForAsync(() => {
         TestBed.configureTestingModule({
@@ -72,7 +74,7 @@ describe('[Instance][Search][Container] DatasetComponent', () => {
                 SortByOutputDisplayPipe
             ],
             providers: [
-                provideMockStore({ }),
+                provideMockStore({}),
                 { provide: AppConfigService, useValue: appConfigServiceStub }
             ]
         });
@@ -90,10 +92,17 @@ describe('[Instance][Search][Container] DatasetComponent', () => {
         const spy = jest.spyOn(store, 'dispatch');
         jest.spyOn(AbstractSearchComponent.prototype, 'ngOnInit').mockReturnThis();
         component.ngOnInit();
-        Promise.resolve(null).then(function() {
+        Promise.resolve(null).then(function () {
             expect(spy).toHaveBeenCalledTimes(1);
             expect(spy).toHaveBeenCalledWith(searchActions.changeStep({ step: 'dataset' }));
             done();
         });
     });
+    it('getAuthenticationEnabled should return true', () => {
+
+        expect(component.getAuthenticationEnabled()).toBe(true);
+    })
+    it('getAdminRoles() an array with one element', () => {
+        expect(component.getAdminRoles().length).toEqual(1);;
+    })
 });
diff --git a/client/src/app/instance/search/containers/result.component.spec.ts b/client/src/app/instance/search/containers/result.component.spec.ts
index d89e8504922d10017a6534f256a22cc645be610f..597e1f2ccecefe758a4fb1493c5d630e2d4ad0b1 100644
--- a/client/src/app/instance/search/containers/result.component.spec.ts
+++ b/client/src/app/instance/search/containers/result.component.spec.ts
@@ -25,6 +25,7 @@ import * as searchActions from '../../store/actions/search.actions';
 import * as imageActions from 'src/app/metamodel/actions/image.actions';
 import * as coneSearchConfigActions from 'src/app/metamodel/actions/cone-search-config.actions';
 import { AbstractSearchComponent } from './abstract-search.component';
+import * as archiveActions from '../../store/actions/archive.actions';
 
 describe('[Instance][Search][Container] ResultComponent', () => {
     @Component({ selector: 'app-spinner', template: '' })
@@ -107,7 +108,7 @@ describe('[Instance][Search][Container] ResultComponent', () => {
                 SortByOutputDisplayPipe,
                 DatasetByNamePipe
             ],
-            providers: [provideMockStore({ })]
+            providers: [provideMockStore({})]
         });
         fixture = TestBed.createComponent(ResultComponent);
         component = fixture.componentInstance;
@@ -123,7 +124,7 @@ describe('[Instance][Search][Container] ResultComponent', () => {
         const spy = jest.spyOn(store, 'dispatch');
         jest.spyOn(AbstractSearchComponent.prototype, 'ngOnInit').mockReturnThis();
         component.ngOnInit();
-        Promise.resolve(null).then(function() {
+        Promise.resolve(null).then(function () {
             expect(spy).toHaveBeenCalledTimes(4);
             expect(spy).toHaveBeenCalledWith(searchActions.changeStep({ step: 'result' }));
             expect(spy).toHaveBeenCalledWith(searchActions.checkResult());
@@ -176,6 +177,42 @@ describe('[Instance][Search][Container] ResultComponent', () => {
         expect(spy).toHaveBeenCalledWith(searchActions.deleteSelectedData({ id: 1 }));
     });
 
+    it('#downloadFile() should  dispach download file action', () => {
+        let spy = jest.spyOn(store, 'dispatch');
+        component.downloadFile({ url: 'test.fr', filename: 'test' });
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(searchActions.downloadFile({ url: 'test.fr', filename: 'test' }));
+    });
+    it('#startTaskCreateArchive() should  dispatch startTaskCreateArchive action', () => {
+        let spy = jest.spyOn(store, 'dispatch');
+        let query = 'test';
+        component.startTaskCreateArchive(query);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(archiveActions.startTaskCreateArchive({ query }));
+    });
+    it('#updateOutputList() should  dispatch updateOutputList action', () => {
+        let spy = jest.spyOn(store, 'dispatch');
+        let outputList = []
+        component.updateOutputList(outputList);
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(searchActions.updateOutputList({ outputList }));
+    });
+    it('#updateBackgroundHref() should  set backgroundHref to test', () => {
+        expect(component.backgroundHref).not.toEqual('test');
+        component.updateBackgroundHref('test');
+        expect(component.backgroundHref).toEqual('test')
+    });
+    it('#updateOpenPlotImage() should  set openPlotImage to true', () => {
+        expect(component.openPlotImage).not.toEqual(true);
+        component.updateOpenPlotImage(true);
+        expect(component.openPlotImage).toEqual(true)
+    });
+    it('#updateSelectId() should  set selectId to 5', () => {
+        expect(component.selectId).not.toEqual(5);
+        component.updateSelectId(5);
+        expect(component.selectId).toEqual(5)
+    });
+
     it('#ngOnDestroy() should dispatch destroyResults action and unsubscribe from pristineSubscription', () => {
         component.pristineSubscription = of().subscribe();
         const unsubscribeSpy = jest.spyOn(component.pristineSubscription, 'unsubscribe');
diff --git a/client/src/app/instance/search/detail/components/detail-content.component.spec.ts b/client/src/app/instance/search/detail/components/detail-content.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..16e98239fcb73b2599ae496cce622c37f6cd0d05
--- /dev/null
+++ b/client/src/app/instance/search/detail/components/detail-content.component.spec.ts
@@ -0,0 +1,38 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { Dataset } from "src/app/metamodel/models";
+import { StyleService } from "src/app/shared/services/style.service";
+import { DetailContentComponent } from "./detail-content.component";
+
+describe('[Instance][Search][Component][Result] DetailContentComponent', () => {
+    let component: DetailContentComponent;
+    let fixture: ComponentFixture<DetailContentComponent>;
+    let dataset: Dataset;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                DetailContentComponent
+            ],
+            providers: [
+                { provide: StyleService, useValue: new StyleService() }
+            ]
+        });
+        fixture = TestBed.createComponent(DetailContentComponent);
+        component = fixture.componentInstance;
+        component.detailConfig = { content: '', id: 1, style_sheet: 'test' };
+        component.dataset = { ...dataset, name: 'test' };
+        fixture.detectChanges();
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/components/renderer/index.spec.ts b/client/src/app/instance/search/detail/components/renderer/index.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..8b46c75dc3d0799c6fec83ac5c4b87b8b2abcbe5
--- /dev/null
+++ b/client/src/app/instance/search/detail/components/renderer/index.spec.ts
@@ -0,0 +1,24 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import * as fromSharedRender from 'src/app/instance/search/shared-renderer/components';
+import { AbstractRendererComponent } from 'src/app/instance/search/shared-renderer/abstract-renderer.component';
+import { getDetailRendererComponent } from '.';
+
+class TestClass extends AbstractRendererComponent { }
+describe('[instance][search][detail][components][renderer] getDetailRendererComponent', () => {
+
+    it('should test getDetailRendererComponent', () => {
+        let spy = jest.spyOn(fromSharedRender, 'getRendererComponent');
+        spy.mockImplementation(() => TestClass);
+        expect(getDetailRendererComponent('test')).toEqual(TestClass);
+        expect(spy).toHaveBeenCalledTimes(1);
+
+    });
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/detail.module.spec.ts b/client/src/app/instance/search/detail/detail.module.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..685b5dddc7e716cfb46f0158a1f633579e39217e
--- /dev/null
+++ b/client/src/app/instance/search/detail/detail.module.spec.ts
@@ -0,0 +1,17 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { DetailModule } from './detail.module';
+
+describe('[Instance][Search][detail] module', () => {
+    it('Test detail module', () => {
+        expect(DetailModule.name).toEqual('DetailModule');
+    });
+});
+
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-fits-cut.component.spec.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-fits-cut.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c8efa0750d6229f1ea3620aae2ae560c4cda1faf
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-fits-cut.component.spec.ts
@@ -0,0 +1,75 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { AppConfigService } from "src/app/app-config.service";
+import { Attribute, Dataset, Image } from "src/app/metamodel/models";
+import { DisplayFitsCutComponent } from "./display-fits-cut.component"
+
+describe('[instance][search][detail][dynamic-content][dynamic-components] DisplayFitsCutComponent', () => {
+    let component: DisplayFitsCutComponent;
+    let fixture: ComponentFixture<DisplayFitsCutComponent>;
+    let dataset: Dataset;
+    let image: Image;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [DisplayFitsCutComponent],
+            providers: [
+                { provide: AppConfigService, useValue: { servicesUrl: 'test' } }
+            ]
+        });
+        fixture = TestBed.createComponent(DisplayFitsCutComponent);
+        component = fixture.componentInstance;
+
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('should return test/fits-cut-to-png/test?filename=test&ra=2&dec=1&radius=3&stretch=test&pmin=1&pmax=2&axes=false', () => {
+        component.dataset = { ...dataset, name: 'test' };
+
+        component.getImage = jest.fn().mockImplementationOnce(() => {
+            return { ...image, file_path: 'test', stretch: 'test', pmax: 2, pmin: 1 } as Image
+        });
+        component.getRaValue = jest.fn().mockImplementationOnce(() => 2);
+        component.getDecValue = jest.fn().mockImplementationOnce(() => 1);
+        component.radius = 3;
+        let expected = 'test/fits-cut-to-png/test?filename=test&ra=2&dec=1&radius=3&stretch=test&pmin=1&pmax=2&axes=false';
+        expect(component.getHref()).toEqual(expected);
+    });
+    it('should return an image with id 2', () => {
+        component.imageList = [
+            { ...image, id: 1 },
+            { ...image, id: 2 }
+        ]
+        component.imageId = 2;
+        expect(component.getImage().id).toEqual(2);
+    });
+    it('getImage() should return  10', () => {
+        component.attributeList = [
+            { ...attribute, id: 1, label: 'test_label_ra' },
+            { ...attribute, id: 2 }
+        ]
+        component.attributeRaId = 1;
+        component.object = { test_label_ra: 10 };
+        expect(component.getRaValue()).toEqual(10);
+    });
+    it('getDecValue() should return  5', () => {
+        component.attributeList = [
+            { ...attribute, id: 1, label: 'test_label_ra' },
+            { ...attribute, id: 2, label: 'test_label_dec' }
+        ]
+        component.attributeDecId = 2;
+        component.object = { test_label_dec: 5 };
+        expect(component.getDecValue()).toEqual(5);
+    });
+
+
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-image.component.spec.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-image.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..04cde93b5452f5c56ccf762ae8511cebf5aa3fc7
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-image.component.spec.ts
@@ -0,0 +1,85 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { AppConfigService } from "src/app/app-config.service";
+import { Attribute, Dataset, Image } from "src/app/metamodel/models";
+import { DisplayImageComponent } from "./display-image.component";
+import * as utils from 'src/app/shared/utils';
+describe('[instance][search][detail][dynamic-content][dynamic-components] DisplayImageComponent', () => {
+    let component: DisplayImageComponent;
+    let fixture: ComponentFixture<DisplayImageComponent>;
+    let dataset: Dataset;
+    let image: Image;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [DisplayImageComponent],
+            providers: [
+                { provide: AppConfigService, useValue: { servicesUrl: 'test', apiUrl: 'test' } }
+            ]
+        });
+        fixture = TestBed.createComponent(DisplayImageComponent);
+        component = fixture.componentInstance;
+
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getValue() should return test/fits-to-png/test?filename=test&stretch=linear&pmin=0.25&pmax=99.75&axes=true', () => {
+        let expected = 'test/fits-to-png/test?filename=test&stretch=linear&pmin=0.25&pmax=99.75&axes=true';
+        component.dataset = { ...dataset, name: 'test' };
+        component.getPath = jest.fn().mockImplementationOnce(() => 'test');
+        component.type = 'fits';
+        expect(component.getValue()).toEqual(expected);
+    });
+    it('getValue() should return test/dataset/test/file-explorertest', () => {
+        let expected = 'test/dataset/test/file-explorertest';
+        component.dataset = { ...dataset, name: 'test' };
+        component.getPath = jest.fn().mockImplementationOnce(() => 'test');
+        let spy = jest.spyOn(utils, 'getHost');
+        spy.mockImplementationOnce(() => 'test');
+        component.type = 'image';
+        expect(component.getValue()).toEqual(expected);
+    });
+    it('getValue() should return test_result', () => {
+        let expected = 'test_result';
+        component.getAttributeImage = jest.fn().mockImplementationOnce(() => {
+            return { ...attribute, label: 'test' } as Attribute
+        });
+        component.object = {
+            test: 'test_result'
+        }
+        expect(component.getValue()).toEqual(expected);
+    });
+
+    it('getPath() should return /test', () => {
+        component.object = { test: 'test' };
+        component.getAttributeImage = jest.fn().mockImplementationOnce(() => {
+            return { ...attribute, label: 'test' } as Attribute
+        });
+        expect(component.getPath()).toEqual('/test');
+    });
+    it('getAttributeImage() should return an attribute with id 2 ', () => {
+        component.attributeList = [
+            { ...attribute, id: 2 },
+            { ...attribute, id: 1 }
+        ];
+        component.attributeImageId = 2;
+        expect(component.getAttributeImage().id).toEqual(2);
+
+    });
+    it('getStyle() should return {"width": "10", "height": "5" }', () => {
+        component.width = '10';
+        component.height = '5';
+        expect(component.getStyle()).toEqual({ "width": "10", "height": "5" });
+
+    });
+
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-json.component.spec.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-json.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0e59c0dff6ef7b9ea47134ef4dd2bbb350c7f60e
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-json.component.spec.ts
@@ -0,0 +1,36 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { Attribute } from "src/app/metamodel/models";
+import { DisplayJsonComponent } from "./display-json.component";
+describe('[instance][search][detail][dynamic-content][dynamic-components] DisplayJsonComponent', () => {
+    let component: DisplayJsonComponent;
+    let fixture: ComponentFixture<DisplayJsonComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [DisplayJsonComponent],
+        });
+        fixture = TestBed.createComponent(DisplayJsonComponent);
+        component = fixture.componentInstance;
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getValue() should return test', () => {
+        component.attributeList = [
+            { ...attribute, id: 1, label: 'test1' },
+            { ...attribute, id: 2, label: 'test2' }
+        ];
+        component.attributeJsonId = 2;
+        component.object = { test2: 'test' };
+        expect(component.getValue()).toEqual('test');
+    });
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object-by-output-category.component.spec.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object-by-output-category.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cf12a4185c00cb712392ae8d8414a4d127718107
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object-by-output-category.component.spec.ts
@@ -0,0 +1,63 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { MockStore, provideMockStore } from '@ngrx/store/testing';
+import { Attribute } from 'src/app/metamodel/models';
+import { DisplayObjectByOutputCategoryComponent } from './display-object-by-output-category.component';
+import * as searchActions from 'src/app/instance/store/actions/search.actions';
+
+describe('[instance][search][detail][dynamic-content][dynamic-components] DisplayObjectByOutputCategoryComponent', () => {
+    let component: DisplayObjectByOutputCategoryComponent;
+    let fixture: ComponentFixture<DisplayObjectByOutputCategoryComponent>;
+    let store: MockStore;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                DisplayObjectByOutputCategoryComponent
+            ],
+            providers: [
+                provideMockStore({}),
+            ],
+            imports: [
+                BrowserAnimationsModule,
+            ],
+        })
+        fixture = TestBed.createComponent(DisplayObjectByOutputCategoryComponent);
+        component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getCategory() should return output category with id 2', () => {
+        component.outputCategoryList = [
+            { id: 1, display: 10, id_output_family: 2, label: 'test1' },
+            { id: 2, display: 10, id_output_family: 2, label: 'test2' }
+        ];
+        component.outputCategoryId = 2;
+        expect(component.getCategory().id).toEqual(2);
+    });
+    it('should return an array with two elements', () => {
+        let attribute: Attribute;
+        component.attributeList = [
+            { ...attribute, id: 1, id_detail_output_category: 2 },
+            { ...attribute, id: 2, id_detail_output_category: 3 },
+            { ...attribute, id: 2, id_detail_output_category: 2 }
+        ]
+        expect(component.getAttributeListByOutputCategory(2).length).toEqual(2);
+    });
+    it('should raises store dispatch event with download file action', () => {
+        let spy = jest.spyOn(store, 'dispatch');
+        component.downloadFile({ url: 'test.fr', filename: 'test' });
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(searchActions.downloadFile({ url: 'test.fr', filename: 'test' }));
+    })
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object-by-output-family.component.spec.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object-by-output-family.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0d4b1dd0d41e571e9b3495704b4ddd8bb539158f
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object-by-output-family.component.spec.ts
@@ -0,0 +1,72 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { MockStore, provideMockStore } from '@ngrx/store/testing';
+import { Attribute } from 'src/app/metamodel/models';
+import * as searchActions from 'src/app/instance/store/actions/search.actions';
+import { DisplayObjectByOutputFamilyComponent } from './display-object-by-output-family.component';
+
+describe('[instance][search][detail][dynamic-content][dynamic-components] DisplayObjectByOutputFamilyComponent', () => {
+    let component: DisplayObjectByOutputFamilyComponent;
+    let fixture: ComponentFixture<DisplayObjectByOutputFamilyComponent>;
+    let store: MockStore;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                DisplayObjectByOutputFamilyComponent
+            ],
+            providers: [
+                provideMockStore({}),
+            ],
+            imports: [
+                BrowserAnimationsModule,
+            ],
+        })
+        fixture = TestBed.createComponent(DisplayObjectByOutputFamilyComponent);
+        component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getFamily() should return output family with id 2', () => {
+        component.outputFamilyList = [
+            { id: 1, display: 10, label: 'test1', opened: true },
+            { id: 2, display: 10, label: 'test2', opened: false }
+        ];
+        component.outputFamilyId = 2;
+        expect(component.getFamily().id).toEqual(2);
+    });
+    it('getOutputCategoryListByFamily should return an array with two elements', () => {
+        component.outputCategoryList = [
+            { id: 1, display: 10, id_output_family: 2, label: 'test1' },
+            { id: 2, display: 10, id_output_family: 3, label: 'test2' },
+            { id: 3, display: 10, id_output_family: 2, label: 'test3' },
+        ]
+        component.getAttributeListByOutputCategory = jest.fn().mockImplementation(() => [1])
+        expect(component.getOutputCategoryListByFamily(2).length).toEqual(2);
+    });
+    it('getAttributeListByOutputCategory should return an array with two elements', () => {
+        let attribute: Attribute;
+        component.attributeList = [
+            { ...attribute, id: 1, id_detail_output_category: 2 },
+            { ...attribute, id: 2, id_detail_output_category: 3 },
+            { ...attribute, id: 2, id_detail_output_category: 2 }
+        ]
+        expect(component.getAttributeListByOutputCategory(2).length).toEqual(2);
+    });
+    it('should raises store dispatch event with download file action', () => {
+        let spy = jest.spyOn(store, 'dispatch');
+        component.downloadFile({ url: 'test.fr', filename: 'test' });
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(searchActions.downloadFile({ url: 'test.fr', filename: 'test' }));
+    })
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object.component.spec.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2548a9530ff97230398c198575c56bfb564cafab
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-object.component.spec.ts
@@ -0,0 +1,72 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+import { MockStore, provideMockStore } from '@ngrx/store/testing';
+import { Attribute } from 'src/app/metamodel/models';
+import * as searchActions from 'src/app/instance/store/actions/search.actions';
+import { DisplayObjectComponent } from './display-object.component';
+
+describe('[instance][search][detail][dynamic-content][dynamic-components] DisplayObjectComponent', () => {
+    let component: DisplayObjectComponent;
+    let fixture: ComponentFixture<DisplayObjectComponent>;
+    let store: MockStore;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [
+                DisplayObjectComponent
+            ],
+            providers: [
+                provideMockStore({}),
+            ],
+            imports: [
+                BrowserAnimationsModule,
+            ],
+        })
+        fixture = TestBed.createComponent(DisplayObjectComponent);
+        component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getOutputFamilyList() should return an array with two elements', () => {
+        component.outputFamilyList = [
+            { id: 1, display: 10, label: 'test1', opened: true },
+            { id: 2, display: 10, label: 'test2', opened: false }
+        ];
+        component.getOutputCategoryListByFamily = jest.fn().mockImplementation(() => [1])
+        expect(component.getOutputFamilyList().length).toEqual(2);
+    });
+    it('getOutputCategoryListByFamily() should return an array with one element', () => {
+        component.outputCategoryList = [
+            { id: 1, display: 10, label: 'test1', id_output_family: 1 },
+            { id: 2, display: 10, label: 'test2', id_output_family: 2 }
+        ];
+        component.getAttributeListByOutputCategory = jest.fn().mockImplementation(() => [1])
+        expect(component.getOutputCategoryListByFamily(1).length).toEqual(1);
+    });
+    it('getAttributeListByOutputCategory should return an array with two elements', () => {
+        let attribute: Attribute;
+        component.attributeList = [
+            { ...attribute, id: 1, id_detail_output_category: 2 },
+            { ...attribute, id: 2, id_detail_output_category: 3 },
+            { ...attribute, id: 2, id_detail_output_category: 2 }
+        ];
+        expect(component.getAttributeListByOutputCategory(2).length).toEqual(2);
+    });
+    it('should raises store dispatch event with download file action', () => {
+        let spy = jest.spyOn(store, 'dispatch');
+        component.downloadFile({ url: 'test.fr', filename: 'test' });
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(searchActions.downloadFile({ url: 'test.fr', filename: 'test' }));
+    })
+
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-ra-dec.component.spec.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-ra-dec.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..130f6e21db758cb6a92a3d161b6423701cac9d41
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-ra-dec.component.spec.ts
@@ -0,0 +1,47 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { Attribute } from "src/app/metamodel/models";
+import { DisplayRaDecComponent } from "./display-ra-dec.component";
+
+describe('[instance][search][detail][dynamic-content][dynamic-components] DisplayRaDecComponent', () => {
+    let component: DisplayRaDecComponent;
+    let fixture: ComponentFixture<DisplayRaDecComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [DisplayRaDecComponent],
+        });
+        fixture = TestBed.createComponent(DisplayRaDecComponent);
+        component = fixture.componentInstance;
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getAttributeRa() should return an attribute with id 2', () => {
+        component.attributeList = [
+            { ...attribute, id: 1, label: 'test1' },
+            { ...attribute, id: 2, label: 'test2' }
+        ];
+        component.attributeRaId = 2;
+        component.object = { test2: 'test' };
+        expect(component.getAttributeRa().id).toEqual(2);
+    });
+    it('getAttributeRa() should return an attribute with id 2', () => {
+        component.attributeList = [
+            { ...attribute, id: 1, label: 'test1' },
+            { ...attribute, id: 2, label: 'test2' }
+        ];
+        component.attributeDecId = 2;
+        component.object = { test2: 'test' };
+        expect(component.getAttributeDec().id).toEqual(2);
+    });
+    
+})
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-spectra.component.spec.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-spectra.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3f92057a6bef67067c1c546304f791ab8ac3f9bb
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-spectra.component.spec.ts
@@ -0,0 +1,52 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { HttpClient, HttpClientModule } from "@angular/common/http";
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { BrowserModule } from "@angular/platform-browser";
+import { AppConfigService } from "src/app/app-config.service";
+import { Attribute } from "src/app/metamodel/models";
+import { DisplaySpectraComponent } from "./display-spectra.component";
+
+describe('[instance][search][detail][dynamic-content][dynamic-components] DisplaySpectraComponent', () => {
+    let component: DisplaySpectraComponent;
+    let fixture: ComponentFixture<DisplaySpectraComponent>;
+    let attribute: Attribute;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [DisplaySpectraComponent],
+            imports: [
+                BrowserModule,
+                HttpClientModule
+            ],
+            providers: [
+                HttpClient,
+                { provide: AppConfigService, useValue: { servicesUrl: 'test' } },
+            ]
+        });
+        fixture = TestBed.createComponent(DisplaySpectraComponent);
+        component = fixture.componentInstance;
+        component.attributeList = [
+            { ...attribute, id: 1, label: 'test' }
+        ];
+        component.object = {};
+        component.attributeSpectraId = 1;
+        fixture.detectChanges();
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getZ() should return test', () => {
+        component.attributeZId = 1;
+        component.object = { test: 5 };
+        expect(component.getZ()).toEqual(5);
+
+    })
+
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-value-by-attribute.component.spec.ts b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-value-by-attribute.component.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..72f74e30ff0ac5dd5472499dc57830cb26f189cf
--- /dev/null
+++ b/client/src/app/instance/search/detail/dynamic-content/dynamic-components/display-value-by-attribute.component.spec.ts
@@ -0,0 +1,51 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { ComponentFixture, TestBed } from "@angular/core/testing";
+import { MockStore, provideMockStore } from "@ngrx/store/testing";
+import { Attribute } from "src/app/metamodel/models";
+import { DisplayValueByAttributeComponent } from "./display-value-by-attribute.component";
+import * as searchActions from 'src/app/instance/store/actions/search.actions';
+
+describe('[instance][search][detail][dynamic-content][dynamic-components] DisplayValueByAttributeComponent', () => {
+    let component: DisplayValueByAttributeComponent;
+    let fixture: ComponentFixture<DisplayValueByAttributeComponent>;
+    let attribute: Attribute;
+    let store: MockStore;
+
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            declarations: [DisplayValueByAttributeComponent],
+            providers: [
+                provideMockStore({}),
+            ]
+        });
+        fixture = TestBed.createComponent(DisplayValueByAttributeComponent);
+        component = fixture.componentInstance;
+        store = TestBed.inject(MockStore);
+
+    });
+    it('should create component', () => {
+        expect(component).toBeTruthy();
+    });
+    it('getAttributeById() should return an attribute with id 2', () => {
+        component.attributeList = [
+            { ...attribute, id: 1 },
+            { ...attribute, id: 2 }
+        ];
+        component.attributeId = 2;
+        expect(component.getAttributeById().id).toEqual(2);
+    });
+    it('should raises store dispatch event with download file action', () => {
+        let spy = jest.spyOn(store, 'dispatch');
+        component.downloadFile({ url: 'test.fr', filename: 'test' });
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledWith(searchActions.downloadFile({ url: 'test.fr', filename: 'test' }));
+    });
+});
\ No newline at end of file
diff --git a/client/src/app/instance/search/search-auth.guard.spec.ts b/client/src/app/instance/search/search-auth.guard.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e6fd8a2ac4b16c21aaf53215707ea245f325bbea
--- /dev/null
+++ b/client/src/app/instance/search/search-auth.guard.spec.ts
@@ -0,0 +1,138 @@
+/**
+ * This file is part of Anis Client.
+ *
+ * @copyright Laboratoire d'Astrophysique de Marseille / CNRS
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+import { TestBed } from '@angular/core/testing';
+import { MockStore, provideMockStore } from '@ngrx/store/testing';
+import { cold } from 'jasmine-marbles';
+import { AppConfigService } from 'src/app/app-config.service';
+import { SearchAuthGuard } from './search-auth.guard';
+import * as datasetSelector from 'src/app/metamodel/selectors/dataset.selector';
+import * as datasetGroupSelector from 'src/app/metamodel/selectors/dataset-group.selector';
+import * as authSelector from 'src/app/auth/auth.selector';
+import { Dataset } from 'src/app/metamodel/models';
+
+describe('[instance][search] SearchAuthGuard', () => {
+    let store: MockStore;
+    let mockDatasetSelectorSelectDatasetListIsLoaded;
+    let mockDatasetGroupSelectorSelectDatasetGroupListIsLoaded;
+    let mockDatasetSelectorSelectDatasetByRouteName;
+    let mockAuthSelectorSelectUserRoles;
+    let mockAuthSelectorSelectIsAuthenticated;
+    let mockDatasetGroupSelectorSelectAllDatasetGroups;
+    let dataset: Dataset;
+    beforeEach(() => {
+        TestBed.configureTestingModule({
+            providers: [
+                provideMockStore({}),],
+        })
+    });
+    it('should create component', () => {
+        TestBed.overrideProvider(AppConfigService, { useValue: { authenticationEnabled: true } })
+        store = TestBed.inject(MockStore);
+        let searchAuthGuard = TestBed.inject(SearchAuthGuard);
+        expect(searchAuthGuard).toBeTruthy();
+    });
+    it('should  return [] when datasetListIsLoaded and datasetGroupListIsLoaded are false', () => {
+        TestBed.overrideProvider(AppConfigService, {
+            useValue: {
+                authenticationEnabled: true,
+                adminRoles: ['test'],
+
+            }
+        })
+        store = TestBed.inject(MockStore);
+        mockDatasetSelectorSelectDatasetListIsLoaded = store.overrideSelector(datasetSelector.selectDatasetListIsLoaded, false);
+        mockDatasetGroupSelectorSelectDatasetGroupListIsLoaded = store.overrideSelector(datasetGroupSelector.selectDatasetGroupListIsLoaded, false);
+        let searchAuthGuard = TestBed.inject(SearchAuthGuard);
+        let result = cold('a', { a: searchAuthGuard.canActivate() });
+        let expected = cold('a', { a: [] });
+        expect(result).toBeObservable(expected);
+
+    })
+    it('should  return true when authenticationEnabled is true ', () => {
+        TestBed.overrideProvider(AppConfigService, { useValue: { authenticationEnabled: false } })
+        store = TestBed.inject(MockStore);
+        mockDatasetSelectorSelectDatasetListIsLoaded = store.overrideSelector(datasetSelector.selectDatasetListIsLoaded, true);
+        mockDatasetGroupSelectorSelectDatasetGroupListIsLoaded = store.overrideSelector(datasetGroupSelector.selectDatasetGroupListIsLoaded, true);
+        mockDatasetSelectorSelectDatasetByRouteName = store.overrideSelector(datasetSelector.selectDatasetByRouteName, { ...dataset, name: 'test' });
+        mockAuthSelectorSelectUserRoles = store.overrideSelector(authSelector.selectUserRoles, ['test']);
+        mockAuthSelectorSelectIsAuthenticated = store.overrideSelector(authSelector.selectIsAuthenticated, true);
+        mockDatasetGroupSelectorSelectAllDatasetGroups = store.overrideSelector(datasetGroupSelector.selectAllDatasetGroups, [
+            {
+                datasets: [],
+                id: 1,
+                instance_name: 'test',
+                role: 'test'
+
+            }]);
+        let searchAuthGuard = TestBed.inject(SearchAuthGuard);
+        let result = searchAuthGuard.canActivate();
+        let expected = cold('a', { a: true });
+        expect(result).toBeObservable(expected);
+
+    });
+    it('should  return false when user is authenticated and accessible is false', () => {
+        TestBed.overrideProvider(AppConfigService, {
+            useValue: {
+                authenticationEnabled: true,
+                adminRoles: [],
+
+            }
+        })
+        store = TestBed.inject(MockStore);
+        mockDatasetSelectorSelectDatasetListIsLoaded = store.overrideSelector(datasetSelector.selectDatasetListIsLoaded, true);
+        mockDatasetGroupSelectorSelectDatasetGroupListIsLoaded = store.overrideSelector(datasetGroupSelector.selectDatasetGroupListIsLoaded, true);
+        mockDatasetSelectorSelectDatasetByRouteName = store.overrideSelector(datasetSelector.selectDatasetByRouteName, { ...dataset, name: 'test' });
+        mockAuthSelectorSelectUserRoles = store.overrideSelector(authSelector.selectUserRoles, ['test']);
+        mockAuthSelectorSelectIsAuthenticated = store.overrideSelector(authSelector.selectIsAuthenticated, true);
+        mockDatasetGroupSelectorSelectAllDatasetGroups = store.overrideSelector(datasetGroupSelector.selectAllDatasetGroups, [
+            {
+                datasets: [],
+                id: 1,
+                instance_name: 'test',
+                role: 'test'
+
+            }]);
+        let searchAuthGuard = TestBed.inject(SearchAuthGuard);
+        let result = searchAuthGuard.canActivate();
+        let expected = cold('a', { a: false });
+        expect(result).toBeObservable(expected);
+    });
+    it('should  return true when user is authenticated and accessible is true', () => {
+        TestBed.overrideProvider(AppConfigService, {
+            useValue: {
+                authenticationEnabled: true,
+                adminRoles: ['test'],
+
+            }
+        })
+        store = TestBed.inject(MockStore);
+        mockDatasetSelectorSelectDatasetListIsLoaded = store.overrideSelector(datasetSelector.selectDatasetListIsLoaded, true);
+        mockDatasetGroupSelectorSelectDatasetGroupListIsLoaded = store.overrideSelector(datasetGroupSelector.selectDatasetGroupListIsLoaded, true);
+        mockDatasetSelectorSelectDatasetByRouteName = store.overrideSelector(datasetSelector.selectDatasetByRouteName, { ...dataset, name: 'dataset_name' });
+        mockAuthSelectorSelectUserRoles = store.overrideSelector(authSelector.selectUserRoles, ['test']);
+        mockAuthSelectorSelectIsAuthenticated = store.overrideSelector(authSelector.selectIsAuthenticated, true);
+        mockDatasetGroupSelectorSelectAllDatasetGroups = store.overrideSelector(datasetGroupSelector.selectAllDatasetGroups, [
+            {
+
+                datasets: ['dataset_name', 'test2'],
+                id: 1,
+                instance_name: 'test',
+                role: 'test'
+
+            }]);
+        let searchAuthGuard = TestBed.inject(SearchAuthGuard);
+        let result = searchAuthGuard.canActivate();
+        let expected = cold('a', { a: true });
+        expect(result).toBeObservable(expected);
+
+    });
+
+})
+
diff --git a/criteria b/criteria
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/search b/search
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391