Skip to content
Snippets Groups Projects
cone-search.effects.spec.ts 5.78 KiB
import { TestBed } from '@angular/core/testing';

import { provideMockActions } from '@ngrx/effects/testing';
import { EffectsMetadata, getEffectsMetadata } from '@ngrx/effects';
import { Observable } from 'rxjs';
import { cold, hot } from 'jasmine-marbles';
import { ToastrService } from 'ngx-toastr';

import { ConeSearchEffects } from './cone-search.effects';
import { ConeSearchService } from '../services/cone-search.service';
import * as coneSearchActions from '../actions/cone-search.actions';
import { Resolver } from '../models';

describe('ConeSearchEffects', () => {
    let actions = new Observable();
    let effects: ConeSearchEffects;
    let metadata: EffectsMetadata<ConeSearchEffects>;
    let coneSearchService: ConeSearchService;
    let toastr: ToastrService;

    beforeEach(() => {
        TestBed.configureTestingModule({
            providers: [
                ConeSearchEffects,
                { provide: ConeSearchService, useValue: { retrieveCoordinates: jest.fn() }},
                { provide: ToastrService, useValue: { error: jest.fn() } },
                provideMockActions(() => actions)
            ]
        }).compileComponents();
        effects = TestBed.inject(ConeSearchEffects);
        metadata = getEffectsMetadata(effects);
        coneSearchService = TestBed.inject(ConeSearchService);
        toastr = TestBed.inject(ToastrService);
    });

    it('should be created', () => {
        expect(effects).toBeTruthy();
    });

    describe('retrieveCoordinates$ effect', () => {
        it('should dispatch the retrieveCoordinatesSuccess action on success', () => {
            const name: string = 'myObjectName';
            const apiResponse = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<Sesame xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
                " xsi:noNamespaceSchemaLocation=\"http://vizier.u-strasbg.fr/xml/sesame_4x.xsd\">\n" +
                "<Target option=\"NSV\">\n" +
                "  <name>myObjectName</name>\n" +
                "  <!-- Q1525761 #1 -->\n" +
                "  <Resolver name=\"N=NED\"><!--delay: 989ms [2] -->\n" +
                "    <otype>!*</otype>\n" +
                "    <jpos>06:45:09.24 -16:42:47.3</jpos>\n" +
                "    <jradeg>1</jradeg>\n" +
                "    <jdedeg>2</jdedeg>\n" +
                "    <refPos>2007A&amp;A...474..653V</refPos>\n" +
                "    <errRAmas>50</errRAmas><errDEmas>500</errDEmas>\n" +
                "    <oname>Sirius</oname>\n" +
                "  </Resolver>\n" +
                "</Target>\n" +
                "</Sesame>\n" +
                "<!--- ====Done (2021-Aug-04,15:00:56z)==== -->\n";
            const resolver: Resolver = { name: 'myObjectName', ra: 1, dec: 2 };
            const action = coneSearchActions.retrieveCoordinates({ name });
            const outcome = coneSearchActions.retrieveCoordinatesSuccess({ resolver });

            actions = hot('-a', { a: action });
            const response = cold('-a|', { a: apiResponse });
            const expected = cold('--b', { b: outcome });
            coneSearchService.retrieveCoordinates = jest.fn(() => response);

            expect(effects.retrieveCoordinates$).toBeObservable(expected);
        });

        it('should dispatch the retrieveCoordinatesFail action on HTTP failure', () => {
            const name: string = 'myObjectName';
            const action = coneSearchActions.retrieveCoordinates({ name });
            const error = new Error();
            const outcome = coneSearchActions.retrieveCoordinatesFail();

            actions = hot('-a', { a: action });
            const response = cold('-#|', {}, error);
            const expected = cold('--b', { b: outcome });
            coneSearchService.retrieveCoordinates = jest.fn(() => response);

            expect(effects.retrieveCoordinates$).toBeObservable(expected);
        });

        it('should dispatch the retrieveCoordinatesFail action on process failure', () => {
            const name: string = 'myObjectName';
            const apiResponse = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
                "<Sesame xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n" +
                " xsi:noNamespaceSchemaLocation=\"http://vizier.u-strasbg.fr/xml/sesame_4x.xsd\">\n" +
                "<Target option=\"NSV\">\n" +
                "  <name>myObjectName</name>\n" +
                "  <!-- Q1525761 #1 -->\n" +
                "</Target>\n" +
                "</Sesame>\n" +
                "<!--- ====Done (2021-Aug-04,15:00:56z)==== -->\n";
            const action = coneSearchActions.retrieveCoordinates({ name });
            const outcome = coneSearchActions.retrieveCoordinatesFail();

            actions = hot('-a', { a: action });
            const response = cold('-a|', { a: apiResponse });
            const expected = cold('--b', { b: outcome });
            coneSearchService.retrieveCoordinates = jest.fn(() => response);

            expect(effects.retrieveCoordinates$).toBeObservable(expected);
        });
    });

    describe('retrieveCoordinatesFail$ effect', () => {
        it('should not dispatch', () => {
            expect(metadata.retrieveCoordinatesFail$).toEqual(
                expect.objectContaining({ dispatch: false })
            );
        });

        it('should display an error notification', () => {
            const spy = jest.spyOn(toastr, 'error');
            const action = coneSearchActions.retrieveCoordinatesFail();

            actions = hot('a', { a: action });
            const expected = cold('a', { a: action });

            expect(effects.retrieveCoordinatesFail$).toBeObservable(expected);
            expect(spy).toHaveBeenCalledTimes(1);
            expect(spy).toHaveBeenCalledWith('Failure to retrieve coordinates', 'The coordinates could not be retrieved');
        });
    });
});