🥅(frontend) improve error catching in forms
- rename CreateMailboxForm into ModalCreateMailbox, and useCreateMailDomain into useAddMailDomain - use useAPIError hook in ModalCreateMailbox.tsx and ModalAddMailDomain - update translations and tests (include removal of e2e test able to be asserted by component tests)
This commit is contained in:
committed by
Sebastien Nobour
parent
25898bbb64
commit
e4aed82ff2
@@ -11,6 +11,7 @@ and this project adheres to
|
||||
### Added
|
||||
|
||||
- 📈(monitoring) configure sentry monitoring #378
|
||||
- 🥅(frontend) improve api error handling #355
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -18,6 +19,7 @@ and this project adheres to
|
||||
- 💬(frontend) fix group member removal text #382
|
||||
- 💬(frontend) fix add mail domain text #382
|
||||
- 🐛(frontend) fix keyboard navigation #379
|
||||
- 🐛(frontend) fix add mail domain form submission #355
|
||||
|
||||
## [1.0.2] - 2024-08-30
|
||||
|
||||
@@ -30,10 +32,6 @@ and this project adheres to
|
||||
|
||||
- 👽️(mailboxes) fix mailbox creation after dimail api improvement (#360)
|
||||
|
||||
### Fixed
|
||||
|
||||
- 🐛(frontend) user can submit form to add mail domain by pressing "Enter" key
|
||||
|
||||
## [1.0.1] - 2024-08-19
|
||||
|
||||
### Fixed
|
||||
|
||||
@@ -48,7 +48,9 @@ export const parseAPIErrorCause = ({
|
||||
}): string[] =>
|
||||
causes.reduce((arrayCauses, cause) => {
|
||||
const foundErrorParams = Object.values(errorParams).find((params) =>
|
||||
params.causes.find((knownCause) => knownCause.match(cause)),
|
||||
params.causes.find((knownCause) =>
|
||||
new RegExp(knownCause, 'i').test(cause),
|
||||
),
|
||||
);
|
||||
|
||||
if (!foundErrorParams) {
|
||||
|
||||
@@ -5,7 +5,13 @@ import { MailDomain } from '@/features/mail-domains';
|
||||
|
||||
import { KEY_LIST_MAIL_DOMAIN } from './useMailDomains';
|
||||
|
||||
export const createMailDomain = async (name: string): Promise<MailDomain> => {
|
||||
export interface AddMailDomainParams {
|
||||
name: string;
|
||||
}
|
||||
|
||||
export const addMailDomain = async (
|
||||
name: AddMailDomainParams['name'],
|
||||
): Promise<MailDomain> => {
|
||||
const response = await fetchAPI(`mail-domains/`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
@@ -23,19 +29,24 @@ export const createMailDomain = async (name: string): Promise<MailDomain> => {
|
||||
return response.json() as Promise<MailDomain>;
|
||||
};
|
||||
|
||||
export function useCreateMailDomain({
|
||||
export const useAddMailDomain = ({
|
||||
onSuccess,
|
||||
onError,
|
||||
}: {
|
||||
onSuccess: (data: MailDomain) => void;
|
||||
}) {
|
||||
onError: (error: APIError) => void;
|
||||
}) => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<MailDomain, APIError, string>({
|
||||
mutationFn: createMailDomain,
|
||||
mutationFn: addMailDomain,
|
||||
onSuccess: (data) => {
|
||||
void queryClient.invalidateQueries({
|
||||
queryKey: [KEY_LIST_MAIL_DOMAIN],
|
||||
});
|
||||
onSuccess(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
onError(error);
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -26,7 +26,6 @@ export const createMailbox = async ({
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
// TODO: extend errorCauses to return the name of the invalid field names to highlight in the form?
|
||||
throw new APIError(
|
||||
'Failed to create the mailbox',
|
||||
await errorCauses(response),
|
||||
@@ -40,7 +39,7 @@ type UseCreateMailboxParams = { mailDomainSlug: string } & UseMutationOptions<
|
||||
CreateMailboxParams
|
||||
>;
|
||||
|
||||
export function useCreateMailbox(options: UseCreateMailboxParams) {
|
||||
export const useCreateMailbox = (options: UseCreateMailboxParams) => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation<void, APIError, CreateMailboxParams>({
|
||||
mutationFn: createMailbox,
|
||||
@@ -61,4 +60,4 @@ export function useCreateMailbox(options: UseCreateMailboxParams) {
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ import { default as MailDomainsLogo } from '../assets/mail-domains-logo.svg';
|
||||
import { PAGE_SIZE } from '../conf';
|
||||
import { MailDomain, MailDomainMailbox } from '../types';
|
||||
|
||||
import { CreateMailboxForm } from './forms/CreateMailboxForm';
|
||||
import { ModalCreateMailbox } from './ModalCreateMailbox';
|
||||
|
||||
export type ViewMailbox = {
|
||||
name: string;
|
||||
@@ -87,7 +87,7 @@ export function MailDomainsContent({ mailDomain }: { mailDomain: MailDomain }) {
|
||||
) : (
|
||||
<>
|
||||
{isCreateMailboxFormVisible && mailDomain ? (
|
||||
<CreateMailboxForm
|
||||
<ModalCreateMailbox
|
||||
mailDomain={mailDomain}
|
||||
closeModal={() => setIsCreateMailboxFormVisible(false)}
|
||||
/>
|
||||
|
||||
@@ -1,91 +1,27 @@
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { Button, Input, Loader, ModalSize } from '@openfun/cunningham-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import React from 'react';
|
||||
import {
|
||||
Controller,
|
||||
FormProvider,
|
||||
UseFormReturn,
|
||||
useForm,
|
||||
} from 'react-hook-form';
|
||||
import React, { useState } from 'react';
|
||||
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { APIError } from '@/api';
|
||||
import { parseAPIError } from '@/api/parseAPIError';
|
||||
import { Box, Text, TextErrors } from '@/components';
|
||||
import { Modal } from '@/components/Modal';
|
||||
import { useCreateMailDomain } from '@/features/mail-domains';
|
||||
import { useAddMailDomain } from '@/features/mail-domains';
|
||||
|
||||
import { default as MailDomainsLogo } from '../assets/mail-domains-logo.svg';
|
||||
|
||||
const FORM_ID = 'form-add-mail-domain';
|
||||
|
||||
const useAddMailDomainApiError = ({
|
||||
error,
|
||||
methods,
|
||||
}: {
|
||||
error: APIError | null;
|
||||
methods: UseFormReturn<{ name: string }> | null;
|
||||
}): string[] | undefined => {
|
||||
const [errorCauses, setErrorCauses] = React.useState<undefined | string[]>(
|
||||
undefined,
|
||||
);
|
||||
const { t } = useTranslation();
|
||||
|
||||
React.useEffect(() => {
|
||||
if (methods && t && error) {
|
||||
let causes = undefined;
|
||||
|
||||
if (error.cause?.length) {
|
||||
const parseCauses = (causes: string[]) =>
|
||||
causes.reduce((arrayCauses, cause) => {
|
||||
switch (cause) {
|
||||
case 'Mail domain with this name already exists.':
|
||||
case 'Mail domain with this Slug already exists.':
|
||||
methods.setError('name', {
|
||||
type: 'manual',
|
||||
message: t(
|
||||
'This mail domain is already used. Please, choose another one.',
|
||||
),
|
||||
});
|
||||
break;
|
||||
default:
|
||||
arrayCauses.push(cause);
|
||||
}
|
||||
|
||||
return arrayCauses;
|
||||
}, [] as string[]);
|
||||
|
||||
causes = parseCauses(error.cause);
|
||||
}
|
||||
|
||||
if (error.status === 500 || !error.cause) {
|
||||
causes = [
|
||||
t(
|
||||
'Your request cannot be processed because the server is experiencing an error. If the problem ' +
|
||||
'persists, please contact our support to resolve the issue: suiteterritoriale@anct.gouv.fr.',
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
setErrorCauses(causes);
|
||||
}
|
||||
}, [methods, t, error]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (errorCauses && methods) {
|
||||
methods.setFocus('name');
|
||||
}
|
||||
}, [methods, errorCauses]);
|
||||
|
||||
return errorCauses;
|
||||
};
|
||||
|
||||
export const ModalAddMailDomain = () => {
|
||||
const { t } = useTranslation();
|
||||
const router = useRouter();
|
||||
|
||||
const createMailDomainValidationSchema = z.object({
|
||||
const [errorCauses, setErrorCauses] = useState<string[]>([]);
|
||||
|
||||
const addMailDomainValidationSchema = z.object({
|
||||
name: z.string().min(1, t('Example: saint-laurent.fr')),
|
||||
});
|
||||
|
||||
@@ -96,26 +32,62 @@ export const ModalAddMailDomain = () => {
|
||||
},
|
||||
mode: 'onChange',
|
||||
reValidateMode: 'onChange',
|
||||
resolver: zodResolver(createMailDomainValidationSchema),
|
||||
resolver: zodResolver(addMailDomainValidationSchema),
|
||||
});
|
||||
|
||||
const {
|
||||
mutate: createMailDomain,
|
||||
isPending,
|
||||
error,
|
||||
} = useCreateMailDomain({
|
||||
const { mutate: addMailDomain, isPending } = useAddMailDomain({
|
||||
onSuccess: (mailDomain) => {
|
||||
router.push(`/mail-domains/${mailDomain.slug}`);
|
||||
},
|
||||
});
|
||||
onError: (error) => {
|
||||
const unhandledCauses = parseAPIError({
|
||||
error,
|
||||
errorParams: {
|
||||
name: {
|
||||
causes: [
|
||||
'Mail domain with this name already exists.',
|
||||
'Mail domain with this Slug already exists.',
|
||||
],
|
||||
handleError: () => {
|
||||
if (methods.formState.errors.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const errorCauses = useAddMailDomainApiError({ error, methods });
|
||||
methods.setError('name', {
|
||||
type: 'manual',
|
||||
message: t(
|
||||
'This mail domain is already used. Please, choose another one.',
|
||||
),
|
||||
});
|
||||
methods.setFocus('name');
|
||||
},
|
||||
},
|
||||
},
|
||||
serverErrorParams: {
|
||||
handleError: () => {
|
||||
methods.setFocus('name');
|
||||
},
|
||||
defaultMessage: t(
|
||||
'Your request cannot be processed because the server is experiencing an error. If the problem ' +
|
||||
'persists, please contact our support to resolve the issue: suiteterritoriale@anct.gouv.fr',
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
setErrorCauses((prevState) =>
|
||||
unhandledCauses &&
|
||||
JSON.stringify(unhandledCauses) !== JSON.stringify(prevState)
|
||||
? unhandledCauses
|
||||
: prevState,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmitCallback = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
|
||||
void methods.handleSubmit(({ name }) => {
|
||||
void createMailDomain(name);
|
||||
void addMailDomain(name);
|
||||
})();
|
||||
};
|
||||
|
||||
@@ -139,7 +111,11 @@ export const ModalAddMailDomain = () => {
|
||||
<Button
|
||||
type="submit"
|
||||
form={FORM_ID}
|
||||
disabled={!methods.watch('name') || isPending}
|
||||
disabled={
|
||||
methods.formState.isSubmitting ||
|
||||
!methods.formState.isValid ||
|
||||
isPending
|
||||
}
|
||||
>
|
||||
{t('Add the domain')}
|
||||
</Button>
|
||||
@@ -163,7 +139,11 @@ export const ModalAddMailDomain = () => {
|
||||
) : null}
|
||||
|
||||
<FormProvider {...methods}>
|
||||
<form id={FORM_ID} onSubmit={onSubmitCallback}>
|
||||
<form
|
||||
id={FORM_ID}
|
||||
onSubmit={onSubmitCallback}
|
||||
title={t('Mail domain addition form')}
|
||||
>
|
||||
<Controller
|
||||
control={methods.control}
|
||||
name="name"
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
VariantType,
|
||||
useToastProvider,
|
||||
} from '@openfun/cunningham-react';
|
||||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Controller,
|
||||
FormProvider,
|
||||
@@ -17,11 +17,12 @@ import { useTranslation } from 'react-i18next';
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { parseAPIError } from '@/api/parseAPIError';
|
||||
import { Box, Text, TextErrors } from '@/components';
|
||||
import { Modal } from '@/components/Modal';
|
||||
|
||||
import { CreateMailboxParams, useCreateMailbox } from '../../api';
|
||||
import { MailDomain } from '../../types';
|
||||
import { CreateMailboxParams, useCreateMailbox } from '../api';
|
||||
import { MailDomain } from '../types';
|
||||
|
||||
const FORM_ID: string = 'form-create-mailbox';
|
||||
|
||||
@@ -32,7 +33,7 @@ const GlobalStyle = createGlobalStyle`
|
||||
}
|
||||
`;
|
||||
|
||||
export const CreateMailboxForm = ({
|
||||
export const ModalCreateMailbox = ({
|
||||
mailDomain,
|
||||
closeModal,
|
||||
}: {
|
||||
@@ -42,6 +43,8 @@ export const CreateMailboxForm = ({
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToastProvider();
|
||||
|
||||
const [errorCauses, setErrorCauses] = useState<string[]>([]);
|
||||
|
||||
const messageInvalidMinChar = t('You must have minimum 1 character');
|
||||
|
||||
const createMailboxValidationSchema = z.object({
|
||||
@@ -77,7 +80,7 @@ export const CreateMailboxForm = ({
|
||||
resolver: zodResolver(createMailboxValidationSchema),
|
||||
});
|
||||
|
||||
const { mutate: createMailbox, error } = useCreateMailbox({
|
||||
const { mutate: createMailbox, isPending } = useCreateMailbox({
|
||||
mailDomainSlug: mailDomain.slug,
|
||||
onSuccess: () => {
|
||||
toast(t('Mailbox created!'), VariantType.SUCCESS, {
|
||||
@@ -86,6 +89,52 @@ export const CreateMailboxForm = ({
|
||||
|
||||
closeModal();
|
||||
},
|
||||
onError: (error) => {
|
||||
const unhandledCauses = parseAPIError({
|
||||
error,
|
||||
errorParams: {
|
||||
local_part: {
|
||||
causes: ['Mailbox with this Local_part and Domain already exists.'],
|
||||
handleError: () => {
|
||||
methods.setError('local_part', {
|
||||
type: 'manual',
|
||||
message: t('This email prefix is already used.'),
|
||||
});
|
||||
methods.setFocus('local_part');
|
||||
},
|
||||
},
|
||||
secret: {
|
||||
causes: [
|
||||
"Please configure your domain's secret before creating any mailbox.",
|
||||
`Secret not valid for this domain`,
|
||||
],
|
||||
causeShown: t(
|
||||
'The mail domain secret is misconfigured. Please, contact ' +
|
||||
'our support team to solve the issue: suiteterritoriale@anct.gouv.fr',
|
||||
),
|
||||
handleError: () => {
|
||||
methods.setFocus('first_name');
|
||||
},
|
||||
},
|
||||
},
|
||||
serverErrorParams: {
|
||||
handleError: () => {
|
||||
methods.setFocus('first_name');
|
||||
},
|
||||
defaultMessage: t(
|
||||
'Your request cannot be processed because the server is experiencing an error. If the problem ' +
|
||||
'persists, please contact our support to resolve the issue: suiteterritoriale@anct.gouv.fr',
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
setErrorCauses((prevState) =>
|
||||
unhandledCauses &&
|
||||
JSON.stringify(unhandledCauses) !== JSON.stringify(prevState)
|
||||
? unhandledCauses
|
||||
: prevState,
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const onSubmitCallback = (event: React.FormEvent) => {
|
||||
@@ -95,20 +144,6 @@ export const CreateMailboxForm = ({
|
||||
)();
|
||||
};
|
||||
|
||||
const causes = error?.cause?.filter((cause) => {
|
||||
const isFound =
|
||||
cause === 'Mailbox with this Local_part and Domain already exists.';
|
||||
|
||||
if (isFound) {
|
||||
methods.setError('local_part', {
|
||||
type: 'manual',
|
||||
message: t('This email prefix is already used.'),
|
||||
});
|
||||
}
|
||||
|
||||
return !isFound;
|
||||
});
|
||||
|
||||
return (
|
||||
<FormProvider {...methods}>
|
||||
<Modal
|
||||
@@ -132,7 +167,11 @@ export const CreateMailboxForm = ({
|
||||
fullWidth
|
||||
type="submit"
|
||||
form={FORM_ID}
|
||||
disabled={methods.formState.isSubmitting}
|
||||
disabled={
|
||||
methods.formState.isSubmitting ||
|
||||
!methods.formState.isValid ||
|
||||
isPending
|
||||
}
|
||||
>
|
||||
{t('Create the mailbox')}
|
||||
</Button>
|
||||
@@ -152,8 +191,12 @@ export const CreateMailboxForm = ({
|
||||
>
|
||||
<GlobalStyle />
|
||||
<Box $width="100%" $margin={{ top: 'none', bottom: 'xl' }}>
|
||||
{!!causes?.length && (
|
||||
<TextErrors $margin={{ bottom: 'small' }} causes={causes} />
|
||||
{!!errorCauses?.length && (
|
||||
<TextErrors
|
||||
$margin={{ bottom: 'small' }}
|
||||
causes={errorCauses}
|
||||
$textAlign="left"
|
||||
/>
|
||||
)}
|
||||
<Text
|
||||
$margin={{ horizontal: 'none', vertical: 'big' }}
|
||||
@@ -188,7 +231,11 @@ const Form = ({
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<form onSubmit={onSubmitCallback} id={FORM_ID}>
|
||||
<form
|
||||
onSubmit={onSubmitCallback}
|
||||
id={FORM_ID}
|
||||
title={t('Mailbox creation form')}
|
||||
>
|
||||
<Box $direction="column" $width="100%" $gap="2rem" $margin="auto">
|
||||
<Box $margin={{ horizontal: 'none' }}>
|
||||
<FieldMailBox
|
||||
@@ -0,0 +1,241 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import React from 'react';
|
||||
|
||||
import { AppWrapper } from '@/tests/utils';
|
||||
|
||||
import { ModalAddMailDomain } from '../ModalAddMailDomain';
|
||||
|
||||
const mockPush = jest.fn();
|
||||
jest.mock('next/navigation', () => ({
|
||||
useRouter: jest.fn().mockImplementation(() => ({
|
||||
push: mockPush,
|
||||
})),
|
||||
}));
|
||||
|
||||
describe('ModalAddMailDomain', () => {
|
||||
const getElements = () => ({
|
||||
modalElement: screen.getByText('Add a mail domain'),
|
||||
formTag: screen.getByTitle('Mail domain addition form'),
|
||||
inputName: screen.getByLabelText(/Domain name/i),
|
||||
buttonCancel: screen.getByRole('button', { name: /Cancel/i, hidden: true }),
|
||||
buttonSubmit: screen.getByRole('button', {
|
||||
name: /Add the domain/i,
|
||||
hidden: true,
|
||||
}),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fetchMock.restore();
|
||||
});
|
||||
|
||||
it('renders all the elements', () => {
|
||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||
|
||||
const { modalElement, formTag, inputName, buttonCancel, buttonSubmit } =
|
||||
getElements();
|
||||
|
||||
expect(modalElement).toBeVisible();
|
||||
expect(formTag).toBeVisible();
|
||||
expect(inputName).toBeVisible();
|
||||
expect(screen.getByText('Example: saint-laurent.fr')).toBeVisible();
|
||||
expect(buttonCancel).toBeVisible();
|
||||
expect(buttonSubmit).toBeVisible();
|
||||
});
|
||||
|
||||
it('should disable submit button when no field is filled', () => {
|
||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||
|
||||
const { buttonSubmit } = getElements();
|
||||
|
||||
expect(buttonSubmit).toBeDisabled();
|
||||
});
|
||||
|
||||
it('displays validation error on empty submit', async () => {
|
||||
fetchMock.mock(`end:mail-domains/`, 201);
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||
|
||||
const { inputName, buttonSubmit } = getElements();
|
||||
|
||||
await user.type(inputName, 'domain.fr');
|
||||
await user.clear(inputName);
|
||||
|
||||
await user.click(buttonSubmit);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(/Example: saint-laurent.fr/i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(fetchMock.lastUrl()).toBeFalsy();
|
||||
});
|
||||
|
||||
it('submits the form when validation passes', async () => {
|
||||
fetchMock.mock(`end:mail-domains/`, {
|
||||
status: 201,
|
||||
body: {
|
||||
name: 'domain.fr',
|
||||
id: '456ac6ca-0402-4615-8005-69bc1efde43f',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
slug: 'domainfr',
|
||||
status: 'enabled',
|
||||
abilities: {
|
||||
get: true,
|
||||
patch: true,
|
||||
put: true,
|
||||
post: true,
|
||||
delete: true,
|
||||
manage_accesses: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||
|
||||
const { inputName, buttonSubmit } = getElements();
|
||||
|
||||
await user.type(inputName, 'domain.fr');
|
||||
|
||||
await user.click(buttonSubmit);
|
||||
|
||||
expect(fetchMock.lastUrl()).toContain('/mail-domains/');
|
||||
expect(fetchMock.lastOptions()).toEqual({
|
||||
body: JSON.stringify({
|
||||
name: 'domain.fr',
|
||||
}),
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
expect(mockPush).toHaveBeenCalledWith(`/mail-domains/domainfr`);
|
||||
});
|
||||
|
||||
it('submits the form on key enter press', async () => {
|
||||
fetchMock.mock(`end:mail-domains/`, 201);
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||
|
||||
const { inputName } = getElements();
|
||||
|
||||
await user.type(inputName, 'domain.fr');
|
||||
await user.type(inputName, '{enter}');
|
||||
|
||||
expect(fetchMock.lastUrl()).toContain('/mail-domains/');
|
||||
expect(fetchMock.lastOptions()).toEqual({
|
||||
body: JSON.stringify({
|
||||
name: 'domain.fr',
|
||||
}),
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
method: 'POST',
|
||||
});
|
||||
});
|
||||
|
||||
it('displays right error message error when maildomain name is already used', async () => {
|
||||
fetchMock.mock(`end:mail-domains/`, {
|
||||
status: 400,
|
||||
body: {
|
||||
name: 'Mail domain with this name already exists.',
|
||||
},
|
||||
});
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||
|
||||
const { inputName, buttonSubmit } = getElements();
|
||||
|
||||
await user.type(inputName, 'domain.fr');
|
||||
|
||||
await user.click(buttonSubmit);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(
|
||||
/This mail domain is already used. Please, choose another one./i,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(inputName).toHaveFocus();
|
||||
|
||||
await user.type(inputName, 'domain2.fr');
|
||||
expect(buttonSubmit).toBeEnabled();
|
||||
});
|
||||
|
||||
it('displays right error message error when maildomain slug is already used', async () => {
|
||||
fetchMock.mock(`end:mail-domains/`, {
|
||||
status: 400,
|
||||
body: {
|
||||
name: 'Mail domain with this Slug already exists.',
|
||||
},
|
||||
});
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||
|
||||
const { inputName, buttonSubmit } = getElements();
|
||||
|
||||
await user.type(inputName, 'domainfr');
|
||||
|
||||
await user.click(buttonSubmit);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(
|
||||
/This mail domain is already used. Please, choose another one./i,
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(inputName).toHaveFocus();
|
||||
|
||||
await user.type(inputName, 'domain2fr');
|
||||
|
||||
expect(buttonSubmit).toBeEnabled();
|
||||
});
|
||||
|
||||
it('displays right error message error when error 500 is received', async () => {
|
||||
fetchMock.mock(`end:mail-domains/`, {
|
||||
status: 500,
|
||||
});
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
render(<ModalAddMailDomain />, { wrapper: AppWrapper });
|
||||
|
||||
const { inputName, buttonSubmit } = getElements();
|
||||
|
||||
await user.type(inputName, 'domain.fr');
|
||||
|
||||
await user.click(buttonSubmit);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Your request cannot be processed because the server is experiencing an error. If the problem ' +
|
||||
'persists, please contact our support to resolve the issue: suiteterritoriale@anct.gouv.fr',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(inputName).toHaveFocus();
|
||||
expect(buttonSubmit).toBeEnabled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,333 @@
|
||||
import { useMutation } from '@tanstack/react-query';
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import fetchMock from 'fetch-mock';
|
||||
import React from 'react';
|
||||
|
||||
import { APIError } from '@/api';
|
||||
import { AppWrapper } from '@/tests/utils';
|
||||
|
||||
import { CreateMailboxParams } from '../../api';
|
||||
import { MailDomain } from '../../types';
|
||||
import { ModalCreateMailbox } from '../ModalCreateMailbox';
|
||||
|
||||
const mockMailDomain: MailDomain = {
|
||||
name: 'domain.fr',
|
||||
id: '456ac6ca-0402-4615-8005-69bc1efde43f',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
slug: 'domainfr',
|
||||
status: 'enabled',
|
||||
abilities: {
|
||||
get: true,
|
||||
patch: true,
|
||||
put: true,
|
||||
post: true,
|
||||
delete: true,
|
||||
manage_accesses: true,
|
||||
},
|
||||
};
|
||||
|
||||
const mockOnSuccess = jest.fn();
|
||||
jest.mock('../../api/useCreateMailbox', () => {
|
||||
const { createMailbox } = jest.requireActual('../../api/useCreateMailbox');
|
||||
|
||||
return {
|
||||
useCreateMailbox: jest.fn().mockImplementation(({ onError }) =>
|
||||
useMutation<void, APIError, CreateMailboxParams>({
|
||||
mutationFn: createMailbox,
|
||||
onSuccess: mockOnSuccess,
|
||||
onError: (error) => onError(error),
|
||||
}),
|
||||
),
|
||||
};
|
||||
});
|
||||
|
||||
describe('ModalCreateMailbox', () => {
|
||||
const mockCloseModal = jest.fn();
|
||||
const renderModalCreateMailbox = () => {
|
||||
return render(
|
||||
<ModalCreateMailbox
|
||||
mailDomain={mockMailDomain}
|
||||
closeModal={mockCloseModal}
|
||||
/>,
|
||||
{ wrapper: AppWrapper },
|
||||
);
|
||||
};
|
||||
|
||||
const getFormElements = () => ({
|
||||
formTag: screen.getByTitle('Mailbox creation form'),
|
||||
inputFirstName: screen.getByLabelText(/First name/i),
|
||||
inputLastName: screen.getByLabelText(/Last name/i),
|
||||
inputLocalPart: screen.getByLabelText(/Email address prefix/i),
|
||||
inputEmailAddress: screen.getByLabelText(/Secondary email address/i),
|
||||
buttonCancel: screen.getByRole('button', { name: /Cancel/i, hidden: true }),
|
||||
buttonSubmit: screen.getByRole('button', {
|
||||
name: /Create the mailbox/i,
|
||||
hidden: true,
|
||||
}),
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fetchMock.restore();
|
||||
});
|
||||
|
||||
it('renders all the elements', () => {
|
||||
renderModalCreateMailbox();
|
||||
const {
|
||||
formTag,
|
||||
inputFirstName,
|
||||
inputLastName,
|
||||
inputLocalPart,
|
||||
inputEmailAddress,
|
||||
buttonCancel,
|
||||
buttonSubmit,
|
||||
} = getFormElements();
|
||||
|
||||
expect(formTag).toBeVisible();
|
||||
expect(inputFirstName).toBeVisible();
|
||||
expect(inputLastName).toBeVisible();
|
||||
expect(inputLocalPart).toBeVisible();
|
||||
expect(screen.getByText(`@${mockMailDomain.name}`)).toBeVisible();
|
||||
expect(inputEmailAddress).toBeVisible();
|
||||
expect(buttonCancel).toBeVisible();
|
||||
expect(buttonSubmit).toBeVisible();
|
||||
});
|
||||
|
||||
it('clicking on cancel button closes modal', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderModalCreateMailbox();
|
||||
|
||||
const { buttonCancel } = getFormElements();
|
||||
|
||||
expect(buttonCancel).toBeVisible();
|
||||
|
||||
await user.click(buttonCancel);
|
||||
|
||||
expect(mockCloseModal).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('displays validation errors on empty submit', async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderModalCreateMailbox();
|
||||
|
||||
const {
|
||||
inputFirstName,
|
||||
inputLastName,
|
||||
inputLocalPart,
|
||||
inputEmailAddress,
|
||||
buttonSubmit,
|
||||
} = getFormElements();
|
||||
|
||||
// To bypass html form validation we need to fill and clear the fields
|
||||
await user.type(inputFirstName, 'John');
|
||||
await user.type(inputLastName, 'Doe');
|
||||
await user.type(inputLocalPart, 'john.doe');
|
||||
await user.type(inputEmailAddress, 'john.doe@mail.com');
|
||||
|
||||
await user.clear(inputFirstName);
|
||||
await user.clear(inputLastName);
|
||||
await user.clear(inputLocalPart);
|
||||
await user.clear(inputEmailAddress);
|
||||
|
||||
await user.click(buttonSubmit);
|
||||
|
||||
expect(screen.getByText(`@${mockMailDomain.name}`)).toBeVisible();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(/Please enter your first name/i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(/Please enter your last name/i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(/You must have minimum 1 character/i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(fetchMock.lastUrl()).toBeFalsy();
|
||||
expect(buttonSubmit).toBeDisabled();
|
||||
});
|
||||
|
||||
it('submits the form when validation passes', async () => {
|
||||
fetchMock.mock(`end:mail-domains/${mockMailDomain.slug}/mailboxes/`, 201);
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderModalCreateMailbox();
|
||||
|
||||
const {
|
||||
inputFirstName,
|
||||
inputLastName,
|
||||
inputLocalPart,
|
||||
inputEmailAddress,
|
||||
buttonSubmit,
|
||||
} = getFormElements();
|
||||
|
||||
await user.type(inputFirstName, 'John');
|
||||
await user.type(inputLastName, 'Doe');
|
||||
await user.type(inputLocalPart, 'john.doe');
|
||||
await user.type(inputEmailAddress, 'john.doe@mail.com');
|
||||
|
||||
await user.click(buttonSubmit);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(/Please enter your first name/i),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(/Please enter your last name/i),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.queryByText(/You must have minimum 1 character/i),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(fetchMock.lastOptions()).toEqual({
|
||||
body: JSON.stringify({
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
local_part: 'john.doe',
|
||||
secondary_email: 'john.doe@mail.com',
|
||||
}),
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
expect(mockOnSuccess).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('submits the form on key enter press', async () => {
|
||||
fetchMock.mock(`end:mail-domains/${mockMailDomain.slug}/mailboxes/`, 201);
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderModalCreateMailbox();
|
||||
|
||||
const {
|
||||
inputFirstName,
|
||||
inputLastName,
|
||||
inputLocalPart,
|
||||
inputEmailAddress,
|
||||
buttonSubmit,
|
||||
} = getFormElements();
|
||||
|
||||
await user.type(inputFirstName, 'John');
|
||||
await user.type(inputLastName, 'Doe');
|
||||
await user.type(inputLocalPart, 'john.doe');
|
||||
|
||||
await user.type(inputEmailAddress, 'john.doe@mail.com');
|
||||
|
||||
await user.type(buttonSubmit, '{enter}');
|
||||
|
||||
expect(fetchMock.lastOptions()).toEqual({
|
||||
body: JSON.stringify({
|
||||
first_name: 'John',
|
||||
last_name: 'Doe',
|
||||
local_part: 'john.doe',
|
||||
secondary_email: 'john.doe@mail.com',
|
||||
}),
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
method: 'POST',
|
||||
});
|
||||
|
||||
expect(mockOnSuccess).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('displays right error message error when mailbox prefix is already used', async () => {
|
||||
// mockCreateMailbox.mockRejectedValueOnce(
|
||||
// new APIError('Failed to create the mailbox', {
|
||||
// status: 400,
|
||||
// cause: ['Mailbox with this Local_part and Domain already exists.'],
|
||||
// }),
|
||||
// );
|
||||
fetchMock.mock(`end:mail-domains/${mockMailDomain.slug}/mailboxes/`, {
|
||||
status: 400,
|
||||
body: {
|
||||
local_part: 'Mailbox with this Local_part and Domain already exists.',
|
||||
},
|
||||
});
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderModalCreateMailbox();
|
||||
|
||||
const {
|
||||
inputFirstName,
|
||||
inputLastName,
|
||||
inputLocalPart,
|
||||
inputEmailAddress,
|
||||
buttonSubmit,
|
||||
} = getFormElements();
|
||||
|
||||
await user.type(inputFirstName, 'John');
|
||||
await user.type(inputLastName, 'Doe');
|
||||
await user.type(inputLocalPart, 'john.doe');
|
||||
await user.type(inputEmailAddress, 'john.doe@mail.com');
|
||||
|
||||
await user.click(buttonSubmit);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(/This email prefix is already used./i),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(inputLocalPart).toHaveFocus();
|
||||
});
|
||||
|
||||
it('displays right error message error when error 500 is received', async () => {
|
||||
fetchMock.mock(`end:mail-domains/${mockMailDomain.slug}/mailboxes/`, {
|
||||
status: 500,
|
||||
});
|
||||
|
||||
const user = userEvent.setup();
|
||||
|
||||
renderModalCreateMailbox();
|
||||
|
||||
const {
|
||||
inputFirstName,
|
||||
inputLastName,
|
||||
inputLocalPart,
|
||||
inputEmailAddress,
|
||||
buttonSubmit,
|
||||
} = getFormElements();
|
||||
|
||||
await user.type(inputFirstName, 'John');
|
||||
await user.type(inputLastName, 'Doe');
|
||||
await user.type(inputLocalPart, 'john.doe');
|
||||
await user.type(inputEmailAddress, 'john.doe@mail.com');
|
||||
|
||||
await user.click(buttonSubmit);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Your request cannot be processed because the server is experiencing an error. If the problem ' +
|
||||
'persists, please contact our support to resolve the issue: suiteterritoriale@anct.gouv.fr',
|
||||
),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
expect(inputFirstName).toHaveFocus();
|
||||
expect(buttonSubmit).toBeEnabled();
|
||||
});
|
||||
});
|
||||
@@ -83,8 +83,10 @@
|
||||
"List members card": "Carte liste des membres",
|
||||
"Logout": "Se déconnecter",
|
||||
"Mail Domains": "Domaines de messagerie",
|
||||
"Mail domain addition form": "Formulaire d'ajout de domaine de messagerie",
|
||||
"Mail domains panel": "Panel des domaines de messagerie",
|
||||
"Mailbox created!": "Boîte mail créée !",
|
||||
"Mailbox creation form": "Formulaire de création de boite mail",
|
||||
"Mailboxes list": "Liste des boîtes mail",
|
||||
"Marianne Logo": "Logo Marianne",
|
||||
"Member": "Membre",
|
||||
@@ -134,6 +136,7 @@
|
||||
"Teams": "Équipes",
|
||||
"The National Agency for Territorial Cohesion undertakes to make its\n service accessible, in accordance with article 47 of law no. 2005-102\n of February 11, 2005.": "L'Agence Nationale de la Cohésion des Territoires s’engage à rendre son service accessible, conformément à l’article 47 de la loi n° 2005-102 du 11 février 2005.",
|
||||
"The domain name encounters an error. Please contact our support team to solve the problem:": "Le nom de domaine rencontre une erreur. Veuillez contacter notre support pour résoudre le problème :",
|
||||
"The mail domain secret is misconfigured. Please, contact our support team to solve the issue: suiteterritoriale@anct.gouv.fr": "Le secret du domaine de messagerie est mal configuré. Veuillez contacter notre support pour résoudre le problème : suiteterritoriale@anct.gouv.fr",
|
||||
"The member has been removed from the team": "Le membre a été supprimé de votre groupe",
|
||||
"The role has been updated": "Le rôle a bien été mis à jour",
|
||||
"The team has been removed.": "Le groupe a été supprimé.",
|
||||
@@ -164,6 +167,7 @@
|
||||
"You cannot update the role of other owner.": "Vous ne pouvez pas mettre à jour les rôles d'autre propriétaire.",
|
||||
"You must have minimum 1 character": "Vous devez entrer au moins 1 caractère",
|
||||
"Your domain name is being validated. You will not be able to create mailboxes until your domain name has been validated by our team.": "Votre nom de domaine est en cours de validation. Vous ne pourrez créer de boîtes mail que lorsque votre nom de domaine sera validé par notre équipe.",
|
||||
"Your request cannot be processed because the server is experiencing an error. If the problem persists, please contact our support to resolve the issue: suiteterritoriale@anct.gouv.fr": "Votre demande ne peut pas être traitée car le serveur rencontre une erreur. Si le problème persiste, veuillez contacter notre support pour résoudre le problème : suiteterritoriale@anct.gouv.fr",
|
||||
"[disabled]": "[désactivé]",
|
||||
"[enabled]": "[actif]",
|
||||
"[failed]": "[erroné]",
|
||||
|
||||
@@ -2,13 +2,13 @@ import React, { ReactElement } from 'react';
|
||||
|
||||
import { Box } from '@/components';
|
||||
import { MailDomainsLayout } from '@/features/mail-domains';
|
||||
import { ModalCreateMailDomain } from '@/features/mail-domains/components/ModalAddMailDomain';
|
||||
import { ModalAddMailDomain } from '@/features/mail-domains/components/ModalAddMailDomain';
|
||||
import { NextPageWithLayout } from '@/types/next';
|
||||
|
||||
const Page: NextPageWithLayout = () => {
|
||||
return (
|
||||
<Box $padding="large" $height="inherit">
|
||||
<ModalCreateMailDomain />
|
||||
<ModalAddMailDomain />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -314,157 +314,4 @@ test.describe('Mail domain create mailbox', () => {
|
||||
page.getByRole('button', { name: 'Create a mailbox' }),
|
||||
).not.toBeInViewport();
|
||||
});
|
||||
|
||||
test('checks client invalidation messages are displayed and no mailbox creation request is sent when fields are not properly filled', async ({
|
||||
page,
|
||||
}) => {
|
||||
let isCreateMailboxRequestSent = false;
|
||||
page.on(
|
||||
'request',
|
||||
(request) =>
|
||||
(isCreateMailboxRequestSent =
|
||||
request.url().includes('/mail-domains/domainfr/mailboxes/') &&
|
||||
request.method() === 'POST'),
|
||||
);
|
||||
|
||||
void interceptCommonApiRequests(page);
|
||||
|
||||
await navigateToMailboxCreationFormForMailDomainFr(page);
|
||||
|
||||
const inputFirstName = page.getByLabel('First name');
|
||||
const inputLastName = page.getByLabel('Last name');
|
||||
const inputLocalPart = page.getByLabel('Email address prefix');
|
||||
const inputSecondaryEmailAddress = page.getByLabel(
|
||||
'Secondary email address',
|
||||
);
|
||||
const textInvalidLocalPart = page.getByText(
|
||||
'It must not contain spaces, accents or special characters (except "." or "-"). E.g.: jean.dupont',
|
||||
);
|
||||
const textInvalidSecondaryEmailAddress = page.getByText(
|
||||
'Please enter a valid email address.\nE.g. : jean.dupont@mail.fr',
|
||||
);
|
||||
|
||||
await inputFirstName.fill(' ');
|
||||
await inputFirstName.clear();
|
||||
await expect(page.getByText('Please enter your first name')).toBeVisible();
|
||||
|
||||
await inputLastName.fill(' ');
|
||||
await inputLastName.clear();
|
||||
await expect(page.getByText('Please enter your last name')).toBeVisible();
|
||||
|
||||
await inputLocalPart.fill('wrong@');
|
||||
await expect(textInvalidLocalPart).toBeVisible();
|
||||
|
||||
await inputSecondaryEmailAddress.fill('uncomplete@mail');
|
||||
await expect(textInvalidSecondaryEmailAddress).toBeVisible();
|
||||
|
||||
await inputLocalPart.clear();
|
||||
await inputLocalPart.fill('wrong ');
|
||||
await expect(textInvalidLocalPart).toBeVisible();
|
||||
|
||||
await inputLocalPart.clear();
|
||||
await expect(
|
||||
page.getByText('You must have minimum 1 character'),
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByRole('button', { name: 'Create the mailbox' }).click();
|
||||
|
||||
expect(isCreateMailboxRequestSent).toBeFalsy();
|
||||
});
|
||||
|
||||
test('checks field invalidation messages are displayed when sending already existing local_part data in mail domain to api', async ({
|
||||
page,
|
||||
}) => {
|
||||
const interceptRequests = (page: Page) => {
|
||||
void interceptCommonApiRequests(page);
|
||||
|
||||
void page.route(
|
||||
'**/api/v1.0/mail-domains/domainfr/mailboxes/',
|
||||
(route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
void route.fulfill({
|
||||
status: 400,
|
||||
json: {
|
||||
local_part: [
|
||||
'Mailbox with this Local_part and Domain already exists.',
|
||||
],
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
{ times: 1 },
|
||||
);
|
||||
};
|
||||
|
||||
void interceptRequests(page);
|
||||
|
||||
await navigateToMailboxCreationFormForMailDomainFr(page);
|
||||
|
||||
const inputFirstName = page.getByLabel('First name');
|
||||
const inputLastName = page.getByLabel('Last name');
|
||||
const inputLocalPart = page.getByLabel('Email address prefix');
|
||||
const inputSecondaryEmailAddress = page.getByLabel(
|
||||
'Secondary email address',
|
||||
);
|
||||
const submitButton = page.getByRole('button', {
|
||||
name: 'Create the mailbox',
|
||||
});
|
||||
|
||||
const textAlreadyUsedLocalPart = page.getByText(
|
||||
'This email prefix is already used.',
|
||||
);
|
||||
|
||||
await inputFirstName.fill('John');
|
||||
await inputLastName.fill('Doe');
|
||||
await inputLocalPart.fill('john.already');
|
||||
await inputSecondaryEmailAddress.fill('john.already@mail.com');
|
||||
|
||||
await submitButton.click();
|
||||
|
||||
await expect(textAlreadyUsedLocalPart).toBeVisible();
|
||||
});
|
||||
|
||||
test('checks unknown api error causes are displayed above form when they are not related with invalid field', async ({
|
||||
page,
|
||||
}) => {
|
||||
const interceptRequests = async (page: Page) => {
|
||||
void interceptCommonApiRequests(page);
|
||||
|
||||
await page.route(
|
||||
'**/api/v1.0/mail-domains/domainfr/mailboxes/',
|
||||
async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 500,
|
||||
json: {
|
||||
unknown_error: ['Unknown error from server'],
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
{ times: 1 },
|
||||
);
|
||||
};
|
||||
|
||||
void interceptRequests(page);
|
||||
|
||||
await navigateToMailboxCreationFormForMailDomainFr(page);
|
||||
|
||||
const inputFirstName = page.getByLabel('First name');
|
||||
const inputLastName = page.getByLabel('Last name');
|
||||
const inputLocalPart = page.getByLabel('Email address prefix');
|
||||
const inputSecondaryEmailAddress = page.getByLabel(
|
||||
'Secondary email address',
|
||||
);
|
||||
|
||||
await inputFirstName.fill('John');
|
||||
await inputLastName.fill('Doe');
|
||||
await inputLocalPart.fill('john.doe');
|
||||
|
||||
await inputSecondaryEmailAddress.fill('john.do@mail.fr');
|
||||
|
||||
await page.getByRole('button', { name: 'Create the mailbox' }).click();
|
||||
|
||||
await expect(page.getByText('Unknown error from server')).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -132,142 +132,6 @@ test.describe('Add Mail Domains', () => {
|
||||
).toBeVisible();
|
||||
});
|
||||
|
||||
test('checks form submits at "Enter" key press', async ({ page }) => {
|
||||
void page.route('**/api/v1.0/mail-domains/', (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
void route.fulfill({
|
||||
json: {
|
||||
id: '2ebcfcfb-1dfa-4ed1-8e4a-554c63307b7c',
|
||||
name: 'enter.fr',
|
||||
slug: 'enterfr',
|
||||
status: 'pending',
|
||||
abilities: {
|
||||
get: true,
|
||||
patch: true,
|
||||
put: true,
|
||||
post: true,
|
||||
delete: true,
|
||||
manage_accesses: true,
|
||||
},
|
||||
created_at: '2024-08-21T10:55:21.081994Z',
|
||||
updated_at: '2024-08-21T10:55:21.082109Z',
|
||||
},
|
||||
});
|
||||
} else {
|
||||
void route.continue();
|
||||
}
|
||||
});
|
||||
|
||||
await page.goto('/mail-domains/');
|
||||
|
||||
const { linkIndexPageAddDomain, inputName } = getElements(page);
|
||||
|
||||
await linkIndexPageAddDomain.click();
|
||||
|
||||
await inputName.fill('enter.fr');
|
||||
await page.keyboard.press('Enter');
|
||||
|
||||
await expect(page).toHaveURL(`/mail-domains/enterfr/`);
|
||||
});
|
||||
|
||||
test('checks error when duplicate mail domain name', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.goto('/mail-domains/');
|
||||
|
||||
const { linkIndexPageAddDomain, inputName, buttonSubmit } =
|
||||
getElements(page);
|
||||
|
||||
const mailDomainName = randomName('duplicate.fr', browserName, 1)[0];
|
||||
const mailDomainSlug = mailDomainName.replace('.', '');
|
||||
|
||||
await linkIndexPageAddDomain.click();
|
||||
await inputName.fill(mailDomainName);
|
||||
await buttonSubmit.click();
|
||||
|
||||
await expect(page).toHaveURL(`/mail-domains\/${mailDomainSlug}\/`);
|
||||
|
||||
await linkIndexPageAddDomain.click();
|
||||
|
||||
await inputName.fill(mailDomainName);
|
||||
await buttonSubmit.click();
|
||||
|
||||
await expect(page).toHaveURL(/mail-domains\//);
|
||||
await expect(
|
||||
page.getByText(
|
||||
'This mail domain is already used. Please, choose another one.',
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(inputName).toBeFocused();
|
||||
});
|
||||
|
||||
test('checks error when duplicate mail domain slug', async ({
|
||||
page,
|
||||
browserName,
|
||||
}) => {
|
||||
await page.goto('/mail-domains/');
|
||||
|
||||
const { linkIndexPageAddDomain, inputName, buttonSubmit } =
|
||||
getElements(page);
|
||||
|
||||
const mailDomainSlug = randomName('duplicate', browserName, 1)[0];
|
||||
|
||||
await linkIndexPageAddDomain.click();
|
||||
await inputName.fill(mailDomainSlug);
|
||||
await buttonSubmit.click();
|
||||
|
||||
await expect(page).toHaveURL(`/mail-domains\/${mailDomainSlug}\/`);
|
||||
|
||||
await linkIndexPageAddDomain.click();
|
||||
|
||||
await inputName.fill(mailDomainSlug);
|
||||
await buttonSubmit.click();
|
||||
|
||||
await expect(page).toHaveURL(/mail-domains\//);
|
||||
await expect(
|
||||
page.getByText(
|
||||
'This mail domain is already used. Please, choose another one.',
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(inputName).toBeFocused();
|
||||
});
|
||||
|
||||
test('checks unknown api error causes are displayed', async ({ page }) => {
|
||||
await page.route(
|
||||
'**/api/v1.0/mail-domains/',
|
||||
async (route) => {
|
||||
if (route.request().method() === 'POST') {
|
||||
await route.fulfill({
|
||||
status: 500,
|
||||
json: {
|
||||
unknown_error: ['Unknown error from server'],
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
{ times: 1 },
|
||||
);
|
||||
|
||||
await page.goto('/mail-domains/');
|
||||
|
||||
const { linkIndexPageAddDomain, inputName, buttonSubmit } =
|
||||
getElements(page);
|
||||
|
||||
await linkIndexPageAddDomain.click();
|
||||
await inputName.fill('server-error.fr');
|
||||
await buttonSubmit.click();
|
||||
|
||||
await expect(page).toHaveURL(/mail-domains\//);
|
||||
await expect(
|
||||
page.getByText(
|
||||
'Your request cannot be processed because the server is experiencing an error. If the problem ' +
|
||||
'persists, please contact our support to resolve the issue: suiteterritoriale@anct.gouv.fr.',
|
||||
),
|
||||
).toBeVisible();
|
||||
await expect(inputName).toBeFocused();
|
||||
});
|
||||
|
||||
test('checks 404 on mail-domains/[slug] page', async ({ page }) => {
|
||||
await page.goto('/mail-domains/unknown-domain');
|
||||
|
||||
|
||||
Reference in New Issue
Block a user