|
import { UseInfiniteQueryResult } from 'react-query' |
|
import { combineQueryResultStatuses, mergeInfiniteQueryResults } from './mergeInfiniteQueryResults' |
|
|
|
// Define or import your types: |
|
type Entity = { ... } |
|
type QueryKey = '...' // or use the generic one: `import { QueryKey } from 'react-query'` |
|
|
|
// Define or import your own dummy data: |
|
const infiniteQueryResult: UseInfiniteQueryResult<Entity, QueryKey> = { /* ... */ } |
|
const entities: Entity[] = [ /* ... */ ] |
|
|
|
// End of customizations |
|
|
|
function isQueryResultProp<T, U>(propName: string): propName is keyof UseInfiniteQueryResult<T, U> { |
|
return Object.prototype.hasOwnProperty.call(infiniteQueryResult, propName) |
|
} |
|
|
|
describe('combining react-query results', () => { |
|
describe('combineQueryResultStatuses', () => { |
|
test('when one of them has an error', () => { |
|
const alpha: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'error', |
|
} |
|
const bravo: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'loading', |
|
} |
|
const charlie: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'idle', |
|
} |
|
const delta: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'success', |
|
} |
|
|
|
expect(combineQueryResultStatuses([alpha, bravo, charlie, delta])).toBe('error') |
|
}) |
|
|
|
test('when one of them is loading and none have errors', () => { |
|
const bravo: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'loading', |
|
} |
|
const charlie: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'idle', |
|
} |
|
const delta: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'success', |
|
} |
|
|
|
expect(combineQueryResultStatuses([bravo, charlie, delta])).toBe('loading') |
|
}) |
|
|
|
test('when one of them is successful and none are loading or have errors', () => { |
|
const charlie: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'idle', |
|
} |
|
const delta: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'success', |
|
} |
|
|
|
expect(combineQueryResultStatuses([charlie, delta])).toBe('success') |
|
}) |
|
|
|
test('when all are idle', () => { |
|
const charlie: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'idle', |
|
} |
|
const delta: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
status: 'idle', |
|
} |
|
|
|
expect(combineQueryResultStatuses([charlie, delta])).toBe('idle') |
|
}) |
|
}) |
|
|
|
describe('mergeInfiniteQueryResults', () => { |
|
test('combined data lists are sorted and de-duped', () => { |
|
const alpha = entities[0] |
|
const bravo = entities[1] |
|
const charlie = entities[0] |
|
const delta = entities[1] |
|
|
|
// Insert the items out of order so we can check that they're sorted later |
|
const test1: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
data: [delta, alpha], |
|
} |
|
const test2: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
// Note that `alpha` appears in both sets |
|
data: [alpha, bravo, charlie], |
|
} |
|
|
|
// Verify test setup |
|
expect(test1.data.concat(test2.data).length).toBe(5) |
|
|
|
// Run test |
|
const merged = mergeInfiniteQueryResults([test1, test2], (a, b) => (a.lastChanged > b.lastChanged ? -1 : 1)) |
|
|
|
expect(merged.data.length).toBe(4) |
|
expect(merged.data[0].id).toBe(alpha.id) |
|
expect(merged.data[1].id).toBe(bravo.id) |
|
expect(merged.data[2].id).toBe(charlie.id) |
|
expect(merged.data[3].id).toBe(delta.id) |
|
}) |
|
|
|
describe('boolean props', () => { |
|
test.each(['isSuccess', 'isFetched', 'isFetchedAfterMount', 'isIdle'])( |
|
'prop that is only true when ALL results have it set to true: %p', |
|
(propName) => { |
|
if (!isQueryResultProp(propName)) { |
|
throw new Error(`Property name is not valid for a UseInfiniteQuery result: ${propName}`) |
|
} |
|
|
|
const isTrue1: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: true, |
|
} |
|
const isTrue2: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: true, |
|
} |
|
const isFalse1: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: false, |
|
} |
|
const isFalse2: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: false, |
|
} |
|
|
|
expect(mergeInfiniteQueryResults([isTrue1])[propName]).toBe(true) |
|
expect(mergeInfiniteQueryResults([isTrue2])[propName]).toBe(true) |
|
expect(mergeInfiniteQueryResults([isFalse1])[propName]).toBe(false) |
|
expect(mergeInfiniteQueryResults([isFalse2])[propName]).toBe(false) |
|
expect(mergeInfiniteQueryResults([isTrue1, isTrue2])[propName]).toBe(true) |
|
expect(mergeInfiniteQueryResults([isTrue1, isFalse1])[propName]).toBe(false) |
|
expect(mergeInfiniteQueryResults([isFalse1, isTrue1])[propName]).toBe(false) |
|
expect(mergeInfiniteQueryResults([isFalse1, isFalse2])[propName]).toBe(false) |
|
} |
|
) |
|
|
|
test.each([ |
|
'isLoading', |
|
'hasNextPage', |
|
'hasPreviousPage', |
|
'isFetching', |
|
'isFetchingNextPage', |
|
'isFetchingPreviousPage', |
|
'isError', |
|
'isLoadingError', |
|
'isPreviousData', |
|
'isRefetchError', |
|
'isRefetching', |
|
'isStale', |
|
'isPlaceholderData', |
|
])('prop that can be true if ANY of the results have it as true: %p', (propName) => { |
|
if (!isQueryResultProp(propName)) { |
|
throw new Error(`Property name is not valid for a UseInfiniteQuery result: ${propName}`) |
|
} |
|
|
|
const isTrue1: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: true, |
|
} |
|
const isTrue2: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: true, |
|
} |
|
const isFalse1: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: false, |
|
} |
|
const isFalse2: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: false, |
|
} |
|
|
|
expect(mergeInfiniteQueryResults([isTrue1])[propName]).toBe(true) |
|
expect(mergeInfiniteQueryResults([isTrue2])[propName]).toBe(true) |
|
expect(mergeInfiniteQueryResults([isFalse1])[propName]).toBe(false) |
|
expect(mergeInfiniteQueryResults([isFalse2])[propName]).toBe(false) |
|
expect(mergeInfiniteQueryResults([isTrue1, isTrue2])[propName]).toBe(true) |
|
expect(mergeInfiniteQueryResults([isTrue1, isFalse1])[propName]).toBe(true) |
|
expect(mergeInfiniteQueryResults([isFalse1, isTrue1])[propName]).toBe(true) |
|
expect(mergeInfiniteQueryResults([isFalse1, isFalse2])[propName]).toBe(false) |
|
}) |
|
}) |
|
|
|
describe('numeric values', () => { |
|
test.each(['failureCount', 'serverTotal', 'errorUpdateCount'])('sums the values for: %p', (propName) => { |
|
if (!isQueryResultProp(propName)) { |
|
throw new Error(`Property name is not valid for a UseInfiniteQuery result: ${propName}`) |
|
} |
|
|
|
const alpha: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: 1, |
|
} |
|
const bravo: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: 7, |
|
} |
|
|
|
expect(mergeInfiniteQueryResults([alpha])[propName]).toBe(1) |
|
expect(mergeInfiniteQueryResults([bravo])[propName]).toBe(7) |
|
expect(mergeInfiniteQueryResults([alpha, bravo])[propName]).toBe(8) |
|
}) |
|
}) |
|
|
|
describe('fetching methods', () => { |
|
test.each(['fetchNextPage', 'fetchPreviousPage', 'refetch', 'remove'])( |
|
'calls the %p method from each result', |
|
(propName) => { |
|
if (!isQueryResultProp(propName)) { |
|
throw new Error(`Property name is not valid for a UseInfiniteQuery result: ${propName}`) |
|
} |
|
|
|
const alphaMock = jest.fn() |
|
const bravoMock = jest.fn() |
|
|
|
const alpha: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: () => |
|
new Promise(() => { |
|
alphaMock() |
|
}), |
|
} |
|
const bravo: UseInfiniteQueryResult<Entity, QueryKey> = { |
|
...infiniteQueryResult, |
|
[propName]: () => |
|
new Promise(() => { |
|
bravoMock() |
|
}), |
|
} |
|
|
|
const merged = mergeInfiniteQueryResults([alpha, bravo]) |
|
|
|
// Tell TS that this is callable |
|
const func = merged[propName] |
|
|
|
if (!(func instanceof Function)) { |
|
throw new Error(`Property is not callable: ${propName}`) |
|
} |
|
|
|
func() |
|
|
|
expect(alphaMock).toBeCalledTimes(1) |
|
expect(bravoMock).toBeCalledTimes(1) |
|
} |
|
) |
|
}) |
|
}) |
|
}) |