diff --git a/src/applications/ivc-champva/10-10D/components/CustomPrefillAlert.jsx b/src/applications/ivc-champva/10-10D/components/CustomPrefillAlert.jsx new file mode 100644 index 000000000000..0c8f24cd1d94 --- /dev/null +++ b/src/applications/ivc-champva/10-10D/components/CustomPrefillAlert.jsx @@ -0,0 +1,29 @@ +import React from 'react'; + +/** + * Displays a custom alert based on the current `certifierRole` value. + * Used to alert the user when we automatically fill in some portions + * of the form based on their previous answers, e.g., adding an applicant + * with the certifier's information if the certifier specified that they + * were applying for themselves. + * @param {object} formData Standard formdata object + * @param {string} role Value we expect certifierRole to have if we are to display this message + * @param {string} msg Optional custom text to display in our alert + * @returns + */ +export default function CustomPrefillMessage(formData, role, msg) { + const text = + msg || + 'We’ve prefilled some details based on information you provided earlier in this form. If you need to make changes, you can edit on this screen.'; + const certifierRole = + formData.certifierRole ?? formData['view:certifierRole']; + if (certifierRole && certifierRole === role) + return ( + +

+ Note: {text} +

+
+ ); + return <>; +} diff --git a/src/applications/ivc-champva/10-10D/config/form.js b/src/applications/ivc-champva/10-10D/config/form.js index ec9c8d045120..aa488d0c069e 100644 --- a/src/applications/ivc-champva/10-10D/config/form.js +++ b/src/applications/ivc-champva/10-10D/config/form.js @@ -20,6 +20,7 @@ import { yesNoSchema, yesNoUI, radioSchema, + radioUI, titleSchema, titleUI, } from 'platform/forms-system/src/js/web-component-patterns'; @@ -27,6 +28,7 @@ import VaTextInputField from 'platform/forms-system/src/js/web-component-fields/ import get from '@department-of-veterans-affairs/platform-forms-system/get'; import { blankSchema } from 'platform/forms-system/src/js/utilities/data/profile'; import SubmissionError from '../../shared/components/SubmissionError'; +import CustomPrefillMessage from '../components/CustomPrefillAlert'; // import { fileUploadUi as fileUploadUI } from '../components/File/upload'; import { ssnOrVaFileNumberCustomUI } from '../components/CustomSsnPattern'; @@ -42,9 +44,12 @@ import { onReviewPage, applicantListSchema, sponsorWording, - populateFirstApplicant, page15aDepends, } from '../helpers/utilities'; +import { + certifierNameValidation, + certifierAddressValidation, +} from '../helpers/validations'; import { MAX_APPLICANTS, ADDITIONAL_FILES_HINT } from './constants'; import { applicantWording, getAgeInYears } from '../../shared/utilities'; import { sponsorNameDobConfig } from '../pages/Sponsor/sponsorInfoConfig'; @@ -107,6 +112,10 @@ import { depends18f3, } from '../pages/ApplicantSponsorMarriageDetailsPage'; import { ApplicantAddressCopyPage } from '../../shared/components/applicantLists/ApplicantAddressPage'; +import { + signerContactInfoPage, + SignerContactInfoPage, +} from '../pages/SignerContactInfoPage'; import { hasReq } from '../../shared/components/fileUploads/MissingFileOverview'; import { fileWithMetadataSchema } from '../../shared/components/fileUploads/attachments'; @@ -114,9 +123,6 @@ import { fileWithMetadataSchema } from '../../shared/components/fileUploads/atta // import mockData from '../tests/e2e/fixtures/data/test-data.json'; import FileFieldWrapped from '../components/FileUploadWrapper'; -// Used by populateFirstApplicant fn: -const SIGNER_REL_PATH = 'signer-relationship'; - // Control whether we show the file overview page by calling `hasReq` to // determine if any required files have not been uploaded function showFileOverviewPage(formData) { @@ -134,7 +140,7 @@ const formConfig = { rootUrl: manifest.rootUrl, urlPrefix: '/', transformForSubmit, - showReviewErrors: !environment.isProduction(), + showReviewErrors: true, // May want to hide in prod later, but for now keeping in due to complexity of this form submitUrl: `${environment.API_URL}/ivc_champva/v1/forms`, footerContent: GetFormHelp, // submit: () => @@ -184,6 +190,36 @@ const formConfig = { certifierInformation: { title: 'Signer information', pages: { + page1: { + // initialData: mockData.data, + path: 'signer-type', + title: 'Which of these best describes you?', + uiSchema: { + ...titleUI('Your information'), + certifierRole: radioUI({ + title: 'Which of these best describes you?', + required: () => true, + labels: { + applicant: 'I’m applying for benefits for myself', + sponsor: + 'I’m a Veteran applying for benefits for my spouse or dependents', + other: + 'I’m a representative applying for benefits on behalf of someone else', + }, + // Changing this data on review messes up the ad hoc prefill + // mapping of certifier -> applicant|sponsor: + hideOnReview: true, + }), + }, + schema: { + type: 'object', + required: ['certifierRole'], + properties: { + titleSchema, + certifierRole: radioSchema(['applicant', 'sponsor', 'other']), + }, + }, + }, page2: { // initialData: mockData.data, path: 'signer-info', @@ -191,6 +227,7 @@ const formConfig = { uiSchema: { ...titleUI('Your name'), certifierName: fullNameUI(), + 'ui:validations': [certifierNameValidation], }, schema: { type: 'object', @@ -210,6 +247,7 @@ const formConfig = { 'We’ll send any important information about this application to your address', ), certifierAddress: addressUI(), + 'ui:validations': [certifierAddressValidation], }, schema: { type: 'object', @@ -223,27 +261,14 @@ const formConfig = { page4: { path: 'signer-contact-info', title: 'Certification', - uiSchema: { - ...titleUI( - 'Your contact information', - 'We use this information to contact you and verify other details.', - ), - certifierPhone: phoneUI(), - certifierEmail: emailUI(), - }, - schema: { - type: 'object', - required: ['certifierPhone', 'certifierEmail'], - properties: { - titleSchema, - certifierPhone: phoneSchema, - certifierEmail: emailSchema, - }, - }, + CustomPage: SignerContactInfoPage, + CustomPageReview: null, + ...signerContactInfoPage, }, page5: { - path: SIGNER_REL_PATH, + path: 'signer-relationship', title: 'Certification', + depends: formData => get('certifierRole', formData) === 'other', uiSchema: { ...titleUI( 'Your relationship to the applicant', @@ -255,7 +280,6 @@ const formConfig = { hint: 'Select all that apply', required: () => true, labels: { - applicant: 'I’m applying for benefits for myself', spouse: 'I’m an applicant’s spouse', child: 'I’m an applicant’s child', parent: 'I’m an applicant’s parent', @@ -280,22 +304,6 @@ const formConfig = { 'ui:options': { updateSchema: (formData, formSchema) => { const fs = formSchema; - if ( - get( - 'certifierRelationship.relationshipToVeteran.applicant', - formData, - ) - ) { - // If the certifier is also an applicant, pre-populate first app slot with certifier info: - populateFirstApplicant( - formData, - SIGNER_REL_PATH, // Used to verify we only ever apply this fn in one location - formData.certifierName, - formData.certifierEmail, - formData.certifierPhone, - formData.certifierAddress, - ); - } // If 'other', open the text field to specify: if ( get( @@ -334,7 +342,6 @@ const formConfig = { type: 'object', properties: { relationshipToVeteran: checkboxGroupSchema([ - 'applicant', 'spouse', 'child', 'parent', @@ -384,6 +391,7 @@ const formConfig = { page8: { path: 'sponsor-status', title: 'Sponsor status', + depends: formData => get('certifierRole', formData) !== 'sponsor', uiSchema: { sponsorInfoTitle: titleUI( 'Sponsor status', @@ -409,7 +417,9 @@ const formConfig = { page9: { path: 'sponsor-status-date', title: 'Sponsor status details', - depends: formData => get('sponsorIsDeceased', formData), + depends: formData => + get('certifierRole', formData) !== 'sponsor' && + get('sponsorIsDeceased', formData), uiSchema: { sponsorInfoTitle: titleUI('Sponsor status details'), sponsorDOD: dateOfDeathUI('When did the sponsor die?'), @@ -440,7 +450,16 @@ const formConfig = { uiSchema: { ...titleUI( ({ formData }) => `${sponsorWording(formData)} mailing address`, - 'We’ll send any important information about this application to this address.', + ({ formData }) => ( + // Prefill message conditionally displays based on `certifierRole` + <> +

