From e625d0c4a10950bc037157a74f23f03fb1c0925a Mon Sep 17 00:00:00 2001 From: Tifenn Guillas <tifenn.guillas@gmail.com> Date: Tue, 5 Oct 2021 16:12:59 +0200 Subject: [PATCH] WIP: Tests on search type components --- .../cone-search-tab.component.spec.ts | 42 ++++ .../criteria/cone-search-tab.component.ts | 8 +- .../criteria-by-family.component.spec.ts | 204 ++++++++++++++++++ .../criteria/criteria-by-family.component.ts | 8 +- .../criteria/criteria-tabs.component.spec.ts | 53 +++++ .../criteria/criteria-tabs.component.ts | 8 +- .../search-type/time.component.spec.ts | 67 ++++++ .../criteria/search-type/time.component.ts | 11 +- .../dataset/dataset-card.component.spec.ts | 35 +++ .../dataset/dataset-card.component.ts | 17 +- .../dataset/dataset-tabs.component.spec.ts | 44 ++++ .../dataset/dataset-tabs.component.ts | 10 +- 12 files changed, 480 insertions(+), 27 deletions(-) create mode 100644 client/src/app/instance/search/components/criteria/cone-search-tab.component.spec.ts create mode 100644 client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts create mode 100644 client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts create mode 100644 client/src/app/instance/search/components/criteria/search-type/time.component.spec.ts create mode 100644 client/src/app/instance/search/components/dataset/dataset-card.component.spec.ts create mode 100644 client/src/app/instance/search/components/dataset/dataset-tabs.component.spec.ts 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 new file mode 100644 index 00000000..769799cf --- /dev/null +++ b/client/src/app/instance/search/components/criteria/cone-search-tab.component.spec.ts @@ -0,0 +1,42 @@ +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, Resolver } from '../../../store/models'; +import { DatasetByNamePipe } from '../../../../shared/pipes/dataset-by-name.pipe'; + +describe('[Instance][Search][Component][Criteria] ConeSearchTabComponent', () => { + @Component({ selector: 'app-cone-search', template: '' }) + class ConeSearchStubComponent { + @Input() coneSearch: ConeSearch; + @Input() resolver: Resolver; + @Input() resolverIsLoading: boolean; + @Input() resolverIsLoaded: boolean; + } + + let component: ConeSearchTabComponent; + let fixture: ComponentFixture<ConeSearchTabComponent>; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + ConeSearchTabComponent, + ConeSearchStubComponent, + DatasetByNamePipe + ], + imports: [ + AccordionModule.forRoot(), + BrowserAnimationsModule + ] + }); + fixture = TestBed.createComponent(ConeSearchTabComponent); + component = fixture.componentInstance; + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/instance/search/components/criteria/cone-search-tab.component.ts b/client/src/app/instance/search/components/criteria/cone-search-tab.component.ts index 85a3f259..841c216d 100644 --- a/client/src/app/instance/search/components/criteria/cone-search-tab.component.ts +++ b/client/src/app/instance/search/components/criteria/cone-search-tab.component.ts @@ -12,15 +12,15 @@ import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from import { Dataset } from 'src/app/metamodel/models'; import { ConeSearch, Resolver } from 'src/app/instance/store/models'; +/** + * @class + * @classdesc Search cone search tab component. + */ @Component({ selector: 'app-cone-search-tab', templateUrl: 'cone-search-tab.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) -/** - * @class - * @classdesc Search cone search tab component. - */ export class ConeSearchTabComponent { @Input() datasetSelected: string; @Input() datasetList: Dataset[]; 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 new file mode 100644 index 00000000..d019066b --- /dev/null +++ b/client/src/app/instance/search/components/criteria/criteria-by-family.component.spec.ts @@ -0,0 +1,204 @@ +import { Component, Input } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { CriteriaByFamilyComponent } from './criteria-by-family.component'; +import { Option } from '../../../../metamodel/models'; +import { Criterion, FieldCriterion } from '../../../store/models'; + +describe('[Instance][Search][Component][Criteria] CriteriaByFamilyComponent', () => { + @Component({ selector: 'app-field', template: '' }) + class FieldStubComponent { + @Input() id: number; + @Input() operator: string; + @Input() label: string; + @Input() placeholder: string; + @Input() attributeType: string; + @Input() criterion: Criterion; + @Input() advancedForm: boolean; + } + + @Component({ selector: 'app-between', template: '' }) + class BetweenStubComponent { + @Input() id: number; + @Input() label: string; + @Input() placeholderMin: string; + @Input() placeholderMax: string; + @Input() criterion: Criterion; + } + + @Component({ selector: 'app-select', template: '' }) + class SelectStubComponent { + @Input() id: number; + @Input() operator: string; + @Input() label: string; + @Input() options: Option[]; + @Input() criterion: Criterion; + @Input() advancedForm: boolean; + } + + @Component({ selector: 'app-select-multiple', template: '' }) + class SelectMultipleStubComponent { + @Input() id: number; + @Input() label: string; + @Input() options: Option[]; + @Input() criterion: Criterion; + } + + @Component({ selector: 'app-datalist', template: '' }) + class DatalistStubComponent { + @Input() id: number; + @Input() operator: string; + @Input() label: string; + @Input() placeholder: string; + @Input() options: Option[]; + @Input() criterion: Criterion; + @Input() advancedForm: boolean; + } + + @Component({ selector: 'app-list', template: '' }) + class ListStubComponent { + @Input() id: number; + @Input() label: string; + @Input() placeholder: string; + @Input() criterion: Criterion; + } + + @Component({ selector: 'app-radio', template: '' }) + class RadioStubComponent { + @Input() id: number; + @Input() operator: string; + @Input() label: string; + @Input() options: Option[]; + @Input() criterion: Criterion; + } + + @Component({ selector: 'app-checkbox', template: '' }) + class CheckboxStubComponent { + @Input() id: number; + @Input() label: string; + @Input() options: Option[]; + @Input() criterion: Criterion; + } + + @Component({ selector: 'app-date', template: '' }) + class DateStubComponent { + @Input() id: number; + @Input() operator: string; + @Input() label: string; + @Input() placeholder: string; + @Input() criterion: Criterion; + @Input() advancedForm: boolean; + } + + @Component({ selector: 'app-between-date', template: '' }) + class BetweenDateStubComponent { + @Input() id: number; + @Input() operator: string; + @Input() label: string; + @Input() criterion: Criterion; + } + + @Component({ selector: 'app-time', template: '' }) + class TimeStubComponent { + @Input() id: number; + @Input() operator: string; + @Input() label: string; + @Input() criterion: Criterion; + @Input() advancedForm: boolean; + } + + @Component({ selector: 'app-datetime', template: '' }) + class DatetimeStubComponent { + @Input() id: number; + @Input() operator: string; + @Input() label: string; + @Input() criterion: Criterion; + @Input() advancedForm: boolean; + } + + @Component({ selector: 'app-json-criteria', template: '' }) + class JsonStubComponent { + @Input() id: number; + @Input() label: string; + @Input() criterion: Criterion; + } + + let component: CriteriaByFamilyComponent; + let fixture: ComponentFixture<CriteriaByFamilyComponent>; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + CriteriaByFamilyComponent, + FieldStubComponent, + BetweenStubComponent, + SelectStubComponent, + SelectMultipleStubComponent, + DatalistStubComponent, + ListStubComponent, + RadioStubComponent, + CheckboxStubComponent, + DateStubComponent, + BetweenDateStubComponent, + TimeStubComponent, + DatetimeStubComponent, + JsonStubComponent + ] + }); + fixture = TestBed.createComponent(CriteriaByFamilyComponent); + component = fixture.componentInstance; + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('#getOptions(attribute) should return options of attribute', () => { + component.attributeList = [ + { + id: 1, + name: 'name_one', + label: 'label_one', + form_label: 'form_label_one', + description: 'description_one', + output_display: 2, + criteria_display: 2, + search_flag: 'ID', + search_type: 'field', + operator: '=', + type: 'integer', + display_detail: 2, + options: [ + { label: 'Three', value: 'three', display: 3 }, + { label: 'One', value: 'one', display: 1 }, + { label: 'Two', value: 'two', display: 2 } + ] + } + ]; + const attributeId = 1; + const options: Option[] = component.getOptions(attributeId); + expect(options.length).toBe(3) + }); + + it('#getCriterion(criterionId) should return correct criterion', () => { + component.criteriaList = [ + { 'id': 1, 'type': 'field', 'operator': 'eq', 'value': 'one' } as FieldCriterion, + { 'id': 2, 'type': 'field', 'operator': 'eq', 'value': 'two' } as FieldCriterion + ]; + const criterionId = 1; + const criterion = component.getCriterion(criterionId) as FieldCriterion; + expect(criterion.value).toBe('one'); + }); + + 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 delete criterion event when clicked', () => { + const criterionId = 1; + component.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1)); + component.emitDelete(criterionId); + }); +}); diff --git a/client/src/app/instance/search/components/criteria/criteria-by-family.component.ts b/client/src/app/instance/search/components/criteria/criteria-by-family.component.ts index b249169a..ebfbed20 100644 --- a/client/src/app/instance/search/components/criteria/criteria-by-family.component.ts +++ b/client/src/app/instance/search/components/criteria/criteria-by-family.component.ts @@ -12,15 +12,15 @@ import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from import { Criterion } from '../../../store/models'; import { Attribute, Option } from 'src/app/metamodel/models'; +/** + * @class + * @classdesc Search criteria by family component. + */ @Component({ selector: 'app-criteria-by-family', templateUrl: 'criteria-by-family.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) -/** - * @class - * @classdesc Search criteria by family component. - */ export class CriteriaByFamilyComponent { @Input() attributeList: Attribute[]; @Input() criteriaList: Criterion[]; 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 new file mode 100644 index 00000000..b2557821 --- /dev/null +++ b/client/src/app/instance/search/components/criteria/criteria-tabs.component.spec.ts @@ -0,0 +1,53 @@ +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 { CriteriaTabsComponent } from './criteria-tabs.component'; +import { Attribute } from '../../../../metamodel/models'; +import { Criterion, FieldCriterion } from '../../../store/models'; +import { AttributeListByFamilyPipe } from '../../../../shared/pipes/attribute-list-by-family.pipe'; + +describe('[Instance][Search][Component][Criteria] CriteriaTabsComponent', () => { + @Component({ selector: 'app-criteria-by-family', template: '' }) + class CriteriaByFamilyStubComponent { + @Input() attributeList: Attribute[]; + @Input() criteriaList: Criterion[]; + } + + let component: CriteriaTabsComponent; + let fixture: ComponentFixture<CriteriaTabsComponent>; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + CriteriaTabsComponent, + CriteriaByFamilyStubComponent, + AttributeListByFamilyPipe + ], + imports: [ + AccordionModule.forRoot(), + BrowserAnimationsModule + ] + }); + fixture = TestBed.createComponent(CriteriaTabsComponent); + component = fixture.componentInstance; + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + 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 delete criterion event when clicked', () => { + const criterionId = 1; + component.deleteCriterion.subscribe((event: number) => expect(event).toEqual(1)); + component.emitDelete(criterionId); + }); +}); diff --git a/client/src/app/instance/search/components/criteria/criteria-tabs.component.ts b/client/src/app/instance/search/components/criteria/criteria-tabs.component.ts index 7c28f7e9..a7c45275 100644 --- a/client/src/app/instance/search/components/criteria/criteria-tabs.component.ts +++ b/client/src/app/instance/search/components/criteria/criteria-tabs.component.ts @@ -12,15 +12,15 @@ import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy } from import { Criterion } from '../../../store/models'; import { CriteriaFamily, Attribute } from 'src/app/metamodel/models'; +/** + * @class + * @classdesc Search criteria tabs component. + */ @Component({ selector: 'app-criteria-tabs', templateUrl: 'criteria-tabs.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) -/** - * @class - * @classdesc Search criteria tabs component. - */ export class CriteriaTabsComponent { @Input() attributeList: Attribute[]; @Input() criteriaFamilyList: CriteriaFamily[]; 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 00000000..d5f3df13 --- /dev/null +++ b/client/src/app/instance/search/components/criteria/search-type/time.component.spec.ts @@ -0,0 +1,67 @@ +import { Component, Input } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { FormsModule, ReactiveFormsModule, FormControl } from '@angular/forms'; + + +import { AccordionModule } from 'ngx-bootstrap/accordion'; + +import { TimeComponent } from './time.component'; +import { NgSelectModule } from '@ng-select/ng-select'; +import { FieldCriterion } from '../../../../store/models/criterion'; + +describe('[Instance][Search][Component][Criteria][SearchType] TimeComponent', () => { + @Component({ selector: 'app-operator', template: '' }) + class OperatorStubComponent { + @Input() operator: string; + @Input() searchType: string; + @Input() advancedForm: boolean; + @Input() disabled: boolean; + } + + let component: TimeComponent; + let fixture: ComponentFixture<TimeComponent>; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + TimeComponent, + OperatorStubComponent + ], + imports: [ + NgSelectModule, + FormsModule, + ReactiveFormsModule + ] + }); + fixture = TestBed.createComponent(TimeComponent); + component = fixture.componentInstance; + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('#changeOperator() should change the operator', () => { + expect(component.operator).toBeUndefined(); + component.changeOperator('toto'); + expect(component.operator).toBe('toto'); + }); + + it('raises the add criterion event when clicked', () => { + component.id = 1; + const operator = 'eq'; + component.operator = operator; + component.form.controls.hh.setValue('15'); + component.form.controls.mm.setValue('47'); + const expectedCriterion = { id: component.id, type: 'field', operator, value: '15:47' } as FieldCriterion; + component.addCriterion.subscribe((event: FieldCriterion) => expect(event).toEqual(expectedCriterion)); + component.emitAdd(); + }); + + it('#initTime(t) should return an array of string with 2 digits from 0 to t', () => { + const n = 10; + expect(component.initTime(n).length).toEqual(n); + expect(component.initTime(n)[5]).toEqual('05'); + }); +}); diff --git a/client/src/app/instance/search/components/criteria/search-type/time.component.ts b/client/src/app/instance/search/components/criteria/search-type/time.component.ts index cc9d32a0..9205c7a6 100644 --- a/client/src/app/instance/search/components/criteria/search-type/time.component.ts +++ b/client/src/app/instance/search/components/criteria/search-type/time.component.ts @@ -12,15 +12,18 @@ import { FormGroup, FormControl, Validators } from '@angular/forms'; import { Criterion, FieldCriterion } from 'src/app/instance/store/models'; +/** + * @class + * @classdesc Time search type component. + * + * @implements OnChanges + */ @Component({ selector: 'app-time', templateUrl: 'time.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) -/** - * @class - * @classdesc Time search type component. - */ + export class TimeComponent implements OnChanges { @Input() id: number; @Input() operator: string; 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 new file mode 100644 index 00000000..c87937f9 --- /dev/null +++ b/client/src/app/instance/search/components/dataset/dataset-card.component.spec.ts @@ -0,0 +1,35 @@ +import { Router } from '@angular/router'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { PopoverModule } from 'ngx-bootstrap/popover'; + +import { DatasetCardComponent } from './dataset-card.component'; + +describe('[Instance][Search][Component][Dataset] DatasetCardComponent', () => { + let component: DatasetCardComponent; + let fixture: ComponentFixture<DatasetCardComponent>; + let router: Router; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [DatasetCardComponent], + imports: [PopoverModule.forRoot()], + providers: [{ provide: Router, useValue: { navigate: jest.fn() }}] + }); + fixture = TestBed.createComponent(DatasetCardComponent); + component = fixture.componentInstance; + router = TestBed.inject(Router); + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); + + it('should create the component', () => { + component.instanceSelected = 'myInstance'; + const spy = jest.spyOn(router, 'navigate'); + component.selectDataset('myDataset'); + expect(spy).toHaveBeenCalledTimes(1); + expect(spy).toHaveBeenCalledWith(["/instance/myInstance/search/dataset/myDataset"]); + }); +}); diff --git a/client/src/app/instance/search/components/dataset/dataset-card.component.ts b/client/src/app/instance/search/components/dataset/dataset-card.component.ts index 2ca57d97..18213276 100644 --- a/client/src/app/instance/search/components/dataset/dataset-card.component.ts +++ b/client/src/app/instance/search/components/dataset/dataset-card.component.ts @@ -7,20 +7,20 @@ * file that was distributed with this source code. */ -import { Component, Input, ChangeDetectionStrategy, Output, EventEmitter } from '@angular/core'; +import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; import { Router } from '@angular/router'; import { Survey, Dataset } from 'src/app/metamodel/models'; +/** + * @class + * @classdesc Search dataset card component. + */ @Component({ selector: 'app-dataset-card', templateUrl: 'dataset-card.component.html', changeDetection: ChangeDetectionStrategy.OnPush }) -/** - * @class - * @classdesc Search dataset card component. - */ export class DatasetCardComponent { @Input() survey: Survey; @Input() dataset: Dataset; @@ -29,7 +29,12 @@ export class DatasetCardComponent { constructor(private router: Router) { } - selectDataset(datasetName: string) { + /** + * Navigates to search form corresponding to the given dataset. + * + * @param {string} datasetName - The dataset name. + */ + selectDataset(datasetName: string): void { this.router.navigate([`/instance/${this.instanceSelected}/search/dataset/${datasetName}`]); } } 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 new file mode 100644 index 00000000..831c35d7 --- /dev/null +++ b/client/src/app/instance/search/components/dataset/dataset-tabs.component.spec.ts @@ -0,0 +1,44 @@ +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 { DatasetTabsComponent } from './dataset-tabs.component'; +import { Dataset, Survey } from '../../../../metamodel/models'; +import { DatasetListByFamilyPipe } from '../../../../shared/pipes/dataset-list-by-family.pipe'; +import { SurveyByNamePipe } from '../../../../shared/pipes/survey-by-name.pipe'; + +describe('[Instance][Search][Component][Dataset] DatasetTabsComponent', () => { + @Component({ selector: 'app-dataset-card', template: '' }) + class DatasetCardStubComponent { + @Input() survey: Survey; + @Input() dataset: Dataset; + @Input() instanceSelected: string; + @Input() datasetSelected: string; + } + + let component: DatasetTabsComponent; + let fixture: ComponentFixture<DatasetTabsComponent>; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [ + DatasetTabsComponent, + DatasetCardStubComponent, + DatasetListByFamilyPipe, + SurveyByNamePipe + ], + imports: [ + AccordionModule.forRoot(), + BrowserAnimationsModule + ] + }); + fixture = TestBed.createComponent(DatasetTabsComponent); + component = fixture.componentInstance; + }); + + it('should create the component', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/instance/search/components/dataset/dataset-tabs.component.ts b/client/src/app/instance/search/components/dataset/dataset-tabs.component.ts index b2b87327..4f1df33a 100644 --- a/client/src/app/instance/search/components/dataset/dataset-tabs.component.ts +++ b/client/src/app/instance/search/components/dataset/dataset-tabs.component.ts @@ -7,20 +7,20 @@ * file that was distributed with this source code. */ -import { Component, Input, ChangeDetectionStrategy, Output, EventEmitter } from '@angular/core'; +import { Component, Input, ChangeDetectionStrategy } from '@angular/core'; import { Survey, Dataset, DatasetFamily } from 'src/app/metamodel/models'; +/** + * @class + * @classdesc Search dataset tabs component. + */ @Component({ selector: 'app-dataset-tabs', templateUrl: 'dataset-tabs.component.html', styleUrls: ['dataset-tabs.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -/** - * @class - * @classdesc Search dataset tabs component. - */ export class DatasetTabsComponent { @Input() surveyList: Survey[]; @Input() datasetList: Dataset[]; -- GitLab