Skip to content
Snippets Groups Projects
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');
        });
    });
});