import * as React from 'react'; import expect from 'expect'; import { screen, render, waitFor, fireEvent } from '@testing-library/react'; import { Basic, PageInfo } from './useInfiniteGetList.stories'; import { QueryClient } from '@tanstack/react-query'; import { testDataProvider } from './testDataProvider'; import { PaginationPayload, SortPayload } from '../types'; import { useInfiniteGetList } from './useInfiniteGetList'; import { CoreAdminContext } from '../core'; describe('useInfiniteGetList', () => { const UseInfiniteGetList = ({ 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 = useInfiniteGetList( resource, { pagination, sort, filter, meta }, options ); if (callback) callback(hookValue); return
hello
; }; it('should call dataProvider.getList() on mount', async () => { const dataProvider = { getList: jest.fn(() => Promise.resolve({ data: [{ id: 73, name: 'France', code: 'FR' }], total: 1, }) ), } as any; render(); await waitFor(() => { expect(dataProvider.getList).toBeCalledTimes(1); expect(dataProvider.getList).toBeCalledWith('heroes', { filter: {}, pagination: { page: 1, perPage: 20 }, sort: { field: 'id', order: 'DESC' }, signal: undefined, }); }); }); it('should not call the dataProvider on update', async () => { const dataProvider = { getList: jest.fn(() => Promise.resolve({ data: [{ id: 73, name: 'France', code: 'FR' }], total: 1, }) ), } as any; 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 = { getList: jest.fn(() => Promise.resolve({ data: [{ id: 73, name: 'France', code: 'FR' }], total: 1, }) ), } as any; 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 = { getList: jest.fn(() => Promise.resolve({ data: [{ id: 73, name: 'France', code: 'FR' }], total: 1, }) ), } as any; render( ); await waitFor(() => { expect(dataProvider.getList).toBeCalledWith('heroes', { filter: {}, pagination: { page: 1, perPage: 20 }, sort: { field: 'id', order: 'DESC' }, meta: { hello: 'world' }, signal: undefined, }); }); }); it('should call success side effects on success', async () => { const onSuccess1 = jest.fn(); const countries = [ { id: 73, name: 'France', code: 'FR' }, { id: 74, name: 'Italia', code: 'IT' }, ]; const dataProvider = { getList: (resource, params) => { return Promise.resolve({ data: countries.slice( (params.pagination.page - 1) * params.pagination.perPage, (params.pagination.page - 1) * params.pagination.perPage + params.pagination.perPage ), total: countries.length, }); }, }; render( ); await waitFor(async () => { expect(onSuccess1).toBeCalledTimes(1); expect(screen.getByLabelText('country').innerHTML).toContain( 'France' ); screen.getByLabelText('refetch-button').click(); await waitFor(async () => { expect(onSuccess1).toBeCalledTimes(2); expect(screen.queryAllByLabelText('country')).toHaveLength(2); }); }); }); 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((_resource, { pagination: { page, perPage } }) => Promise.resolve({ data: Array.from(Array(perPage).keys()).map(index => ({ id: index + 1 + (page - 1) * perPage, title: `item ${index + 1 + (page - 1) * perPage}`, })), total: perPage * 2, }) ), }); render( ); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ pages: expect.arrayContaining([ 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 not pre-populate getOne Query Cache if more than 100 results across several pages', async () => { let hookValue; const callback: any = jest.fn(value => { hookValue = value; }); const queryClient = new QueryClient(); const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn((_resource, { pagination: { page, perPage } }) => Promise.resolve({ data: Array.from(Array(perPage).keys()).map(index => ({ id: index + 1 + (page - 1) * perPage, title: `item ${index + 1 + (page - 1) * perPage}`, })), total: perPage * 2, }) ), }); render( ); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ pages: expect.arrayContaining([ expect.objectContaining({ data: expect.arrayContaining([ { id: 1, title: 'item 1' }, { id: 51, title: 'item 51' }, ]), }), ]), }), }) ); }); expect( queryClient.getQueryData(['posts', 'getOne', { id: '1' }]) ).toBeDefined(); expect( queryClient.getQueryData(['posts', 'getOne', { id: '51' }]) ).toBeDefined(); expect( queryClient.getQueryData(['posts', 'getOne', { id: '52' }]) ).not.toBeDefined(); // Fetch next page hookValue.fetchNextPage(); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ pages: expect.arrayContaining([ expect.objectContaining({ data: expect.arrayContaining([ { id: 52, title: 'item 52' }, { id: 102, title: 'item 102' }, ]), }), ]), }), }) ); }); expect( queryClient.getQueryData(['posts', 'getOne', { id: '1' }]) ).toBeDefined(); expect( queryClient.getQueryData(['posts', 'getOne', { id: '51' }]) ).toBeDefined(); // query data for item 52 should still be undefined expect( queryClient.getQueryData(['posts', 'getOne', { id: '52' }]) ).not.toBeDefined(); }); it('should only populate the getOne Query Cache with the records from the last fetched page', async () => { let hookValue; const callback: any = jest.fn(value => { hookValue = value; }); const queryClient = new QueryClient(); const dataProvider = testDataProvider({ // @ts-ignore getList: jest.fn((_resource, { pagination: { page } }) => Promise.resolve({ data: [ { id: page, title: `item ${page}`, }, ], total: 2, }) ), }); render( ); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ pages: expect.arrayContaining([ expect.objectContaining({ data: [{ id: 1, title: 'item 1' }], }), ]), }), }) ); }); expect( queryClient.getQueryData(['posts', 'getOne', { id: '1' }]) ).toEqual({ id: 1, title: 'item 1' }); expect( queryClient.getQueryData(['posts', 'getOne', { id: '2' }]) ).toBeUndefined(); // Manually change query data for item 1 queryClient.setQueryData(['posts', 'getOne', { id: '1' }], { id: 1, title: 'changed!', }); // Fetch next page hookValue.fetchNextPage(); await waitFor(() => { expect(callback).toHaveBeenCalledWith( expect.objectContaining({ data: expect.objectContaining({ pages: expect.arrayContaining([ expect.objectContaining({ data: [{ id: 2, title: 'item 2' }], }), ]), }), }) ); }); expect( queryClient.getQueryData(['posts', 'getOne', { id: '2' }]) ).toEqual({ id: 2, title: 'item 2' }); // Check that the getOne Query Cache for item 1 has not been overriden expect( queryClient.getQueryData(['posts', 'getOne', { id: '1' }]) ).toEqual({ id: 1, title: 'changed!' }); }); 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', 'getInfiniteList'], }); await waitFor(() => { expect(abort).toHaveBeenCalled(); }); }); describe('fetchNextPage', () => { it('should fetch the next page when the dataProvider uses total', async () => { render(); const button = await screen.findByLabelText('refetch-button'); fireEvent.click(button); await screen.findByText('Belgium -- BE'); }); it('should fetch the next page when the dataProvider uses pageInfo', async () => { render(); const button = await screen.findByLabelText('refetch-button'); fireEvent.click(button); await screen.findByText('Belgium -- BE'); }); }); });