Skip to content

Commit

Permalink
✨(frontend) add support email field on domain creation form
Browse files Browse the repository at this point in the history
Add new field to give email of support to manage actions
required on domain.
  • Loading branch information
sdemagny committed Feb 10, 2025
1 parent aa34fe5 commit 9e0ab6e
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ describe('MailDomainAccessesPage', () => {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: '[email protected]',
abilities: {
get: true,
patch: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ describe('AccessAction', () => {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: '[email protected]',
abilities: {
get: true,
patch: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('AccessesContent', () => {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: '[email protected]',
abilities: {
get: true,
patch: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const mockMailDomain: MailDomain = {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: '[email protected]',
abilities: {
manage_accesses: true,
get: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ describe('ModalDelete', () => {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: '[email protected]',
abilities: {
get: true,
patch: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('ModalAddMailDomain', () => {
modalElement: screen.getByText('Add a mail domain'),
formTag: screen.getByTitle('Mail domain addition form'),
inputName: screen.getByLabelText(/Domain name/i),
inputSupportEmail: screen.getByLabelText(/Support email address/i),
buttonCancel: screen.getByRole('button', { name: /Cancel/i, hidden: true }),
buttonSubmit: screen.getByRole('button', {
name: /Add the domain/i,
Expand All @@ -37,12 +38,19 @@ describe('ModalAddMailDomain', () => {
it('renders all the elements', () => {
render(<ModalAddMailDomain />, { wrapper: AppWrapper });

const { modalElement, formTag, inputName, buttonCancel, buttonSubmit } =
getElements();
const {
modalElement,
formTag,
inputName,
inputSupportEmail,
buttonCancel,
buttonSubmit,
} = getElements();

expect(modalElement).toBeVisible();
expect(formTag).toBeVisible();
expect(inputName).toBeVisible();
expect(inputSupportEmail).toBeVisible();
expect(screen.getByText('Example: saint-laurent.fr')).toBeVisible();
expect(buttonCancel).toBeVisible();
expect(buttonSubmit).toBeVisible();
Expand Down Expand Up @@ -104,16 +112,18 @@ describe('ModalAddMailDomain', () => {

render(<ModalAddMailDomain />, { wrapper: AppWrapper });

const { inputName, buttonSubmit } = getElements();
const { inputName, inputSupportEmail, buttonSubmit } = getElements();

await user.type(inputName, 'domain.fr');
await user.type(inputSupportEmail, '[email protected]');

await user.click(buttonSubmit);

expect(fetchMock.lastUrl()).toContain('/mail-domains/');
expect(fetchMock.lastOptions()).toEqual({
body: JSON.stringify({
name: 'domain.fr',
support_email: '[email protected]',
}),
credentials: 'include',
headers: { 'Content-Type': 'application/json' },
Expand All @@ -123,29 +133,6 @@ describe('ModalAddMailDomain', () => {
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,
Expand All @@ -158,10 +145,10 @@ describe('ModalAddMailDomain', () => {

render(<ModalAddMailDomain />, { wrapper: AppWrapper });

const { inputName, buttonSubmit } = getElements();
const { inputName, inputSupportEmail, buttonSubmit } = getElements();

await user.type(inputName, 'domain.fr');

await user.type(inputSupportEmail, '[email protected]');
await user.click(buttonSubmit);

await waitFor(() => {
Expand All @@ -175,6 +162,7 @@ describe('ModalAddMailDomain', () => {
expect(inputName).toHaveFocus();

await user.type(inputName, 'domain2.fr');

expect(buttonSubmit).toBeEnabled();
});

Expand All @@ -190,9 +178,10 @@ describe('ModalAddMailDomain', () => {

render(<ModalAddMailDomain />, { wrapper: AppWrapper });

const { inputName, buttonSubmit } = getElements();
const { inputName, inputSupportEmail, buttonSubmit } = getElements();

await user.type(inputName, 'domainfr');
await user.type(inputSupportEmail, '[email protected]');

await user.click(buttonSubmit);

Expand Down Expand Up @@ -220,9 +209,10 @@ describe('ModalAddMailDomain', () => {

render(<ModalAddMailDomain />, { wrapper: AppWrapper });

const { inputName, buttonSubmit } = getElements();
const { inputName, inputSupportEmail, buttonSubmit } = getElements();

await user.type(inputName, 'domain.fr');
await user.type(inputSupportEmail, '[email protected]');

await user.click(buttonSubmit);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import { KEY_LIST_MAIL_DOMAIN } from './useMailDomains';

export interface AddMailDomainParams {
name: string;
supportEmail: string;
}

export const addMailDomain = async (
name: AddMailDomainParams['name'],
): Promise<MailDomain> => {
export const addMailDomain = async ({
name,
supportEmail,
}: AddMailDomainParams): Promise<MailDomain> => {
const response = await fetchAPI(`mail-domains/`, {
method: 'POST',
body: JSON.stringify({
name,
}),
body: JSON.stringify({ name, support_email: supportEmail }),
});

if (!response.ok) {
Expand All @@ -38,7 +38,7 @@ export const useAddMailDomain = ({
onError: (error: APIError) => void;
}) => {
const queryClient = useQueryClient();
return useMutation<MailDomain, APIError, string>({
return useMutation<MailDomain, APIError, AddMailDomainParams>({
mutationFn: addMailDomain,
onSuccess: (data) => {
void queryClient.invalidateQueries({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ 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';
Expand All @@ -23,12 +24,14 @@ export const ModalAddMailDomain = () => {

const addMailDomainValidationSchema = z.object({
name: z.string().min(1, t('Example: saint-laurent.fr')),
supportEmail: z.string().email(t('Please enter a valid email address')),
});

const methods = useForm<{ name: string }>({
const methods = useForm<{ name: string; supportEmail: string }>({
delayError: 0,
defaultValues: {
name: '',
supportEmail: '',
},
mode: 'onChange',
reValidateMode: 'onChange',
Expand All @@ -39,7 +42,7 @@ export const ModalAddMailDomain = () => {
onSuccess: (mailDomain) => {
router.push(`/mail-domains/${mailDomain.slug}`);
},
onError: (error) => {
onError: (error: APIError) => {
const unhandledCauses = parseAPIError({
error,
errorParams: [
Expand Down Expand Up @@ -87,8 +90,8 @@ export const ModalAddMailDomain = () => {
const onSubmitCallback = (event: React.FormEvent) => {
event.preventDefault();

void methods.handleSubmit(({ name }) => {
void addMailDomain(name);
void methods.handleSubmit(({ name, supportEmail }) => {
void addMailDomain({ name, supportEmail });
})();
};

Expand Down Expand Up @@ -167,6 +170,27 @@ export const ModalAddMailDomain = () => {
/>
)}
/>
<Box $margin={{ vertical: '10px' }}>
<Controller
control={methods.control}
name="supportEmail"
render={({ fieldState }) => (
<Input
aria-invalid={!!fieldState.error}
aria-required
required
label={t('Support email address')}
state={fieldState.error ? 'error' : 'default'}
text={
fieldState?.error?.message
? fieldState.error.message
: t('E.g. : [email protected]')
}
{...methods.register('supportEmail')}
/>
)}
/>
</Box>
</form>

{isPending && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export interface MailDomain {
updated_at: string;
slug: string;
status: 'pending' | 'enabled' | 'failed' | 'disabled';
support_email: string;
abilities: {
get: boolean;
patch: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const mockMailDomain: MailDomain = {
name: 'example.com',
slug: 'example-com',
status: 'enabled',
support_email: '[email protected]',
abilities: {
get: true,
patch: true,
Expand All @@ -31,6 +32,7 @@ const mockMailDomainAsViewer: MailDomain = {
name: 'example.com',
slug: 'example-com',
status: 'enabled',
support_email: '[email protected]',
abilities: {
get: true,
patch: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const mockMailDomain: MailDomain = {
status: 'enabled',
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
support_email: '[email protected]',
abilities: {
get: true,
patch: true,
Expand Down
23 changes: 19 additions & 4 deletions src/frontend/apps/e2e/__tests__/app-desk/mail-domains-add.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const getElements = (page: Page) => {
});
const form = page.locator('form');
const inputName = form.getByLabel('Domain name');
const inputSupportEmail = form.getByLabel('Support email address');
const buttonSubmit = page.getByRole('button', {
name: 'Add the domain',
});
Expand All @@ -22,6 +23,7 @@ const getElements = (page: Page) => {
linkIndexPageAddDomain,
form,
inputName,
inputSupportEmail,
buttonCancel,
buttonSubmit,
};
Expand Down Expand Up @@ -50,6 +52,7 @@ test.describe('Add Mail Domains', () => {
}),
).toBeVisible();

await expect(inputName).toBeVisible();
await expect(inputName).toBeVisible();

await expect(page.getByText('Example: saint-laurent.fr')).toBeVisible();
Expand Down Expand Up @@ -82,12 +85,17 @@ test.describe('Add Mail Domains', () => {
test('checks form invalid status', async ({ page }) => {
await page.goto('/mail-domains/');

const { linkIndexPageAddDomain, inputName, buttonSubmit } =
getElements(page);
const {
linkIndexPageAddDomain,
inputName,
inputSupportEmail,
buttonSubmit,
} = getElements(page);

await linkIndexPageAddDomain.click();

await expect(inputName).toBeVisible();
await expect(inputSupportEmail).toBeVisible();
await expect(page.getByText('Example: saint-laurent.fr')).toBeVisible();

await expect(
Expand All @@ -111,16 +119,23 @@ test.describe('Add Mail Domains', () => {
browserName,
}) => {
const mailDomainName = randomName('versailles.fr', browserName, 1)[0];
const mailDomainSupportMail = 'support@'.concat(mailDomainName);
const mailDomainSlug = mailDomainName.replace('.', '');

await page.goto('/mail-domains/');

const { linkIndexPageAddDomain, inputName, buttonSubmit } =
getElements(page);
const {
linkIndexPageAddDomain,
inputName,
inputSupportEmail,
buttonSubmit,
} = getElements(page);

await linkIndexPageAddDomain.click();

await inputName.fill(mailDomainName);
await inputSupportEmail.fill(mailDomainSupportMail);

await buttonSubmit.click();

await expect(page).toHaveURL(`/mail-domains/${mailDomainSlug}/`);
Expand Down

0 comments on commit 9e0ab6e

Please sign in to comment.