Skip to content
Snippets Groups Projects
Commit 751282a1 authored by Tifenn Guillas's avatar Tifenn Guillas
Browse files

Tests on search containers => DONE

parent 8e25ea94
No related branches found
No related tags found
2 merge requests!29Develop,!18Resolve "Add tests for instance search module"
Showing with 314 additions and 74 deletions
import { Component } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { of } from 'rxjs';
import { AbstractSearchComponent } from './abstract-search.component';
import * as searchActions from '../../store/actions/search.actions';
describe('[Instance][Search][Container] AbstractSearchComponent', () => {
@Component({
selector: 'app-fake',
template: ''
})
class MyFakeComponent extends AbstractSearchComponent {
ngOnInit() {
super.ngOnInit();
}
ngOnDestroy() {
super.ngOnDestroy();
}
}
let component: MyFakeComponent;
let fixture: ComponentFixture<MyFakeComponent>;
let store: MockStore;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [MyFakeComponent],
providers: [provideMockStore({ })]
});
fixture = TestBed.createComponent(MyFakeComponent);
component = fixture.componentInstance;
store = TestBed.inject(MockStore);
}));
it('should create the component', () => {
expect(component).toBeTruthy();
});
it('should execute ngOnInit lifecycle', (done) => {
component.attributeListIsLoaded = of(true);
const spy = jest.spyOn(store, 'dispatch');
component.ngOnInit();
Promise.resolve(null).then(function() {
expect(spy).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenCalledWith(searchActions.initSearch());
expect(spy).toHaveBeenCalledWith(searchActions.loadDefaultFormParameters());
done();
});
});
it('#ngOnDestroy() should unsubscribe from attributeListIsLoadedSubscription', () => {
component.attributeListIsLoadedSubscription = of().subscribe();
const spy = jest.spyOn(component.attributeListIsLoadedSubscription, 'unsubscribe');
component.ngOnDestroy();
expect(spy).toHaveBeenCalledTimes(1);
});
});
......@@ -16,6 +16,14 @@ import * as searchActions from '../../store/actions/search.actions';
import * as searchSelector from '../../store/selectors/search.selector';
import * as coneSearchSelector from '../../store/selectors/cone-search.selector';
/**
* @abstract
* @class
* @classdesc Abstract search container.
*
* @implements OnInit
* @implements OnDestroy
*/
@Directive()
export abstract class AbstractSearchComponent implements OnInit, OnDestroy {
public datasetSelected: Observable<string>;
......@@ -41,8 +49,7 @@ export abstract class AbstractSearchComponent implements OnInit, OnDestroy {
public outputList: Observable<number[]>;
public queryParams: Observable<SearchQueryParams>;
public coneSearch: Observable<ConeSearch>;
private attributeListIsLoadedSubscription: Subscription;
public attributeListIsLoadedSubscription: Subscription;
constructor(protected store: Store<{ }>) {
this.datasetSelected = store.select(datasetSelector.selectDatasetNameByRoute);
......@@ -70,7 +77,7 @@ export abstract class AbstractSearchComponent implements OnInit, OnDestroy {
this.coneSearch = this.store.select(coneSearchSelector.selectConeSearch);
}
ngOnInit() {
ngOnInit(): void {
// Create a micro task that is processed after the current synchronous code
// This micro task prevent the expression has changed after view init error
Promise.resolve(null).then(() => this.store.dispatch(searchActions.initSearch()));
......@@ -81,7 +88,7 @@ export abstract class AbstractSearchComponent implements OnInit, OnDestroy {
});
}
ngOnDestroy() {
ngOnDestroy(): void {
if (this.attributeListIsLoadedSubscription) this.attributeListIsLoadedSubscription.unsubscribe();
}
}
......@@ -18,11 +18,11 @@
(deleteConeSearch)="deleteConeSearch()"
(retrieveCoordinates)="retrieveCoordinates($event)">
</app-cone-search-tab>
<app-criteria-tabs
<app-criteria-tabs
[attributeList]="attributeList | async | sortByCriteriaDisplay"
[criteriaFamilyList]="criteriaFamilyList | async"
[criteriaList]="criteriaList | async"
(addCriterion)="addCriterion($event)"
(addCriterion)="addCriterion($event)"
(deleteCriterion)="deleteCriterion($event)">
</app-criteria-tabs>
</div>
......@@ -32,7 +32,7 @@
[currentStep]="currentStep | async"
[datasetSelected]="datasetSelected | async"
[datasetList]="datasetList | async"
[attributeList]="attributeList | async | SortByOutputDisplayPipe"
[attributeList]="attributeList | async | sortByCriteriaDisplay"
[criteriaFamilyList]="criteriaFamilyList | async"
[outputFamilyList]="outputFamilyList | async"
[outputCategoryList]="outputCategoryList | async"
......@@ -45,8 +45,8 @@
</div>
<div class="row mt-5 justify-content-between">
<div class="col">
<a routerLink="/instance/{{ instanceSelected | async }}/search/dataset/{{ datasetSelected | async }}"
[queryParams]="queryParams | async"
<a routerLink="/instance/{{ instanceSelected | async }}/search/dataset/{{ datasetSelected | async }}"
[queryParams]="queryParams | async"
class="btn btn-outline-secondary">
<span class="fas fa-arrow-left"></span> Dataset
</a>
......
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { Component, Input } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { CriteriaComponent } from './criteria.component';
import { Attribute, CriteriaFamily, Dataset, OutputCategory, OutputFamily } from '../../../metamodel/models';
import { ConeSearch, Criterion, Resolver, SearchQueryParams } from '../../store/models';
import { SortByCriteriaDisplayPipe } from '../pipes/sort-by-criteria-display.pipe';
import * as searchActions from '../../store/actions/search.actions';
import { AbstractSearchComponent } from './abstract-search.component';
import * as coneSearchActions from '../../store/actions/cone-search.actions';
describe('[Instance][Search][Container] CriteriaComponent', () => {
@Component({ selector: 'app-spinner', template: '' })
class SpinnerStubComponent { }
@Component({ selector: 'app-cone-search-tab', template: '' })
class ConeSearchStubComponent {
@Input() datasetSelected: string;
@Input() datasetList: Dataset[];
@Input() coneSearch: ConeSearch;
@Input() resolver: Resolver;
@Input() resolverIsLoading: boolean;
@Input() resolverIsLoaded: boolean;
}
@Component({ selector: 'app-criteria-tabs', template: '' })
class CriteriaTabsStubComponent {
@Input() attributeList: Attribute[];
@Input() criteriaFamilyList: CriteriaFamily[];
@Input() criteriaList: Criterion[];
}
@Component({ selector: 'app-summary', template: '' })
class SummaryStubComponent {
@Input() currentStep: string;
@Input() datasetSelected: string;
@Input() datasetList: Dataset[];
@Input() attributeList: Attribute[];
@Input() criteriaFamilyList: CriteriaFamily[];
@Input() outputFamilyList: OutputFamily[];
@Input() outputCategoryList: OutputCategory[];
@Input() criteriaList: Criterion[];
@Input() outputList: number[];
@Input() queryParams: SearchQueryParams;
@Input() coneSearch: ConeSearch;
}
let component: CriteriaComponent;
let fixture: ComponentFixture<CriteriaComponent>;
let store: MockStore;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [
CriteriaComponent,
SpinnerStubComponent,
ConeSearchStubComponent,
CriteriaTabsStubComponent,
SummaryStubComponent,
SortByCriteriaDisplayPipe
],
providers: [provideMockStore({ })]
});
fixture = TestBed.createComponent(CriteriaComponent);
component = fixture.componentInstance;
store = TestBed.inject(MockStore);
}));
it('should create the component', () => {
expect(component).toBeTruthy();
});
it('should execute ngOnInit lifecycle', (done) => {
const spy = jest.spyOn(store, 'dispatch');
jest.spyOn(AbstractSearchComponent.prototype, 'ngOnInit').mockReturnThis();
component.ngOnInit();
Promise.resolve(null).then(function() {
expect(spy).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenCalledWith(searchActions.changeStep({ step: 'criteria' }));
expect(spy).toHaveBeenCalledWith(searchActions.checkCriteria());
done();
});
});
it('#addCriterion() should dispatch addCriterion action', () => {
const criterion: Criterion = { id: 1, type: 'field' };
const spy = jest.spyOn(store, 'dispatch');
component.addCriterion(criterion);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(searchActions.addCriterion({ criterion }));
});
it('#deleteCriterion() should dispatch deleteCriterion action', () => {
const spy = jest.spyOn(store, 'dispatch');
component.deleteCriterion(1);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(searchActions.deleteCriterion({ idCriterion: 1 }));
});
it('#addConeSearch() should dispatch addConeSearch action', () => {
const coneSearch: ConeSearch = { ra: 1, dec: 2, radius: 3 };
const spy = jest.spyOn(store, 'dispatch');
component.addConeSearch(coneSearch);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(coneSearchActions.addConeSearch({ coneSearch }));
});
it('#deleteConeSearch() should dispatch deleteConeSearch action', () => {
const spy = jest.spyOn(store, 'dispatch');
component.deleteConeSearch();
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(coneSearchActions.deleteConeSearch());
});
it('#retrieveCoordinates() should dispatch deleteConeSearch action', () => {
const spy = jest.spyOn(store, 'dispatch');
component.retrieveCoordinates('myObject');
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith(coneSearchActions.retrieveCoordinates({ name: 'myObject' }));
});
});
......@@ -18,14 +18,14 @@ import * as searchActions from '../../store/actions/search.actions';
import * as coneSearchActions from '../../store/actions/cone-search.actions';
import * as coneSearchSelector from '../../store/selectors/cone-search.selector';
@Component({
selector: 'app-criteria',
templateUrl: 'criteria.component.html'
})
/**
* @class
* @classdesc Search criteria container.
*/
@Component({
selector: 'app-criteria',
templateUrl: 'criteria.component.html'
})
export class CriteriaComponent extends AbstractSearchComponent {
public resolver: Observable<Resolver>;
public resolverIsLoading: Observable<boolean>;
......@@ -38,7 +38,9 @@ export class CriteriaComponent extends AbstractSearchComponent {
this.resolverIsLoaded = this.store.select(coneSearchSelector.selectResolverIsLoaded);
}
ngOnInit() {
ngOnInit(): void {
// Create a micro task that is processed after the current synchronous code
// This micro task prevent the expression has changed after view init error
Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'criteria' })));
Promise.resolve(null).then(() => this.store.dispatch(searchActions.checkCriteria()));
super.ngOnInit();
......@@ -62,14 +64,27 @@ export class CriteriaComponent extends AbstractSearchComponent {
this.store.dispatch(searchActions.deleteCriterion({ idCriterion }));
}
/**
* Dispatches action to add cone search.
*
* @param {ConeSearch} coneSearch - The cone search.
*/
addConeSearch(coneSearch: ConeSearch): void {
this.store.dispatch(coneSearchActions.addConeSearch({ coneSearch }));
}
/**
* Dispatches action to remove the cone search.
*/
deleteConeSearch(): void {
this.store.dispatch(coneSearchActions.deleteConeSearch());
}
/**
* Dispatches action to retrieve object coordinates.
*
* @param {string} name - The object name.
*/
retrieveCoordinates(name: string): void {
this.store.dispatch(coneSearchActions.retrieveCoordinates({ name }));
}
......
......@@ -4,7 +4,7 @@
</app-spinner>
<div *ngIf="(datasetFamilyListIsLoaded | async)
&& (datasetListIsLoaded | async)
&& (datasetListIsLoaded | async)
&& (surveyListIsLoaded | async)" class="row mt-4">
<ng-container *ngIf="(datasetList | async).length === 0">
<div class="col-12 lead text-center">
......@@ -30,7 +30,7 @@
[currentStep]="currentStep | async"
[datasetSelected]="datasetSelected | async"
[datasetList]="datasetList | async"
[attributeList]="attributeList | async | SortByOutputDisplayPipe"
[attributeList]="attributeList | async | sortByOutputDisplay"
[criteriaFamilyList]="criteriaFamilyList | async"
[outputFamilyList]="outputFamilyList | async"
[outputCategoryList]="outputCategoryList | async"
......
/**
* 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, waitForAsync } from '@angular/core/testing';
import { Component, Input } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
import { Component} from '@angular/core';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { of } from 'rxjs';
import { Store } from '@ngrx/store';
import { Observable, Subscription } from 'rxjs';
import { AbstractSearchComponent } from './abstract-search.component';
import { Survey, DatasetFamily } from 'src/app/metamodel/models';
import { DatasetComponent } from './dataset.component';
import {
Attribute,
CriteriaFamily,
Dataset,
DatasetFamily,
OutputCategory,
OutputFamily,
Survey
} from '../../../metamodel/models';
import { ConeSearch, Criterion, SearchQueryParams } from '../../store/models';
import { SortByOutputDisplayPipe } from '../pipes/sort-by-output-display.pipe';
import * as searchActions from '../../store/actions/search.actions';
import * as authSelector from 'src/app/auth/auth.selector';
import * as datasetFamilySelector from 'src/app/metamodel/selectors/dataset-family.selector';
import * as surveySelector from 'src/app/metamodel/selectors/survey.selector';
import { AbstractSearchComponent } from './abstract-search.component';
/**
* @class
* @classdesc Search dataset container.
*/
@Component({
selector: 'app-dataset',
templateUrl: 'dataset.component.html'
})
export class DatasetComponent extends AbstractSearchComponent {
public isAuthenticated: Observable<boolean>;
public datasetFamilyListIsLoading: Observable<boolean>;
public datasetFamilyListIsLoaded: Observable<boolean>;
public datasetFamilyList: Observable<DatasetFamily[]>;
public surveyListIsLoading: Observable<boolean>;
public surveyListIsLoaded: Observable<boolean>;
public surveyList: Observable<Survey[]>;
public datasetSelectedSubscription: Subscription;
describe('[Instance][Search][Container] DatasetComponent', () => {
@Component({ selector: 'app-spinner', template: '' })
class SpinnerStubComponent { }
constructor(protected store: Store<{ }>) {
super(store);
this.isAuthenticated = store.select(authSelector.selectIsAuthenticated);
this.datasetFamilyListIsLoading = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoading);
this.datasetFamilyListIsLoaded = store.select(datasetFamilySelector.selectDatasetFamilyListIsLoaded);
this.datasetFamilyList = store.select(datasetFamilySelector.selectAllDatasetFamilies);
this.surveyListIsLoading = store.select(surveySelector.selectSurveyListIsLoading);
this.surveyListIsLoaded = store.select(surveySelector.selectSurveyListIsLoaded);
this.surveyList = store.select(surveySelector.selectAllSurveys);
@Component({ selector: 'app-dataset-tabs', template: '' })
class DatasetTabsStubComponent {
@Input() surveyList: Survey[];
@Input() datasetList: Dataset[];
@Input() datasetFamilyList: DatasetFamily[];
@Input() instanceSelected: string;
@Input() datasetSelected: string;
}
ngOnInit(): void {
Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'dataset' })));
this.datasetSelectedSubscription = this.datasetSelected.subscribe(datasetSelected => {
if (datasetSelected) {
Promise.resolve(null).then(() => this.store.dispatch(searchActions.initSearch()));
}
})
super.ngOnInit();
@Component({ selector: 'app-summary', template: '' })
class SummaryStubComponent {
@Input() currentStep: string;
@Input() datasetSelected: string;
@Input() datasetList: Dataset[];
@Input() attributeList: Attribute[];
@Input() criteriaFamilyList: CriteriaFamily[];
@Input() outputFamilyList: OutputFamily[];
@Input() outputCategoryList: OutputCategory[];
@Input() criteriaList: Criterion[];
@Input() outputList: number[];
@Input() queryParams: SearchQueryParams;
@Input() coneSearch: ConeSearch;
}
ngOnDestroy(): void {
this.datasetSelectedSubscription.unsubscribe();
super.ngOnDestroy();
}
}
let component: DatasetComponent;
let fixture: ComponentFixture<DatasetComponent>;
let store: MockStore;
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [
DatasetComponent,
SpinnerStubComponent,
DatasetTabsStubComponent,
SummaryStubComponent,
SortByOutputDisplayPipe
],
providers: [provideMockStore({ })]
});
fixture = TestBed.createComponent(DatasetComponent);
component = fixture.componentInstance;
store = TestBed.inject(MockStore);
}));
it('should create the component', () => {
expect(component).toBeTruthy();
});
it('should execute ngOnInit lifecycle', (done) => {
component.datasetSelected = of('myDataset');
const spy = jest.spyOn(store, 'dispatch');
jest.spyOn(AbstractSearchComponent.prototype, 'ngOnInit').mockReturnThis();
component.ngOnInit();
Promise.resolve(null).then(function() {
expect(spy).toHaveBeenCalledTimes(2);
expect(spy).toHaveBeenCalledWith(searchActions.changeStep({ step: 'dataset' }));
expect(spy).toHaveBeenCalledWith(searchActions.initSearch());
done();
});
});
it('#ngOnDestroy() should unsubscribe from datasetSelectedSubscription', () => {
component.datasetSelectedSubscription = of().subscribe();
const spy = jest.spyOn(component.datasetSelectedSubscription, 'unsubscribe');
component.ngOnDestroy();
expect(spy).toHaveBeenCalledTimes(1);
});
});
......@@ -49,6 +49,8 @@ export class DatasetComponent extends AbstractSearchComponent {
}
ngOnInit(): void {
// Create a micro task that is processed after the current synchronous code
// This micro task prevent the expression has changed after view init error
Promise.resolve(null).then(() => this.store.dispatch(searchActions.changeStep({ step: 'dataset' })));
this.datasetSelectedSubscription = this.datasetSelected.subscribe(datasetSelected => {
if (datasetSelected) {
......@@ -59,7 +61,7 @@ export class DatasetComponent extends AbstractSearchComponent {
}
ngOnDestroy(): void {
this.datasetSelectedSubscription.unsubscribe();
if (this.datasetSelectedSubscription) this.datasetSelectedSubscription.unsubscribe();
super.ngOnDestroy();
}
}
......@@ -161,7 +161,7 @@ describe('[Instance][Search][Container] ResultComponent', () => {
expect(spy).toHaveBeenCalledWith(searchActions.deleteSelectedData({ id: 1 }));
});
it('#ngOnDestroy() should dispatch destroyResults action, unsubscribe from pristineSubscription and call #ngOnDestroy() on extended class', () => {
it('#ngOnDestroy() should dispatch destroyResults action and unsubscribe from pristineSubscription', () => {
component.pristineSubscription = of().subscribe();
const unsubscribeSpy = jest.spyOn(component.pristineSubscription, 'unsubscribe');
const dispatchSpy = jest.spyOn(store, 'dispatch');
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment