-
François Agneray authoredFrançois Agneray authored
search-multiple.effects.spec.ts 19.58 KiB
import { TestBed } from '@angular/core/testing';
import { provideMockActions } from '@ngrx/effects/testing';
import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects';
import { MockStore, provideMockStore } from '@ngrx/store/testing';
import { Observable } from 'rxjs';
import { cold, hot } from 'jasmine-marbles';
import { ToastrService } from 'ngx-toastr';
import { SearchMultipleEffects } from './search-multiple.effects';
import { SearchService } from '../services/search.service';
import * as fromSearch from '../reducers/search.reducer';
import * as fromSearchMultiple from '../reducers/search-multiple.reducer';
import * as fromInstance from '../../../metamodel/reducers/instance.reducer';
import * as datasetSelector from '../../../metamodel/selectors/dataset.selector';
import * as coneSearchSelector from '../selectors/cone-search.selector';
import * as coneSearchActions from '../actions/cone-search.actions';
import { ConeSearch, SearchMultipleDatasetLength } from '../models';
import * as searchMultipleSelector from '../selectors/search-multiple.selector';
import * as instanceSelector from '../../../metamodel/selectors/instance.selector';
import * as searchMultipleActions from '../actions/search-multiple.actions';
describe('[Instance][Store] SearchMultipleEffects', () => {
let actions = new Observable();
let effects: SearchMultipleEffects;
let metadata: EffectsMetadata<SearchMultipleEffects>;
let searchService: SearchService;
let toastr: ToastrService;
let store: MockStore;
const initialState = {
metamodel: { instance: { ...fromInstance.initialState }},
instance: {
search: {...fromSearch.initialState},
searchMultiple: {...fromSearchMultiple.initialState}
}
};
let mockSearchMultipleSelectorSelectPristine;
let mockSearchMultipleSelectorSelectSelectedDatasetsByRoute;
let mockSearchMultipleSelectorSelectSelectedDatasets;
let mockConeSearchSelectorSelectConeSearchByRoute;
let mockConeSearchSelectorSelectConeSearch;
let mockInstanceSelectorSelectorSelectInstanceByRouteName;
let mockDatasetSelectorSelectAllConeSearchDatasets;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
SearchMultipleEffects,
{ provide: SearchService, useValue: { retrieveDataLength: jest.fn() }},
{ provide: ToastrService, useValue: { error: jest.fn() }},
provideMockActions(() => actions),
provideMockStore({ initialState }),
]
}).compileComponents();
effects = TestBed.inject(SearchMultipleEffects);
metadata = getEffectsMetadata(effects);
searchService = TestBed.inject(SearchService);
toastr = TestBed.inject(ToastrService);
store = TestBed.inject(MockStore);
mockSearchMultipleSelectorSelectPristine = store.overrideSelector(
searchMultipleSelector.selectPristine,true
);
mockSearchMultipleSelectorSelectSelectedDatasetsByRoute = store.overrideSelector(
searchMultipleSelector.selectSelectedDatasetsByRoute,''
);
mockSearchMultipleSelectorSelectSelectedDatasets = store.overrideSelector(
searchMultipleSelector.selectSelectedDatasets, []
);
mockConeSearchSelectorSelectConeSearchByRoute = store.overrideSelector(
coneSearchSelector.selectConeSearchByRoute,''
);
mockConeSearchSelectorSelectConeSearch = store.overrideSelector(
coneSearchSelector.selectConeSearch,undefined
);
mockInstanceSelectorSelectorSelectInstanceByRouteName = store.overrideSelector(
instanceSelector.selectInstanceByRouteName,undefined
);
mockDatasetSelectorSelectAllConeSearchDatasets = store.overrideSelector(
datasetSelector.selectAllConeSearchDatasets,[]
);
});
it('should be created', () => {
expect(effects).toBeTruthy();
});
describe('initSearch$ effect', () => {
it('should dispatch the restartSearch action when dataset or cone search changed', () => {
mockSearchMultipleSelectorSelectPristine = store.overrideSelector(
searchMultipleSelector.selectPristine,false
);
mockConeSearchSelectorSelectConeSearchByRoute = store.overrideSelector(
coneSearchSelector.selectConeSearchByRoute,''
);
const action = searchMultipleActions.initSearch();
actions = hot('-a', { a: action });
const expected = cold('-(bc)', {
b: coneSearchActions.deleteConeSearch(),
c: searchMultipleActions.restartSearch()
});
expect(effects.initSearch$).toBeObservable(expected);
});
it('should not dispatch action when default form parameters already loaded or no dataset selected', () => {
mockSearchMultipleSelectorSelectPristine = store.overrideSelector(
searchMultipleSelector.selectPristine,false
);
mockConeSearchSelectorSelectConeSearchByRoute = store.overrideSelector(
coneSearchSelector.selectConeSearchByRoute,'1:2:3'
);
const action = searchMultipleActions.initSearch();
actions = hot('-a', { a: action });
const expected = cold('-b', { b: { type: '[No Action] Load Default Form Parameters' } });
expect(effects.initSearch$).toBeObservable(expected);
});
it('should dispatch a bunch of actions when page is reloaded with cone search in it', () => {
mockSearchMultipleSelectorSelectPristine = store.overrideSelector(
searchMultipleSelector.selectPristine,true
);
mockConeSearchSelectorSelectConeSearchByRoute = store.overrideSelector(
coneSearchSelector.selectConeSearchByRoute,'1:2:3'
);
mockSearchMultipleSelectorSelectSelectedDatasetsByRoute = store.overrideSelector(
searchMultipleSelector.selectSelectedDatasetsByRoute,''
);
mockInstanceSelectorSelectorSelectInstanceByRouteName = store.overrideSelector(
instanceSelector.selectInstanceByRouteName, {
name: 'myInstance',
label: 'My Instance',
description: 'My Instance description',
scientific_manager: 'M. Dupont',
instrument: 'Multiple',
wavelength_domain: 'Visible',
display: 10,
data_path: 'data/path',
files_path: 'files',
public: true,
portal_logo: 'logo.png',
design_color: 'green',
design_background_color: 'darker green',
design_logo: 'path/to/logo',
design_favicon: 'path/to/favicon',
navbar_background_color: '#F8F9FA',
navbar_border_bottom_color: '#DEE2E6',
navbar_color_href: '#000000',
footer_background_color: '#F8F9FA',
footer_border_top_color: '#DEE2E6',
footer_text_color: '#000000',
family_border_color: '#DFDFDF',
family_header_background_color: '#F7F7F7',
family_title_color: '#007BFF',
family_title_bold: false,
family_color: '#212529',
progress_bar_title: 'Dataset search',
progress_bar_title_color: '#000000',
progress_bar_subtitle: 'Select a dataset, add criteria, select output columns and display the result.',
progress_bar_subtitle_color: '#6C757D',
progress_bar_color: '#E9ECEF',
progress_bar_active_color: '#7AC29A',
progress_bar_circle_color: '#FFFFFF',
progress_bar_circle_icon_color: '#CCCCCC',
progress_bar_circle_icon_active_color: '#FFFFFF',
progress_bar_text_color: '#91B2BF',
samp_enabled: true,
back_to_portal: true,
user_menu_enabled: true,
search_by_criteria_allowed: true,
search_by_criteria_label: 'Search',
search_multiple_allowed: true,
search_multiple_label: 'Search multiple',
search_multiple_all_datasets_selected: false,
documentation_allowed: true,
documentation_label: 'Documentation',
nb_dataset_families: 1,
nb_datasets: 2
}
);
const coneSearch: ConeSearch = { ra: 1, dec: 2, radius: 3 };
const action = searchMultipleActions.initSearch();
actions = hot('-a', { a: action });
const expected = cold('-(bc)', {
b: searchMultipleActions.markAsDirty(),
c: coneSearchActions.addConeSearch({ coneSearch })
});
expect(effects.initSearch$).toBeObservable(expected);
});
it('should dispatch a bunch of actions when page is reloaded with selected datasets in it', () => {
mockSearchMultipleSelectorSelectPristine = store.overrideSelector(
searchMultipleSelector.selectPristine,true
);
mockConeSearchSelectorSelectConeSearchByRoute = store.overrideSelector(
coneSearchSelector.selectConeSearchByRoute,''
);
mockSearchMultipleSelectorSelectSelectedDatasetsByRoute = store.overrideSelector(
searchMultipleSelector.selectSelectedDatasetsByRoute,'d1;d2'
);
const selectedDatasets: string[] = ['d1', 'd2'];
const action = searchMultipleActions.initSearch();
actions = hot('-a', { a: action });
const expected = cold('-(bcd)', {
b: searchMultipleActions.markAsDirty(),
c: searchMultipleActions.updateSelectedDatasets({ selectedDatasets }),
d: searchMultipleActions.checkDatasets()
});
expect(effects.initSearch$).toBeObservable(expected);
});
it('should dispatch a bunch of actions when page is reloaded with default selected datasets', () => {
mockSearchMultipleSelectorSelectPristine = store.overrideSelector(
searchMultipleSelector.selectPristine,true
);
mockConeSearchSelectorSelectConeSearchByRoute = store.overrideSelector(
coneSearchSelector.selectConeSearchByRoute,''
);
mockSearchMultipleSelectorSelectSelectedDatasetsByRoute = store.overrideSelector(
searchMultipleSelector.selectSelectedDatasetsByRoute,''
);
mockInstanceSelectorSelectorSelectInstanceByRouteName = store.overrideSelector(
instanceSelector.selectInstanceByRouteName, {
name: 'myInstance',
label: 'My Instance',
description: 'My Instance description',
scientific_manager: 'M. Dupont',
instrument: 'Multiple',
wavelength_domain: 'Visible',
display: 10,
data_path: 'data/path',
files_path: 'files',
public: true,
portal_logo: 'logo.png',
design_color: 'green',
design_background_color: 'darker green',
design_logo: 'path/to/logo',
design_favicon: 'path/to/favicon',
navbar_background_color: '#F8F9FA',
navbar_border_bottom_color: '#DEE2E6',
navbar_color_href: '#000000',
footer_background_color: '#F8F9FA',
footer_border_top_color: '#DEE2E6',
footer_text_color: '#000000',
family_border_color: '#DFDFDF',
family_header_background_color: '#F7F7F7',
family_title_color: '#007BFF',
family_title_bold: false,
family_color: '#212529',
progress_bar_title: 'Dataset search',
progress_bar_title_color: '#000000',
progress_bar_subtitle: 'Select a dataset, add criteria, select output columns and display the result.',
progress_bar_subtitle_color: '#6C757D',
progress_bar_color: '#E9ECEF',
progress_bar_active_color: '#7AC29A',
progress_bar_circle_color: '#FFFFFF',
progress_bar_circle_icon_color: '#CCCCCC',
progress_bar_circle_icon_active_color: '#FFFFFF',
progress_bar_text_color: '#91B2BF',
samp_enabled: true,
back_to_portal: true,
user_menu_enabled: true,
search_by_criteria_allowed: true,
search_by_criteria_label: 'Search',
search_multiple_allowed: true,
search_multiple_label: 'Search multiple',
search_multiple_all_datasets_selected: true,
documentation_allowed: true,
documentation_label: 'Documentation',
nb_dataset_families: 1,
nb_datasets: 2
}
);
mockDatasetSelectorSelectAllConeSearchDatasets = store.overrideSelector(
datasetSelector.selectAllConeSearchDatasets, [
{
name: 'myDataset',
table_ref: '',
label: '',
description: '',
display: 1,
data_path: '',
public: true,
download_json: true,
download_csv: true,
download_ascii: true,
download_vo: true,
server_link_enabled: true,
datatable_enabled: true,
datatable_selectable_rows: true,
cone_search_config_id: 1,
id_database: 1,
id_dataset_family: 1,
full_data_path: ''
}
]
);
const selectedDatasets: string[] = ['myDataset'];
const action = searchMultipleActions.initSearch();
actions = hot('-a', { a: action });
const expected = cold('-(bc)', {
b: searchMultipleActions.markAsDirty(),
c: searchMultipleActions.updateSelectedDatasets({ selectedDatasets })
});
expect(effects.initSearch$).toBeObservable(expected);
});
});
describe('restartSearch$ effect', () => {
it('should dispatch the initSearch action', () => {
const action = searchMultipleActions.restartSearch();
const outcome = searchMultipleActions.initSearch();
actions = hot('-a', { a: action });
const expected = cold('-b', { b: outcome });
expect(effects.restartSearch$).toBeObservable(expected);
});
});
describe('retrieveDataLength$ effect', () => {
it('should dispatch the retrieveDataLengthSuccess action on success', () => {
mockSearchMultipleSelectorSelectSelectedDatasets = store.overrideSelector(
searchMultipleSelector.selectSelectedDatasets, ['myDataset']
);
mockConeSearchSelectorSelectConeSearch = store.overrideSelector(
coneSearchSelector.selectConeSearch, { ra: 1, dec: 2, radius: 3 }
);
const dataLength: SearchMultipleDatasetLength[] = [{ datasetName: 'myDataset', length: 1 }];
const action = searchMultipleActions.retrieveDataLength();
const outcome = searchMultipleActions.retrieveDataLengthSuccess({ dataLength });
actions = hot('-a', { a: action });
const response = cold('-b|', { b: [{ nb: 1 }] });
const expected = cold('---c', { c: outcome });
searchService.retrieveDataLength = jest.fn(() => response);
expect(effects.retrieveDataLength$).toBeObservable(expected);
});
it('should dispatch the retrieveDataLengthFail action on failure', () => {
mockSearchMultipleSelectorSelectSelectedDatasets = store.overrideSelector(
searchMultipleSelector.selectSelectedDatasets, ['myDataset']
);
mockConeSearchSelectorSelectConeSearch = store.overrideSelector(
coneSearchSelector.selectConeSearch, { ra: 1, dec: 2, radius: 3 }
);
const action = searchMultipleActions.retrieveDataLength();
const error = new Error();
const outcome = searchMultipleActions.retrieveDataLengthFail();
actions = hot('-a', { a: action });
const response = cold('-#|', {}, error);
const expected = cold('--b', { b: outcome });
searchService.retrieveDataLength = jest.fn(() => response);
expect(effects.retrieveDataLength$).toBeObservable(expected);
});
it('should pass correct query to the service', () => {
mockSearchMultipleSelectorSelectSelectedDatasets = store.overrideSelector(
searchMultipleSelector.selectSelectedDatasets, ['myDataset', 'myOtherDataset']
);
mockConeSearchSelectorSelectConeSearch = store.overrideSelector(
coneSearchSelector.selectConeSearch, { ra: 1, dec: 2, radius: 3 }
);
const dataLength: SearchMultipleDatasetLength[] = [
{ datasetName: 'myDataset', length: 1 },
{ datasetName: 'myOtherDataset', length: 1 }
];
jest.spyOn(searchService, 'retrieveDataLength');
const action = searchMultipleActions.retrieveDataLength();
const outcome = searchMultipleActions.retrieveDataLengthSuccess({ dataLength });
actions = hot('-a', { a: action });
const response = cold('-b|', { b: [{ nb: 1 }] });
const expected = cold('---c', { c: outcome });
searchService.retrieveDataLength = jest.fn(() => response);
expect(effects.retrieveDataLength$).toBeObservable(expected);
expect(searchService.retrieveDataLength).toHaveBeenCalledTimes(2);
expect(searchService.retrieveDataLength).toHaveBeenCalledWith('myDataset?a=count&cs=1:2:3');
expect(searchService.retrieveDataLength).toHaveBeenCalledWith('myOtherDataset?a=count&cs=1:2:3');
});
});
describe('retrieveDataLengthFail$ effect', () => {
it('should not dispatch', () => {
expect(metadata.retrieveDataLengthFail$).toEqual(
expect.objectContaining({ dispatch: false })
);
});
it('should display a error notification', () => {
const spy = jest.spyOn(toastr, 'error');
const action = searchMultipleActions.retrieveDataLengthFail();
actions = hot('a', { a: action });
const expected = cold('a', { a: action });
expect(effects.retrieveDataLengthFail$).toBeObservable(expected);
expect(spy).toHaveBeenCalledTimes(1);
expect(spy).toHaveBeenCalledWith('Loading Failed', 'The search multiple data length loading failed');
});
});
});