Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,21 +36,7 @@ exports[`client has multiple campaign ids matches snapshot on initial render 1`]
<div
class="nhsuk-grid-column-two-thirds"
>
<form
action="javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')"
>
<input
name="form-id"
readonly=""
type="hidden"
value="upload-large-print-letter-template"
/>
<input
name="csrf_token"
readonly=""
type="hidden"
value="no_token"
/>
<form>
<div
class="nhsuk-form-group nhsuk-u-margin-bottom-6"
>
Expand Down Expand Up @@ -314,21 +300,7 @@ exports[`client has multiple campaign ids renders errors when blank form is subm
<div
class="nhsuk-grid-column-two-thirds"
>
<form
action="javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')"
>
<input
name="form-id"
readonly=""
type="hidden"
value="upload-large-print-letter-template"
/>
<input
name="csrf_token"
readonly=""
type="hidden"
value="no_token"
/>
<form>
<div
class="nhsuk-form-group nhsuk-u-margin-bottom-6 nhsuk-form-group--error"
>
Expand Down Expand Up @@ -583,21 +555,7 @@ exports[`client has one campaign id matches snapshot on initial render 1`] = `
<div
class="nhsuk-grid-column-two-thirds"
>
<form
action="javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')"
>
<input
name="form-id"
readonly=""
type="hidden"
value="upload-large-print-letter-template"
/>
<input
name="csrf_token"
readonly=""
type="hidden"
value="no_token"
/>
<form>
<div
class="nhsuk-form-group nhsuk-u-margin-bottom-6"
>
Expand Down Expand Up @@ -848,21 +806,7 @@ exports[`client has one campaign id renders errors when blank form is submitted
<div
class="nhsuk-grid-column-two-thirds"
>
<form
action="javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')"
>
<input
name="form-id"
readonly=""
type="hidden"
value="upload-large-print-letter-template"
/>
<input
name="csrf_token"
readonly=""
type="hidden"
value="no_token"
/>
<form>
<div
class="nhsuk-form-group nhsuk-u-margin-bottom-6 nhsuk-form-group--error"
>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
import { uploadDocxTemplate } from '@utils/form-actions';
import { mockDeep } from 'jest-mock-extended';
import type { TemplateDto } from 'nhs-notify-web-template-management-types';
import { handleSubmit } from '@app/upload-british-sign-language-letter-template/form-action';
import type { AppRouterInstance } from 'next/dist/shared/lib/app-router-context.shared-runtime';

jest.mock('@utils/form-actions');

const mockRouter = mockDeep<AppRouterInstance>();

function createSubmitEvent(formData: FormData): React.SubmitEvent {
const event = mockDeep<React.SubmitEvent>({
preventDefault: jest.fn(),
});

jest.spyOn(globalThis, 'FormData').mockImplementation(() => formData);
return event;
}

describe('handleSubmit', () => {
beforeEach(() => {
jest.restoreAllMocks();
});

it('uploads template and navigates when all fields are valid', async () => {
const mockUploadDocxTemplate = jest
.mocked(uploadDocxTemplate)
.mockResolvedValue(
mockDeep<TemplateDto>({
id: 'template-id',
})
);

const setState = jest.fn();

const formData = new FormData();
formData.append('name', 'Test Template');
formData.append('campaignId', 'Campaign 1');

const file = new File(['content'], 'template.docx', {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
formData.append('file', file);

const event = createSubmitEvent(formData);
const handler = handleSubmit(mockRouter, [{}, setState]);
await handler(event);

expect(event.preventDefault).toHaveBeenCalled();
expect(mockUploadDocxTemplate).toHaveBeenCalledWith(
{
name: 'Test Template',
campaignId: 'Campaign 1',
letterType: 'q4',
language: 'en',
templateType: 'LETTER',
letterVersion: 'AUTHORING',
},
file
);

expect(mockRouter.push).toHaveBeenCalledWith(
'/preview-letter-template/template-id?from=upload'
);
expect(setState).not.toHaveBeenCalled();
});

it('sets error state when name is empty', async () => {
const setState = jest.fn();

const formData = new FormData();
formData.append('name', '');
formData.append('campaignId', 'Campaign 1');

const file = new File(['content'], 'template.docx', {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
formData.append('file', file);

const event = createSubmitEvent(formData);
const handler = handleSubmit(mockRouter, [{}, setState]);
await handler(event);

expect(setState).toHaveBeenCalledWith(
expect.objectContaining({
errorState: expect.objectContaining({
fieldErrors: expect.objectContaining({
name: ['Enter a template name'],
}),
}),
})
);
expect(mockRouter.push).not.toHaveBeenCalled();
});

it('sets error state when campaignId is empty', async () => {
const setState = jest.fn();

const formData = new FormData();
formData.append('name', 'Test Template');
formData.append('campaignId', '');

const file = new File(['content'], 'template.docx', {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
formData.append('file', file);

const event = createSubmitEvent(formData);
const handler = handleSubmit(mockRouter, [{}, setState]);
await handler(event);

expect(setState).toHaveBeenCalledWith(
expect.objectContaining({
errorState: expect.objectContaining({
fieldErrors: expect.objectContaining({
campaignId: ['Choose a campaign'],
}),
}),
})
);
expect(mockRouter.push).not.toHaveBeenCalled();
});

it('sets error state when file is missing', async () => {
const setState = jest.fn();

const formData = new FormData();
formData.append('name', 'Test Template');
formData.append('campaignId', 'Campaign 1');

const event = createSubmitEvent(formData);
const handler = handleSubmit(mockRouter, [{}, setState]);
await handler(event);

expect(setState).toHaveBeenCalledWith(
expect.objectContaining({
errorState: expect.objectContaining({
fieldErrors: expect.objectContaining({
file: ['Choose a template file'],
}),
}),
})
);
expect(mockRouter.push).not.toHaveBeenCalled();
});

it('sets error state when file has incorrect MIME type', async () => {
const setState = jest.fn();

const formData = new FormData();
formData.append('name', 'Test Template');
formData.append('campaignId', 'Campaign 1');

const file = new File(['content'], 'template.pdf', {
type: 'application/pdf',
});
formData.append('file', file);

const event = createSubmitEvent(formData);
const handler = handleSubmit(mockRouter, [{}, setState]);
await handler(event);

expect(setState).toHaveBeenCalledWith(
expect.objectContaining({
errorState: expect.objectContaining({
fieldErrors: expect.objectContaining({
file: ['Choose a template file'],
}),
}),
})
);
expect(mockRouter.push).not.toHaveBeenCalled();
});

it('sets error state when file is too large', async () => {
const setState = jest.fn();

const formData = new FormData();
formData.append('name', 'Test Template');
formData.append('campaignId', 'Campaign 1');

const largeContent = new Uint8Array(6 * 1024 * 1024);
const file = new File([largeContent], 'template.docx', {
type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
});
formData.append('file', file);

const event = createSubmitEvent(formData);
const handler = handleSubmit(mockRouter, [{}, setState]);
await handler(event);

expect(setState).toHaveBeenCalledWith(
expect.objectContaining({
errorState: expect.objectContaining({
fieldErrors: expect.objectContaining({
file: [
'Your file is too large. The file must be smaller than 5MB. Upload a different letter template file',
],
}),
}),
})
);
expect(mockRouter.push).not.toHaveBeenCalled();
});

it('sets multiple validation errors when multiple fields are invalid', async () => {
const setState = jest.fn();

const formData = new FormData();
formData.append('name', '');
formData.append('campaignId', '');

const event = createSubmitEvent(formData);
const handler = handleSubmit(mockRouter, [{}, setState]);
await handler(event);

expect(setState).toHaveBeenCalledWith(
expect.objectContaining({
errorState: expect.objectContaining({
fieldErrors: expect.objectContaining({
name: ['Enter a template name'],
campaignId: ['Choose a campaign'],
file: ['Choose a template file'],
}),
}),
})
);
expect(mockRouter.push).not.toHaveBeenCalled();
});
});
Loading
Loading