import * as React from 'react';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import expect from 'expect';
import { testDataProvider } from '../../dataProvider/testDataProvider';
import { CoreAdminContext } from '../../core';
import { useReferenceManyFieldController } from './useReferenceManyFieldController';
import { memoryStore } from '../../store';
import {
Basic,
defaultDataProvider,
} from './useReferenceManyFieldController.stories';
const ReferenceManyFieldController = props => {
const { children, page = 1, perPage = 25, ...rest } = props;
const controllerProps = useReferenceManyFieldController({
page,
perPage,
...rest,
});
return children(controllerProps);
};
describe('useReferenceManyFieldController', () => {
it('should set isLoading to true when related records are not yet fetched', async () => {
const ComponentToTest = ({ isLoading }: { isLoading?: boolean }) => {
return
isLoading: {isLoading?.toString()}
;
};
const dataProvider = testDataProvider({
getManyReference: () => Promise.resolve({ data: [], total: 0 }),
});
render(
{props => }
);
expect(screen.queryByText('isLoading: true')).not.toBeNull();
await waitFor(() => {
expect(screen.queryByText('isLoading: false')).not.toBeNull();
});
});
it('should set isLoading to false when related records have been fetched and there are results', async () => {
const ComponentToTest = ({ isLoading }: { isLoading?: boolean }) => {
return isLoading: {isLoading?.toString()}
;
};
const dataProvider = testDataProvider({
getManyReference: () =>
Promise.resolve({
data: [
{ id: 1, title: 'hello' },
{ id: 2, title: 'world' },
],
total: 2,
}) as any,
});
render(
{props => }
);
expect(screen.queryByText('isLoading: true')).not.toBeNull();
await waitFor(() => {
expect(screen.queryByText('isLoading: false')).not.toBeNull();
});
});
it('should call dataProvider.getManyReferences on mount', async () => {
const dataProvider = testDataProvider({
getManyReference: jest
.fn()
.mockResolvedValue({ data: [], total: 0 }),
});
render(
{() => 'null'}
);
await waitFor(() => {
expect(dataProvider.getManyReference).toHaveBeenCalledWith(
'books',
{
id: 123,
target: 'author_id',
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
filter: {},
signal: undefined,
}
);
});
});
it('should pass data to children function', async () => {
const children = jest.fn().mockReturnValue('children');
const dataProvider = testDataProvider({
getManyReference: () =>
Promise.resolve({
data: [
{ id: 1, title: 'hello' },
{ id: 2, title: 'world' },
],
total: 2,
}) as any,
});
render(
{children}
);
await waitFor(() => {
expect(children).toHaveBeenCalledWith(
expect.objectContaining({
data: [
{ id: 1, title: 'hello' },
{ id: 2, title: 'world' },
],
total: 2,
hasPreviousPage: false,
hasNextPage: false,
})
);
});
});
it('should handle partial pagination', async () => {
const children = jest.fn().mockReturnValue('children');
const dataProvider = testDataProvider({
getManyReference: () =>
Promise.resolve({
data: [
{ id: 1, title: 'hello' },
{ id: 2, title: 'world' },
],
pageInfo: {
hasPreviousPage: false,
hasNextPage: false,
},
}) as any,
});
render(
{children}
);
await waitFor(() => {
expect(children).toHaveBeenCalledWith(
expect.objectContaining({
total: undefined,
hasPreviousPage: false,
hasNextPage: false,
})
);
});
});
it('should support custom source', async () => {
const dataProvider = testDataProvider({
getManyReference: jest
.fn()
.mockResolvedValue({ data: [], total: 0 }),
});
render(
{() => 'null'}
);
await waitFor(() => {
expect(dataProvider.getManyReference).toHaveBeenCalledWith(
'books',
{
id: 'jamesjoyce256',
target: 'author_id',
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
filter: {},
signal: undefined,
}
);
});
});
it('should call crudGetManyReference when its props changes', async () => {
const dataProvider = testDataProvider({
getManyReference: jest
.fn()
.mockResolvedValue({ data: [], total: 0 }),
});
const ControllerWrapper = props => (
{() => 'null'}
);
const { rerender } = render();
expect(dataProvider.getManyReference).toBeCalledTimes(1);
rerender();
await waitFor(() => {
expect(dataProvider.getManyReference).toBeCalledTimes(2);
expect(dataProvider.getManyReference).toHaveBeenCalledWith(
'books',
{
id: 123,
target: 'author_id',
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'ASC' },
filter: {},
signal: undefined,
}
);
});
});
it('should take only last change in case of a burst of setFilters calls (case of inputs being currently edited)', async () => {
const childFunction = ({ setFilters, filterValues }) => (
{
setFilters({ q: event.target.value }, undefined, true);
}}
/>
);
const dataProvider = testDataProvider({
getManyReference: jest
.fn()
.mockResolvedValue({ data: [], total: 0 }),
});
render(
{childFunction}
);
const searchInput = screen.getByLabelText('search');
fireEvent.change(searchInput, { target: { value: 'hel' } });
fireEvent.change(searchInput, { target: { value: 'hell' } });
fireEvent.change(searchInput, { target: { value: 'hello' } });
await waitFor(() => new Promise(resolve => setTimeout(resolve, 600)));
// Called twice: on load and on filter changes
expect(dataProvider.getManyReference).toHaveBeenCalledTimes(2);
expect(dataProvider.getManyReference).toHaveBeenCalledWith('books', {
target: 'author_id',
id: 123,
filter: { q: 'hello' },
pagination: { page: 1, perPage: 25 },
sort: { field: 'id', order: 'DESC' },
meta: undefined,
signal: undefined,
});
});
it('should store selection state linked to referencing record', async () => {
const store = memoryStore();
const setStore = jest.spyOn(store, 'setItem');
render(
{({ onToggleItem }) => {
return (
);
}}
);
fireEvent.click(await screen.findByText('Toggle'));
await waitFor(() => {
expect(setStore).toHaveBeenCalledWith(
'authors.123.books.selectedIds',
[456]
);
});
});
it('should support custom storeKey', async () => {
const store = memoryStore();
const setStore = jest.spyOn(store, 'setItem');
render(
{({ onToggleItem }) => {
return (
);
}}
);
fireEvent.click(await screen.findByText('Toggle'));
await waitFor(() => {
expect(setStore).toHaveBeenCalledWith('customKey.selectedIds', [
456,
]);
});
});
describe('response metadata', () => {
it('should return response metadata as meta', async () => {
const dataProvider = testDataProvider({
getManyReference: () =>
Promise.resolve({
data: [
{ id: 1, title: 'hello' },
{ id: 2, title: 'world' },
],
total: 2,
meta: {
facets: [
{ foo: 'bar', count: 1 },
{ foo: 'baz', count: 2 },
],
},
}) as any,
});
const ListMetadataInspector = () => {
const listContext = useReferenceManyFieldController({
resource: 'authors',
source: 'id',
record: { id: 123, name: 'James Joyce' },
reference: 'books',
target: 'author_id',
});
return (
<>
Response metadata:{' '}
{JSON.stringify(listContext.meta, null)}
>
);
};
render(
);
await screen.findByText(
'{"facets":[{"foo":"bar","count":1},{"foo":"baz","count":2}]}'
);
});
});
describe('onSelectAll', () => {
it('should select all records', async () => {
render();
await waitFor(() => {
expect(screen.getByTestId('selected_ids').textContent).toBe(
'Selected ids: []'
);
});
fireEvent.click(await screen.findByText('Select All'));
await waitFor(() => {
expect(screen.getByTestId('selected_ids').textContent).toBe(
'Selected ids: [0,1]'
);
});
});
it('should select all records even though some records are already selected', async () => {
render();
await waitFor(() => {
expect(screen.getByTestId('selected_ids').textContent).toBe(
'Selected ids: []'
);
});
fireEvent.click(await screen.findByTestId('checkbox-1'));
await waitFor(() => {
expect(screen.getByTestId('selected_ids').textContent).toBe(
'Selected ids: [1]'
);
});
fireEvent.click(await screen.findByText('Select All'));
await waitFor(() => {
expect(screen.getByTestId('selected_ids').textContent).toBe(
'Selected ids: [0,1]'
);
});
});
it('should not select more records than the provided limit', async () => {
const dataProvider = defaultDataProvider;
const getManyReference = jest.spyOn(
dataProvider,
'getManyReference'
);
render();
await waitFor(() => {
expect(screen.getByTestId('selected_ids').textContent).toBe(
'Selected ids: []'
);
});
fireEvent.click(await screen.findByText('Limited Select All'));
await waitFor(() => {
expect(screen.getByTestId('selected_ids').textContent).toBe(
'Selected ids: [0]'
);
});
await waitFor(() => {
expect(getManyReference).toHaveBeenCalledWith(
'books',
expect.objectContaining({
pagination: { page: 1, perPage: 1 },
})
);
});
});
});
});