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 { useGetList } from './useGetList'; import { PaginationPayload, SortPayload } from '../types'; import { testDataProvider } from './testDataProvider'; const UseGetList = ({ resource = 'posts', pagination = { page: 1, perPage: 10 }, sort = { field: 'id', order: 'DESC' } as const, filter = {}, options = {}, meta = undefined, callback = null, }: { resource?: string; pagination?: PaginationPayload; sort?: SortPayload; filter?: any; options?: any; meta?: any; callback?: any; }) => { const hookValue = useGetList( resource, { pagination, sort, filter, meta }, options ); if (callback) callback(hookValue); return
hello
; }; describe('useGetList', () => { it('should call dataProvider.getList() on mount', async () => { const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.resolve({ data: [{ id: 1, title: 'foo' }], total: 1, }) ), }); render( ); await waitFor(() => { expect(dataProvider.getList).toBeCalledTimes(1); expect(dataProvider.getList).toBeCalledWith('posts', { filter: {}, pagination: { page: 1, perPage: 20 }, sort: { field: 'id', order: 'DESC' }, signal: undefined, }); }); }); it('should not call the dataProvider on update', async () => { const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.resolve({ data: [{ id: 1, title: 'foo' }], total: 1 }) ), }); const { rerender } = render( ); await waitFor(() => { expect(dataProvider.getList).toBeCalledTimes(1); }); rerender( ); await waitFor(() => { expect(dataProvider.getList).toBeCalledTimes(1); }); }); it('should call the dataProvider on update when the resource changes', async () => { const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.resolve({ data: [{ id: 1, title: 'foo' }], total: 1 }) ), }); const { rerender } = render( ); await waitFor(() => { expect(dataProvider.getList).toBeCalledTimes(1); }); rerender( ); await waitFor(() => { expect(dataProvider.getList).toBeCalledTimes(2); }); }); it('should accept a meta parameter', async () => { const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.resolve({ data: [{ id: 1, title: 'foo' }], total: 1 }) ), }); render( ); await waitFor(() => { expect(dataProvider.getList).toBeCalledWith('posts', { filter: {}, pagination: { page: 1, perPage: 20 }, sort: { field: 'id', order: 'DESC' }, meta: { hello: 'world' }, signal: undefined, }); }); }); it('should return initial data based on Query Cache', async () => { const callback = jest.fn(); const queryClient = new QueryClient(); queryClient.setQueryData( [ 'posts', 'getList', { pagination: { page: 1, perPage: 10 }, sort: { field: 'id', order: 'DESC' }, filter: {}, }, ], { data: [{ id: 1, title: 'cached' }], total: 1, } ); const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.resolve({ data: [{ id: 1, title: 'live' }], total: 1 }) ), }); render( ); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: [{ id: 1, title: 'cached' }] }) ); }); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: [{ id: 1, title: 'live' }] }) ); }); }); it('should return isFetching false once the dataProvider returns', async () => { const callback = jest.fn(); const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.resolve({ data: [ { id: 1, title: 'foo' }, { id: 2, title: 'bar' }, ], total: 2, }) ), }); render( ); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ isFetching: true }) ); }); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ isFetching: false }) ); }); }); it('should set the error state when the dataProvider fails', async () => { jest.spyOn(console, 'error').mockImplementation(() => {}); const callback = jest.fn(); const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.reject(new Error('failed'))), }); render( ); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ error: null }) ); }); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ error: new Error('failed') }) ); }); }); it('should execute success side effects on success', async () => { const onSuccess1 = jest.fn(); const onSuccess2 = jest.fn(); const dataProvider = testDataProvider({ // @ts-ignore getList: jest .fn() .mockReturnValueOnce( Promise.resolve({ data: [ { id: 1, title: 'foo' }, { id: 2, title: 'bar' }, ], total: 2, }) ) .mockReturnValueOnce( Promise.resolve({ data: [ { id: 3, foo: 1 }, { id: 4, foo: 2 }, ], total: 2, }) ), }); render( ); await waitFor(() => { expect(onSuccess1).toBeCalledTimes(1); expect(onSuccess1.mock.calls.pop()[0]).toEqual({ data: [ { id: 1, title: 'foo' }, { id: 2, title: 'bar' }, ], total: 2, }); expect(onSuccess2).toBeCalledTimes(1); expect(onSuccess2.mock.calls.pop()[0]).toEqual({ data: [ { id: 3, foo: 1 }, { id: 4, foo: 2 }, ], total: 2, }); }); }); it('should execute error side effects on failure', async () => { jest.spyOn(console, 'error').mockImplementation(() => {}); const onError = jest.fn(); const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.reject(new Error('failed'))), }); render( ); await waitFor(() => { expect(onError).toBeCalledTimes(1); expect(onError.mock.calls.pop()[0]).toEqual(new Error('failed')); }); }); it('should pre-populate getOne Query Cache', async () => { const callback = jest.fn(); const queryClient = new QueryClient(); const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.resolve({ data: [{ id: 1, title: 'live' }], total: 1 }) ), }); render( ); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: [{ id: 1, title: 'live' }] }) ); }); expect( queryClient.getQueryData(['posts', 'getOne', { id: '1' }]) ).toEqual({ id: 1, title: 'live' }); }); it('should still pre-populate getOne Query Cache with custom onSuccess', async () => { const callback = jest.fn(); const onSuccess = jest.fn(); const queryClient = new QueryClient(); const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.resolve({ data: [{ id: 1, title: 'live' }], total: 1 }) ), }); render( ); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: [{ id: 1, title: 'live' }] }) ); }); await waitFor(() => { expect(onSuccess).toHaveBeenCalledWith( expect.objectContaining({ data: [{ id: 1, title: 'live' }] }) ); }); expect( queryClient.getQueryData(['posts', 'getOne', { id: '1' }]) ).toEqual({ id: 1, title: 'live' }); }); it('should not pre-populate getOne Query Cache if more than 100 results', async () => { const callback: any = jest.fn(); const queryClient = new QueryClient(); const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn(() => Promise.resolve({ data: Array.from(Array(101).keys()).map(index => ({ id: index + 1, title: `item ${index + 1}`, })), total: 101, }) ), }); render( ); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: expect.arrayContaining([ { id: 1, title: 'item 1' }, { id: 101, title: 'item 101' }, ]), }) ); }); expect( queryClient.getQueryData(['posts', 'getOne', { id: '1' }]) ).toBeUndefined(); }); it('should abort the request if the query is canceled', async () => { const abort = jest.fn(); const dataProvider = testDataProvider({ getList: 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.getList).toHaveBeenCalled(); }); queryClient.cancelQueries({ queryKey: ['posts', 'getList'], }); await waitFor(() => { expect(abort).toHaveBeenCalled(); }); }); });