Skip to content
Snippets Groups Projects
search-multiple.effects.spec.ts 22.6 KiB
Newer Older
  • Learn to ignore specific revisions
  • 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';
    
    Tifenn Guillas's avatar
    Tifenn Guillas committed
    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',
    
    François Agneray's avatar
    François Agneray committed
                        description: 'My Instance description',
    
    François Agneray's avatar
    François Agneray committed
                        scientific_manager: 'M. Dupont',
                        instrument: 'Multiple',
                        wavelength_domain: 'Visible',
    
    François Agneray's avatar
    François Agneray committed
                        display: 10,
    
                        data_path: 'data/path',
    
    François Agneray's avatar
    François Agneray committed
                        files_path: 'files',
                        public: true,
    
    François Agneray's avatar
    François Agneray committed
                        portal_logo: 'logo.png',
                        design_color: 'green',
                        design_background_color: 'darker green',
                        design_logo: 'path/to/logo',
                        design_favicon: 'path/to/favicon',
    
    François Agneray's avatar
    François Agneray committed
                        navbar_background_color: '#F8F9FA',
                        navbar_border_bottom_color: '#DEE2E6',
                        navbar_color_href: '#000000',
    
    François Agneray's avatar
    François Agneray committed
                        navbar_font_family: 'Roboto, sans-serif',
                        navbar_sign_in_btn_color: '#28A745',
                        navbar_user_btn_color: '#7AC29A',
    
    François Agneray's avatar
    François Agneray committed
                        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_background_color: '#FFFFFF',
    
                        family_color: '#212529',
    
                        footer_logos: null,
    
                        progress_bar_title: 'Dataset search',
    
    François Agneray's avatar
    François Agneray committed
                        progress_bar_title_color: '#000000',
    
                        progress_bar_subtitle: 'Select a dataset, add criteria, select output columns and display the result.',
    
    François Agneray's avatar
    François Agneray committed
                        progress_bar_subtitle_color: '#6C757D',
    
                        progress_bar_step_dataset_title: 'Dataset selection',
                        progress_bar_step_criteria_title: 'Search criteria',
                        progress_bar_step_output_title: 'Output columns',
                        progress_bar_step_result_title: 'Result table',
    
                        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',
    
    François Agneray's avatar
    François Agneray committed
                        result_header_background_color: '#E9ECEF',
                        result_header_text_color: '#000000',
                        result_header_btn_color: '#007BFF',
                        result_header_btn_hover_color: '#0069D9',
                        result_header_btn_text_color: '#FFFFFF',
    
    François Agneray's avatar
    François Agneray committed
                        result_datatable_bordered: true,
                        result_datatable_border_color: '#DEE2E6',
                        result_datatable_header_background_color: '#FFFFFF',
                        result_datatable_header_text_color: '#000000',
                        result_datatable_rows_background_color: '#FFFFFF',
                        result_datatable_rows_text_color: '#000000',
                        result_datatable_sorted_color: '#C5C5C5',
                        result_datatable_sorted_active_color: '#000000',
                        result_datatable_link_color: '#007BFF',
                        result_datatable_link_hover_color: '#0056B3',
                        result_datatable_rows_selected_color: '#7AC29A',
    
                        samp_enabled: true,
    
    François Agneray's avatar
    François Agneray committed
                        back_to_portal: true,
    
    François Agneray's avatar
    François Agneray committed
                        user_menu_enabled: true,
    
    François Agneray's avatar
    François Agneray committed
                        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',
    
    François Agneray's avatar
    François Agneray committed
                        description: 'My Instance description',
    
    François Agneray's avatar
    François Agneray committed
                        scientific_manager: 'M. Dupont',
                        instrument: 'Multiple',
                        wavelength_domain: 'Visible',
    
    François Agneray's avatar
    François Agneray committed
                        display: 10,
    
                        data_path: 'data/path',
    
    François Agneray's avatar
    François Agneray committed
                        files_path: 'files',
                        public: true,
    
    François Agneray's avatar
    François Agneray committed
                        portal_logo: 'logo.png',
                        design_color: 'green',
                        design_background_color: 'darker green',
                        design_logo: 'path/to/logo',
                        design_favicon: 'path/to/favicon',
    
    François Agneray's avatar
    François Agneray committed
                        navbar_background_color: '#F8F9FA',
                        navbar_border_bottom_color: '#DEE2E6',
                        navbar_color_href: '#000000',
    
    François Agneray's avatar
    François Agneray committed
                        navbar_font_family: 'Roboto, sans-serif',
                        navbar_sign_in_btn_color: '#28A745',
                        navbar_user_btn_color: '#7AC29A',
    
    François Agneray's avatar
    François Agneray committed
                        footer_background_color: '#F8F9FA',
                        footer_border_top_color: '#DEE2E6',
                        footer_text_color: '#000000',
    
                        footer_logos: null,
    
                        family_border_color: '#DFDFDF',
                        family_header_background_color: '#F7F7F7',
                        family_title_color: '#007BFF',
                        family_title_bold: false,
    
                        family_background_color: '#FFFFFF',
    
                        family_color: '#212529',
    
                        progress_bar_title: 'Dataset search',
    
    François Agneray's avatar
    François Agneray committed
                        progress_bar_title_color: '#000000',
    
                        progress_bar_subtitle: 'Select a dataset, add criteria, select output columns and display the result.',
    
    François Agneray's avatar
    François Agneray committed
                        progress_bar_subtitle_color: '#6C757D',
    
                        progress_bar_step_dataset_title: 'Dataset selection',
                        progress_bar_step_criteria_title: 'Search criteria',
                        progress_bar_step_output_title: 'Output columns',
                        progress_bar_step_result_title: 'Result table',
    
                        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',
    
    François Agneray's avatar
    François Agneray committed
                        result_header_background_color: '#E9ECEF',
                        result_header_text_color: '#000000',
                        result_header_btn_color: '#007BFF',
                        result_header_btn_hover_color: '#0069D9',
                        result_header_btn_text_color: '#FFFFFF',
    
    François Agneray's avatar
    François Agneray committed
                        result_datatable_bordered: true,
                        result_datatable_border_color: '#DEE2E6',
                        result_datatable_header_background_color: '#FFFFFF',
                        result_datatable_header_text_color: '#000000',
                        result_datatable_rows_background_color: '#FFFFFF',
                        result_datatable_rows_text_color: '#000000',
                        result_datatable_sorted_color: '#C5C5C5',
                        result_datatable_sorted_active_color: '#000000',
                        result_datatable_link_color: '#007BFF',
                        result_datatable_link_hover_color: '#0056B3',
                        result_datatable_rows_selected_color: '#7AC29A',
    
                        samp_enabled: true,
    
    François Agneray's avatar
    François Agneray committed
                        back_to_portal: true,
    
    François Agneray's avatar
    François Agneray committed
                        user_menu_enabled: true,
    
    François Agneray's avatar
    François Agneray committed
                        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,
    
    François Agneray's avatar
    François Agneray committed
                            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');
            });
        });
    });