import { Mock } from 'ts-mockery'; import { GetVisitsOverviewAction, VisitsOverview, loadVisitsOverview as loadVisitsOverviewCreator, visitsOverviewReducerCreator, } from '../../../src/visits/reducers/visitsOverview'; import { createNewVisits, CreateVisitsAction } from '../../../src/visits/reducers/visitCreation'; import { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient'; import { ShlinkVisitsOverview } from '../../../src/api/types'; import { ShlinkState } from '../../../src/container/types'; import { CreateVisit, OrphanVisit, Visit } from '../../../src/visits/types'; describe('visitsOverviewReducer', () => { const getVisitsOverview = jest.fn(); const buildApiClientMock = () => Mock.of({ getVisitsOverview }); const loadVisitsOverview = loadVisitsOverviewCreator(buildApiClientMock); const { reducer } = visitsOverviewReducerCreator(loadVisitsOverview); beforeEach(jest.clearAllMocks); describe('reducer', () => { const action = (type: string) => Mock.of({ type }) as GetVisitsOverviewAction & CreateVisitsAction; const state = (payload: Partial = {}) => Mock.of(payload); it('returns loading on GET_OVERVIEW_START', () => { const { loading } = reducer( state({ loading: false, error: false }), action(loadVisitsOverview.pending.toString()), ); expect(loading).toEqual(true); }); it('stops loading and returns error on GET_OVERVIEW_ERROR', () => { const { loading, error } = reducer( state({ loading: true, error: false }), action(loadVisitsOverview.rejected.toString()), ); expect(loading).toEqual(false); expect(error).toEqual(true); }); it('return visits overview on GET_OVERVIEW', () => { const { loading, error, visitsCount } = reducer(state({ loading: true, error: false }), { type: loadVisitsOverview.fulfilled.toString(), payload: { visitsCount: 100 }, }); expect(loading).toEqual(false); expect(error).toEqual(false); expect(visitsCount).toEqual(100); }); it.each([ [50, 53], [0, 3], [undefined, 3], ])('returns updated amounts on CREATE_VISITS', (providedOrphanVisitsCount, expectedOrphanVisitsCount) => { const { visitsCount, orphanVisitsCount } = reducer( state({ visitsCount: 100, orphanVisitsCount: providedOrphanVisitsCount }), { type: createNewVisits.toString(), payload: { createdVisits: [ Mock.of({ visit: Mock.all() }), Mock.of({ visit: Mock.all() }), Mock.of({ visit: Mock.of({ visitedUrl: '' }), }), Mock.of({ visit: Mock.of({ visitedUrl: '' }), }), Mock.of({ visit: Mock.of({ visitedUrl: '' }), }), ], }, } as unknown as GetVisitsOverviewAction & CreateVisitsAction, ); expect(visitsCount).toEqual(102); expect(orphanVisitsCount).toEqual(expectedOrphanVisitsCount); }); }); describe('loadVisitsOverview', () => { const dispatchMock = jest.fn(); const getState = () => Mock.of(); beforeEach(() => dispatchMock.mockReset()); it('dispatches start and error when promise is rejected', async () => { getVisitsOverview.mockRejectedValue(undefined); await loadVisitsOverview()(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(2); expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: loadVisitsOverview.pending.toString(), })); expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: loadVisitsOverview.rejected.toString(), })); expect(getVisitsOverview).toHaveBeenCalledTimes(1); }); it('dispatches start and success when promise is resolved', async () => { const resolvedOverview = Mock.of({ visitsCount: 50 }); getVisitsOverview.mockResolvedValue(resolvedOverview); await loadVisitsOverview()(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(2); expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: loadVisitsOverview.pending.toString(), })); expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: loadVisitsOverview.fulfilled.toString(), payload: { visitsCount: 50 }, })); expect(getVisitsOverview).toHaveBeenCalledTimes(1); }); }); });