UNPKG

@reduxjs/toolkit

Version:

The official, opinionated, batteries-included toolset for efficient Redux development

215 lines (176 loc) 5.82 kB
import { createSlice } from '@reduxjs/toolkit' import { createApi } from '@reduxjs/toolkit/query' import { delay } from 'msw' import { setupApiStore } from '../../tests/utils/helpers' let shouldApiResponseSuccess = true const baseQuery = (args?: any) => ({ data: args }) const api = createApi({ baseQuery, tagTypes: ['SUCCEED', 'FAILED'], endpoints: (build) => ({ getUser: build.query<{ url: string; success: boolean }, number>({ query(id) { return { url: `user/${id}`, success: shouldApiResponseSuccess } }, providesTags: (result) => (result?.success ? ['SUCCEED'] : ['FAILED']), }), }), }) const { getUser } = api.endpoints const authSlice = createSlice({ name: 'auth', initialState: { token: '1234', }, reducers: { setToken(state, action) { state.token = action.payload }, }, }) const storeRef = setupApiStore(api, { auth: authSlice.reducer }) describe('buildSlice', () => { beforeEach(() => { shouldApiResponseSuccess = true }) it('only resets the api state when resetApiState is dispatched', async () => { storeRef.store.dispatch({ type: 'unrelated' }) // trigger "registered middleware" into place const initialState = storeRef.store.getState() await storeRef.store.dispatch( getUser.initiate(1, { subscriptionOptions: { pollingInterval: 10 } }), ) const initialQueryState = { api: { config: { focused: true, invalidationBehavior: 'delayed', keepUnusedDataFor: 60, middlewareRegistered: true, online: true, reducerPath: 'api', refetchOnFocus: false, refetchOnMountOrArgChange: false, refetchOnReconnect: false, }, mutations: {}, provided: expect.any(Object), queries: { 'getUser(1)': { data: { success: true, url: 'user/1', }, endpointName: 'getUser', fulfilledTimeStamp: expect.any(Number), originalArgs: 1, requestId: expect.any(String), startedTimeStamp: expect.any(Number), status: 'fulfilled', }, }, // Filled some time later subscriptions: {}, }, auth: { token: '1234', }, } expect(storeRef.store.getState()).toEqual(initialQueryState) storeRef.store.dispatch(api.util.resetApiState()) expect(storeRef.store.getState()).toEqual(initialState) }) it('replaces previous tags with new provided tags', async () => { await storeRef.store.dispatch(getUser.initiate(1)) expect( api.util.selectInvalidatedBy(storeRef.store.getState(), ['SUCCEED']), ).toHaveLength(1) expect( api.util.selectInvalidatedBy(storeRef.store.getState(), ['FAILED']), ).toHaveLength(0) shouldApiResponseSuccess = false storeRef.store.dispatch(getUser.initiate(1)).refetch() await delay(10) expect( api.util.selectInvalidatedBy(storeRef.store.getState(), ['SUCCEED']), ).toHaveLength(0) expect( api.util.selectInvalidatedBy(storeRef.store.getState(), ['FAILED']), ).toHaveLength(1) }) }) describe('`merge` callback', () => { const baseQuery = (args?: any) => ({ data: args }) interface Todo { id: string text: string } it('Calls `merge` once there is existing data, and allows mutations of cache state', async () => { let mergeCalled = false let queryFnCalls = 0 const todoTexts = ['A', 'B', 'C', 'D'] const api = createApi({ baseQuery, endpoints: (build) => ({ getTodos: build.query<Todo[], void>({ async queryFn() { const text = todoTexts[queryFnCalls] return { data: [{ id: `${queryFnCalls++}`, text }] } }, merge(currentCacheValue, responseData) { mergeCalled = true currentCacheValue.push(...responseData) }, }), }), }) const storeRef = setupApiStore(api, undefined, { withoutTestLifecycles: true, }) const selectTodoEntry = api.endpoints.getTodos.select() const res = storeRef.store.dispatch(api.endpoints.getTodos.initiate()) await res expect(mergeCalled).toBe(false) const todoEntry1 = selectTodoEntry(storeRef.store.getState()) expect(todoEntry1.data).toEqual([{ id: '0', text: 'A' }]) res.refetch() await delay(10) expect(mergeCalled).toBe(true) const todoEntry2 = selectTodoEntry(storeRef.store.getState()) expect(todoEntry2.data).toEqual([ { id: '0', text: 'A' }, { id: '1', text: 'B' }, ]) }) it('Allows returning a different value from `merge`', async () => { let firstQueryFnCall = true const api = createApi({ baseQuery, endpoints: (build) => ({ getTodos: build.query<Todo[], void>({ async queryFn() { const item = firstQueryFnCall ? { id: '0', text: 'A' } : { id: '1', text: 'B' } firstQueryFnCall = false return { data: [item] } }, merge(currentCacheValue, responseData) { return responseData }, }), }), }) const storeRef = setupApiStore(api, undefined, { withoutTestLifecycles: true, }) const selectTodoEntry = api.endpoints.getTodos.select() const res = storeRef.store.dispatch(api.endpoints.getTodos.initiate()) await res const todoEntry1 = selectTodoEntry(storeRef.store.getState()) expect(todoEntry1.data).toEqual([{ id: '0', text: 'A' }]) res.refetch() await delay(10) const todoEntry2 = selectTodoEntry(storeRef.store.getState()) expect(todoEntry2.data).toEqual([{ id: '1', text: 'B' }]) }) })