diff --git a/src/server/plugins/engine/form-availability.test.ts b/src/server/plugins/engine/form-availability.test.ts index aa50c8f95..341c76275 100644 --- a/src/server/plugins/engine/form-availability.test.ts +++ b/src/server/plugins/engine/form-availability.test.ts @@ -1,3 +1,4 @@ +import { FormStatus } from '@defra/forms-model' import Boom from '@hapi/boom' import { @@ -8,17 +9,48 @@ import { metadata } from '~/test/fixtures/form.js' describe('form-availability', () => { describe('assertFormAvailable', () => { - it('should do nothing if form is online', () => { + it('should do nothing if live form is online', () => { expect(() => - assertFormAvailable({ ...metadata, offline: false }) + assertFormAvailable( + { ...metadata, offline: false }, + FormStatus.Live, + false + ) ).not.toThrow() expect(() => - assertFormAvailable({ ...metadata, offline: undefined }) + assertFormAvailable( + { ...metadata, offline: undefined }, + FormStatus.Live, + false + ) + ).not.toThrow() + }) + + it('should do nothing if draft form or live preview is online', () => { + expect(() => + assertFormAvailable( + { ...metadata, offline: false }, + FormStatus.Draft, + true + ) + ).not.toThrow() + expect(() => + assertFormAvailable( + { ...metadata, offline: undefined }, + FormStatus.Live, + true + ) ).not.toThrow() }) it('should throw a 503 Boom error if form is offline', () => { - expect(() => assertFormAvailable({ ...metadata, offline: true })).toThrow( + expect(() => + assertFormAvailable( + { ...metadata, offline: true }, + FormStatus.Live, + false + ) + ).toThrow( expect.objectContaining({ message: `Form ${metadata.slug} is offline`, data: { diff --git a/src/server/plugins/engine/form-availability.ts b/src/server/plugins/engine/form-availability.ts index 214df81fb..de140534e 100644 --- a/src/server/plugins/engine/form-availability.ts +++ b/src/server/plugins/engine/form-availability.ts @@ -1,4 +1,4 @@ -import { type FormMetadata } from '@defra/forms-model' +import { FormStatus, type FormMetadata } from '@defra/forms-model' import Boom from '@hapi/boom' export interface OfflineBoomData { @@ -11,8 +11,16 @@ export interface OfflineBoomData { * unavailable-response extension catches the marker and renders the * "Sorry, this form is unavailable" view at HTTP 200. */ -export function assertFormAvailable(metadata: FormMetadata): void { - if (metadata.offline === true) { +export function assertFormAvailable( + metadata: FormMetadata, + formState: FormStatus, + isPreview: boolean +): void { + if ( + metadata.offline === true && + formState === FormStatus.Live && + !isPreview + ) { const data: OfflineBoomData = { offline: true, metadata } throw Boom.boomify(new Error(`Form ${metadata.slug} is offline`), { statusCode: 503, diff --git a/src/server/plugins/engine/beta/form-context.test.ts b/src/server/plugins/engine/form-context.test.ts similarity index 95% rename from src/server/plugins/engine/beta/form-context.test.ts rename to src/server/plugins/engine/form-context.test.ts index aed997266..c6f4aa340 100644 --- a/src/server/plugins/engine/beta/form-context.test.ts +++ b/src/server/plugins/engine/form-context.test.ts @@ -6,7 +6,7 @@ import { getFormModel, resolveFormModel, type FormModelOptions -} from '~/src/server/plugins/engine/beta/form-context.js' +} from '~/src/server/plugins/engine/form-context.js' import { PageController } from '~/src/server/plugins/engine/pageControllers/PageController.js' import { type PageControllerClass } from '~/src/server/plugins/engine/pageControllers/helpers/pages.js' import { type FormContext } from '~/src/server/plugins/engine/types.js' @@ -17,7 +17,7 @@ const mockGetCacheService = jest.fn() const mockCacheService = { getState: jest.fn() } const mockCheckEmailAddressForLiveFormSubmission = jest.fn() -jest.mock('../models/index.ts', () => ({ +jest.mock('~/src/server/plugins/engine/models/index.ts', () => ({ __esModule: true, FormModel: jest.fn() })) @@ -32,7 +32,7 @@ jest.mock('~/src/server/plugins/engine/services/index.js', () => ({ outputService: {} })) -jest.mock('../pageControllers/index.ts', () => { +jest.mock('~/src/server/plugins/engine/pageControllers/index.ts', () => { class MockTerminalPageController { path = '' } @@ -43,8 +43,8 @@ jest.mock('../pageControllers/index.ts', () => { } }) -jest.mock('../helpers.ts', () => ({ - ...jest.requireActual('../helpers.ts'), +jest.mock('~/src/server/plugins/engine/helpers.ts', () => ({ + ...jest.requireActual('~/src/server/plugins/engine/helpers.ts'), getCacheService: (...args: unknown[]): unknown => mockGetCacheService(...args), checkEmailAddressForLiveFormSubmission: (...args: unknown[]): unknown => @@ -56,13 +56,14 @@ const mockServices: { } = jest.requireMock('~/src/server/plugins/engine/services/index.js') const mockFormsService = mockServices.formsService -const { FormModel }: { FormModel: jest.Mock } = - jest.requireMock('../models/index.ts') +const { FormModel }: { FormModel: jest.Mock } = jest.requireMock( + '~/src/server/plugins/engine/models/index.ts' +) const { TerminalPageController: MockTerminalPageController }: { TerminalPageController: new () => { path: string } } = jest.requireMock( - '../pageControllers/index.ts' + '~/src/server/plugins/engine/pageControllers/index.ts' ) describe('getFormContext helper', () => { diff --git a/src/server/plugins/engine/beta/form-context.ts b/src/server/plugins/engine/form-context.ts similarity index 98% rename from src/server/plugins/engine/beta/form-context.ts rename to src/server/plugins/engine/form-context.ts index 88b06839d..c3c4f4786 100644 --- a/src/server/plugins/engine/beta/form-context.ts +++ b/src/server/plugins/engine/form-context.ts @@ -53,7 +53,7 @@ export async function getFormModel( const formState = resolveState(state) const metadata = await formsService.getFormMetadata(slug) - assertFormAvailable(metadata) + assertFormAvailable(metadata, formState, isPreview) const definition = await formsService.getFormDefinition( metadata.id, @@ -136,9 +136,9 @@ export async function resolveFormModel( const { formsService } = services const metadata = await formsService.getFormMetadata(slug) - assertFormAvailable(metadata) const formState = resolveState(state) const isPreview = options.isPreview ?? isPreviewState(state, options) + assertFormAvailable(metadata, formState, isPreview) const stateMetadata = metadata[formState] if (!stateMetadata) { diff --git a/src/server/plugins/engine/index.ts b/src/server/plugins/engine/index.ts index 1e62f5e67..fa0efdfaa 100644 --- a/src/server/plugins/engine/index.ts +++ b/src/server/plugins/engine/index.ts @@ -19,7 +19,7 @@ export { getFormContext, getFormModel, resolveFormModel -} from '~/src/server/plugins/engine/beta/form-context.js' +} from '~/src/server/plugins/engine/form-context.js' export * from '~/src/server/plugins/engine/form-availability.js' const globals = { diff --git a/src/server/plugins/engine/models/unavailable-view-model.test.ts b/src/server/plugins/engine/models/unavailable-view-model.test.ts index dc9ab8636..bc8f2ad80 100644 --- a/src/server/plugins/engine/models/unavailable-view-model.test.ts +++ b/src/server/plugins/engine/models/unavailable-view-model.test.ts @@ -17,46 +17,8 @@ describe('unavailableViewModel', () => { it('should strip the organisation suffix if present', () => { const result = unavailableViewModel({ ...metadata, - organisation: 'Rural Payments Agency – RPA' + organisation: 'Rural Payments Agency - RPA' } as FormMetadata) - expect(result.organisationName).toBe('Rural Payments Agency') - }) - - it('should handle multiple phone lines correctly', () => { - const result = unavailableViewModel({ - ...metadata, - contact: { - phone: '01234 567 890\n09876 543 210' - } - } as FormMetadata) - expect(result.phoneLines).toEqual(['01234 567 890', '09876 543 210']) - }) - - it('should filter out empty phone lines and trim whitespace', () => { - const result = unavailableViewModel({ - ...metadata, - contact: { - phone: ' 01234 567 890 \n \n 09876 543 210 ' - } - } as FormMetadata) - expect(result.phoneLines).toEqual(['01234 567 890', '09876 543 210']) - }) - - it('should return undefined if phone is empty or only whitespace', () => { - const result = unavailableViewModel({ - ...metadata, - contact: { - phone: ' \n ' - } - } as FormMetadata) - expect(result.phoneLines).toBeUndefined() - }) - - it('should handle missing contact property', () => { - const result = unavailableViewModel({ - ...metadata, - contact: undefined - } as FormMetadata) - expect(result.phoneLines).toBeUndefined() + expect(result.organisationName).toBe('Rural Payments Agency - RPA') }) }) diff --git a/src/server/plugins/engine/models/unavailable-view-model.ts b/src/server/plugins/engine/models/unavailable-view-model.ts index 244607516..f69d477b9 100644 --- a/src/server/plugins/engine/models/unavailable-view-model.ts +++ b/src/server/plugins/engine/models/unavailable-view-model.ts @@ -1,27 +1,10 @@ -import { type FormMetadata } from '@defra/forms-model' +import { type FormMetadata, type FormMetadataContact } from '@defra/forms-model' export interface UnavailableViewModel { pageTitle: string formTitle: string organisationName: string - phoneLines?: string[] -} - -/** - * Defra organisations carry an abbreviation suffix on the enum value, e.g. - * "Rural Payments Agency – RPA". The unavailable page reads cleanly without it. - */ -function stripOrgSuffix(organisation: string) { - return organisation.split(' – ')[0] -} - -function splitPhoneLines(phone: string | undefined) { - if (!phone) return undefined - const lines = phone - .split('\n') - .map((line) => line.trim()) - .filter((line) => line.length > 0) - return lines.length > 0 ? lines : undefined + contact?: FormMetadataContact } export function unavailableViewModel( @@ -30,7 +13,7 @@ export function unavailableViewModel( return { pageTitle: 'Sorry, this form is unavailable', formTitle: metadata.title, - organisationName: stripOrgSuffix(metadata.organisation), - phoneLines: splitPhoneLines(metadata.contact?.phone) + organisationName: metadata.organisation, + contact: metadata.contact } } diff --git a/src/server/plugins/engine/routes/index.ts b/src/server/plugins/engine/routes/index.ts index ef324cd6b..de05dcb5b 100644 --- a/src/server/plugins/engine/routes/index.ts +++ b/src/server/plugins/engine/routes/index.ts @@ -10,11 +10,11 @@ import { EXTERNAL_STATE_APPENDAGE, EXTERNAL_STATE_PAYLOAD } from '~/src/server/constants.js' -import { resolveFormModel } from '~/src/server/plugins/engine/beta/form-context.js' import { FormComponent, isFormState } from '~/src/server/plugins/engine/components/FormComponent.js' +import { resolveFormModel } from '~/src/server/plugins/engine/form-context.js' import { checkFormStatus, findPage, diff --git a/src/server/plugins/engine/views/unavailable.html b/src/server/plugins/engine/views/unavailable.html index f82fb10c9..6d61a5ff8 100644 --- a/src/server/plugins/engine/views/unavailable.html +++ b/src/server/plugins/engine/views/unavailable.html @@ -4,17 +4,31 @@

Sorry, this form is unavailable

-

'{{ formTitle }}' has been archived and is no longer available.

-

Contact the {{ organisationName }}.

+

'{{ formTitle }}' is no longer available.

+

Contact details for {{ organisationName }}

- {% if phoneLines %} -
    - {% for line in phoneLines %} -
  • {{ line }}
  • - {% endfor %} -
+ {% if contact.phone %} +

Telephone

+
+ {{ contact.phone | markdown(3) | safe }} +

Find out about call charges

{% endif %} + + {% if contact.email %} +

Email

+ + {% endif %} + + {% if contact.online %} +

Online contact form

+ + {% endif %}
{% endblock %}