+ We’ll send any important information about this application + to this address. +

+ {CustomPrefillMessage(formData, 'sponsor')} + + ), ), sponsorAddress: { ...addressUI({ @@ -500,15 +519,14 @@ const formConfig = { title: 'Applicant information', path: 'applicant-info', uiSchema: { - ...titleUI('Applicant name and date of birth', () => ( + ...titleUI('Applicant name and date of birth', ({ formData }) => ( + // Prefill message conditionally displays based on `certifierRole` <> - Enter the information for any applicants you want to enroll in - CHAMPVA benefits. -
-
- {`You can add up to ${MAX_APPLICANTS} applicants in a single application. If you - need to add more than ${MAX_APPLICANTS} applicants, you'll need to submit a - separate application for them.`} +

+ Enter the information for any applicants you want to enroll in + CHAMPVA benefits. +

+ {CustomPrefillMessage(formData, 'applicant')} )), applicants: { @@ -624,11 +642,20 @@ const formConfig = { ...titleUI( ({ formData }) => `${applicantWording(formData)} mailing address`, + ({ formData, formContext }) => { + const txt = + 'We’ll send any important information about your application to this address'; + // Prefill message conditionally displays based on `certifierRole` + return formContext.pagePerItemIndex === '0' ? ( + <> +

{txt}

+ {CustomPrefillMessage(formData, 'applicant')} + + ) : ( +

{txt}

+ ); + }, ), - 'view:description': { - 'ui:description': - 'We’ll send any important information about your application to this address.', - }, applicantAddress: { ...addressUI({ labels: { @@ -642,7 +669,6 @@ const formConfig = { }, schema: applicantListSchema([], { titleSchema, - 'view:description': blankSchema, applicantAddress: addressSchema(), }), }, @@ -657,8 +683,24 @@ const formConfig = { items: { ...titleUI( ({ formData }) => - `${applicantWording(formData)} contact information`, - 'This information helps us contact you faster if we need to follow up with you about your application', + `${applicantWording(formData)} mailing address`, + ({ formData, formContext }) => { + const txt = `We'll use this information to contact ${applicantWording( + formData, + false, + false, + true, + )} if we need to follow up about this application.`; + // Prefill message conditionally displays based on `certifierRole` + return formContext.pagePerItemIndex === '0' ? ( + <> +

{txt}

+ {CustomPrefillMessage(formData, 'applicant')} + + ) : ( +

{txt}

+ ); + }, ), applicantPhone: phoneUI(), applicantEmailAddress: emailUI(), diff --git a/src/applications/ivc-champva/10-10D/config/submitTransformer.js b/src/applications/ivc-champva/10-10D/config/submitTransformer.js index 28d4299658db..c1af3461484c 100644 --- a/src/applications/ivc-champva/10-10D/config/submitTransformer.js +++ b/src/applications/ivc-champva/10-10D/config/submitTransformer.js @@ -13,6 +13,12 @@ function fmtDate(date) { : date; } +// Returns list of object keys where obj[key] === true. Used +// to get the list of relationships a certifier has w/ the applicants: +function trueKeysOnly(obj) { + return Object.keys(obj ?? {}).filter(k => obj[k] === true); +} + // Simplify a relationship object from (potentially) multiple nested objects to a single string function transformRelationship(obj) { if (typeof obj === 'string') return obj; @@ -107,8 +113,7 @@ export default function transformForSubmit(formConfig, form) { // If certifier is also applicant, we don't need to fill out the // bottom "Certification" section of the PDF: certification: - transformedData?.certifierRelationship?.relationshipToVeteran - ?.applicant === true + transformedData?.certifierRole === 'applicant' ? { date: currentDate } : { date: currentDate, @@ -116,9 +121,10 @@ export default function transformForSubmit(formConfig, form) { middleInitial: transformedData?.certifierName?.middle || '', firstName: transformedData?.certifierName?.first || '', phoneNumber: transformedData?.certifierPhone || '', - relationship: transformRelationship( - transformedData?.certifierRelationship, - ), + // will produce string list of checkboxgroup keys (e.g., "spouse; child") + relationship: trueKeysOnly( + transformedData?.certifierRelationship?.relationshipToVeteran, + ).join('; '), streetAddress: transformedData?.certifierAddress?.streetCombined || '', city: transformedData?.certifierAddress?.city || '', @@ -156,12 +162,7 @@ export default function transformForSubmit(formConfig, form) { dataPostTransform.supportingDocs = dataPostTransform.supportingDocs .flat() .concat(supDocs); - // TODO - remove usage of `certifierRole` in vets-api - dataPostTransform.certifierRole = - dataPostTransform?.certifierRelationship?.relationshipToVeteran - ?.applicant === true - ? 'applicant' - : 'other'; + dataPostTransform.certifierRole = transformedData.certifierRole; dataPostTransform.statementOfTruthSignature = transformedData.statementOfTruthSignature; // `primaryContactInfo` is who BE callback API emails if there's a notification event diff --git a/src/applications/ivc-champva/10-10D/helpers/utilities.js b/src/applications/ivc-champva/10-10D/helpers/utilities.js index 08bf96d0c1b9..265eef2f5263 100644 --- a/src/applications/ivc-champva/10-10D/helpers/utilities.js +++ b/src/applications/ivc-champva/10-10D/helpers/utilities.js @@ -54,24 +54,12 @@ export function sponsorWording(formData, isPosessive = true, cap = true) { * array. This is used to add the certifier info to the first applicant * slot so users don't have to enter info twice if the certifier is also an app. * @param {object} formData standard formData object - * @param {string} path formconfig page path of the page where this function should apply * @param {object} name standard fullNameUI name to populate * @param {string} email email address to populate * @param {string} phone phone number to populate * @param {object} address standard addressUI address object to populate */ -export function populateFirstApplicant( - formData, - path, - name, - email, - phone, - address, -) { - // Make sure we apply this change when the user is on the certifier relationship - // page, as firing later in the form would potentially add duplicate info: - if (!window.location.href.endsWith(path)) return formData; - +export function populateFirstApplicant(formData, name, email, phone, address) { const modifiedFormData = formData; // changes will affect original formData const newApplicant = { applicantName: name, @@ -103,12 +91,20 @@ export function populateFirstApplicant( // Only show address dropdown if we're not the certifier // AND there's another address present to choose from: export function page15aDepends(formData, index) { - const certifierIsApp = - get('certifierRelationship.relationshipToVeteran.applicant', formData) === - true; + const certifierIsApp = get('certifierRole', formData) === 'applicant'; const certAddress = get('street', formData?.certifierAddress); return ( (index && index > 0) || (certAddress && !(certifierIsApp && index === 0)) ); } + +// Compare two objects (with the same keys) and identify differences +export function objDiff(obj1, obj2, ignoreViewOnlyProps = true) { + return Object.keys(obj1).filter( + k => + (obj1[k] && obj2[k]) !== undefined && + obj1[k] !== obj2[k] && + !(ignoreViewOnlyProps && k.includes('view:')), + ); +} diff --git a/src/applications/ivc-champva/10-10D/helpers/validations.js b/src/applications/ivc-champva/10-10D/helpers/validations.js new file mode 100644 index 000000000000..6c862a369e93 --- /dev/null +++ b/src/applications/ivc-champva/10-10D/helpers/validations.js @@ -0,0 +1,99 @@ +import { objDiff } from './utilities'; +import { makeHumanReadable } from '../../shared/utilities'; + +/* +This validation checks if the `certProp` value matches the corresponding +`appProp` or `sponsorProp` entry (since the certifier may be either +an applicant or the sponsor). Helps ensure their information is entered +consistently across the form. +*/ +export const fieldsMustMatchValidation = ( + errors, + page, + formData, + certProp, + appProp, + sponsorProp, +) => { + let target; // will populate with form data prop we want to compare against + if (formData.certifierRole === 'applicant') { + target = formData?.applicants?.[0]?.[appProp]; + } else if (formData.certifierRole === 'sponsor') { + target = formData[sponsorProp]; + } else { + return; // This validation is not applicable here. + } + + if (target === undefined) return; + + // E.g.: `certifierName` => `Name`: + const friendlyName = makeHumanReadable(certProp) + .toLowerCase() + .split(' ') + .at(-1); + + // For string props like phones/emails: + if (typeof target === 'string' && page[certProp] !== target) { + errors[certProp].addError( + `Must match corresponding ${ + formData.certifierRole + } ${friendlyName}: ${target}`, + ); + } else { + // Identify which fields of the multi-field name object are different (e.g., address fields): + const diff = objDiff(page[certProp], target); + if (target && diff.length > 0) { + diff.forEach(k => + errors[certProp][k].addError( + `Must match corresponding ${ + formData.certifierRole + } ${friendlyName}: ${target[k]}`, + ), + ); + } + } +}; + +export const certifierNameValidation = (errors, page, formData) => { + return fieldsMustMatchValidation( + errors, + page, + formData, + 'certifierName', + 'applicantName', + 'veteransFullName', + ); +}; + +export const certifierAddressValidation = (errors, page, formData) => { + return fieldsMustMatchValidation( + errors, + page, + formData, + 'certifierAddress', + 'applicantAddress', + 'sponsorAddress', + ); +}; + +export const certifierPhoneValidation = (errors, page, formData) => { + return fieldsMustMatchValidation( + errors, + page, + formData, + 'certifierPhone', + 'applicantPhone', + 'sponsorPhone', + ); +}; + +export const certifierEmailValidation = (errors, page, formData) => { + return fieldsMustMatchValidation( + errors, + page, + formData, + 'certifierEmail', + 'applicantEmailAddress', + '_undefined', // Sponsor has no email field + ); +}; diff --git a/src/applications/ivc-champva/10-10D/pages/SignerContactInfoPage.jsx b/src/applications/ivc-champva/10-10D/pages/SignerContactInfoPage.jsx new file mode 100644 index 000000000000..8e7da694d141 --- /dev/null +++ b/src/applications/ivc-champva/10-10D/pages/SignerContactInfoPage.jsx @@ -0,0 +1,126 @@ +/* eslint-disable react/sort-prop-types */ +import React from 'react'; +import PropTypes from 'prop-types'; +import SchemaForm from '@department-of-veterans-affairs/platform-forms-system/SchemaForm'; +import { + titleUI, + titleSchema, + phoneSchema, + phoneUI, + emailSchema, + emailUI, +} from 'platform/forms-system/src/js/web-component-patterns'; +import FormNavButtons from '~/platform/forms-system/src/js/components/FormNavButtons'; +import { populateFirstApplicant } from '../helpers/utilities'; +import { + certifierPhoneValidation, + certifierEmailValidation, +} from '../helpers/validations'; + +export const signerContactInfoPage = { + uiSchema: { + ...titleUI( + 'Your contact information', + 'We use this information to contact you if we have more questions.', + ), + certifierPhone: phoneUI(), + certifierEmail: emailUI(), + 'ui:validations': [certifierPhoneValidation, certifierEmailValidation], + }, + schema: { + type: 'object', + required: ['certifierPhone', 'certifierEmail'], + properties: { + titleSchema, + certifierPhone: phoneSchema, + certifierEmail: emailSchema, + }, + }, +}; + +export function signerContactOnGoForward(props) { + const formData = props.data; // changes made here will reflect in global formData; + if (props?.data?.certifierRole === 'applicant') { + populateFirstApplicant( + formData, + formData.certifierName, + formData.certifierEmail, + formData.certifierPhone, + formData.certifierAddress, + ); + } else if (props?.data?.certifierRole === 'sponsor') { + // Populate some sponsor fields with certifier info: + formData.sponsorIsDeceased = false; + formData.veteransFullName = formData.certifierName; + formData.sponsorAddress = formData.certifierAddress; + formData.sponsorPhone = formData.certifierPhone; + } + + if (formData?.applicants?.[0]) { + // This is so we have an accurate `certifierRole` prop inside the + // list loop where that context is normally unavailable: + formData.applicants[0]['view:certifierRole'] = formData?.certifierRole; + } +} + +/** @type {CustomPageType} */ +export function SignerContactInfoPage(props) { + const updateButton = ( + // eslint-disable-next-line @department-of-veterans-affairs/prefer-button-component + + ); + + return ( + + <> + {/* contentBeforeButtons = save-in-progress links */} + {props.contentBeforeButtons} + {props.onReviewPage ? ( + updateButton + ) : ( + signerContactOnGoForward(props)} + submitToContinue + /> + )} + {props.contentAfterButtons} + + + ); +} + +SignerContactInfoPage.propTypes = { + name: PropTypes.string.isRequired, + schema: PropTypes.object.isRequired, + uiSchema: PropTypes.object.isRequired, + appStateData: PropTypes.object, + contentAfterButtons: PropTypes.node, + contentBeforeButtons: PropTypes.node, + data: PropTypes.object, + formContext: PropTypes.object, + goBack: PropTypes.func, + onChange: PropTypes.func, + onContinue: PropTypes.func, + onReviewPage: PropTypes.bool, + onSubmit: PropTypes.func, + pagePerItemIndex: PropTypes.number, + setFormData: PropTypes.func, + title: PropTypes.string, + trackingPrefix: PropTypes.string, + updatePage: PropTypes.func, +}; diff --git a/src/applications/ivc-champva/10-10D/pages/Sponsor/sponsorInfoConfig.js b/src/applications/ivc-champva/10-10D/pages/Sponsor/sponsorInfoConfig.js index eea3b7238a8b..928a50e7f492 100644 --- a/src/applications/ivc-champva/10-10D/pages/Sponsor/sponsorInfoConfig.js +++ b/src/applications/ivc-champva/10-10D/pages/Sponsor/sponsorInfoConfig.js @@ -1,3 +1,4 @@ +import React from 'react'; import { dateOfBirthUI, dateOfBirthSchema, @@ -7,6 +8,7 @@ import { titleSchema, } from 'platform/forms-system/src/js/web-component-patterns'; +import CustomPrefillMessage from '../../components/CustomPrefillAlert'; import { sponsorWording } from '../../helpers/utilities'; function descriptionText(formData) { @@ -26,7 +28,13 @@ export const sponsorNameDobConfig = { uiSchema: { ...titleUI( ({ formData }) => `${sponsorWording(formData)} name and date of birth`, - ({ formData }) => descriptionText(formData), + ({ formData }) => ( + // Prefill message conditionally displays based on `certifierRole` + <> +

{descriptionText(formData)}

+ {CustomPrefillMessage(formData, 'sponsor')} + + ), ), veteransFullName: fullNameUI(), sponsorDob: dateOfBirthUI(), diff --git a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/app-sa-cs-medab.json b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/app-sa-cs-medab.json index 5acbd0e81907..62abda78fdf3 100644 --- a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/app-sa-cs-medab.json +++ b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/app-sa-cs-medab.json @@ -3,13 +3,13 @@ "data": { "applicants": [ { - "applicantPhone": "2341232345", + "applicantPhone": "1231231234", "applicantEmailAddress": "certifier@email.gov", "applicantAddress": { "country": "USA", - "street": "321 Street Av", - "city": "Cityburg", - "state": "AS", + "street": "123 Certifier Street", + "city": "CertTown", + "state": "AK", "postalCode": "12312" }, "applicantSSN": { @@ -85,13 +85,7 @@ "suffix": "Sr." }, "sponsorDob": "1999-01-01", - "certifierRelationship": { - "relationshipToVeteran": { - "applicant": true, - "spouse": false, - "thirdParty": true - } - }, + "certifierRole": "applicant", "certifierPhone": "1231231234", "certifierEmail": "certifier@email.gov", "certifierAddress": { diff --git a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/app-sd-cb-ohi.json b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/app-sd-cb-ohi.json index 162b56cd6783..a54e673f7fb4 100644 --- a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/app-sd-cb-ohi.json +++ b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/app-sd-cb-ohi.json @@ -70,6 +70,7 @@ "ssn": { "ssn": "123123123" }, + "certifierRole": "other", "certifierRelationship": { "relationshipToVeteran": { "spouse": true, diff --git a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/cert-sd-spoused.json b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/cert-sd-spoused.json index 9f113bef9bd1..ff07929803df 100644 --- a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/cert-sd-spoused.json +++ b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/cert-sd-spoused.json @@ -47,6 +47,7 @@ "last": "Jones" }, "sponsorDob": "1950-01-01", + "certifierRole": "other", "certifierRelationship": { "relationshipToVeteran": { "spouse": false, diff --git a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/maximal-test.json b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/maximal-test.json index 1e191a67f51b..e77fa6dfb084 100644 --- a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/maximal-test.json +++ b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/maximal-test.json @@ -191,6 +191,7 @@ "last": "Jones" }, "sponsorDob": "1950-01-01", + "certifierRole": "other", "certifierRelationship": { "relationshipToVeteran": { "spouse": true, diff --git a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/test-data.json b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/test-data.json index c228fb817da3..474fe6e43bed 100644 --- a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/test-data.json +++ b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/test-data.json @@ -42,6 +42,7 @@ "first": "Joe", "last": "Johnson" }, + "certifierRole": "other", "certifierRelationship": { "relationshipToVeteran": { "spouse": true, diff --git a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/vet-2a-spouse-child-medabd-ohi.json b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/vet-2a-spouse-child-medabd-ohi.json index 46f0091efb60..c61f33a1cd00 100644 --- a/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/vet-2a-spouse-child-medabd-ohi.json +++ b/src/applications/ivc-champva/10-10D/tests/e2e/fixtures/data/vet-2a-spouse-child-medabd-ohi.json @@ -129,6 +129,7 @@ "first": "Sponsor", "last": "Jones" }, + "certifierRole": "other", "certifierRelationship": { "relationshipToVeteran": { "spouse": true, diff --git a/src/applications/ivc-champva/10-10D/tests/unit/config/form.unit.spec.jsx b/src/applications/ivc-champva/10-10D/tests/unit/config/form.unit.spec.jsx index caa68778b00d..eafd8f6e534e 100644 --- a/src/applications/ivc-champva/10-10D/tests/unit/config/form.unit.spec.jsx +++ b/src/applications/ivc-champva/10-10D/tests/unit/config/form.unit.spec.jsx @@ -34,7 +34,7 @@ testNumberOfWebComponentFields( formConfig, formConfig.chapters.certifierInformation.pages.page5.schema, formConfig.chapters.certifierInformation.pages.page5.uiSchema, - 7, + 6, 'Signer relationship', { certifierRelationship: { diff --git a/src/applications/ivc-champva/10-10D/tests/unit/config/submitTransformer.unit.spec.js b/src/applications/ivc-champva/10-10D/tests/unit/config/submitTransformer.unit.spec.js index fa9c59fa81b9..f728c04cb428 100644 --- a/src/applications/ivc-champva/10-10D/tests/unit/config/submitTransformer.unit.spec.js +++ b/src/applications/ivc-champva/10-10D/tests/unit/config/submitTransformer.unit.spec.js @@ -9,24 +9,45 @@ describe('transform for submit', () => { const transformed = JSON.parse( transformForSubmit(formConfig, { data: { - certifierRelationship: 'Spouse', + applicants: [{ applicantRelationshipToSponsor: 'Spouse' }], }, }), ); - expect(transformed.certification.relationship).to.equal('Spouse'); + expect(transformed.applicants[0].vetRelationship).to.equal('Spouse'); }); it('should flatten relationship details', () => { const transformed = JSON.parse( transformForSubmit(formConfig, { data: { + applicants: [ + { + applicantRelationshipToSponsor: { + relationshipToVeteran: 'other', + otherRelationshipToVeteran: 'Sibling', + }, + }, + ], + }, + }), + ); + expect(transformed.applicants[0].vetRelationship).to.equal('Sibling'); + }); + it('should produce a semicolon separated list of relationships for third party certifiers', () => { + const transformed = JSON.parse( + transformForSubmit(formConfig, { + data: { + certifierRole: 'other', certifierRelationship: { - relationshipToVeteran: 'other', - otherRelationshipToVeteran: 'Sibling', + relationshipToVeteran: { + spouse: true, + parent: true, + thirdParty: false, + }, }, }, }), ); - expect(transformed.certification.relationship).to.equal('Sibling'); + expect(transformed.certification.relationship).to.equal('spouse; parent'); }); it('should insert blank values as needed', () => { const transformed = JSON.parse( diff --git a/src/applications/ivc-champva/10-10D/tests/unit/helpers/helpers.unit.spec.js b/src/applications/ivc-champva/10-10D/tests/unit/helpers/helpers.unit.spec.js index 22a1e96b1e6a..7d61f0f7603f 100644 --- a/src/applications/ivc-champva/10-10D/tests/unit/helpers/helpers.unit.spec.js +++ b/src/applications/ivc-champva/10-10D/tests/unit/helpers/helpers.unit.spec.js @@ -103,7 +103,6 @@ describe('populateFirstApplicant', () => { const formData = { applicants: [{ applicantName: { first: 'Test' } }] }; const result = populateFirstApplicant( formData, - '', newAppInfo.name, newAppInfo.email, newAppInfo.phone, @@ -118,7 +117,6 @@ describe('populateFirstApplicant', () => { const formData = {}; const result = populateFirstApplicant( formData, - '', newAppInfo.name, newAppInfo.email, newAppInfo.phone, @@ -130,7 +128,7 @@ describe('populateFirstApplicant', () => { describe('page15a depends function', () => { const isApp = { - certifierRelationship: { relationshipToVeteran: { applicant: true } }, + certifierRole: 'applicant', certifierAddress: { street: '123' }, }; const notApp = { diff --git a/src/applications/ivc-champva/10-10D/tests/unit/helpers/validations.unit.spec.js b/src/applications/ivc-champva/10-10D/tests/unit/helpers/validations.unit.spec.js new file mode 100644 index 000000000000..7152dfa79124 --- /dev/null +++ b/src/applications/ivc-champva/10-10D/tests/unit/helpers/validations.unit.spec.js @@ -0,0 +1,66 @@ +import { expect } from 'chai'; +import { fieldsMustMatchValidation } from '../../../helpers/validations'; + +describe('fieldsMustMatchValidation helper', () => { + let errorMessage = []; + + const errors = { + addError: message => { + errorMessage.push(message || ''); + }, + }; + + beforeEach(() => { + errorMessage = []; + }); + + it('should add error message when certifierPhone does not match applicantPhone', () => { + expect(errorMessage[0]).to.be.undefined; + + const props = { + page: { certifierPhone: '' }, + formData: { + certifierRole: 'applicant', + applicants: [{ applicantPhone: '1231231234' }], + sponsorPhone: '', + }, + }; + + fieldsMustMatchValidation( + { + certifierPhone: errors, + }, + props.page, + props.formData, + 'certifierPhone', + 'applicantPhone', + 'sponsorPhone', + ); + expect(errorMessage.length > 0).to.be.true; + }); + + it('should add error message when certifierName does not match veteransFullName', () => { + expect(errorMessage[0]).to.be.undefined; + + const props = { + page: { certifierName: { first: '', last: '' } }, + formData: { + certifierRole: 'sponsor', + applicants: [{ applicantName: { first: '', last: '' } }], + veteransFullName: { first: 'x', last: 'y' }, + }, + }; + + fieldsMustMatchValidation( + { + certifierName: { first: errors, last: errors }, + }, + props.page, + props.formData, + 'certifierName', + 'applicantName', + 'veteransFullName', + ); + expect(errorMessage.length > 0).to.be.true; + }); +}); diff --git a/src/applications/ivc-champva/10-10D/tests/unit/pages/combinedPageTests.unit.spec.js b/src/applications/ivc-champva/10-10D/tests/unit/pages/combinedPageTests.unit.spec.js index af6f4ddcf2e5..1581cfe07c75 100644 --- a/src/applications/ivc-champva/10-10D/tests/unit/pages/combinedPageTests.unit.spec.js +++ b/src/applications/ivc-champva/10-10D/tests/unit/pages/combinedPageTests.unit.spec.js @@ -1,4 +1,5 @@ import React from 'react'; +import { expect } from 'chai'; import { testComponentRender } from '../../../../shared/tests/pages/pageTests.spec'; import { ApplicantDependentStatusPage, @@ -19,6 +20,10 @@ import { ApplicantRelOriginPage, ApplicantRelOriginReviewPage, } from '../../../pages/ApplicantRelOriginPage'; +import { + SignerContactInfoPage, + signerContactOnGoForward, +} from '../../../pages/SignerContactInfoPage'; import mockData from '../../e2e/fixtures/data/test-data.json'; // Basic render tests: @@ -76,3 +81,37 @@ testComponentRender( title={() => {}} />, ); +testComponentRender( + 'SignerContactInfoPage', + , +); +testComponentRender( + 'SignerContactInfoPage', + , +); +describe('SignerContactOnGoForward', () => { + it("should copy certifier info to sponsor if certifierRole === 'sponsor'", () => { + const props = { + data: { + certifierRole: 'sponsor', + certifierName: { first: 'first', last: 'last' }, + }, + }; + signerContactOnGoForward(props); // operates directly on `props` + expect(props.data.veteransFullName.first).to.eq( + props.data.certifierName.first, + ); + }); + it("should copy certifier info to applicant array if certifierRole === 'applicant'", () => { + const props = { + data: { + certifierRole: 'applicant', + certifierName: { first: 'first', last: 'last' }, + }, + }; + signerContactOnGoForward(props); // operates directly on `props` + expect(props.data.applicants[0].applicantName.first).to.eq( + props.data.certifierName.first, + ); + }); +});