diff --git a/src/short-urls/reducers/shortUrlCreation.ts b/src/short-urls/reducers/shortUrlCreation.ts index c68c3a28..2fa080ab 100644 --- a/src/short-urls/reducers/shortUrlCreation.ts +++ b/src/short-urls/reducers/shortUrlCreation.ts @@ -38,10 +38,7 @@ const initialState: ShortUrlCreation = { export const createShortUrl = (buildShlinkApiClient: ShlinkApiClientBuilder) => createAsyncThunk( `${REDUCER_PREFIX}/createShortUrl`, - (data: ShortUrlData, { getState }): Promise => { - const { createShortUrl: shlinkCreateShortUrl } = buildShlinkApiClient(getState); - return shlinkCreateShortUrl(data); - }, + (data: ShortUrlData, { getState }): Promise => buildShlinkApiClient(getState).createShortUrl(data), ); export const shortUrlCreationReducerCreator = (createShortUrlThunk: ReturnType) => { diff --git a/test/mercure/reducers/mercureInfo.test.ts b/test/mercure/reducers/mercureInfo.test.ts index f76c5f99..27be5710 100644 --- a/test/mercure/reducers/mercureInfo.test.ts +++ b/test/mercure/reducers/mercureInfo.test.ts @@ -16,21 +16,21 @@ describe('mercureInfoReducer', () => { describe('reducer', () => { it('returns loading on GET_MERCURE_INFO_START', () => { - expect(reducer(undefined, { type: loadMercureInfo.pending.toString() })).toEqual({ + expect(reducer(undefined, loadMercureInfo.pending(''))).toEqual({ loading: true, error: false, }); }); it('returns error on GET_MERCURE_INFO_ERROR', () => { - expect(reducer(undefined, { type: loadMercureInfo.rejected.toString() })).toEqual({ + expect(reducer(undefined, loadMercureInfo.rejected(null, ''))).toEqual({ loading: false, error: true, }); }); it('returns mercure info on GET_MERCURE_INFO', () => { - expect(reducer(undefined, { type: loadMercureInfo.fulfilled.toString(), payload: mercureInfo })).toEqual( + expect(reducer(undefined, loadMercureInfo.fulfilled(mercureInfo, ''))).toEqual( expect.objectContaining({ ...mercureInfo, loading: false, error: false }), ); }); @@ -52,11 +52,8 @@ describe('mercureInfoReducer', () => { expect(getMercureInfo).not.toHaveBeenCalled(); expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: loadMercureInfo.pending.toString(), - })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: loadMercureInfo.rejected.toString(), + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ + error: new Error('Real time updates not enabled'), })); }); @@ -68,31 +65,7 @@ describe('mercureInfoReducer', () => { expect(getMercureInfo).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: loadMercureInfo.pending.toString(), - })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: loadMercureInfo.fulfilled.toString(), - payload: mercureInfo, - })); - }); - - it('throws error on failure', async () => { - const error = 'Error'; - const getState = createGetStateMock(true); - - getMercureInfo.mockRejectedValue(error); - - await loadMercureInfo()(dispatch, getState, {}); - - expect(getMercureInfo).toHaveBeenCalledTimes(1); - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: loadMercureInfo.pending.toString(), - })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: loadMercureInfo.rejected.toString(), - })); + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: mercureInfo })); }); }); }); diff --git a/test/servers/reducers/remoteServers.test.ts b/test/servers/reducers/remoteServers.test.ts index 54dd80a4..37b493bb 100644 --- a/test/servers/reducers/remoteServers.test.ts +++ b/test/servers/reducers/remoteServers.test.ts @@ -1,7 +1,6 @@ import { Mock } from 'ts-mockery'; import type { HttpClient } from '../../../src/common/services/HttpClient'; import { fetchServers } from '../../../src/servers/reducers/remoteServers'; -import { createServers } from '../../../src/servers/reducers/servers'; describe('remoteServersReducer', () => { afterEach(jest.clearAllMocks); @@ -84,13 +83,9 @@ describe('remoteServersReducer', () => { await doFetchServers()(dispatch, jest.fn(), {}); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: doFetchServers.pending.toString(), - })); - expect(dispatch).toHaveBeenNthCalledWith(2, { type: createServers.toString(), payload: expectedNewServers }); - expect(dispatch).toHaveBeenNthCalledWith(3, expect.objectContaining({ - type: doFetchServers.fulfilled.toString(), - })); + expect(dispatch).toHaveBeenCalledTimes(3); + expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ payload: expectedNewServers })); + expect(dispatch).toHaveBeenNthCalledWith(3, expect.objectContaining({ payload: undefined })); expect(fetchJson).toHaveBeenCalledTimes(1); }); }); diff --git a/test/servers/reducers/selectedServer.test.ts b/test/servers/reducers/selectedServer.test.ts index 0a4796d2..664d69d8 100644 --- a/test/servers/reducers/selectedServer.test.ts +++ b/test/servers/reducers/selectedServer.test.ts @@ -23,18 +23,12 @@ describe('selectedServerReducer', () => { describe('reducer', () => { it('returns default when action is RESET_SELECTED_SERVER', () => - expect(reducer(null, { type: resetSelectedServer.toString(), payload: null })).toBeNull()); + expect(reducer(null, resetSelectedServer())).toBeNull()); it('returns selected server when action is SELECT_SERVER', () => { const payload = Mock.of({ id: 'abc123' }); - expect(reducer(null, { type: selectServer.fulfilled.toString(), payload })).toEqual(payload); - }); - }); - - describe('resetSelectedServer', () => { - it('returns proper action', () => { - expect(resetSelectedServer()).toEqual({ type: resetSelectedServer.toString() }); + expect(reducer(null, selectServer.fulfilled(payload, '', ''))).toEqual(payload); }); }); @@ -63,23 +57,10 @@ describe('selectedServerReducer', () => { await selectServer(id)(dispatch, getState, {}); - expect(dispatch).toHaveBeenCalledTimes(3); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: selectServer.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: resetSelectedServer.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(3, expect.objectContaining({ - type: selectServer.fulfilled.toString(), - payload: expectedSelectedServer, - })); - }); - - it('invokes dependencies', async () => { - const id = uuid(); - const getState = createGetStateMock(id); - - await selectServer(id)(jest.fn(), getState, {}); - expect(getState).toHaveBeenCalledTimes(1); expect(buildApiClient).toHaveBeenCalledTimes(1); + expect(dispatch).toHaveBeenCalledTimes(3); // "Pending", "reset" and "fulfilled" + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: expectedSelectedServer })); }); it('dispatches error when health endpoint fails', async () => { @@ -92,10 +73,7 @@ describe('selectedServerReducer', () => { await selectServer(id)(dispatch, getState, {}); expect(health).toHaveBeenCalled(); - expect(dispatch).toHaveBeenNthCalledWith(3, expect.objectContaining({ - type: selectServer.fulfilled.toString(), - payload: expectedSelectedServer, - })); + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: expectedSelectedServer })); }); it('dispatches error when server is not found', async () => { @@ -107,10 +85,7 @@ describe('selectedServerReducer', () => { expect(getState).toHaveBeenCalled(); expect(health).not.toHaveBeenCalled(); - expect(dispatch).toHaveBeenNthCalledWith(3, expect.objectContaining({ - type: selectServer.fulfilled.toString(), - payload: expectedSelectedServer, - })); + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: expectedSelectedServer })); }); }); diff --git a/test/servers/reducers/servers.test.ts b/test/servers/reducers/servers.test.ts index 188f0678..d385eeaf 100644 --- a/test/servers/reducers/servers.test.ts +++ b/test/servers/reducers/servers.test.ts @@ -1,6 +1,6 @@ import { dissoc, values } from 'ramda'; import { Mock } from 'ts-mockery'; -import type { RegularServer } from '../../../src/servers/data'; +import type { RegularServer, ServerWithId } from '../../../src/servers/data'; import { createServers, deleteServer, @@ -19,38 +19,24 @@ describe('serversReducer', () => { describe('reducer', () => { it('returns edited server when action is EDIT_SERVER', () => - expect(serversReducer(list, { - type: editServer.toString(), - payload: { serverId: 'abc123', serverData: { foo: 'foo' } }, - })).toEqual({ - abc123: { id: 'abc123', foo: 'foo' }, + expect(serversReducer(list, editServer('abc123', { name: 'foo' }))).toEqual({ + abc123: { id: 'abc123', name: 'foo' }, def456: { id: 'def456' }, })); it('returns as it is when action is EDIT_SERVER and server does not exist', () => - expect(serversReducer(list, { - type: editServer.toString(), - payload: { serverId: 'invalid', serverData: { foo: 'foo' } }, - })).toEqual({ + expect(serversReducer(list, editServer('invalid', { name: 'foo' }))).toEqual({ abc123: { id: 'abc123' }, def456: { id: 'def456' }, })); it('removes server when action is DELETE_SERVER', () => - expect(serversReducer(list, { - type: deleteServer.toString(), - payload: { id: 'abc123' }, - })).toEqual({ + expect(serversReducer(list, deleteServer(Mock.of({ id: 'abc123' })))).toEqual({ def456: { id: 'def456' }, })); it('appends server when action is CREATE_SERVERS', () => - expect(serversReducer(list, { - type: createServers.toString(), - payload: { - ghi789: { id: 'ghi789' }, - }, - })).toEqual({ + expect(serversReducer(list, createServers([Mock.of({ id: 'ghi789' })]))).toEqual({ abc123: { id: 'abc123' }, def456: { id: 'def456' }, ghi789: { id: 'ghi789' }, @@ -60,10 +46,7 @@ describe('serversReducer', () => { [true], [false], ])('returns state as it is when trying to set auto-connect on invalid server', (autoConnect) => - expect(serversReducer(list, { - type: setAutoConnect.toString(), - payload: { serverId: 'invalid', autoConnect }, - })).toEqual({ + expect(serversReducer(list, setAutoConnect(Mock.of({ id: 'invalid' }), autoConnect))).toEqual({ abc123: { id: 'abc123' }, def456: { id: 'def456' }, })); @@ -74,10 +57,10 @@ describe('serversReducer', () => { abc123: { ...list.abc123, autoConnect: true }, }; - expect(serversReducer(listWithDisabledAutoConnect, { - type: setAutoConnect.toString(), - payload: { serverId: 'abc123', autoConnect: false }, - })).toEqual({ + expect(serversReducer( + listWithDisabledAutoConnect, + setAutoConnect(Mock.of({ id: 'abc123' }), false), + )).toEqual({ abc123: { id: 'abc123', autoConnect: false }, def456: { id: 'def456' }, }); @@ -89,10 +72,10 @@ describe('serversReducer', () => { abc123: { ...list.abc123, autoConnect: true }, }; - expect(serversReducer(listWithEnabledAutoConnect, { - type: setAutoConnect.toString(), - payload: { serverId: 'def456', autoConnect: true }, - })).toEqual({ + expect(serversReducer( + listWithEnabledAutoConnect, + setAutoConnect(Mock.of({ id: 'def456' }), true), + )).toEqual({ abc123: { id: 'abc123', autoConnect: false }, def456: { id: 'def456', autoConnect: true }, }); @@ -103,33 +86,27 @@ describe('serversReducer', () => { describe('editServer', () => { it('returns expected action', () => { const serverData = { name: 'edited' }; - const result = editServer('123', serverData); + const { payload } = editServer('123', serverData); - expect(result).toEqual({ - type: editServer.toString(), - payload: { serverId: '123', serverData }, - }); + expect(payload).toEqual({ serverId: '123', serverData }); }); }); describe('deleteServer', () => { it('returns expected action', () => { const serverToDelete = Mock.of({ id: 'abc123' }); - const result = deleteServer(serverToDelete); + const { payload } = deleteServer(serverToDelete); - expect(result).toEqual({ - type: deleteServer.toString(), - payload: { id: 'abc123' }, - }); + expect(payload).toEqual({ id: 'abc123' }); }); }); describe('createServers', () => { it('returns expected action', () => { const newServers = values(list); - const result = createServers(newServers); + const { payload } = createServers(newServers); - expect(result).toEqual(expect.objectContaining({ type: createServers.toString() })); + expect(payload).toEqual(list); }); it('generates an id for every provided server if they do not have it', () => { @@ -146,12 +123,9 @@ describe('serversReducer', () => { [false], ])('returns expected action', (autoConnect) => { const serverToEdit = Mock.of({ id: 'abc123' }); - const result = setAutoConnect(serverToEdit, autoConnect); + const { payload } = setAutoConnect(serverToEdit, autoConnect); - expect(result).toEqual({ - type: setAutoConnect.toString(), - payload: { serverId: 'abc123', autoConnect }, - }); + expect(payload).toEqual({ serverId: 'abc123', autoConnect }); }); }); }); diff --git a/test/settings/reducers/settings.test.ts b/test/settings/reducers/settings.test.ts index 6199b7ce..c9f94470 100644 --- a/test/settings/reducers/settings.test.ts +++ b/test/settings/reducers/settings.test.ts @@ -20,86 +20,56 @@ describe('settingsReducer', () => { describe('reducer', () => { it('returns realTimeUpdates when action is SET_SETTINGS', () => { - expect( - settingsReducer(undefined, { type: toggleRealTimeUpdates.toString(), payload: { realTimeUpdates } }), - ).toEqual(settings); + expect(settingsReducer(undefined, toggleRealTimeUpdates(realTimeUpdates.enabled))).toEqual(settings); }); }); describe('toggleRealTimeUpdates', () => { it.each([[true], [false]])('updates settings with provided value and then loads updates again', (enabled) => { - const result = toggleRealTimeUpdates(enabled); - - expect(result).toEqual({ - type: toggleRealTimeUpdates.toString(), - payload: { realTimeUpdates: { enabled } }, - }); + const { payload } = toggleRealTimeUpdates(enabled); + expect(payload).toEqual({ realTimeUpdates: { enabled } }); }); }); describe('setRealTimeUpdatesInterval', () => { it.each([[0], [1], [2], [10]])('updates settings with provided value and then loads updates again', (interval) => { - const result = setRealTimeUpdatesInterval(interval); - - expect(result).toEqual({ - type: setRealTimeUpdatesInterval.toString(), - payload: { realTimeUpdates: { interval } }, - }); + const { payload } = setRealTimeUpdatesInterval(interval); + expect(payload).toEqual({ realTimeUpdates: { interval } }); }); }); describe('setShortUrlCreationSettings', () => { it('creates action to set shortUrlCreation settings', () => { - const result = setShortUrlCreationSettings({ validateUrls: true }); - - expect(result).toEqual({ - type: setShortUrlCreationSettings.toString(), - payload: { shortUrlCreation: { validateUrls: true } }, - }); + const { payload } = setShortUrlCreationSettings({ validateUrls: true }); + expect(payload).toEqual({ shortUrlCreation: { validateUrls: true } }); }); }); describe('setUiSettings', () => { it('creates action to set ui settings', () => { - const result = setUiSettings({ theme: 'dark' }); - - expect(result).toEqual({ - type: setUiSettings.toString(), - payload: { ui: { theme: 'dark' } }, - }); + const { payload } = setUiSettings({ theme: 'dark' }); + expect(payload).toEqual({ ui: { theme: 'dark' } }); }); }); describe('setVisitsSettings', () => { it('creates action to set visits settings', () => { - const result = setVisitsSettings({ defaultInterval: 'last180Days' }); - - expect(result).toEqual({ - type: setVisitsSettings.toString(), - payload: { visits: { defaultInterval: 'last180Days' } }, - }); + const { payload } = setVisitsSettings({ defaultInterval: 'last180Days' }); + expect(payload).toEqual({ visits: { defaultInterval: 'last180Days' } }); }); }); describe('setTagsSettings', () => { it('creates action to set tags settings', () => { - const result = setTagsSettings({ defaultMode: 'list' }); - - expect(result).toEqual({ - type: setTagsSettings.toString(), - payload: { tags: { defaultMode: 'list' } }, - }); + const { payload } = setTagsSettings({ defaultMode: 'list' }); + expect(payload).toEqual({ tags: { defaultMode: 'list' } }); }); }); describe('setShortUrlsListSettings', () => { it('creates action to set short URLs list settings', () => { - const result = setShortUrlsListSettings({ defaultOrdering: DEFAULT_SHORT_URLS_ORDERING }); - - expect(result).toEqual({ - type: setShortUrlsListSettings.toString(), - payload: { shortUrlsList: { defaultOrdering: DEFAULT_SHORT_URLS_ORDERING } }, - }); + const { payload } = setShortUrlsListSettings({ defaultOrdering: DEFAULT_SHORT_URLS_ORDERING }); + expect(payload).toEqual({ shortUrlsList: { defaultOrdering: DEFAULT_SHORT_URLS_ORDERING } }); }); }); }); diff --git a/test/short-urls/reducers/shortUrlCreation.test.ts b/test/short-urls/reducers/shortUrlCreation.test.ts index 1f8cf99b..deeda1fe 100644 --- a/test/short-urls/reducers/shortUrlCreation.test.ts +++ b/test/short-urls/reducers/shortUrlCreation.test.ts @@ -1,9 +1,7 @@ import { Mock } from 'ts-mockery'; import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient'; import type { ShlinkState } from '../../../src/container/types'; -import type { ShortUrl } from '../../../src/short-urls/data'; -import type { - CreateShortUrlAction } from '../../../src/short-urls/reducers/shortUrlCreation'; +import type { ShortUrl, ShortUrlData } from '../../../src/short-urls/data'; import { createShortUrl as createShortUrlCreator, shortUrlCreationReducerCreator, @@ -19,12 +17,8 @@ describe('shortUrlCreationReducer', () => { afterEach(jest.resetAllMocks); describe('reducer', () => { - const action = (type: string, args: Partial = {}) => Mock.of( - { type, ...args }, - ); - it('returns loading on CREATE_SHORT_URL_START', () => { - expect(reducer(undefined, action(createShortUrl.pending.toString()))).toEqual({ + expect(reducer(undefined, createShortUrl.pending('', Mock.all()))).toEqual({ saving: true, saved: false, error: false, @@ -32,7 +26,7 @@ describe('shortUrlCreationReducer', () => { }); it('returns error on CREATE_SHORT_URL_ERROR', () => { - expect(reducer(undefined, action(createShortUrl.rejected.toString()))).toEqual({ + expect(reducer(undefined, createShortUrl.rejected(null, '', Mock.all()))).toEqual({ saving: false, saved: false, error: true, @@ -40,7 +34,7 @@ describe('shortUrlCreationReducer', () => { }); it('returns result on CREATE_SHORT_URL', () => { - expect(reducer(undefined, action(createShortUrl.fulfilled.toString(), { payload: shortUrl }))).toEqual({ + expect(reducer(undefined, createShortUrl.fulfilled(shortUrl, '', Mock.all()))).toEqual({ result: shortUrl, saving: false, saved: true, @@ -49,7 +43,7 @@ describe('shortUrlCreationReducer', () => { }); it('returns default state on RESET_CREATE_SHORT_URL', () => { - expect(reducer(undefined, action(resetCreateShortUrl.toString()))).toEqual({ + expect(reducer(undefined, resetCreateShortUrl())).toEqual({ saving: false, saved: false, error: false, @@ -57,10 +51,6 @@ describe('shortUrlCreationReducer', () => { }); }); - describe('resetCreateShortUrl', () => { - it('returns proper action', () => expect(resetCreateShortUrl()).toEqual({ type: resetCreateShortUrl.toString() })); - }); - describe('createShortUrl', () => { const dispatch = jest.fn(); const getState = () => Mock.all(); @@ -71,30 +61,7 @@ describe('shortUrlCreationReducer', () => { expect(createShortUrlCall).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: createShortUrl.pending.toString(), - })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: createShortUrl.fulfilled.toString(), - payload: shortUrl, - })); - }); - - it('throws on error', async () => { - const error = new Error('Error message'); - createShortUrlCall.mockRejectedValue(error); - - await createShortUrl({ longUrl: 'foo' })(dispatch, getState, {}); - - expect(createShortUrlCall).toHaveBeenCalledTimes(1); - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: createShortUrl.pending.toString(), - })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: createShortUrl.rejected.toString(), - error: expect.objectContaining({ message: 'Error message' }), - })); + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: shortUrl })); }); }); }); diff --git a/test/short-urls/reducers/shortUrlDeletion.test.ts b/test/short-urls/reducers/shortUrlDeletion.test.ts index d91476f3..1db369a6 100644 --- a/test/short-urls/reducers/shortUrlDeletion.test.ts +++ b/test/short-urls/reducers/shortUrlDeletion.test.ts @@ -16,7 +16,7 @@ describe('shortUrlDeletionReducer', () => { describe('reducer', () => { it('returns loading on DELETE_SHORT_URL_START', () => - expect(reducer(undefined, { type: deleteShortUrl.pending.toString() })).toEqual({ + expect(reducer(undefined, deleteShortUrl.pending('', { shortCode: '' }))).toEqual({ shortCode: '', loading: true, error: false, @@ -24,7 +24,7 @@ describe('shortUrlDeletionReducer', () => { })); it('returns default on RESET_DELETE_SHORT_URL', () => - expect(reducer(undefined, { type: resetDeleteShortUrl.toString() })).toEqual({ + expect(reducer(undefined, resetDeleteShortUrl())).toEqual({ shortCode: '', loading: false, error: false, @@ -32,10 +32,7 @@ describe('shortUrlDeletionReducer', () => { })); it('returns shortCode on SHORT_URL_DELETED', () => - expect(reducer(undefined, { - type: deleteShortUrl.fulfilled.toString(), - payload: { shortCode: 'foo' }, - })).toEqual({ + expect(reducer(undefined, deleteShortUrl.fulfilled({ shortCode: 'foo' }, '', { shortCode: 'foo' }))).toEqual({ shortCode: 'foo', loading: false, error: false, @@ -44,9 +41,9 @@ describe('shortUrlDeletionReducer', () => { it('returns errorData on DELETE_SHORT_URL_ERROR', () => { const errorData = Mock.of({ type: 'bar', detail: 'detail', title: 'title', status: 400 }); - const error = errorData; + const error = errorData as unknown as Error; - expect(reducer(undefined, { type: deleteShortUrl.rejected.toString(), error })).toEqual({ + expect(reducer(undefined, deleteShortUrl.rejected(error, '', { shortCode: '' }))).toEqual({ shortCode: '', loading: false, error: true, @@ -56,11 +53,6 @@ describe('shortUrlDeletionReducer', () => { }); }); - describe('resetDeleteShortUrl', () => { - it('returns expected action', () => - expect(resetDeleteShortUrl()).toEqual({ type: resetDeleteShortUrl.toString() })); - }); - describe('deleteShortUrl', () => { const dispatch = jest.fn(); const getState = jest.fn().mockReturnValue({ selectedServer: {} }); @@ -73,32 +65,12 @@ describe('shortUrlDeletionReducer', () => { await deleteShortUrl({ shortCode, domain })(dispatch, getState, {}); expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: deleteShortUrl.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: deleteShortUrl.fulfilled.toString(), + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: { shortCode, domain }, })); expect(deleteShortUrlCall).toHaveBeenCalledTimes(1); expect(deleteShortUrlCall).toHaveBeenCalledWith(shortCode, domain); }); - - it('dispatches proper actions if API client request fails', async () => { - const data = { foo: 'bar' }; - const shortCode = 'abc123'; - - deleteShortUrlCall.mockRejectedValue({ response: { data } }); - - await deleteShortUrl({ shortCode })(dispatch, getState, {}); - - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: deleteShortUrl.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: deleteShortUrl.rejected.toString(), - })); - - expect(deleteShortUrlCall).toHaveBeenCalledTimes(1); - expect(deleteShortUrlCall).toHaveBeenCalledWith(shortCode, undefined); - }); }); }); diff --git a/test/short-urls/reducers/shortUrlDetail.test.ts b/test/short-urls/reducers/shortUrlDetail.test.ts index 2080c437..52ffa603 100644 --- a/test/short-urls/reducers/shortUrlDetail.test.ts +++ b/test/short-urls/reducers/shortUrlDetail.test.ts @@ -2,7 +2,6 @@ import { Mock } from 'ts-mockery'; import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient'; import type { ShlinkState } from '../../../src/container/types'; import type { ShortUrl } from '../../../src/short-urls/data'; -import type { ShortUrlDetailAction } from '../../../src/short-urls/reducers/shortUrlDetail'; import { shortUrlDetailReducerCreator } from '../../../src/short-urls/reducers/shortUrlDetail'; import type { ShortUrlsList } from '../../../src/short-urls/reducers/shortUrlsList'; @@ -14,17 +13,13 @@ describe('shortUrlDetailReducer', () => { beforeEach(jest.clearAllMocks); describe('reducer', () => { - const action = (type: string) => Mock.of({ type }); - it('returns loading on GET_SHORT_URL_DETAIL_START', () => { - const state = reducer({ loading: false, error: false }, action(getShortUrlDetail.pending.toString())); - const { loading } = state; - + const { loading } = reducer({ loading: false, error: false }, getShortUrlDetail.pending('', { shortCode: '' })); expect(loading).toEqual(true); }); it('stops loading and returns error on GET_SHORT_URL_DETAIL_ERROR', () => { - const state = reducer({ loading: true, error: false }, action(getShortUrlDetail.rejected.toString())); + const state = reducer({ loading: true, error: false }, getShortUrlDetail.rejected(null, '', { shortCode: '' })); const { loading, error } = state; expect(loading).toEqual(false); @@ -35,7 +30,7 @@ describe('shortUrlDetailReducer', () => { const actionShortUrl = Mock.of({ longUrl: 'foo', shortCode: 'bar' }); const state = reducer( { loading: true, error: false }, - { type: getShortUrlDetail.fulfilled.toString(), payload: actionShortUrl }, + getShortUrlDetail.fulfilled(actionShortUrl, '', { shortCode: '' }), ); const { loading, error, shortUrl } = state; @@ -49,21 +44,6 @@ describe('shortUrlDetailReducer', () => { const dispatchMock = jest.fn(); const buildGetState = (shortUrlsList?: ShortUrlsList) => () => Mock.of({ shortUrlsList }); - it('dispatches start and error when promise is rejected', async () => { - getShortUrlCall.mockRejectedValue({}); - - await getShortUrlDetail({ shortCode: 'abc123', domain: '' })(dispatchMock, buildGetState(), {}); - - expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getShortUrlDetail.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getShortUrlDetail.rejected.toString(), - })); - expect(getShortUrlCall).toHaveBeenCalledTimes(1); - }); - it.each([ [undefined], [Mock.all()], @@ -86,13 +66,7 @@ describe('shortUrlDetailReducer', () => { await getShortUrlDetail({ shortCode: 'abc123', domain: '' })(dispatchMock, buildGetState(shortUrlsList), {}); expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getShortUrlDetail.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getShortUrlDetail.fulfilled.toString(), - payload: resolvedShortUrl, - })); + expect(dispatchMock).toHaveBeenLastCalledWith(expect.objectContaining({ payload: resolvedShortUrl })); expect(getShortUrlCall).toHaveBeenCalledTimes(1); }); @@ -111,13 +85,7 @@ describe('shortUrlDetailReducer', () => { ); expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getShortUrlDetail.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getShortUrlDetail.fulfilled.toString(), - payload: foundShortUrl, - })); + expect(dispatchMock).toHaveBeenLastCalledWith(expect.objectContaining({ payload: foundShortUrl })); expect(getShortUrlCall).not.toHaveBeenCalled(); }); }); diff --git a/test/short-urls/reducers/shortUrlEdition.test.ts b/test/short-urls/reducers/shortUrlEdition.test.ts index fad29937..c5ef958b 100644 --- a/test/short-urls/reducers/shortUrlEdition.test.ts +++ b/test/short-urls/reducers/shortUrlEdition.test.ts @@ -2,8 +2,7 @@ import { Mock } from 'ts-mockery'; import type { ShlinkState } from '../../../src/container/types'; import type { SelectedServer } from '../../../src/servers/data'; import type { ShortUrl } from '../../../src/short-urls/data'; -import type { - ShortUrlEditedAction } from '../../../src/short-urls/reducers/shortUrlEdition'; +import type { EditShortUrl } from '../../../src/short-urls/reducers/shortUrlEdition'; import { editShortUrl as editShortUrlCreator, shortUrlEditionReducerCreator, @@ -22,7 +21,7 @@ describe('shortUrlEditionReducer', () => { describe('reducer', () => { it('returns loading on EDIT_SHORT_URL_START', () => { - expect(reducer(undefined, Mock.of({ type: editShortUrl.pending.toString() }))).toEqual({ + expect(reducer(undefined, editShortUrl.pending('', Mock.all()))).toEqual({ saving: true, saved: false, error: false, @@ -30,7 +29,7 @@ describe('shortUrlEditionReducer', () => { }); it('returns error on EDIT_SHORT_URL_ERROR', () => { - expect(reducer(undefined, Mock.of({ type: editShortUrl.rejected.toString() }))).toEqual({ + expect(reducer(undefined, editShortUrl.rejected(null, '', Mock.all()))).toEqual({ saving: false, saved: false, error: true, @@ -38,7 +37,7 @@ describe('shortUrlEditionReducer', () => { }); it('returns provided tags and shortCode on SHORT_URL_EDITED', () => { - expect(reducer(undefined, { type: editShortUrl.fulfilled.toString(), payload: shortUrl })).toEqual({ + expect(reducer(undefined, editShortUrl.fulfilled(shortUrl, '', Mock.all()))).toEqual({ shortUrl, saving: false, saved: true, @@ -60,28 +59,7 @@ describe('shortUrlEditionReducer', () => { expect(updateShortUrl).toHaveBeenCalledTimes(1); expect(updateShortUrl).toHaveBeenCalledWith(shortCode, domain, { longUrl }); expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: editShortUrl.pending.toString(), - })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: editShortUrl.fulfilled.toString(), - payload: shortUrl, - })); - }); - - it('dispatches error on failure', async () => { - const error = new Error(); - - updateShortUrl.mockRejectedValue(error); - - await editShortUrl({ shortCode, data: { longUrl } })(dispatch, createGetState(), {}); - - expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); - expect(updateShortUrl).toHaveBeenCalledTimes(1); - expect(updateShortUrl).toHaveBeenCalledWith(shortCode, undefined, { longUrl }); - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: editShortUrl.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: editShortUrl.rejected.toString() })); + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: shortUrl })); }); }); }); diff --git a/test/short-urls/reducers/shortUrlsList.test.ts b/test/short-urls/reducers/shortUrlsList.test.ts index 095cd137..f03aea60 100644 --- a/test/short-urls/reducers/shortUrlsList.test.ts +++ b/test/short-urls/reducers/shortUrlsList.test.ts @@ -1,15 +1,17 @@ import { Mock } from 'ts-mockery'; import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient'; import type { ShlinkPaginator, ShlinkShortUrlsResponse } from '../../../src/api/types'; -import type { ShortUrl } from '../../../src/short-urls/data'; +import type { ShortUrl, ShortUrlData } from '../../../src/short-urls/data'; import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation'; import { shortUrlDeleted } from '../../../src/short-urls/reducers/shortUrlDeletion'; +import type { EditShortUrl } from '../../../src/short-urls/reducers/shortUrlEdition'; import { editShortUrl as editShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlEdition'; import { listShortUrls as listShortUrlsCreator, shortUrlsListReducerCreator, } from '../../../src/short-urls/reducers/shortUrlsList'; import { createNewVisits } from '../../../src/visits/reducers/visitCreation'; +import type { CreateVisit } from '../../../src/visits/types'; describe('shortUrlsListReducer', () => { const shortCode = 'abc123'; @@ -24,20 +26,20 @@ describe('shortUrlsListReducer', () => { describe('reducer', () => { it('returns loading on LIST_SHORT_URLS_START', () => - expect(reducer(undefined, { type: listShortUrls.pending.toString() })).toEqual({ + expect(reducer(undefined, listShortUrls.pending(''))).toEqual({ loading: true, error: false, })); it('returns short URLs on LIST_SHORT_URLS', () => - expect(reducer(undefined, { type: listShortUrls.fulfilled.toString(), payload: { data: [] } })).toEqual({ + expect(reducer(undefined, listShortUrls.fulfilled(Mock.of({ data: [] }), ''))).toEqual({ shortUrls: { data: [] }, loading: false, error: false, })); it('returns error on LIST_SHORT_URLS_ERROR', () => - expect(reducer(undefined, { type: listShortUrls.rejected.toString() })).toEqual({ + expect(reducer(undefined, listShortUrls.rejected(null, ''))).toEqual({ loading: false, error: true, })); @@ -58,7 +60,7 @@ describe('shortUrlsListReducer', () => { error: false, }; - expect(reducer(state, { type: shortUrlDeleted.toString(), payload: { shortCode } })).toEqual({ + expect(reducer(state, shortUrlDeleted(Mock.of({ shortCode })))).toEqual({ shortUrls: { data: [{ shortCode, domain: 'example.com' }, { shortCode: 'foo' }], pagination: { totalItems: 9 }, @@ -68,7 +70,7 @@ describe('shortUrlsListReducer', () => { }); }); - const createNewShortUrlVisit = (visitsCount: number) => ({ + const createNewShortUrlVisit = (visitsCount: number) => Mock.of({ shortUrl: { shortCode: 'abc123', visitsCount }, }); @@ -76,7 +78,6 @@ describe('shortUrlsListReducer', () => { [[createNewShortUrlVisit(11)], 11], [[createNewShortUrlVisit(30)], 30], [[createNewShortUrlVisit(20), createNewShortUrlVisit(40)], 40], - [[{}], 10], [[], 10], ])('updates visits count on CREATE_VISITS', (createdVisits, expectedCount) => { const state = { @@ -91,7 +92,7 @@ describe('shortUrlsListReducer', () => { error: false, }; - expect(reducer(state, { type: createNewVisits.toString(), payload: { createdVisits } })).toEqual({ + expect(reducer(state, createNewVisits(createdVisits))).toEqual({ shortUrls: { data: [ { shortCode, domain: 'example.com', visitsCount: 5 }, @@ -148,7 +149,7 @@ describe('shortUrlsListReducer', () => { error: false, }; - expect(reducer(state, { type: createShortUrl.fulfilled.toString(), payload: newShortUrl })).toEqual({ + expect(reducer(state, createShortUrl.fulfilled(newShortUrl, '', Mock.all()))).toEqual({ shortUrls: { data: expectedData, pagination: { totalItems: 16 }, @@ -187,7 +188,7 @@ describe('shortUrlsListReducer', () => { error: false, }; - const result = reducer(state, { type: editShortUrl.fulfilled.toString(), payload: editedShortUrl }); + const result = reducer(state, editShortUrl.fulfilled(editedShortUrl, '', Mock.of())); expect(result.shortUrls?.data).toEqual(expectedList); }); @@ -203,23 +204,7 @@ describe('shortUrlsListReducer', () => { await listShortUrls()(dispatch, getState, {}); expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: listShortUrls.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: listShortUrls.fulfilled.toString(), - payload: {}, - })); - - expect(listShortUrlsMock).toHaveBeenCalledTimes(1); - }); - - it('dispatches proper actions if API client request fails', async () => { - listShortUrlsMock.mockRejectedValue(undefined); - - await listShortUrls()(dispatch, getState, {}); - - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: listShortUrls.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: listShortUrls.rejected.toString() })); + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: {} })); expect(listShortUrlsMock).toHaveBeenCalledTimes(1); }); diff --git a/test/tags/reducers/tagDelete.test.ts b/test/tags/reducers/tagDelete.test.ts index 9b0b161b..e07b964d 100644 --- a/test/tags/reducers/tagDelete.test.ts +++ b/test/tags/reducers/tagDelete.test.ts @@ -12,7 +12,7 @@ describe('tagDeleteReducer', () => { describe('reducer', () => { it('returns loading on DELETE_TAG_START', () => { - expect(reducer(undefined, { type: deleteTag.pending.toString() })).toEqual({ + expect(reducer(undefined, deleteTag.pending('', ''))).toEqual({ deleting: true, deleted: false, error: false, @@ -20,7 +20,7 @@ describe('tagDeleteReducer', () => { }); it('returns error on DELETE_TAG_ERROR', () => { - expect(reducer(undefined, { type: deleteTag.rejected.toString() })).toEqual({ + expect(reducer(undefined, deleteTag.rejected(null, '', ''))).toEqual({ deleting: false, deleted: false, error: true, @@ -28,7 +28,7 @@ describe('tagDeleteReducer', () => { }); it('returns tag names on DELETE_TAG', () => { - expect(reducer(undefined, { type: deleteTag.fulfilled.toString() })).toEqual({ + expect(reducer(undefined, deleteTag.fulfilled(undefined, '', ''))).toEqual({ deleting: false, deleted: true, error: false, @@ -37,11 +37,9 @@ describe('tagDeleteReducer', () => { }); describe('tagDeleted', () => { - it('returns action based on provided params', () => - expect(tagDeleted('foo')).toEqual({ - type: tagDeleted.toString(), - payload: 'foo', - })); + it('returns action based on provided params', () => { + expect(tagDeleted('foo').payload).toEqual('foo'); + }); }); describe('deleteTag', () => { @@ -58,27 +56,7 @@ describe('tagDeleteReducer', () => { expect(deleteTagsCall).toHaveBeenNthCalledWith(1, [tag]); expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: deleteTag.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: deleteTag.fulfilled.toString() })); - }); - - it('throws on error', async () => { - const error = 'Error'; - const tag = 'foo'; - deleteTagsCall.mockRejectedValue(error); - - try { - await deleteTag(tag)(dispatch, getState, {}); - } catch (e) { - expect(e).toEqual(error); - } - - expect(deleteTagsCall).toHaveBeenCalledTimes(1); - expect(deleteTagsCall).toHaveBeenNthCalledWith(1, [tag]); - - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: deleteTag.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: deleteTag.rejected.toString() })); + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: undefined })); }); }); }); diff --git a/test/tags/reducers/tagEdit.test.ts b/test/tags/reducers/tagEdit.test.ts index deab5355..8e3f0ba2 100644 --- a/test/tags/reducers/tagEdit.test.ts +++ b/test/tags/reducers/tagEdit.test.ts @@ -1,7 +1,7 @@ import { Mock } from 'ts-mockery'; import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient'; import type { ShlinkState } from '../../../src/container/types'; -import type { EditTagAction } from '../../../src/tags/reducers/tagEdit'; +import type { EditTag } from '../../../src/tags/reducers/tagEdit'; import { editTag as editTagCreator, tagEdited, tagEditReducerCreator } from '../../../src/tags/reducers/tagEdit'; import type { ColorGenerator } from '../../../src/utils/services/ColorGenerator'; @@ -17,7 +17,7 @@ describe('tagEditReducer', () => { describe('reducer', () => { it('returns loading on EDIT_TAG_START', () => { - expect(reducer(undefined, Mock.of({ type: editTag.pending.toString() }))).toEqual({ + expect(reducer(undefined, editTag.pending('', Mock.all()))).toEqual({ editing: true, edited: false, error: false, @@ -25,7 +25,7 @@ describe('tagEditReducer', () => { }); it('returns error on EDIT_TAG_ERROR', () => { - expect(reducer(undefined, Mock.of({ type: editTag.rejected.toString() }))).toEqual({ + expect(reducer(undefined, editTag.rejected(null, '', Mock.all()))).toEqual({ editing: false, edited: false, error: true, @@ -33,10 +33,7 @@ describe('tagEditReducer', () => { }); it('returns tag names on EDIT_TAG', () => { - expect(reducer(undefined, { - type: editTag.fulfilled.toString(), - payload: { oldName, newName, color }, - })).toEqual({ + expect(reducer(undefined, editTag.fulfilled({ oldName, newName, color }, '', Mock.all()))).toEqual({ editing: false, edited: true, error: false, @@ -47,15 +44,10 @@ describe('tagEditReducer', () => { }); describe('tagEdited', () => { - it('returns action based on provided params', () => - expect(tagEdited({ oldName: 'foo', newName: 'bar', color: '#ff0000' })).toEqual({ - type: tagEdited.toString(), - payload: { - oldName: 'foo', - newName: 'bar', - color: '#ff0000', - }, - })); + it('returns action based on provided params', () => { + const payload = { oldName: 'foo', newName: 'bar', color: '#ff0000' }; + expect(tagEdited(payload).payload).toEqual(payload); + }); }); describe('editTag', () => { @@ -76,31 +68,9 @@ describe('tagEditReducer', () => { expect(colorGenerator.setColorForKey).toHaveBeenCalledWith(newName, color); expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: editTag.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: editTag.fulfilled.toString(), + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: { oldName, newName, color }, })); }); - - it('throws on error', async () => { - const error = 'Error'; - editTagCall.mockRejectedValue(error); - - try { - await editTag({ oldName, newName, color })(dispatch, getState, {}); - } catch (e) { - expect(e).toEqual(error); - } - - expect(editTagCall).toHaveBeenCalledTimes(1); - expect(editTagCall).toHaveBeenCalledWith(oldName, newName); - - expect(colorGenerator.setColorForKey).not.toHaveBeenCalled(); - - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: editTag.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: editTag.rejected.toString() })); - }); }); }); diff --git a/test/tags/reducers/tagsList.test.ts b/test/tags/reducers/tagsList.test.ts index d69b8645..54746842 100644 --- a/test/tags/reducers/tagsList.test.ts +++ b/test/tags/reducers/tagsList.test.ts @@ -1,6 +1,6 @@ import { Mock } from 'ts-mockery'; import type { ShlinkState } from '../../../src/container/types'; -import type { ShortUrl } from '../../../src/short-urls/data'; +import type { ShortUrl, ShortUrlData } from '../../../src/short-urls/data'; import { createShortUrl as createShortUrlCreator } from '../../../src/short-urls/reducers/shortUrlCreation'; import { tagDeleted } from '../../../src/tags/reducers/tagDelete'; import { tagEdited } from '../../../src/tags/reducers/tagEdit'; @@ -23,14 +23,14 @@ describe('tagsListReducer', () => { describe('reducer', () => { it('returns loading on LIST_TAGS_START', () => { - expect(reducer(undefined, { type: listTags.pending.toString() })).toEqual(expect.objectContaining({ + expect(reducer(undefined, listTags.pending(''))).toEqual(expect.objectContaining({ loading: true, error: false, })); }); it('returns error on LIST_TAGS_ERROR', () => { - expect(reducer(undefined, { type: listTags.rejected.toString() })).toEqual(expect.objectContaining({ + expect(reducer(undefined, listTags.rejected(null, ''))).toEqual(expect.objectContaining({ loading: false, error: true, })); @@ -39,10 +39,7 @@ describe('tagsListReducer', () => { it('returns provided tags as filtered and regular tags on LIST_TAGS', () => { const tags = ['foo', 'bar', 'baz']; - expect(reducer(undefined, { - type: listTags.fulfilled.toString(), - payload: { tags }, - })).toEqual({ + expect(reducer(undefined, listTags.fulfilled(Mock.of({ tags }), ''))).toEqual({ tags, filteredTags: tags, loading: false, @@ -57,7 +54,7 @@ describe('tagsListReducer', () => { expect(reducer( state({ tags, filteredTags: tags }), - { type: tagDeleted.toString(), payload: tag }, + tagDeleted(tag), )).toEqual({ tags: expectedTags, filteredTags: expectedTags, @@ -81,7 +78,7 @@ describe('tagsListReducer', () => { }, }, }), - { type: tagEdited.toString(), payload: { oldName, newName } }, + tagEdited({ oldName, newName, color: '' }), )).toEqual({ tags: expectedTags, filteredTags: expectedTags, @@ -103,7 +100,7 @@ describe('tagsListReducer', () => { const payload = 'Fo'; const filteredTags = ['foo', 'Foo2', 'fo']; - expect(reducer(state({ tags }), { type: filterTags.toString(), payload })).toEqual({ + expect(reducer(state({ tags }), filterTags(payload))).toEqual({ tags, filteredTags, }); @@ -117,14 +114,14 @@ describe('tagsListReducer', () => { const tags = ['foo', 'bar', 'baz', 'foo2', 'fo']; const payload = Mock.of({ tags: shortUrlTags }); - expect(reducer(state({ tags }), { type: createShortUrl.fulfilled.toString(), payload })).toEqual({ + expect(reducer(state({ tags }), createShortUrl.fulfilled(payload, '', Mock.of()))).toEqual({ tags: expectedTags, }); }); }); describe('filterTags', () => { - it('creates expected action', () => expect(filterTags('foo')).toEqual({ type: filterTags.toString(), payload: 'foo' })); + it('creates expected action', () => expect(filterTags('foo').payload).toEqual('foo')); }); describe('listTags', () => { @@ -159,39 +156,9 @@ describe('tagsListReducer', () => { expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); expect(getState).toHaveBeenCalledTimes(1); expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: listTags.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: listTags.fulfilled.toString(), + expect(dispatch).toHaveBeenLastCalledWith(expect.objectContaining({ payload: { tags, stats: {} }, })); }); - - const assertErrorResult = async () => { - await listTags()(dispatch, getState, {}); - - expect(buildShlinkApiClient).toHaveBeenCalledTimes(1); - expect(getState).toHaveBeenCalledTimes(1); - expect(dispatch).toHaveBeenCalledTimes(2); - expect(dispatch).toHaveBeenNthCalledWith(1, expect.objectContaining({ type: listTags.pending.toString() })); - expect(dispatch).toHaveBeenNthCalledWith(2, expect.objectContaining({ type: listTags.rejected.toString() })); - }; - - it('dispatches error when error occurs on list call', async () => { - listTagsMock.mockRejectedValue(new Error()); - buildShlinkApiClient.mockReturnValue({ listTags: listTagsMock }); - - await assertErrorResult(); - - expect(listTagsMock).toHaveBeenCalledTimes(1); - }); - - it('dispatches error when error occurs on build call', async () => { - buildShlinkApiClient.mockImplementation(() => { - throw new Error(); - }); - - await assertErrorResult(); - expect(listTagsMock).not.toHaveBeenCalled(); - }); }); }); diff --git a/test/visits/reducers/domainVisits.test.ts b/test/visits/reducers/domainVisits.test.ts index 61859d6b..0135eb65 100644 --- a/test/visits/reducers/domainVisits.test.ts +++ b/test/visits/reducers/domainVisits.test.ts @@ -8,14 +8,15 @@ import { formatIsoDate } from '../../../src/utils/helpers/date'; import type { DateInterval } from '../../../src/utils/helpers/dateIntervals'; import { rangeOf } from '../../../src/utils/utils'; import type { - DomainVisits } from '../../../src/visits/reducers/domainVisits'; + DomainVisits, LoadDomainVisits, +} from '../../../src/visits/reducers/domainVisits'; import { DEFAULT_DOMAIN, domainVisitsReducerCreator, getDomainVisits as getDomainVisitsCreator, } from '../../../src/visits/reducers/domainVisits'; import { createNewVisits } from '../../../src/visits/reducers/visitCreation'; -import type { Visit } from '../../../src/visits/types'; +import type { CreateVisit, Visit } from '../../../src/visits/types'; describe('domainVisitsReducer', () => { const now = new Date(); @@ -31,22 +32,28 @@ describe('domainVisitsReducer', () => { const buildState = (data: Partial) => Mock.of(data); it('returns loading on GET_DOMAIN_VISITS_START', () => { - const { loading } = reducer(buildState({ loading: false }), { type: getDomainVisits.pending.toString() }); + const { loading } = reducer( + buildState({ loading: false }), + getDomainVisits.pending('', Mock.all()), + ); expect(loading).toEqual(true); }); it('returns loadingLarge on GET_DOMAIN_VISITS_LARGE', () => { - const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: getDomainVisits.large.toString() }); + const { loadingLarge } = reducer(buildState({ loadingLarge: false }), getDomainVisits.large()); expect(loadingLarge).toEqual(true); }); it('returns cancelLoad on GET_DOMAIN_VISITS_CANCEL', () => { - const { cancelLoad } = reducer(buildState({ cancelLoad: false }), { type: cancelGetDomainVisits.toString() }); + const { cancelLoad } = reducer(buildState({ cancelLoad: false }), cancelGetDomainVisits()); expect(cancelLoad).toEqual(true); }); it('stops loading and returns error on GET_DOMAIN_VISITS_ERROR', () => { - const state = reducer(buildState({ loading: true, error: false }), { type: getDomainVisits.rejected.toString() }); + const state = reducer( + buildState({ loading: true, error: false }), + getDomainVisits.rejected(null, '', Mock.all()), + ); const { loading, error } = state; expect(loading).toEqual(false); @@ -54,11 +61,11 @@ describe('domainVisitsReducer', () => { }); it('return visits on GET_DOMAIN_VISITS', () => { - const actionVisits = [{}, {}]; - const { loading, error, visits } = reducer(buildState({ loading: true, error: false }), { - type: getDomainVisits.fulfilled.toString(), - payload: { visits: actionVisits }, - }); + const actionVisits = [Mock.all(), Mock.all()]; + const { loading, error, visits } = reducer( + buildState({ loading: true, error: false }), + getDomainVisits.fulfilled({ visits: actionVisits }, '', Mock.all()), + ); expect(loading).toEqual(false); expect(error).toEqual(false); @@ -121,25 +128,23 @@ describe('domainVisitsReducer', () => { ], ])('prepends new visits on CREATE_VISIT', (state, shortUrlDomain, expectedVisits) => { const shortUrl = Mock.of({ domain: shortUrlDomain }); - const { visits } = reducer(buildState({ ...state, visits: visitsMocks }), { - type: createNewVisits.toString(), - payload: { createdVisits: [{ shortUrl, visit: { date: formatIsoDate(now) ?? undefined } }] }, - }); + const { visits } = reducer(buildState({ ...state, visits: visitsMocks }), createNewVisits([ + Mock.of({ shortUrl, visit: { date: formatIsoDate(now) ?? undefined } }), + ])); expect(visits).toHaveLength(expectedVisits); }); it('returns new progress on GET_DOMAIN_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: getDomainVisits.progressChanged.toString(), payload: 85 }); - - expect(state).toEqual(expect.objectContaining({ progress: 85 })); + const { progress } = reducer(undefined, getDomainVisits.progressChanged(85)); + expect(progress).toEqual(85); }); it('returns fallbackInterval on GET_DOMAIN_VISITS_FALLBACK_TO_INTERVAL', () => { const fallbackInterval: DateInterval = 'last30Days'; const state = reducer( undefined, - { type: getDomainVisits.fallbackToInterval.toString(), payload: fallbackInterval }, + getDomainVisits.fallbackToInterval(fallbackInterval), ); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); @@ -153,21 +158,6 @@ describe('domainVisitsReducer', () => { }); const domain = 'foo.com'; - it('dispatches start and error when promise is rejected', async () => { - getDomainVisitsCall.mockRejectedValue(new Error()); - - await getDomainVisits({ domain })(dispatchMock, getState, {}); - - expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getDomainVisits.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getDomainVisits.rejected.toString(), - })); - expect(getDomainVisitsCall).toHaveBeenCalledTimes(1); - }); - it.each([ [undefined], [{}], @@ -185,11 +175,7 @@ describe('domainVisitsReducer', () => { await getDomainVisits({ domain, query })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getDomainVisits.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getDomainVisits.fulfilled.toString(), + expect(dispatchMock).toHaveBeenLastCalledWith(expect.objectContaining({ payload: { visits, domain, query: query ?? {} }, })); expect(getDomainVisitsCall).toHaveBeenCalledTimes(1); @@ -198,12 +184,12 @@ describe('domainVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 20)) })], - { type: getDomainVisits.fallbackToInterval.toString(), payload: 'last30Days' }, + getDomainVisits.fallbackToInterval('last30Days'), 3, ], [ [Mock.of({ date: formatISO(subDays(now, 100)) })], - { type: getDomainVisits.fallbackToInterval.toString(), payload: 'last180Days' }, + getDomainVisits.fallbackToInterval('last180Days'), 3, ], [[], expect.objectContaining({ type: getDomainVisits.fulfilled.toString() }), 2], @@ -227,16 +213,8 @@ describe('domainVisitsReducer', () => { await getDomainVisits({ domain, doIntervalFallback: true })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(expectedDispatchCalls); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getDomainVisits.pending.toString(), - })); expect(dispatchMock).toHaveBeenNthCalledWith(2, expectedSecondDispatch); expect(getDomainVisitsCall).toHaveBeenCalledTimes(2); }); }); - - describe('cancelGetDomainVisits', () => { - it('just returns the action with proper type', () => - expect(cancelGetDomainVisits()).toEqual(expect.objectContaining({ type: cancelGetDomainVisits.toString() }))); - }); }); diff --git a/test/visits/reducers/nonOrphanVisits.test.ts b/test/visits/reducers/nonOrphanVisits.test.ts index 3dfafacd..98b1037e 100644 --- a/test/visits/reducers/nonOrphanVisits.test.ts +++ b/test/visits/reducers/nonOrphanVisits.test.ts @@ -28,27 +28,24 @@ describe('nonOrphanVisitsReducer', () => { const buildState = (data: Partial) => Mock.of(data); it('returns loading on GET_NON_ORPHAN_VISITS_START', () => { - const { loading } = reducer(buildState({ loading: false }), { type: getNonOrphanVisits.pending.toString() }); + const { loading } = reducer(buildState({ loading: false }), getNonOrphanVisits.pending('', {})); expect(loading).toEqual(true); }); it('returns loadingLarge on GET_NON_ORPHAN_VISITS_LARGE', () => { - const { loadingLarge } = reducer( - buildState({ loadingLarge: false }), - { type: getNonOrphanVisits.large.toString() }, - ); + const { loadingLarge } = reducer(buildState({ loadingLarge: false }), getNonOrphanVisits.large()); expect(loadingLarge).toEqual(true); }); it('returns cancelLoad on GET_NON_ORPHAN_VISITS_CANCEL', () => { - const { cancelLoad } = reducer(buildState({ cancelLoad: false }), { type: cancelGetNonOrphanVisits.toString() }); + const { cancelLoad } = reducer(buildState({ cancelLoad: false }), cancelGetNonOrphanVisits()); expect(cancelLoad).toEqual(true); }); it('stops loading and returns error on GET_NON_ORPHAN_VISITS_ERROR', () => { const { loading, error } = reducer( buildState({ loading: true, error: false }), - { type: getNonOrphanVisits.rejected.toString() }, + getNonOrphanVisits.rejected(null, '', {}), ); expect(loading).toEqual(false); @@ -56,11 +53,11 @@ describe('nonOrphanVisitsReducer', () => { }); it('return visits on GET_NON_ORPHAN_VISITS', () => { - const actionVisits = [{}, {}]; - const { loading, error, visits } = reducer(buildState({ loading: true, error: false }), { - type: getNonOrphanVisits.fulfilled.toString(), - payload: { visits: actionVisits }, - }); + const actionVisits = [Mock.all(), Mock.all()]; + const { loading, error, visits } = reducer( + buildState({ loading: true, error: false }), + getNonOrphanVisits.fulfilled({ visits: actionVisits }, '', {}), + ); expect(loading).toEqual(false); expect(error).toEqual(false); @@ -103,25 +100,19 @@ describe('nonOrphanVisitsReducer', () => { const prevState = buildState({ ...state, visits: visitsMocks }); const visit = Mock.of({ date: formatIsoDate(now) ?? undefined }); - const { visits } = reducer(prevState, { - type: createNewVisits.toString(), - payload: { createdVisits: [{ visit }, { visit }] }, - }); + const { visits } = reducer(prevState, createNewVisits([{ visit }, { visit }])); expect(visits).toHaveLength(expectedVisits); }); it('returns new progress on GET_NON_ORPHAN_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: getNonOrphanVisits.progressChanged.toString(), payload: 85 }); - expect(state).toEqual(expect.objectContaining({ progress: 85 })); + const { progress } = reducer(undefined, getNonOrphanVisits.progressChanged(85)); + expect(progress).toEqual(85); }); it('returns fallbackInterval on GET_NON_ORPHAN_VISITS_FALLBACK_TO_INTERVAL', () => { const fallbackInterval: DateInterval = 'last30Days'; - const state = reducer( - undefined, - { type: getNonOrphanVisits.fallbackToInterval.toString(), payload: fallbackInterval }, - ); + const state = reducer(undefined, getNonOrphanVisits.fallbackToInterval(fallbackInterval)); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); }); @@ -135,21 +126,6 @@ describe('nonOrphanVisitsReducer', () => { beforeEach(jest.resetAllMocks); - it('dispatches start and error when promise is rejected', async () => { - getNonOrphanVisitsCall.mockRejectedValue({}); - - await getNonOrphanVisits({})(dispatchMock, getState, {}); - - expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getNonOrphanVisits.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getNonOrphanVisits.rejected.toString(), - })); - expect(getNonOrphanVisitsCall).toHaveBeenCalledTimes(1); - }); - it.each([ [undefined], [{}], @@ -167,11 +143,7 @@ describe('nonOrphanVisitsReducer', () => { await getNonOrphanVisits({ query })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining( - { type: getNonOrphanVisits.pending.toString() }, - )); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getNonOrphanVisits.fulfilled.toString(), + expect(dispatchMock).toHaveBeenLastCalledWith(expect.objectContaining({ payload: { visits, query: query ?? {} }, })); expect(getNonOrphanVisitsCall).toHaveBeenCalledTimes(1); @@ -180,12 +152,12 @@ describe('nonOrphanVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 5)) })], - { type: getNonOrphanVisits.fallbackToInterval.toString(), payload: 'last7Days' }, + getNonOrphanVisits.fallbackToInterval('last7Days'), 3, ], [ [Mock.of({ date: formatISO(subDays(now, 200)) })], - { type: getNonOrphanVisits.fallbackToInterval.toString(), payload: 'last365Days' }, + getNonOrphanVisits.fallbackToInterval('last365Days'), 3, ], [[], expect.objectContaining({ type: getNonOrphanVisits.fulfilled.toString() }), 2], @@ -209,16 +181,8 @@ describe('nonOrphanVisitsReducer', () => { await getNonOrphanVisits({ doIntervalFallback: true })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(expectedAmountOfDispatches); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getNonOrphanVisits.pending.toString(), - })); expect(dispatchMock).toHaveBeenNthCalledWith(2, expectedSecondDispatch); expect(getNonOrphanVisitsCall).toHaveBeenCalledTimes(2); }); }); - - describe('cancelGetNonOrphanVisits', () => { - it('just returns the action with proper type', () => - expect(cancelGetNonOrphanVisits()).toEqual({ type: cancelGetNonOrphanVisits.toString() })); - }); }); diff --git a/test/visits/reducers/orphanVisits.test.ts b/test/visits/reducers/orphanVisits.test.ts index 1d00955d..1071321f 100644 --- a/test/visits/reducers/orphanVisits.test.ts +++ b/test/visits/reducers/orphanVisits.test.ts @@ -28,24 +28,24 @@ describe('orphanVisitsReducer', () => { const buildState = (data: Partial) => Mock.of(data); it('returns loading on GET_ORPHAN_VISITS_START', () => { - const { loading } = reducer(buildState({ loading: false }), { type: getOrphanVisits.pending.toString() }); + const { loading } = reducer(buildState({ loading: false }), getOrphanVisits.pending('', {})); expect(loading).toEqual(true); }); it('returns loadingLarge on GET_ORPHAN_VISITS_LARGE', () => { - const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: getOrphanVisits.large.toString() }); + const { loadingLarge } = reducer(buildState({ loadingLarge: false }), getOrphanVisits.large()); expect(loadingLarge).toEqual(true); }); it('returns cancelLoad on GET_ORPHAN_VISITS_CANCEL', () => { - const { cancelLoad } = reducer(buildState({ cancelLoad: false }), { type: cancelGetOrphanVisits.toString() }); + const { cancelLoad } = reducer(buildState({ cancelLoad: false }), cancelGetOrphanVisits()); expect(cancelLoad).toEqual(true); }); it('stops loading and returns error on GET_ORPHAN_VISITS_ERROR', () => { const { loading, error } = reducer( buildState({ loading: true, error: false }), - { type: getOrphanVisits.rejected.toString() }, + getOrphanVisits.rejected(null, '', {}), ); expect(loading).toEqual(false); @@ -53,11 +53,11 @@ describe('orphanVisitsReducer', () => { }); it('return visits on GET_ORPHAN_VISITS', () => { - const actionVisits = [{}, {}]; - const { loading, error, visits } = reducer(buildState({ loading: true, error: false }), { - type: getOrphanVisits.fulfilled.toString(), - payload: { visits: actionVisits }, - }); + const actionVisits = [Mock.all(), Mock.all()]; + const { loading, error, visits } = reducer( + buildState({ loading: true, error: false }), + getOrphanVisits.fulfilled({ visits: actionVisits }, '', {}), + ); expect(loading).toEqual(false); expect(error).toEqual(false); @@ -100,25 +100,19 @@ describe('orphanVisitsReducer', () => { const prevState = buildState({ ...state, visits: visitsMocks }); const visit = Mock.of({ date: formatIsoDate(now) ?? undefined }); - const { visits } = reducer(prevState, { - type: createNewVisits.toString(), - payload: { createdVisits: [{ visit }, { visit }] }, - }); + const { visits } = reducer(prevState, createNewVisits([{ visit }, { visit }])); expect(visits).toHaveLength(expectedVisits); }); it('returns new progress on GET_ORPHAN_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: getOrphanVisits.progressChanged.toString(), payload: 85 }); - expect(state).toEqual(expect.objectContaining({ progress: 85 })); + const { progress } = reducer(undefined, getOrphanVisits.progressChanged(85)); + expect(progress).toEqual(85); }); it('returns fallbackInterval on GET_ORPHAN_VISITS_FALLBACK_TO_INTERVAL', () => { const fallbackInterval: DateInterval = 'last30Days'; - const state = reducer( - undefined, - { type: getOrphanVisits.fallbackToInterval.toString(), payload: fallbackInterval }, - ); + const state = reducer(undefined, getOrphanVisits.fallbackToInterval(fallbackInterval)); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); }); @@ -130,21 +124,6 @@ describe('orphanVisitsReducer', () => { orphanVisits: { cancelLoad: false }, }); - it('dispatches start and error when promise is rejected', async () => { - getOrphanVisitsCall.mockRejectedValue({}); - - await getOrphanVisits({})(dispatchMock, getState, {}); - - expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getOrphanVisits.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getOrphanVisits.rejected.toString(), - })); - expect(getOrphanVisitsCall).toHaveBeenCalledTimes(1); - }); - it.each([ [undefined], [{}], @@ -162,11 +141,7 @@ describe('orphanVisitsReducer', () => { await getOrphanVisits({ query })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getOrphanVisits.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getOrphanVisits.fulfilled.toString(), + expect(dispatchMock).toHaveBeenLastCalledWith(expect.objectContaining({ payload: { visits, query: query ?? {} }, })); expect(getOrphanVisitsCall).toHaveBeenCalledTimes(1); @@ -175,12 +150,12 @@ describe('orphanVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 5)) })], - { type: getOrphanVisits.fallbackToInterval.toString(), payload: 'last7Days' }, + getOrphanVisits.fallbackToInterval('last7Days'), 3, ], [ [Mock.of({ date: formatISO(subDays(now, 200)) })], - { type: getOrphanVisits.fallbackToInterval.toString(), payload: 'last365Days' }, + getOrphanVisits.fallbackToInterval('last365Days'), 3, ], [[], expect.objectContaining({ type: getOrphanVisits.fulfilled.toString() }), 2], @@ -204,16 +179,8 @@ describe('orphanVisitsReducer', () => { await getOrphanVisits({ doIntervalFallback: true })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(expectedDispatchCalls); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getOrphanVisits.pending.toString(), - })); expect(dispatchMock).toHaveBeenNthCalledWith(2, expectedSecondDispatch); expect(getOrphanVisitsCall).toHaveBeenCalledTimes(2); }); }); - - describe('cancelGetOrphanVisits', () => { - it('just returns the action with proper type', () => - expect(cancelGetOrphanVisits()).toEqual({ type: cancelGetOrphanVisits.toString() })); - }); }); diff --git a/test/visits/reducers/shortUrlVisits.test.ts b/test/visits/reducers/shortUrlVisits.test.ts index ba0820dd..e5523e0d 100644 --- a/test/visits/reducers/shortUrlVisits.test.ts +++ b/test/visits/reducers/shortUrlVisits.test.ts @@ -13,7 +13,7 @@ import { shortUrlVisitsReducerCreator, } from '../../../src/visits/reducers/shortUrlVisits'; import { createNewVisits } from '../../../src/visits/reducers/visitCreation'; -import type { Visit } from '../../../src/visits/types'; +import type { CreateVisit, Visit } from '../../../src/visits/types'; describe('shortUrlVisitsReducer', () => { const now = new Date(); @@ -29,27 +29,24 @@ describe('shortUrlVisitsReducer', () => { const buildState = (data: Partial) => Mock.of(data); it('returns loading on GET_SHORT_URL_VISITS_START', () => { - const { loading } = reducer(buildState({ loading: false }), { type: getShortUrlVisits.pending.toString() }); + const { loading } = reducer(buildState({ loading: false }), getShortUrlVisits.pending('', { shortCode: '' })); expect(loading).toEqual(true); }); it('returns loadingLarge on GET_SHORT_URL_VISITS_LARGE', () => { - const { loadingLarge } = reducer( - buildState({ loadingLarge: false }), - { type: getShortUrlVisits.large.toString() }, - ); + const { loadingLarge } = reducer(buildState({ loadingLarge: false }), getShortUrlVisits.large()); expect(loadingLarge).toEqual(true); }); it('returns cancelLoad on GET_SHORT_URL_VISITS_CANCEL', () => { - const { cancelLoad } = reducer(buildState({ cancelLoad: false }), { type: cancelGetShortUrlVisits.toString() }); + const { cancelLoad } = reducer(buildState({ cancelLoad: false }), cancelGetShortUrlVisits()); expect(cancelLoad).toEqual(true); }); it('stops loading and returns error on GET_SHORT_URL_VISITS_ERROR', () => { const { loading, error } = reducer( buildState({ loading: true, error: false }), - { type: getShortUrlVisits.rejected.toString() }, + getShortUrlVisits.rejected(null, '', { shortCode: '' }), ); expect(loading).toEqual(false); @@ -57,11 +54,11 @@ describe('shortUrlVisitsReducer', () => { }); it('return visits on GET_SHORT_URL_VISITS', () => { - const actionVisits = [{}, {}]; - const { loading, error, visits } = reducer(buildState({ loading: true, error: false }), { - type: getShortUrlVisits.fulfilled.toString(), - payload: { visits: actionVisits }, - }); + const actionVisits = [Mock.all(), Mock.all()]; + const { loading, error, visits } = reducer( + buildState({ loading: true, error: false }), + getShortUrlVisits.fulfilled({ visits: actionVisits }, '', { shortCode: '' }), + ); expect(loading).toEqual(false); expect(error).toEqual(false); @@ -124,25 +121,22 @@ describe('shortUrlVisitsReducer', () => { visits: visitsMocks, }); - const { visits } = reducer(prevState, { - type: createNewVisits.toString(), - payload: { createdVisits: [{ shortUrl, visit: { date: formatIsoDate(now) ?? undefined } }] }, - }); + const { visits } = reducer( + prevState, + createNewVisits([Mock.of({ shortUrl, visit: { date: formatIsoDate(now) ?? undefined } })]), + ); expect(visits).toHaveLength(expectedVisits); }); it('returns new progress on GET_SHORT_URL_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: getShortUrlVisits.progressChanged.toString(), payload: 85 }); - expect(state).toEqual(expect.objectContaining({ progress: 85 })); + const { progress } = reducer(undefined, getShortUrlVisits.progressChanged(85)); + expect(progress).toEqual(85); }); it('returns fallbackInterval on GET_SHORT_URL_VISITS_FALLBACK_TO_INTERVAL', () => { const fallbackInterval: DateInterval = 'last30Days'; - const state = reducer( - undefined, - { type: getShortUrlVisits.fallbackToInterval.toString(), payload: fallbackInterval }, - ); + const state = reducer(undefined, getShortUrlVisits.fallbackToInterval(fallbackInterval)); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); }); @@ -154,21 +148,6 @@ describe('shortUrlVisitsReducer', () => { shortUrlVisits: Mock.of({ cancelLoad: false }), }); - it('dispatches start and error when promise is rejected', async () => { - getShortUrlVisitsCall.mockRejectedValue({}); - - await getShortUrlVisits({ shortCode: 'abc123' })(dispatchMock, getState, {}); - - expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getShortUrlVisits.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getShortUrlVisits.rejected.toString(), - })); - expect(getShortUrlVisitsCall).toHaveBeenCalledTimes(1); - }); - it.each([ [undefined, undefined], [{}, undefined], @@ -188,11 +167,7 @@ describe('shortUrlVisitsReducer', () => { await getShortUrlVisits({ shortCode, query })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getShortUrlVisits.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getShortUrlVisits.fulfilled.toString(), + expect(dispatchMock).toHaveBeenLastCalledWith(expect.objectContaining({ payload: { visits, shortCode, domain, query: query ?? {} }, })); expect(getShortUrlVisitsCall).toHaveBeenCalledTimes(1); @@ -223,12 +198,12 @@ describe('shortUrlVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 5)) })], - { type: getShortUrlVisits.fallbackToInterval.toString(), payload: 'last7Days' }, + getShortUrlVisits.fallbackToInterval('last7Days'), 3, ], [ [Mock.of({ date: formatISO(subDays(now, 200)) })], - { type: getShortUrlVisits.fallbackToInterval.toString(), payload: 'last365Days' }, + getShortUrlVisits.fallbackToInterval('last365Days'), 3, ], [[], expect.objectContaining({ type: getShortUrlVisits.fulfilled.toString() }), 2], @@ -252,16 +227,8 @@ describe('shortUrlVisitsReducer', () => { await getShortUrlVisits({ shortCode: 'abc123', doIntervalFallback: true })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(expectedDispatchCalls); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getShortUrlVisits.pending.toString(), - })); expect(dispatchMock).toHaveBeenNthCalledWith(2, expectedSecondDispatch); expect(getShortUrlVisitsCall).toHaveBeenCalledTimes(2); }); }); - - describe('cancelGetShortUrlVisits', () => { - it('just returns the action with proper type', () => - expect(cancelGetShortUrlVisits()).toEqual({ type: cancelGetShortUrlVisits.toString() })); - }); }); diff --git a/test/visits/reducers/tagVisits.test.ts b/test/visits/reducers/tagVisits.test.ts index 7f2e1bc3..f62d068b 100644 --- a/test/visits/reducers/tagVisits.test.ts +++ b/test/visits/reducers/tagVisits.test.ts @@ -13,7 +13,7 @@ import { tagVisitsReducerCreator, } from '../../../src/visits/reducers/tagVisits'; import { createNewVisits } from '../../../src/visits/reducers/visitCreation'; -import type { Visit } from '../../../src/visits/types'; +import type { CreateVisit, Visit } from '../../../src/visits/types'; describe('tagVisitsReducer', () => { const now = new Date(); @@ -29,24 +29,24 @@ describe('tagVisitsReducer', () => { const buildState = (data: Partial) => Mock.of(data); it('returns loading on GET_TAG_VISITS_START', () => { - const { loading } = reducer(buildState({ loading: false }), { type: getTagVisits.pending.toString() }); + const { loading } = reducer(buildState({ loading: false }), getTagVisits.pending('', { tag: '' })); expect(loading).toEqual(true); }); it('returns loadingLarge on GET_TAG_VISITS_LARGE', () => { - const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: getTagVisits.large.toString() }); + const { loadingLarge } = reducer(buildState({ loadingLarge: false }), getTagVisits.large()); expect(loadingLarge).toEqual(true); }); it('returns cancelLoad on GET_TAG_VISITS_CANCEL', () => { - const { cancelLoad } = reducer(buildState({ cancelLoad: false }), { type: cancelGetTagVisits.toString() }); + const { cancelLoad } = reducer(buildState({ cancelLoad: false }), cancelGetTagVisits()); expect(cancelLoad).toEqual(true); }); it('stops loading and returns error on GET_TAG_VISITS_ERROR', () => { const { loading, error } = reducer( buildState({ loading: true, error: false }), - { type: getTagVisits.rejected.toString() }, + getTagVisits.rejected(null, '', { tag: '' }), ); expect(loading).toEqual(false); @@ -54,11 +54,11 @@ describe('tagVisitsReducer', () => { }); it('return visits on GET_TAG_VISITS', () => { - const actionVisits = [{}, {}]; - const { loading, error, visits } = reducer(buildState({ loading: true, error: false }), { - type: getTagVisits.fulfilled.toString(), - payload: { visits: actionVisits }, - }); + const actionVisits = [Mock.all(), Mock.all()]; + const { loading, error, visits } = reducer( + buildState({ loading: true, error: false }), + getTagVisits.fulfilled({ visits: actionVisits }, '', { tag: '' }), + ); expect(loading).toEqual(false); expect(error).toEqual(false); @@ -121,22 +121,22 @@ describe('tagVisitsReducer', () => { visits: visitsMocks, }); - const { visits } = reducer(prevState, { - type: createNewVisits.toString(), - payload: { createdVisits: [{ shortUrl, visit: { date: formatIsoDate(now) ?? undefined } }] }, - }); + const { visits } = reducer( + prevState, + createNewVisits([Mock.of({ shortUrl, visit: { date: formatIsoDate(now) ?? undefined } })]), + ); expect(visits).toHaveLength(expectedVisits); }); it('returns new progress on GET_TAG_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: getTagVisits.progressChanged.toString(), payload: 85 }); - expect(state).toEqual(expect.objectContaining({ progress: 85 })); + const { progress } = reducer(undefined, getTagVisits.progressChanged(85)); + expect(progress).toEqual(85); }); it('returns fallbackInterval on GET_TAG_VISITS_FALLBACK_TO_INTERVAL', () => { const fallbackInterval: DateInterval = 'last30Days'; - const state = reducer(undefined, { type: getTagVisits.fallbackToInterval.toString(), payload: fallbackInterval }); + const state = reducer(undefined, getTagVisits.fallbackToInterval(fallbackInterval)); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); }); @@ -149,21 +149,6 @@ describe('tagVisitsReducer', () => { }); const tag = 'foo'; - it('dispatches start and error when promise is rejected', async () => { - getTagVisitsCall.mockRejectedValue(new Error()); - - await getTagVisits({ tag })(dispatchMock, getState, {}); - - expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getTagVisits.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getTagVisits.rejected.toString(), - })); - expect(getTagVisitsCall).toHaveBeenCalledTimes(1); - }); - it.each([ [undefined], [{}], @@ -181,11 +166,7 @@ describe('tagVisitsReducer', () => { await getTagVisits({ tag, query })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(2); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getTagVisits.pending.toString(), - })); - expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ - type: getTagVisits.fulfilled.toString(), + expect(dispatchMock).toHaveBeenLastCalledWith(expect.objectContaining({ payload: { visits, tag, query: query ?? {} }, })); expect(getTagVisitsCall).toHaveBeenCalledTimes(1); @@ -194,12 +175,12 @@ describe('tagVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 20)) })], - { type: getTagVisits.fallbackToInterval.toString(), payload: 'last30Days' }, + getTagVisits.fallbackToInterval('last30Days'), 3, ], [ [Mock.of({ date: formatISO(subDays(now, 100)) })], - { type: getTagVisits.fallbackToInterval.toString(), payload: 'last180Days' }, + getTagVisits.fallbackToInterval('last180Days'), 3, ], [[], expect.objectContaining({ type: getTagVisits.fulfilled.toString() }), 2], @@ -223,16 +204,8 @@ describe('tagVisitsReducer', () => { await getTagVisits({ tag, doIntervalFallback: true })(dispatchMock, getState, {}); expect(dispatchMock).toHaveBeenCalledTimes(expectedDispatchCalls); - expect(dispatchMock).toHaveBeenNthCalledWith(1, expect.objectContaining({ - type: getTagVisits.pending.toString(), - })); expect(dispatchMock).toHaveBeenNthCalledWith(2, expectedSecondDispatch); expect(getTagVisitsCall).toHaveBeenCalledTimes(2); }); }); - - describe('cancelGetTagVisits', () => { - it('just returns the action with proper type', () => - expect(cancelGetTagVisits()).toEqual({ type: cancelGetTagVisits.toString() })); - }); }); diff --git a/test/visits/reducers/visitCreation.test.ts b/test/visits/reducers/visitCreation.test.ts index 2fc8b4ea..7c9a35ed 100644 --- a/test/visits/reducers/visitCreation.test.ts +++ b/test/visits/reducers/visitCreation.test.ts @@ -9,10 +9,8 @@ describe('visitCreationReducer', () => { const visit = Mock.all(); it('just returns the action with proper type', () => { - expect(createNewVisits([{ shortUrl, visit }])).toEqual({ - type: createNewVisits.toString(), - payload: { createdVisits: [{ shortUrl, visit }] }, - }); + const { payload } = createNewVisits([{ shortUrl, visit }]); + expect(payload).toEqual({ createdVisits: [{ shortUrl, visit }] }); }); }); }); diff --git a/test/visits/reducers/visitsOverview.test.ts b/test/visits/reducers/visitsOverview.test.ts index a5fbf757..a46f9561 100644 --- a/test/visits/reducers/visitsOverview.test.ts +++ b/test/visits/reducers/visitsOverview.test.ts @@ -2,11 +2,12 @@ import { Mock } from 'ts-mockery'; import type { ShlinkApiClient } from '../../../src/api/services/ShlinkApiClient'; import type { ShlinkVisitsOverview } from '../../../src/api/types'; import type { ShlinkState } from '../../../src/container/types'; -import type { CreateVisitsAction } from '../../../src/visits/reducers/visitCreation'; import { createNewVisits } from '../../../src/visits/reducers/visitCreation'; import type { - GetVisitsOverviewAction, ParsedVisitsOverview, - PartialVisitsSummary, VisitsOverview } from '../../../src/visits/reducers/visitsOverview'; + ParsedVisitsOverview, + PartialVisitsSummary, + VisitsOverview, +} from '../../../src/visits/reducers/visitsOverview'; import { loadVisitsOverview as loadVisitsOverviewCreator, visitsOverviewReducerCreator, @@ -22,14 +23,12 @@ describe('visitsOverviewReducer', () => { 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()), + loadVisitsOverview.pending(''), ); expect(loading).toEqual(true); @@ -38,7 +37,7 @@ describe('visitsOverviewReducer', () => { it('stops loading and returns error on GET_OVERVIEW_ERROR', () => { const { loading, error } = reducer( state({ loading: true, error: false }), - action(loadVisitsOverview.rejected.toString()), + loadVisitsOverview.rejected(null, ''), ); expect(loading).toEqual(false); @@ -145,23 +144,6 @@ describe('visitsOverviewReducer', () => { 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.each([ [ // Shlink <3.5.0 @@ -191,13 +173,7 @@ describe('visitsOverviewReducer', () => { 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: dispatchedPayload, - })); + expect(dispatchMock).toHaveBeenNthCalledWith(2, expect.objectContaining({ payload: dispatchedPayload })); expect(getVisitsOverview).toHaveBeenCalledTimes(1); }); });