diff --git a/charts/prl-citizen-frontend/values.preview.template.yaml b/charts/prl-citizen-frontend/values.preview.template.yaml index 7379abd31e..5c7b2ce263 100644 --- a/charts/prl-citizen-frontend/values.preview.template.yaml +++ b/charts/prl-citizen-frontend/values.preview.template.yaml @@ -17,10 +17,10 @@ nodejs: SERVICE_AUTH_PROVIDER_URL: 'http://rpe-service-auth-provider-{{ .Values.global.environment }}.service.core-compute-{{ .Values.global.environment }}.internal' IDAM_WEB_URL: 'https://idam-web-public.{{ .Values.global.environment }}.platform.hmcts.net/login' IDAM_API_URL: 'https://idam-api.{{ .Values.global.environment }}.platform.hmcts.net/o/token' + CCD_URL: 'https://ccd-data-store-api-prl-ccd-definitions-pr-2429.preview.platform.hmcts.net' EQUALITY_URL: 'https://pcq.aat.platform.hmcts.net' - CCD_URL: 'http://ccd-data-store-api-aat.service.core-compute-aat.internal' - DOCUMENT_MANAGEMENT_URL: 'http://ccd-case-document-am-api-aat.service.core-compute-aat.internal' - COS_URL: 'http://prl-cos-aat.service.core-compute-aat.internal' + DOCUMENT_MANAGEMENT_URL: 'https://ccd-case-document-am-api-prl-ccd-definitions-pr-2429.preview.platform.hmcts.net' + COS_URL: 'https://prl-cos-pr-2740.preview.platform.hmcts.net' REASONABLE_ADJUSTMENTS: 'https://cui-ra.aat.platform.hmcts.net' FACT_URL: 'http://fact-api-aat.service.core-compute-aat.internal' REDIS_HOST: 'prl-citizen-frontend-secondary-redis-{{ .Values.global.environment }}.redis.cache.windows.net' diff --git a/config/development.yaml b/config/development.yaml index 8d58ba015d..9c886b3a89 100644 --- a/config/development.yaml +++ b/config/development.yaml @@ -21,11 +21,11 @@ services: citizen: uploadDocsEmail: UPLOAD_DOCUMENTS_EMAIL case: - url: 'http://ccd-data-store-api-aat.service.core-compute-aat.internal' + url: 'https://ccd-data-store-api-prl-ccd-definitions-pr-2429.preview.platform.hmcts.net' cos: - url: 'http://prl-cos-aat.service.core-compute-aat.internal' + url: 'https://prl-cos-pr-2740.preview.platform.hmcts.net' documentManagement: - url: 'http://ccd-case-document-am-api-aat.service.core-compute-aat.internal' + url: 'https://ccd-case-document-am-api-prl-ccd-definitions-pr-2429.preview.platform.hmcts.net' fact: url: 'http://fact-api-aat.service.core-compute-aat.internal' reasonableAdjustments: diff --git a/src/main/app/case/case.ts b/src/main/app/case/case.ts index 216ee157c5..6ff09fea1b 100644 --- a/src/main/app/case/case.ts +++ b/src/main/app/case/case.ts @@ -456,6 +456,8 @@ export interface Case { citizenUserPhoneNumberText?: string; citizenUserDateOfBirth?: CaseDate; citizenUserDateOfBirthText?: string; + citizenUserLivingInRefugeText?: string; + refugeDocumentText?: string; applicant1Occupation?: string; citizenUserSelectAddress?: string; citizenUserPlaceOfBirth?: string; @@ -475,6 +477,7 @@ export interface Case { citizenUserManualAddressTown?: string; citizenUserManualAddressCounty?: string; citizenUserManualAddressPostcode?: string; + isCitizenLivingInRefuge?: YesOrNo; //applicant1LanguagePreference?: LanguagePreference; //support you need during the case @@ -691,6 +694,8 @@ export interface Case { awp_supportingDocuments?: DocumentInfo[]; awp_applicationType?: AWPApplicationType; awp_applicationReason?: AWPApplicationReason; + refugeDocument?: Document; + reUploadRefugeDocument?: YesOrNo; } export interface CitizenNotification { diff --git a/src/main/app/case/definition.ts b/src/main/app/case/definition.ts index 24e564e2c8..e7b40838b7 100644 --- a/src/main/app/case/definition.ts +++ b/src/main/app/case/definition.ts @@ -113,6 +113,8 @@ export interface PartyDetails { contactPreferences?: ContactPreference | null; isRemoveLegalRepresentativeRequested?: YesOrNo; partyId: string; + liveInRefuge: YesOrNo; + refugeConfidentialityC8Form?: Document | null; } export interface User { @@ -672,6 +674,8 @@ export type C100Applicant = { applicantAddressTown?: string; applicantAddressCounty?: string; country?: string; + liveInRefuge?: YesOrNo; + refugeConfidentialityC8Form?: Document; applicantAddressHistory?: YesOrNo; applicantProvideDetailsOfPreviousAddresses?: string; personalDetails: { @@ -696,12 +700,12 @@ export interface RelationshipToChildren { } export interface ContactDetail { - canProvideEmail?: YesNoEmpty; + canProvideEmail?: YesOrNo | null; emailAddress?: string; - canProvideTelephoneNumber?: YesNoEmpty; + canProvideTelephoneNumber?: YesOrNo | null; telephoneNumber?: string; canNotProvideTelephoneNumberReason?: string; - canLeaveVoiceMail?: YesNoEmpty; + canLeaveVoiceMail?: YesOrNo | null; applicantContactPreferences?: string; } @@ -2767,6 +2771,8 @@ export type C100RebuildPartyDetails = { donKnowTelephoneNumber?: YesOrNo; }; addressUnknown?: YesOrNo; + liveInRefuge?: YesOrNo; + refugeConfidentialityC8Form?: Document; }; export interface RelationshipToChildren { @@ -2965,6 +2971,7 @@ export enum PaymentErrorContext { export enum RootContext { C100_REBUILD = 'c100-rebuild', RESPONDENT = 'respondent', + APPLICANT = 'applicant' } export enum DomesticAbuseExemptions { diff --git a/src/main/routes.ts b/src/main/routes.ts index d68d3ab5b0..efee8e06dc 100644 --- a/src/main/routes.ts +++ b/src/main/routes.ts @@ -15,6 +15,7 @@ import { routeGuard } from './steps/application-within-proceedings/routeGuard'; import { processAWPApplication } from './steps/application-within-proceedings/utils'; import CaseDataController from './steps/common/CaseDataController'; import DownloadDocumentController from './steps/common/documents/download/DownloadDocumentController'; +import { C8RefugeSequence } from './steps/common/refuge/sequence'; import { AohSequence } from './steps/common/safety-concerns/sequence'; import CaseDetailsGetController from './steps/common/task-list/controllers/CaseDetailsGetController'; import TaskListGetController from './steps/common/task-list/controllers/TaskListGetController'; @@ -110,6 +111,7 @@ export class Routes { ...stepsWithContent, ...getStepsWithContent(AohSequence.getSequence(), '/common'), ...getStepsWithContent(await RAProvider.getSequence(), '/common'), + ...getStepsWithContent(C8RefugeSequence.getSequence(), '/common'), ]; for (const step of steps) { diff --git a/src/main/steps/applicant/confirm-contact-details/checkanswers/content.test.ts b/src/main/steps/applicant/confirm-contact-details/checkanswers/content.test.ts index ccba0f8cd7..6853e1df2a 100644 --- a/src/main/steps/applicant/confirm-contact-details/checkanswers/content.test.ts +++ b/src/main/steps/applicant/confirm-contact-details/checkanswers/content.test.ts @@ -13,6 +13,8 @@ const en = { citizenUserFullName: 'Name', citizenUserDateOfBirthText: 'Date of birth', citizenUserPlaceOfBirthText: 'Place of birth', + citizenUserLivingInRefugeText: 'Living in refuge', + refugeDocumentText: 'C8 refuge document', citizenUserAddressText: 'Address', citizenUserAddressHistory: 'Address history', citizenUserPhoneNumberText: 'Phone number', diff --git a/src/main/steps/c100-rebuild/applicant/add-applicants/postController.ts b/src/main/steps/c100-rebuild/applicant/add-applicants/postController.ts index 82487073df..7aa118ff69 100644 --- a/src/main/steps/c100-rebuild/applicant/add-applicants/postController.ts +++ b/src/main/steps/c100-rebuild/applicant/add-applicants/postController.ts @@ -168,12 +168,12 @@ export default class AddApplicantPostController extends PostController l.canProvideEmailLabel, - selected: canProvideEmail === YesNoEmpty.YES, - value: YesNoEmpty.YES, + selected: canProvideEmail === YesOrNo.YES, + value: YesOrNo.YES, subFields: { emailAddress: { type: 'text', @@ -147,8 +147,8 @@ export const generateFormFields = ( }, { label: l => l.canNotProvideEmailLabel, - selected: canProvideEmail === YesNoEmpty.NO, - value: YesNoEmpty.NO, + selected: canProvideEmail === YesOrNo.NO, + value: YesOrNo.NO, }, ], validator: isFieldFilledIn, @@ -159,8 +159,8 @@ export const generateFormFields = ( values: [ { label: l => l.canProvideTelephoneNumberLabel, - selected: canProvideTelephoneNumber === YesNoEmpty.YES, - value: YesNoEmpty.YES, + selected: canProvideTelephoneNumber === YesOrNo.YES, + value: YesOrNo.YES, subFields: { telephoneNumber: { type: 'text', @@ -173,8 +173,8 @@ export const generateFormFields = ( }, { label: l => l.canNotProvideTelephoneNumberLabel, - selected: canProvideTelephoneNumber === YesNoEmpty.NO, - value: YesNoEmpty.NO, + selected: canProvideTelephoneNumber === YesOrNo.NO, + value: YesOrNo.NO, subFields: { canNotProvideTelephoneNumberReason: { type: 'text', @@ -197,13 +197,13 @@ export const generateFormFields = ( values: [ { label: l => l.voiceMailYesLabel, - selected: canLeaveVoiceMail === YesNoEmpty.YES, - value: YesNoEmpty.YES, + selected: canLeaveVoiceMail === YesOrNo.YES, + value: YesOrNo.YES, }, { label: l => l.voiceMailNoLabel, - selected: canLeaveVoiceMail === YesNoEmpty.NO, - value: YesNoEmpty.NO, + selected: canLeaveVoiceMail === YesOrNo.NO, + value: YesOrNo.NO, }, ], validator: isFieldFilledIn, diff --git a/src/main/steps/c100-rebuild/applicant/contact-detail/postController.ts b/src/main/steps/c100-rebuild/applicant/contact-detail/postController.ts index e169ddd992..2a666d1f8b 100644 --- a/src/main/steps/c100-rebuild/applicant/contact-detail/postController.ts +++ b/src/main/steps/c100-rebuild/applicant/contact-detail/postController.ts @@ -1,7 +1,7 @@ import autobind from 'autobind-decorator'; import { Response } from 'express'; -import { C100Applicant, YesNoEmpty } from '../../../../app/case/definition'; +import { C100Applicant, YesOrNo } from '../../../../app/case/definition'; import { AppRequest } from '../../../../app/controller/AppRequest'; import { AnyObject, PostController } from '../../../../app/controller/PostController'; import { Form, FormFields, FormFieldsFn } from '../../../../app/form/Form'; @@ -24,12 +24,12 @@ export default class ContactDetailPostController extends PostController i.id === applicantId) as number; req.session.userCase.appl_allApplicants![applicantIndex].applicantContactDetail = { ...req.session.userCase?.appl_allApplicants?.[applicantIndex].applicantContactDetail, - canProvideEmail: req.body['canProvideEmail'] as YesNoEmpty, - emailAddress: formData['canProvideEmail'] === YesNoEmpty.NO ? '' : (req.body['emailAddress'] as string), - canProvideTelephoneNumber: req.body['canProvideTelephoneNumber'] as YesNoEmpty, + canProvideEmail: req.body['canProvideEmail'] as YesOrNo, + emailAddress: formData['canProvideEmail'] === YesOrNo.NO ? '' : (req.body['emailAddress'] as string), + canProvideTelephoneNumber: req.body['canProvideTelephoneNumber'] as YesOrNo, telephoneNumber: req.body['telephoneNumber'] as string, - canNotProvideTelephoneNumberReason: req.body['canNotProvideTelephoneNumberReason'] as YesNoEmpty, - canLeaveVoiceMail: req.body['canLeaveVoiceMail'] as YesNoEmpty, + canNotProvideTelephoneNumberReason: req.body['canNotProvideTelephoneNumberReason'] as YesOrNo, + canLeaveVoiceMail: req.body['canLeaveVoiceMail'] as YesOrNo, }; if (onlycontinue) { req.session.errors = form.getErrors(formData); diff --git a/src/main/steps/c100-rebuild/applicant/navigationController.test.ts b/src/main/steps/c100-rebuild/applicant/navigationController.test.ts index ce3328bba9..6c3732b401 100644 --- a/src/main/steps/c100-rebuild/applicant/navigationController.test.ts +++ b/src/main/steps/c100-rebuild/applicant/navigationController.test.ts @@ -96,7 +96,7 @@ describe('ApplicantDetailsNavigationController', () => { ).toBe('/c100-rebuild/applicant/2cd885a0-135e-45f1-85b7-aa46a1f78f46/contact-preference'); }); - test('From RELATIONSHIP CHILD screen -> navigate to ADDRESS LOOKUP screen', async () => { + test('From RELATIONSHIP CHILD screen -> navigate to refuge screen', async () => { dummyRequest.params.childId = '7483640e-0817-4ddc-b709-6723f7925635'; dummyRequest.params.applicantId = '2cd885a0-135e-45f1-85b7-aa46a1f78f46'; expect( @@ -105,7 +105,7 @@ describe('ApplicantDetailsNavigationController', () => { dummyRequest.session.userCase, dummyRequest.params ) - ).toBe('/c100-rebuild/applicant/2cd885a0-135e-45f1-85b7-aa46a1f78f46/address/lookup'); + ).toBe('/c100-rebuild/refuge/staying-in-refuge/2cd885a0-135e-45f1-85b7-aa46a1f78f46?'); }); test('From CONFIDENTIALITY START ALT -> navigate to FEEDBACK screen', async () => { diff --git a/src/main/steps/c100-rebuild/applicant/navigationController.ts b/src/main/steps/c100-rebuild/applicant/navigationController.ts index c17c2c64ef..5e6565a3ec 100644 --- a/src/main/steps/c100-rebuild/applicant/navigationController.ts +++ b/src/main/steps/c100-rebuild/applicant/navigationController.ts @@ -1,5 +1,5 @@ import { Case } from '../../../app/case/case'; -import { C100Applicant, ChildrenDetails, YesOrNo } from '../../../app/case/definition'; +import { C100Applicant, ChildrenDetails, RootContext, YesOrNo } from '../../../app/case/definition'; import { applyParms } from '../../common/url-parser'; import { C100_APPLICANTS_PERSONAL_DETAILS, @@ -16,6 +16,7 @@ import { C100_APPLICANT_RELATIONSHIP_TO_CHILD, C100_RESPONDENT_DETAILS_ADD, PageLink, + STAYING_IN_REFUGE, } from '../../urls'; import { getPartyDetails } from '../people/util'; @@ -94,8 +95,9 @@ class ApplicantNavigationController { applicantId: this.applicantId, childId: nextChild?.id, }) - : applyParms(C100_APPLICANT_ADDRESS_LOOKUP, { - applicantId: this.applicantId, + : applyParms(STAYING_IN_REFUGE, { + root: RootContext.C100_REBUILD, + id: this.applicantId, }); break; } diff --git a/src/main/steps/c100-rebuild/c100sequence.test.ts b/src/main/steps/c100-rebuild/c100sequence.test.ts index 7aafaa57e2..cd9b24ee32 100644 --- a/src/main/steps/c100-rebuild/c100sequence.test.ts +++ b/src/main/steps/c100-rebuild/c100sequence.test.ts @@ -525,7 +525,7 @@ describe('C100Sequence', () => { ); expect(C100Sequence[91].showInSection).toBe('c100'); expect(C100Sequence[91].getNextStep(otherPersonMockData.session.userCase, otherPersonMockData)).toBe( - '/c100-rebuild/other-person-details/7228444b-ef3f-4202-a1e7-cdcd2316e1f6/address/lookup' + '/c100-rebuild/refuge/staying-in-refuge/7228444b-ef3f-4202-a1e7-cdcd2316e1f6?' ); expect(C100Sequence[92].url).toBe('/c100-rebuild/child-details/:childId/live-with/mainly-live-with'); @@ -569,5 +569,19 @@ describe('C100Sequence', () => { expect(C100Sequence[99].url).toBe('/c100-rebuild/check-your-answers'); expect(C100Sequence[99].showInSection).toBe('c100'); expect(C100Sequence[99].getNextStep({})).toBe('/c100-rebuild/check-your-answers'); + + expect(C100Sequence[100].url).toBe('/c100-rebuild/applicant/:applicantId/contact-preference'); + expect(C100Sequence[100].showInSection).toBe('c100'); + expect(C100Sequence[100].getNextStep(applicantMockRequest.session.userCase, applicantMockRequest)).toBe( + '/c100-rebuild/applicant/2cd885a0-135e-45f1-85b7-aa46a1f78f46/confidentiality/details-know' + ); + + expect(C100Sequence[101].url).toBe('/c100-rebuild/:caseId/withdraw'); + expect(C100Sequence[101].showInSection).toBe('c100'); + expect(C100Sequence[101].getNextStep({})).toBe('/task-list/applicant'); + + expect(C100Sequence[102].url).toBe('/c100-rebuild/withdraw/confirmation'); + expect(C100Sequence[102].showInSection).toBe('c100'); + expect(C100Sequence[102].getNextStep({})).toBe('/'); }); }); diff --git a/src/main/steps/c100-rebuild/check-your-answers/CheckYourAnswersGetController.test.ts b/src/main/steps/c100-rebuild/check-your-answers/CheckYourAnswersGetController.test.ts index b926faf89b..7aa07c8613 100644 --- a/src/main/steps/c100-rebuild/check-your-answers/CheckYourAnswersGetController.test.ts +++ b/src/main/steps/c100-rebuild/check-your-answers/CheckYourAnswersGetController.test.ts @@ -91,6 +91,33 @@ describe('DocumentUpload Get Controller', () => { // expect(req.session.paymentError).toStrictEqual({ hasError: true, errorContext: 'defaultPaymentError' }); // }); + test('should save errors when refuge is yes but documents not present', async () => { + req.session.userCase = { + ...req.session.userCase, + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Applicant', + liveInRefuge: 'Yes', + }, + ], + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Other', + liveInRefuge: 'Yes', + }, + ], + }; + req.locals.C100Api.saveC100DraftApplication = jest.fn(); + updateCaserMock.mockResolvedValue(req.session.userCase); + + await controller.get(req, res); + expect(req.session.save).toHaveBeenCalled(); + }); + test('should catch errors and set payment error for successful payment', async () => { req.session.userCase.paymentSuccessDetails = { amount: 'MOCK_AMOUNT', @@ -104,7 +131,7 @@ describe('DocumentUpload Get Controller', () => { payment_group_reference: 'PAYMENT_GROUP_REFERENCE', }; req.session.paymentError = { hasError: false, errorContext: null }; - req.locals.C100Api.updateCase.mockImplementation(() => { + req.locals.C100Api.saveC100DraftApplication.mockImplementation(() => { throw new Error(); }); diff --git a/src/main/steps/c100-rebuild/check-your-answers/CheckYourAnswersGetController.ts b/src/main/steps/c100-rebuild/check-your-answers/CheckYourAnswersGetController.ts index b4b93be095..16704209f4 100644 --- a/src/main/steps/c100-rebuild/check-your-answers/CheckYourAnswersGetController.ts +++ b/src/main/steps/c100-rebuild/check-your-answers/CheckYourAnswersGetController.ts @@ -1,11 +1,14 @@ import autobind from 'autobind-decorator'; import { Response } from 'express'; +import _ from 'lodash'; import { FieldPrefix } from '../../../app/case/case'; import { PaymentErrorContext, PaymentStatus, YesOrNo } from '../../../app/case/definition'; import { AppRequest } from '../../../app/controller/AppRequest'; import { GetController, TranslationFn } from '../../../app/controller/GetController'; +import { isMandatoryFieldsFilled } from './mainUtil'; + @autobind export default class CheckYourAnswersGetController extends GetController { constructor( @@ -35,7 +38,36 @@ export default class CheckYourAnswersGetController extends GetController { req.session.paymentError = { hasError: false, errorContext: null }; req.session.save(); }, 1000); - super.get(req, res); + if (!isMandatoryFieldsFilled(req.session.userCase)) { + req.session.errors = []; + req.session.userCase?.appl_allApplicants?.forEach(applicant => { + if (applicant.liveInRefuge === YesOrNo.YES && _.isEmpty(applicant.refugeConfidentialityC8Form)) { + req.session.errors?.push({ + propertyName: `c8RefugeDocument-applicant-${req.session.userCase?.appl_allApplicants?.indexOf( + applicant + )}`, + errorType: 'required', + }); + } + }); + + req.session.userCase?.oprs_otherPersons?.forEach(otherPerson => { + if (otherPerson.liveInRefuge === YesOrNo.YES && _.isEmpty(otherPerson.refugeConfidentialityC8Form)) { + req.session.errors?.push({ + propertyName: `c8RefugeDocument-otherPerson-${req.session.userCase?.oprs_otherPersons?.indexOf( + otherPerson + )}`, + errorType: 'required', + }); + } + }); + + req.session.save(() => { + super.get(req, res); + }); + } else { + super.get(req, res); + } } catch (error) { req.locals.logger.error('error in update case', error); diff --git a/src/main/steps/c100-rebuild/check-your-answers/common/htmlSelectors.ts b/src/main/steps/c100-rebuild/check-your-answers/common/htmlSelectors.ts index fd16dc6285..f3d5229ec3 100644 --- a/src/main/steps/c100-rebuild/check-your-answers/common/htmlSelectors.ts +++ b/src/main/steps/c100-rebuild/check-your-answers/common/htmlSelectors.ts @@ -22,4 +22,6 @@ export enum HTML { STATEMENT_OF_TRUTH_H2_CLOSE = '', BOLD = '', BOLD_CLOSE = '', + ERROR_MESSAGE_SPAN = '', + SPAN_CLOSE = '', } diff --git a/src/main/steps/c100-rebuild/check-your-answers/content.test.ts b/src/main/steps/c100-rebuild/check-your-answers/content.test.ts index 826c3b944d..f2ddec6982 100644 --- a/src/main/steps/c100-rebuild/check-your-answers/content.test.ts +++ b/src/main/steps/c100-rebuild/check-your-answers/content.test.ts @@ -1,6 +1,6 @@ /* eslint-disable import/no-unresolved */ import { cy as CyMidiationDocument, en as EnMidiationDocument } from '.././miam/mediator-document/content'; -import { Miam_urgency } from '../../../app/case/case'; +import { CaseWithId, Miam_urgency } from '../../../app/case/case'; import { C1ASafteyConcernsAbout, YesOrNo } from '../../../app/case/definition'; import { FormContent, FormFields, FormOptions, LanguageLookup } from '../../../app/form/Form'; import { atLeastOneFieldIsChecked } from '../../../app/form/validation'; @@ -518,6 +518,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/childaddress', text: 'Edit', visuallyHiddenText: 'Where do the children live?', + attributes: {}, }, ], }, @@ -539,6 +540,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you have a written agreement with the other people in the case that you want the court to review?', + attributes: {}, }, ], }, @@ -559,6 +561,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/screening-questions/legal-representation', text: 'Edit', visuallyHiddenText: 'Will you be using a legal representative in these proceedings?', + attributes: {}, }, ], }, @@ -580,6 +583,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Is there any reason that you would need permission from the court to make this application?', + attributes: {}, }, ], }, @@ -605,6 +609,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Are the children involved in any emergency protection, care or supervision proceedings (or have they been)? ', + attributes: {}, }, ], }, @@ -627,6 +632,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: 'Edit', visuallyHiddenText: 'Have the children been involved in a court case?', + attributes: {}, }, ], }, @@ -642,6 +648,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: 'Edit', visuallyHiddenText: 'Have you had a court order made for your protection?', + attributes: {}, }, ], }, @@ -662,6 +669,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/typeoforder/select-courtorder', text: 'Edit', visuallyHiddenText: 'What are you asking the court to do?', + attributes: {}, }, ], }, @@ -678,6 +686,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Describe what you want the court to do regarding the children in this application', + attributes: {}, }, ], }, @@ -698,6 +707,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/hearing-urgency/urgent', text: 'Edit', visuallyHiddenText: 'Does your situation qualify for an urgent first hearing?', + attributes: {}, }, ], }, @@ -713,6 +723,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/hearing-without-notice/hearing-part1', text: 'Edit', visuallyHiddenText: 'Are you asking for a without notice hearing?', + attributes: {}, }, ], }, @@ -741,6 +752,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/child-details/further-information', text: 'Edit', visuallyHiddenText: 'Are any of the children known to social services?', + attributes: {}, }, ], }, @@ -758,6 +770,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/child-details/further-information', text: 'Edit', visuallyHiddenText: 'Are any of the children the subject of a child protection plan?', + attributes: {}, }, ], }, @@ -779,6 +792,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you or any respondents have other children who are not part of this application?', + attributes: {}, }, ], }, @@ -807,6 +821,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-person-details/other-person-check', text: 'Edit', visuallyHiddenText: 'Is there anyone else who should know about your application?', + attributes: {}, }, ], }, @@ -832,6 +847,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/safety-concerns/concerns-for-safety', text: 'Edit', visuallyHiddenText: 'Do you have any concerns for your safety or the safety of the children?', + attributes: {}, }, ], }, @@ -849,6 +865,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/safety-concerns/concern-about', text: 'Edit', visuallyHiddenText: 'Who are you concerned about?', + attributes: {}, }, ], }, @@ -872,6 +889,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'What type of behaviour have the children experienced or are at risk of experiencing?', + attributes: {}, }, ], }, @@ -895,6 +913,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'What type of behaviour have the children experienced or are at risk of experiencing?', + attributes: {}, }, ], }, @@ -917,6 +936,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/safety-concerns/other-concerns/drugs', text: 'Edit', visuallyHiddenText: 'Have the children been impacted by drug, alcohol or substance abuse?', + attributes: {}, }, ], }, @@ -932,6 +952,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/safety-concerns/other-concerns/other-issues', text: 'Edit', visuallyHiddenText: 'Do you have any other concerns about the children’s safety and wellbeing?', + attributes: {}, }, ], }, @@ -947,6 +968,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/safety-concerns/orders-required/court-action', text: 'Edit', visuallyHiddenText: 'What do you want the court to do to keep you and the children safe?', + attributes: {}, }, ], }, @@ -963,6 +985,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you agree to the children spending time with the other people in this application?', + attributes: {}, }, ], }, @@ -979,6 +1002,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you agree to the other people in this application being in touch with the children in other ways?', + attributes: {}, }, ], }, @@ -999,6 +1023,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/international-elements/start', text: 'Edit', visuallyHiddenText: "Are the children's lives mainly based outside of England and Wales?", + attributes: {}, }, ], }, @@ -1017,6 +1042,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: "Are the children's parents (or anyone significant to the children) mainly based outside of England and Wales?", + attributes: {}, }, ], }, @@ -1035,6 +1061,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Could another person in the application apply for a similar order in a country outside England or Wales?', + attributes: {}, }, ], }, @@ -1053,6 +1080,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Has another country asked (or been asked) for information or help for the children?', + attributes: {}, }, ], }, @@ -1075,6 +1103,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/attending-court', text: 'Edit', visuallyHiddenText: 'Would you be able to take part in hearings by video and phone?', + attributes: {}, }, ], }, @@ -1092,6 +1121,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/language-requirements', text: 'Edit', visuallyHiddenText: 'Do you have any language requirements?', + attributes: {}, }, ], }, @@ -1109,6 +1139,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/special-arrangements', text: 'Edit', visuallyHiddenText: 'Do you or the children need special arrangements at court?', + attributes: {}, }, ], }, @@ -1127,6 +1158,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you have a physical, mental or learning disability or health condition that means you need support during your case?', + attributes: {}, }, ], }, @@ -1149,6 +1181,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/help-with-fees/need-help-with-fees', text: 'Edit', visuallyHiddenText: 'Do you need help with paying the fee for this application?', + attributes: {}, }, ], }, @@ -2088,7 +2121,12 @@ describe('Content.ts toggle test cases', () => { { actions: { items: [ - { href: '/c100-rebuild/childaddress', text: ' Golygu', visuallyHiddenText: "Ble mae'r plant yn byw?" }, + { + href: '/c100-rebuild/childaddress', + text: ' Golygu', + visuallyHiddenText: "Ble mae'r plant yn byw?", + attributes: {}, + }, ], }, key: { text: "Ble mae'r plant yn byw?" }, @@ -2107,6 +2145,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: "A oes gennych chi gytundeb ysgrifenedig gyda'r bobl eraill yn yr achos, yr ydych am i'r llys ei adolygu?", + attributes: {}, }, ], }, @@ -2127,6 +2166,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/typeoforder/select-courtorder', text: ' Golygu', visuallyHiddenText: "Beth ydych chi'n gofyn i'r llys ei wneud?", + attributes: {}, }, ], }, @@ -2141,6 +2181,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'Disgrifiwch yr hyn rydych chi eisiau i’r llys ei wneud o ran y plant yn y cais hwn', + attributes: {}, }, ], }, @@ -2159,6 +2200,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/hearing-urgency/urgent', text: ' Golygu', visuallyHiddenText: 'Ydy eich sefyllfa’n gymwys i gael gwrandawiad cyntaf brys?', + attributes: {}, }, ], }, @@ -2172,6 +2214,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/hearing-without-notice/hearing-part1', text: ' Golygu', visuallyHiddenText: ' Ydych chi’n gofyn am wrandawiad heb rybudd?', + attributes: {}, }, ], }, @@ -2192,6 +2235,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/child-details/further-information', text: ' Golygu', visuallyHiddenText: 'A yw gwasanaethau cymdeithasol yn gyfarwydd ag unrhyw un o’r plant?', + attributes: {}, }, ], }, @@ -2205,6 +2249,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/child-details/further-information', text: ' Golygu', visuallyHiddenText: 'A yw unrhyw un o’r plant yn destun cynllun amddiffyn plentyn?', + attributes: {}, }, ], }, @@ -2224,6 +2269,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'A oes gennych chi neu unrhyw atebwyr blant eraill nad ydynt yn rhan o’r cais hwn?', + attributes: {}, }, ], }, @@ -2244,6 +2290,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-person-details/other-person-check', text: ' Golygu', visuallyHiddenText: 'A oes unrhyw un arall y dylai wybod am eich cais?', + attributes: {}, }, ], }, @@ -2264,6 +2311,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: ' Golygu', visuallyHiddenText: 'Ydy’r plant wedi bod ynghlwm ag achos llys?', + attributes: {}, }, ], }, @@ -2277,6 +2325,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: ' Golygu', visuallyHiddenText: 'A oes gorchymyn llys wedi ei wneud ar eich cyfer er mwyn eich diogelu chi?', + attributes: {}, }, ], }, @@ -2295,6 +2344,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/safety-concerns/concerns-for-safety', text: ' Golygu', visuallyHiddenText: 'A oes gennych chi unrhyw bryderon am eich diogelwch chi neu ddiogelwch y plant?', + attributes: {}, }, ], }, @@ -2313,6 +2363,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/international-elements/start', text: ' Golygu', visuallyHiddenText: 'A yw’r plant yn byw yn bennaf y tu allan i Gymru a Lloegr?', + attributes: {}, }, ], }, @@ -2327,6 +2378,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: "A yw rhieni’r plant (neu unrhyw un arwyddocaol i'r plant) wedi eu lleoli yn bennaf y tu allan i Gymru a Lloegr?", + attributes: {}, }, ], }, @@ -2343,6 +2395,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'A allai unigolyn arall yn y cais wneud cais am orchymyn tebyg mewn gwlad y tu allan i Gymru neu Loegr? ', + attributes: {}, }, ], }, @@ -2359,6 +2412,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: "A oes gwlad arall wedi gofyn (neu a ofynnwyd i wlad arall) am wybodaeth neu help i'r plant?", + attributes: {}, }, ], }, @@ -2379,6 +2433,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/attending-court', text: ' Golygu', visuallyHiddenText: 'A fyddech chi’n gallu cymryd rhan mewn gwrandawiadau drwy fideo a dros y ffôn?', + attributes: {}, }, ], }, @@ -2392,6 +2447,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/language-requirements', text: ' Golygu', visuallyHiddenText: 'A oes gennych chi unrhyw ofynion ieithyddol?', + attributes: {}, }, ], }, @@ -2405,6 +2461,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/special-arrangements', text: ' Golygu', visuallyHiddenText: "Ydych chi neu'r plant angen trefniadau arbennig yn y llys?", + attributes: {}, }, ], }, @@ -2419,6 +2476,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'A oes gennych anabledd corfforol, meddyliol neu addysgol neu gyflwr iechyd sy’n golygu bod angen cymorth arnoch yn ystod eich achos?', + attributes: {}, }, ], }, @@ -2439,6 +2497,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/help-with-fees/need-help-with-fees', text: ' Golygu', visuallyHiddenText: 'A ydych angen help i dalu’r ffi am wneud y cais hwn?', + attributes: {}, }, ], }, @@ -2466,7 +2525,12 @@ describe('Content.ts toggle test cases', () => { { actions: { items: [ - { href: '/c100-rebuild/childaddress', text: ' Golygu', visuallyHiddenText: "Ble mae'r plant yn byw?" }, + { + href: '/c100-rebuild/childaddress', + text: ' Golygu', + visuallyHiddenText: "Ble mae'r plant yn byw?", + attributes: {}, + }, ], }, key: { text: "Ble mae'r plant yn byw?" }, @@ -2485,6 +2549,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: "A oes gennych chi gytundeb ysgrifenedig gyda'r bobl eraill yn yr achos, yr ydych am i'r llys ei adolygu?", + attributes: {}, }, ], }, @@ -2505,6 +2570,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/screening-questions/legal-representation', text: ' Golygu', visuallyHiddenText: 'A fyddwch yn defnyddio cynrychiolydd cyfreithiol yn yr achos hwn?', + attributes: {}, }, ], }, @@ -2524,6 +2590,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'A oes yna unrhyw reswm pam y byddech angen caniatâd gan y llys i wneud y cais hwn?', + attributes: {}, }, ], }, @@ -2544,6 +2611,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'A yw’r plant ynghlwm ag unrhyw achos diogelu, gofal neu oruchwyliaeth brys (neu a fuont ynghlwm ag achosion o’r fath)?', + attributes: {}, }, ], }, @@ -2564,6 +2632,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: ' Golygu', visuallyHiddenText: 'Ydy’r plant wedi bod ynghlwm ag achos llys?', + attributes: {}, }, ], }, @@ -2577,6 +2646,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: ' Golygu', visuallyHiddenText: 'A oes gorchymyn llys wedi ei wneud ar eich cyfer er mwyn eich diogelu chi?', + attributes: {}, }, ], }, @@ -2595,6 +2665,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/typeoforder/select-courtorder', text: ' Golygu', visuallyHiddenText: "Beth ydych chi'n gofyn i'r llys ei wneud?", + attributes: {}, }, ], }, @@ -2609,6 +2680,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'Disgrifiwch yr hyn rydych chi eisiau i’r llys ei wneud o ran y plant yn y cais hwn', + attributes: {}, }, ], }, @@ -2627,6 +2699,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/hearing-urgency/urgent', text: ' Golygu', visuallyHiddenText: 'Ydy eich sefyllfa’n gymwys i gael gwrandawiad cyntaf brys?', + attributes: {}, }, ], }, @@ -2640,6 +2713,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/hearing-without-notice/hearing-part1', text: ' Golygu', visuallyHiddenText: ' Ydych chi’n gofyn am wrandawiad heb rybudd?', + attributes: {}, }, ], }, @@ -2660,6 +2734,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/child-details/further-information', text: ' Golygu', visuallyHiddenText: 'A yw gwasanaethau cymdeithasol yn gyfarwydd ag unrhyw un o’r plant?', + attributes: {}, }, ], }, @@ -2673,6 +2748,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/child-details/further-information', text: ' Golygu', visuallyHiddenText: 'A yw unrhyw un o’r plant yn destun cynllun amddiffyn plentyn?', + attributes: {}, }, ], }, @@ -2692,6 +2768,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'A oes gennych chi neu unrhyw atebwyr blant eraill nad ydynt yn rhan o’r cais hwn?', + attributes: {}, }, ], }, @@ -2712,6 +2789,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-person-details/other-person-check', text: ' Golygu', visuallyHiddenText: 'A oes unrhyw un arall y dylai wybod am eich cais?', + attributes: {}, }, ], }, @@ -2732,6 +2810,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/safety-concerns/concerns-for-safety', text: ' Golygu', visuallyHiddenText: 'A oes gennych chi unrhyw bryderon am eich diogelwch chi neu ddiogelwch y plant?', + attributes: {}, }, ], }, @@ -2750,6 +2829,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/international-elements/start', text: ' Golygu', visuallyHiddenText: 'A yw’r plant yn byw yn bennaf y tu allan i Gymru a Lloegr?', + attributes: {}, }, ], }, @@ -2764,6 +2844,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: "A yw rhieni’r plant (neu unrhyw un arwyddocaol i'r plant) wedi eu lleoli yn bennaf y tu allan i Gymru a Lloegr?", + attributes: {}, }, ], }, @@ -2780,6 +2861,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'A allai unigolyn arall yn y cais wneud cais am orchymyn tebyg mewn gwlad y tu allan i Gymru neu Loegr? ', + attributes: {}, }, ], }, @@ -2796,6 +2878,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: "A oes gwlad arall wedi gofyn (neu a ofynnwyd i wlad arall) am wybodaeth neu help i'r plant?", + attributes: {}, }, ], }, @@ -2816,6 +2899,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/attending-court', text: ' Golygu', visuallyHiddenText: 'A fyddech chi’n gallu cymryd rhan mewn gwrandawiadau drwy fideo a dros y ffôn?', + attributes: {}, }, ], }, @@ -2829,6 +2913,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/language-requirements', text: ' Golygu', visuallyHiddenText: 'A oes gennych chi unrhyw ofynion ieithyddol?', + attributes: {}, }, ], }, @@ -2842,6 +2927,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/special-arrangements', text: ' Golygu', visuallyHiddenText: "Ydych chi neu'r plant angen trefniadau arbennig yn y llys?", + attributes: {}, }, ], }, @@ -2856,6 +2942,7 @@ describe('Content.ts toggle test cases', () => { text: ' Golygu', visuallyHiddenText: 'A oes gennych anabledd corfforol, meddyliol neu addysgol neu gyflwr iechyd sy’n golygu bod angen cymorth arnoch yn ystod eich achos?', + attributes: {}, }, ], }, @@ -2876,6 +2963,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/help-with-fees/need-help-with-fees', text: ' Golygu', visuallyHiddenText: 'A ydych angen help i dalu’r ffi am wneud y cais hwn?', + attributes: {}, }, ], }, @@ -3365,6 +3453,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/childaddress', text: 'Edit', visuallyHiddenText: 'Where do the children live?', + attributes: {}, }, ], }, @@ -3386,6 +3475,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you have a written agreement with the other people in the case that you want the court to review?', + attributes: {}, }, ], }, @@ -3406,6 +3496,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/screening-questions/legal-representation', text: 'Edit', visuallyHiddenText: 'Will you be using a legal representative in these proceedings?', + attributes: {}, }, ], }, @@ -3427,6 +3518,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Is there any reason that you would need permission from the court to make this application?', + attributes: {}, }, ], }, @@ -3452,6 +3544,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Are the children involved in any emergency protection, care or supervision proceedings (or have they been)? ', + attributes: {}, }, ], }, @@ -3469,6 +3562,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/miam/attendance', text: 'Edit', visuallyHiddenText: 'Have you attended a MIAM?', + attributes: {}, }, ], }, @@ -3486,6 +3580,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/miam/mediator-document', text: 'Edit', visuallyHiddenText: 'Do you have a document signed by the mediator? ', + attributes: {}, }, ], }, @@ -3506,6 +3601,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: 'Edit', visuallyHiddenText: 'Have the children been involved in a court case?', + attributes: {}, }, ], }, @@ -3521,6 +3617,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: 'Edit', visuallyHiddenText: 'Have you had a court order made for your protection?', + attributes: {}, }, ], }, @@ -3541,6 +3638,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/typeoforder/select-courtorder', text: 'Edit', visuallyHiddenText: 'What are you asking the court to do?', + attributes: {}, }, ], }, @@ -3557,6 +3655,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Describe what you want the court to do regarding the children in this application', + attributes: {}, }, ], }, @@ -3577,6 +3676,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/hearing-urgency/urgent', text: 'Edit', visuallyHiddenText: 'Does your situation qualify for an urgent first hearing?', + attributes: {}, }, ], }, @@ -3592,6 +3692,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/hearing-without-notice/hearing-part1', text: 'Edit', visuallyHiddenText: 'Are you asking for a without notice hearing?', + attributes: {}, }, ], }, @@ -3620,6 +3721,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/child-details/further-information', text: 'Edit', visuallyHiddenText: 'Are any of the children known to social services?', + attributes: {}, }, ], }, @@ -3637,6 +3739,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/child-details/further-information', text: 'Edit', visuallyHiddenText: 'Are any of the children the subject of a child protection plan?', + attributes: {}, }, ], }, @@ -3658,6 +3761,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you or any respondents have other children who are not part of this application?', + attributes: {}, }, ], }, @@ -3686,6 +3790,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/other-person-details/other-person-check', text: 'Edit', visuallyHiddenText: 'Is there anyone else who should know about your application?', + attributes: {}, }, ], }, @@ -3711,6 +3816,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/safety-concerns/concerns-for-safety', text: 'Edit', visuallyHiddenText: 'Do you have any concerns for your safety or the safety of the children?', + attributes: {}, }, ], }, @@ -3731,6 +3837,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/international-elements/start', text: 'Edit', visuallyHiddenText: "Are the children's lives mainly based outside of England and Wales?", + attributes: {}, }, ], }, @@ -3749,6 +3856,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: "Are the children's parents (or anyone significant to the children) mainly based outside of England and Wales?", + attributes: {}, }, ], }, @@ -3767,6 +3875,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Could another person in the application apply for a similar order in a country outside England or Wales?', + attributes: {}, }, ], }, @@ -3785,6 +3894,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Has another country asked (or been asked) for information or help for the children?', + attributes: {}, }, ], }, @@ -3807,6 +3917,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/attending-court', text: 'Edit', visuallyHiddenText: 'Would you be able to take part in hearings by video and phone?', + attributes: {}, }, ], }, @@ -3824,6 +3935,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/language-requirements', text: 'Edit', visuallyHiddenText: 'Do you have any language requirements?', + attributes: {}, }, ], }, @@ -3841,6 +3953,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/reasonable-adjustments/special-arrangements', text: 'Edit', visuallyHiddenText: 'Do you or the children need special arrangements at court?', + attributes: {}, }, ], }, @@ -3859,6 +3972,7 @@ describe('Content.ts toggle test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you have a physical, mental or learning disability or health condition that means you need support during your case?', + attributes: {}, }, ], }, @@ -3881,6 +3995,7 @@ describe('Content.ts toggle test cases', () => { href: '/c100-rebuild/help-with-fees/need-help-with-fees', text: 'Edit', visuallyHiddenText: 'Do you need help with paying the fee for this application?', + attributes: {}, }, ], }, @@ -3963,4 +4078,110 @@ describe('Content.ts toggle test cases', () => { expect((form?.submit?.text as LanguageLookup)(generatedContent)).toBe(enContent.StatementOfTruth.SubmitButton); }); + + test('generateContent should add error error text for c8 refuge document', () => { + const generatedRefugeErrorsContent = generateContent({ + ...commonContent, + language: 'en', + userCase: { + ...commonContent.userCase, + appl_allApplicants: [ + { + id: '00ad391d-60b1-450d-ba05-674809fee4e5', + applicantFirstName: 'Test', + applicantLastName: 'Applicant', + detailsKnown: 'Yes', + startAlternative: 'No', + start: '', + contactDetailsPrivate: ['Address'], + contactDetailsPrivateAlternative: [], + relationshipDetails: { + relationshipToChildren: [ + { + childId: '39bc0ed2-503e-4d6e-a957-b57e8f35bc70', + relationshipType: 'Other', + otherRelationshipTypeDetails: 'test', + }, + ], + }, + personalDetails: { + haveYouChangeName: 'Yes', + applPreviousName: 'sasdasd', + dateOfBirth: { + year: '1999', + month: '11', + day: '11', + }, + gender: 'Other', + otherGenderDetails: 'Test', + applicantPlaceOfBirth: 'okdsdsd', + }, + applicantContactDetail: { + canProvideEmail: 'No', + emailAddress: '', + telephoneNumber: '+447205308786', + canNotProvideTelephoneNumberReason: '', + canLeaveVoiceMail: 'Yes', + canProvideTelephoneNumber: 'Yes', + }, + applicantAddressPostcode: '', + applicantAddress1: 'dasdas', + applicantAddress2: '', + applicantAddressTown: 'dada', + applicantAddressCounty: '', + applicantAddressHistory: 'Yes', + applicantProvideDetailsOfPreviousAddresses: '', + country: 'United Kingdom', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: null, + }, + ], + oprs_otherPersons: [ + { + id: '3b32bc4f-7417-443b-ba94-5eacfcee04c4', + firstName: 'Respondent', + lastName: 'FirstPage', + personalDetails: { + dateOfBirth: { + year: '1999', + month: '01', + day: '11', + }, + isDateOfBirthUnknown: 'No', + approxDateOfBirth: { + year: '1999', + month: '01', + day: '11', + }, + }, + relationshipDetails: { + relationshipToChildren: [ + { + childId: '39bc0ed2-503e-4d6e-a957-b57e8f35bc70', + relationshipType: 'Grandparent', + otherRelationshipTypeDetails: '', + }, + ], + }, + liveInRefuge: 'Yes', + addressUnknown: 'Yes', + cd_children: [ + { + id: '39bc0ed2-503e-4d6e-a957-b57e8f35bc70', + firstName: 'Nir', + lastName: 'Sin', + }, + ], + }, + ], + } as unknown as CaseWithId, + }); + + expect(generatedRefugeErrorsContent.errors?.['c8RefugeDocument-applicant-0']).toStrictEqual({ + required: 'You must upload a C8 document', + }); + expect(generatedRefugeErrorsContent.errors?.['c8RefugeDocument-otherPerson-0']).toStrictEqual({ + required: 'You must upload a C8 document', + }); + }); }); diff --git a/src/main/steps/c100-rebuild/check-your-answers/content.ts b/src/main/steps/c100-rebuild/check-your-answers/content.ts index 104246300c..b02ab7f09c 100644 --- a/src/main/steps/c100-rebuild/check-your-answers/content.ts +++ b/src/main/steps/c100-rebuild/check-your-answers/content.ts @@ -37,6 +37,7 @@ import { TypeOfApplication, TypeOfOrder, WithoutNoticeHearing, + isMandatoryFieldsFilled, reasonableAdjustment, whereDoChildrenLive, } from './mainUtil'; @@ -88,6 +89,7 @@ export const enContent = { telephone_number: 'Telephone number', dont_know_email_address: 'I dont know their email address', dont_know_telephone: 'I dont know their telephone number', + completeSectionError: 'Complete this section', StatementOfTruth: { title: 'Statement of Truth', heading: 'Confirm before you submit the application', @@ -114,6 +116,9 @@ export const enContent = { applicationNotSubmitted: 'Your payment was successful but you need to resubmit your application', paymentUnsuccessful: 'Your payment was unsuccessful. Make the payment again and resubmit your application', }, + refugeDocumentText: { + required: 'You must upload a C8 document', + }, }, sectionTitles: { locationDetails: '[^^sectionNo^^]. Location details', // section 1 @@ -171,6 +176,8 @@ export const enContent = { otherPerson: 'Other person', contactDetailsOf: 'Contact details of [^applicantName^]', addressDetails: 'Address details', + refuge: 'Living in refuge', + c8RefugeDocument: 'C8 refuge document', doNotHaveParentalResponsibility: 'I do not have parental responsibility for the children', courtOrderPrevent: 'There is a court order preventing me from making an application without first getting the permission of the court', @@ -227,6 +234,7 @@ export const cyContent = { email: 'E-bost', Male: 'Gwryw', Female: 'Benyw', + completeSectionError: 'Llenwch yr adran hon', StatementOfTruth: { title: 'Datganiad Gwirionedd', heading: 'Cadarnhau cyn ichi gyflwyno’r cais', @@ -254,6 +262,9 @@ export const cyContent = { paymentUnsuccessful: 'Your payment was unsuccessful. Make the payment again and resubmit your application (welsh)', }, + refugeDocumentText: { + required: 'Mae’n rhaid i chi uwchlwytho dogfen C8', + }, }, sectionTitles: { locationDetails: '[^^sectionNo^^]. Manylion lleoliad', // section 1 @@ -311,6 +322,8 @@ export const cyContent = { otherPerson: 'Rhywun arall', contactDetailsOf: 'Manylion cyswllt [^applicantName^]', addressDetails: 'Manylion cyfeiriad', + refuge: 'Byw mewn lloches', + c8RefugeDocument: 'Dogfen lloches C8', doNotHaveParentalResponsibility: 'Nid oes gennyf gyfrifoldeb rhiant dros y plant', courtOrderPrevent: 'Mae gorchymyn llys sy’n fy rhwystro rhag gwneud cais heb gael caniatâd gan y llys yn gyntaf', anotherReason: 'Rheswm arall', @@ -656,6 +669,7 @@ export const form: FormContent = { }, submit: { text: l => l.onlycontinue, + disabled: false, }, saveAndComeLater: { text: l => l.saveAndComeLater, @@ -730,8 +744,25 @@ export const generateContent: TranslationFn = content => { text: l => l.StatementOfTruth['payAndSubmitButton'], }; } + form.submit.disabled = !isMandatoryFieldsFilled(content.userCase!); + const refugeErrors = {}; + + content.userCase?.appl_allApplicants?.forEach(applicant => { + refugeErrors[`c8RefugeDocument-applicant-${content.userCase?.appl_allApplicants?.indexOf(applicant)}`] = + translations.errors.refugeDocumentText; + }); + + content.userCase?.oprs_otherPersons?.forEach(otherPerson => { + refugeErrors[`c8RefugeDocument-otherPerson-${content.userCase?.oprs_otherPersons?.indexOf(otherPerson)}`] = + translations.errors.refugeDocumentText; + }); + return { ...translations, form, + errors: { + ...translations.errors, + ...refugeErrors, + }, }; }; diff --git a/src/main/steps/c100-rebuild/check-your-answers/lib/lib.ts b/src/main/steps/c100-rebuild/check-your-answers/lib/lib.ts index d077b51230..acfe79a20f 100644 --- a/src/main/steps/c100-rebuild/check-your-answers/lib/lib.ts +++ b/src/main/steps/c100-rebuild/check-your-answers/lib/lib.ts @@ -16,6 +16,9 @@ export interface GovUkNunjucksSummary { href: string; text: string; visuallyHiddenText: string; + attributes?: { + id?: string; + }; } ]; }; @@ -24,6 +27,7 @@ export interface GovUkNunjucksSummary { export interface SummaryListRow { key?: string; + anchorReference?: string; keyHtml?: string; value?: string; valueHtml?: string; @@ -65,6 +69,11 @@ export const getSectionSummaryList = (rows: SummaryListRow[], content: PageConte href: changeUrl, // text: content.change as string, visuallyHiddenText: `${item.key}`, + attributes: item.anchorReference + ? { + id: item.anchorReference, + } + : {}, }, ], }, diff --git a/src/main/steps/c100-rebuild/check-your-answers/mainUtil.test.ts b/src/main/steps/c100-rebuild/check-your-answers/mainUtil.test.ts index 61ce9dfc78..504b3d3ec5 100644 --- a/src/main/steps/c100-rebuild/check-your-answers/mainUtil.test.ts +++ b/src/main/steps/c100-rebuild/check-your-answers/mainUtil.test.ts @@ -26,6 +26,7 @@ import { TypeOfApplication, TypeOfOrder, WithoutNoticeHearing, + areRefugeDocumentsNotPresent, getYesNoTranslation, reasonableAdjustment, whereDoChildrenLive, @@ -96,6 +97,8 @@ const keys = { mediatorConfirmation: 'mediatorConfirmation', midatatorDocumentTitle: 'midatatorDocumentTitle', childInvolvementInSupervision: 'childInvolvementInSupervision', + refuge: 'refuge', + c8RefugeDocument: 'c8RefugeDocument', }; const language = 'en'; const content = { @@ -117,6 +120,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/typeoforder/select-courtorder', text: undefined, visuallyHiddenText: 'whatAreYouAsking', + attributes: {}, }, ], }, @@ -132,6 +136,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/typeoforder/shortstatement', text: undefined, visuallyHiddenText: 'wantingCourtToDo', + attributes: {}, }, ], }, @@ -158,6 +163,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/hearing-urgency/urgent', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -171,6 +177,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/hearing-without-notice/hearing-part1', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -267,6 +274,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/screening-questions/permission', text: undefined, visuallyHiddenText: 'reasonPermissionRequired', + attributes: {}, }, ], }, @@ -282,6 +290,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/screening-questions/permissions-why', text: undefined, visuallyHiddenText: 'whyPermissionRequiredFromCourt', + attributes: {}, }, ], }, @@ -297,6 +306,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/screening-questions/permissions-request', text: undefined, visuallyHiddenText: 'whyCourtGrantSubmittingPermission', + attributes: {}, }, ], }, @@ -427,6 +437,12 @@ describe('test cases for main util', () => { applicantAddressHistory: 'Yes', applicantProvideDetailsOfPreviousAddresses: '', country: 'United Kingdom', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'DUMMY_URL', + document_binary_url: 'DUMMY_BINARY_URL', + document_filename: 'filename.docx', + }, }, ], } as ANYTYPE; @@ -498,6 +514,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/add-children', text: undefined, visuallyHiddenText: 'fullName', + attributes: {}, }, ], }, @@ -515,6 +532,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/personal-details', text: undefined, visuallyHiddenText: 'approxCheckboxLabel', + attributes: {}, }, ], }, @@ -530,6 +548,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/personal-details', text: undefined, visuallyHiddenText: 'approxDobLabel', + attributes: {}, }, ], }, @@ -545,6 +564,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/personal-details', text: undefined, visuallyHiddenText: 'childGenderLabel', + attributes: {}, }, ], }, @@ -562,6 +582,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/child-matters', text: undefined, visuallyHiddenText: 'orderAppliedFor', + attributes: {}, }, ], }, @@ -579,6 +600,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/parental-responsibility', text: undefined, visuallyHiddenText: 'parentalResponsibility', + attributes: {}, }, ], }, @@ -665,6 +687,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/has-other-children', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -686,6 +709,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/other-children/names', text: undefined, visuallyHiddenText: 'fullName', + attributes: {}, }, ], }, @@ -703,6 +727,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/other-children/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/personal-details', text: undefined, visuallyHiddenText: 'dobLabel', + attributes: {}, }, ], }, @@ -720,6 +745,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/other-children/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/personal-details', text: undefined, visuallyHiddenText: 'childGenderLabel', + attributes: {}, }, ], }, @@ -776,6 +802,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/has-other-children', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -797,6 +824,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/other-children/names', text: undefined, visuallyHiddenText: 'fullName', + attributes: {}, }, ], }, @@ -814,6 +842,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/other-children/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/personal-details', text: undefined, visuallyHiddenText: 'approxCheckboxLabel', + attributes: {}, }, ], }, @@ -827,6 +856,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/other-children/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/personal-details', text: undefined, visuallyHiddenText: 'approxDobLabel', + attributes: {}, }, ], }, @@ -844,6 +874,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/other-children/39bc0ed2-503e-4d6e-a957-b57e8f35bc70/personal-details', text: undefined, visuallyHiddenText: 'childGenderLabel', + attributes: {}, }, ], }, @@ -937,6 +968,12 @@ describe('test cases for main util', () => { PostTown: 'postTown', County: 'county', }, + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'DUMMY_URL', + document_binary_url: 'DUMMY_BINARY_URL', + document_filename: 'filename.docx', + }, }, ], cd_children: [ @@ -962,6 +999,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/other-person-details/add-other-persons', text: undefined, visuallyHiddenText: 'fullName', + attributes: {}, }, ], }, @@ -979,6 +1017,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/other-person-details/3b32bc4f-7417-443b-ba94-5eacfcee04c4/personal-details', text: undefined, visuallyHiddenText: 'hasNameChanged', + attributes: {}, }, ], }, @@ -994,6 +1033,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/other-person-details/3b32bc4f-7417-443b-ba94-5eacfcee04c4/personal-details', text: undefined, visuallyHiddenText: 'childGenderLabel', + attributes: {}, }, ], }, @@ -1011,6 +1051,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/other-person-details/3b32bc4f-7417-443b-ba94-5eacfcee04c4/personal-details', text: undefined, visuallyHiddenText: 'dobLabel', + attributes: {}, }, ], }, @@ -1028,6 +1069,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/other-person-details/3b32bc4f-7417-443b-ba94-5eacfcee04c4/relationship-to-child/39bc0ed2-503e-4d6e-a957-b57e8f35bc70', text: undefined, visuallyHiddenText: 'relationshipTo Nir Sin', + attributes: {}, }, ], }, @@ -1038,6 +1080,44 @@ describe('test cases for main util', () => { text: 'Other', }, }, + { + actions: { + items: [ + { + href: '/c100-rebuild/refuge/staying-in-refuge/3b32bc4f-7417-443b-ba94-5eacfcee04c4?', + text: undefined, + visuallyHiddenText: 'refuge', + attributes: {}, + }, + ], + }, + key: { + text: 'refuge', + }, + value: { + text: 'Yes', + }, + }, + { + actions: { + items: [ + { + href: '/c100-rebuild/refuge/upload-refuge-document/3b32bc4f-7417-443b-ba94-5eacfcee04c4', + text: undefined, + visuallyHiddenText: 'c8RefugeDocument', + attributes: { + id: 'c8RefugeDocument-otherPerson-0', + }, + }, + ], + }, + key: { + text: 'c8RefugeDocument', + }, + value: { + html: 'filename.docx', + }, + }, { actions: { items: [ @@ -1045,6 +1125,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/other-person-details/3b32bc4f-7417-443b-ba94-5eacfcee04c4/address/manual', text: undefined, visuallyHiddenText: 'addressDetails', + attributes: {}, }, ], }, @@ -1115,6 +1196,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/7483640e-0817-4ddc-b709-6723f7925474/live-with/mainly-live-with', text: undefined, visuallyHiddenText: 'Who does Bob Silly mainly live with?', + attributes: {}, }, ], }, @@ -1132,6 +1214,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/7483640e-0817-4ddc-b709-6723f7925474/live-with/living-arrangements', text: undefined, visuallyHiddenText: "Bob Silly's living arrangements", + attributes: {}, }, ], }, @@ -1204,6 +1287,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/7483640e-0817-4ddc-b709-6723f7925474/live-with/mainly-live-with', text: undefined, visuallyHiddenText: 'Who does Bob Silly mainly live with?', + attributes: {}, }, ], }, @@ -1221,6 +1305,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/child-details/7483640e-0817-4ddc-b709-6723f7925474/live-with/living-arrangements', text: undefined, visuallyHiddenText: "Bob Silly's living arrangements", + attributes: {}, }, ], }, @@ -1338,6 +1423,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/child/concerns-about', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -1353,6 +1439,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/child/report-abuse/physicalAbuse', text: undefined, visuallyHiddenText: 'detailsOfChildConcern', + attributes: {}, }, ], }, @@ -1368,6 +1455,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/child/report-abuse/psychologicalAbuse', text: undefined, visuallyHiddenText: 'detailsOfChildConcern', + attributes: {}, }, ], }, @@ -1384,6 +1472,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/abduction/child-location', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -1397,6 +1486,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/abduction/child-location', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -1410,6 +1500,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/abduction/passport-office', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -1425,6 +1516,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/abduction/passport-office-notified', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -1438,6 +1530,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/abduction/threats', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -1453,6 +1546,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/abduction/previousabductions', text: undefined, visuallyHiddenText: 'detailsofAbduction', + attributes: {}, }, ], }, @@ -1468,6 +1562,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/abduction/previousabductions', text: undefined, visuallyHiddenText: 'c1A_policeOrInvestigatorInvolved', + attributes: {}, }, ], }, @@ -1502,6 +1597,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/child/concerns-about', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -1589,6 +1685,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/add-respondents', text: undefined, visuallyHiddenText: 'fullName', + attributes: {}, }, ], }, @@ -1606,6 +1703,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/974b73a9-730e-4db0-b703-19ed3eab0342/personal-details', text: undefined, visuallyHiddenText: 'hasNameChanged', + attributes: {}, }, ], }, @@ -1623,6 +1721,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/974b73a9-730e-4db0-b703-19ed3eab0342/personal-details', text: undefined, visuallyHiddenText: 'childGenderLabel', + attributes: {}, }, ], }, @@ -1640,6 +1739,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/974b73a9-730e-4db0-b703-19ed3eab0342/personal-details', text: undefined, visuallyHiddenText: 'approxCheckboxLabel', + attributes: {}, }, ], }, @@ -1657,6 +1757,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/974b73a9-730e-4db0-b703-19ed3eab0342/personal-details', text: undefined, visuallyHiddenText: 'approxDobLabel', + attributes: {}, }, ], }, @@ -1672,6 +1773,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/974b73a9-730e-4db0-b703-19ed3eab0342/personal-details', text: undefined, visuallyHiddenText: 'respondentPlaceOfBirthUnknown', + attributes: {}, }, ], }, @@ -1689,6 +1791,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/974b73a9-730e-4db0-b703-19ed3eab0342/relationship-to-child/39bc0ed2-503e-4d6e-a957-b57e8f35bc70', text: undefined, visuallyHiddenText: 'relationshipTo Nir Sin', + attributes: {}, }, ], }, @@ -1706,6 +1809,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/974b73a9-730e-4db0-b703-19ed3eab0342/address/manual', text: undefined, visuallyHiddenText: 'addressDetails', + attributes: {}, }, ], }, @@ -1723,6 +1827,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/974b73a9-730e-4db0-b703-19ed3eab0342/contact-details', text: undefined, visuallyHiddenText: 'E-mail', + attributes: {}, }, ], }, @@ -1740,6 +1845,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/respondent-details/974b73a9-730e-4db0-b703-19ed3eab0342/contact-details', text: undefined, visuallyHiddenText: 'Telephone number', + attributes: {}, }, ], }, @@ -1822,6 +1928,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/miam/other-proceedings', text: undefined, visuallyHiddenText: 'childInvolvementInSupervision', + attributes: {}, }, ], }, @@ -1839,6 +1946,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/miam/attendance', text: undefined, visuallyHiddenText: 'attendedMiamMidiation', + attributes: {}, }, ], }, @@ -1856,6 +1964,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/miam/valid-reason', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -1915,6 +2024,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: undefined, visuallyHiddenText: 'childrenInvolvedCourtCase', + attributes: {}, }, ], }, @@ -1932,6 +2042,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/other-proceedings/current-previous-proceedings', text: undefined, visuallyHiddenText: 'courtOrderProtection', + attributes: {}, }, ], }, @@ -1947,6 +2058,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/other-proceedings/proceeding-details', text: undefined, visuallyHiddenText: 'optitle', + attributes: {}, }, ], }, @@ -1985,6 +2097,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/other-concerns/drugs', text: undefined, visuallyHiddenText: 'childDrugAbuse', + attributes: {}, }, ], }, @@ -2002,6 +2115,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/other-concerns/other-issues', text: undefined, visuallyHiddenText: 'otherWellBeingIssues', + attributes: {}, }, ], }, @@ -2019,6 +2133,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/orders-required/court-action', text: undefined, visuallyHiddenText: 'doWantCourtToAction', + attributes: {}, }, ], }, @@ -2036,6 +2151,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/orders-required/unsupervised', text: undefined, visuallyHiddenText: 'selectSupervisionAgreementLabel', + attributes: {}, }, ], }, @@ -2051,6 +2167,7 @@ describe('test cases for main util', () => { href: '/c100-rebuild/safety-concerns/orders-required/unsupervised', text: undefined, visuallyHiddenText: 'supervisionAgreementOtherWaysLabel', + attributes: {}, }, ], }, @@ -2069,4 +2186,56 @@ describe('test cases for main util', () => { test('getYesNoTranslation should return correct welsh translation', () => { expect(getYesNoTranslation('cy', 'Yes', 'oesTranslation')).toBe('Oes'); }); + + describe('areRefugeDocumentsNotPresent', () => { + test('should return true if refuge document not present for applicant', () => { + expect(areRefugeDocumentsNotPresent({ appl_allApplicants: [{ liveInRefuge: 'Yes' }] } as CaseWithId)).toBe(true); + }); + + test('should return false if refuge is no for applicant', () => { + expect(areRefugeDocumentsNotPresent({ appl_allApplicants: [{ liveInRefuge: 'No' }] } as CaseWithId)).toBe(false); + }); + + test('should return false if refuge document is present for applicant', () => { + expect( + areRefugeDocumentsNotPresent({ + appl_allApplicants: [ + { + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + } as CaseWithId) + ).toBe(false); + }); + + test('should return true if refuge document not present for other person', () => { + expect(areRefugeDocumentsNotPresent({ oprs_otherPersons: [{ liveInRefuge: 'Yes' }] } as CaseWithId)).toBe(true); + }); + + test('should return false if refuge is no other person', () => { + expect(areRefugeDocumentsNotPresent({ oprs_otherPersons: [{ liveInRefuge: 'No' }] } as CaseWithId)).toBe(false); + }); + + test('should return false if refuge document is present for other person', () => { + expect( + areRefugeDocumentsNotPresent({ + oprs_otherPersons: [ + { + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + } as CaseWithId) + ).toBe(false); + }); + }); }); diff --git a/src/main/steps/c100-rebuild/check-your-answers/mainUtil.ts b/src/main/steps/c100-rebuild/check-your-answers/mainUtil.ts index a9133508b8..d931ae2e2b 100644 --- a/src/main/steps/c100-rebuild/check-your-answers/mainUtil.ts +++ b/src/main/steps/c100-rebuild/check-your-answers/mainUtil.ts @@ -1,6 +1,8 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ /* eslint-disable import/no-unresolved */ +import _ from 'lodash'; + import { CaseWithId } from '../../../app/case/case'; import { C1AAbuseTypes, @@ -424,8 +426,14 @@ export const ApplicantDetails = ( language ): SummaryList | undefined => { const sessionApplicantData = userCase['appl_allApplicants']; - const newApplicantData: { key: string; keyHtml?: string; value: string; valueHtml?: string; changeUrl: string }[] = - []; + const newApplicantData: { + key: string; + anchorReference?: string; + keyHtml?: string; + value: string; + valueHtml?: string; + changeUrl: string; + }[] = []; for (const applicant in sessionApplicantData) { const fullname = sessionApplicantData[applicant]['applicantFirstName'] + @@ -533,15 +541,40 @@ export const ApplicantDetails = ( }); }); - newApplicantData.push( - { - key: keys['addressDetails'], + newApplicantData.push({ + key: keys['refuge'], + value: getYesNoTranslation(language, sessionApplicantData[applicant]['liveInRefuge'], 'ydwTranslation'), + changeUrl: applyParms(Urls.STAYING_IN_REFUGE, { + root: RootContext.C100_REBUILD, + id: sessionApplicantData[applicant]['id'], + }), + }); + + if (sessionApplicantData[applicant]['liveInRefuge'] === YesOrNo.YES) { + newApplicantData.push({ + key: keys['c8RefugeDocument'], + anchorReference: `c8RefugeDocument-applicant-${applicant}`, value: '', - valueHtml: applicantAddressParser(sessionApplicantData[applicant], keys, language), - changeUrl: applyParms(Urls['C100_APPLICANT_ADDRESS_MANUAL'], { - applicantId: sessionApplicantData[applicant]['id'], + valueHtml: !_.isEmpty(sessionApplicantData[applicant]['refugeConfidentialityC8Form']) + ? sessionApplicantData[applicant]['refugeConfidentialityC8Form']?.['document_filename'] + : HTML.ERROR_MESSAGE_SPAN + translation('completeSectionError', language) + HTML.SPAN_CLOSE, + changeUrl: applyParms(Urls.C100_REFUGE_UPLOAD_DOC, { + root: RootContext.C100_REBUILD, + id: sessionApplicantData[applicant]['id'], }), - }, + }); + } + + newApplicantData.push({ + key: keys['addressDetails'], + value: '', + valueHtml: applicantAddressParser(sessionApplicantData[applicant], keys, language), + changeUrl: applyParms(Urls['C100_APPLICANT_ADDRESS_MANUAL'], { + applicantId: sessionApplicantData[applicant]['id'], + }), + }); + + newApplicantData.push( { key: keys['contactDetailsOf'].split('[^applicantName^]').join(` ${fullname} `), value: '', @@ -1264,6 +1297,7 @@ export const OtherPeopleDetails = ( const sessionOtherPeopleData = userCase['oprs_otherPersons']; const newOtherPeopleStorage: { key: string; + anchorReference?: string; keyHtml?: string; value?: string; valueHtml?: string; @@ -1343,6 +1377,30 @@ export const OtherPeopleDetails = ( }); }); + newOtherPeopleStorage.push({ + key: keys['refuge'], + value: getYesNoTranslation(language, sessionOtherPeopleData[respondent]['liveInRefuge'], 'ydwTranslation'), + changeUrl: applyParms(Urls.STAYING_IN_REFUGE, { + root: RootContext.C100_REBUILD, + id: sessionOtherPeopleData[respondent]['id'], + }), + }); + + if (sessionOtherPeopleData[respondent]['liveInRefuge'] === YesOrNo.YES) { + newOtherPeopleStorage.push({ + key: keys['c8RefugeDocument'], + anchorReference: `c8RefugeDocument-otherPerson-${respondent}`, + value: '', + valueHtml: !_.isEmpty(sessionOtherPeopleData[respondent]['refugeConfidentialityC8Form']) + ? sessionOtherPeopleData[respondent]['refugeConfidentialityC8Form']?.['document_filename'] + : HTML.ERROR_MESSAGE_SPAN + translation('completeSectionError', language) + HTML.SPAN_CLOSE, + changeUrl: applyParms(Urls.C100_REFUGE_UPLOAD_DOC, { + root: RootContext.C100_REBUILD, + id: sessionOtherPeopleData[respondent]['id'], + }), + }); + } + if (!sessionOtherPeopleData[respondent].hasOwnProperty('addressUnknown')) { newOtherPeopleStorage.push({ key: keys['addressDetails'], @@ -1603,3 +1661,18 @@ const populateDateOfBirth = ( } return newChildDataStorage; }; + +export const areRefugeDocumentsNotPresent = (caseData: Partial): boolean => { + return !!( + caseData.appl_allApplicants?.find( + applicant => applicant.liveInRefuge === YesOrNo.YES && _.isEmpty(applicant.refugeConfidentialityC8Form) + ) || + caseData.oprs_otherPersons?.find( + otherPerson => otherPerson.liveInRefuge === YesOrNo.YES && _.isEmpty(otherPerson.refugeConfidentialityC8Form) + ) + ); +}; + +export const isMandatoryFieldsFilled = (caseData: Partial): boolean => { + return !areRefugeDocumentsNotPresent(caseData); +}; diff --git a/src/main/steps/c100-rebuild/other-person-details/navigationController.test.ts b/src/main/steps/c100-rebuild/other-person-details/navigationController.test.ts index 6014a16955..795240a71d 100644 --- a/src/main/steps/c100-rebuild/other-person-details/navigationController.test.ts +++ b/src/main/steps/c100-rebuild/other-person-details/navigationController.test.ts @@ -166,7 +166,7 @@ describe('OtherPersonsDetailsNavigationController', () => { dummyRequest.session.userCase, dummyparams.params ) - ).toBe('/c100-rebuild/other-person-details/2732dd53-2e6c-46f9-88cd-08230e735b08/address/lookup'); + ).toBe('/c100-rebuild/refuge/staying-in-refuge/2732dd53-2e6c-46f9-88cd-08230e735b08?'); }); test('From OtherPerson1 address lookup screen -> navigate to other person address select', async () => { diff --git a/src/main/steps/c100-rebuild/other-person-details/navigationController.ts b/src/main/steps/c100-rebuild/other-person-details/navigationController.ts index 773bf6fdb6..cf31f64e49 100644 --- a/src/main/steps/c100-rebuild/other-person-details/navigationController.ts +++ b/src/main/steps/c100-rebuild/other-person-details/navigationController.ts @@ -1,5 +1,5 @@ import { Case } from '../../../app/case/case'; -import { C100RebuildPartyDetails, ChildrenDetails, YesOrNo } from '../../../app/case/definition'; +import { C100RebuildPartyDetails, ChildrenDetails, RootContext, YesOrNo } from '../../../app/case/definition'; import { applyParms } from '../../common/url-parser'; import { C100_CHILDERN_MAINLY_LIVE_WITH, @@ -11,6 +11,7 @@ import { C100_OTHER_PERSON_DETAILS_PERSONAL_DETAILS, C100_OTHER_PERSON_DETAILS_RELATIONSHIP_TO_CHILD, PageLink, + STAYING_IN_REFUGE, } from '../../urls'; import { getNextPerson } from '../people/util'; @@ -57,8 +58,9 @@ class OtherPersonsDetailsNavigationController { otherPersonId: this.otherPersonId, childId: nextChild.id as ChildrenDetails['id'], }) - : applyParms(C100_OTHER_PERSON_DETAILS_ADDRESS_LOOKUP, { - otherPersonId: this.otherPersonId, + : applyParms(STAYING_IN_REFUGE, { + root: RootContext.C100_REBUILD, + id: this.otherPersonId, }); break; } diff --git a/src/main/steps/c100-rebuild/people/util.ts b/src/main/steps/c100-rebuild/people/util.ts index 383d7bfea1..5768224fe2 100644 --- a/src/main/steps/c100-rebuild/people/util.ts +++ b/src/main/steps/c100-rebuild/people/util.ts @@ -13,7 +13,7 @@ import { YesOrNo, } from '../../../app/case/definition'; -type People = ChildrenDetails | OtherChildrenDetails | C100RebuildPartyDetails | C100Applicant; +export type People = ChildrenDetails | OtherChildrenDetails | C100RebuildPartyDetails | C100Applicant; export enum PartyDetailsVariant { PERSONAL_DETAILS = 'personalDetails', diff --git a/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsGetController.ts b/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsGetController.ts index f9dc93fc45..b9023bf958 100644 --- a/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsGetController.ts +++ b/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsGetController.ts @@ -53,11 +53,13 @@ export class ConfirmContactDetailsGetController extends GetController { const fieldsArray: string[] = [ 'citizenUserFullName', + 'citizenUserLivingInRefugeText', 'citizenUserPlaceOfBirthText', 'citizenUserAddressText', 'citizenUserPhoneNumberText', 'citizenUserEmailAddressText', 'citizenUserDateOfBirthText', + 'refugeDocumentText', ]; function setRedirectUrl(req: AppRequest>) { diff --git a/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsPostController.test.ts b/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsPostController.test.ts index 5c44f7358e..1d30a3a017 100644 --- a/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsPostController.test.ts +++ b/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsPostController.test.ts @@ -59,6 +59,7 @@ describe('ConfirmContactDetailsPostController', () => { idamId: '0c09b130-2eba-4ca8-a910-1f001bac01e6', email: 'test@example.net', }, + liveInRefuge: 'No', }, }, ]; @@ -89,6 +90,7 @@ describe('ConfirmContactDetailsPostController', () => { citizenUserPlaceOfBirth: 'london', citizenUserAdditionalName: 'Johnny Smith', citizenUserSafeToCall: '4 pm', + isCitizenLivingInRefuge: 'No', respondents: [ { id: '0c09b130-2eba-4ca8-a910-1f001bac01e6', @@ -151,6 +153,8 @@ describe('ConfirmContactDetailsPostController', () => { placeOfBirth: 'london', previousName: 'Johnny Smith', response: { safeToCallOption: '4 pm' }, + liveInRefuge: 'No', + refugeConfidentialityC8Form: null, }; updated = { @@ -174,6 +178,7 @@ describe('ConfirmContactDetailsPostController', () => { idamId: '0c09b130-2eba-4ca8-a910-1f001bac01e6', email: 'test@example.net', }, + liveInRefuge: 'No', }; retrieveByCaseIdMock.mockResolvedValue(req.session.userCase); @@ -320,4 +325,13 @@ describe('ConfirmContactDetailsPostController', () => { 'ConfirmContactDetailsPostController - error when saving contact details and redirecting' ); }); + + test('Should not update the userCase when refuge is yes and no refuge document present', async () => { + req.session.userCase.isCitizenLivingInRefuge = 'Yes'; + await controller.post(req, res); + expect(req.session.save).not.toBeCalled; + expect(retrieveByCaseIdMock).not.toBeCalled; + expect(updateCaserMock).not.toBeCalled; + expect(res.redirect).toHaveBeenCalledWith('/task-list/applicant'); + }); }); diff --git a/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsPostController.ts b/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsPostController.ts index 648525f1ed..496184c2e3 100644 --- a/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsPostController.ts +++ b/src/main/steps/common/confirm-contact-details/checkanswers/ConfirmContactDetailsPostController.ts @@ -10,7 +10,14 @@ import { prepareContactPreferenceRequest } from '../../../../steps/common/contac import { applyParms } from '../../../../steps/common/url-parser'; import { getCasePartyType } from '../../../../steps/prl-cases/dashboard/utils'; import { getPartyDetails, mapDataInSession } from '../../../../steps/tasklistresponse/utils'; -import { PARTY_TASKLIST, PageLink, RESPOND_TO_APPLICATION, REVIEW_CONTACT_PREFERENCE } from '../../../../steps/urls'; +import { + APPLICANT_CHECK_ANSWERS, + PARTY_TASKLIST, + PageLink, + RESPONDENT_CHECK_ANSWERS, + RESPOND_TO_APPLICATION, + REVIEW_CONTACT_PREFERENCE, +} from '../../../../steps/urls'; import { mapConfirmContactDetails, @@ -18,6 +25,7 @@ import { setAddressFields, //setContactDetails } from './ContactDetailsMapper'; +import { isMandatoryFieldsFilled } from './utils'; @autobind export class ConfirmContactDetailsPostController extends PostController { @@ -79,24 +87,28 @@ export const saveAndRedirectContactDetailsAndPreference = async ( }, }); - try { - req.session.userCase = await client.updateCaseData( - userCase.id, - partyDetails, - partyType, - userCase.caseTypeOfApplication as CaseType, - req.session.applicationSettings?.navFromContactPreferences - ? CaseEvent.CONTACT_PREFERENCE - : CaseEvent.CONFIRM_YOUR_DETAILS - ); - mapDataInSession(req.session.userCase, user.id); - req.session.userCase.citizenUserAddressText = setAddressFields(req).citizenUserAddressText; - req.session.save(() => { - const redirectUrl = getRedirectUrl(partyType, req); - res.redirect(redirectUrl); - }); - } catch (error) { - throw new Error('ConfirmContactDetailsPostController - Case could not be updated.'); + if (!isMandatoryFieldsFilled(userCase)) { + res.redirect(partyType === PartyType.RESPONDENT ? RESPONDENT_CHECK_ANSWERS : APPLICANT_CHECK_ANSWERS); + } else { + try { + req.session.userCase = await client.updateCaseData( + userCase.id, + partyDetails, + partyType, + userCase.caseTypeOfApplication as CaseType, + req.session.applicationSettings?.navFromContactPreferences + ? CaseEvent.CONTACT_PREFERENCE + : CaseEvent.CONFIRM_YOUR_DETAILS + ); + mapDataInSession(req.session.userCase, user.id); + req.session.userCase.citizenUserAddressText = setAddressFields(req).citizenUserAddressText; + req.session.save(() => { + const redirectUrl = getRedirectUrl(partyType, req); + res.redirect(redirectUrl); + }); + } catch (error) { + throw new Error('ConfirmContactDetailsPostController - Case could not be updated.'); + } } } }; diff --git a/src/main/steps/common/confirm-contact-details/checkanswers/ContactDetailsMapper.test.ts b/src/main/steps/common/confirm-contact-details/checkanswers/ContactDetailsMapper.test.ts index b586607d2e..7b7f21e763 100644 --- a/src/main/steps/common/confirm-contact-details/checkanswers/ContactDetailsMapper.test.ts +++ b/src/main/steps/common/confirm-contact-details/checkanswers/ContactDetailsMapper.test.ts @@ -1,9 +1,12 @@ import { mockRequest } from '../../../../../test/unit/utils/mockRequest'; +import { CosApiClient } from '../../../../app/case/CosApiClient'; import { mapConfirmContactDetails, prepareRequest, setTextFields } from './ContactDetailsMapper'; let respondents; +const deleteDocumentMock = jest.spyOn(CosApiClient.prototype, 'deleteDocument'); + describe('ContactDetailsMapper', () => { let req = mockRequest(); beforeEach(() => { @@ -220,6 +223,7 @@ describe('ContactDetailsMapper', () => { citizenUserAddressPostcode: 'SW13ND', isAtAddressLessThan5Years: 'Yes', citizenUserAddressHistory: "Don't want to state", + isCitizenLivingInRefuge: 'Yes', }, }, }); @@ -252,6 +256,7 @@ describe('ContactDetailsMapper', () => { isAtAddressLessThan5Years: 'Yes', citizenUserAddressHistory: "Don't want to state", citizenUserPlaceOfBirthText: 'london', + citizenUserLivingInRefugeText: 'Yes', }) ); }); @@ -304,4 +309,24 @@ describe('ContactDetailsMapper', () => { }) ); }); + + test('Should delete c8 document when isCitizenLivingInRefuge is no', async () => { + req = mockRequest({ + session: { + userCase: { + isCitizenLivingInRefuge: 'No', + refugeDocument: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + }, + }); + req.session.user.id = '0c09b130-2eba-4ca8-a910-1f001bac01e7'; + req.session.userCase = setTextFields(req); + deleteDocumentMock.mockResolvedValue('SUCCESS'); + + expect(req.session.userCase.refugeDocument).toBe(undefined); + }); }); diff --git a/src/main/steps/common/confirm-contact-details/checkanswers/ContactDetailsMapper.ts b/src/main/steps/common/confirm-contact-details/checkanswers/ContactDetailsMapper.ts index 8bff18144c..fee46f71e0 100644 --- a/src/main/steps/common/confirm-contact-details/checkanswers/ContactDetailsMapper.ts +++ b/src/main/steps/common/confirm-contact-details/checkanswers/ContactDetailsMapper.ts @@ -1,4 +1,6 @@ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import _ from 'lodash'; + import { CaseWithId } from '../../../../app/case/case'; import { ContactPreference, PartyDetails, YesOrNo } from '../../../../app/case/definition'; import { fromApiDate } from '../../../../app/case/from-api-format'; @@ -25,6 +27,8 @@ export const prepareRequest = (userCase: CaseWithId): Partial => { citizenUserAddressPostcode, isAtAddressLessThan5Years, citizenUserAddressHistory, + isCitizenLivingInRefuge, + refugeDocument, } = userCase; Object.assign(request, { @@ -47,6 +51,8 @@ export const prepareRequest = (userCase: CaseWithId): Partial => { County: citizenUserAddressCounty, PostCode: citizenUserAddressPostcode, }, + liveInRefuge: isCitizenLivingInRefuge, + refugeConfidentialityC8Form: refugeDocument, }); //data clean up @@ -58,6 +64,10 @@ export const prepareRequest = (userCase: CaseWithId): Partial => { request.addressLivedLessThan5YearsDetails = ''; } + if (isCitizenLivingInRefuge === YesOrNo.NO) { + request.refugeConfidentialityC8Form = null; + } + if (userCase.partyContactPreference) { if (userCase.partyContactPreference === ContactPreference.EMAIL && !request?.email?.trim()) { request.contactPreferences = null; @@ -83,6 +93,8 @@ export const mapConfirmContactDetails = (partyDetails: PartyDetails): Partial => { } else { req.session.userCase.citizenUserEmailAddressText = req.session.userCase.citizenUserEmailAddress; } + if (!req.session.userCase.isCitizenLivingInRefuge) { + req.session.userCase.citizenUserLivingInRefugeText = ''; + } else { + req.session.userCase.citizenUserLivingInRefugeText = req.session.userCase.isCitizenLivingInRefuge; + } + + req.session.userCase.refugeDocumentText = !_.isEmpty(req.session.userCase.refugeDocument) + ? req.session.userCase.refugeDocument.document_filename + : ''; + + if (req.session.userCase.isCitizenLivingInRefuge === YesOrNo.NO) { + delete req.session.userCase.refugeDocument; + delete req.session.userCase.refugeDocumentText; + } + setAddressFields(req); return req.session.userCase; }; diff --git a/src/main/steps/common/confirm-contact-details/checkanswers/content.test.ts b/src/main/steps/common/confirm-contact-details/checkanswers/content.test.ts index fc5fbbaad0..7cc9698b95 100644 --- a/src/main/steps/common/confirm-contact-details/checkanswers/content.test.ts +++ b/src/main/steps/common/confirm-contact-details/checkanswers/content.test.ts @@ -18,6 +18,8 @@ const en = { citizenUserFullName: 'Name', citizenUserDateOfBirthText: 'Date of birth', citizenUserPlaceOfBirthText: 'Place of birth', + citizenUserLivingInRefugeText: 'Living in refuge', + refugeDocumentText: 'C8 refuge document', citizenUserAddressText: 'Address', citizenUserAddressHistory: 'Address history', citizenUserPhoneNumberText: 'Phone number', @@ -41,6 +43,8 @@ const cy: typeof en = { citizenUserFullName: 'Enw', citizenUserDateOfBirthText: 'Dyddiad geni', citizenUserPlaceOfBirthText: 'Lleoliad geni', + citizenUserLivingInRefugeText: 'Byw mewn lloches', + refugeDocumentText: 'Dogfen lloches C8', citizenUserAddressText: 'Cyfeiriad', citizenUserAddressHistory: 'Hanes cyfeiriad', citizenUserPhoneNumberText: 'Rhif ffôn', @@ -81,6 +85,9 @@ describe('address confirmation > content', () => { href: 'addresshistory', text: 'Edit', visuallyHiddenText: 'Address history', + attributes: { + id: 'citizenUserAddressHistory', + }, }, ], }, @@ -112,6 +119,9 @@ describe('address confirmation > content', () => { href: 'addresshistory', text: 'Golygu', visuallyHiddenText: 'Hanes cyfeiriad', + attributes: { + id: 'citizenUserAddressHistory', + }, }, ], }, @@ -131,4 +141,87 @@ describe('address confirmation > content', () => { const form = generatedContent.form as FormContent; expect((form.submit?.text as Function)(enContent)).toBe('Save and continue'); }); + + test('should generate correct summary list when isCitizenLivingInRefuge is Yes', () => { + expect( + generateContent({ + ...commonContent, + userCase: { + isCitizenLivingInRefuge: 'Yes', + citizenUserLivingInRefugeText: 'Yes', + refugeDocumentText: 'MOCK_FILENAME', + refugeDocument: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + language: 'en', + } as unknown as CommonContent).sections + ).toStrictEqual([ + { + rows: [ + { + actions: { + items: [ + { + href: '../refuge/staying-in-refuge', + text: 'Edit', + visuallyHiddenText: 'Living in refuge', + attributes: { + id: 'citizenUserLivingInRefugeText', + }, + }, + ], + }, + key: { + text: 'Living in refuge', + }, + value: { + html: 'Yes', + }, + }, + { + actions: { + items: [ + { + href: '../refuge/upload-refuge-document', + text: 'Edit', + visuallyHiddenText: 'C8 refuge document', + attributes: { + id: 'refugeDocumentText', + }, + }, + ], + }, + key: { + text: 'C8 refuge document', + }, + value: { + html: 'MOCK_FILENAME', + }, + }, + { + actions: { + items: [ + { + href: 'addresshistory', + text: 'Edit', + visuallyHiddenText: 'Address history', + attributes: { + id: 'citizenUserAddressHistory', + }, + }, + ], + }, + key: { + text: 'Address history', + }, + value: {}, + }, + ], + title: '', + }, + ]); + }); }); diff --git a/src/main/steps/common/confirm-contact-details/checkanswers/content.ts b/src/main/steps/common/confirm-contact-details/checkanswers/content.ts index 3ac9b16a9d..305dfd6a10 100644 --- a/src/main/steps/common/confirm-contact-details/checkanswers/content.ts +++ b/src/main/steps/common/confirm-contact-details/checkanswers/content.ts @@ -1,5 +1,5 @@ import { CaseWithId } from '../../../../app/case/case'; -import { CaseType, PartyType } from '../../../../app/case/definition'; +import { CaseType, PartyType, YesOrNo } from '../../../../app/case/definition'; import { UserDetails } from '../../../../app/controller/AppRequest'; import { TranslationFn } from '../../../../app/controller/GetController'; import { FormContent } from '../../../../app/form/Form'; @@ -8,6 +8,8 @@ import { getCasePartyType } from '../../../../steps/prl-cases/dashboard/utils'; import { CommonContent } from '../../../common/common.content'; import { getFormattedDate, summaryList } from '../../../common/summary/utils'; +import { isMandatoryFieldsFilled } from './utils'; + export const enContent = { section: 'Check your details', title: 'Read the information to make sure it is correct, and add any missing details', @@ -22,6 +24,8 @@ export const enContent = { citizenUserFullName: 'Name', citizenUserDateOfBirthText: 'Date of birth', citizenUserPlaceOfBirthText: 'Place of birth', + citizenUserLivingInRefugeText: 'Living in refuge', + refugeDocumentText: 'C8 refuge document', citizenUserAddressText: 'Address', citizenUserAddressHistory: 'Address history', citizenUserPhoneNumberText: 'Phone number', @@ -73,6 +77,8 @@ export const cyContent: typeof enContent = { citizenUserFullName: 'Enw', citizenUserDateOfBirthText: 'Dyddiad geni', citizenUserPlaceOfBirthText: 'Lleoliad geni', + citizenUserLivingInRefugeText: 'Byw mewn lloches', + refugeDocumentText: 'Dogfen lloches C8', citizenUserAddressText: 'Cyfeiriad', citizenUserAddressHistory: 'Hanes cyfeiriad', citizenUserPhoneNumberText: 'Rhif ffôn', @@ -87,6 +93,8 @@ const urls = { citizenUserFullName: 'personaldetails', citizenUserDateOfBirthText: 'personaldetails', citizenUserPlaceOfBirthText: 'personaldetails', + citizenUserLivingInRefugeText: '../refuge/staying-in-refuge', + refugeDocumentText: '../refuge/upload-refuge-document', citizenUserAddressText: 'addressdetails', citizenUserAddressHistory: 'addresshistory', citizenUserPhoneNumberText: 'contactdetails', @@ -124,6 +132,7 @@ export const form: FormContent = { fields: {}, submit: { text: l => l.continue, + disabled: false, }, }; @@ -134,6 +143,7 @@ const languages = { export const generateContent: TranslationFn = content => { const translations = languages[content.language](content); + form.submit!.disabled = !isMandatoryFieldsFilled(content.userCase!); return { ...translations, form, @@ -158,5 +168,9 @@ export const removeFields = ( delete keys.citizenUserSafeToCall; } + if (caseData.isCitizenLivingInRefuge === YesOrNo.NO || caseData.isCitizenLivingInRefuge === null) { + delete keys.refugeDocumentText; + } + return { ...content, keys }; }; diff --git a/src/main/steps/common/confirm-contact-details/checkanswers/utils.test.ts b/src/main/steps/common/confirm-contact-details/checkanswers/utils.test.ts new file mode 100644 index 0000000000..138c663542 --- /dev/null +++ b/src/main/steps/common/confirm-contact-details/checkanswers/utils.test.ts @@ -0,0 +1,28 @@ +import { CaseWithId } from '../../../../app/case/case'; + +import { isMandatoryFieldsFilled } from './utils'; + +describe('confirm contact details -> check answers -> utils', () => { + describe('isMandatoryFieldsFilled', () => { + test('should return false if living in refuge is yes and no document uploaded', () => { + expect(isMandatoryFieldsFilled({ isCitizenLivingInRefuge: 'Yes' } as unknown as CaseWithId)).toBe(false); + }); + + test('should return true if living in refuge is yes and document uploaded', () => { + expect( + isMandatoryFieldsFilled({ + isCitizenLivingInRefuge: 'Yes', + refugeDocument: { + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + document_url: 'MOCK_URL', + }, + } as unknown as CaseWithId) + ).toBe(true); + }); + + test('should return true if living in refuge is no', () => { + expect(isMandatoryFieldsFilled({ isCitizenLivingInRefuge: 'No' } as unknown as CaseWithId)).toBe(true); + }); + }); +}); diff --git a/src/main/steps/common/confirm-contact-details/checkanswers/utils.ts b/src/main/steps/common/confirm-contact-details/checkanswers/utils.ts new file mode 100644 index 0000000000..1ce67eef87 --- /dev/null +++ b/src/main/steps/common/confirm-contact-details/checkanswers/utils.ts @@ -0,0 +1,8 @@ +import _ from 'lodash'; + +import { CaseWithId } from '../../../../app/case/case'; +import { YesOrNo } from '../../../../app/case/definition'; + +export const isMandatoryFieldsFilled = (caseData: Partial): boolean => { + return !(caseData?.isCitizenLivingInRefuge === YesOrNo.YES && _.isEmpty(caseData?.refugeDocument)); +}; diff --git a/src/main/steps/common/contact-preference/choose-a-contact-preference/ReviewContactPreferencePostController.test.ts b/src/main/steps/common/contact-preference/choose-a-contact-preference/ReviewContactPreferencePostController.test.ts index c10f4efd0d..ce35851302 100644 --- a/src/main/steps/common/contact-preference/choose-a-contact-preference/ReviewContactPreferencePostController.test.ts +++ b/src/main/steps/common/contact-preference/choose-a-contact-preference/ReviewContactPreferencePostController.test.ts @@ -51,6 +51,26 @@ describe('ContactPreferencesPostController', () => { req.session.userCase = { ...req.session.userCase, state: 'PREPARE_FOR_HEARING_CONDUCT_HEARING', + citizenUserAddress1: 'Flatc1', + citizenUserAddress2: 'Unkonwn lane', + citizenUserAddressCounty: 'Dummy County', + citizenUserAddressPostcode: 'SW13ND', + citizenUserAddressTown: 'Dummy Town', + citizenUserAddressHistory: '', + citizenUserDateOfBirth: { + year: '2000', + month: '11', + day: '14', + }, + citizenUserEmailAddress: 'a.b@test.com', + citizenUserFirstNames: 'John', + isAtAddressLessThan5Years: 'No', + citizenUserLastNames: 'Smith', + citizenUserPhoneNumber: '0987654321', + citizenUserPlaceOfBirth: 'london', + citizenUserAdditionalName: 'Johnny Smith', + citizenUserSafeToCall: '4 pm', + isCitizenLivingInRefuge: 'No', applicants: [ { id: '0c09b130-2eba-4ca8-a910-1f001bac01e6', @@ -110,6 +130,7 @@ describe('ContactPreferencesPostController', () => { }, response: 'MOCK_RESPONSE', contactPreferences: 'email', + liveInRefuge: 'No', }, }, ], diff --git a/src/main/steps/common/fileupload/macro-v1.njk b/src/main/steps/common/fileupload/macro-v1.njk index 50c34d6f62..f2f88b7b27 100644 --- a/src/main/steps/common/fileupload/macro-v1.njk +++ b/src/main/steps/common/fileupload/macro-v1.njk @@ -5,7 +5,7 @@ {% macro fileUpload(params) %} {% set allowedFileTypes = "image/jpeg,image/tiff,image/png,application/pdf,image/jpg,image/bmp,image/tif,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"%} {% set labelHtml %} -
{{params.labelText}}
+

{{params.labelText}}

{{params.hintText}}
{% set tableRows = [] %} diff --git a/src/main/steps/common/refuge/keeping-details-safe/content.test.ts b/src/main/steps/common/refuge/keeping-details-safe/content.test.ts new file mode 100644 index 0000000000..0af97c5052 --- /dev/null +++ b/src/main/steps/common/refuge/keeping-details-safe/content.test.ts @@ -0,0 +1,135 @@ +import languageAssertions from '../../../../../test/unit/utils/languageAssertions'; +import { CaseWithId } from '../../../../app/case/case'; +import { FormContent, LanguageLookup } from '../../../../app/form/Form'; +import { CommonContent, generatePageContent } from '../../../common/common.content'; + +import { generateContent } from './content'; + +jest.mock('../../../../app/form/validation'); + +const en = { + c100: { + title: "Keeping {name}'s details safe", + understandSafety: + "We understand how important it is to feel safe, and know that {name}'s details will be kept private.", + detailsKeptConfidential: + "{name}'s details will be kept confidential and will only be used by the court, as well as by Cafcass or Cafcass Cymru. They will not be shared with anyone else.", + helpKeepDetailsPrivate: + "To help us to keep {name}'s details safe, do not include their details in any other communications during the case.", + }, + applicantRespondent: { + title: 'Keeping your details safe', + understandSafety: + 'We understand how important it is to feel safe, and know that your details will be kept private.', + detailsKeptConfidential: + 'Your details will be kept confidential and will only be used by the court, as well as by Cafcass or Cafcass Cymru. They will not be shared with anyone else.', + helpKeepDetailsPrivate: + 'To help us to keep your details safe, do not include them in any other communications during the case.', + }, + continue: 'Continue', + cancel: 'Cancel', +}; + +const cy = { + c100: { + title: 'Cadw manylion {name} yn ddiogel', + understandSafety: + 'Rydym yn deall pa mor bwysig yw hi i deimlo’n ddiogel, a gwybod y bydd manylion {name} yn cael eu cadw’n breifat.', + detailsKeptConfidential: + 'Bydd manylion {name} yn cael eu cadw’n gyfrinachol a dim ond y llys yn ogystal â Cafcass neu Cafcass Cymru fydd yn eu defnyddio. Ni fyddant yn cael eu rhannu ag unrhyw un arall.', + helpKeepDetailsPrivate: + 'Er mwyn ein helpu ni i gadw manylion {name} yn ddiogel, peidiwch â chynnwys eu manylion mewn unrhyw gyfathrebiadau eraill yn ystod yr achos.', + }, + applicantRespondent: { + title: 'Cadw eich manylion yn ddiogel', + understandSafety: + 'Rydym yn deall pa mor bwysig yw hi i deimlo’n ddiogel, a gwybod y bydd eich manylion yn cael eu cadw’n breifat.', + detailsKeptConfidential: + 'Bydd eich manylion yn cael eu cadw’n gyfrinachol a dim ond y llys yn ogystal â Cafcass neu Cafcass Cymru fydd yn eu defnyddio. Ni fyddant yn cael eu rhannu ag unrhyw un arall.', + helpKeepDetailsPrivate: + "Er mwyn ein helpu ni i gadw eich manylion yn ddiogel, peidiwch â'u cynnwys mewn unrhyw gyfathrebiadau eraill yn ystod yr achos.", + }, + continue: 'Parhau', + cancel: 'Canslo', +}; + +/* eslint-disable @typescript-eslint/ban-types */ +describe('C8 Refuge > keeping details safe > content', () => { + const commonContent = { + language: 'en', + } as CommonContent; + const additionalData = { + req: { + originalUrl: '/c100-rebuild/', + params: { + root: 'c100-rebuild', + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }, + }; + const userCase = { + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + }, + ], + } as unknown as CaseWithId; + let generatedContent; + let form; + + describe('c100-rebuild journey', () => { + beforeEach(() => { + generatedContent = generateContent({ ...commonContent, additionalData, userCase }); + form = generatedContent.form as FormContent; + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct english content', () => { + languageAssertions('en', en, () => generatedContent); + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct welsh content', () => { + languageAssertions('cy', cy, () => + generateContent({ ...commonContent, additionalData, userCase, language: 'cy' }) + ); + }); + + test('should contain continue button', () => { + expect((form.onlyContinue?.text as Function)(generatedContent)).toBe(en.continue); + }); + + test('should contain saveAndComeLater button', () => { + expect( + (form?.saveAndComeLater?.text as LanguageLookup)( + generatePageContent({ language: 'en', additionalData, userCase }) as Record + ) + ).toBe('Save and come back later'); + }); + }); + + describe('applicant/respondent journey', () => { + beforeEach(() => { + generatedContent = generateContent({ ...commonContent, additionalData: {}, userCase: {} }); + form = generatedContent.form as FormContent; + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct english content', () => { + languageAssertions('en', en, () => generatedContent); + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct welsh content', () => { + languageAssertions('cy', cy, () => + generateContent({ ...commonContent, additionalData: {}, userCase: {}, language: 'cy' }) + ); + }); + + test('should contain continue button', () => { + expect((form.onlyContinue?.text as Function)(generatedContent)).toBe(en.continue); + }); + }); +}); diff --git a/src/main/steps/common/refuge/keeping-details-safe/content.ts b/src/main/steps/common/refuge/keeping-details-safe/content.ts new file mode 100644 index 0000000000..f8156c5f2b --- /dev/null +++ b/src/main/steps/common/refuge/keeping-details-safe/content.ts @@ -0,0 +1,104 @@ +import { PageContent } from '../../../../app/controller/GetController'; +import { FormContent } from '../../../../app/form/Form'; +import { getPeople } from '../../../../steps/c100-rebuild/child-details/live-with/utils'; +import { interpolate } from '../../../../steps/common/string-parser'; +import { C100_URL } from '../../../../steps/urls'; +import { CommonContent } from '../../../common/common.content'; + +const en = { + c100: { + title: "Keeping {name}'s details safe", + understandSafety: + "We understand how important it is to feel safe, and know that {name}'s details will be kept private.", + detailsKeptConfidential: + "{name}'s details will be kept confidential and will only be used by the court, as well as by Cafcass or Cafcass Cymru. They will not be shared with anyone else.", + helpKeepDetailsPrivate: + "To help us to keep {name}'s details safe, do not include their details in any other communications during the case.", + }, + applicantRespondent: { + title: 'Keeping your details safe', + understandSafety: + 'We understand how important it is to feel safe, and know that your details will be kept private.', + detailsKeptConfidential: + 'Your details will be kept confidential and will only be used by the court, as well as by Cafcass or Cafcass Cymru. They will not be shared with anyone else.', + helpKeepDetailsPrivate: + 'To help us to keep your details safe, do not include them in any other communications during the case.', + }, + continue: 'Continue', + cancel: 'Cancel', +}; + +const cy: typeof en = { + c100: { + title: 'Cadw manylion {name} yn ddiogel', + understandSafety: + 'Rydym yn deall pa mor bwysig yw hi i deimlo’n ddiogel, a gwybod y bydd manylion {name} yn cael eu cadw’n breifat.', + detailsKeptConfidential: + 'Bydd manylion {name} yn cael eu cadw’n gyfrinachol a dim ond y llys yn ogystal â Cafcass neu Cafcass Cymru fydd yn eu defnyddio. Ni fyddant yn cael eu rhannu ag unrhyw un arall.', + helpKeepDetailsPrivate: + 'Er mwyn ein helpu ni i gadw manylion {name} yn ddiogel, peidiwch â chynnwys eu manylion mewn unrhyw gyfathrebiadau eraill yn ystod yr achos.', + }, + applicantRespondent: { + title: 'Cadw eich manylion yn ddiogel', + understandSafety: + 'Rydym yn deall pa mor bwysig yw hi i deimlo’n ddiogel, a gwybod y bydd eich manylion yn cael eu cadw’n breifat.', + detailsKeptConfidential: + 'Bydd eich manylion yn cael eu cadw’n gyfrinachol a dim ond y llys yn ogystal â Cafcass neu Cafcass Cymru fydd yn eu defnyddio. Ni fyddant yn cael eu rhannu ag unrhyw un arall.', + helpKeepDetailsPrivate: + "Er mwyn ein helpu ni i gadw eich manylion yn ddiogel, peidiwch â'u cynnwys mewn unrhyw gyfathrebiadau eraill yn ystod yr achos.", + }, + continue: 'Parhau', + cancel: 'Canslo', +}; + +const languages = { + en, + cy, +}; + +export const form: FormContent = { + fields: {}, + onlyContinue: { + text: l => l.continue, + }, +}; + +export const generateContent = (content: CommonContent): PageContent => { + const translations = languages[content.language]; + const c100Person = getPeople(content.userCase!).find(person => person.id === content.additionalData?.req.params.id); + const C100rebuildJourney = content.additionalData?.req?.originalUrl?.startsWith(C100_URL); + + delete form.saveAndComeLater; + if (C100rebuildJourney) { + Object.assign(form, { + saveAndComeLater: { + text: l => l.saveAndComeLater, + }, + }); + } + + return { + ...translations, + title: C100rebuildJourney + ? interpolate(translations.c100.title, { + name: `${c100Person?.firstName} ${c100Person?.lastName}`, + }) + : translations.applicantRespondent.title, + understandSafety: C100rebuildJourney + ? interpolate(translations.c100.understandSafety, { + name: `${c100Person?.firstName} ${c100Person?.lastName}`, + }) + : translations.applicantRespondent.understandSafety, + detailsKeptConfidential: C100rebuildJourney + ? interpolate(translations.c100.detailsKeptConfidential, { + name: `${c100Person?.firstName} ${c100Person?.lastName}`, + }) + : translations.applicantRespondent.detailsKeptConfidential, + helpKeepDetailsPrivate: C100rebuildJourney + ? interpolate(translations.c100.helpKeepDetailsPrivate, { + name: `${c100Person?.firstName} ${c100Person?.lastName}`, + }) + : translations.applicantRespondent.helpKeepDetailsPrivate, + form, + }; +}; diff --git a/src/main/steps/common/refuge/keeping-details-safe/template.njk b/src/main/steps/common/refuge/keeping-details-safe/template.njk new file mode 100644 index 0000000000..6085e38c71 --- /dev/null +++ b/src/main/steps/common/refuge/keeping-details-safe/template.njk @@ -0,0 +1,8 @@ +{% extends "common/template.njk" %} + +{% block page_content %} +

{{title}}

+

{{understandSafety}}

+

{{detailsKeptConfidential}}

+

{{helpKeepDetailsPrivate}}

+{% endblock %} \ No newline at end of file diff --git a/src/main/steps/common/refuge/navigationController.test.ts b/src/main/steps/common/refuge/navigationController.test.ts new file mode 100644 index 0000000000..b94ef0c1bd --- /dev/null +++ b/src/main/steps/common/refuge/navigationController.test.ts @@ -0,0 +1,291 @@ +import { mockRequest } from '../../../../test/unit/utils/mockRequest'; +import { CaseType } from '../../../app/case/definition'; +import { + C100_REFUGE_UPLOAD_DOC, + DASHBOARD_URL, + REFUGE_DOC_ALREADY_UPLOADED, + REFUGE_KEEPING_SAFE, + REFUGE_UPLOAD_DOC, + STAYING_IN_REFUGE, +} from '../../urls'; + +import RefugeNavigationController from './navigationController'; + +describe('C8 refuge > navigationController', () => { + const req = mockRequest({ + params: { + id: '7483640e-0817-4ddc-b709-6723f7925474', + }, + session: { + userCase: { + appl_allApplicants: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + }, + ], + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + }, + ], + }, + }, + }); + + test('should redirect from staying in refuge for C100 applicant when yes selected', async () => { + req.originalUrl = '/c100-rebuild'; + req.session.userCase = { + ...req.session.userCase, + appl_allApplicants: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + }, + ], + }; + expect(RefugeNavigationController.getNextPageUrl(STAYING_IN_REFUGE, req.session.userCase, req)).toBe( + '/c100-rebuild/refuge/keeping-details-safe/7483640e-0817-4ddc-b709-6723f7925474?' + ); + }); + + test('should redirect from staying in refuge for C100 applicant when no selected', async () => { + req.originalUrl = '/c100-rebuild'; + req.session.userCase = { + ...req.session.userCase, + appl_allApplicants: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'No', + }, + ], + }; + expect(RefugeNavigationController.getNextPageUrl(STAYING_IN_REFUGE, req.session.userCase, req)).toBe( + '/c100-rebuild/applicant/7483640e-0817-4ddc-b709-6723f7925474/address/lookup' + ); + }); + + test('should redirect from staying in refuge for applicant/respondent when yes selected', async () => { + req.originalUrl = '/applicant'; + req.session.userCase = { + ...req.session.userCase, + isCitizenLivingInRefuge: 'Yes', + }; + expect(RefugeNavigationController.getNextPageUrl(STAYING_IN_REFUGE, req.session.userCase, req)).toBe( + '/applicant/refuge/keeping-details-safe' + ); + }); + + test('should redirect from staying in refuge for applicant/respondent when no selected', async () => { + req.originalUrl = '/applicant'; + req.session.userCase = { + ...req.session.userCase, + isCitizenLivingInRefuge: 'No', + }; + expect(RefugeNavigationController.getNextPageUrl(STAYING_IN_REFUGE, req.session.userCase, req)).toBe( + '/applicant/confirm-contact-details/addressdetails' + ); + }); + + test('should redirect from keeping safe for C100 applicant when document already uploaded', async () => { + req.originalUrl = '/c100-rebuild'; + req.session.userCase = { + ...req.session.userCase, + appl_allApplicants: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }; + expect(RefugeNavigationController.getNextPageUrl(REFUGE_KEEPING_SAFE, req.session.userCase, req)).toBe( + '/c100-rebuild/refuge/refuge-document-already-uploaded/7483640e-0817-4ddc-b709-6723f7925474?' + ); + }); + + test('should redirect from keeping safe for C100 applicant when document not already uploaded', async () => { + req.originalUrl = '/c100-rebuild'; + req.session.userCase = { + ...req.session.userCase, + appl_allApplicants: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + }, + ], + }; + expect(RefugeNavigationController.getNextPageUrl(REFUGE_KEEPING_SAFE, req.session.userCase, req)).toBe( + '/c100-rebuild/refuge/upload-refuge-document/7483640e-0817-4ddc-b709-6723f7925474' + ); + }); + + test('should redirect from keeping safe for applicant/respondent when document already uploaded', async () => { + req.originalUrl = '/applicant'; + req.session.userCase = { + ...req.session.userCase, + isCitizenLivingInRefuge: 'Yes', + refugeDocument: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }; + expect(RefugeNavigationController.getNextPageUrl(REFUGE_KEEPING_SAFE, req.session.userCase, req)).toBe( + '/applicant/refuge/refuge-document-already-uploaded' + ); + }); + + test('should redirect from keeping safe for applicant/respondent when document not already uploaded', async () => { + req.originalUrl = '/applicant'; + req.session.userCase = { + ...req.session.userCase, + isCitizenLivingInRefuge: 'Yes', + refugeDocument: undefined, + }; + expect(RefugeNavigationController.getNextPageUrl(REFUGE_KEEPING_SAFE, req.session.userCase, req)).toBe( + '/applicant/refuge/upload-refuge-document' + ); + }); + + test('should redirect from upload doc', async () => { + req.originalUrl = '/applicant'; + expect(RefugeNavigationController.getNextPageUrl(REFUGE_UPLOAD_DOC, req.session.userCase, req)).toBe( + '/applicant/confirm-contact-details/addressdetails' + ); + }); + + test('should redirect from c100 upload doc', async () => { + req.originalUrl = '/c100-rebuild'; + expect(RefugeNavigationController.getNextPageUrl(C100_REFUGE_UPLOAD_DOC, req.session.userCase, req)).toBe( + '/c100-rebuild/applicant/7483640e-0817-4ddc-b709-6723f7925474/address/lookup' + ); + }); + + test('should redirect from doument already uploaded for C100 other person when yes selected', async () => { + req.originalUrl = '/c100-rebuild'; + req.session.userCase = { + ...req.session.userCase, + reUploadRefugeDocument: 'Yes', + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }; + req.params = { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }; + expect(RefugeNavigationController.getNextPageUrl(REFUGE_DOC_ALREADY_UPLOADED, req.session.userCase, req)).toBe( + '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58' + ); + }); + + test('should redirect from doument already uploaded for C100 applicant when no selected', async () => { + req.originalUrl = '/c100-rebuild'; + req.session.userCase = { + ...req.session.userCase, + reUploadRefugeDocument: 'No', + appl_allApplicants: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }; + req.params = { + id: '7483640e-0817-4ddc-b709-6723f7925474', + }; + expect(RefugeNavigationController.getNextPageUrl(REFUGE_DOC_ALREADY_UPLOADED, req.session.userCase, req)).toBe( + '/c100-rebuild/applicant/7483640e-0817-4ddc-b709-6723f7925474/address/lookup' + ); + }); + + test('should redirect from doument already uploaded for applicant/respondent when yes selected', async () => { + req.originalUrl = '/applicant'; + req.session.userCase = { + ...req.session.userCase, + reUploadRefugeDocument: 'Yes', + isCitizenLivingInRefuge: 'Yes', + refugeDocument: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }; + expect(RefugeNavigationController.getNextPageUrl(REFUGE_DOC_ALREADY_UPLOADED, req.session.userCase, req)).toBe( + '/applicant/refuge/upload-refuge-document' + ); + }); + + test('should redirect from doument already uploaded for applicant/respondent when no selected', async () => { + req.originalUrl = '/respondent'; + req.session.userCase = { + ...req.session.userCase, + reUploadRefugeDocument: 'No', + isCitizenLivingInRefuge: 'Yes', + refugeDocument: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + caseTypeOfApplication: CaseType.FL401, + caseInvites: [ + { + id: '1234', + value: { + isApplicant: 'No', + invitedUserId: '1234', + }, + }, + ], + respondentsFL401: { + user: { + id: '1234', + idamId: '1234', + }, + }, + }; + req.session.user = { + id: '1234', + idamId: '1234', + }; + expect(RefugeNavigationController.getNextPageUrl(REFUGE_DOC_ALREADY_UPLOADED, req.session.userCase, req)).toBe( + '/respondent/confirm-contact-details/addressdetails' + ); + }); + + test('should return current url by default', () => { + expect(RefugeNavigationController.getNextPageUrl(DASHBOARD_URL, req.session.userCase, req)).toBe('/dashboard'); + }); +}); diff --git a/src/main/steps/common/refuge/navigationController.ts b/src/main/steps/common/refuge/navigationController.ts new file mode 100644 index 0000000000..bc93bfe1ca --- /dev/null +++ b/src/main/steps/common/refuge/navigationController.ts @@ -0,0 +1,139 @@ +import _ from 'lodash'; + +import { CaseWithId } from '../../../app/case/case'; +import { + C100Applicant, + C100RebuildPartyDetails, + PartyType, + People, + RootContext, + YesOrNo, +} from '../../../app/case/definition'; +import { AppRequest } from '../../../app/controller/AppRequest'; +import { getPeople } from '../../../steps/c100-rebuild/child-details/live-with/utils'; +import { getPartyDetails } from '../../../steps/c100-rebuild/people/util'; +import { getCasePartyType } from '../../prl-cases/dashboard/utils'; +import { + APPLICANT_ADDRESS_DETAILS, + C100_APPLICANT_ADDRESS_LOOKUP, + C100_OTHER_PERSON_DETAILS_ADDRESS_LOOKUP, + C100_REFUGE_UPLOAD_DOC, + C100_URL, + PageLink, + REFUGE_DOC_ALREADY_UPLOADED, + REFUGE_KEEPING_SAFE, + REFUGE_UPLOAD_DOC, + RESPONDENT_ADDRESS_DETAILS, + STAYING_IN_REFUGE, +} from '../../urls'; +import { applyParms } from '../url-parser'; + +import { getC8DocumentForC100 } from './utils'; + +class RefugeNavigationController { + public getNextPageUrl(currentPageUrl: PageLink, caseData: Partial, req: AppRequest) { + const partyType = getCasePartyType(caseData, req.session.user.id); + let url; + const id = req.params.id ? req.params.id : req.params.removeFileId; + const C100rebuildJourney = req?.originalUrl?.startsWith(C100_URL); + const c100Person = getPeople(req.session.userCase).find(person => person.id === id); + const partyRootContext = partyType === PartyType.RESPONDENT ? RootContext.RESPONDENT : RootContext.APPLICANT; + + const partyAddressDetails = + partyType === PartyType.RESPONDENT ? RESPONDENT_ADDRESS_DETAILS : APPLICANT_ADDRESS_DETAILS; + const c100AddressDetails = + c100Person?.partyType === PartyType.APPLICANT + ? (applyParms(C100_APPLICANT_ADDRESS_LOOKUP, { applicantId: id }) as PageLink) + : (applyParms(C100_OTHER_PERSON_DETAILS_ADDRESS_LOOKUP, { otherPersonId: id }) as PageLink); + const addressDetails = C100rebuildJourney ? c100AddressDetails : partyAddressDetails; + const isPersonLivingInRefuge = C100rebuildJourney + ? this.isC100PersonLivingInRefuge(req.session.userCase, id, c100Person!) + : caseData.isCitizenLivingInRefuge === YesOrNo.YES; + + switch (currentPageUrl) { + case STAYING_IN_REFUGE: { + const nextUrl = C100rebuildJourney + ? (applyParms(REFUGE_KEEPING_SAFE, { + root: RootContext.C100_REBUILD, + id, + }) as PageLink) + : (applyParms(REFUGE_KEEPING_SAFE, { root: partyRootContext }) as PageLink); + url = isPersonLivingInRefuge ? nextUrl : addressDetails; + break; + } + case REFUGE_KEEPING_SAFE: { + url = this.getKeepingSafeNextUrl( + id, + C100rebuildJourney, + partyRootContext, + req, + c100Person!, + caseData as CaseWithId + ); + break; + } + case REFUGE_UPLOAD_DOC: { + url = addressDetails; + break; + } + case C100_REFUGE_UPLOAD_DOC: { + url = addressDetails; + break; + } + case REFUGE_DOC_ALREADY_UPLOADED: { + const uploadDocUrl = C100rebuildJourney + ? (applyParms(C100_REFUGE_UPLOAD_DOC, { + root: RootContext.C100_REBUILD, + id, + }) as PageLink) + : (applyParms(REFUGE_UPLOAD_DOC, { root: partyRootContext }) as PageLink); + url = caseData.reUploadRefugeDocument === YesOrNo.YES ? uploadDocUrl : addressDetails; + break; + } + default: { + url = currentPageUrl; + break; + } + } + return url; + } + + private isC100PersonLivingInRefuge(caseData: CaseWithId, id: string, person: People): boolean { + if (person.partyType === PartyType.APPLICANT) { + const applicantDetails = getPartyDetails(id, caseData.appl_allApplicants) as C100Applicant; + return applicantDetails.liveInRefuge === YesOrNo.YES; + } else { + const otherPersonDetails = getPartyDetails(id, caseData.oprs_otherPersons) as C100RebuildPartyDetails; + return otherPersonDetails.liveInRefuge === YesOrNo.YES; + } + } + + private getKeepingSafeNextUrl( + id: string, + C100rebuildJourney: boolean, + partyRootContext: RootContext, + req: AppRequest, + c100Person: People, + caseData: CaseWithId + ): PageLink { + const isDocumentUploaded = !_.isEmpty( + C100rebuildJourney ? getC8DocumentForC100(id, req.session.userCase, c100Person) : caseData.refugeDocument + ); + + const alreadyUploadedDocUrl = C100rebuildJourney + ? (applyParms(REFUGE_DOC_ALREADY_UPLOADED, { + root: RootContext.C100_REBUILD, + id, + }) as PageLink) + : (applyParms(REFUGE_DOC_ALREADY_UPLOADED, { root: partyRootContext }) as PageLink); + const uploadDocUrl = C100rebuildJourney + ? (applyParms(C100_REFUGE_UPLOAD_DOC, { + root: RootContext.C100_REBUILD, + id, + }) as PageLink) + : (applyParms(REFUGE_UPLOAD_DOC, { root: partyRootContext }) as PageLink); + return isDocumentUploaded ? alreadyUploadedDocUrl : uploadDocUrl; + } +} + +export default new RefugeNavigationController(); diff --git a/src/main/steps/common/refuge/refuge-document-already-uploaded/content.test.ts b/src/main/steps/common/refuge/refuge-document-already-uploaded/content.test.ts new file mode 100644 index 0000000000..c9e7aa7654 --- /dev/null +++ b/src/main/steps/common/refuge/refuge-document-already-uploaded/content.test.ts @@ -0,0 +1,185 @@ +import languageAssertions from '../../../../../test/unit/utils/languageAssertions'; +import { CaseWithId } from '../../../../app/case/case'; +import { YesOrNo } from '../../../../app/case/definition'; +import { FormContent, FormFields, FormOptions, LanguageLookup } from '../../../../app/form/Form'; +import { Validator, isFieldFilledIn } from '../../../../app/form/validation'; +import { CommonContent, generatePageContent } from '../../common.content'; +import { interpolate } from '../../string-parser'; + +import { generateContent } from './content'; + +jest.mock('../../../../app/form/validation'); + +const en = { + title: 'Upload a new C8 form', + previouslyUploaded: 'You have previously uploaded a C8 form.', + c100PreviouslyUploaded: 'A C8 form has already been uploaded for {name}', + canView: 'You can view the uploaded document', + viewDoc: 'here (opens in a new tab)', + uploadC8Label: 'Do you still want to upload a new C8 form?', + one: 'Yes', + two: 'No', + continue: 'Continue', + bannerHeading: 'Important', + errors: { + reUploadRefugeDocument: { + required: 'Select if you want to upload a new C8 form', + }, + }, +}; + +const cy = { + title: 'Uwchlwytho ffurflen C8 newydd', + previouslyUploaded: 'Rydych eisoes wedi uwchlwytho ffurflen C8.', + c100PreviouslyUploaded: 'Mae ffurflen C8 eisoes wedi’i huwchlwytho ar gyfer {name}', + canView: 'Gallwch weld hon', + viewDoc: 'yma (yn agor mewn tab newydd)', + uploadC8Label: 'Ydych chi dal eisiau uwchlwytho ffurflen C8 newydd?', + one: 'Ydw', + two: 'Nac ydw', + continue: 'Parhau', + bannerHeading: 'Pwysig', + errors: { + reUploadRefugeDocument: { + required: 'Dewiswch a ydych chi eisiau uwchlwytho ffurflen C8 newydd', + }, + }, +}; + +/* eslint-disable @typescript-eslint/ban-types */ +describe('C8 Refuge > C8 already uploaded > content', () => { + const commonContent = { + language: 'en', + } as CommonContent; + const userCase = { + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + } as unknown as CaseWithId; + const additionalData = { + req: { + originalUrl: '/c100-rebuild/', + params: { + root: 'c100-rebuild', + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + session: { + userCase, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }, + }, + }; + let generatedContent; + let form; + let fields; + + describe('c100-rebuild journey', () => { + beforeEach(() => { + generatedContent = generateContent({ ...commonContent, additionalData, userCase }); + form = generatedContent.form as FormContent; + fields = form.fields as FormFields; + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct english content', () => { + languageAssertions( + 'en', + { ...en, previouslyUploaded: interpolate(en.c100PreviouslyUploaded, { name: 'Test Test' }) }, + () => generatedContent + ); + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct welsh content', () => { + languageAssertions( + 'cy', + { ...cy, previouslyUploaded: interpolate(cy.c100PreviouslyUploaded, { name: 'Test Test' }) }, + () => generateContent({ ...commonContent, additionalData, userCase, language: 'cy' }) + ); + }); + + test('should contain continue button', () => { + expect((form.onlyContinue?.text as Function)(generatedContent)).toBe(en.continue); + }); + + test('should contain saveAndComeLater button', () => { + expect( + (form?.saveAndComeLater?.text as LanguageLookup)( + generatePageContent({ language: 'en', additionalData, userCase }) as Record + ) + ).toBe('Save and come back later'); + }); + + test('should contain correct fields', () => { + const reUploadRefugeDocumentField = fields.reUploadRefugeDocument as FormOptions; + expect(reUploadRefugeDocumentField.type).toBe('radios'); + expect(reUploadRefugeDocumentField.classes).toBe('govuk-radios'); + expect((reUploadRefugeDocumentField.label as Function)(generatedContent)).toBe(en.uploadC8Label); + expect((reUploadRefugeDocumentField.values[0].label as Function)(generatedContent)).toBe(en.one); + expect(reUploadRefugeDocumentField.values[0].value).toBe(YesOrNo.YES); + expect((reUploadRefugeDocumentField.values[1].label as Function)(generatedContent)).toBe(en.two); + expect(reUploadRefugeDocumentField.values[1].value).toBe(YesOrNo.NO); + (reUploadRefugeDocumentField.validator as Validator)('test value'); + expect(reUploadRefugeDocumentField.validator).toBe(isFieldFilledIn); + }); + }); + + describe('applicant/respondent journey', () => { + const applicantRespondentAdditionalData = { + req: { + params: {}, + session: { + userCase: { + refugeDocument: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }, + }, + }; + beforeEach(() => { + generatedContent = generateContent({ + ...commonContent, + additionalData: applicantRespondentAdditionalData, + }); + form = generatedContent.form as FormContent; + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct english content', () => { + languageAssertions('en', en, () => generatedContent); + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct welsh content', () => { + languageAssertions('cy', cy, () => + generateContent({ + ...commonContent, + additionalData: applicantRespondentAdditionalData, + userCase: {}, + language: 'cy', + }) + ); + }); + + test('should contain continue button', () => { + expect((form.onlyContinue?.text as Function)(generatedContent)).toBe(en.continue); + }); + }); +}); diff --git a/src/main/steps/common/refuge/refuge-document-already-uploaded/content.ts b/src/main/steps/common/refuge/refuge-document-already-uploaded/content.ts new file mode 100644 index 0000000000..5d8334a523 --- /dev/null +++ b/src/main/steps/common/refuge/refuge-document-already-uploaded/content.ts @@ -0,0 +1,122 @@ +import { YesOrNo } from '../../../../app/case/definition'; +import { PageContent } from '../../../../app/controller/GetController'; +import { FormContent } from '../../../../app/form/Form'; +import { isFieldFilledIn } from '../../../../app/form/validation'; +import { getPeople } from '../../../c100-rebuild/child-details/live-with/utils'; +import { getCasePartyType } from '../../../prl-cases/dashboard/utils'; +import { C100_URL, DOWNLOAD_DOCUMENT } from '../../../urls'; +import { CommonContent } from '../../common.content'; +import { transformFileName } from '../../documents/download/utils'; +import { interpolate } from '../../string-parser'; +import { applyParms } from '../../url-parser'; +import { getC8DocumentForC100 } from '../utils'; +export * from './routeGuard'; + +const en = { + title: 'Upload a new C8 form', + previouslyUploaded: 'You have previously uploaded a C8 form.', + c100PreviouslyUploaded: 'A C8 form has already been uploaded for {name}', + canView: 'You can view the uploaded document', + viewDoc: 'here (opens in a new tab)', + uploadC8Label: 'Do you still want to upload a new C8 form?', + one: 'Yes', + two: 'No', + continue: 'Continue', + bannerHeading: 'Important', + errors: { + reUploadRefugeDocument: { + required: 'Select if you want to upload a new C8 form', + }, + }, +}; + +const cy: typeof en = { + title: 'Uwchlwytho ffurflen C8 newydd', + previouslyUploaded: 'Rydych eisoes wedi uwchlwytho ffurflen C8.', + c100PreviouslyUploaded: 'Mae ffurflen C8 eisoes wedi’i huwchlwytho ar gyfer {name}', + canView: 'Gallwch weld hon', + viewDoc: 'yma (yn agor mewn tab newydd)', + uploadC8Label: 'Ydych chi dal eisiau uwchlwytho ffurflen C8 newydd?', + one: 'Ydw', + two: 'Nac ydw', + continue: 'Parhau', + bannerHeading: 'Pwysig', + errors: { + reUploadRefugeDocument: { + required: 'Dewiswch a ydych chi eisiau uwchlwytho ffurflen C8 newydd', + }, + }, +}; + +const languages = { + en, + cy, +}; + +export const form: FormContent = { + fields: { + reUploadRefugeDocument: { + type: 'radios', + classes: 'govuk-radios', + label: l => l.uploadC8Label, + labelSize: 'm', + values: [ + { + label: l => l.one, + value: YesOrNo.YES, + }, + { + label: l => l.two, + value: YesOrNo.NO, + }, + ], + validator: isFieldFilledIn, + }, + }, + onlyContinue: { + text: l => l.continue, + }, +}; + +export const generateContent = (content: CommonContent): PageContent => { + const translations = languages[content.language]; + const request = content.additionalData?.req; + const caseData = request.session?.userCase; + let c8Document = caseData.refugeDocument; + const C100rebuildJourney = request.originalUrl?.startsWith(C100_URL); + const id = request.params.id; + + const c100Person = getPeople(caseData).find(person => person.id === id); + + delete form.saveAndComeLater; + if (C100rebuildJourney) { + Object.assign(form, { + saveAndComeLater: { + text: l => l.saveAndComeLater, + }, + }); + + c8Document = getC8DocumentForC100(id, caseData, c100Person!); + } + + const partyType = getCasePartyType(caseData, request.session?.user.id); + const url = applyParms(DOWNLOAD_DOCUMENT, { + partyType, + documentId: c8Document?.document_url.substring(c8Document.document_url.lastIndexOf('/') + 1), + documentName: transformFileName(c8Document?.document_filename), + }); + + return { + ...translations, + previouslyUploaded: C100rebuildJourney + ? interpolate(translations.c100PreviouslyUploaded, { + name: `${c100Person?.firstName} ${c100Person?.lastName}`, + }) + : translations.previouslyUploaded, + form, + c8Document: { + href: url, + fileName: c8Document.document_filename, + }, + }; +}; diff --git a/src/main/steps/common/refuge/refuge-document-already-uploaded/routeGuard.test.ts b/src/main/steps/common/refuge/refuge-document-already-uploaded/routeGuard.test.ts new file mode 100644 index 0000000000..745281761c --- /dev/null +++ b/src/main/steps/common/refuge/refuge-document-already-uploaded/routeGuard.test.ts @@ -0,0 +1,33 @@ +import { mockRequest } from '../../../../../test/unit/utils/mockRequest'; +import { mockResponse } from '../../../../../test/unit/utils/mockResponse'; + +import { routeGuard } from './routeGuard'; + +describe('C8 Refuge > C8 already uploaded > routeGuard', () => { + test('Should delete the reUploadRefugeDocument value when page is loaded', async () => { + const req = mockRequest({ + session: { + userCase: { + reUploadRefugeDocument: 'Yes', + }, + }, + }); + const res = mockResponse(); + const next = jest.fn(); + routeGuard.get(req, res, next); + expect(next).toHaveBeenCalled(); + expect(req.session.userCase.reUploadRefugeDocument).toBe(undefined); + }); + + test('Should call next when reUploadRefugeDocument is not present', async () => { + const req = mockRequest({ + session: { + userCase: {}, + }, + }); + const res = mockResponse(); + const next = jest.fn(); + routeGuard.get(req, res, next); + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/src/main/steps/common/refuge/refuge-document-already-uploaded/routeGuard.ts b/src/main/steps/common/refuge/refuge-document-already-uploaded/routeGuard.ts new file mode 100644 index 0000000000..691cc0e8b7 --- /dev/null +++ b/src/main/steps/common/refuge/refuge-document-already-uploaded/routeGuard.ts @@ -0,0 +1,15 @@ +import { NextFunction, Response } from 'express'; + +import { AppRequest } from '../../../../app/controller/AppRequest'; + +export const routeGuard = { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + get: async (req: AppRequest, res: Response, next: NextFunction) => { + if (req.session.userCase.hasOwnProperty('reUploadRefugeDocument')) { + delete req.session.userCase.reUploadRefugeDocument; + return req.session.save(next); + } + + next(); + }, +}; diff --git a/src/main/steps/common/refuge/refuge-document-already-uploaded/template.njk b/src/main/steps/common/refuge/refuge-document-already-uploaded/template.njk new file mode 100644 index 0000000000..5b67261f2e --- /dev/null +++ b/src/main/steps/common/refuge/refuge-document-already-uploaded/template.njk @@ -0,0 +1,19 @@ +{% extends "common/template.njk" %} +{% from "govuk/components/notification-banner/macro.njk" import govukNotificationBanner %} + +{% block page_content %} +

{{title}}

+ + {% set html %} +

{{previouslyUploaded}}

+

{{canView}} + {{ viewDoc | safe }} +

+ {% endset %} + + {{ govukNotificationBanner({ + titleText: bannerHeading, + html: html +}) }} + +{% endblock %} \ No newline at end of file diff --git a/src/main/steps/common/refuge/sequence.test.ts b/src/main/steps/common/refuge/sequence.test.ts new file mode 100644 index 0000000000..7cb707b2b1 --- /dev/null +++ b/src/main/steps/common/refuge/sequence.test.ts @@ -0,0 +1,49 @@ +import { mockRequest } from '../../../../test/unit/utils/mockRequest'; + +import { C8RefugeSequence } from './sequence'; + +const refugeMockData = mockRequest({ + params: { + root: '/respondent', + }, + session: { + userCase: {}, + }, +}); + +describe('C8 refuge > sequence', () => { + test('should contain 1 entries in respondent 1 screen sequence', () => { + const sequence = C8RefugeSequence.getSequence(); + expect(sequence).toHaveLength(5); + + expect(sequence[0].url).toBe('/:root/refuge/staying-in-refuge/:id?'); + expect(sequence[0].showInSection).toBe('c100'); + expect(sequence[0].getNextStep(refugeMockData.session.userCase, refugeMockData)).toBe( + '/applicant/confirm-contact-details/addressdetails' + ); + + expect(sequence[1].url).toBe('/:root/refuge/keeping-details-safe/:id?'); + expect(sequence[1].showInSection).toBe('c100'); + expect(sequence[1].getNextStep(refugeMockData.session.userCase, refugeMockData)).toBe( + '/applicant/refuge/upload-refuge-document' + ); + + expect(sequence[2].url).toBe('/:root/refuge/upload-refuge-document/:removeFileId?'); + expect(sequence[2].showInSection).toBe('c100'); + expect(sequence[2].getNextStep(refugeMockData.session.userCase, refugeMockData)).toBe( + '/applicant/confirm-contact-details/addressdetails' + ); + + expect(sequence[3].url).toBe('/:root/refuge/upload-refuge-document/:id/:removeFileId?'); + expect(sequence[3].showInSection).toBe('c100'); + expect(sequence[3].getNextStep(refugeMockData.session.userCase, refugeMockData)).toBe( + '/applicant/confirm-contact-details/addressdetails' + ); + + expect(sequence[4].url).toBe('/:root/refuge/refuge-document-already-uploaded/:id?'); + expect(sequence[4].showInSection).toBe('c100'); + expect(sequence[4].getNextStep(refugeMockData.session.userCase, refugeMockData)).toBe( + '/applicant/confirm-contact-details/addressdetails' + ); + }); +}); diff --git a/src/main/steps/common/refuge/sequence.ts b/src/main/steps/common/refuge/sequence.ts new file mode 100644 index 0000000000..3338cee62e --- /dev/null +++ b/src/main/steps/common/refuge/sequence.ts @@ -0,0 +1,54 @@ +import { Sections, Step } from '../../../steps/constants'; +import { + C100_REFUGE_UPLOAD_DOC, + REFUGE_DOC_ALREADY_UPLOADED, + REFUGE_KEEPING_SAFE, + REFUGE_UPLOAD_DOC, + STAYING_IN_REFUGE, +} from '../../../steps/urls'; + +import RefugeNavigationController from './navigationController'; + +export class RefugeSequence { + getSequence(): Step[] { + return [ + { + url: STAYING_IN_REFUGE, + showInSection: Sections.C100, + getNextStep: (caseData, req) => { + return RefugeNavigationController.getNextPageUrl(STAYING_IN_REFUGE, caseData, req!); + }, + }, + { + url: REFUGE_KEEPING_SAFE, + showInSection: Sections.C100, + getNextStep: (caseData, req) => { + return RefugeNavigationController.getNextPageUrl(REFUGE_KEEPING_SAFE, caseData, req!); + }, + }, + { + url: REFUGE_UPLOAD_DOC, + showInSection: Sections.C100, + getNextStep: (caseData, req) => { + return RefugeNavigationController.getNextPageUrl(REFUGE_UPLOAD_DOC, caseData, req!); + }, + }, + { + url: C100_REFUGE_UPLOAD_DOC, + showInSection: Sections.C100, + getNextStep: (caseData, req) => { + return RefugeNavigationController.getNextPageUrl(REFUGE_UPLOAD_DOC, caseData, req!); + }, + }, + { + url: REFUGE_DOC_ALREADY_UPLOADED, + showInSection: Sections.C100, + getNextStep: (caseData, req) => { + return RefugeNavigationController.getNextPageUrl(REFUGE_DOC_ALREADY_UPLOADED, caseData, req!); + }, + }, + ]; + } +} + +export const C8RefugeSequence = new RefugeSequence(); diff --git a/src/main/steps/common/refuge/staying-in-refuge/content.test.ts b/src/main/steps/common/refuge/staying-in-refuge/content.test.ts new file mode 100644 index 0000000000..add62be53d --- /dev/null +++ b/src/main/steps/common/refuge/staying-in-refuge/content.test.ts @@ -0,0 +1,274 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import languageAssertions from '../../../../../test/unit/utils/languageAssertions'; +import { CaseWithId } from '../../../../app/case/case'; +import { YesOrNo } from '../../../../app/case/definition'; +import { FormContent, FormFields, FormOptions, LanguageLookup } from '../../../../app/form/Form'; +import { Validator, isFieldFilledIn } from '../../../../app/form/validation'; +import { CommonContent, generatePageContent } from '../../../common/common.content'; +import { interpolate } from '../../string-parser'; + +import { generateContent } from './content'; + +jest.mock('../../../../app/form/validation'); + +const en = { + title: 'Staying in a refuge', + refuge: + 'A refuge is a secure place for people and their children to stay when they are escaping domestic abuse. It provides a space to feel safe and supported.', + citizensAdvice: + 'Find out more about refuges at Citizen\'s Advice (opens in a new tab).', + refugeLabel: 'Do you currently live in a refuge?', + C100RefugeLabel: 'Does {firstName} {lastName} currently live in a refuge?', + one: 'Yes', + two: 'No', + you: 'you', + they: 'they', + continue: 'Continue', + errors: { + isCitizenLivingInRefuge: { + required: 'Select yes if you currently live in a refuge', + }, + }, +}; + +const cy = { + title: 'Aros mewn lloches', + refuge: + 'Mae lloches yn lle diogel i bobl a’u plant aros pan fyddant yn dianc rhag cam-drin domestig. Mae’n darparu lle i deimlo’n ddiogel a chael cefnogaeth.', + citizensAdvice: + 'Dysgwch fwy am lochesau yn Cyngor ar Bopeth (yn agor mewn tab newydd).', + refugeLabel: 'Ydych chi’n byw mewn lloches ar hyn o bryd?', + C100RefugeLabel: 'A yw {firstName} {lastName} yn byw mewn lloches ar hyn o bryd?', + one: 'Ydw', + two: 'Nac ydw', + you: "ydych chi'n", + they: 'ydynt yn', + continue: 'Parhau', + errors: { + isCitizenLivingInRefuge: { + required: "Dewiswch ydw os ydych chi'n byw mewn lloches ar hyn o bryd", + }, + }, +}; + +/* eslint-disable @typescript-eslint/ban-types */ +describe('C8 Refuge > staying in refuge > content', () => { + const commonContent = { + language: 'en', + } as CommonContent; + const userCase = { + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + }, + ], + } as unknown as CaseWithId; + const additionalData = { + req: { + originalUrl: '/c100-rebuild/', + params: { + root: 'c100-rebuild', + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + session: { + userCase, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }, + }, + }; + let generatedContent; + let form; + let fields; + + describe('c100-rebuild journey', () => { + beforeEach(() => { + generatedContent = generateContent({ ...commonContent, additionalData, userCase }); + form = generatedContent.form as FormContent; + fields = form.fields as FormFields; + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct english content', () => { + languageAssertions('en', en, () => generatedContent); + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct welsh content', () => { + languageAssertions('cy', cy, () => + generateContent({ ...commonContent, additionalData, userCase, language: 'cy' }) + ); + }); + + test('should contain continue button', () => { + expect((form.onlyContinue?.text as Function)(generatedContent)).toBe(en.continue); + }); + + test('should have correct error text for otherPerson', () => { + const generatedOtherPersonContent = generateContent({ + ...commonContent, + additionalData: { + req: { + originalUrl: '/c100-rebuild/', + params: { + root: 'c100-rebuild', + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + session: { + userCase: { + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Test', + }, + ], + }, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }, + }, + }, + userCase: { + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Test', + }, + ], + } as unknown as CaseWithId, + }) as any; + + expect(generatedOtherPersonContent.errors.isCitizenLivingInRefuge.required).toBe( + 'Select yes if they currently live in a refuge' + ); + }); + + test('should have correct welsh error text for otherPerson', () => { + const generatedOtherPersonContent = generateContent({ + ...commonContent, + additionalData: { + req: { + originalUrl: '/c100-rebuild/', + params: { + root: 'c100-rebuild', + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + session: { + userCase: { + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Test', + }, + ], + }, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }, + }, + }, + userCase: { + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Test', + }, + ], + } as unknown as CaseWithId, + language: 'cy', + }) as any; + + expect(generatedOtherPersonContent.errors.isCitizenLivingInRefuge.required).toBe( + 'Dewiswch ydw os ydynt yn byw mewn lloches ar hyn o bryd' + ); + }); + + test('should contain saveAndComeLater button', () => { + expect( + (form?.saveAndComeLater?.text as LanguageLookup)( + generatePageContent({ language: 'en', additionalData, userCase }) as Record + ) + ).toBe('Save and come back later'); + }); + + test('should contain correct fields', () => { + const isCitizenLivingInRefugeField = fields.isCitizenLivingInRefuge as FormOptions; + expect(isCitizenLivingInRefugeField.type).toBe('radios'); + expect(isCitizenLivingInRefugeField.classes).toBe('govuk-radios'); + expect((isCitizenLivingInRefugeField.label as Function)(generatedContent)).toBe( + interpolate(en.C100RefugeLabel, { firstName: 'Test', lastName: 'Test' }) + ); + expect((isCitizenLivingInRefugeField.values[0].label as Function)(generatedContent)).toBe(en.one); + expect(isCitizenLivingInRefugeField.values[0].value).toBe(YesOrNo.YES); + expect((isCitizenLivingInRefugeField.values[1].label as Function)(generatedContent)).toBe(en.two); + expect(isCitizenLivingInRefugeField.values[1].value).toBe(YesOrNo.NO); + (isCitizenLivingInRefugeField.validator as Validator)('test value'); + expect(isCitizenLivingInRefugeField.validator).toBe(isFieldFilledIn); + }); + }); + + describe('applicant/respondent journey', () => { + const applicantRespondentAdditionalData = { + req: { + params: {}, + session: { + userCase: {}, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }, + }, + }; + beforeEach(() => { + generatedContent = generateContent({ + ...commonContent, + additionalData: applicantRespondentAdditionalData, + }); + form = generatedContent.form as FormContent; + fields = form.fields as FormFields; + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct english content', () => { + languageAssertions('en', en, () => generatedContent); + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct welsh content', () => { + languageAssertions('cy', cy, () => + generateContent({ + ...commonContent, + additionalData: applicantRespondentAdditionalData, + userCase: {}, + language: 'cy', + }) + ); + }); + + test('should contain correct fields', () => { + const isCitizenLivingInRefugeField = fields.isCitizenLivingInRefuge as FormOptions; + expect(isCitizenLivingInRefugeField.type).toBe('radios'); + expect(isCitizenLivingInRefugeField.classes).toBe('govuk-radios'); + expect((isCitizenLivingInRefugeField.label as Function)(generatedContent)).toBe(en.refugeLabel); + expect((isCitizenLivingInRefugeField.values[0].label as Function)(generatedContent)).toBe(en.one); + expect(isCitizenLivingInRefugeField.values[0].value).toBe(YesOrNo.YES); + expect((isCitizenLivingInRefugeField.values[1].label as Function)(generatedContent)).toBe(en.two); + expect(isCitizenLivingInRefugeField.values[1].value).toBe(YesOrNo.NO); + (isCitizenLivingInRefugeField.validator as Validator)('test value'); + expect(isCitizenLivingInRefugeField.validator).toBe(isFieldFilledIn); + }); + + test('should contain continue button', () => { + expect((form.onlyContinue?.text as Function)(generatedContent)).toBe(en.continue); + }); + }); +}); diff --git a/src/main/steps/common/refuge/staying-in-refuge/content.ts b/src/main/steps/common/refuge/staying-in-refuge/content.ts new file mode 100644 index 0000000000..f9c367a127 --- /dev/null +++ b/src/main/steps/common/refuge/staying-in-refuge/content.ts @@ -0,0 +1,120 @@ +import { CaseWithId } from '../../../../app/case/case'; +import { PartyType, YesOrNo } from '../../../../app/case/definition'; +import { AppRequest } from '../../../../app/controller/AppRequest'; +import { PageContent } from '../../../../app/controller/GetController'; +import { FormContent, FormFields, FormFieldsFn } from '../../../../app/form/Form'; +import { isFieldFilledIn } from '../../../../app/form/validation'; +import { getPeople } from '../../../../steps/c100-rebuild/child-details/live-with/utils'; +import { interpolate } from '../../../../steps/common/string-parser'; +import { C100_URL } from '../../../../steps/urls'; +import { CommonContent } from '../../../common/common.content'; +export * from './routeGuard'; + +const en = { + title: 'Staying in a refuge', + refuge: + 'A refuge is a secure place for people and their children to stay when they are escaping domestic abuse. It provides a space to feel safe and supported.', + citizensAdvice: + 'Find out more about refuges at Citizen\'s Advice (opens in a new tab).', + refugeLabel: 'Do you currently live in a refuge?', + C100RefugeLabel: 'Does {firstName} {lastName} currently live in a refuge?', + one: 'Yes', + two: 'No', + you: 'you', + they: 'they', + continue: 'Continue', + errors: { + isCitizenLivingInRefuge: { + required: 'Select yes if {youOrThey} currently live in a refuge', + }, + }, +}; + +const cy: typeof en = { + title: 'Aros mewn lloches', + refuge: + 'Mae lloches yn lle diogel i bobl a’u plant aros pan fyddant yn dianc rhag cam-drin domestig. Mae’n darparu lle i deimlo’n ddiogel a chael cefnogaeth.', + citizensAdvice: + 'Dysgwch fwy am lochesau yn Cyngor ar Bopeth (yn agor mewn tab newydd).', + refugeLabel: 'Ydych chi’n byw mewn lloches ar hyn o bryd?', + C100RefugeLabel: 'A yw {firstName} {lastName} yn byw mewn lloches ar hyn o bryd?', + one: 'Ydw', + two: 'Nac ydw', + you: "ydych chi'n", + they: 'ydynt yn', + continue: 'Parhau', + errors: { + isCitizenLivingInRefuge: { + required: 'Dewiswch ydw os {youOrThey} byw mewn lloches ar hyn o bryd', + }, + }, +}; + +const languages = { + en, + cy, +}; + +export const form: FormContent = { + fields: (userCase: Partial, req: AppRequest): FormFields => { + const C100rebuildJourney = req?.originalUrl?.startsWith(C100_URL); + const c100Person = getPeople(userCase).find(person => person.id === req.params.id)!; + return { + isCitizenLivingInRefuge: { + type: 'radios', + classes: 'govuk-radios', + label: l => + C100rebuildJourney + ? interpolate(l.C100RefugeLabel, { firstName: c100Person?.firstName, lastName: c100Person?.lastName }) + : l.refugeLabel, + labelSize: 'm', + values: [ + { + label: l => l.one, + value: YesOrNo.YES, + }, + { + label: l => l.two, + value: YesOrNo.NO, + }, + ], + validator: isFieldFilledIn, + }, + }; + }, + onlyContinue: { + text: l => l.continue, + }, +}; + +export const generateContent = (content: CommonContent): PageContent => { + const translations = languages[content.language]; + const C100rebuildJourney = content.additionalData?.req?.originalUrl?.startsWith(C100_URL); + const request = content.additionalData?.req; + const caseData = request.session?.userCase; + const id = request.params.id; + const c100Person = getPeople(caseData).find(person => person.id === id); + + delete form.saveAndComeLater; + if (C100rebuildJourney) { + Object.assign(form, { + saveAndComeLater: { + text: l => l.saveAndComeLater, + }, + }); + } + + return { + ...translations, + form: { ...form, fields: (form.fields as FormFieldsFn)(content.userCase || {}, content.additionalData?.req) }, + errors: { + ...translations.errors, + isCitizenLivingInRefuge: { + ...translations.errors.isCitizenLivingInRefuge, + required: interpolate(translations.errors.isCitizenLivingInRefuge.required, { + youOrThey: c100Person?.partyType === PartyType.OTHER_PERSON ? translations.they : translations.you, + }), + }, + }, + }; +}; diff --git a/src/main/steps/common/refuge/staying-in-refuge/postController.test.ts b/src/main/steps/common/refuge/staying-in-refuge/postController.test.ts new file mode 100644 index 0000000000..8ef8daa337 --- /dev/null +++ b/src/main/steps/common/refuge/staying-in-refuge/postController.test.ts @@ -0,0 +1,318 @@ +import { mockRequest } from '../../../../../test/unit/utils/mockRequest'; +import { mockResponse } from '../../../../../test/unit/utils/mockResponse'; +import { CosApiClient } from '../../../../app/case/CosApiClient'; +import { FormContent } from '../../../../app/form/Form'; +import { CommonContent } from '../../../common/common.content'; + +import { generateContent } from './content'; +import PersonaldetailsPostController from './postController'; + +const deleteDocumentMock = jest.spyOn(CosApiClient.prototype, 'deleteDocument'); + +describe('C8 refuge > staying in refuge > postController', () => { + const commonContent = { + language: 'en', + userCase: { + appl_allApplicants: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + }, + ], + oprs_otherPersons: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925475', + firstName: 'dummy', + lastName: 'Test', + }, + ], + }, + additionalData: { + req: { + session: { + userCase: { + appl_allApplicants: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + }, + ], + oprs_otherPersons: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925475', + firstName: 'dummy', + lastName: 'Test', + }, + ], + }, + }, + params: { + applicantId: '7483640e-0817-4ddc-b709-6723f7925474', + }, + }, + }, + } as unknown as CommonContent; + + describe('c100 applicant', () => { + test('Should update party details and navigate to the next page when there are no errors', async () => { + const mockFormContent = { + fields: {}, + } as unknown as FormContent; + const controller = new PersonaldetailsPostController(mockFormContent.fields); + const language = 'en'; + const req = mockRequest({ + params: { + id: '7483640e-0817-4ddc-b709-6723f7925474', + }, + body: { + onlyContinue: true, + isCitizenLivingInRefuge: 'Yes', + }, + session: { + lang: language, + userCase: { + ...commonContent.userCase, + }, + }, + }); + req.originalUrl = '/c100-rebuild'; + const res = mockResponse(); + generateContent(commonContent); + await controller.post(req, res); + + expect(res.redirect).toHaveBeenCalled; + expect(req.session.userCase.appl_allApplicants[0].liveInRefuge).toBe('Yes'); + }); + + test('Should update party details and delete existing document if no is selected', async () => { + const mockFormContent = { + fields: {}, + } as unknown as FormContent; + const controller = new PersonaldetailsPostController(mockFormContent.fields); + const language = 'en'; + const req = mockRequest({ + params: { + id: '7483640e-0817-4ddc-b709-6723f7925474', + }, + body: { + onlyContinue: true, + isCitizenLivingInRefuge: 'No', + }, + session: { + lang: language, + userCase: { + ...commonContent.userCase, + appl_allApplicants: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }, + }, + }); + req.originalUrl = '/c100-rebuild'; + const res = mockResponse(); + generateContent(commonContent); + deleteDocumentMock.mockResolvedValueOnce('SUCCESS'); + + await controller.post(req, res); + + expect(res.redirect).toHaveBeenCalled; + expect(req.session.userCase.appl_allApplicants[0].liveInRefuge).toBe('No'); + expect(req.session.userCase.appl_allApplicants[0].refugeConfidentialityC8Form).toBe(undefined); + }); + }); + + describe('c100 other people', () => { + test('Should update party details and navigate to the next page when there are no errors for c100 other people', async () => { + const mockFormContent = { + fields: {}, + } as unknown as FormContent; + const controller = new PersonaldetailsPostController(mockFormContent.fields); + const language = 'en'; + const req = mockRequest({ + params: { + id: '7483640e-0817-4ddc-b709-6723f7925475', + }, + body: { + onlyContinue: true, + isCitizenLivingInRefuge: 'Yes', + }, + session: { + lang: language, + userCase: { + ...commonContent.userCase, + }, + }, + }); + req.originalUrl = '/c100-rebuild'; + const res = mockResponse(); + generateContent(commonContent); + await controller.post(req, res); + + expect(res.redirect).toHaveBeenCalled; + expect(req.session.userCase.oprs_otherPersons[0].liveInRefuge).toBe('Yes'); + }); + + test('Should update party details and delete existing document if no is selected', async () => { + const mockFormContent = { + fields: {}, + } as unknown as FormContent; + const controller = new PersonaldetailsPostController(mockFormContent.fields); + const language = 'en'; + const req = mockRequest({ + params: { + id: '7483640e-0817-4ddc-b709-6723f7925475', + }, + body: { + onlyContinue: true, + isCitizenLivingInRefuge: 'No', + }, + session: { + lang: language, + userCase: { + ...commonContent.userCase, + oprs_otherPersons: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925475', + firstName: 'dummy', + lastName: 'Test', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }, + }, + }); + req.originalUrl = '/c100-rebuild'; + const res = mockResponse(); + generateContent(commonContent); + deleteDocumentMock.mockResolvedValueOnce('SUCCESS'); + + await controller.post(req, res); + + expect(res.redirect).toHaveBeenCalled; + expect(req.session.userCase.oprs_otherPersons[0].liveInRefuge).toBe('No'); + expect(req.session.userCase.appl_allApplicants[0].refugeConfidentialityC8Form).toBe(undefined); + }); + + test('Should catch error when deleting document', async () => { + const mockFormContent = { + fields: {}, + } as unknown as FormContent; + const controller = new PersonaldetailsPostController(mockFormContent.fields); + const language = 'en'; + const req = mockRequest({ + params: { + id: '7483640e-0817-4ddc-b709-6723f7925475', + }, + body: { + onlyContinue: true, + isCitizenLivingInRefuge: 'No', + }, + session: { + lang: language, + userCase: { + ...commonContent.userCase, + oprs_otherPersons: [ + { + id: '7483640e-0817-4ddc-b709-6723f7925475', + firstName: 'dummy', + lastName: 'Test', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }, + }, + }); + req.originalUrl = '/c100-rebuild'; + const res = mockResponse(); + generateContent(commonContent); + deleteDocumentMock.mockRejectedValueOnce('failure'); + + await expect(controller.post(req, res)).rejects.toThrow( + 'Error occured, document could not be deleted. - deleteDocument' + ); + + expect(res.redirect).toHaveBeenCalled; + expect(req.session.userCase.oprs_otherPersons[0].liveInRefuge).toBe('No'); + expect(req.session.userCase.oprs_otherPersons[0].refugeConfidentialityC8Form).toStrictEqual({ + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }); + }); + }); + + describe('applicant/respondent', () => { + test('Should update party details and navigate to the next page when there are no errors for c100 other people', async () => { + const mockFormContent = { + fields: {}, + } as unknown as FormContent; + const controller = new PersonaldetailsPostController(mockFormContent.fields); + const language = 'en'; + const req = mockRequest({ + params: {}, + body: { + onlyContinue: true, + isCitizenLivingInRefuge: 'Yes', + }, + session: { + lang: language, + userCase: { + ...commonContent.userCase, + }, + }, + }); + const res = mockResponse(); + generateContent(commonContent); + await controller.post(req, res); + + expect(res.redirect).toHaveBeenCalled; + expect(req.session.userCase.isCitizenLivingInRefuge).toBe('Yes'); + }); + }); + + test('Should update case when save and come back button is clicked', async () => { + const mockFormContent = { + fields: {}, + } as unknown as FormContent; + const controller = new PersonaldetailsPostController(mockFormContent.fields); + const language = 'en'; + const req = mockRequest({ + params: { + id: '7483640e-0817-4ddc-b709-6723f7925474', + }, + body: { + saveAndComeLater: true, + }, + session: { + lang: language, + userCase: { + ...commonContent.userCase, + }, + }, + }); + const res = mockResponse(); + generateContent(commonContent); + await controller.post(req, res); + + expect(res.redirect).toHaveBeenCalled(); + }); +}); diff --git a/src/main/steps/common/refuge/staying-in-refuge/postController.ts b/src/main/steps/common/refuge/staying-in-refuge/postController.ts new file mode 100644 index 0000000000..eb42c577fa --- /dev/null +++ b/src/main/steps/common/refuge/staying-in-refuge/postController.ts @@ -0,0 +1,83 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import autobind from 'autobind-decorator'; +import { Response } from 'express'; +import _ from 'lodash'; + +import { CosApiClient } from '../../../../app/case/CosApiClient'; +import { CaseWithId } from '../../../../app/case/case'; +import { C100Applicant, C100RebuildPartyDetails, PartyType, People, YesOrNo } from '../../../../app/case/definition'; +import { AppRequest } from '../../../../app/controller/AppRequest'; +import { AnyObject, PostController } from '../../../../app/controller/PostController'; +import { Form, FormFields, FormFieldsFn } from '../../../../app/form/Form'; +import { getPeople } from '../../../../steps/c100-rebuild/child-details/live-with/utils'; +import { getPartyDetails } from '../../../../steps/c100-rebuild/people/util'; +import { C100_URL } from '../../../../steps/urls'; +import { deleteC100RefugeDoc, getC8DocumentForC100, updateApplicantOtherPersonDetails } from '../utils'; + +import { form as refugeForm } from './content'; +@autobind +export default class StayingInRefugeController extends PostController { + constructor(protected readonly fields: FormFields | FormFieldsFn) { + super(fields); + } + + public async post(req: AppRequest, res: Response): Promise { + const { onlyContinue, saveAndComeLater, ...formFields } = req.body; + const userCase = req.session.userCase; + const id = req.params.id; + const { isCitizenLivingInRefuge } = formFields; + + if (!_.isEmpty(isCitizenLivingInRefuge)) { + if (req?.originalUrl?.startsWith(C100_URL)) { + const c100Person = getPeople(userCase).find(person => person.id === id); + const isApplicant = c100Person?.partyType === PartyType.APPLICANT; + const partyDetailsList = isApplicant ? userCase.appl_allApplicants : userCase.oprs_otherPersons; + const partyDetails = getPartyDetails(id, partyDetailsList) as C100Applicant | C100RebuildPartyDetails; + Object.assign(partyDetails, { liveInRefuge: isCitizenLivingInRefuge }); + + req.session.userCase = updateApplicantOtherPersonDetails( + userCase, + partyDetails, + partyDetailsList!, + isApplicant + ); + + await this.deleteC8RefugeDocument(req, id, userCase, c100Person!, isCitizenLivingInRefuge as YesOrNo); + } else { + userCase.isCitizenLivingInRefuge = isCitizenLivingInRefuge as YesOrNo; + } + } + + const form = new Form((refugeForm.fields as FormFieldsFn)(req.session.userCase, req)); + const { _csrf, ...formData } = form.getParsedBody(formFields); + req.session.errors = form.getErrors(formData); + + if (onlyContinue) { + return this.redirect(req, res); + } else if (saveAndComeLater) { + super.saveAndComeLater(req, res, { + appl_allApplicants: req.session.userCase.appl_allApplicants, + oprs_otherPersons: req.session.userCase.oprs_otherPersons, + }); + } + } + + private async deleteC8RefugeDocument( + req: AppRequest, + id: string, + userCase: CaseWithId, + c100Person: People, + livingInRefuge: YesOrNo + ): Promise { + const uploadedDocument = getC8DocumentForC100(id, userCase, c100Person); + if (livingInRefuge === YesOrNo.NO && !_.isEmpty(uploadedDocument)) { + try { + const client = new CosApiClient(req.session.user.accessToken, req.locals.logger); + await client.deleteDocument(_.toString(_.last(uploadedDocument.document_url.split('/')))); + deleteC100RefugeDoc(req, userCase, id); + } catch (e) { + throw new Error('Error occured, document could not be deleted. - deleteDocument'); + } + } + } +} diff --git a/src/main/steps/common/refuge/staying-in-refuge/routeGuard.test.ts b/src/main/steps/common/refuge/staying-in-refuge/routeGuard.test.ts new file mode 100644 index 0000000000..0dfbd1a8a8 --- /dev/null +++ b/src/main/steps/common/refuge/staying-in-refuge/routeGuard.test.ts @@ -0,0 +1,70 @@ +import { mockRequest } from '../../../../../test/unit/utils/mockRequest'; +import { mockResponse } from '../../../../../test/unit/utils/mockResponse'; + +import { routeGuard } from './routeGuard'; + +describe('C8 Refuge > staying in refuge > routeGuard', () => { + test('Should set isCitizenLivingInRefuge for c100 applicant', async () => { + const req = mockRequest({ + params: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + session: { + userCase: { + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + }, + ], + }, + }, + }); + req.originalUrl = '/c100-rebuild'; + const res = mockResponse(); + const next = jest.fn(); + routeGuard.get(req, res, next); + expect(next).toHaveBeenCalled(); + expect(req.session.userCase.isCitizenLivingInRefuge).toBe('Yes'); + }); + + test('Should set isCitizenLivingInRefuge for c100 other people', async () => { + const req = mockRequest({ + params: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + session: { + userCase: { + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + }, + ], + }, + }, + }); + req.originalUrl = '/c100-rebuild'; + const res = mockResponse(); + const next = jest.fn(); + routeGuard.get(req, res, next); + expect(next).toHaveBeenCalled(); + expect(req.session.userCase.isCitizenLivingInRefuge).toBe('Yes'); + }); + + test('Should call next when not c100', async () => { + const req = mockRequest({ + session: { + userCase: {}, + }, + }); + const res = mockResponse(); + const next = jest.fn(); + routeGuard.get(req, res, next); + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/src/main/steps/common/refuge/staying-in-refuge/routeGuard.ts b/src/main/steps/common/refuge/staying-in-refuge/routeGuard.ts new file mode 100644 index 0000000000..087eb9fdd2 --- /dev/null +++ b/src/main/steps/common/refuge/staying-in-refuge/routeGuard.ts @@ -0,0 +1,27 @@ +import { NextFunction, Response } from 'express'; + +import { C100Applicant, C100RebuildPartyDetails, PartyType } from '../../../../app/case/definition'; +import { AppRequest } from '../../../../app/controller/AppRequest'; +import { getPeople } from '../../../../steps/c100-rebuild/child-details/live-with/utils'; +import { getPartyDetails } from '../../../../steps/c100-rebuild/people/util'; +import { C100_URL } from '../../../../steps/urls'; + +export const routeGuard = { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + get: async (req: AppRequest, res: Response, next: NextFunction) => { + const userCase = req.session.userCase; + const id = req.params.id; + + if (req?.originalUrl?.startsWith(C100_URL)) { + const c100Person = getPeople(userCase).find(person => person.id === id); + const partyDetailsList = + c100Person?.partyType === PartyType.APPLICANT ? userCase.appl_allApplicants : userCase.oprs_otherPersons; + const partyDetails = getPartyDetails(id, partyDetailsList) as C100Applicant | C100RebuildPartyDetails; + userCase.isCitizenLivingInRefuge = partyDetails.liveInRefuge; + + return req.session.save(next); + } + + next(); + }, +}; diff --git a/src/main/steps/common/refuge/staying-in-refuge/template.njk b/src/main/steps/common/refuge/staying-in-refuge/template.njk new file mode 100644 index 0000000000..6201b805b2 --- /dev/null +++ b/src/main/steps/common/refuge/staying-in-refuge/template.njk @@ -0,0 +1,7 @@ +{% extends "common/template.njk" %} + +{% block page_content %} +

{{title}}

+

{{refuge}}

+

{{citizensAdvice | safe}}

+{% endblock %} \ No newline at end of file diff --git a/src/main/steps/common/refuge/upload-refuge-document/content.test.ts b/src/main/steps/common/refuge/upload-refuge-document/content.test.ts new file mode 100644 index 0000000000..6a29297143 --- /dev/null +++ b/src/main/steps/common/refuge/upload-refuge-document/content.test.ts @@ -0,0 +1,274 @@ +import languageAssertions from '../../../../../test/unit/utils/languageAssertions'; +import { CaseWithId } from '../../../../app/case/case'; +import { FormContent, LanguageLookup } from '../../../../app/form/Form'; +import { CommonContent, generatePageContent } from '../../common.content'; +import { interpolate } from '../../string-parser'; + +import { generateContent } from './content'; + +jest.mock('../../../../app/form/validation'); + +const en = { + title: 'Upload a C8 form', + uploadFileHeading: 'Upload a document', + uploadGuidance: + 'You can download the form here. Your address, email address and contact number will be kept confidential.', + c100uploadGuidance: + 'You can download the form here. {name}\'s address, email address and contact number will be kept confidential.', + uplodFileHint: + 'When uploading documents, name the files clearly. For example, position-statement.doc. Files must end with JPG, BMP, PNG,TIF, PDF, DOC or DOCX.', + uploadButtonLabel: 'Upload file', + filesUploadedLabel: 'Files uploaded', + noFilesUploaded: 'No files uploaded', + removeDocumentLabel: 'Remove', + uploadGuidelinesAccordionLabel: 'How to take a picture of a document on your phone and upload it', + uploadGuidelines: [ + 'Place your document on a flat service in a well-lit room. Use a flash if you need to.', + 'Take a picture of the whole document. You should be able to see its edges.', + 'Check you can read all the writing, including the handwriting.', + 'Email or send the photo or scan to the device you are using now.', + 'Upload it here.', + ], + errors: { + c8RefugeDocument: { + empty: 'You must upload a C8 document', + uploadError: 'Document could not be uploaded', + deleteError: 'Document could not be deleted', + multipleFiles: + 'You can upload only one file. If you wish to upload a new file, remove the existing file and upload a new one', + fileSize: 'The file you uploaded is too large. Maximum file size allowed is 20MB', + fileFormat: 'The file you uploaded is in the wrong format. Upload your file again in the correct format', + }, + }, +}; + +const cy = { + title: 'Uwchlwytho ffurflen C8', + uploadGuidance: + 'Gallwch lawrlwytho\'r ffurflen yma. Bydd eich cyfeiriad, cyfeiriad e-bost a rhif cyswllt yn cael eu cadw\'n gyfrinachol.', + c100uploadGuidance: + 'Gallwch lawrlwytho\'r ffurflen yma. Bydd cyfeiriad, cyfeiriad e-bost a rhif cyswllt {name} yn cael eu cadw’n gyfrinachol.', + uploadFileHeading: 'Llwytho dogfen', + uplodFileHint: + 'Pan fyddwch yn llwytho dogfennau, gwnewch yn siŵr eich bod yn enwi’r ffeiliau yn glir. Er enghraifft, datganiad-safbwynt.doc. Rhaid i’r ffeiliau fod ar ffurf JPG, BMP, PNG,TIF, PDF, DOC neu DOCX.', + uploadButtonLabel: 'Llwytho ffeil', + filesUploadedLabel: 'Ffeiliau sydd wedi’u llwytho', + noFilesUploaded: "Nid oes ffeiliau wedi'u llwytho", + removeDocumentLabel: 'Dileu', + uploadGuidelinesAccordionLabel: 'Sut i dynnu llun o ddogfen ar eich ffôn a’i lwytho', + uploadGuidelines: [ + 'Rhowch eich dogfen ar rywbeth gwastad mewn ystafell sydd â digon o olau. Defnyddiwch fflach y camera os bydd angen.', + "Tynnwch lun o’r ddogfen gyfan. Dylech allu gweld corneli'r ddogfen.", + 'Gwiriwch eich bod yn gallu gweld yr ysgrifen i gyd, gan gynnwys y llawysgrifen.', + 'Anfonwch y llun trwy e-bost neu sganiwch y ddogfen i’r ddyfais rydych yn ei defnyddio nawr.', + 'Llwythwch y ffeil yma.', + ], + errors: { + c8RefugeDocument: { + empty: 'Mae’n rhaid i chi uwchlwytho dogfen C8', + uploadError: "Nid oedd modd uwchlwytho'r ddogfen", + deleteError: "Nid oedd modd dileu'r ddogfen", + multipleFiles: + 'Gallwch uwchlwytho un ffeil yn unig. Os ydych eisiau uwchlwytho ffeil newydd, dylech ddileu’r ffeil bresennol ac uwchlwytho ffeil newydd', + fileSize: "Mae'r ffeil a uwchlwythwyd gennych yn rhy fawr. Uchafswm maint y ffeil a ganiateir yw 20MB", + fileFormat: + "Mae'r ffeil a uwchlwythwyd gennych yn y fformat anghywir. Uwchlwythwch eich ffeil eto yn y fformat cywir", + }, + }, +}; + +/* eslint-disable @typescript-eslint/ban-types */ +describe('C8 Refuge > upload refuge doc > content', () => { + const commonContent = { + language: 'en', + } as CommonContent; + const userCase = { + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + }, + ], + } as unknown as CaseWithId; + const additionalData = { + req: { + originalUrl: '/c100-rebuild/', + params: { + root: 'c100-rebuild', + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + session: { + userCase, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }, + }, + }; + let generatedContent; + let form; + + describe('c100-rebuild journey', () => { + beforeEach(() => { + generatedContent = generateContent({ ...commonContent, additionalData, userCase }); + form = generatedContent.form as FormContent; + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct english content', () => { + languageAssertions( + 'en', + { ...en, uploadGuidance: interpolate(en.c100uploadGuidance, { name: 'Test Test' }) }, + () => generatedContent + ); + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct welsh content', () => { + languageAssertions( + 'cy', + { ...cy, uploadGuidance: interpolate(cy.c100uploadGuidance, { name: 'Test Test' }) }, + () => generateContent({ ...commonContent, additionalData, userCase, language: 'cy' }) + ); + }); + + test('should contain Continue button', () => { + expect( + (form?.onlyContinue?.text as LanguageLookup)(generatePageContent({ language: 'en' }) as Record) + ).toBe('Continue'); + }); + + test('should contain saveAndComeLater button', () => { + expect( + (form?.saveAndComeLater?.text as LanguageLookup)( + generatePageContent({ language: 'en', additionalData, userCase }) as Record + ) + ).toBe('Save and come back later'); + }); + + test('should contain correct uploadedFiles when file present', () => { + generatedContent = generateContent({ + ...commonContent, + additionalData: { + req: { + ...additionalData.req, + session: { + ...additionalData.req.session, + userCase: { + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL/1234', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }, + }, + }, + }, + userCase, + }); + expect(generatedContent.fileUploadConfig.uploadedFiles).toStrictEqual([ + { + filename: 'MOCK_FILENAME', + fileremoveUrl: '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58/1234?', + }, + ]); + }); + }); + + describe('applicant/respondent journey', () => { + const applicantRespondentAdditionalData = { + req: { + params: {}, + session: { + userCase: {}, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }, + }, + }; + beforeEach(() => { + generatedContent = generateContent({ + ...commonContent, + additionalData: applicantRespondentAdditionalData, + }); + form = generatedContent.form as FormContent; + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct english content', () => { + languageAssertions('en', en, () => generatedContent); + }); + + // eslint-disable-next-line jest/expect-expect + test('should return correct welsh content', () => { + languageAssertions('cy', cy, () => + generateContent({ + ...commonContent, + additionalData: applicantRespondentAdditionalData, + userCase: {}, + language: 'cy', + }) + ); + }); + + test('should contain Continue button', () => { + expect( + (form?.onlyContinue?.text as LanguageLookup)(generatePageContent({ language: 'en' }) as Record) + ).toBe('Continue'); + }); + + test('should contain correct uploadedFiles when file present', () => { + generatedContent = generateContent({ + ...commonContent, + additionalData: { + req: { + ...applicantRespondentAdditionalData.req, + session: { + ...applicantRespondentAdditionalData.req.session, + userCase: { + refugeDocument: { + document_url: 'MOCK_URL/1234', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + }, + }, + }, + userCase, + }); + expect(generatedContent.fileUploadConfig.uploadedFiles).toStrictEqual([ + { + filename: 'MOCK_FILENAME', + fileremoveUrl: '/applicant/refuge/upload-refuge-document/1234?', + }, + ]); + }); + }); + + test('should get document upload errors correctly', () => { + generatedContent = generateContent({ + ...commonContent, + additionalData: { + req: { + ...additionalData.req, + session: { + ...additionalData.req.session, + errors: [{ errorType: 'uploadError', propertyName: 'c8RefugeDocument' }], + }, + }, + }, + userCase, + }); + expect(generatedContent.fileUploadConfig.errorMessage).toBe(en.errors.c8RefugeDocument.uploadError); + }); +}); diff --git a/src/main/steps/common/refuge/upload-refuge-document/content.ts b/src/main/steps/common/refuge/upload-refuge-document/content.ts new file mode 100644 index 0000000000..76f24a3f9f --- /dev/null +++ b/src/main/steps/common/refuge/upload-refuge-document/content.ts @@ -0,0 +1,153 @@ +import _ from 'lodash'; + +import { RootContext } from '../../../../app/case/definition'; +import { TranslationFn } from '../../../../app/controller/GetController'; +import { FormContent } from '../../../../app/form/Form'; +import { getPeople } from '../../../c100-rebuild/child-details/live-with/utils'; +import { getCasePartyType } from '../../../prl-cases/dashboard/utils'; +import { C100_REFUGE_UPLOAD_DOC, C100_URL, REFUGE_UPLOAD_DOC } from '../../../urls'; +import { interpolate } from '../../string-parser'; +import { applyParms } from '../../url-parser'; +import { getC8DocumentForC100 } from '../utils'; +export * from './routeGuard'; + +const en = { + title: 'Upload a C8 form', + uploadFileHeading: 'Upload a document', + uploadGuidance: + 'You can download the form here. Your address, email address and contact number will be kept confidential.', + c100uploadGuidance: + 'You can download the form here. {name}\'s address, email address and contact number will be kept confidential.', + uplodFileHint: + 'When uploading documents, name the files clearly. For example, position-statement.doc. Files must end with JPG, BMP, PNG,TIF, PDF, DOC or DOCX.', + uploadButtonLabel: 'Upload file', + filesUploadedLabel: 'Files uploaded', + noFilesUploaded: 'No files uploaded', + removeDocumentLabel: 'Remove', + uploadGuidelinesAccordionLabel: 'How to take a picture of a document on your phone and upload it', + uploadGuidelines: [ + 'Place your document on a flat service in a well-lit room. Use a flash if you need to.', + 'Take a picture of the whole document. You should be able to see its edges.', + 'Check you can read all the writing, including the handwriting.', + 'Email or send the photo or scan to the device you are using now.', + 'Upload it here.', + ], + errors: { + c8RefugeDocument: { + empty: 'You must upload a C8 document', + uploadError: 'Document could not be uploaded', + deleteError: 'Document could not be deleted', + multipleFiles: + 'You can upload only one file. If you wish to upload a new file, remove the existing file and upload a new one', + fileSize: 'The file you uploaded is too large. Maximum file size allowed is 20MB', + fileFormat: 'The file you uploaded is in the wrong format. Upload your file again in the correct format', + }, + }, +}; + +const cy: typeof en = { + title: 'Uwchlwytho ffurflen C8', + uploadGuidance: + 'Gallwch lawrlwytho\'r ffurflen yma. Bydd eich cyfeiriad, cyfeiriad e-bost a rhif cyswllt yn cael eu cadw\'n gyfrinachol.', + c100uploadGuidance: + 'Gallwch lawrlwytho\'r ffurflen yma. Bydd cyfeiriad, cyfeiriad e-bost a rhif cyswllt {name} yn cael eu cadw’n gyfrinachol.', + uploadFileHeading: 'Llwytho dogfen', + uplodFileHint: + 'Pan fyddwch yn llwytho dogfennau, gwnewch yn siŵr eich bod yn enwi’r ffeiliau yn glir. Er enghraifft, datganiad-safbwynt.doc. Rhaid i’r ffeiliau fod ar ffurf JPG, BMP, PNG,TIF, PDF, DOC neu DOCX.', + uploadButtonLabel: 'Llwytho ffeil', + filesUploadedLabel: 'Ffeiliau sydd wedi’u llwytho', + noFilesUploaded: "Nid oes ffeiliau wedi'u llwytho", + removeDocumentLabel: 'Dileu', + uploadGuidelinesAccordionLabel: 'Sut i dynnu llun o ddogfen ar eich ffôn a’i lwytho', + uploadGuidelines: [ + 'Rhowch eich dogfen ar rywbeth gwastad mewn ystafell sydd â digon o olau. Defnyddiwch fflach y camera os bydd angen.', + "Tynnwch lun o’r ddogfen gyfan. Dylech allu gweld corneli'r ddogfen.", + 'Gwiriwch eich bod yn gallu gweld yr ysgrifen i gyd, gan gynnwys y llawysgrifen.', + 'Anfonwch y llun trwy e-bost neu sganiwch y ddogfen i’r ddyfais rydych yn ei defnyddio nawr.', + 'Llwythwch y ffeil yma.', + ], + errors: { + c8RefugeDocument: { + empty: 'Mae’n rhaid i chi uwchlwytho dogfen C8', + uploadError: "Nid oedd modd uwchlwytho'r ddogfen", + deleteError: "Nid oedd modd dileu'r ddogfen", + multipleFiles: + 'Gallwch uwchlwytho un ffeil yn unig. Os ydych eisiau uwchlwytho ffeil newydd, dylech ddileu’r ffeil bresennol ac uwchlwytho ffeil newydd', + fileSize: "Mae'r ffeil a uwchlwythwyd gennych yn rhy fawr. Uchafswm maint y ffeil a ganiateir yw 20MB", + fileFormat: + "Mae'r ffeil a uwchlwythwyd gennych yn y fformat anghywir. Uwchlwythwch eich ffeil eto yn y fformat cywir", + }, + }, +}; + +export const languages = { + en, + cy, +}; + +export const form: FormContent = { + fields: {}, + onlyContinue: { + text: l => l.onlycontinue, + }, +}; + +export const generateContent: TranslationFn = content => { + const translations = languages[content.language]; + const { session } = content.additionalData?.req ?? {}; + const uploadDocError = session?.errors?.find(error => error.propertyName === 'c8RefugeDocument') ?? null; + let uploadedDocument = session?.userCase?.refugeDocument; + const partyType = getCasePartyType(session.userCase, session.user.id); + const C100rebuildJourney = content.additionalData?.req?.originalUrl?.startsWith(C100_URL); + const id = content.additionalData?.req.params.id + ? content.additionalData?.req.params.id + : content.additionalData?.req.params.removeFileId; + const c100Person = getPeople(session.userCase).find(person => person.id === id); + + delete form.saveAndComeLater; + if (C100rebuildJourney) { + Object.assign(form, { + saveAndComeLater: { + text: l => l.saveAndComeLater, + }, + }); + + uploadedDocument = getC8DocumentForC100(id, session.userCase, c100Person!); + } + + return { + ...translations, + uploadGuidance: C100rebuildJourney + ? interpolate(translations.c100uploadGuidance, { + name: `${c100Person?.firstName} ${c100Person?.lastName}`, + }) + : translations.uploadGuidance, + form, + fileUploadConfig: { + labelText: translations.uploadFileHeading, + hintText: translations.uplodFileHint, + uploadButtonText: translations.uploadButtonLabel, + uploadedFilesCaption: translations.filesUploadedLabel, + noFilesText: translations.noFilesUploaded, + removeFileText: translations.removeDocumentLabel, + errorMessage: uploadDocError ? translations.errors.c8RefugeDocument?.[uploadDocError.errorType] ?? null : null, + uploadedFiles: uploadedDocument?.document_url + ? [ + { + filename: uploadedDocument.document_filename, + fileremoveUrl: C100rebuildJourney + ? applyParms(C100_REFUGE_UPLOAD_DOC, { + root: RootContext.C100_REBUILD, + id, + removeFileId: _.toString(_.last(uploadedDocument.document_url.split('/'))), + }) + : applyParms(REFUGE_UPLOAD_DOC, { + root: partyType, + removeFileId: _.toString(_.last(uploadedDocument.document_url.split('/'))), + }), + }, + ] + : [], + }, + }; +}; diff --git a/src/main/steps/common/refuge/upload-refuge-document/postController.test.ts b/src/main/steps/common/refuge/upload-refuge-document/postController.test.ts new file mode 100644 index 0000000000..d0852b785e --- /dev/null +++ b/src/main/steps/common/refuge/upload-refuge-document/postController.test.ts @@ -0,0 +1,376 @@ +import { mockRequest } from '../../../../../test/unit/utils/mockRequest'; +import { mockResponse } from '../../../../../test/unit/utils/mockResponse'; +import { CaseApi } from '../../../../app/case/C100CaseApi'; +import { CosApiClient } from '../../../../app/case/CosApiClient'; + +import C8RefugeploadDocumentPostController from './postController'; + +const uploadDocumentMock = jest.spyOn(CosApiClient.prototype, 'uploadDocument'); +const saveC100DraftApplicationMock = jest.spyOn(CaseApi.prototype, 'saveC100DraftApplication'); + +describe('C8 Refuge > upload refuge doc > postController', () => { + let controller; + let req; + let res; + + beforeEach(() => { + controller = new C8RefugeploadDocumentPostController({}); + req = mockRequest(); + res = mockResponse(); + }); + + test('should upload document and redirect', async () => { + req.body = { uploadFile: true }; + req.files = { c8RefugeDocument: { name: 'test.jpg', data: '', mimetype: 'text' } }; + req.url = '/applicant/refuge/upload-refuge-document'; + + uploadDocumentMock.mockResolvedValue({ + status: 'Success', + document: { + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }, + }); + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(req.session.userCase.refugeDocument).toStrictEqual({ + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }); + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.errors).toStrictEqual([]); + }); + + test('should upload document and redirect for c100 applicant', async () => { + req.body = { uploadFile: true }; + req.params = { id: '6b792169-84df-4e9a-8299-c2c77c9b7e58' }; + req.session = { + ...req.session, + userCase: { + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + }, + ], + }, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }; + req.files = { c8RefugeDocument: { name: 'test.jpg', data: '', mimetype: 'text' } }; + req.originalUrl = '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58'; + + uploadDocumentMock.mockResolvedValue({ + status: 'Success', + document: { + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }, + }); + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(req.session.userCase.appl_allApplicants[0].refugeConfidentialityC8Form).toStrictEqual({ + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }); + expect(res.redirect).toHaveBeenCalledWith( + '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58' + ); + expect(req.session.errors).toStrictEqual([]); + }); + + test('should upload document and redirect for c100 other person', async () => { + req.body = { uploadFile: true }; + req.params = { id: '6b792169-84df-4e9a-8299-c2c77c9b7e58' }; + req.session = { + ...req.session, + userCase: { + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + }, + ], + }, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }; + req.files = { c8RefugeDocument: { name: 'test.jpg', data: '', mimetype: 'text' } }; + req.originalUrl = '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58'; + + uploadDocumentMock.mockResolvedValue({ + status: 'Success', + document: { + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }, + }); + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(req.session.userCase.oprs_otherPersons[0].refugeConfidentialityC8Form).toStrictEqual({ + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }); + expect(res.redirect).toHaveBeenCalledWith( + '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58' + ); + expect(req.session.errors).toStrictEqual([]); + }); + + test('should not upload document and add error if document already present', async () => { + req.body = { uploadFile: true }; + req.files = { c8RefugeDocument: { name: 'test.jpg', data: '', mimetype: 'text' } }; + req.url = '/applicant/refuge/upload-refuge-document'; + req.session.userCase = { + refugeDocument: { + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }, + }; + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.errors).toStrictEqual([ + { + errorType: 'multipleFiles', + propertyName: 'c8RefugeDocument', + }, + ]); + }); + + test('should not upload document and add error if no file present', async () => { + req.body = { uploadFile: true }; + req.url = '/applicant/refuge/upload-refuge-document'; + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.errors).toStrictEqual([ + { + errorType: 'empty', + propertyName: 'c8RefugeDocument', + }, + ]); + }); + + test('should set error when document upload status is not Success', async () => { + req.body = { uploadFile: true }; + req.url = '/applicant/refuge/upload-refuge-document'; + req.files = { c8RefugeDocument: { name: 'test.jpg', data: '', mimetype: 'text' } }; + + uploadDocumentMock.mockResolvedValue({ + status: 'Failure', + document: { + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }, + }); + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.errors).toStrictEqual([ + { + errorType: 'uploadError', + propertyName: 'c8RefugeDocument', + }, + ]); + }); + + test('should catch error when uploading document', async () => { + req.body = { uploadFile: true }; + req.files = { c8RefugeDocument: { name: 'test.jpg', data: '', mimetype: 'text' } }; + req.url = '/applicant/refuge/upload-refuge-document'; + + uploadDocumentMock.mockRejectedValue({ + status: 'Failure', + }); + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.errors).toStrictEqual([ + { + errorType: 'uploadError', + propertyName: 'c8RefugeDocument', + }, + ]); + }); + + test('should redirect without error when onlyContinue is true and document is present', async () => { + req.body = { onlyContinue: true }; + req.session.userCase = { + refugeDocument: { + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }, + }; + req.url = '/applicant/refuge/upload-refuge-document'; + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/dashboard'); + expect(req.session.errors).toBeUndefined(); + }); + + test('should redirect without error when onlyContinue is true and document is present for c100', async () => { + req.body = { onlyContinue: true }; + req.params = { id: '6b792169-84df-4e9a-8299-c2c77c9b7e58' }; + req.session = { + ...req.session, + userCase: { + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }, + user: { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + }; + req.originalUrl = '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58'; + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/dashboard'); + expect(req.session.errors).toBeUndefined(); + }); + + test('should redirect with error when onlyContinue is true and document is not present', async () => { + req.body = { onlyContinue: true }; + req.url = '/applicant/refuge/upload-refuge-document'; + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.errors).toStrictEqual([ + { + errorType: 'empty', + propertyName: 'c8RefugeDocument', + }, + ]); + }); + + test('should catch error when uploading non allowed document type', async () => { + req.body = { uploadFile: true }; + req.files = { c8RefugeDocument: { name: 'test.rtf', data: '', mimetype: 'text' } }; + req.url = '/applicant/refuge/upload-refuge-document'; + + uploadDocumentMock.mockRejectedValue({ + status: 'Failure', + }); + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.errors).toStrictEqual([ + { + errorType: 'fileFormat', + propertyName: 'c8RefugeDocument', + }, + ]); + }); + + test('should catch error when uploading beyond allowed size document', async () => { + req.body = { uploadFile: true }; + req.files = { c8RefugeDocument: { name: 'test.jpg', data: '', size: '3000000000000000', mimetype: 'text' } }; + req.url = '/applicant/refuge/upload-refuge-document'; + + uploadDocumentMock.mockRejectedValue({ + status: 'Failure', + }); + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.errors).toStrictEqual([ + { + errorType: 'fileSize', + propertyName: 'c8RefugeDocument', + }, + ]); + }); + + test('should redirect without error when saveAndComeLater is true', async () => { + req.body = { saveAndComeLater: true }; + req.session.userCase = { + refugeDocument: { + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }, + }; + req.locals.C100Api.saveC100DraftApplication = jest.fn(); + req.url = '/applicant/refuge/upload-refuge-document'; + req.originalUrl = '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58'; + req.path = '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58'; + saveC100DraftApplicationMock.mockResolvedValue(req.session.userCase); + + await controller.post(req, res); + await new Promise(process.nextTick); + + expect(res.redirect).toHaveBeenCalledWith('/task-list/applicant'); + expect(req.session.userCase.c100RebuildReturnUrl).toBe( + '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58' + ); + expect(req.session.save).toHaveBeenCalled(); + expect(req.session.errors).toStrictEqual([]); + }); +}); diff --git a/src/main/steps/common/refuge/upload-refuge-document/postController.ts b/src/main/steps/common/refuge/upload-refuge-document/postController.ts new file mode 100644 index 0000000000..6efe8a0c87 --- /dev/null +++ b/src/main/steps/common/refuge/upload-refuge-document/postController.ts @@ -0,0 +1,117 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import autobind from 'autobind-decorator'; +import { Response } from 'express'; +import _ from 'lodash'; + +import { CosApiClient } from '../../../../app/case/CosApiClient'; +import { C100Applicant, C100RebuildPartyDetails, PartyType, RootContext } from '../../../../app/case/definition'; +import { AppRequest } from '../../../../app/controller/AppRequest'; +import { AnyObject, PostController } from '../../../../app/controller/PostController'; +import { FormFields, FormFieldsFn } from '../../../../app/form/Form'; +import { getPeople } from '../../../c100-rebuild/child-details/live-with/utils'; +import { getPartyDetails } from '../../../c100-rebuild/people/util'; +import { getCasePartyType } from '../../../prl-cases/dashboard/utils'; +import { C100_REFUGE_UPLOAD_DOC, C100_URL, REFUGE_UPLOAD_DOC } from '../../../urls'; +import { applyParms } from '../../url-parser'; +import { getUploadedDocumentErrorType, handleError, removeErrors } from '../../utils'; +import { getC8DocumentForC100, updateApplicantOtherPersonDetails } from '../utils'; + +@autobind +export default class C8RefugeploadDocumentPostController extends PostController { + constructor(protected readonly fields: FormFields | FormFieldsFn) { + super(fields); + } + + private async uploadDocument(req: AppRequest, res: Response): Promise { + const { session, files } = req; + const { user, userCase: caseData } = session; + const C100rebuildJourney = req?.originalUrl?.startsWith(C100_URL); + const id = req.params.id ? req.params.id : req.params.removeFileId; + const c100Person = getPeople(caseData).find(person => person.id === id); + + req.url = C100rebuildJourney + ? applyParms(C100_REFUGE_UPLOAD_DOC, { + root: RootContext.C100_REBUILD, + id, + }) + : applyParms(REFUGE_UPLOAD_DOC, { + root: getCasePartyType(caseData, req.session.user.id), + }); + + const existingDocument = C100rebuildJourney + ? getC8DocumentForC100(id, caseData, c100Person!) + : caseData?.refugeDocument; + + const errorType = getUploadedDocumentErrorType(existingDocument, 'c8RefugeDocument', files); + if (!_.isEmpty(errorType)) { + req.session.errors = handleError(req.session.errors, errorType, 'c8RefugeDocument'); + return this.redirect(req, res); + } + + try { + const client = new CosApiClient(user.accessToken, req.locals.logger); + const response = await client.uploadDocument(user, { + files: [files?.['c8RefugeDocument']], + }); + + if (response.status !== 'Success') { + req.session.errors = handleError(req.session.errors, 'uploadError', 'c8RefugeDocument', true); + return; + } + + if (C100rebuildJourney) { + const isApplicant = c100Person?.partyType === PartyType.APPLICANT; + const partyDetailsList = isApplicant ? caseData.appl_allApplicants : caseData.oprs_otherPersons; + const partyDetails = getPartyDetails(id, partyDetailsList) as C100Applicant | C100RebuildPartyDetails; + Object.assign(partyDetails, { refugeConfidentialityC8Form: response.document }); + req.session.userCase = updateApplicantOtherPersonDetails( + caseData, + partyDetails, + partyDetailsList!, + isApplicant + ); + } else { + req.session.userCase.refugeDocument = response.document; + } + req.session.errors = removeErrors(req.session.errors); + } catch (e) { + req.session.errors = handleError(req.session.errors, 'uploadError', 'c8RefugeDocument', true); + } finally { + this.redirect(req, res, req.url); + } + } + + private async onContinue(req: AppRequest, res: Response): Promise { + const { userCase: caseData } = req.session; + + const C100rebuildJourney = req?.originalUrl?.startsWith(C100_URL); + const id = req.params.id ? req.params.id : req.params.removeFileId; + const c100Person = getPeople(caseData).find(person => person.id === id); + let uploadedDocument = caseData?.refugeDocument; + + if (C100rebuildJourney) { + uploadedDocument = getC8DocumentForC100(id, caseData, c100Person!); + } + + if (!uploadedDocument?.document_binary_url) { + req.session.errors = handleError(req.session.errors, 'empty', 'c8RefugeDocument', true); + } + + this.redirect(req, res); + } + + public async post(req: AppRequest, res: Response): Promise { + const { onlyContinue, uploadFile, saveAndComeLater } = req.body; + + if (uploadFile) { + this.uploadDocument(req, res); + } else if (onlyContinue) { + this.onContinue(req, res); + } else if (saveAndComeLater) { + super.saveAndComeLater(req, res, { + appl_allApplicants: req.session.userCase.appl_allApplicants, + oprs_otherPersons: req.session.userCase.oprs_otherPersons, + }); + } + } +} diff --git a/src/main/steps/common/refuge/upload-refuge-document/routeGuard.test.ts b/src/main/steps/common/refuge/upload-refuge-document/routeGuard.test.ts new file mode 100644 index 0000000000..7c399e2f2c --- /dev/null +++ b/src/main/steps/common/refuge/upload-refuge-document/routeGuard.test.ts @@ -0,0 +1,124 @@ +import { mockRequest } from '../../../../../test/unit/utils/mockRequest'; +import { mockResponse } from '../../../../../test/unit/utils/mockResponse'; +import { CosApiClient } from '../../../../app/case/CosApiClient'; + +import { routeGuard } from './routeGuard'; + +const deleteDocumentMock = jest.spyOn(CosApiClient.prototype, 'deleteDocument'); + +describe('C8 Refuge > upload refuge doc > routeGuard', () => { + test('Should delete the c8 document if removeFileId is present', async () => { + const req = mockRequest({ + params: { + removeFileId: '1234', + }, + session: { + userCase: { + refugeDocument: { + document_url: 'MOCK_URL/1234', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + }, + }); + const res = mockResponse(); + const next = jest.fn(); + deleteDocumentMock.mockResolvedValueOnce('SUCCESS'); + + await routeGuard.get(req, res, next); + + expect(req.session.save).toHaveBeenCalled(); + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.userCase.refugeDocument).toBe(undefined); + expect(req.session.errors).toStrictEqual([]); + }); + + test('Should delete the c8 document if removeFileId is present for c100 applicant', async () => { + const req = mockRequest({ + params: { + removeFileId: '1234', + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + session: { + userCase: { + appl_allApplicants: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }, + }, + }); + req.originalUrl = '/c100-rebuild'; + const res = mockResponse(); + const next = jest.fn(); + deleteDocumentMock.mockResolvedValueOnce('SUCCESS'); + + await routeGuard.get(req, res, next); + + expect(req.session.save).toHaveBeenCalled(); + expect(res.redirect).toHaveBeenCalledWith( + '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58' + ); + expect(req.session.userCase.appl_allApplicants[0].refugeConfidentialityC8Form).toBe(undefined); + expect(req.session.errors).toStrictEqual([]); + }); + + test('Should delete the c8 document if removeFileId is present for c100 other person', async () => { + const req = mockRequest({ + params: { + removeFileId: '1234', + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + }, + session: { + userCase: { + oprs_otherPersons: [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + applicantFirstName: 'Test', + applicantLastName: 'Test', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ], + }, + }, + }); + req.originalUrl = '/c100-rebuild'; + const res = mockResponse(); + const next = jest.fn(); + deleteDocumentMock.mockResolvedValueOnce('SUCCESS'); + + await routeGuard.get(req, res, next); + + expect(req.session.save).toHaveBeenCalled(); + expect(res.redirect).toHaveBeenCalledWith( + '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58' + ); + expect(req.session.userCase.oprs_otherPersons[0].refugeConfidentialityC8Form).toBe(undefined); + expect(req.session.errors).toStrictEqual([]); + }); + + test('Should call next if removeFileId is not present', async () => { + const req = mockRequest({ + session: { + userCase: {}, + }, + }); + const res = mockResponse(); + const next = jest.fn(); + routeGuard.get(req, res, next); + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/src/main/steps/common/refuge/upload-refuge-document/routeGuard.ts b/src/main/steps/common/refuge/upload-refuge-document/routeGuard.ts new file mode 100644 index 0000000000..d0c01ab49c --- /dev/null +++ b/src/main/steps/common/refuge/upload-refuge-document/routeGuard.ts @@ -0,0 +1,23 @@ +import { NextFunction, Response } from 'express'; + +import { AppRequest } from '../../../../app/controller/AppRequest'; +import { deleteDocument } from '../utils'; + +export const routeGuard = { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + get: async (req: AppRequest, res: Response, next: NextFunction) => { + const userCase = req.session.userCase; + if ( + req.params?.removeFileId && + !( + userCase.appl_allApplicants?.find(applicant => applicant.id === req.params?.removeFileId) || + userCase.oprs_otherPersons?.find(person => person.id === req.params?.removeFileId) + ) + ) { + await deleteDocument(req, res, req.params?.removeFileId, req.params?.id); + return; + } + + next(); + }, +}; diff --git a/src/main/steps/common/refuge/upload-refuge-document/template.njk b/src/main/steps/common/refuge/upload-refuge-document/template.njk new file mode 100644 index 0000000000..fb8d9ebb58 --- /dev/null +++ b/src/main/steps/common/refuge/upload-refuge-document/template.njk @@ -0,0 +1,45 @@ +{% from "govuk/components/details/macro.njk" import govukDetails %} +{% extends "common/template.njk" %} +{% from "common/fileupload/macro-v1.njk" import fileUpload with context %} + +{% block page_content %} +

+ {{title}} +

+ +

{{uploadGuidance | safe}}

+ {% if fileUploadConfig.errorMessage != undefined %} +
+ {% endif %} + + {{ fileUpload({ + name: "c8RefugeDocument", + uploadButtonText: fileUploadConfig.uploadButtonText, + labelText: fileUploadConfig.labelText, + hintText: fileUploadConfig.hintText, + allowedFileTypes: "image/jpg,image/jpeg,application/rtf,image/bmp,image/png,image/tiff,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document", + uploadUrl: "", + uploadedFiles: fileUploadConfig.uploadedFiles, + errorMessage: fileUploadConfig.errorMessage, + uploadedFilesCaption: fileUploadConfig.uploadedFilesCaption, + noFilesText: fileUploadConfig.noFilesText, + removeFileText: fileUploadConfig.removeFileText, + token: csrfToken + }) }} + + {% if fileUploadConfig.errorMessage != undefined %} +
+ {% endif %} + + {% set uploadGuidelinesList = ""%} + {% for item in uploadGuidelines %} + {% set uploadGuidelinesList = uploadGuidelinesList + "
  • " + item + "
  • "%} + {% endfor %} + {% set uploadGuidelinesText = "
      " + uploadGuidelinesList + "
    " %} + + {{ govukDetails({ + summaryText: uploadGuidelinesAccordionLabel, + text: uploadGuidelinesText | safe +}) }} + +{% endblock %} \ No newline at end of file diff --git a/src/main/steps/common/refuge/utils.test.ts b/src/main/steps/common/refuge/utils.test.ts new file mode 100644 index 0000000000..3463e95d86 --- /dev/null +++ b/src/main/steps/common/refuge/utils.test.ts @@ -0,0 +1,407 @@ +import { mockRequest } from '../../../../test/unit/utils/mockRequest'; +import { mockResponse } from '../../../../test/unit/utils/mockResponse'; +import { CosApiClient } from '../../../app/case/CosApiClient'; +import { C100Applicant, C100RebuildPartyDetails, PartyType } from '../../../app/case/definition'; +import { People } from '../../c100-rebuild/people/util'; + +import { deleteC100RefugeDoc, deleteDocument, getC8DocumentForC100, updateApplicantOtherPersonDetails } from './utils'; + +const deleteDocumentMock = jest.spyOn(CosApiClient.prototype, 'deleteDocument'); + +describe('C8 refuge > utils', () => { + describe('deleteDocument', () => { + let req; + let res; + + beforeEach(() => { + req = mockRequest(); + res = mockResponse(); + }); + + test('should delete document with no errors', async () => { + req.params = { removeFileId: '1234' }; + req.session.userCase.refugeDocument = { + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + document_creation_date: '1/1/2024', + }; + deleteDocumentMock.mockResolvedValue('SUCCESS'); + + await deleteDocument(req, res, '1234'); + + expect(req.session.save).toHaveBeenCalled(); + expect(res.redirect).toHaveBeenCalledWith('/applicant/refuge/upload-refuge-document'); + expect(req.session.userCase.refugeDocument).toBe(undefined); + expect(req.session.errors).toStrictEqual([]); + }); + + test('should delete document with no errors for c100 applicant', async () => { + req.params = { id: '7483640e-0817-4ddc-b709-6723f7925474', removeFileId: '1234' }; + req.originalUrl = '/c100-rebuild'; + req.session.userCase.appl_allApplicants = [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]; + deleteDocumentMock.mockResolvedValue('SUCCESS'); + + await deleteDocument(req, res, '1234', '7483640e-0817-4ddc-b709-6723f7925474'); + + expect(req.session.save).toHaveBeenCalled(); + expect(res.redirect).toHaveBeenCalledWith( + '/c100-rebuild/refuge/upload-refuge-document/7483640e-0817-4ddc-b709-6723f7925474' + ); + expect(req.session.userCase.appl_allApplicants[0].refugeConfidentialityC8Form).toBe(undefined); + expect(req.session.errors).toStrictEqual([]); + }); + + test('should delete document with no errors for c100 other person', async () => { + req.params = { id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', removeFileId: '1234' }; + req.originalUrl = '/c100-rebuild'; + req.session.userCase.oprs_otherPersons = [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]; + deleteDocumentMock.mockResolvedValue('SUCCESS'); + + await deleteDocument(req, res, '1234', '6b792169-84df-4e9a-8299-c2c77c9b7e58'); + + expect(req.session.save).toHaveBeenCalled(); + expect(res.redirect).toHaveBeenCalledWith( + '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58' + ); + expect(req.session.userCase.oprs_otherPersons[0].refugeConfidentialityC8Form).toBe(undefined); + expect(req.session.errors).toStrictEqual([]); + }); + + test('should catch error when deleting document', async () => { + req.params = { id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', removeFileId: '1234' }; + req.originalUrl = '/c100-rebuild'; + req.session.userCase.oprs_otherPersons = [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]; + deleteDocumentMock.mockRejectedValue('Failure'); + + await deleteDocument(req, res, '1234', '6b792169-84df-4e9a-8299-c2c77c9b7e58'); + + expect(req.session.save).toHaveBeenCalled(); + expect(res.redirect).toHaveBeenCalledWith( + '/c100-rebuild/refuge/upload-refuge-document/6b792169-84df-4e9a-8299-c2c77c9b7e58' + ); + expect(req.session.userCase.oprs_otherPersons).toStrictEqual([ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]); + expect(req.session.errors).toStrictEqual([ + { + errorType: 'deleteError', + propertyName: 'c8RefugeDocument', + }, + ]); + }); + }); + + describe('deleteC100RefugeDoc', () => { + let req; + + beforeEach(() => { + req = mockRequest(); + }); + + test('should delete document for c100 applicant', () => { + req.session.userCase.appl_allApplicants = [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]; + deleteC100RefugeDoc(req, req.session.userCase, '7483640e-0817-4ddc-b709-6723f7925474'); + expect(req.session.userCase.appl_allApplicants[0].refugeConfidentialityC8Form).toBe(undefined); + }); + + test('should delete document for c100 other person', () => { + req.session.userCase.oprs_otherPersons = [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]; + deleteC100RefugeDoc(req, req.session.userCase, '6b792169-84df-4e9a-8299-c2c77c9b7e58'); + expect(req.session.userCase.oprs_otherPersons[0].refugeConfidentialityC8Form).toBe(undefined); + }); + }); + + describe('getC8DocumentForC100', () => { + let req; + + beforeEach(() => { + req = mockRequest(); + }); + + test('should get c8 document for applicant', () => { + req.session.userCase.appl_allApplicants = [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]; + + expect( + getC8DocumentForC100('7483640e-0817-4ddc-b709-6723f7925474', req.session.userCase, { + id: '7483640e-0817-4ddc-b709-6723f7925474', + firstName: 'dummy', + lastName: 'Test', + partyType: PartyType.APPLICANT, + }) + ).toStrictEqual({ + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }); + }); + + test('should get c8 document for other person', () => { + req.session.userCase.oprs_otherPersons = [ + { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'Test', + lastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]; + + expect( + getC8DocumentForC100('6b792169-84df-4e9a-8299-c2c77c9b7e58', req.session.userCase, { + id: '6b792169-84df-4e9a-8299-c2c77c9b7e58', + firstName: 'dummy', + lastName: 'Test', + partyType: PartyType.OTHER_PERSON, + }) + ).toStrictEqual({ + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }); + }); + }); + + describe('updateApplicantOtherPersonDetails', () => { + let req; + + beforeEach(() => { + req = mockRequest(); + }); + + test('should update applicant details', () => { + req.session.userCase.appl_allApplicants = [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]; + + expect( + updateApplicantOtherPersonDetails( + req.session.userCase, + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'No', + } as unknown as C100Applicant, + [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + { + id: '7483640e-0817-4ddc-b709-6723f7925475', + applicantFirstName: 'dummy', + applicantLastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ] as unknown as People[], + true + ) + ).toStrictEqual({ + appl_allApplicants: [ + { + applicantFirstName: 'dummy', + applicantLastName: 'Test', + id: '7483640e-0817-4ddc-b709-6723f7925474', + liveInRefuge: 'No', + }, + { + applicantFirstName: 'dummy', + applicantLastName: 'Test', + id: '7483640e-0817-4ddc-b709-6723f7925475', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + document_url: 'MOCK_URL', + }, + }, + ], + id: '1234', + }); + }); + + test('should update other person details details', () => { + req.session.userCase.oprs_otherPersons = [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + firstName: 'dummy', + lastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ]; + + expect( + updateApplicantOtherPersonDetails( + req.session.userCase, + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + firstName: 'dummy', + lastName: 'Test', + liveInRefuge: 'No', + } as unknown as C100RebuildPartyDetails, + [ + { + id: '7483640e-0817-4ddc-b709-6723f7925474', + firstName: 'dummy', + lastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + { + id: '7483640e-0817-4ddc-b709-6723f7925475', + firstName: 'dummy', + lastName: 'Test', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + ] as unknown as People[], + false + ) + ).toStrictEqual({ + oprs_otherPersons: [ + { + firstName: 'dummy', + lastName: 'Test', + id: '7483640e-0817-4ddc-b709-6723f7925474', + liveInRefuge: 'No', + }, + { + firstName: 'dummy', + lastName: 'Test', + id: '7483640e-0817-4ddc-b709-6723f7925475', + liveInRefuge: 'Yes', + refugeConfidentialityC8Form: { + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + document_url: 'MOCK_URL', + }, + }, + ], + id: '1234', + }); + }); + }); +}); diff --git a/src/main/steps/common/refuge/utils.ts b/src/main/steps/common/refuge/utils.ts new file mode 100644 index 0000000000..dc266dea37 --- /dev/null +++ b/src/main/steps/common/refuge/utils.ts @@ -0,0 +1,93 @@ +import { Response } from 'express'; + +import { CosApiClient } from '../../../app/case/CosApiClient'; +import { CaseWithId } from '../../../app/case/case'; +import { + C100Applicant, + C100RebuildPartyDetails, + Document, + PartyType, + People, + RootContext, +} from '../../../app/case/definition'; +import { AppRequest } from '../../../app/controller/AppRequest'; +import { getPeople } from '../../../steps/c100-rebuild/child-details/live-with/utils'; +import { People as CombinedPeople, getPartyDetails, updatePartyDetails } from '../../../steps/c100-rebuild/people/util'; +import { getCasePartyType } from '../../prl-cases/dashboard/utils'; +import { C100_REFUGE_UPLOAD_DOC, C100_URL, REFUGE_UPLOAD_DOC } from '../../urls'; +import { applyParms } from '../url-parser'; +import { handleError, removeErrors } from '../utils'; + +export const deleteDocument = async ( + req: AppRequest, + res: Response, + removeFileId: string, + id?: string +): Promise => { + const { session } = req; + const { user: userDetails, userCase: caseData } = session; + const partyType = getCasePartyType(caseData, userDetails.id); + const client = new CosApiClient(userDetails.accessToken, req.locals.logger); + const C100rebuildJourney = req?.originalUrl?.startsWith(C100_URL); + + try { + if (C100rebuildJourney) { + await client.deleteDocument(removeFileId); + deleteC100RefugeDoc(req, caseData, id!); + } else if (req.session.userCase.hasOwnProperty('refugeDocument')) { + delete req.session.userCase.refugeDocument; + } + + req.session.errors = removeErrors(req.session.errors); + } catch (e) { + req.session.errors = handleError(req.session.errors, 'deleteError', 'c8RefugeDocument', true); + } finally { + if (!req.url.includes('checkanswers')) { + const redirectUrl = C100rebuildJourney + ? applyParms(C100_REFUGE_UPLOAD_DOC, { + root: RootContext.C100_REBUILD, + id: id!, + }) + : applyParms(REFUGE_UPLOAD_DOC, { + root: partyType, + }); + + req.session.save(() => { + res.redirect(redirectUrl); + }); + } + } +}; + +export const deleteC100RefugeDoc = (req: AppRequest, caseData: CaseWithId, id: string): void => { + const c100Person = getPeople(caseData).find(person => person.id === id); + const isApplicant = c100Person?.partyType === PartyType.APPLICANT; + const partyDetailsList = isApplicant ? caseData.appl_allApplicants : caseData.oprs_otherPersons; + const partyDetails = getPartyDetails(id, partyDetailsList) as C100Applicant | C100RebuildPartyDetails; + + if (partyDetails.hasOwnProperty('refugeConfidentialityC8Form')) { + delete partyDetails.refugeConfidentialityC8Form; + + req.session.userCase = updateApplicantOtherPersonDetails(caseData, partyDetails, partyDetailsList!, isApplicant); + } +}; + +export const updateApplicantOtherPersonDetails = ( + caseData: CaseWithId, + partyDetails: C100Applicant | C100RebuildPartyDetails, + partyDetailsList: CombinedPeople[], + isApplicant: boolean +): CaseWithId => { + if (isApplicant) { + caseData.appl_allApplicants = updatePartyDetails(partyDetails, partyDetailsList) as C100Applicant[]; + } else { + caseData.oprs_otherPersons = updatePartyDetails(partyDetails, partyDetailsList) as C100RebuildPartyDetails[]; + } + return caseData; +}; + +export const getC8DocumentForC100 = (id: string, caseData: CaseWithId, person: People): Document | undefined => { + return person.partyType === PartyType.APPLICANT + ? (getPartyDetails(id, caseData.appl_allApplicants) as C100Applicant).refugeConfidentialityC8Form + : (getPartyDetails(id, caseData.oprs_otherPersons) as C100RebuildPartyDetails).refugeConfidentialityC8Form; +}; diff --git a/src/main/steps/common/safety-concerns/review/content.test.ts b/src/main/steps/common/safety-concerns/review/content.test.ts index 6a114ef478..51ee5148df 100644 --- a/src/main/steps/common/safety-concerns/review/content.test.ts +++ b/src/main/steps/common/safety-concerns/review/content.test.ts @@ -229,6 +229,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/concerns-for-safety', text: 'Edit', visuallyHiddenText: 'Do you have any concerns for your safety or the safety of the children?', + attributes: {}, }, ], }, @@ -256,6 +257,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/concerns-for-safety', text: 'Edit', visuallyHiddenText: 'Do you have any concerns for your safety or the safety of the children?', + attributes: {}, }, ], }, @@ -273,6 +275,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/concern-about', text: 'Edit', visuallyHiddenText: 'Who are you concerned about?', + attributes: {}, }, ], }, @@ -296,6 +299,7 @@ describe('Content.ts test cases', () => { text: 'Edit', visuallyHiddenText: 'What type of behaviour have the children experienced or are at risk of experiencing?', + attributes: {}, }, ], }, @@ -318,6 +322,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/other-concerns/drugs', text: 'Edit', visuallyHiddenText: 'Have the children been impacted by drug, alcohol or substance abuse?', + attributes: {}, }, ], }, @@ -335,6 +340,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/other-concerns/other-issues', text: 'Edit', visuallyHiddenText: 'Do you have any other concerns about the children’s safety and wellbeing?', + attributes: {}, }, ], }, @@ -352,6 +358,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/orders-required/court-action', text: 'Edit', visuallyHiddenText: 'What do you want the court to do to keep you and the children safe?', + attributes: {}, }, ], }, @@ -368,6 +375,7 @@ describe('Content.ts test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you agree to the children spending time with the other people in this application?', + attributes: {}, }, ], }, @@ -384,6 +392,7 @@ describe('Content.ts test cases', () => { text: 'Edit', visuallyHiddenText: 'Do you agree to the other people in this application being in touch with the children in other ways?', + attributes: {}, }, ], }, @@ -425,6 +434,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/concerns-for-safety', text: 'Golygu', visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -450,6 +460,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/concerns-for-safety', text: 'Golygu', visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -465,6 +476,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/concern-about', text: 'Golygu', visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -485,6 +497,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/child/concerns-about', text: 'Golygu', visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -505,6 +518,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/other-concerns/drugs', text: 'Golygu', visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -520,6 +534,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/other-concerns/other-issues', text: 'Golygu', visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -535,6 +550,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/orders-required/court-action', text: 'Golygu', visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -548,6 +564,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/orders-required/unsupervised', text: 'Golygu', visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -561,6 +578,7 @@ describe('Content.ts test cases', () => { href: '/respondent/safety-concerns/orders-required/unsupervised', text: 'Golygu', visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, diff --git a/src/main/steps/common/statement-of-service/upload/postController.ts b/src/main/steps/common/statement-of-service/upload/postController.ts index 8a0ce6bb19..b916f955bd 100644 --- a/src/main/steps/common/statement-of-service/upload/postController.ts +++ b/src/main/steps/common/statement-of-service/upload/postController.ts @@ -1,16 +1,16 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import autobind from 'autobind-decorator'; import { Response } from 'express'; +import _ from 'lodash'; import { CosApiClient } from '../../../../app/case/CosApiClient'; import { PartyType } from '../../../../app/case/definition'; import { AppRequest } from '../../../../app/controller/AppRequest'; import { AnyObject, PostController } from '../../../../app/controller/PostController'; import { FormFields, FormFieldsFn } from '../../../../app/form/Form'; -import { isFileSizeGreaterThanMaxAllowed, isValidFileFormat } from '../../../../app/form/validation'; import { applyParms } from '../../../../steps/common/url-parser'; import { UPLOAD_STATEMENT_OF_SERVICE } from '../../../../steps/urls'; -import { handleError, removeErrors } from '../utils'; +import { getUploadedDocumentErrorType, handleError, removeErrors } from '../../utils'; @autobind export default class SOSUploadDocumentPostController extends PostController { @@ -24,40 +24,27 @@ export default class SOSUploadDocumentPostController extends PostController utils', () => { - describe('removeErrors', () => { - test('should not return statementOfServiceDoc errors', () => { - expect( - removeErrors([ - { - errorType: 'multipleFiles', - propertyName: 'statementOfServiceDoc', - }, - { - errorType: 'uploadError', - propertyName: 'statementOfServiceDoc', - }, - { errorType: 'required', propertyName: 'needsResolution' }, - ]) - ).toStrictEqual([{ errorType: 'required', propertyName: 'needsResolution' }]); - }); - }); - - describe('handleError', () => { - test('should return existing statementOfServiceDoc and add new error if omitOtherErrors false', () => { - expect( - handleError([{ errorType: 'uploadError', propertyName: 'statementOfServiceDoc' }], 'multipleFiles', false) - ).toStrictEqual([ - { errorType: 'uploadError', propertyName: 'statementOfServiceDoc' }, - { - errorType: 'multipleFiles', - propertyName: 'statementOfServiceDoc', - }, - ]); - }); - - test('should remmove existing statementOfServiceDoc errors and add new error if omitOtherErrors true', () => { - expect( - handleError([{ errorType: 'uploadError', propertyName: 'statementOfServiceDoc' }], 'multipleFiles', true) - ).toStrictEqual([ - { - errorType: 'multipleFiles', - propertyName: 'statementOfServiceDoc', - }, - ]); - }); - }); - describe('deleteDocument', () => { let req; let res; diff --git a/src/main/steps/common/statement-of-service/utils.ts b/src/main/steps/common/statement-of-service/utils.ts index c846895414..8bdeb2063e 100644 --- a/src/main/steps/common/statement-of-service/utils.ts +++ b/src/main/steps/common/statement-of-service/utils.ts @@ -7,29 +7,11 @@ import { CosApiClient } from '../../../app/case/CosApiClient'; import { CaseWithId } from '../../../app/case/case'; import { PartyType } from '../../../app/case/definition'; import { AppRequest } from '../../../app/controller/AppRequest'; -import { FormError } from '../../../app/form/Form'; import { GovUkNunjucksSummary } from '../../../steps/c100-rebuild/check-your-answers/lib/lib'; import { getCasePartyType } from '../../../steps/prl-cases/dashboard/utils'; import { STATEMENT_OF_SERVICE_WHO_WAS_SERVED, UPLOAD_STATEMENT_OF_SERVICE } from '../../../steps/urls'; import { applyParms } from '../url-parser'; - -export const removeErrors = (errors: FormError[] | undefined): FormError[] => { - return errors?.length ? errors.filter(error => error.propertyName !== 'statementOfServiceDoc') : []; -}; - -export const handleError = ( - errors: FormError[] | undefined, - errorType: string, - omitOtherErrors?: boolean -): FormError[] => { - let _errors: FormError[] = errors?.length ? errors : []; - - if (omitOtherErrors) { - _errors = [...removeErrors(_errors)]; - } - - return [..._errors, { errorType, propertyName: 'statementOfServiceDoc' }]; -}; +import { handleError, removeErrors } from '../utils'; export const deleteDocument = async (req: AppRequest, res: Response): Promise => { const { params, session } = req; @@ -46,7 +28,7 @@ export const deleteDocument = async (req: AppRequest, res: Response): Promise { res.redirect( diff --git a/src/main/steps/common/summary/utils.test.ts b/src/main/steps/common/summary/utils.test.ts index 8693bad5a3..73b8cb9337 100644 --- a/src/main/steps/common/summary/utils.test.ts +++ b/src/main/steps/common/summary/utils.test.ts @@ -60,6 +60,9 @@ describe('common > summary > utils', () => { href: '/tasklistresponse/miam/miam-start', text: 'Edit', visuallyHiddenText: 'What is a Mediation Information and Assessment Meeting (MIAM)?', + attributes: { + id: 'miamStart', + }, }, ], }, @@ -77,6 +80,9 @@ describe('common > summary > utils', () => { href: '/tasklistresponse/consent-to-application', text: 'Edit', visuallyHiddenText: 'When did you receive the application?', + attributes: { + id: 'applicationReceivedDate', + }, }, ], }, @@ -94,6 +100,9 @@ describe('common > summary > utils', () => { href: '/tasklistresponse/consent-to-application', text: 'Edit', visuallyHiddenText: 'When did you receive the application?', + attributes: { + id: 'invalidApplicationReceivedDate', + }, }, ], }, diff --git a/src/main/steps/common/summary/utils.ts b/src/main/steps/common/summary/utils.ts index 0e35b4b32c..b5329cfa4a 100644 --- a/src/main/steps/common/summary/utils.ts +++ b/src/main/steps/common/summary/utils.ts @@ -40,6 +40,11 @@ export const getSectionSummaryList = ( href: changeUrl, text: language === 'en' ? en.edit : cy.edit, visuallyHiddenText: `${item.key}`, + attributes: item.anchorReference + ? { + id: item.anchorReference, + } + : {}, }, ], }, @@ -92,6 +97,15 @@ const setkey = (userCase: Partial, key: string, language: string | u ); } break; + case 'citizenUserLivingInRefugeText': + if (!userCase.isCitizenLivingInRefuge) { + return userCase.citizenUserLivingInRefugeText; + } else { + translationLabel = 'ydwTranslation'; + } + break; + case 'refugeDocumentText': + return userCase.refugeDocumentText; default: return userkey; } @@ -112,6 +126,7 @@ export const summaryList = ( const keyLabel = keys[key]; const row = { key: keyLabel, + anchorReference: key, value: userCase[key]?.hasOwnProperty('day') && userCase[key].hasOwnProperty('month') && @@ -120,7 +135,7 @@ export const summaryList = ( : setkey(userCase, key, language)!, changeUrl: urls[key], }; - if (row.value || key === 'citizenUserAddressHistory') { + if (row.value || key === 'citizenUserAddressHistory' || key === 'isCitizenLivingInRefuge') { if (key !== 'citizenUserSafeToCall') { summaryData.push(row); } diff --git a/src/main/steps/common/task-list/components/tasklist/index.test.ts b/src/main/steps/common/task-list/components/tasklist/index.test.ts index 1c917bf75c..16fb06eeda 100644 --- a/src/main/steps/common/task-list/components/tasklist/index.test.ts +++ b/src/main/steps/common/task-list/components/tasklist/index.test.ts @@ -265,6 +265,7 @@ describe('tasklist index', () => { otherPersonRelationshipToChildren: [], isAtAddressLessThan5YearsWithDontKnow: 'string', response: {}, + liveInRefuge: 'No', user: { email: 'string', idamId: '123', diff --git a/src/main/steps/common/task-list/components/tasklist/utils.ts b/src/main/steps/common/task-list/components/tasklist/utils.ts index 4999ee4768..ad53c4974b 100644 --- a/src/main/steps/common/task-list/components/tasklist/utils.ts +++ b/src/main/steps/common/task-list/components/tasklist/utils.ts @@ -135,8 +135,14 @@ export const getConfirmOrEditYourContactDetailsStatus = ( partyDetails.phoneNumber, partyDetails.email, partyDetails.dateOfBirth, + partyDetails.liveInRefuge, + partyDetails.refugeConfidentialityC8Form?.document_filename, ]; + if (partyDetails.liveInRefuge === YesOrNo.NO) { + summaryField.splice(8, 1); + } + if (caseData?.caseTypeOfApplication === CaseType.FL401) { summaryField.splice(2, 1); } diff --git a/src/main/steps/common/utils.test.ts b/src/main/steps/common/utils.test.ts new file mode 100644 index 0000000000..d5f3539766 --- /dev/null +++ b/src/main/steps/common/utils.test.ts @@ -0,0 +1,75 @@ +import { getUploadedDocumentErrorType, handleError, removeErrors } from './utils'; + +describe('removeErrors', () => { + test('should not return statementOfServiceDoc errors', () => { + expect( + removeErrors([ + { + errorType: 'multipleFiles', + propertyName: 'statementOfServiceDoc', + }, + { + errorType: 'uploadError', + propertyName: 'statementOfServiceDoc', + }, + { errorType: 'required', propertyName: 'needsResolution' }, + ]) + ).toStrictEqual([{ errorType: 'required', propertyName: 'needsResolution' }]); + }); +}); + +describe('handleError', () => { + test('should return existing statementOfServiceDoc and add new error if omitOtherErrors false', () => { + expect( + handleError( + [{ errorType: 'uploadError', propertyName: 'statementOfServiceDoc' }], + 'multipleFiles', + 'statementOfServiceDoc', + false + ) + ).toStrictEqual([ + { errorType: 'uploadError', propertyName: 'statementOfServiceDoc' }, + { + errorType: 'multipleFiles', + propertyName: 'statementOfServiceDoc', + }, + ]); + }); + + test('should remmove existing statementOfServiceDoc errors and add new error if omitOtherErrors true', () => { + expect( + handleError( + [{ errorType: 'uploadError', propertyName: 'statementOfServiceDoc' }], + 'multipleFiles', + 'statementOfServiceDoc', + true + ) + ).toStrictEqual([ + { + errorType: 'multipleFiles', + propertyName: 'statementOfServiceDoc', + }, + ]); + }); + + describe('getUploadedDocumentErrorType', () => { + test('should return correct errorType for multiple files', () => { + expect( + getUploadedDocumentErrorType( + { + document_url: 'test2/1234', + document_binary_url: 'binary/test2/1234', + document_filename: 'test_document_2', + document_hash: '1234', + }, + 'statementOfServiceDoc', + [] + ) + ).toBe('multipleFiles'); + }); + + test('should return correct errorType for no files', () => { + expect(getUploadedDocumentErrorType(undefined, 'statementOfServiceDoc', undefined)).toBe('empty'); + }); + }); +}); diff --git a/src/main/steps/common/utils.ts b/src/main/steps/common/utils.ts new file mode 100644 index 0000000000..6b974c3fc7 --- /dev/null +++ b/src/main/steps/common/utils.ts @@ -0,0 +1,50 @@ +import { Document } from '../../app/case/definition'; +import { FormError } from '../../app/form/Form'; +import { isFileSizeGreaterThanMaxAllowed, isValidFileFormat } from '../../app/form/validation'; + +export const removeErrors = (errors: FormError[] | undefined): FormError[] => { + return errors?.length + ? errors.filter( + error => error.propertyName !== 'statementOfServiceDoc' && error.propertyName !== 'c8RefugeDocument' + ) + : []; +}; + +export const handleError = ( + errors: FormError[] | undefined, + errorType: string, + propertyName: string, + omitOtherErrors?: boolean +): FormError[] => { + let _errors: FormError[] = errors?.length ? errors : []; + + if (omitOtherErrors) { + _errors = [...removeErrors(_errors)]; + } + + return [..._errors, { errorType, propertyName }]; +}; + +export const getUploadedDocumentErrorType = ( + uploadedDocument: Document | undefined, + errorPropertyName: string, + files: + | { + [fieldname: string]: Express.Multer.File[]; + } + | Express.Multer.File[] + | undefined +): string => { + let errorType; + if (uploadedDocument?.document_binary_url) { + errorType = 'multipleFiles'; + } else if (!files) { + errorType = 'empty'; + } else if (!isValidFileFormat({ documents: files[errorPropertyName] })) { + errorType = 'fileFormat'; + } else if (isFileSizeGreaterThanMaxAllowed({ documents: files[errorPropertyName] })) { + errorType = 'fileSize'; + } + + return errorType; +}; diff --git a/src/main/steps/index.ts b/src/main/steps/index.ts index 3cfe322601..df01521e56 100644 --- a/src/main/steps/index.ts +++ b/src/main/steps/index.ts @@ -13,6 +13,7 @@ import { RASequence } from '../modules/reasonable-adjustments/sequence'; import { applicantCaseSequence } from './applicant/applicantCaseSequence'; import { applicationWithinProceedingsSequence } from './applicationWithinProceedingsSequence'; import { C100Sequence } from './c100-rebuild/c100sequence'; +import { C8RefugeSequence } from './common/refuge/sequence'; import { AohSequence } from './common/safety-concerns/sequence'; import { parseUrl } from './common/url-parser'; import { Step } from './constants'; @@ -55,6 +56,7 @@ export const getNextStepUrl = (req: AppRequest, data: Partial): string => ...applicationWithinProceedingsSequence, ...AohSequence.getSequence(), ...RASequence.getSequence(), + ...C8RefugeSequence.getSequence(), ].find(s => s.url === path); const url = nextStep ? nextStep.getNextStep(data, req) : DASHBOARD_URL; const { path: urlPath, queryString: urlQueryStr } = getPathAndQueryStringFromUrl(url); diff --git a/src/main/steps/respondent/confirm-contact-details/checkanswers/content.test.ts b/src/main/steps/respondent/confirm-contact-details/checkanswers/content.test.ts index a6dfb4c152..a3e095fae3 100644 --- a/src/main/steps/respondent/confirm-contact-details/checkanswers/content.test.ts +++ b/src/main/steps/respondent/confirm-contact-details/checkanswers/content.test.ts @@ -11,6 +11,8 @@ const en = { }, keys: { citizenUserFullName: 'Name', + citizenUserLivingInRefugeText: 'Living in refuge', + refugeDocumentText: 'C8 refuge document', citizenUserDateOfBirthText: 'Date of birth', citizenUserPlaceOfBirthText: 'Place of birth', citizenUserAddressText: 'Address', diff --git a/src/main/steps/tasklistresponse/proceedings/mainUtils.test.ts b/src/main/steps/tasklistresponse/proceedings/mainUtils.test.ts index 953b83a8bc..e8f2af3ac1 100644 --- a/src/main/steps/tasklistresponse/proceedings/mainUtils.test.ts +++ b/src/main/steps/tasklistresponse/proceedings/mainUtils.test.ts @@ -58,6 +58,7 @@ describe('test cases for main util', () => { href: '/tasklistresponse/proceedings/start', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -71,6 +72,7 @@ describe('test cases for main util', () => { href: '/tasklistresponse/proceedings/start', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, @@ -84,6 +86,7 @@ describe('test cases for main util', () => { href: '/tasklistresponse/proceedings/courtproceedings', text: undefined, visuallyHiddenText: 'undefined', + attributes: {}, }, ], }, diff --git a/src/main/steps/tasklistresponse/start/tasklist.test.ts b/src/main/steps/tasklistresponse/start/tasklist.test.ts index 311c6f3ac0..4a09b848a7 100644 --- a/src/main/steps/tasklistresponse/start/tasklist.test.ts +++ b/src/main/steps/tasklistresponse/start/tasklist.test.ts @@ -124,6 +124,7 @@ describe('generateRespondentTaskList', () => { otherPersonRelationshipToChildren: [], isAtAddressLessThan5YearsWithDontKnow: '', partyId: '1234', + liveInRefuge: '' as YesOrNo, }, }, ]; diff --git a/src/main/steps/tasklistresponse/start/utils.test.ts b/src/main/steps/tasklistresponse/start/utils.test.ts index 67d817a93a..bb9ac125c7 100644 --- a/src/main/steps/tasklistresponse/start/utils.test.ts +++ b/src/main/steps/tasklistresponse/start/utils.test.ts @@ -60,6 +60,7 @@ describe('utils', () => { }; partyDetails[0].value.email = 'dummy'; partyDetails[0].value.phoneNumber = 'dummy'; + partyDetails[0].value.liveInRefuge = 'No'; userCase.respondents = partyDetails; expect(getConfirmOrEditYourContactDetails(userCase, '0c09b130-2eba-4ca8-a910-1f001bac01e6')).toBe( diff --git a/src/main/steps/tasklistresponse/start/utils.ts b/src/main/steps/tasklistresponse/start/utils.ts index 8ec52c30c2..571418b5e1 100644 --- a/src/main/steps/tasklistresponse/start/utils.ts +++ b/src/main/steps/tasklistresponse/start/utils.ts @@ -58,7 +58,12 @@ export const getConfirmOrEditYourContactDetails = ( resp.phoneNumber, resp.email, resp.dateOfBirth, + resp.liveInRefuge, + resp.refugeConfidentialityC8Form?.document_filename, ]; + if (resp.liveInRefuge === YesOrNo.NO) { + summaryField.splice(8, 1); + } if (summaryField.every(currentValue => currentValue)) { return SectionStatus.COMPLETED; } diff --git a/src/main/steps/tasklistresponse/summary/content.test.ts b/src/main/steps/tasklistresponse/summary/content.test.ts index d3d14548e7..eb2161ff0b 100644 --- a/src/main/steps/tasklistresponse/summary/content.test.ts +++ b/src/main/steps/tasklistresponse/summary/content.test.ts @@ -70,6 +70,108 @@ describe('citizen-home content', () => { expect(generatedContent1.section).toEqual('Check your answers'); expect(generatedContent1).not.toEqual(''); }); + + test('should return correct english content when refuge document is present', () => { + const refugeContent = { language: 'en' } as CommonContent; + refugeContent.userCase = { + ...mockUserCase, + attendingToCourt: [''], + hearingDetails: '', + languageRequirements: [''], + languageDetails: '', + safetyArrangements: [''], + safetyArrangementsDetails: 'Please describe your need in detail', + reasonableAdjustments: [''], + c1A_haveSafetyConcerns: YesOrNo.YES, + c1A_safetyConernAbout: [C1ASafteyConcernsAbout.RESPONDENT], + isCitizenLivingInRefuge: YesOrNo.YES, + refugeDocument: { + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + document_url: 'MOCK_URL', + }, + }; + const generatedRefugeContent = generateContent(refugeContent); + form = generatedRefugeContent.form as FormContent; + expect(form.onlyContinue.disabled).toBe(false); + expect(generatedRefugeContent.sections?.[4].rows[0].value).toStrictEqual({ + html: 'MOCK_FILENAME', + }); + }); + + test('should return correct english content when refuge document not present', () => { + const refugeContent = { language: 'en' } as CommonContent; + refugeContent.userCase = { + ...mockUserCase, + attendingToCourt: [''], + hearingDetails: '', + languageRequirements: [''], + languageDetails: '', + safetyArrangements: [''], + safetyArrangementsDetails: 'Please describe your need in detail', + reasonableAdjustments: [''], + c1A_haveSafetyConcerns: YesOrNo.YES, + c1A_safetyConernAbout: [C1ASafteyConcernsAbout.RESPONDENT], + isCitizenLivingInRefuge: YesOrNo.YES, + }; + const generatedRefugeContent = generateContent(refugeContent); + form = generatedRefugeContent.form as FormContent; + expect(form.onlyContinue.disabled).toBe(true); + expect(generatedRefugeContent.sections?.[4].rows[0].value).toStrictEqual({ + html: 'Complete this section', + }); + }); + + test('should return correct welsh content when refuge document is present', () => { + const refugeContent = { language: 'cy' } as CommonContent; + refugeContent.userCase = { + ...mockUserCase, + attendingToCourt: [''], + hearingDetails: '', + languageRequirements: [''], + languageDetails: '', + safetyArrangements: [''], + safetyArrangementsDetails: 'Please describe your need in detail', + reasonableAdjustments: [''], + c1A_haveSafetyConcerns: YesOrNo.YES, + c1A_safetyConernAbout: [C1ASafteyConcernsAbout.RESPONDENT], + isCitizenLivingInRefuge: YesOrNo.YES, + refugeDocument: { + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + document_url: 'MOCK_URL', + }, + }; + const generatedRefugeContent = generateContent(refugeContent); + form = generatedRefugeContent.form as FormContent; + expect(form.onlyContinue.disabled).toBe(false); + expect(generatedRefugeContent.sections?.[4].rows[0].value).toStrictEqual({ + html: 'MOCK_FILENAME', + }); + }); + + test('should return correct welsh content when refuge document not present', () => { + const refugeContent = { language: 'cy' } as CommonContent; + refugeContent.userCase = { + ...mockUserCase, + attendingToCourt: [''], + hearingDetails: '', + languageRequirements: [''], + languageDetails: '', + safetyArrangements: [''], + safetyArrangementsDetails: 'Please describe your need in detail', + reasonableAdjustments: [''], + c1A_haveSafetyConcerns: YesOrNo.YES, + c1A_safetyConernAbout: [C1ASafteyConcernsAbout.RESPONDENT], + isCitizenLivingInRefuge: YesOrNo.YES, + }; + const generatedRefugeContent = generateContent(refugeContent); + form = generatedRefugeContent.form as FormContent; + expect(form.onlyContinue.disabled).toBe(true); + expect(generatedRefugeContent.sections?.[4].rows[0].value).toStrictEqual({ + html: 'Llenwch yr adran hon', + }); + }); }); /* eslint-enable @typescript-eslint/ban-types */ diff --git a/src/main/steps/tasklistresponse/summary/content.ts b/src/main/steps/tasklistresponse/summary/content.ts index d20b77ff7a..a839f6deaf 100644 --- a/src/main/steps/tasklistresponse/summary/content.ts +++ b/src/main/steps/tasklistresponse/summary/content.ts @@ -1,10 +1,15 @@ /* eslint-disable import/no-unresolved */ +import _ from 'lodash'; + +import { CaseWithId } from '../../../app/case/case'; import { C1AAbuseTypes, C1ASafteyConcernsAbout, PartyType, YesOrNo } from '../../../app/case/definition'; import { TranslationFn } from '../../../app/controller/GetController'; import { FormContent } from '../../../app/form/Form'; import { atLeastOneFieldIsChecked } from '../../../app/form/validation'; +import { HTML } from '../../../steps/c100-rebuild/check-your-answers/common/htmlSelectors'; import { CommonContent } from '../../../steps/common/common.content'; import { removeFields } from '../../../steps/common/confirm-contact-details/checkanswers/content'; +import { isMandatoryFieldsFilled } from '../../../steps/common/confirm-contact-details/checkanswers/utils'; import { applyParms } from '../../../steps/common/url-parser'; import { CONSENT_TO_APPLICATION, @@ -18,6 +23,7 @@ import { MIAM_START, PROCEEDINGS_COURT_PROCEEDINGS, PROCEEDINGS_START, + REFUGE_UPLOAD_DOC, RESPONDENT_ADDRESS_DETAILS, RESPONDENT_ADDRESS_HISTORY, RESPONDENT_CONTACT_DETAILS, @@ -25,6 +31,7 @@ import { RESPOND_TO_AOH, RESPONSE_TO_AOH, START_ALTERNATIVE, + STAYING_IN_REFUGE, } from '../../../steps/urls'; import { summaryList as prepareRASummaryList } from '../../common/reasonable-adjustments/review/content'; import { @@ -39,6 +46,8 @@ import { PastAndCurrentProceedings } from '../proceedings/mainUtils'; import { ANYTYPE } from './common/index'; import { populateSummaryData } from './handler'; +export * from './routeGuard'; + export const enlegalRepresntationContent = { sectionTitles: { title: '1. Legal representation', @@ -91,6 +100,8 @@ export const enConfirmYourDetailsContent = { citizenUserFullName: 'Name', citizenUserDateOfBirthText: 'Date of birth', citizenUserPlaceOfBirthText: 'Place of birth', + citizenUserLivingInRefugeText: 'Living in refuge', + refugeDocumentText: 'C8 refuge document', citizenUserAddressText: 'Address', citizenUserAddressHistory: 'Address history', citizenUserPhoneNumberText: 'Phone number', @@ -114,6 +125,9 @@ export const enContent = { declarationCheck: { required: 'Please confirm the declaration', }, + refugeDocumentText: { + required: 'You must upload a C8 document', + }, }, continue: 'Submit your response', warning1: 'Warning', @@ -132,6 +146,7 @@ export const enContent = { forRecords: 'Please note this draft is for your records. Only the completed response will be admitted in court.', downloadDraft: 'Download draft response', downloadDraftWelsh: 'Download draft response welsh', + completeSectionError: 'Complete this section', }; export const enInternationalContent = { @@ -304,6 +319,9 @@ export const cyContent: typeof enContent = { declarationCheck: { required: 'Cadarnhewch y datganiad', }, + refugeDocumentText: { + required: 'Mae’n rhaid i chi uwchlwytho dogfen C8', + }, }, continue: 'Cyflwyno eich ymateb', warning1: 'Rhybudd', @@ -322,6 +340,7 @@ export const cyContent: typeof enContent = { forRecords: 'Noder mai drafft yw hwn ar gyfer eich cofnodion. Dim ond yr ymateb terfynol a dderbynnir yn y llys.', downloadDraft: 'Lawrlwytho drafft o’r ymateb', downloadDraftWelsh: 'Lawrlwytho drafft o’r ymateb cymraeg', + completeSectionError: 'Llenwch yr adran hon', }; export const cyContentProceding = { @@ -421,6 +440,8 @@ export const cyConfirmYourDetailsContent = { citizenUserFullName: 'Enw', citizenUserDateOfBirthText: 'Dyddiad geni', citizenUserPlaceOfBirthText: 'Lleoliad geni', + citizenUserLivingInRefugeText: 'Byw mewn lloches', + refugeDocumentText: 'Dogfen lloches C8', citizenUserAddressText: 'Cyfeiriad', citizenUserAddressHistory: 'Hanes cyfeiriad', citizenUserPhoneNumberText: 'Rhif ffôn', @@ -576,6 +597,8 @@ const urls = { citizenUserAddressHistory: RESPONDENT_ADDRESS_HISTORY, citizenUserPhoneNumberText: RESPONDENT_CONTACT_DETAILS, citizenUserEmailAddressText: RESPONDENT_CONTACT_DETAILS, + citizenUserLivingInRefugeText: applyParms(STAYING_IN_REFUGE, { root: PartyType.RESPONDENT }), + refugeDocumentText: applyParms(REFUGE_UPLOAD_DOC, { root: PartyType.RESPONDENT }), start: INTERNATIONAL_FACTORS_START, iFactorsStartProvideDetails: INTERNATIONAL_FACTORS_START, parents: INTERNATIONAL_FACTORS_PARENTS, @@ -609,8 +632,12 @@ const toggleApplicantSafetyConcerns = (safteyConcernsAboutKey, userCase, childCo const en = (content: CommonContent) => { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - populateSummaryData(content.userCase, content.userIdamId); const userCase = content.userCase!; + userCase.refugeDocumentText = !_.isEmpty(userCase.refugeDocument) + ? userCase.refugeDocument.document_filename + : HTML.ERROR_MESSAGE_SPAN + enContent.completeSectionError + HTML.SPAN_CLOSE; + populateSummaryData(userCase, content.userIdamId); + const sections = [] as ANYTYPE; sections.push( summaryList( @@ -666,8 +693,12 @@ const en = (content: CommonContent) => { }; const cy: typeof en = (content: CommonContent) => { - populateSummaryData(content.userCase, content.userIdamId); const userCase = content.userCase!; + userCase.refugeDocumentText = !_.isEmpty(userCase.refugeDocument) + ? userCase.refugeDocument.document_filename + : HTML.ERROR_MESSAGE_SPAN + cyContent.completeSectionError + HTML.SPAN_CLOSE; + populateSummaryData(userCase, content.userIdamId); + const sections = [] as ANYTYPE; sections.push( summaryList( @@ -738,6 +769,7 @@ export const form: FormContent = { }, onlyContinue: { text: l => l.continue, + disabled: false, }, }; @@ -748,6 +780,8 @@ const languages = { export const generateContent: TranslationFn = content => { const translations = languages[content.language](content); + const caseData = content.userCase as CaseWithId; + form.onlyContinue!.disabled = !isMandatoryFieldsFilled(caseData); return { ...translations, diff --git a/src/main/steps/tasklistresponse/summary/handler.test.ts b/src/main/steps/tasklistresponse/summary/handler.test.ts index 49eff6e883..ff474e682d 100644 --- a/src/main/steps/tasklistresponse/summary/handler.test.ts +++ b/src/main/steps/tasklistresponse/summary/handler.test.ts @@ -121,15 +121,17 @@ describe('handler', () => { data.citizenUserDateOfBirth = ''; data.citizenUserPhoneNumber = ''; data.citizenUserEmailAddress = ''; + data.isCitizenLivingInRefuge = ''; populateSummaryData(data, '123'); expect(data.citizenUserPlaceOfBirthText).toEqual(''); expect(data.citizenUserDateOfBirthText).toEqual(''); expect(data.citizenUserPhoneNumberText).toEqual(''); expect(data.citizenUserEmailAddressText).toEqual(''); + expect(data.citizenUserLivingInRefugeText).toEqual(''); }); test('detail', () => { - data.citizenUserPlaceOfBirth = ''; + data.citizenUserPlaceOfBirth = 'London'; data.citizenUserDateOfBirth = { year: '2023', month: '12', @@ -137,13 +139,36 @@ describe('handler', () => { }; data.citizenUserPhoneNumber = '9876'; data.citizenUserEmailAddress = 'abc'; + data.isCitizenLivingInRefuge = 'Yes'; + data.refugeDocument = { + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + document_url: 'MOCK_URL', + }; populateSummaryData(data, '123'); - expect(data.citizenUserPlaceOfBirthText).toEqual(''); + expect(data.citizenUserPlaceOfBirthText).toEqual('London'); expect(data.citizenUserDateOfBirthText).toEqual('25 December 2023'); expect(data.citizenUserPhoneNumberText).toEqual('9876'); expect(data.citizenUserEmailAddressText).toEqual('abc'); + expect(data.citizenUserLivingInRefugeText).toBe('Yes'); + expect(data.refugeDocumentText).toBe('MOCK_FILENAME'); }); + + test('should delete refuge documents when refuge is no', () => { + data.isCitizenLivingInRefuge = 'No'; + data.refugeDocument = { + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + document_url: 'MOCK_URL', + }; + populateSummaryData(data, '123'); + + expect(data.citizenUserLivingInRefugeText).toBe('No'); + expect(data.refugeDocumentText).toBe(undefined); + expect(data.refugeDocument).toBe(undefined); + }); + test('address', () => { data.respondents[0].value.address = { AddressLine1: '1', diff --git a/src/main/steps/tasklistresponse/summary/handler.ts b/src/main/steps/tasklistresponse/summary/handler.ts index ccc6333d70..f2a82c5ff8 100644 --- a/src/main/steps/tasklistresponse/summary/handler.ts +++ b/src/main/steps/tasklistresponse/summary/handler.ts @@ -1,3 +1,5 @@ +import _ from 'lodash'; + import { getFormattedDate } from '../../../app/case/answers/formatDate'; import { CaseWithId } from '../../../app/case/case'; import { Respondent, YesOrNo } from '../../../app/case/definition'; @@ -131,13 +133,6 @@ function dataCleanupConfirmContactDetail(userCase: Partial, responde userCase.citizenUserEmailAddress = respondent?.value?.email; } - userCase.citizenUserPlaceOfBirthText = !userCase.citizenUserPlaceOfBirth ? '' : userCase.citizenUserPlaceOfBirth; - userCase.citizenUserDateOfBirthText = !userCase.citizenUserDateOfBirth - ? '' - : getFormattedDate(userCase.citizenUserDateOfBirth); - userCase.citizenUserPhoneNumberText = !userCase.citizenUserPhoneNumber ? '' : userCase.citizenUserPhoneNumber; - userCase.citizenUserEmailAddressText = !userCase.citizenUserEmailAddress ? '' : userCase.citizenUserEmailAddress; - if (respondent?.value.address) { prepareAddress(respondent, userCase); } @@ -145,6 +140,7 @@ function dataCleanupConfirmContactDetail(userCase: Partial, responde userCase.citizenUserAddressHistory = respondent?.value.addressLivedLessThan5YearsDetails; } + mapContactDetailsTextFields(userCase); mapAddressText(userCase); if (YesOrNo.NO === userCase.isAtAddressLessThan5Years) { userCase.citizenUserAddressHistory = ''; @@ -168,6 +164,24 @@ function mapAddressText(userCase: Partial) { } } +const mapContactDetailsTextFields = (userCase: Partial) => { + userCase.citizenUserPlaceOfBirthText = !userCase.citizenUserPlaceOfBirth ? '' : userCase.citizenUserPlaceOfBirth; + userCase.citizenUserDateOfBirthText = !userCase.citizenUserDateOfBirth + ? '' + : getFormattedDate(userCase.citizenUserDateOfBirth); + userCase.citizenUserPhoneNumberText = !userCase.citizenUserPhoneNumber ? '' : userCase.citizenUserPhoneNumber; + userCase.citizenUserEmailAddressText = !userCase.citizenUserEmailAddress ? '' : userCase.citizenUserEmailAddress; + userCase.citizenUserLivingInRefugeText = !userCase.isCitizenLivingInRefuge ? '' : userCase.isCitizenLivingInRefuge; + userCase.refugeDocumentText = !_.isEmpty(userCase.refugeDocument) + ? userCase.refugeDocument.document_filename + : userCase.refugeDocumentText; + + if (userCase.isCitizenLivingInRefuge === YesOrNo.NO) { + delete userCase.refugeDocument; + delete userCase.refugeDocumentText; + } +}; + function prepareAddress(respondent: Respondent, userCase: Partial) { if (respondent?.value.address.AddressLine1) { userCase.citizenUserAddress1 = respondent?.value.address.AddressLine1; diff --git a/src/main/steps/tasklistresponse/summary/routeGuard.test.ts b/src/main/steps/tasklistresponse/summary/routeGuard.test.ts new file mode 100644 index 0000000000..b0b852234a --- /dev/null +++ b/src/main/steps/tasklistresponse/summary/routeGuard.test.ts @@ -0,0 +1,52 @@ +import { mockRequest } from '../../../../test/unit/utils/mockRequest'; +import { mockResponse } from '../../../../test/unit/utils/mockResponse'; + +import { routeGuard } from './routeGuard'; + +describe('C7 Response > summary > routeGuard', () => { + test('Should add refuge document error if refuge is yes and document not present', async () => { + const req = mockRequest({ + session: { + userCase: { + isCitizenLivingInRefuge: 'Yes', + }, + }, + }); + const res = mockResponse(); + const next = jest.fn(); + routeGuard.get(req, res, next); + expect(next).toHaveBeenCalled(); + expect(req.session.errors).toStrictEqual([{ propertyName: 'refugeDocumentText', errorType: 'required' }]); + }); + + test('Should call next when refuge is yes and document is present', async () => { + const req = mockRequest({ + session: { + userCase: { + isCitizenLivingInRefuge: 'Yes', + refugeDocument: { + document_url: 'MOCK_URL', + document_binary_url: 'MOCK_BINARY_URL', + document_filename: 'MOCK_FILENAME', + }, + }, + }, + }); + const res = mockResponse(); + const next = jest.fn(); + routeGuard.get(req, res, next); + expect(next).toHaveBeenCalled(); + }); + + test('Should call next when refuge is no', async () => { + const req = mockRequest({ + session: { + userCase: { isCitizenLivingInRefuge: 'No' }, + }, + }); + const res = mockResponse(); + const next = jest.fn(); + routeGuard.get(req, res, next); + expect(next).toHaveBeenCalled(); + }); +}); diff --git a/src/main/steps/tasklistresponse/summary/routeGuard.ts b/src/main/steps/tasklistresponse/summary/routeGuard.ts new file mode 100644 index 0000000000..41dea43809 --- /dev/null +++ b/src/main/steps/tasklistresponse/summary/routeGuard.ts @@ -0,0 +1,20 @@ +import { NextFunction, Response } from 'express'; + +import { AppRequest } from '../../../app/controller/AppRequest'; +import { isMandatoryFieldsFilled } from '../../../steps/common/confirm-contact-details/checkanswers/utils'; + +export const routeGuard = { + // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types + get: (req: AppRequest, res: Response, next: NextFunction) => { + const caseData = req.session.userCase; + if (!isMandatoryFieldsFilled(caseData)) { + req.session.errors = [ + { + propertyName: 'refugeDocumentText', + errorType: 'required', + }, + ]; + } + req.session.save(next); + }, +}; diff --git a/src/main/steps/urls.ts b/src/main/steps/urls.ts index 4ffea6494b..6afde96798 100644 --- a/src/main/steps/urls.ts +++ b/src/main/steps/urls.ts @@ -493,6 +493,13 @@ export const UPLOAD_STATEMENT_OF_SERVICE: PageLink = `${STATEMENT_OF_SERVICE}/up export const STATEMENT_OF_SERVICE_REVIEW: PageLink = `${STATEMENT_OF_SERVICE}/review/:context`; export const STATEMENT_OF_SERVICE_SUCCESS: PageLink = `${STATEMENT_OF_SERVICE}/success`; +const REFUGE_BASE_URL = 'refuge'; +export const STAYING_IN_REFUGE: PageLink = `/:root/${REFUGE_BASE_URL}/staying-in-refuge/:id?`; +export const REFUGE_KEEPING_SAFE: PageLink = `/:root/${REFUGE_BASE_URL}/keeping-details-safe/:id?`; +export const REFUGE_UPLOAD_DOC: PageLink = `/:root/${REFUGE_BASE_URL}/upload-refuge-document/:removeFileId?`; +export const C100_REFUGE_UPLOAD_DOC: PageLink = `/:root/${REFUGE_BASE_URL}/upload-refuge-document/:id/:removeFileId?`; +export const REFUGE_DOC_ALREADY_UPLOADED: PageLink = `/:root/${REFUGE_BASE_URL}/refuge-document-already-uploaded/:id?`; + export const ANONYMOUS_URLS = [HEALTH_URL, ...SCREENING_QUESTIONS]; export const COMMON_PAGE_URLS = [ DASHBOARD_URL, diff --git a/src/test/end-to-end/contents/ApplicantDetails-content.js b/src/test/end-to-end/contents/ApplicantDetails-content.js index c65624456c..8b7fa25ef5 100644 --- a/src/test/end-to-end/contents/ApplicantDetails-content.js +++ b/src/test/end-to-end/contents/ApplicantDetails-content.js @@ -14,6 +14,9 @@ module.exports = { placeOfBirthSubHeading: 'Your place of birth', placeOfBirth: 'London', relationshipToChildPageTitle: 'relationship to', + stayingInRefugePageTitle: 'Staying in a Refuge', + keepingDetailsSafePageTitle: 'details safe', + uploadC8FormPageTitle: 'Upload a C8 form', addressDetailsOfApplicantPageTitle: 'Address of', addressHintText: 'Current postcode', postcode: 'B11LS', diff --git a/src/test/end-to-end/contents/OtherPersonDetails-content.js b/src/test/end-to-end/contents/OtherPersonDetails-content.js index 35b3938e17..a5e76cd6a1 100644 --- a/src/test/end-to-end/contents/OtherPersonDetails-content.js +++ b/src/test/end-to-end/contents/OtherPersonDetails-content.js @@ -11,6 +11,9 @@ module.exports = { month: '07', year: '1963', otherPersonRelationshipPageTitle: 'relationship to', + stayingInRefugePageTitle: 'Staying in a Refuge', + keepingDetailsSafePageTitle: 'details safe', + uploadC8FormPageTitle: 'Upload a C8 form', addressOfOtherPersonPageTitle: 'Address of', addressOfOtherPersonSubHeading: 'Current postcode', postcode: 'MK9 3DX', diff --git a/src/test/end-to-end/pages/C100-Rebuild/ApplicantDetails.js b/src/test/end-to-end/pages/C100-Rebuild/ApplicantDetails.js index d8f0dd18ff..443fde859c 100644 --- a/src/test/end-to-end/pages/C100-Rebuild/ApplicantDetails.js +++ b/src/test/end-to-end/pages/C100-Rebuild/ApplicantDetails.js @@ -17,7 +17,10 @@ module.exports = { monthDOB: '//*[@id="dateOfBirth-month"]', yearDOB: '//*[@id="dateOfBirth-year"]', applicantPlaceOfBirth: '//*[@id="applicantPlaceOfBirth"]', - fatherOption: '//*[@id="relationshipType-2"]', + fatherOption: '//*[@id="relationshipType-2"]', + livesInRefugeYes: '//*[@id="isCitizenLivingInRefuge"]', + livesInRefugeNo: '//*[@id="isCitizenLivingInRefuge-2"]', + fileUploadInput:'#fileupload', addressPostcode: '//*[@id="addressPostcode"]', //address look up addressList: '//*[@id="selectAddress"]', @@ -116,6 +119,24 @@ module.exports = { await I.retry(retryCount).click(this.fields.postOption); await I.retry(retryCount).click('Continue'); }, + async refugeDetailsOfApplicant() { + await I.retry(retryCount).waitForText(ApplicantDetails.stayingInRefugePageTitle , 30); + + await I.retry(retryCount).click(this.fields.livesInRefugeYes); + await I.retry(retryCount).click('Continue'); + + await I.retry(retryCount).waitForText(ApplicantDetails.keepingDetailsSafePageTitle , 30); + await I.retry(retryCount).click('Continue'); + + await I.retry(retryCount).waitForText(ApplicantDetails.uploadC8FormPageTitle , 30); + await I.retry(retryCount).waitForText('Upload a C8 form'); + await I.attachFile(this.fields.fileUploadInput, '../resource/dummy.pdf') + await I.wait('1'); + await I.retry(retryCount).click('Upload file'); + await I.wait('5'); + await I.retry(retryCount).dontSee('No files uploaded'); + await I.retry(retryCount).click('Continue'); + }, async applicantDetails() { await this.fillApplicantDetails(); await this.confidentiality(); @@ -123,6 +144,7 @@ module.exports = { await this.confidentialitySummary(); await this.provideDetailsPage(); await this.relationshipToChild(); + await this.refugeDetailsOfApplicant(); await this.addressDetailsOfApplicant(); await this.addressLookUp(); await this.confirmAddress(); diff --git a/src/test/end-to-end/pages/C100-Rebuild/OtherPersonDetails.js b/src/test/end-to-end/pages/C100-Rebuild/OtherPersonDetails.js index 2aed4914e8..59e11dfdde 100644 --- a/src/test/end-to-end/pages/C100-Rebuild/OtherPersonDetails.js +++ b/src/test/end-to-end/pages/C100-Rebuild/OtherPersonDetails.js @@ -12,7 +12,10 @@ module.exports = { dayDOB: '//*[@id="dateOfBirth-day"]', monthDOB: '//*[@id="dateOfBirth-month"]', yearDOB: '//*[@id="dateOfBirth-year"]', - grandparentOptionButton: '//*[@id="relationshipType-5"]', + grandparentOptionButton: '//*[@id="relationshipType-5"]', + livesInRefugeYes: '//*[@id="isCitizenLivingInRefuge"]', + livesInRefugeNo: '//*[@id="isCitizenLivingInRefuge-2"]', + fileUploadInput:'#fileupload', otherPersonPostCodeField: '//*[@id="PostCode"]', addressList: '//*[@id="selectAddress"]', liveWithFirstOptionButton: '//*[@id="mainlyLiveWith"]', @@ -75,12 +78,33 @@ module.exports = { await I.retry(retryCount).click('Continue'); }, + async refugeDetailsOfOtherPerson() { + await I.retry(retryCount).waitForText(OtherPersonDetails.stayingInRefugePageTitle , 30); + + await I.retry(retryCount).click(this.fields.livesInRefugeYes); + await I.retry(retryCount).click('Continue'); + + await I.retry(retryCount).waitForText(OtherPersonDetails.keepingDetailsSafePageTitle , 30); + await I.retry(retryCount).click('Continue'); + + await I.retry(retryCount).waitForText(OtherPersonDetails.uploadC8FormPageTitle , 30); + await I.retry(retryCount).waitForText('Upload a C8 form'); + await I.attachFile(this.fields.fileUploadInput, '../resource/dummy.pdf') + await I.wait('1'); + await I.retry(retryCount).click('Upload file'); + await I.wait('5'); + await I.retry(retryCount).dontSee('No files uploaded'); + + await I.retry(retryCount).click('Continue'); + }, + //With Other Person async otherPersonDetails() { await this.otherPerson(true); await this.otherPersonName(); await this.otherPersonDetailsInfo(); await this.otherPersonRelationship(); + await this.refugeDetailsOfOtherPerson(); await this.addressOfOtherPerson(); await this.addressLookUpPage(); await this.confirmAddress();