chore: remove history & clean up some U&P tests (#16992)

This commit is contained in:
Josh 2023-06-19 10:22:38 +01:00 committed by GitHub
parent 86eaf9f728
commit bc6034cf84
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 671 additions and 3570 deletions

View File

@ -1,6 +1,14 @@
import React, { useCallback, useMemo } from 'react';
import { Box, Checkbox, Flex, Grid, GridItem, Typography } from '@strapi/design-system';
import {
Box,
Checkbox,
Flex,
Typography,
Grid,
GridItem,
VisuallyHidden,
} from '@strapi/design-system';
import { Cog as CogIcon } from '@strapi/icons';
import get from 'lodash/get';
import PropTypes from 'prop-types';
@ -90,10 +98,20 @@ const SubCategory = ({ subCategory }) => {
</Checkbox>
<button
type="button"
data-testid="action-cog"
onClick={() => onSelectedAction(action.name)}
style={{ display: 'inline-flex', alignItems: 'center' }}
>
<VisuallyHidden as="span">
{formatMessage(
{
id: 'app.utils.show-bound-route',
defaultMessage: 'Show bound route for {route}',
},
{
route: action.name,
}
)}
</VisuallyHidden>
<CogIcon />
</button>
</CheckboxWrapper>

View File

@ -1,5 +1,5 @@
// eslint-disable-next-line import/prefer-default-export
export { default as useFetchRole } from './useFetchRole';
export { default as useForm } from './useForm';
export { default as usePlugins } from './usePlugins';
export { default as useRolesList } from './useRolesList';
export * from './usePlugins';
export { default as useFetchRole } from './useFetchRole';

View File

@ -0,0 +1,71 @@
import { useEffect } from 'react';
import { useNotification, useFetchClient, useAPIErrorHandler } from '@strapi/helper-plugin';
import { useQueries } from 'react-query';
import pluginId from '../pluginId';
import { cleanPermissions, getTrad } from '../utils';
export const usePlugins = () => {
const toggleNotification = useNotification();
const { get } = useFetchClient();
const { formatAPIError } = useAPIErrorHandler(getTrad);
const [
{
data: permissions,
isLoading: isLoadingPermissions,
error: permissionsError,
refetch: refetchPermissions,
},
{ data: routes, isLoading: isLoadingRoutes, error: routesError, refetch: refetchRoutes },
] = useQueries([
{
queryKey: [pluginId, 'permissions'],
async queryFn() {
const res = await get(`/${pluginId}/permissions`);
return res.data.permissions;
},
},
{
queryKey: [pluginId, 'routes'],
async queryFn() {
const res = await get(`/${pluginId}/routes`);
return res.data.routes;
},
},
]);
const refetchQueries = async () => {
await Promise.all([refetchPermissions(), refetchRoutes()]);
};
useEffect(() => {
if (permissionsError) {
toggleNotification({
type: 'warning',
message: formatAPIError(permissionsError),
});
}
}, [toggleNotification, permissionsError, formatAPIError]);
useEffect(() => {
if (routesError) {
toggleNotification({
type: 'warning',
message: formatAPIError(routesError),
});
}
}, [toggleNotification, routesError, formatAPIError]);
const isLoading = isLoadingPermissions || isLoadingRoutes;
return {
permissions: permissions ? cleanPermissions(permissions) : {},
routes: routes ?? {},
getData: refetchQueries,
isLoading,
};
};

View File

@ -1,70 +0,0 @@
import { useCallback, useEffect, useReducer } from 'react';
import { useFetchClient, useNotification } from '@strapi/helper-plugin';
import get from 'lodash/get';
import pluginId from '../../pluginId';
import { cleanPermissions } from '../../utils';
import init from './init';
import reducer, { initialState } from './reducer';
const usePlugins = (shouldFetchData = true) => {
const toggleNotification = useNotification();
const [{ permissions, routes, isLoading }, dispatch] = useReducer(reducer, initialState, () =>
init(initialState, shouldFetchData)
);
const fetchClient = useFetchClient();
const fetchPlugins = useCallback(async () => {
try {
dispatch({
type: 'GET_DATA',
});
const [{ permissions }, { routes }] = await Promise.all(
[`/${pluginId}/permissions`, `/${pluginId}/routes`].map(async (endpoint) => {
const res = await fetchClient.get(endpoint);
return res.data;
})
);
dispatch({
type: 'GET_DATA_SUCCEEDED',
permissions: cleanPermissions(permissions),
routes,
});
} catch (err) {
const message = get(err, ['response', 'payload', 'message'], 'An error occured');
dispatch({
type: 'GET_DATA_ERROR',
});
if (message !== 'Forbidden') {
toggleNotification({
type: 'warning',
message,
});
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [toggleNotification]);
useEffect(() => {
if (shouldFetchData) {
fetchPlugins();
}
}, [fetchPlugins, shouldFetchData]);
return {
permissions,
routes,
getData: fetchPlugins,
isLoading,
};
};
export default usePlugins;

View File

@ -1,5 +0,0 @@
const init = (initialState, shouldFetchData) => {
return { ...initialState, isLoading: shouldFetchData };
};
export default init;

View File

@ -1,34 +0,0 @@
/* eslint-disable consistent-return */
import produce from 'immer';
export const initialState = {
permissions: {},
routes: {},
isLoading: true,
};
const reducer = (state, action) =>
produce(state, (draftState) => {
switch (action.type) {
case 'GET_DATA': {
draftState.isLoading = true;
draftState.permissions = {};
draftState.routes = {};
break;
}
case 'GET_DATA_SUCCEEDED': {
draftState.permissions = action.permissions;
draftState.routes = action.routes;
draftState.isLoading = false;
break;
}
case 'GET_DATA_ERROR': {
draftState.isLoading = false;
break;
}
default:
return draftState;
}
});
export default reducer;

View File

@ -1,32 +0,0 @@
import init from '../init';
describe('USERS PERMISSIONS | HOOKS | usePlugins | init', () => {
it('should return the initial state and set the isLoading key to true', () => {
const initialState = {
ok: true,
};
const expected = {
ok: true,
isLoading: true,
};
expect(init(initialState, true)).toEqual(expected);
});
it('should return the initial state and set the isLoading key to false', () => {
const initialState = {
permissions: {},
routes: {},
isLoading: null,
};
const expected = {
permissions: {},
routes: {},
isLoading: false,
};
expect(init(initialState, false)).toEqual(expected);
});
});

View File

@ -1,96 +0,0 @@
import reducer from '../reducer';
describe('USERS PERMISSIONS | HOOKS | usePlugins | reducer', () => {
describe('DEFAULT_ACTION', () => {
it('should return the initialState', () => {
const state = {
test: true,
};
expect(reducer(state, {})).toEqual(state);
});
});
describe('GET_DATA', () => {
it('should set the isLoading key to true', () => {
const state = {
permissions: { ok: true },
routes: { ok: true },
isLoading: false,
};
const action = {
type: 'GET_DATA',
};
const expected = {
permissions: {},
routes: {},
isLoading: true,
};
expect(reducer(state, action)).toEqual(expected);
});
});
describe('GET_DATA_ERROR', () => {
it('should set isLoading to false is an error occured', () => {
const action = {
type: 'GET_DATA_ERROR',
};
const initialState = {
permissions: {},
routes: {},
isLoading: true,
};
const expected = {
permissions: {},
routes: {},
isLoading: false,
};
expect(reducer(initialState, action)).toEqual(expected);
});
});
describe('GET_DATA_SUCCEEDED', () => {
it('should return the state with the permissions list', () => {
const action = {
type: 'GET_DATA_SUCCEEDED',
permissions: {
application: {
controllers: {
address: {
count: { enabled: false },
},
},
},
},
routes: {
application: [{ method: 'GET', path: '/addresses' }],
},
};
const initialState = {
permissions: {},
isLoading: true,
};
const expected = {
permissions: {
application: {
controllers: {
address: {
count: { enabled: false },
},
},
},
},
routes: {
application: [{ method: 'GET', path: '/addresses' }],
},
isLoading: false,
};
expect(reducer(initialState, action)).toEqual(expected);
});
});
});

View File

@ -8,8 +8,6 @@ import { QueryClient, QueryClientProvider } from 'react-query';
import ProtectedAdvancedSettingsPage from '../index';
import server from './utils/server';
jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useNotification: jest.fn(),
@ -38,19 +36,12 @@ const App = (
);
describe('ADMIN | Pages | Settings | Advanced Settings', () => {
beforeAll(() => server.listen());
beforeEach(() => {
jest.clearAllMocks();
});
afterEach(() => {
server.resetHandlers();
});
afterAll(() => {
jest.resetAllMocks();
server.close();
});
it('renders and matches the snapshot', async () => {

View File

@ -1,26 +0,0 @@
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const handlers = [
rest.get('*/advanced', (req, res, ctx) => {
return res(
ctx.delay(100),
ctx.status(200),
ctx.json({
roles: [{ name: 'Authenticated', type: 'authenticated' }],
settings: {
allow_register: false,
default_role: 'authenticated',
email_confirmation: false,
email_confirmation_redirection: '',
email_reset_password: 'https://cat-bounce.com/',
unique_email: false,
},
})
);
}),
];
const server = setupServer(...handlers);
export default server;

View File

@ -8,8 +8,6 @@ import { QueryClient, QueryClientProvider } from 'react-query';
import ProtectedEmailTemplatesPage from '../index';
import server from './utils/server';
jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useNotification: jest.fn(),
@ -38,19 +36,12 @@ const App = (
);
describe('ADMIN | Pages | Settings | Email Templates', () => {
beforeAll(() => server.listen());
beforeEach(() => {
jest.clearAllMocks();
});
afterEach(() => {
server.resetHandlers();
});
afterAll(() => {
jest.resetAllMocks();
server.close();
});
it('renders and matches the snapshot', async () => {

View File

@ -1,41 +0,0 @@
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const handlers = [
rest.get('*/email-templates', (req, res, ctx) => {
return res(
ctx.delay(100),
ctx.status(200),
ctx.json({
email_confirmation: {
display: 'Email.template.email_confirmation',
options: {
from: {
email: 'mochoko@strapi.io',
name: 'Administration Panel',
},
message: 'Thank you for registering. Please click on the link below.',
object: 'Account confirmation',
response_email: '',
},
},
reset_password: {
display: 'Email.template.reset_password',
options: {
from: {
email: 'mochoko@strapi.io',
name: 'Administration Panel',
},
message: 'We heard that you lost your password. Sorry about that!',
object: 'Reset password',
response_email: '',
},
},
})
);
}),
];
const server = setupServer(...handlers);
export default server;

View File

@ -8,8 +8,6 @@ import { QueryClient, QueryClientProvider } from 'react-query';
import { ProvidersPage } from '../index';
import server from './server';
jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useNotification: jest.fn(),
@ -36,16 +34,10 @@ const App = (
);
describe('Admin | containers | ProvidersPage', () => {
beforeAll(() => server.listen());
beforeEach(() => {
jest.clearAllMocks();
});
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
it('renders and matches the snapshot', () => {
useRBAC.mockImplementation(() => ({
isLoading: true,

View File

@ -1,26 +0,0 @@
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const handlers = [
rest.get('*/providers', (req, res, ctx) => {
return res(
ctx.delay(1000),
ctx.status(200),
ctx.json({
email: { enabled: true, icon: 'envelope' },
discord: {
callback: '/auth/discord/callback',
enabled: false,
icon: 'discord',
key: '',
scope: ['identify', 'email'],
secret: '',
},
})
);
}),
];
const server = setupServer(...handlers);
export default server;

View File

@ -26,14 +26,14 @@ import { Formik } from 'formik';
import { useIntl } from 'react-intl';
import { useHistory } from 'react-router-dom';
import UsersPermissions from '../../../components/UsersPermissions';
import { usePlugins } from '../../../hooks';
import pluginId from '../../../pluginId';
import getTrad from '../../../utils/getTrad';
import UsersPermissions from '../../components/UsersPermissions';
import { usePlugins } from '../../hooks';
import pluginId from '../../pluginId';
import getTrad from '../../utils/getTrad';
import schema from './utils/schema';
import { createRoleSchema } from './constants';
const EditPage = () => {
const CreatePage = () => {
const { formatMessage } = useIntl();
const [isSubmitting, setIsSubmitting] = useState(false);
const toggleNotification = useNotification();
@ -85,7 +85,7 @@ const EditPage = () => {
enableReinitialize
initialValues={{ name: '', description: '' }}
onSubmit={handleCreateRoleSubmit}
validationSchema={schema}
validationSchema={createRoleSchema}
>
{({ handleSubmit, values, handleChange, errors }) => (
<Form noValidate onSubmit={handleSubmit}>
@ -182,4 +182,4 @@ const EditPage = () => {
);
};
export default EditPage;
export default CreatePage;

View File

@ -1,65 +0,0 @@
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import pluginId from '../../../../pluginId';
const handlers = [
// Mock create role route
rest.post(`*/${pluginId}/roles`, (req, res, ctx) => {
return res(ctx.delay(100), ctx.status(200), ctx.json({ ok: true }));
}),
// Mock get permissions
rest.get(`*/${pluginId}/permissions`, (req, res, ctx) => {
return res(
ctx.delay(100),
ctx.status(200),
ctx.json({
permissions: {
'api::address': {
controllers: {
address: {
create: {
enabled: false,
policy: '',
},
},
},
},
},
})
);
}),
// Mock get all routes route
rest.get(`*/${pluginId}/routes`, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
routes: {
'api::address': [
{
method: 'POST',
path: '/addresses',
handler: 'address.create',
config: {
policies: [],
auth: {
scope: 'api::address.address.create',
},
},
info: {
apiName: 'address',
type: 'content-api',
},
},
],
},
})
);
}),
];
const server = setupServer(...handlers);
export default server;

View File

@ -1,38 +1,38 @@
import React, { useRef, useState } from 'react';
import {
Box,
Button,
ContentLayout,
Flex,
Grid,
GridItem,
HeaderLayout,
Main,
Textarea,
Button,
Flex,
Box,
TextInput,
Textarea,
Typography,
GridItem,
Grid,
} from '@strapi/design-system';
import {
Form,
Link,
LoadingIndicatorPage,
SettingsPageTitle,
useFetchClient,
useNotification,
useOverlayBlocker,
SettingsPageTitle,
LoadingIndicatorPage,
Form,
useNotification,
Link,
} from '@strapi/helper-plugin';
import { ArrowLeft, Check } from '@strapi/icons';
import { Formik } from 'formik';
import { useIntl } from 'react-intl';
import { useRouteMatch } from 'react-router-dom';
import UsersPermissions from '../../../components/UsersPermissions';
import { useFetchRole, usePlugins } from '../../../hooks';
import pluginId from '../../../pluginId';
import getTrad from '../../../utils/getTrad';
import UsersPermissions from '../../components/UsersPermissions';
import { usePlugins, useFetchRole } from '../../hooks';
import pluginId from '../../pluginId';
import getTrad from '../../utils/getTrad';
import schema from './utils/schema';
import { createRoleSchema } from './constants';
const EditPage = () => {
const { formatMessage } = useIntl();
@ -90,7 +90,7 @@ const EditPage = () => {
enableReinitialize
initialValues={{ name: role.name, description: role.description }}
onSubmit={handleEditRoleSubmit}
validationSchema={schema}
validationSchema={createRoleSchema}
>
{({ handleSubmit, values, handleChange, errors }) => (
<Form noValidate onSubmit={handleSubmit}>

View File

@ -1,95 +0,0 @@
import { rest } from 'msw';
import { setupServer } from 'msw/node';
import pluginId from '../../../../pluginId';
const handlers = [
// Mock get role route
rest.get(`*/${pluginId}/roles/:roleId`, (req, res, ctx) => {
return res(
ctx.delay(100),
ctx.status(200),
ctx.json({
role: {
id: req.params.roleId,
name: 'Authenticated',
description: 'Default role given to authenticated user.',
type: 'authenticated',
createdAt: '2021-09-08T16:26:18.061Z',
updatedAt: '2021-09-08T16:26:18.061Z',
permissions: {
'api::address': {
controllers: {
address: {
create: {
enabled: false,
policy: '',
},
},
},
},
},
},
})
);
}),
// Mock edit role route
rest.put(`*/${pluginId}/roles/:roleId`, (req, res, ctx) => {
return res(ctx.delay(500), ctx.status(200), ctx.json({ ok: true }));
}),
// Mock get all routes route
rest.get(`*/${pluginId}/routes`, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
routes: {
'api::address': [
{
method: 'POST',
path: '/addresses',
handler: 'address.create',
config: {
policies: [],
auth: {
scope: 'api::address.address.create',
},
},
info: {
apiName: 'address',
type: 'content-api',
},
},
],
},
})
);
}),
// Mock permissions route
rest.get(`*/${pluginId}/permissions`, (req, res, ctx) => {
return res(
ctx.delay(100),
ctx.status(200),
ctx.json({
permissions: {
'api::address': {
controllers: {
address: {
create: {
enabled: false,
policy: '',
},
},
},
},
},
})
);
}),
];
const server = setupServer(...handlers);
export default server;

View File

@ -1,9 +0,0 @@
import { translatedErrors } from '@strapi/helper-plugin';
import * as yup from 'yup';
const schema = yup.object().shape({
name: yup.string().required(translatedErrors.required),
description: yup.string().required(translatedErrors.required),
});
export default schema;

File diff suppressed because one or more lines are too long

View File

@ -1,33 +0,0 @@
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const handlers = [
rest.get('*/roles', (req, res, ctx) => {
return res(
ctx.delay(1000),
ctx.status(200),
ctx.json({
roles: [
{
id: 1,
name: 'Authenticated',
description: 'Default role given to authenticated user.',
type: 'authenticated',
nb_users: 0,
},
{
id: 2,
name: 'Public',
description: 'Default role given to unauthenticated user.',
type: 'public',
nb_users: 0,
},
],
})
);
}),
];
const server = setupServer(...handlers);
export default server;

View File

@ -15,7 +15,7 @@ export const fetchData = async (toggleNotification, notifyStatus) => {
message: { id: 'notification.error' },
});
throw new Error('error');
throw new Error(err);
}
};

View File

@ -2,8 +2,9 @@ import React from 'react';
import { CheckPagePermissions } from '@strapi/helper-plugin';
import pluginPermissions from '../../../permissions';
import RolesCreatePage from '../CreatePage';
import pluginPermissions from '../../permissions';
import RolesCreatePage from './CreatePage';
const ProtectedRolesCreatePage = () => (
<CheckPagePermissions permissions={pluginPermissions.createRole}>

View File

@ -2,8 +2,9 @@ import React from 'react';
import { CheckPagePermissions } from '@strapi/helper-plugin';
import pluginPermissions from '../../../permissions';
import RolesEditPage from '../EditPage';
import pluginPermissions from '../../permissions';
import RolesEditPage from './EditPage';
const ProtectedRolesEditPage = () => (
<CheckPagePermissions permissions={pluginPermissions.updateRole}>

View File

@ -2,8 +2,9 @@ import React from 'react';
import { CheckPagePermissions } from '@strapi/helper-plugin';
import pluginPermissions from '../../../permissions';
import RolesListPage from '../ListPage';
import pluginPermissions from '../../permissions';
import RolesListPage from './ListPage';
const ProtectedRolesListPage = () => {
return (

View File

@ -1,9 +1,7 @@
import { translatedErrors } from '@strapi/helper-plugin';
import * as yup from 'yup';
const schema = yup.object().shape({
export const createRoleSchema = yup.object().shape({
name: yup.string().required(translatedErrors.required),
description: yup.string().required(translatedErrors.required),
});
export default schema;

View File

@ -0,0 +1,108 @@
import React from 'react';
import { ThemeProvider, lightTheme } from '@strapi/design-system';
import { NotificationsProvider } from '@strapi/helper-plugin';
import { fireEvent, render as renderRTL, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from 'react-intl';
import { QueryClient, QueryClientProvider } from 'react-query';
import { MemoryRouter, Switch, Route } from 'react-router-dom';
import pluginId from '../../../pluginId';
import RolesCreatePage from '../CreatePage';
jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useOverlayBlocker: jest.fn(() => ({ lockApp: jest.fn(), unlockApp: jest.fn() })),
}));
const render = () => ({
...renderRTL(<Route path={`/settings/${pluginId}/roles/new`} component={RolesCreatePage} />, {
wrapper({ children }) {
const client = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
return (
<IntlProvider locale="en" messages={{}} textComponent="span">
<ThemeProvider theme={lightTheme}>
<QueryClientProvider client={client}>
<NotificationsProvider>
<MemoryRouter initialEntries={[`/settings/${pluginId}/roles/new`]}>
<Switch>{children}</Switch>
</MemoryRouter>
</NotificationsProvider>
</QueryClientProvider>
</ThemeProvider>
</IntlProvider>
);
},
}),
user: userEvent.setup(),
});
describe('Roles CreatePage', () => {
beforeEach(() => jest.clearAllMocks());
it('renders correctly', async () => {
const { getByRole, user } = render();
expect(getByRole('heading', { name: 'Create a role' })).toBeInTheDocument();
expect(getByRole('heading', { name: 'Role details' })).toBeInTheDocument();
/**
* This means the `usePlugins` hook has finished fetching
*/
await waitFor(() => expect(getByRole('heading', { name: 'Permissions' })).toBeInTheDocument());
expect(getByRole('heading', { name: 'Advanced settings' })).toBeInTheDocument();
expect(getByRole('button', { name: 'Save' })).toBeInTheDocument();
expect(getByRole('textbox', { name: 'Name' })).toBeInTheDocument();
expect(getByRole('textbox', { name: 'Description' })).toBeInTheDocument();
await user.click(getByRole('button', { name: 'Address' }));
expect(getByRole('region', { name: 'Address' })).toBeInTheDocument();
expect(getByRole('checkbox', { name: 'Select all' })).toBeInTheDocument();
expect(getByRole('checkbox', { name: 'create' })).toBeInTheDocument();
});
it('will show an error if the user does not fill the name or description field', async () => {
const { getByRole, getAllByText } = render();
await waitFor(() => expect(getByRole('heading', { name: 'Permissions' })).toBeInTheDocument());
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() =>
expect(getByRole('textbox', { name: 'Name' })).toHaveAttribute('aria-invalid', 'true')
);
expect(getByRole('textbox', { name: 'Description' })).toHaveAttribute('aria-invalid', 'true');
expect(getAllByText('Invalid value')).toHaveLength(2);
});
it('can create a new role and show a notification', async () => {
const { getByRole, getByText, user } = render();
await waitFor(() => expect(getByRole('heading', { name: 'Permissions' })).toBeInTheDocument());
await user.type(getByRole('textbox', { name: 'Name' }), 'Test role');
await user.type(getByRole('textbox', { name: 'Description' }), 'This is a test role');
await user.click(getByRole('button', { name: 'Address' }));
await user.click(getByRole('checkbox', { name: 'create' }));
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Role created')).toBeInTheDocument());
});
});

View File

@ -0,0 +1,155 @@
import React from 'react';
import { ThemeProvider, lightTheme } from '@strapi/design-system';
import { NotificationsProvider } from '@strapi/helper-plugin';
import {
fireEvent,
render as renderRTL,
waitFor,
waitForElementToBeRemoved,
} from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { IntlProvider } from 'react-intl';
import { QueryClient, QueryClientProvider } from 'react-query';
import { MemoryRouter, Switch, Route } from 'react-router-dom';
import pluginId from '../../../pluginId';
import RolesEditPage from '../EditPage';
jest.mock('@strapi/helper-plugin', () => ({
...jest.requireActual('@strapi/helper-plugin'),
useOverlayBlocker: jest.fn(() => ({ lockApp: jest.fn(), unlockApp: jest.fn() })),
}));
const render = () => ({
...renderRTL(<Route path={`/settings/${pluginId}/roles/:id`} component={RolesEditPage} />, {
wrapper({ children }) {
const client = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
return (
<IntlProvider locale="en" messages={{}} textComponent="span">
<ThemeProvider theme={lightTheme}>
<QueryClientProvider client={client}>
<NotificationsProvider>
<MemoryRouter initialEntries={[`/settings/${pluginId}/roles/1`]}>
<Switch>{children}</Switch>
</MemoryRouter>
</NotificationsProvider>
</QueryClientProvider>
</ThemeProvider>
</IntlProvider>
);
},
}),
user: userEvent.setup(),
});
describe('Roles EditPage', () => {
beforeEach(() => jest.clearAllMocks());
it('renders correctly', async () => {
const { getByTestId, getByRole, user } = render();
await waitForElementToBeRemoved(() => getByTestId('loader'));
expect(getByRole('link', { name: 'Back' })).toBeInTheDocument();
expect(getByRole('heading', { name: 'Authenticated' })).toBeInTheDocument();
expect(getByRole('heading', { name: 'Role details' })).toBeInTheDocument();
expect(getByRole('heading', { name: 'Permissions' })).toBeInTheDocument();
expect(getByRole('heading', { name: 'Advanced settings' })).toBeInTheDocument();
expect(getByRole('button', { name: 'Save' })).toBeInTheDocument();
expect(getByRole('textbox', { name: 'Name' })).toBeInTheDocument();
expect(getByRole('textbox', { name: 'Description' })).toBeInTheDocument();
await user.click(getByRole('button', { name: 'Address' }));
expect(getByRole('region', { name: 'Address' })).toBeInTheDocument();
expect(getByRole('checkbox', { name: 'Select all' })).toBeInTheDocument();
expect(getByRole('checkbox', { name: 'create' })).toBeInTheDocument();
});
it('will show an error if the user does not fill the name field', async () => {
const { getByRole, user, getByTestId, getByText } = render();
await waitForElementToBeRemoved(() => getByTestId('loader'));
await user.clear(getByRole('textbox', { name: 'Name' }));
await user.click(getByRole('button', { name: 'Save' }));
expect(getByRole('textbox', { name: 'Name' })).toHaveAttribute('aria-invalid', 'true');
expect(getByText('Invalid value')).toBeInTheDocument();
});
it('will show an error if the user does not fill out the description field', async () => {
const { getByRole, user, getByTestId, getByText } = render();
await waitForElementToBeRemoved(() => getByTestId('loader'));
await user.clear(getByRole('textbox', { name: 'Description' }));
await user.click(getByRole('button', { name: 'Save' }));
expect(getByRole('textbox', { name: 'Description' })).toHaveAttribute('aria-invalid', 'true');
expect(getByText('Invalid value')).toBeInTheDocument();
});
it("can update a role's name and description", async () => {
const { getByRole, user, getByTestId, getByText } = render();
await waitForElementToBeRemoved(() => getByTestId('loader'));
await user.type(getByRole('textbox', { name: 'Name' }), 'test');
await user.type(getByRole('textbox', { name: 'Description' }), 'testing');
/**
* @note user.click will not trigger the form.
*/
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Role edited')).toBeInTheDocument());
});
it("can update a role's permissions", async () => {
const { getByRole, user, getByText, getByTestId } = render();
await waitForElementToBeRemoved(() => getByTestId('loader'));
await user.click(getByRole('button', { name: 'Address' }));
await user.click(getByRole('checkbox', { name: 'create' }));
/**
* @note user.click will not trigger the form.
*/
fireEvent.click(getByRole('button', { name: 'Save' }));
await waitFor(() => expect(getByText('Role edited')).toBeInTheDocument());
});
it('will update the Advanced Settings panel when you click on the cog icon of a specific permission', async () => {
const { getByRole, user, getByText, getByTestId } = render();
await waitForElementToBeRemoved(() => getByTestId('loader'));
await user.click(getByRole('button', { name: 'Address' }));
await user.hover(getByRole('checkbox', { name: 'create' }));
await user.click(getByRole('button', { name: /Show bound route/i }));
expect(getByRole('heading', { name: 'Bound route to address .create' })).toBeInTheDocument();
expect(getByText('POST')).toBeInTheDocument();
expect(getByText('/addresses')).toBeInTheDocument();
});
});

View File

@ -3,4 +3,5 @@
module.exports = {
preset: '../../../jest-preset.front.js',
displayName: 'Users & Permissions plugin',
setupFilesAfterEnv: ['./tests/setup.js'],
};

View File

@ -54,7 +54,6 @@
"@testing-library/dom": "9.2.0",
"@testing-library/react": "14.0.0",
"@testing-library/user-event": "14.4.3",
"history": "^4.9.0",
"msw": "1.2.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@ -0,0 +1,191 @@
'use strict';
const { setupServer } = require('msw/node');
const { rest } = require('msw');
const pluginId = require('../admin/src/pluginId').default;
const handlers = [
// Mock get role route
rest.get(`*/${pluginId}/roles/:roleId`, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
role: {
id: req.params.roleId,
name: 'Authenticated',
description: 'Default role given to authenticated user.',
type: 'authenticated',
createdAt: '2021-09-08T16:26:18.061Z',
updatedAt: '2021-09-08T16:26:18.061Z',
permissions: {
'api::address': {
controllers: {
address: {
create: {
enabled: false,
policy: '',
},
},
},
},
},
},
})
);
}),
// Mock edit role route
rest.put(`*/${pluginId}/roles/:roleId`, (req, res, ctx) => {
return res(ctx.status(200), ctx.json({ ok: true }));
}),
// Mock create role route
rest.post(`*/${pluginId}/roles`, (req, res, ctx) => {
return res(ctx.status(200), ctx.json({ ok: true }));
}),
// Mock get all routes route
rest.get(`*/${pluginId}/routes`, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
routes: {
'api::address': [
{
method: 'POST',
path: '/addresses',
handler: 'address.create',
config: {
policies: [],
auth: {
scope: 'api::address.address.create',
},
},
info: {
apiName: 'address',
type: 'content-api',
},
},
],
},
})
);
}),
// Mock permissions route
rest.get(`*/${pluginId}/permissions`, (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
permissions: {
'api::address': {
controllers: {
address: {
create: {
enabled: false,
policy: '',
},
},
},
},
},
})
);
}),
rest.get('*/roles', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
roles: [
{
id: 1,
name: 'Authenticated',
description: 'Default role given to authenticated user.',
type: 'authenticated',
nb_users: 0,
},
{
id: 2,
name: 'Public',
description: 'Default role given to unauthenticated user.',
type: 'public',
nb_users: 0,
},
],
})
);
}),
rest.get('*/providers', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
email: { enabled: true, icon: 'envelope' },
discord: {
callback: '/auth/discord/callback',
enabled: false,
icon: 'discord',
key: '',
scope: ['identify', 'email'],
secret: '',
},
})
);
}),
rest.get('*/email-templates', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
email_confirmation: {
display: 'Email.template.email_confirmation',
options: {
from: {
email: 'mochoko@strapi.io',
name: 'Administration Panel',
},
message: 'Thank you for registering. Please click on the link below.',
object: 'Account confirmation',
response_email: '',
},
},
reset_password: {
display: 'Email.template.reset_password',
options: {
from: {
email: 'mochoko@strapi.io',
name: 'Administration Panel',
},
message: 'We heard that you lost your password. Sorry about that!',
object: 'Reset password',
response_email: '',
},
},
})
);
}),
rest.get('*/advanced', (req, res, ctx) => {
return res(
ctx.status(200),
ctx.json({
roles: [{ name: 'Authenticated', type: 'authenticated' }],
settings: {
allow_register: false,
default_role: 'authenticated',
email_confirmation: false,
email_confirmation_redirection: '',
email_reset_password: 'https://cat-bounce.com/',
unique_email: false,
},
})
);
}),
];
const server = setupServer(...handlers);
module.exports = {
server,
};

View File

@ -0,0 +1,15 @@
'use strict';
const { server } = require('./server');
beforeAll(() => {
server.listen();
});
afterEach(() => {
server.resetHandlers();
});
afterAll(() => {
server.close();
});

View File

@ -8514,7 +8514,6 @@ __metadata:
bcryptjs: 2.4.3
formik: 2.4.0
grant-koa: 5.4.8
history: ^4.9.0
immer: 9.0.19
jsonwebtoken: 9.0.0
jwk-to-pem: 2.0.5