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 (
<>