From 85452cde234b145af9dee20ca8c13b66ea9437ae Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Sat, 31 Dec 2022 10:34:29 +0100 Subject: [PATCH] Enhanced visits async thunk so that it wraps both standard async thunk actions and extra ones --- src/visits/reducers/common.ts | 33 ++++++++++---------- test/visits/reducers/domainVisits.test.ts | 15 +++++---- test/visits/reducers/nonOrphanVisits.test.ts | 18 ++++++----- test/visits/reducers/orphanVisits.test.ts | 15 +++++---- test/visits/reducers/shortUrlVisits.test.ts | 18 ++++++----- test/visits/reducers/tagVisits.test.ts | 15 +++++---- 6 files changed, 58 insertions(+), 56 deletions(-) diff --git a/src/visits/reducers/common.ts b/src/visits/reducers/common.ts index f64078cd..b3abfddd 100644 --- a/src/visits/reducers/common.ts +++ b/src/visits/reducers/common.ts @@ -29,11 +29,11 @@ interface VisitsAsyncThunkOptions( { typePrefix, createLoaders, getExtraFulfilledPayload, shouldCancel }: VisitsAsyncThunkOptions, ) => { - const progressChangedAction = createAction(`${typePrefix}/progressChanged`); - const largeAction = createAction(`${typePrefix}/large`); - const fallbackToIntervalAction = createAction(`${typePrefix}/fallbackToInterval`); + const progressChanged = createAction(`${typePrefix}/progressChanged`); + const large = createAction(`${typePrefix}/large`); + const fallbackToInterval = createAction(`${typePrefix}/fallbackToInterval`); - const asyncThunk = createAsyncThunk(typePrefix, async (params: T, { getState, dispatch }): Promise => { + const asyncThunk = createAsyncThunk(typePrefix, async (params: T, { getState, dispatch }): Promise> => { const [visitsLoader, lastVisitLoader] = createLoaders(params, getState); const loadVisitsInParallel = async (pages: number[]): Promise => @@ -46,7 +46,7 @@ export const createVisitsAsyncThunk = PARALLEL_REQUESTS_COUNT) { - dispatch(largeAction()); + dispatch(large()); } return data.concat(await loadPagesBlocks(pagesBlocks)); @@ -77,13 +77,14 @@ export const createVisitsAsyncThunk = >( { name, asyncThunkCreator, initialState, filterCreatedVisits }: VisitsReducerOptions, ) => { - const { asyncThunk, largeAction, fallbackToIntervalAction, progressChangedAction } = asyncThunkCreator; + const { pending, rejected, fulfilled, large, progressChanged, fallbackToInterval } = asyncThunkCreator; const { reducer, actions } = createSlice({ name, initialState, @@ -115,17 +116,17 @@ export const createVisitsReducer = ({ ...state, cancelLoad: true }), }, extraReducers: (builder) => { - builder.addCase(asyncThunk.pending, () => ({ ...initialState, loading: true })); - builder.addCase(asyncThunk.rejected, (_, { error }) => ( + builder.addCase(pending, () => ({ ...initialState, loading: true })); + builder.addCase(rejected, (_, { error }) => ( { ...initialState, error: true, errorData: parseApiError(error) } )); - builder.addCase(asyncThunk.fulfilled, (state, { payload }) => ( + builder.addCase(fulfilled, (state, { payload }) => ( { ...state, ...payload, loading: false, loadingLarge: false, error: false } )); - builder.addCase(largeAction, (state) => ({ ...state, loadingLarge: true })); - builder.addCase(progressChangedAction, (state, { payload: progress }) => ({ ...state, progress })); - builder.addCase(fallbackToIntervalAction, (state, { payload: fallbackInterval }) => ( + builder.addCase(large, (state) => ({ ...state, loadingLarge: true })); + builder.addCase(progressChanged, (state, { payload: progress }) => ({ ...state, progress })); + builder.addCase(fallbackToInterval, (state, { payload: fallbackInterval }) => ( { ...state, fallbackInterval } )); diff --git a/test/visits/reducers/domainVisits.test.ts b/test/visits/reducers/domainVisits.test.ts index b7024e0d..2f508a88 100644 --- a/test/visits/reducers/domainVisits.test.ts +++ b/test/visits/reducers/domainVisits.test.ts @@ -21,9 +21,8 @@ describe('domainVisitsReducer', () => { const visitsMocks = rangeOf(2, () => Mock.all()); const getDomainVisitsCall = jest.fn(); const buildApiClientMock = () => Mock.of({ getDomainVisits: getDomainVisitsCall }); - const creator = getDomainVisitsCreator(buildApiClientMock); - const { asyncThunk: getDomainVisits, progressChangedAction, largeAction, fallbackToIntervalAction } = creator; - const { reducer, cancelGetVisits: cancelGetDomainVisits } = domainVisitsReducerCreator(creator); + const getDomainVisits = getDomainVisitsCreator(buildApiClientMock); + const { reducer, cancelGetVisits: cancelGetDomainVisits } = domainVisitsReducerCreator(getDomainVisits); beforeEach(jest.clearAllMocks); @@ -36,7 +35,7 @@ describe('domainVisitsReducer', () => { }); it('returns loadingLarge on GET_DOMAIN_VISITS_LARGE', () => { - const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: largeAction.toString() }); + const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: getDomainVisits.large.toString() }); expect(loadingLarge).toEqual(true); }); @@ -130,7 +129,7 @@ describe('domainVisitsReducer', () => { }); it('returns new progress on GET_DOMAIN_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: progressChangedAction.toString(), payload: 85 }); + const state = reducer(undefined, { type: getDomainVisits.progressChanged.toString(), payload: 85 }); expect(state).toEqual(expect.objectContaining({ progress: 85 })); }); @@ -139,7 +138,7 @@ describe('domainVisitsReducer', () => { const fallbackInterval: DateInterval = 'last30Days'; const state = reducer( undefined, - { type: fallbackToIntervalAction.toString(), payload: fallbackInterval }, + { type: getDomainVisits.fallbackToInterval.toString(), payload: fallbackInterval }, ); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); @@ -198,12 +197,12 @@ describe('domainVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 20)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last30Days' }, + { type: getDomainVisits.fallbackToInterval.toString(), payload: 'last30Days' }, 3, ], [ [Mock.of({ date: formatISO(subDays(now, 100)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last180Days' }, + { type: getDomainVisits.fallbackToInterval.toString(), payload: 'last180Days' }, 3, ], [[], expect.objectContaining({ type: getDomainVisits.fulfilled.toString() }), 2], diff --git a/test/visits/reducers/nonOrphanVisits.test.ts b/test/visits/reducers/nonOrphanVisits.test.ts index 89dc2671..33ad5db5 100644 --- a/test/visits/reducers/nonOrphanVisits.test.ts +++ b/test/visits/reducers/nonOrphanVisits.test.ts @@ -19,9 +19,8 @@ describe('nonOrphanVisitsReducer', () => { const visitsMocks = rangeOf(2, () => Mock.all()); const getNonOrphanVisitsCall = jest.fn(); const buildShlinkApiClient = () => Mock.of({ getNonOrphanVisits: getNonOrphanVisitsCall }); - const creator = getNonOrphanVisitsCreator(buildShlinkApiClient); - const { asyncThunk: getNonOrphanVisits, progressChangedAction, largeAction, fallbackToIntervalAction } = creator; - const { reducer, cancelGetVisits: cancelGetNonOrphanVisits } = nonOrphanVisitsReducerCreator(creator); + const getNonOrphanVisits = getNonOrphanVisitsCreator(buildShlinkApiClient); + const { reducer, cancelGetVisits: cancelGetNonOrphanVisits } = nonOrphanVisitsReducerCreator(getNonOrphanVisits); beforeEach(jest.clearAllMocks); @@ -34,7 +33,10 @@ describe('nonOrphanVisitsReducer', () => { }); it('returns loadingLarge on GET_NON_ORPHAN_VISITS_LARGE', () => { - const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: largeAction.toString() }); + const { loadingLarge } = reducer( + buildState({ loadingLarge: false }), + { type: getNonOrphanVisits.large.toString() }, + ); expect(loadingLarge).toEqual(true); }); @@ -110,7 +112,7 @@ describe('nonOrphanVisitsReducer', () => { }); it('returns new progress on GET_NON_ORPHAN_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: progressChangedAction.toString(), payload: 85 }); + const state = reducer(undefined, { type: getNonOrphanVisits.progressChanged.toString(), payload: 85 }); expect(state).toEqual(expect.objectContaining({ progress: 85 })); }); @@ -118,7 +120,7 @@ describe('nonOrphanVisitsReducer', () => { const fallbackInterval: DateInterval = 'last30Days'; const state = reducer( undefined, - { type: fallbackToIntervalAction.toString(), payload: fallbackInterval }, + { type: getNonOrphanVisits.fallbackToInterval.toString(), payload: fallbackInterval }, ); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); @@ -178,12 +180,12 @@ describe('nonOrphanVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 5)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last7Days' }, + { type: getNonOrphanVisits.fallbackToInterval.toString(), payload: 'last7Days' }, 3, ], [ [Mock.of({ date: formatISO(subDays(now, 200)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last365Days' }, + { type: getNonOrphanVisits.fallbackToInterval.toString(), payload: 'last365Days' }, 3, ], [[], expect.objectContaining({ type: getNonOrphanVisits.fulfilled.toString() }), 2], diff --git a/test/visits/reducers/orphanVisits.test.ts b/test/visits/reducers/orphanVisits.test.ts index 083b7fc4..27abb4c4 100644 --- a/test/visits/reducers/orphanVisits.test.ts +++ b/test/visits/reducers/orphanVisits.test.ts @@ -19,9 +19,8 @@ describe('orphanVisitsReducer', () => { const visitsMocks = rangeOf(2, () => Mock.all()); const getOrphanVisitsCall = jest.fn(); const buildShlinkApiClientMock = () => Mock.of({ getOrphanVisits: getOrphanVisitsCall }); - const creator = getOrphanVisitsCreator(buildShlinkApiClientMock); - const { asyncThunk: getOrphanVisits, largeAction, progressChangedAction, fallbackToIntervalAction } = creator; - const { reducer, cancelGetVisits: cancelGetOrphanVisits } = orphanVisitsReducerCreator(creator); + const getOrphanVisits = getOrphanVisitsCreator(buildShlinkApiClientMock); + const { reducer, cancelGetVisits: cancelGetOrphanVisits } = orphanVisitsReducerCreator(getOrphanVisits); beforeEach(jest.clearAllMocks); @@ -34,7 +33,7 @@ describe('orphanVisitsReducer', () => { }); it('returns loadingLarge on GET_ORPHAN_VISITS_LARGE', () => { - const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: largeAction.toString() }); + const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: getOrphanVisits.large.toString() }); expect(loadingLarge).toEqual(true); }); @@ -110,7 +109,7 @@ describe('orphanVisitsReducer', () => { }); it('returns new progress on GET_ORPHAN_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: progressChangedAction.toString(), payload: 85 }); + const state = reducer(undefined, { type: getOrphanVisits.progressChanged.toString(), payload: 85 }); expect(state).toEqual(expect.objectContaining({ progress: 85 })); }); @@ -118,7 +117,7 @@ describe('orphanVisitsReducer', () => { const fallbackInterval: DateInterval = 'last30Days'; const state = reducer( undefined, - { type: fallbackToIntervalAction.toString(), payload: fallbackInterval }, + { type: getOrphanVisits.fallbackToInterval.toString(), payload: fallbackInterval }, ); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); @@ -176,12 +175,12 @@ describe('orphanVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 5)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last7Days' }, + { type: getOrphanVisits.fallbackToInterval.toString(), payload: 'last7Days' }, 3, ], [ [Mock.of({ date: formatISO(subDays(now, 200)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last365Days' }, + { type: getOrphanVisits.fallbackToInterval.toString(), payload: 'last365Days' }, 3, ], [[], expect.objectContaining({ type: getOrphanVisits.fulfilled.toString() }), 2], diff --git a/test/visits/reducers/shortUrlVisits.test.ts b/test/visits/reducers/shortUrlVisits.test.ts index 6a2b7452..d59b7621 100644 --- a/test/visits/reducers/shortUrlVisits.test.ts +++ b/test/visits/reducers/shortUrlVisits.test.ts @@ -19,9 +19,8 @@ describe('shortUrlVisitsReducer', () => { const visitsMocks = rangeOf(2, () => Mock.all()); const getShortUrlVisitsCall = jest.fn(); const buildApiClientMock = () => Mock.of({ getShortUrlVisits: getShortUrlVisitsCall }); - const creator = getShortUrlVisitsCreator(buildApiClientMock); - const { asyncThunk: getShortUrlVisits, largeAction, progressChangedAction, fallbackToIntervalAction } = creator; - const { reducer, cancelGetVisits: cancelGetShortUrlVisits } = shortUrlVisitsReducerCreator(creator); + const getShortUrlVisits = getShortUrlVisitsCreator(buildApiClientMock); + const { reducer, cancelGetVisits: cancelGetShortUrlVisits } = shortUrlVisitsReducerCreator(getShortUrlVisits); beforeEach(jest.clearAllMocks); @@ -34,7 +33,10 @@ describe('shortUrlVisitsReducer', () => { }); it('returns loadingLarge on GET_SHORT_URL_VISITS_LARGE', () => { - const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: largeAction.toString() }); + const { loadingLarge } = reducer( + buildState({ loadingLarge: false }), + { type: getShortUrlVisits.large.toString() }, + ); expect(loadingLarge).toEqual(true); }); @@ -130,7 +132,7 @@ describe('shortUrlVisitsReducer', () => { }); it('returns new progress on GET_SHORT_URL_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: progressChangedAction.toString(), payload: 85 }); + const state = reducer(undefined, { type: getShortUrlVisits.progressChanged.toString(), payload: 85 }); expect(state).toEqual(expect.objectContaining({ progress: 85 })); }); @@ -138,7 +140,7 @@ describe('shortUrlVisitsReducer', () => { const fallbackInterval: DateInterval = 'last30Days'; const state = reducer( undefined, - { type: fallbackToIntervalAction.toString(), payload: fallbackInterval }, + { type: getShortUrlVisits.fallbackToInterval.toString(), payload: fallbackInterval }, ); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); @@ -220,12 +222,12 @@ describe('shortUrlVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 5)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last7Days' }, + { type: getShortUrlVisits.fallbackToInterval.toString(), payload: 'last7Days' }, 3, ], [ [Mock.of({ date: formatISO(subDays(now, 200)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last365Days' }, + { type: getShortUrlVisits.fallbackToInterval.toString(), payload: 'last365Days' }, 3, ], [[], expect.objectContaining({ type: getShortUrlVisits.fulfilled.toString() }), 2], diff --git a/test/visits/reducers/tagVisits.test.ts b/test/visits/reducers/tagVisits.test.ts index c96eca44..e8e4125c 100644 --- a/test/visits/reducers/tagVisits.test.ts +++ b/test/visits/reducers/tagVisits.test.ts @@ -19,9 +19,8 @@ describe('tagVisitsReducer', () => { const visitsMocks = rangeOf(2, () => Mock.all()); const getTagVisitsCall = jest.fn(); const buildShlinkApiClientMock = () => Mock.of({ getTagVisits: getTagVisitsCall }); - const creator = getTagVisitsCreator(buildShlinkApiClientMock); - const { asyncThunk: getTagVisits, fallbackToIntervalAction, largeAction, progressChangedAction } = creator; - const { reducer, cancelGetVisits: cancelGetTagVisits } = tagVisitsReducerCreator(creator); + const getTagVisits = getTagVisitsCreator(buildShlinkApiClientMock); + const { reducer, cancelGetVisits: cancelGetTagVisits } = tagVisitsReducerCreator(getTagVisits); beforeEach(jest.clearAllMocks); @@ -34,7 +33,7 @@ describe('tagVisitsReducer', () => { }); it('returns loadingLarge on GET_TAG_VISITS_LARGE', () => { - const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: largeAction.toString() }); + const { loadingLarge } = reducer(buildState({ loadingLarge: false }), { type: getTagVisits.large.toString() }); expect(loadingLarge).toEqual(true); }); @@ -130,13 +129,13 @@ describe('tagVisitsReducer', () => { }); it('returns new progress on GET_TAG_VISITS_PROGRESS_CHANGED', () => { - const state = reducer(undefined, { type: progressChangedAction.toString(), payload: 85 }); + const state = reducer(undefined, { type: getTagVisits.progressChanged.toString(), payload: 85 }); expect(state).toEqual(expect.objectContaining({ progress: 85 })); }); it('returns fallbackInterval on GET_TAG_VISITS_FALLBACK_TO_INTERVAL', () => { const fallbackInterval: DateInterval = 'last30Days'; - const state = reducer(undefined, { type: fallbackToIntervalAction.toString(), payload: fallbackInterval }); + const state = reducer(undefined, { type: getTagVisits.fallbackToInterval.toString(), payload: fallbackInterval }); expect(state).toEqual(expect.objectContaining({ fallbackInterval })); }); @@ -194,12 +193,12 @@ describe('tagVisitsReducer', () => { it.each([ [ [Mock.of({ date: formatISO(subDays(now, 20)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last30Days' }, + { type: getTagVisits.fallbackToInterval.toString(), payload: 'last30Days' }, 3, ], [ [Mock.of({ date: formatISO(subDays(now, 100)) })], - { type: fallbackToIntervalAction.toString(), payload: 'last180Days' }, + { type: getTagVisits.fallbackToInterval.toString(), payload: 'last180Days' }, 3, ], [[], expect.objectContaining({ type: getTagVisits.fulfilled.toString() }), 2],