import * as React from 'react'; import expect from 'expect'; import { render, fireEvent, waitFor, screen, act, } from '@testing-library/react'; import lolex from 'lolex'; // TODO: we shouldn't import mui components in ra-core import { TextField } from '@mui/material'; import { createMemoryHistory } from 'history'; import { testDataProvider } from '../../dataProvider'; import { memoryStore } from '../../store'; import { ListController } from './ListController'; import { getListControllerProps, sanitizeListRestProps, } from './useListController'; import { CoreAdminContext } from '../../core'; describe('useListController', () => { const defaultProps = { children: jest.fn(), resource: 'posts', debounce: 200, }; describe('queryOptions', () => { it('should accept custom client query options', async () => { const mock = jest .spyOn(console, 'error') .mockImplementation(() => {}); const getList = jest .fn() .mockImplementationOnce(() => Promise.reject(new Error())); const onError = jest.fn(); const dataProvider = testDataProvider({ getList }); render( {() =>
} ); await waitFor(() => { expect(getList).toHaveBeenCalled(); expect(onError).toHaveBeenCalled(); }); mock.mockRestore(); }); it('should accept meta in queryOptions', async () => { const getList = jest .fn() .mockImplementationOnce(() => Promise.resolve({ data: [], total: 25 }) ); const dataProvider = testDataProvider({ getList }); render( {() =>
} ); await waitFor(() => { expect(getList).toHaveBeenCalledWith('posts', { filter: {}, pagination: { page: 1, perPage: 10 }, sort: { field: 'id', order: 'ASC' }, meta: { foo: 'bar' }, }); }); }); it('should reset page when enabled is set to false', async () => { const children = jest.fn().mockReturnValue(children); const dataProvider = testDataProvider(); const props = { ...defaultProps, children }; render( ); act(() => { // @ts-ignore children.mock.calls.at(-1)[0].setPage(3); }); await waitFor(() => { expect(children).toHaveBeenCalledWith( expect.objectContaining({ page: 1, }) ); }); }); }); describe('setFilters', () => { let clock; let childFunction = ({ setFilters, filterValues }) => ( // TODO: we shouldn't import mui components in ra-core { setFilters({ q: event.target.value }); }} /> ); beforeEach(() => { clock = lolex.install(); }); it('should take only last change in case of a burst of changes (case of inputs being currently edited)', () => { const props = { ...defaultProps, children: childFunction, }; const store = memoryStore(); const storeSpy = jest.spyOn(store, 'setItem'); render( ); const searchInput = screen.getByLabelText('search'); fireEvent.change(searchInput, { target: { value: 'hel' } }); fireEvent.change(searchInput, { target: { value: 'hell' } }); fireEvent.change(searchInput, { target: { value: 'hello' } }); clock.tick(210); expect(storeSpy).toHaveBeenCalledTimes(1); expect(storeSpy).toHaveBeenCalledWith('posts.listParams', { filter: { q: 'hello' }, order: 'ASC', page: 1, perPage: 10, sort: 'id', }); }); it('should remove empty filters', () => { const props = { ...defaultProps, children: childFunction, }; const history = createMemoryHistory({ initialEntries: [ `/posts?filter=${JSON.stringify({ q: 'hello', })}&displayedFilters=${JSON.stringify({ q: true })}`, ], }); const store = memoryStore(); const storeSpy = jest.spyOn(store, 'setItem'); render( ); expect(storeSpy).toHaveBeenCalledTimes(1); const searchInput = screen.getByLabelText('search'); // FIXME: For some reason, triggering the change event with an empty string // does not call the event handler on childFunction fireEvent.change(searchInput, { target: { value: '' } }); clock.tick(210); expect(storeSpy).toHaveBeenCalledTimes(2); expect(storeSpy).toHaveBeenCalledWith('posts.listParams', { filter: {}, displayedFilters: { q: true }, order: 'ASC', page: 1, perPage: 10, sort: 'id', }); }); it('should update data if permanent filters change', () => { const children = jest.fn().mockReturnValue(children); const props = { ...defaultProps, debounce: 200, children, }; const getList = jest .fn() .mockImplementation(() => Promise.resolve({ data: [], total: 0 }) ); const dataProvider = testDataProvider({ getList }); const history = createMemoryHistory({ initialEntries: [`/posts`], }); const { rerender } = render( ); // Check that the permanent filter was used in the query expect(getList).toHaveBeenCalledTimes(1); expect(getList).toHaveBeenCalledWith( 'posts', expect.objectContaining({ filter: { foo: 1 } }) ); // Check that the permanent filter is not included in the displayedFilters and filterValues (passed to Filter form and button) expect(children).toHaveBeenCalledTimes(1); expect(children).toHaveBeenCalledWith( expect.objectContaining({ displayedFilters: {}, filterValues: {}, }) ); rerender( ); // Check that the permanent filter was used in the query expect(getList).toHaveBeenCalledTimes(2); expect(getList).toHaveBeenCalledWith( 'posts', expect.objectContaining({ filter: { foo: 2 } }) ); expect(children).toHaveBeenCalledTimes(2); }); afterEach(() => { clock.uninstall(); }); }); describe('showFilter', () => { it('Does not remove previously shown filter when adding a new one', async () => { let currentDisplayedFilters; let childFunction = ({ showFilter, displayedFilters, filterValues, }) => { currentDisplayedFilters = displayedFilters; return ( <>