import type { TagDescription } from '@reduxjs/toolkit/query' import { createApi, fakeBaseQuery } from '@reduxjs/toolkit/query' import { waitFor } from '@testing-library/react' import { delay } from 'msw' import { setupApiStore } from '../../tests/utils/helpers' const tagTypes = [ 'apple', 'pear', 'banana', 'tomato', 'cat', 'dog', 'giraffe', ] as const type TagTypes = (typeof tagTypes)[number] type ProvidedTags = TagDescription[] type InvalidatesTags = (ProvidedTags[number] | null | undefined)[] /** providesTags, invalidatesTags, shouldInvalidate */ const caseMatrix: [ProvidedTags, InvalidatesTags, boolean][] = [ // ***************************** // basic invalidation behavior // ***************************** // string [['apple'], ['apple'], true], [['apple'], ['pear'], false], // string and type only behave identical [[{ type: 'apple' }], ['apple'], true], [[{ type: 'apple' }], ['pear'], false], [['apple'], [{ type: 'apple' }], true], [['apple'], [{ type: 'pear' }], false], // type only invalidates type + id [[{ type: 'apple', id: 1 }], [{ type: 'apple' }], true], [[{ type: 'pear', id: 1 }], ['apple'], false], // type + id never invalidates type only [['apple'], [{ type: 'apple', id: 1 }], false], [['pear'], [{ type: 'apple', id: 1 }], false], // type + id invalidates type + id [[{ type: 'apple', id: 1 }], [{ type: 'apple', id: 1 }], true], [[{ type: 'apple', id: 1 }], [{ type: 'apple', id: 2 }], false], // null and undefined [['apple'], [null], false], [['apple'], [undefined], false], [['apple'], [null, 'apple'], true], [['apple'], [undefined, 'apple'], true], // ***************************** // test multiple values in array // ***************************** [['apple', 'banana', 'tomato'], ['apple'], true], [['apple'], ['pear', 'banana', 'tomato'], false], [ [ { type: 'apple', id: 1 }, { type: 'apple', id: 3 }, { type: 'apple', id: 4 }, ], [{ type: 'apple', id: 1 }], true, ], [ [{ type: 'apple', id: 1 }], [ { type: 'apple', id: 2 }, { type: 'apple', id: 3 }, { type: 'apple', id: 4 }, ], false, ], ] test.each(caseMatrix)( '\tprovidesTags: %O,\n\tinvalidatesTags: %O,\n\tshould invalidate: %s', async (providesTags, invalidatesTags, shouldInvalidate) => { let queryCount = 0 const { store, api, api: { endpoints: { invalidating, providing, unrelated }, }, } = setupApiStore( createApi({ baseQuery: fakeBaseQuery(), tagTypes, endpoints: (build) => ({ providing: build.query({ queryFn() { queryCount++ return { data: {} } }, providesTags, }), unrelated: build.query({ queryFn() { return { data: {} } }, providesTags: ['cat', 'dog', { type: 'giraffe', id: 8 }], }), invalidating: build.mutation({ queryFn() { return { data: {} } }, invalidatesTags, }), }), }), undefined, { withoutTestLifecycles: true }, ) store.dispatch(providing.initiate()) store.dispatch(unrelated.initiate()) expect(queryCount).toBe(1) await waitFor(() => { expect(api.endpoints.providing.select()(store.getState()).status).toBe( 'fulfilled', ) expect(api.endpoints.unrelated.select()(store.getState()).status).toBe( 'fulfilled', ) }) const toInvalidate = api.util.selectInvalidatedBy( store.getState(), invalidatesTags, ) if (shouldInvalidate) { expect(toInvalidate).toEqual([ { queryCacheKey: 'providing(undefined)', endpointName: 'providing', originalArgs: undefined, }, ]) } else { expect(toInvalidate).toEqual([]) } store.dispatch(invalidating.initiate()) expect(queryCount).toBe(1) await delay(2) expect(queryCount).toBe(shouldInvalidate ? 2 : 1) }, )