import * as React from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import expect from 'expect';
import { Route } from 'react-router-dom';
import { CoreAdminContext } from './CoreAdminContext';
import { RouterNavigateFunction, TestMemoryRouter } from '../routing';
import { CoreAdminRoutes } from './CoreAdminRoutes';
import { Resource } from './Resource';
import { CustomRoutes } from './CustomRoutes';
import { CoreLayoutProps } from '../types';
import { testDataProvider } from '../dataProvider';
import { Basic } from './CoreAdminRoutes.stories';
const Layout = ({ children }: CoreLayoutProps) =>
Layout {children}
;
const CatchAll = () => ;
const Loading = () => <>Loading>;
describe('', () => {
const defaultProps = {
customRoutes: [],
};
describe('With resources as regular children', () => {
it('should render resources and custom routes with and without layout', async () => {
let navigate: RouterNavigateFunction | null = null;
render(
{
navigate = n;
}}
/>
);
await screen.findByText('Layout');
navigate!('/posts');
await screen.findByText('PostList');
navigate!('/comments');
await screen.findByText('CommentList');
navigate!('/foo');
await screen.findByText('Foo');
expect(screen.queryByText('Layout')).toBeNull();
navigate!('/bar');
await screen.findByText('Layout');
await screen.findByText('Bar');
});
});
describe('With children returned from a function as children', () => {
it('should render resources and custom routes with and without layout when there is no authProvider', async () => {
let navigate: RouterNavigateFunction | null = null;
render(
{
navigate = n;
}}
>
Foo} />
{() => (
<>
Bar}
/>
PostList}
/>
CommentList}
/>
>
)}
);
navigate!('/foo');
await screen.findByText('Foo');
expect(screen.queryByText('Layout')).toBeNull();
navigate!('/bar');
await screen.findByText('Bar');
await screen.findByText('Layout');
await screen.findByText('Bar');
navigate!('/posts');
await screen.findByText('PostList');
navigate!('/comments');
await screen.findByText('CommentList');
});
it('should render resources and custom routes with and without layout when there is an authProvider', async () => {
let navigate: RouterNavigateFunction | null = null;
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: jest.fn().mockResolvedValue(''),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
};
render(
{
navigate = n;
}}
/>
);
navigate!('/foo');
await screen.findByText('Foo');
expect(screen.queryByText('Layout')).toBeNull();
navigate!('/bar');
await screen.findByText('Bar');
expect(screen.queryByText('Layout')).not.toBeNull();
navigate!('/posts');
await screen.findByText('PostList');
navigate!('/comments');
await screen.findByText('CommentList');
});
it('should show the first resource by default when there is no authProvider', async () => {
render();
await screen.findByText('PostList');
});
it('should show the first resource by default when there is an authProvider', async () => {
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: jest.fn().mockResolvedValue(''),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
};
render();
await screen.findByText('PostList');
});
it('should show the first resource by default when there is an authProvider that supports canAccess', async () => {
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: jest.fn().mockResolvedValue(''),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
canAccess: jest.fn().mockResolvedValue(true),
};
render();
await screen.findByText('PostList');
});
it('should show the first allowed resource by default when there is an authProvider that supports canAccess', async () => {
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: jest.fn().mockResolvedValue(''),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
canAccess: jest.fn(({ resource }) =>
Promise.resolve(resource === 'comments')
),
};
render();
await screen.findByText('CommentList');
});
it('should return loading while the function child is not resolved', async () => {
jest.useFakeTimers();
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: jest.fn().mockResolvedValue(''),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
};
const Custom = () => <>Custom>;
let navigate: RouterNavigateFunction | null = null;
render(
{
navigate = n;
}}
>
} />
{() => new Promise(() => null)}
);
// Timeout needed because we wait for a second before displaying the loading screen
jest.advanceTimersByTime(1010);
navigate!('/posts');
await screen.findByText('Loading');
navigate!('/foo');
await screen.findByText('Custom');
expect(screen.queryByText('Loading')).toBeNull();
jest.useRealTimers();
});
});
describe('anonymous access', () => {
it('should not wait for the authProvider.checkAuth to return before rendering by default', () => {
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: (): Promise => new Promise(() => {}), // never resolves
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
};
render(
PostList}
/>
);
expect(screen.queryByText('PostList')).not.toBeNull();
expect(screen.queryByText('Loading')).toBeNull();
});
it('should render custom routes with no layout when the user is not authenticated ', async () => {
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: () => Promise.reject('Not authenticated'),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
};
let navigate;
render(
{
navigate = n;
}}
>
Custom} />
Login} />
PostList}
/>
);
expect(screen.queryByText('PostList')).not.toBeNull();
expect(screen.queryByText('Loading')).toBeNull();
navigate('/custom');
await new Promise(resolve => setTimeout(resolve, 1100));
await waitFor(() =>
expect(screen.queryByText('Custom')).not.toBeNull()
);
});
});
describe('requireAuth', () => {
it('should wait for the authProvider.checkAuth to return before rendering when requireAuth is true', async () => {
let resolve;
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: (): Promise =>
new Promise(res => (resolve = res)),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
};
render(
PostList}
/>
);
expect(screen.queryByText('PostList')).toBeNull();
expect(screen.queryByText('Loading')).not.toBeNull();
resolve();
await waitFor(() =>
expect(screen.queryByText('PostList')).not.toBeNull()
);
});
it('should redirect anonymous users to login when requireAuth is true and user accesses a resource page', async () => {
let reject;
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: (): Promise =>
new Promise((res, rej) => (reject = rej)),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
};
render(
Login} />
PostList}
/>
);
expect(screen.queryByText('PostList')).toBeNull();
expect(screen.queryByText('Loading')).not.toBeNull();
reject();
await waitFor(() =>
expect(screen.queryByText('Login')).not.toBeNull()
);
});
it('should redirect anonymous users to login when requireAuth is true and user accesses a custom route', async () => {
let reject;
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: (): Promise =>
new Promise((res, rej) => (reject = rej)),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
};
render(
Custom} />
Login} />
);
expect(screen.queryByText('Custom')).toBeNull();
expect(screen.queryByText('Loading')).not.toBeNull();
reject();
await waitFor(() =>
expect(screen.queryByText('Login')).not.toBeNull()
);
});
it('should render custom routes with no layout even for anonymous users', async () => {
let reject;
const authProvider = {
login: jest.fn().mockResolvedValue(''),
logout: jest.fn().mockResolvedValue(''),
checkAuth: (): Promise =>
new Promise((res, rej) => (reject = rej)),
checkError: jest.fn().mockResolvedValue(''),
getPermissions: jest.fn().mockResolvedValue(''),
};
render(
Custom} />
Login} />
);
// the custom page should show during loading and after the checkAuth promise is rejected
expect(screen.queryByText('Custom')).not.toBeNull();
expect(screen.queryByText('Loading')).toBeNull();
reject();
expect(screen.queryByText('Custom')).not.toBeNull();
});
});
});