import * as React from 'react';
import expect from 'expect';
import { render, waitFor } from '@testing-library/react';
import { QueryClient } from '@tanstack/react-query';
import { CoreAdminContext } from '../core';
import { useGetMany } from './useGetMany';
import { useGetOne } from './useGetOne';
import { testDataProvider } from '../dataProvider';
const UseGetMany = ({
resource,
ids,
meta = undefined,
options = {},
callback = undefined,
}: {
resource: string;
ids: any[];
meta?: any;
options?: any;
callback?: Function;
}) => {
const hookValue = useGetMany(resource, { ids, meta }, options);
if (callback) callback(hookValue);
return
hello
;
};
let updateState;
const UseCustomGetMany = ({
resource,
ids,
options = {},
callback = undefined,
}: {
resource: string;
ids: any[];
options?: any;
callback?: Function;
}) => {
const [stateIds, setStateIds] = React.useState(ids);
const hookValue = useGetMany(resource, { ids: stateIds }, options);
if (callback) callback(hookValue);
updateState = newIds => {
setStateIds(newIds);
};
return hello
;
};
describe('useGetMany', () => {
let dataProvider;
beforeEach(() => {
dataProvider = testDataProvider({
getMany: jest
.fn()
.mockResolvedValue({ data: [{ id: 1, title: 'foo' }] }),
});
});
it('should call dataProvider.getMany() on mount', async () => {
render(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
expect(dataProvider.getMany).toHaveBeenCalledWith('posts', {
ids: [1],
signal: undefined,
});
});
});
it('should not call dataProvider.getMany() on mount if enabled is false', async () => {
const { rerender } = render(
);
await new Promise(resolve => setTimeout(resolve));
expect(dataProvider.getMany).toHaveBeenCalledTimes(0);
rerender(
);
await new Promise(resolve => setTimeout(resolve));
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
});
it('should not call dataProvider.getMany() on update', async () => {
const { rerender } = render(
);
await new Promise(resolve => setTimeout(resolve));
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
rerender(
);
await new Promise(resolve => setTimeout(resolve));
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
});
it('should recall dataProvider.getMany() when ids changes', async () => {
const { rerender } = render(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
});
rerender(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(2);
});
});
it('should recall dataProvider.getMany() when resource changes', async () => {
const { rerender } = render(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
});
rerender(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(2);
});
});
it('should accept a meta parameter', async () => {
render(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
expect(dataProvider.getMany).toHaveBeenCalledWith('posts', {
ids: [1],
meta: { hello: 'world' },
signal: undefined,
});
});
});
it('should use data from query cache on mount', async () => {
const FecthGetMany = () => {
useGetMany('posts', { ids: ['1'] });
return dummy;
};
const hookValue = jest.fn();
const { rerender } = render(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
});
rerender(
);
expect(hookValue).toHaveBeenCalledWith(
expect.objectContaining({
data: [{ id: 1, title: 'foo' }],
isFetching: true,
isLoading: false,
error: null,
})
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(2);
});
await waitFor(() => {
expect(hookValue).toHaveBeenCalledWith(
expect.objectContaining({
data: [{ id: 1, title: 'foo' }],
isFetching: false,
isLoading: false,
error: null,
})
);
});
});
it('should set the error state when the dataProvider fails', async () => {
jest.spyOn(console, 'error').mockImplementation(() => {});
const hookValue = jest.fn();
const dataProvider = testDataProvider({
getMany: jest.fn().mockRejectedValue(new Error('failed')),
});
render(
);
expect(hookValue).toHaveBeenCalledWith(
expect.objectContaining({
error: null,
})
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
});
await waitFor(() => {
expect(hookValue).toHaveBeenCalledWith(
expect.objectContaining({
error: new Error('failed'),
})
);
});
});
it('should execute success side effects on success', async () => {
const onSuccess = jest.fn();
render(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
expect(onSuccess).toHaveBeenCalledWith([{ id: 1, title: 'foo' }]);
});
});
it('should execute error side effects on failure', async () => {
jest.spyOn(console, 'error').mockImplementationOnce(() => {});
const dataProvider = testDataProvider({
getMany: jest.fn().mockRejectedValue(new Error('failed')),
});
const onError = jest.fn();
render(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalledTimes(1);
expect(onError).toHaveBeenCalledWith(new Error('failed'));
});
});
it('should update loading state when ids change', async () => {
const dataProvider = testDataProvider({
// @ts-ignore
getMany: jest.fn((resource, params) => {
if (params.ids.length === 1) {
return Promise.resolve({
data: [{ id: 1, title: 'foo' }],
});
} else {
return Promise.resolve({
data: [
{ id: 1, title: 'foo' },
{ id: 2, title: 'bar' },
],
});
}
}),
});
const hookValue = jest.fn();
render(
);
await waitFor(() => {
expect(dataProvider.getMany).toBeCalledTimes(1);
});
expect(hookValue.mock.calls[0][0]).toStrictEqual(
expect.objectContaining({
data: undefined,
isError: false,
isFetching: true,
isLoading: true,
})
);
await waitFor(() => {
expect(hookValue.mock.calls[1][0]).toStrictEqual(
expect.objectContaining({
data: [{ id: 1, title: 'foo' }],
isError: false,
isFetching: false,
isLoading: false,
})
);
});
// Updating ids...
updateState([1, 2]);
await waitFor(() => {
expect(dataProvider.getMany).toBeCalledTimes(2);
});
await waitFor(() => {
expect(hookValue).toBeCalledTimes(4);
});
expect(hookValue.mock.calls[2][0]).toStrictEqual(
expect.objectContaining({
data: undefined,
isError: false,
isFetching: true,
isLoading: true,
})
);
expect(hookValue.mock.calls[3][0]).toStrictEqual(
expect.objectContaining({
data: [
{ id: 1, title: 'foo' },
{ id: 2, title: 'bar' },
],
isError: false,
isFetching: false,
isLoading: false,
})
);
});
it('should abort the request if the query is canceled', async () => {
const abort = jest.fn();
const dataProvider = testDataProvider({
getMany: jest.fn(
(_resource, { signal }) =>
new Promise(() => {
signal.addEventListener('abort', () => {
abort(signal.reason);
});
})
) as any,
});
dataProvider.supportAbortSignal = true;
const queryClient = new QueryClient();
render(
);
await waitFor(() => {
expect(dataProvider.getMany).toHaveBeenCalled();
});
queryClient.cancelQueries({
queryKey: ['posts', 'getMany', { ids: ['1'] }],
});
await waitFor(() => {
expect(abort).toHaveBeenCalled();
});
});
describe('TypeScript', () => {
it('should return the parametric type', () => {
type Foo = { id: number; name: string };
const _Dummy = () => {
const { data, error, isPending } = useGetMany('posts', {
ids: [1],
});
if (isPending || error) return null;
return {data[0].name}
;
};
// no render needed, only checking types
});
it('should accept empty id param', () => {
const _Dummy = () => {
type Post = {
id: number;
tag_ids: number[];
};
const { data: comment } = useGetOne('comments', {
id: 1,
});
type Tag = {
id: number;
name: string;
};
const { data, error, isPending } = useGetMany('posts', {
ids: comment?.tag_ids,
});
if (isPending || error) return null;
return (
{data.map(tag => (
- {tag.name}
))}
);
};
// no render needed, only checking types
});
});
});