diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/actions.js b/src/applications/survivor-dependent-education-benefit/22-5490/actions.js index 367218635842..fcd303d8e00a 100644 --- a/src/applications/survivor-dependent-education-benefit/22-5490/actions.js +++ b/src/applications/survivor-dependent-education-benefit/22-5490/actions.js @@ -166,25 +166,3 @@ export function fetchClaimStatus(selectedChapter) { }); }; } - -// export function fetchDirectDeposit() { -// const ddEndpoint = LIGHTHOUSE_DIRECT_DEPOSIT_ENDPOINT; - -// return async dispatch => { -// dispatch({ type: FETCH_DIRECT_DEPOSIT }); - -// return apiRequest(ddEndpoint) -// .then(response => { -// dispatch({ -// type: FETCH_DIRECT_DEPOSIT_SUCCESS, -// response, -// }); -// }) -// .catch(errors => { -// dispatch({ -// type: FETCH_DIRECT_DEPOSIT_FAILED, -// errors, -// }); -// }); -// }; -// } diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/components/CustomEmailField.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/components/CustomEmailField.jsx index 27a0dfdeb2e4..59cc4d2daeb5 100644 --- a/src/applications/survivor-dependent-education-benefit/22-5490/components/CustomEmailField.jsx +++ b/src/applications/survivor-dependent-education-benefit/22-5490/components/CustomEmailField.jsx @@ -49,7 +49,6 @@ const mapStateToProps = state => { duplicateEmail: state?.data?.duplicateEmail, mobilePhone: state?.form?.data['view:phoneNumbers']?.mobilePhoneNumber?.phone, - showMebEnhancements08: state?.featureToggles?.showMebEnhancements08, formData: state?.form?.data, }; }; diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/config/form.js b/src/applications/survivor-dependent-education-benefit/22-5490/config/form.js index c873e070fe13..490fcc0497ec 100644 --- a/src/applications/survivor-dependent-education-benefit/22-5490/config/form.js +++ b/src/applications/survivor-dependent-education-benefit/22-5490/config/form.js @@ -196,10 +196,6 @@ const formConfig = { errors.addError('Please enter your first name'); } else if (field.length > 20) { errors.addError('Must be 20 characters or less'); - } else if (field[0] === ' ' || field[0] === "'") { - errors.addError( - 'First character must be a letter with no leading space.', - ); } } else if (!isValidName(field)) { errors.addError( @@ -214,11 +210,7 @@ const formConfig = { 'ui:validations': [ (errors, field) => { if (isValidName(field)) { - if (field[0] === ' ' || field[0] === "'") { - errors.addError( - 'First character must be a letter with no leading space.', - ); - } else if (field.length > 20) { + if (field.length > 20) { errors.addError('Must be 20 characters or less'); } } else if (!isValidName(field)) { @@ -240,14 +232,6 @@ const formConfig = { errors.addError('Must be 2 characters or more'); } else if (field.length > 26) { errors.addError('Must be 26 characters or less'); - } else if ( - field[0] === ' ' || - field[0] === "'" || - field[0] === '-' - ) { - errors.addError( - 'First character must be a letter with no leading space.', - ); } } else if (!isValidName(field)) { errors.addError( diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/containers/IntroductionPage.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/containers/IntroductionPage.jsx index 37af2e104c3c..2016ff4dec42 100644 --- a/src/applications/survivor-dependent-education-benefit/22-5490/containers/IntroductionPage.jsx +++ b/src/applications/survivor-dependent-education-benefit/22-5490/containers/IntroductionPage.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { connect } from 'react-redux'; import { getIntroState } from 'platform/forms/save-in-progress/selectors'; @@ -169,12 +170,26 @@ export const IntroductionPage = ({ ); }; +IntroductionPage.propTypes = { + route: PropTypes.shape({ + formConfig: PropTypes.shape({ + prefillEnabled: PropTypes.bool, + savedFormMessages: PropTypes.arrayOf(PropTypes.string), + }), + pageList: PropTypes.arrayOf(PropTypes.object), + }).isRequired, + isLOA3: PropTypes.bool, + isLoggedIn: PropTypes.bool, + isPersonalInfoFetchFailed: PropTypes.bool, + showMeb5490EMaintenanceAlert: PropTypes.bool, +}; + const mapStateToProps = state => ({ ...getIntroState(state), ...getAppData(state), - isPersonalInfoFetchFailed: state.data.isPersonalInfoFetchFailed || false, + isPersonalInfoFetchFailed: state.data?.isPersonalInfoFetchFailed || false, showMeb5490EMaintenanceAlert: - state.featureToggles.showMeb5490EMaintenanceAlert, + state.featureToggles?.showMeb5490EMaintenanceAlert, }); export default connect(mapStateToProps)(IntroductionPage); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/components/CustomPhoneNumberField.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/CustomPhoneNumberField.unit.spec.jsx similarity index 65% rename from src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/components/CustomPhoneNumberField.unit.spec.jsx rename to src/applications/survivor-dependent-education-benefit/22-5490/tests/components/CustomPhoneNumberField.unit.spec.jsx index d5bfbbde879e..411fcbc22799 100644 --- a/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/components/CustomPhoneNumberField.unit.spec.jsx +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/CustomPhoneNumberField.unit.spec.jsx @@ -1,14 +1,11 @@ import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; import { Provider } from 'react-redux'; -import { render } from '@testing-library/react'; import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import { expect } from 'chai'; - -import { $ } from '@department-of-veterans-affairs/platform-forms-system/ui'; -import CustomPhoneNumberField from '../../../components/CustomPhoneNumberField'; +import CustomPhoneNumberField from '../../components/CustomPhoneNumberField'; -const initialData = { +const initialState = { user: { profile: { userFullName: { @@ -42,18 +39,24 @@ const initialData = { }, }, }, + schema: { + type: 'number', + }, + options: { + inputType: 'number', + }, }; describe('CustomPhoneNumberField', () => { - const middleware = [thunk]; - const mockStore = configureStore(middleware); + const mockStore = configureStore(); + const store = mockStore(initialState); xit('should render with data', () => { - const { container } = render( - + const wrapper = mount( + , ); - expect($('.personal-info-header', container)).to.exist; + expect(wrapper.text()).to.include('John M Doe'); }); }); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/DirectDepositViewField.unit.spec.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/DirectDepositViewField.unit.spec.js new file mode 100644 index 000000000000..f1157a4851d3 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/DirectDepositViewField.unit.spec.js @@ -0,0 +1,36 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import DirectDepositViewField from '../../components/DirectDepositViewField'; + +describe('DirectDepositViewField component', () => { + const initialState = { + bankAccount: { + accountType: 'checking', + accountNumber: '123123123', + routingNumber: '321321321', + }, + }; + + it('renders the DirectDepositViewField footer', () => { + expect(); + }); + + it('should render obfuscated checking account information', () => { + const wrapper = mount(); + expect(wrapper.text()).to.include('Checking account'); + expect(wrapper.text()).to.include('●●●●●1321'); + expect(wrapper.text()).to.include('●●●●●3123'); + wrapper.unmount(); + }); + + it('should render account information when no account type is provided', () => { + initialState.bankAccount.accountType = ''; + + const wrapper = mount(); + expect(wrapper.text()).to.include('Account'); + expect(wrapper.text()).to.include('●●●●●1321'); + expect(wrapper.text()).to.include('●●●●●3123'); + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/DuplicateContactInfoModal.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/DuplicateContactInfoModal.unit.spec.jsx new file mode 100644 index 000000000000..73c82c492cf9 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/DuplicateContactInfoModal.unit.spec.jsx @@ -0,0 +1,40 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; + +import DuplicateContactInfoModal from '../../components/DuplicateContactInfoModal'; + +const initialState = { + form: { + data: { + email: { + email: 'test@test.com', + }, + }, + }, + data: { + duplicateEmail: [{ dupe: true, acknowledged: undefined }], + duplicatePhone: [], + openModal: true, + }, +}; + +describe('DuplicateContactInfoModal', () => { + const mockStore = configureStore(); + + const store = mockStore(initialState); + + it('should render with data', () => { + const wrapper = mount( + + + , + ); + + expect(wrapper.text()).to.include('This will impact how we:'); + + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/EmailReviewField.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/EmailReviewField.unit.spec.jsx new file mode 100644 index 000000000000..21f710c58122 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/EmailReviewField.unit.spec.jsx @@ -0,0 +1,17 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import EmailReviewField from '../../components/EmailReviewField'; + +describe('EmailReviewField component', () => { + it('renders the EmailReviewField footer', () => { + const wrapper = mount( + +
+ , + ); + expect(wrapper.text()).to.include('test'); + + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/FormFooter.unit.spec.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/FormFooter.unit.spec.js new file mode 100644 index 000000000000..5028581e4349 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/FormFooter.unit.spec.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import FormFooter from '../../components/FormFooter'; + +describe('FormFooter component', () => { + it('renders the FormFooter footer', () => { + const wrapper = mount(); + + expect(wrapper.text()).to.include( + 'If you need help with your application or have questions about', + ); + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/MailingAddressViewField.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/MailingAddressViewField.unit.spec.jsx new file mode 100644 index 000000000000..be4ddfe42f12 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/MailingAddressViewField.unit.spec.jsx @@ -0,0 +1,26 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import MailingAddressViewField from '../../components/MailingAddressViewField'; + +describe('MailingAddressViewField component', () => { + const initialState = { + address: { + street: '123 true st', + street2: '#1', + city: 'Fairfax', + state: 'VA', + postalCode: '22042', + }, + }; + + it('should render the mailing address', () => { + const wrapper = mount(); + expect(wrapper.text()).to.include('123 true st'); + expect(wrapper.text()).to.include('#1'); + expect(wrapper.text()).to.include('Fairfax'); + expect(wrapper.text()).to.include('VA'); + expect(wrapper.text()).to.include('22042'); + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/ObfuscateReviewField.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/ObfuscateReviewField.unit.spec.jsx new file mode 100644 index 000000000000..cc63cf521401 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/ObfuscateReviewField.unit.spec.jsx @@ -0,0 +1,52 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import ObfuscateReviewField from '../../components/ObfuscateReviewField'; + +describe('ObfuscateReviewField component', () => { + it('renders without crashing', () => { + const uiSchema = { 'ui:title': 'Obfuscated Title' }; + const formData = 'Sensitive Information'; + + const wrapper = mount( + + + , + ); + + expect(wrapper.exists()).to.be.true; + wrapper.unmount(); + }); + + it('displays the title from uiSchema', () => { + const uiSchema = { 'ui:title': 'Obfuscated Title' }; + const formData = 'Sensitive Information'; + + const wrapper = mount( + + + , + ); + + expect(wrapper.find('dt').text()).to.equal('Obfuscated Title'); + wrapper.unmount(); + }); + + it('obfuscates the formData content', () => { + const uiSchema = { 'ui:title': 'Obfuscated Title' }; + const formData = 'Sensitive Information'; + + const wrapper = mount( + + + , + ); + + const obfuscatedText = wrapper.find('dd').text(); + + // Adjusted expectation to check for `●` characters + expect(obfuscatedText).to.not.equal(formData); + expect(obfuscatedText).to.match(/●+/); // Checks if the obfuscation contains `●` characters + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PersonalInformation.unit.spec.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PersonalInformation.unit.spec.js new file mode 100644 index 000000000000..f28bf8838210 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PersonalInformation.unit.spec.js @@ -0,0 +1,51 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import PersonalInformation from '../../components/PersonalInformation'; + +describe('PersonalInformation component', () => { + const mockStore = configureStore(); + const initialState = { + user: { + profile: { + userFullName: { + first: 'John', + middle: 'M', + last: 'Doe', + }, + dob: '1990-01-01', + }, + }, + }; + + it('renders the PersonalInformation footer', () => { + expect(); + }); + + it('should render the user full name and formatted date of birth', () => { + const store = mockStore(initialState); + const wrapper = mount( + + + , + ); + expect(wrapper.text()).to.include('John M Doe'); + expect(wrapper.text()).to.include('Date of birth: January 1, 1990'); + wrapper.unmount(); + }); + + it('should render the user full name and formatted date of birth', () => { + initialState.user.profile.dob = ''; + const store = mockStore(initialState); + const wrapper = mount( + + + , + ); + expect(wrapper.text()).to.include('John M Doe'); + expect(wrapper.text()).to.include('Date of birth: Not available'); + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PersonalInformationReviewField.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PersonalInformationReviewField.unit.spec.jsx new file mode 100644 index 000000000000..66d85058367e --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PersonalInformationReviewField.unit.spec.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import { Provider } from 'react-redux'; +import configureStore from 'redux-mock-store'; +import PersonalInformationReviewField from '../../components/PersonalInformationReviewField'; + +describe('PersonalInformationReviewField component', () => { + const mockStore = configureStore(); + const initialState = { + user: { + profile: { + userFullName: { + first: 'test', + middle: 'T', + last: 'testerson', + }, + dob: '1990-01-01', + }, + }, + }; + it('renders the PersonalInformationReviewField footer', () => { + const store = mockStore(initialState); + + const wrapper = mount( + + + , + ); + expect(wrapper.text()).to.include('test T testerson'); + + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PhoneReviewField.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PhoneReviewField.unit.spec.jsx new file mode 100644 index 000000000000..2b3972d04351 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PhoneReviewField.unit.spec.jsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import PhoneReviewField from '../../components/PhoneReviewField'; + +describe('PhoneReviewField component', () => { + it('renders the PhoneReviewField footer', () => { + const formData = { + phone: '3333333333', + }; + const uiSchema = { + phone: { + 'ui:title': 'Review Phone', + }, + }; + const wrapper = mount( + , + ); + expect(wrapper.text()).to.include('3333333333'); + expect(wrapper.text()).to.include('Review Phone'); + + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PhoneViewField.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PhoneViewField.unit.spec.jsx new file mode 100644 index 000000000000..13de4d4b29e0 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PhoneViewField.unit.spec.jsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import PhoneViewField from '../../components/PhoneViewField'; + +describe('PhoneViewField component', () => { + it('renders the PhoneViewField footer', () => { + const formData = { + phone: '3333333333', + }; + const wrapper = mount(); + expect(wrapper.text()).to.include('333-333-3333'); + + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PreSubmitInfo.uniit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PreSubmitInfo.uniit.spec.jsx new file mode 100644 index 000000000000..a90724f4353d --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/PreSubmitInfo.uniit.spec.jsx @@ -0,0 +1,42 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import CustomPreSubmitInfo from '../../components/PreSubmitInfo'; + +describe(' component', () => { + let wrapper; + + beforeEach(() => { + wrapper = mount(); + }); + + afterEach(() => { + wrapper.unmount(); + }); + + it('renders the note text with the correct warning message', () => { + expect(wrapper.text()).to.include( + 'According to federal law, there are criminal penalties, including a fine and/or imprisonment for up to 5 years, for withholding information or for providing incorrect information (See 18 U.S.C. 1001).', + ); + }); + + it('renders a link with the correct href and target attributes', () => { + const link = wrapper.find('a'); + expect(link.prop('href')).to.equal('https://www.va.gov/privacy-policy/'); + expect(link.prop('target')).to.equal('_blank'); + expect(link.prop('rel')).to.include('noreferrer'); + }); + + it('has the correct aria-label for the link', () => { + const link = wrapper.find('a'); + expect(link.prop('aria-label')).to.equal( + 'Privacy policy, will open in new tab', + ); + }); + + it('renders the "Note:" label in bold', () => { + const strongTag = wrapper.find('strong'); + expect(strongTag).to.have.lengthOf(1); + expect(strongTag.text()).to.equal('Note:'); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/YesNoReviewField.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/YesNoReviewField.unit.spec.jsx new file mode 100644 index 000000000000..22dd9585f751 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/components/YesNoReviewField.unit.spec.jsx @@ -0,0 +1,20 @@ +import React from 'react'; +import { expect } from 'chai'; +import { mount } from 'enzyme'; +import YesNoReviewField from '../../components/YesNoReviewField'; + +describe('YesNoReviewField component', () => { + it('renders the YesNoReviewField footer', () => { + const uiSchema = { + 'ui:title': 'test title', + }; + const wrapper = mount( + +
+ , + ); + expect(wrapper.text()).to.include('test title'); + + wrapper.unmount(); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/containers/ConfirmationPage.unit.spec.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/containers/ConfirmationPage.unit.spec.js new file mode 100644 index 000000000000..e983b08ca495 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/containers/ConfirmationPage.unit.spec.js @@ -0,0 +1,73 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { render } from '@testing-library/react'; +import { expect } from 'chai'; + +import ConfirmationPage from '../../containers/ConfirmationPage'; + +describe('', () => { + // Utility function to create mock store and mock data + const getData = ({ formId = '5490', fullName = {}, chosenBenefit } = {}) => ({ + mockStore: { + getState: () => ({ + form: { + formId, + data: { + fullName: { + first: fullName.first || 'Jane', + middle: fullName.middle || 'A', + last: fullName.last || 'Doe', + suffix: fullName.suffix || '', + }, + chosenBenefit, + }, + submission: {}, + }, + confirmationError: null, + confirmationLoading: false, + }), + subscribe: () => {}, + dispatch: () => {}, + }, + }); + + // Render the component wrapped in the Redux provider + const renderComponent = ({ mockStore }) => + render( + + + , + ); + + it('should render the UnderReviewConfirmationFry component when chosenBenefit is "fry"', () => { + const { mockStore } = getData({ chosenBenefit: 'fry' }); + const { getByText } = renderComponent({ mockStore }); + + // Check for specific text indicating the Fry Scholarship component is rendered + expect( + getByText( + 'We’ll review your eligibility for the Fry Scholarship (Chapter 33).', + ), + ).to.exist; + }); + + it('should render the UnderReviewConfirmationDEAChapter35 component when chosenBenefit is "dea"', () => { + const { mockStore } = getData({ chosenBenefit: 'dea' }); + const { getByText } = renderComponent({ mockStore }); + + // Check for specific text indicating DEA Chapter 35 component is rendered + expect( + getByText( + "We’ll review your eligibility for the Survivors' and Dependents' Educational Assistance (Chapter 35).", + ), + ).to.exist; + }); + + it('should render nothing if chosenBenefit is neither "fry" nor "dea"', () => { + const { mockStore } = getData({ chosenBenefit: 'unknown' }); + const { container } = renderComponent({ mockStore }); + + // Expect the container to be empty, as no component should render for "unknown" + expect(container.innerHTML).to.equal(''); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/containers/IntroductionPage.unit.spec.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/containers/IntroductionPage.unit.spec.js new file mode 100644 index 000000000000..2ca6290fbfc2 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/containers/IntroductionPage.unit.spec.js @@ -0,0 +1,130 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { render } from '@testing-library/react'; +import { expect } from 'chai'; +import sinon from 'sinon'; + +import IntroductionPage from '../../containers/IntroductionPage'; +import formConfig from '../../config/form'; + +describe('', () => { + const selectors = { + wrapper: '.schemaform-intro', + title: '[data-testid="form-title"]', + introText: 'strong', // Selecting the introductory tag in the paragraph + processTimeline: 'va-process-list', + ombInfo: 'va-omb-info', + startButton: '[data-testid="start-button"]', // Make sure to add `data-testid="start-button"` in the component + }; + + const getData = ( + isLoggedIn = false, + isLOA3 = false, + isPersonalInfoFetchFailed = false, + showMeb5490EMaintenanceAlert = false, + ) => ({ + props: { + route: { + pageList: [{ path: '/introduction' }, { path: '/next', formConfig }], + formConfig: { + prefillEnabled: true, + savedFormMessages: ['Your form is saved.'], + }, + }, + router: { + push: sinon.spy(), + }, + isLoggedIn, + isLOA3, + isPersonalInfoFetchFailed, + showMeb5490EMaintenanceAlert, + }, + mockStore: { + getState: () => ({ + form: { + formId: formConfig.formId, + data: {}, + loadedData: { + metadata: {}, + }, + lastSavedDate: null, + migrations: [], + }, + user: { + login: { + currentlyLoggedIn: isLoggedIn, + }, + profile: { + loading: false, + loa: { current: isLOA3 ? 3 : null }, + savedForms: [], + prefillsAvailable: [], + }, + }, + scheduledDowntime: { + globalDowntime: null, + isReady: true, + isPending: false, + serviceMap: { + get: () => {}, + }, + dismissedDowntimeWarnings: [], + }, + data: { + isPersonalInfoFetchFailed, + }, + featureToggles: { + showMeb5490EMaintenanceAlert, + }, + }), + subscribe: () => {}, + dispatch: () => {}, + }, + }); + + const renderComponent = ({ mockStore, props }) => + render( + + + , + ); + + context('when the page renders', () => { + it('should contain the correct page title and introductory text', () => { + const { mockStore, props } = getData(); + const { container, getByText } = renderComponent({ mockStore, props }); + + expect(container.querySelector(selectors.wrapper)).to.exist; + expect(getByText('Apply for education benefits as an eligible dependant')) + .to.exist; + expect( + getByText( + "Form 22-5490 (Dependent's Application for VA Education Benefits)", + ), + ).to.exist; + expect(container.querySelector(selectors.introText)).to.exist; + }); + + it('should contain the process timeline for steps to get started with the application', () => { + const { mockStore, props } = getData(); + const { container } = renderComponent({ mockStore, props }); + expect(container.querySelector(selectors.processTimeline)).to.exist; + }); + }); + + context('conditional rendering tests', () => { + it('should not show the start button if maintenance alert is active', () => { + const { mockStore, props } = getData(true, true, false, true); // Enable maintenance alert + const { container } = renderComponent({ mockStore, props }); + const startButton = container.querySelector(selectors.startButton); + expect(startButton).to.not.exist; + }); + + it('should not show the start button if user is not LOA3', () => { + const { mockStore, props } = getData(true, false); // Set LOA3 to false + const { container } = renderComponent({ mockStore, props }); + const startButton = container.querySelector(selectors.startButton); + expect(startButton).to.not.exist; + }); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/fixtures/data/form-submission-test-data.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/fixtures/data/form-submission-test-data.js index db933d18d675..45d5605a83ca 100644 --- a/src/applications/survivor-dependent-education-benefit/22-5490/tests/fixtures/data/form-submission-test-data.js +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/fixtures/data/form-submission-test-data.js @@ -1,146 +1,2733 @@ export const submissionForm = { - claimantId: '1000000000000246', - loanPayment: 'No', - 'view:loanPaymentAdditionalInfo': {}, - seniorRotcCommission: 'Yes', - 'view:rotcCommissionAdditionalInfo': {}, - federallySponsoredAcademy: 'Yes', - 'view:academyCommissionAdditionalInfo': {}, - activeDutyKicker: 'Yes', - 'view:activeDutyKickerAdditionalInfo': {}, - selectedReserveKicker: 'No', - 'view:post911Notice': {}, - 'view:benefitSelection': { - benefitRelinquished: 'CannotRelinquish', + submission: { + status: false, + errorMessage: false, + id: false, + timestamp: false, + hasAttemptedSubmit: false, }, - 'view:activeDutyNotice': {}, - benefitEffectiveDate: '2021-02-02', - 'view:effectiveDateNotes': {}, - 'view:subHeading': {}, - toursOfDuty: [ - { - dateRange: { - from: '2011-08-01', - to: '2014-07-30', - }, - exclusionPeriods: [ - { - from: '2011-08-01', - to: '2011-09-14', - }, - { - from: '2011-11-01', - to: '2011-12-14', - }, - ], - separationReason: 'Expiration term of service', - serviceBranch: 'Navy', - serviceCharacter: 'Honorable', - trainingPeriods: [ - { - from: '2011-08-01', - to: '2011-09-14', - }, - { - from: '2011-11-01', - to: '2011-12-14', - }, - ], - }, - { - dateRange: { - from: '2015-04-04', - to: '2017-10-12', - }, - separationReason: 'Disability', - serviceBranch: 'Navy', - serviceCharacter: 'Honorable', - }, - ], - 'view:serviceHistory': { - serviceHistoryIncorrect: true, + formId: '22-5490', + loadedData: { + formData: {}, + metadata: {}, }, - incorrectServiceHistoryExplanation: { - incorrectServiceHistoryText: 'Service periods are missing.', + reviewPageView: { + openChapters: {}, + viewedPages: {}, }, - 'view:contactMethodIntro': {}, - contactMethod: 'Email', - 'view:receiveTextMessages': { - receiveTextMessages: 'Yes, send me text message notifications', - }, - 'view:textMessagesAlert': {}, - 'view:subHeadings': {}, - 'view:mailingAddress': { - livesOnMilitaryBase: false, - livesOnMilitaryBaseInfo: {}, - address: { - city: 'Austin', - country: 'USA', - state: 'NY', - postalCode: '00662', - street: '1493 Martin Luther King Rd', - street2: 'Apt 1', - addressLine3: null, - addressPou: 'CORRESPONDENCE', - addressType: 'DOMESTIC', - countryName: 'United States', - countryCodeFips: 'US', - countryCodeIso2: 'US', - countryCodeIso3: 'USA', - createdAt: '2018-04-21T20:09:50Z', - effectiveEndDate: '2018-04-21T20:09:50Z', - effectiveStartDate: '2018-04-21T20:09:50Z', - id: 123, - internationalPostalCode: '54321', - province: 'string', - sourceDate: '2018-04-21T20:09:50Z', - stateCode: 'NY', - updatedAt: '2018-04-21T20:09:50Z', - zipCode: '97062', - zipCodeSuffix: '1234', + trackingPrefix: 'edu-22-5490-', + formErrors: {}, + data: { + relativeDateOfBirth: '1932-02-05', + claimantFullName: { + first: 'Hector', + middle: 'M', + last: 'Allen', + suffix: 'Sr.', }, - }, - 'view:phoneNumbers': { - mobilePhoneNumber: { - phone: '5035551234', - isInternational: false, + 'view:directDeposit': { + bankAccount: { + accountNumber: '123123123', + accountType: 'checking', + routingNumber: '123123124', + }, }, - phoneNumber: { - phone: '5032222222', - isInternational: true, + 'view:learnMore': {}, + contactMethod: 'Email', + 'view:subHeadings': {}, + notificationMethod: 'no', + mailingAddressInput: { + livesOnMilitaryBaseInfo: {}, + address: { + street: '4000 Wilson Blvd', + street2: '#1', + city: 'ARLINGTON', + country: 'USA', + state: 'VA', + postalCode: '22203', + }, }, - }, - email: { - email: 'myemail@gmail.com', - confirmEmail: 'myemail@gmail.com', - }, - 'view:userFullName': { - userFullName: { - first: 'ANDREA', - middle: 'L', - last: 'MITCHELL', - suffix: 'Sr.', + 'view:EmailAndphoneNumbers': {}, + mobilePhone: { + phone: '5125554586', }, + homePhone: { + phone: '5125554585', + }, + email: 'test@test.com', + confirmEmail: 'test@test.com', + 'view:confirmDuplicateData': {}, + felonyOrWarrant: 'no', + remarriageDate: '2020-01-01', + remarriageStatus: 'yes', + marriageDate: '2015-01-01', + marriageStatus: 'divorced', + 'view:personalInformation': {}, + highSchoolDiploma: 'yes', + graduationDate: '2010-01-02', + 'view:subHeading': {}, + 'view:fry': {}, + 'view:dea': {}, + 'view:benefitInfo': {}, + chosenBenefit: 'dea', + relationShipToMember: 'spouse', + relativeSocialSecurityNumber: '321321321', + fullName: { + first: 'test', + middle: 't', + last: 'testerson', + }, + dateOfBirth: '1990-01-01', + ssn: '123123123', + duplicatePhone: [ + { + value: '', + dupe: '', + }, + ], }, - dateOfBirth: '1989-11-11', - 'view:reserveKickerAdditionalInfo': {}, - veteranFullName: { - first: 'Andrea', - middle: 'L', - last: 'Mitchell', - }, - userFullName: { - first: 'Andrea', - middle: 'L', - last: 'Mitchell', + pages: { + applicantInformation: { + uiSchema: { + relationShipToMember: { + 'ui:title': + "What's your relationship to the Veteran or service member whose benefits you'd like to use?", + 'ui:widget': 'radio', + 'ui:options': { + labels: { + spouse: 'Spouse', + child: 'Child', + }, + }, + }, + fullName: { + first: { + 'ui:title': 'First name', + 'ui:autocomplete': 'given-name', + 'ui:errorMessages': { + required: 'Please enter a first name', + }, + 'ui:validations': [null], + }, + last: { + 'ui:title': 'Last name', + 'ui:autocomplete': 'family-name', + 'ui:errorMessages': { + required: 'Please enter a last name', + }, + 'ui:validations': [null], + }, + middle: { + 'ui:title': 'Middle name', + 'ui:autocomplete': 'additional-name', + 'ui:validations': [null], + }, + suffix: { + 'ui:title': 'Suffix', + 'ui:autocomplete': 'honorific-suffix', + 'ui:options': { + widgetClassNames: 'form-select-medium', + }, + }, + 'ui:title': 'Veteran or service member information', + }, + dateOfBirth: { + 'ui:title': 'Date of birth', + 'ui:widget': 'date', + 'ui:validations': [null], + 'ui:errorMessages': { + pattern: 'Please enter a valid current or past date', + required: 'Please enter a date', + }, + }, + ssn: { + 'ui:title': 'Social Security number', + 'ui:options': { + widgetClassNames: 'usa-input-medium masked-ssn', + }, + 'ui:validations': [null], + 'ui:errorMessages': { + pattern: + 'Please enter a valid 9 digit Social Security number (dashes allowed)', + required: 'Please enter a Social Security number', + }, + }, + }, + schema: { + type: 'object', + required: ['relationShipToMember', 'fullName', 'ssn', 'dateOfBirth'], + properties: { + relationShipToMember: { + type: 'string', + enum: ['spouse', 'child'], + }, + fullName: { + type: 'object', + properties: { + first: { + type: 'string', + minLength: 1, + maxLength: 30, + }, + middle: { + type: 'string', + }, + last: { + type: 'string', + minLength: 1, + maxLength: 30, + }, + suffix: { + type: 'string', + enum: ['Jr.', 'Sr.', 'II', 'III', 'IV'], + }, + }, + required: ['first', 'last'], + }, + dateOfBirth: { + pattern: + '^(\\d{4}|XXXX)-(0[1-9]|1[0-2]|XX)-(0[1-9]|[1-2][0-9]|3[0-1]|XX)$', + type: 'string', + }, + ssn: { + type: 'string', + pattern: '^[0-9]{9}$', + }, + }, + }, + editMode: false, + }, + benefitSelection: { + uiSchema: { + 'view:subHeading': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'div', + key: null, + ref: null, + props: { + children: [ + { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Choose the benefit you’d like to apply for:', + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: [ + { + type: 'strong', + key: null, + ref: null, + props: { + children: 'Note:', + }, + _owner: null, + _store: {}, + }, + ' If you are eligible for both the Fry Scholarship and Survivors’ and Dependents’ Educational Assistance benefits, you’ll need to choose which one to use. Once you make this choice, you can’t switch to the other program.', + ], + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + 'view:fry': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'div', + key: null, + ref: null, + props: { + className: 'usa-alert background-color-only', + children: [ + { + type: 'h5', + key: null, + ref: null, + props: { + className: + 'vads-u-font-size--base vads-u-font-family--sans vads-u-font-weight--normal vads-u-margin-y--0', + children: 'CHAPTER 33', + }, + _owner: null, + _store: {}, + }, + { + type: 'h4', + key: null, + ref: null, + props: { + className: + 'vads-u-font-size--h3 vads-u-margin-top--0 vads-u-margin-bottom--2', + children: 'Fry Scholarship', + }, + _owner: null, + _store: {}, + }, + { + type: 'h4', + key: null, + ref: null, + props: { + className: + 'vads-u-font-size--h5 vads-u-margin-top--0 vads-u-margin-bottom--2', + children: + 'Receive up to 36 months of benefits, including:', + }, + _owner: null, + _store: {}, + }, + { + type: 'ul', + key: null, + ref: null, + props: { + className: + 'fry-dea-benefits-list vads-u-margin--0 vads-u-padding--0 vads-u-margin-bottom--3', + children: [ + { + type: 'li', + key: null, + ref: null, + props: { + children: [ + { + type: 'va-icon', + key: null, + ref: null, + props: { + size: 4, + icon: 'school', + className: 'fry-dea-benefit-selection-icon', + 'aria-hidden': 'true', + }, + _owner: null, + _store: {}, + }, + ' ', + 'Tuition & fees', + ], + }, + _owner: null, + _store: {}, + }, + { + type: 'li', + key: null, + ref: null, + props: { + children: [ + { + type: 'va-icon', + key: null, + ref: null, + props: { + size: 4, + icon: 'home', + className: 'fry-dea-benefit-selection-icon', + 'aria-hidden': 'true', + }, + _owner: null, + _store: {}, + }, + ' ', + 'Money for housing', + ], + }, + _owner: null, + _store: {}, + }, + { + type: 'li', + key: null, + ref: null, + props: { + children: [ + { + type: 'va-icon', + key: null, + ref: null, + props: { + size: 4, + icon: 'local_library', + className: 'fry-dea-benefit-selection-icon', + 'aria-hidden': 'true', + }, + _owner: null, + _store: {}, + }, + ' ', + 'Money for books & supplies', + ], + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + { + type: 'a', + key: null, + ref: null, + props: { + href: + 'https://www.va.gov/education/survivor-dependent-benefits/fry-scholarship/', + target: '_blank', + rel: 'noreferrer', + children: + 'Learn more about the Fry Scholarship education benefit', + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + 'view:dea': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'div', + key: null, + ref: null, + props: { + className: 'usa-alert background-color-only', + children: [ + { + type: 'h5', + key: null, + ref: null, + props: { + className: + 'vads-u-font-size--base vads-u-font-family--sans vads-u-font-weight--normal vads-u-margin-y--0', + children: 'DEA, CHAPTER 35', + }, + _owner: null, + _store: {}, + }, + { + type: 'h4', + key: null, + ref: null, + props: { + className: + 'vads-u-font-size--h3 vads-u-margin-top--0 vads-u-margin-bottom--2', + children: + "Survivors' and Dependents' Educational Assistance", + }, + _owner: null, + _store: {}, + }, + { + type: 'h4', + key: null, + ref: null, + props: { + className: + 'vads-u-font-size--h5 vads-u-margin-top--0 vads-u-margin-bottom--2', + children: + 'Receive up to 36 months of benefits, including:', + }, + _owner: null, + _store: {}, + }, + { + type: 'ul', + key: null, + ref: null, + props: { + className: + 'fry-dea-benefits-list vads-u-margin--0 vads-u-padding--0 vads-u-margin-bottom--3', + children: { + type: 'li', + key: null, + ref: null, + props: { + children: [ + { + type: 'va-icon', + key: null, + ref: null, + props: { + size: 4, + icon: 'attach_money', + className: 'fry-dea-benefit-selection-icon', + 'aria-hidden': 'true', + }, + _owner: null, + _store: {}, + }, + ' ', + 'Monthly stipened', + ], + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + { + type: 'a', + key: null, + ref: null, + props: { + href: + 'https://www.va.gov/education/survivor-dependent-benefits/dependents-education-assistance/', + target: '_blank', + rel: 'noreferrer', + children: 'Learn more about DEA education benefit', + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + 'view:benefitInfo': { + 'ui:description': { + key: null, + ref: null, + props: { + children: [ + { + type: 'span', + key: null, + ref: null, + props: { + className: + 'fry-dea-labels_label--main vads-u-padding-left--1', + children: + 'Which education benefit would you like to apply for?', + }, + _owner: null, + _store: {}, + }, + { + type: 'br', + key: null, + ref: null, + props: {}, + _owner: null, + _store: {}, + }, + { + type: 'br', + key: null, + ref: null, + props: {}, + _owner: null, + _store: {}, + }, + { + type: 'span', + key: null, + ref: null, + props: { + className: + 'fry-dea-labels_label--secondary fry-dea-input-message fry-dea-review-view-hidden vads-u-background-color--primary-alt-lightest vads-u-padding--1 vads-u-margin-top--1', + children: [ + { + type: 'va-icon', + key: null, + ref: null, + props: { + size: 3, + icon: 'info', + className: 'vads-u-margin-right--1', + 'aria-hidden': 'true', + }, + _owner: null, + _store: {}, + }, + ' ', + { + type: 'span', + key: null, + ref: null, + props: { + className: 'sr-only', + children: 'Informational Note:', + }, + _owner: null, + _store: {}, + }, + ' If you’re the child of a veteran or service member who died in the line of duty before August 1, 2011 you can use both Fry Scholarship and DEA and get up to 81 months of benefits. You’ll need to apply separately and use one program at a time.', + ], + }, + _owner: null, + _store: {}, + }, + { + type: 'br', + key: null, + ref: null, + props: {}, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + chosenBenefit: { + 'ui:title': 'Select one benefit', + 'ui:errorMessages': { + required: 'Please select an education benefit', + }, + 'ui:widget': 'radio', + 'ui:options': { + labels: { + fry: 'Fry Scholarship (Chapter 33)', + dea: + 'Survivors’ and Dependents’ Educational Assistance (DEA, Chapter 35)', + }, + widgetProps: { + fry: { + 'data-info': 'fry', + }, + dea: { + 'data-info': 'dea', + }, + }, + selectedProps: { + fry: { + 'aria-describedby': 'fry', + }, + dea: { + 'aria-describedby': 'dea', + }, + }, + }, + }, + }, + schema: { + type: 'object', + required: ['chosenBenefit'], + properties: { + 'view:subHeading': { + type: 'object', + properties: {}, + }, + 'view:fry': { + type: 'object', + properties: {}, + }, + 'view:dea': { + type: 'object', + properties: {}, + }, + 'view:benefitInfo': { + type: 'object', + properties: {}, + }, + chosenBenefit: { + type: 'string', + enum: ['fry', 'dea'], + }, + }, + }, + editMode: false, + }, + reviewPersonalInformation: { + CustomPageReview: { + compare: null, + }, + uiSchema: { + 'view:subHeadings': { + 'ui:description': { + key: null, + ref: null, + props: { + children: [ + { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Review your personal information', + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: [ + 'We have this personal information on file for you. Any updates you make will change the information for your education benefits only. If you want to update your personal information for other VA benefits, update your information on your', + ' ', + { + type: 'a', + key: null, + ref: null, + props: { + target: '_blank', + href: '/profile/personal-information', + children: 'profile', + }, + _owner: null, + _store: {}, + }, + '.', + ], + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: [ + { + type: 'strong', + key: null, + ref: null, + props: { + children: 'Note:', + }, + _owner: null, + _store: {}, + }, + ' If you want to request that we change your name or date of birth, you will need to send additional information. Learn more on how to change your legal name', + ' ', + { + type: 'a', + key: null, + ref: null, + props: { + target: '_blank', + href: + '/resources/how-to-change-your-legal-name-on-file-with-va/?_ga=2.13947071.963379013.1690376239-159354255.1663160782', + children: 'on file with VA.', + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + 'view:personalInformation': { + 'ui:description': { + type: { + compare: null, + }, + key: null, + ref: null, + props: {}, + _owner: null, + _store: {}, + }, + }, + highSchoolDiploma: { + 'ui:title': 'Did you earn a high school or equivalency certificate?', + 'ui:widget': 'radio', + 'ui:options': { + labels: { + yes: 'Yes', + no: 'No', + }, + }, + }, + graduationDate: { + 'ui:title': + 'When did you earn your high school diploma or equivalency?', + 'ui:widget': 'date', + 'ui:validations': [null], + 'ui:errorMessages': { + pattern: 'Please enter a valid current or past date', + required: 'Please enter a date', + }, + 'ui:options': {}, + }, + }, + schema: { + type: 'object', + required: ['highSchoolDiploma', 'graduationDate'], + properties: { + 'view:subHeadings': { + type: 'object', + properties: {}, + }, + 'view:personalInformation': { + type: 'object', + properties: {}, + }, + highSchoolDiploma: { + type: 'string', + enum: ['yes', 'no'], + }, + graduationDate: { + pattern: + '^(\\d{4}|XXXX)-(0[1-9]|1[0-2]|XX)-(0[1-9]|[1-2][0-9]|3[0-1]|XX)$', + type: 'string', + }, + }, + }, + editMode: false, + }, + marriageInformation: { + uiSchema: { + 'view:subHeadings': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Marriage information', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + marriageStatus: { + 'ui:title': + "What's the status of your marriage with your chosen Veteran or service member?", + 'ui:widget': 'radio', + 'ui:options': { + labels: { + married: 'Married', + divorced: 'Divorced (or divorce in progress)', + anulled: 'Marriage was annulled (or an annullment in progress)', + widowed: 'Widowed', + }, + }, + }, + }, + schema: { + type: 'object', + required: ['marriageStatus'], + properties: { + 'view:subHeadings': { + type: 'object', + properties: {}, + }, + marriageStatus: { + type: 'string', + enum: ['married', 'divorced', 'anulled', 'widowed'], + }, + }, + }, + editMode: false, + }, + marriageDate: { + uiSchema: { + 'view:subHeadings': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Marriage Date', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + marriageDate: { + 'ui:title': + 'When did you get married to your chosen Veteran or service member?', + 'ui:widget': 'date', + 'ui:validations': [null], + 'ui:errorMessages': { + pattern: 'Please enter a valid current or past date', + required: 'Please enter a date', + }, + }, + }, + schema: { + type: 'object', + required: ['marriageDate'], + properties: { + 'view:subHeadings': { + type: 'object', + properties: {}, + }, + marriageDate: { + pattern: + '^(\\d{4}|XXXX)-(0[1-9]|1[0-2]|XX)-(0[1-9]|[1-2][0-9]|3[0-1]|XX)$', + type: 'string', + }, + }, + }, + editMode: false, + }, + remarriageInformation: { + uiSchema: { + 'view:subHeadings': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Remarriage', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + remarriageStatus: { + 'ui:title': 'Have you been remarried since your divorce?', + 'ui:widget': 'radio', + 'ui:options': { + labels: { + yes: 'Yes', + no: 'No', + }, + }, + }, + }, + schema: { + type: 'object', + required: ['remarriageStatus'], + properties: { + 'view:subHeadings': { + type: 'object', + properties: {}, + }, + remarriageStatus: { + type: 'string', + enum: ['yes', 'no'], + }, + }, + }, + editMode: false, + }, + remarriageDate: { + uiSchema: { + 'view:subHeadings': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Remarriage Date', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + remarriageDate: { + 'ui:title': 'When did you get remarried?', + 'ui:widget': 'date', + 'ui:validations': [null], + 'ui:errorMessages': { + pattern: 'Please enter a valid current or past date', + required: 'Please enter a date', + }, + }, + }, + schema: { + type: 'object', + required: ['remarriageDate'], + properties: { + 'view:subHeadings': { + type: 'object', + properties: {}, + }, + remarriageDate: { + pattern: + '^(\\d{4}|XXXX)-(0[1-9]|1[0-2]|XX)-(0[1-9]|[1-2][0-9]|3[0-1]|XX)$', + type: 'string', + }, + }, + }, + editMode: false, + }, + outstandingFelony: { + uiSchema: { + 'view:subHeadings': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Outstanding felony', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + felonyOrWarrant: { + 'ui:title': + 'Do you or your chosen Veteran or service member have an outstanding felony or warrant?', + 'ui:widget': 'radio', + 'ui:options': { + labels: { + yes: 'Yes', + no: 'No', + }, + }, + }, + }, + schema: { + type: 'object', + required: ['felonyOrWarrant'], + properties: { + 'view:subHeadings': { + type: 'object', + properties: {}, + }, + felonyOrWarrant: { + type: 'string', + enum: ['yes', 'no'], + }, + }, + }, + editMode: false, + }, + contactInformation: { + uiSchema: { + 'view:subHeadings': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Review your phone number and email address', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + 'view:EmailAndphoneNumbers': { + 'ui:description': { + key: null, + ref: null, + props: { + children: [ + { + type: 'h4', + key: null, + ref: null, + props: { + children: 'We’ll use this information to:', + }, + _owner: null, + _store: {}, + }, + { + type: 'ul', + key: null, + ref: null, + props: { + children: [ + { + type: 'li', + key: null, + ref: null, + props: { + children: + 'Contact you if we have questions about your application', + }, + _owner: null, + _store: {}, + }, + { + type: 'li', + key: null, + ref: null, + props: { + children: + 'Tell you important information about your benefits', + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: + 'This is the contact information we have on file for you. If you notice any errors, please correct them now. Any updates you make here will be used for your education benefits only.', + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: [ + { + type: 'strong', + key: null, + ref: null, + props: { + children: 'Note:', + }, + _owner: null, + _store: {}, + }, + ' If you want to update your contact information for other VA benefits, you can do that from your profile.', + ], + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: { + type: 'a', + key: null, + ref: null, + props: { + target: '_blank', + href: + 'https://www.va.gov/resources/managing-your-vagov-profile/', + rel: 'noreferrer', + children: 'Go to your profile', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + mobilePhone: { + 'ui:options': { + hideLabelText: true, + showFieldLabel: false, + }, + phone: { + 'ui:widget': { + compare: null, + }, + 'ui:title': 'Mobile phone number', + 'ui:autocomplete': 'tel', + 'ui:errorMessages': { + pattern: + 'Please enter a 10-digit phone number (with or without dashes)', + minLength: + 'Please enter a 10-digit phone number (with or without dashes)', + required: 'Please enter a phone number', + }, + 'ui:options': { + widgetClassNames: 'va-input-medium-large', + }, + 'ui:validations': [null], + }, + isInternational: { + 'ui:title': 'This mobile phone number is international', + 'ui:options': {}, + }, + }, + homePhone: { + 'ui:options': { + hideLabelText: true, + showFieldLabel: false, + }, + phone: { + 'ui:title': 'Home phone number', + 'ui:autocomplete': 'tel', + 'ui:errorMessages': { + pattern: + 'Please enter a 10-digit phone number (with or without dashes)', + minLength: + 'Please enter a 10-digit phone number (with or without dashes)', + required: 'Please enter a phone number', + }, + 'ui:options': { + widgetClassNames: 'va-input-medium-large', + }, + 'ui:validations': [null], + }, + isInternational: { + 'ui:title': 'This home phone number is international', + 'ui:options': {}, + }, + }, + email: { + 'ui:title': 'Email address', + 'ui:errorMessages': { + format: + 'Enter a valid email address using the format email@domain.com. Your email address can only have letters, numbers, the @ symbol and a period, with no spaces.', + pattern: + 'Enter a valid email address using the format email@domain.com. Your email address can only have letters, numbers, the @ symbol and a period, with no spaces.', + required: 'Please enter an email address', + }, + 'ui:autocomplete': 'email', + 'ui:options': { + inputType: 'email', + }, + 'ui:widget': { + compare: null, + }, + }, + confirmEmail: { + 'ui:title': 'Confirm email address', + 'ui:errorMessages': { + format: + 'Enter a valid email address using the format email@domain.com. Your email address can only have letters, numbers, the @ symbol and a period, with no spaces.', + pattern: + 'Enter a valid email address using the format email@domain.com. Your email address can only have letters, numbers, the @ symbol and a period, with no spaces.', + required: 'Please enter an email address', + }, + 'ui:autocomplete': 'email', + 'ui:options': { + inputType: 'email', + hideOnReview: true, + }, + }, + 'ui:validations': [null], + 'view:confirmDuplicateData': { + 'ui:description': { + compare: null, + }, + }, + }, + schema: { + type: 'object', + required: ['email', 'confirmEmail'], + properties: { + 'view:subHeadings': { + type: 'object', + properties: {}, + }, + 'view:EmailAndphoneNumbers': { + type: 'object', + properties: {}, + }, + mobilePhone: { + type: 'object', + properties: { + phone: { + type: 'string', + pattern: '^\\d[-]?\\d(?:[0-9-]*\\d)?$', + }, + isInternational: { + type: 'boolean', + }, + }, + }, + homePhone: { + type: 'object', + properties: { + phone: { + type: 'string', + pattern: '^\\d[-]?\\d(?:[0-9-]*\\d)?$', + }, + isInternational: { + type: 'boolean', + 'ui:hidden': true, + }, + }, + }, + email: { + type: 'string', + format: 'email', + }, + confirmEmail: { + type: 'string', + format: 'email', + }, + 'view:confirmDuplicateData': { + type: 'object', + properties: {}, + }, + }, + }, + editMode: false, + }, + mailingAddress: { + uiSchema: { + 'view:subHeadings': { + 'ui:description': { + key: null, + ref: null, + props: { + children: [ + { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Review your mailing address', + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: + 'We’ll send any important information about your application to this address.', + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: + 'This is the mailing address we have on file for you. If you notice any errors, please correct them now. Any updates you make will change the information for your education benefits only.', + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: [ + { + type: 'strong', + key: null, + ref: null, + props: { + children: 'Note:', + }, + _owner: null, + _store: {}, + }, + ' If you want to update your personal information for other VA benefits, you can do that from your profile.', + ], + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: { + type: 'a', + key: null, + ref: null, + props: { + target: '_blank', + href: '/profile/personal-information', + children: 'Go to your profile', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + mailingAddressInput: { + 'ui:description': { + key: null, + ref: null, + props: { + children: [ + { + type: 'h4', + key: null, + ref: null, + props: { + className: + 'form-review-panel-page-header vads-u-font-size--h5 meb-review-page-only', + children: 'Mailing address', + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + className: 'meb-review-page-only', + children: + 'If you’d like to update your mailing address, please edit the form fields below.', + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + livesOnMilitaryBase: { + 'ui:title': { + type: 'span', + key: null, + ref: null, + props: { + id: 'LiveOnMilitaryBaseTooltip', + children: + 'I live on a United States military base outside of the country', + }, + _owner: null, + _store: {}, + }, + }, + livesOnMilitaryBaseInfo: { + 'ui:description': { + type: 'va-additional-info', + key: null, + ref: null, + props: { + trigger: 'Learn more about military base addresses', + children: { + type: 'p', + key: null, + ref: null, + props: { + className: 'vads-u-margin-top--0', + children: + 'U.S. military bases are considered a domestic address and a part of the United States.', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + address: { + 'ui:title': '', + 'ui:validations': [null], + 'ui:options': {}, + 'ui:order': [ + 'country', + 'street', + 'street2', + 'city', + 'state', + 'postalCode', + ], + country: { + 'ui:title': 'Country', + 'ui:disabled': false, + 'ui:options': {}, + }, + street: { + 'ui:title': 'Street address', + 'ui:errorMessages': { + required: 'Please enter your full street address', + }, + 'ui:validations': [null], + }, + street2: { + 'ui:title': 'Street address line 2', + 'ui:autocomplete': 'address-line2', + }, + street3: { + 'ui:title': 'Street address line 3', + 'ui:autocomplete': 'address-line3', + }, + city: { + 'ui:errorMessages': { + required: 'Please enter a valid city', + }, + 'ui:validations': [null], + 'ui:options': {}, + }, + state: { + 'ui:title': 'State/County/Province', + }, + postalCode: { + 'ui:errorMessages': { + required: 'Zip code must be 5 digits', + }, + 'ui:options': {}, + }, + }, + 'ui:options': { + hideLabelText: true, + showFieldLabel: false, + }, + }, + }, + schema: { + type: 'object', + properties: { + 'view:subHeadings': { + type: 'object', + properties: {}, + }, + mailingAddressInput: { + type: 'object', + properties: { + livesOnMilitaryBase: { + type: 'boolean', + }, + livesOnMilitaryBaseInfo: { + type: 'object', + properties: {}, + }, + address: { + type: 'object', + required: ['street', 'city', 'country', 'state', 'postalCode'], + properties: { + street: { + type: 'string', + minLength: 1, + maxLength: 50, + }, + street2: { + type: 'string', + minLength: 1, + maxLength: 50, + }, + city: { + type: 'string', + title: 'City', + }, + country: { + default: 'USA', + type: 'string', + enum: [ + 'USA', + 'AFG', + 'ALB', + 'DZA', + 'AND', + 'AGO', + 'AIA', + 'ATA', + 'ATG', + 'ARG', + 'ARM', + 'ABW', + 'AUS', + 'AUT', + 'AZE', + 'BHS', + 'BHR', + 'BGD', + 'BRB', + 'BLR', + 'BEL', + 'BLZ', + 'BEN', + 'BMU', + 'BTN', + 'BOL', + 'BIH', + 'BWA', + 'BVT', + 'BRA', + 'IOT', + 'BRN', + 'BGR', + 'BFA', + 'BDI', + 'KHM', + 'CMR', + 'CAN', + 'CPV', + 'CYM', + 'CAF', + 'TCD', + 'CHL', + 'CHN', + 'CXR', + 'CCK', + 'COL', + 'COM', + 'COG', + 'COD', + 'COK', + 'CRI', + 'CIV', + 'HRV', + 'CUB', + 'CYP', + 'CZE', + 'DNK', + 'DJI', + 'DMA', + 'DOM', + 'ECU', + 'EGY', + 'SLV', + 'GNQ', + 'ERI', + 'EST', + 'ETH', + 'FLK', + 'FRO', + 'FJI', + 'FIN', + 'FRA', + 'GUF', + 'PYF', + 'ATF', + 'GAB', + 'GMB', + 'GEO', + 'DEU', + 'GHA', + 'GIB', + 'GRC', + 'GRL', + 'GRD', + 'GLP', + 'GTM', + 'GIN', + 'GNB', + 'GUY', + 'HTI', + 'HMD', + 'HND', + 'HKG', + 'HUN', + 'ISL', + 'IND', + 'IDN', + 'IRN', + 'IRQ', + 'IRL', + 'ISR', + 'ITA', + 'JAM', + 'JPN', + 'JOR', + 'KAZ', + 'KEN', + 'KIR', + 'PRK', + 'KOR', + 'KWT', + 'KGZ', + 'LAO', + 'LVA', + 'LBN', + 'LSO', + 'LBR', + 'LBY', + 'LIE', + 'LTU', + 'LUX', + 'MAC', + 'MKD', + 'MDG', + 'MWI', + 'MYS', + 'MDV', + 'MLI', + 'MLT', + 'MTQ', + 'MRT', + 'MUS', + 'MYT', + 'MEX', + 'FSM', + 'MDA', + 'MCO', + 'MNG', + 'MSR', + 'MAR', + 'MOZ', + 'MMR', + 'NAM', + 'NRU', + 'NPL', + 'ANT', + 'NLD', + 'NCL', + 'NZL', + 'NIC', + 'NER', + 'NGA', + 'NIU', + 'NFK', + 'NOR', + 'OMN', + 'PAK', + 'PAN', + 'PNG', + 'PRY', + 'PER', + 'PHL', + 'PCN', + 'POL', + 'PRT', + 'QAT', + 'REU', + 'ROU', + 'RUS', + 'RWA', + 'SHN', + 'KNA', + 'LCA', + 'SPM', + 'VCT', + 'SMR', + 'STP', + 'SAU', + 'SEN', + 'SCG', + 'SYC', + 'SLE', + 'SGP', + 'SVK', + 'SVN', + 'SLB', + 'SOM', + 'ZAF', + 'SGS', + 'ESP', + 'LKA', + 'SDN', + 'SUR', + 'SWZ', + 'SWE', + 'CHE', + 'SYR', + 'TWN', + 'TJK', + 'TZA', + 'THA', + 'TLS', + 'TGO', + 'TKL', + 'TON', + 'TTO', + 'TUN', + 'TUR', + 'TKM', + 'TCA', + 'TUV', + 'UGA', + 'UKR', + 'ARE', + 'GBR', + 'URY', + 'UZB', + 'VUT', + 'VAT', + 'VEN', + 'VNM', + 'VGB', + 'WLF', + 'ESH', + 'YEM', + 'ZMB', + 'ZWE', + ], + enumNames: [ + 'United States', + 'Afghanistan', + 'Albania', + 'Algeria', + 'Andorra', + 'Angola', + 'Anguilla', + 'Antarctica', + 'Antigua', + 'Argentina', + 'Armenia', + 'Aruba', + 'Australia', + 'Austria', + 'Azerbaijan', + 'Bahamas', + 'Bahrain', + 'Bangladesh', + 'Barbados', + 'Belarus', + 'Belgium', + 'Belize', + 'Benin', + 'Bermuda', + 'Bhutan', + 'Bolivia', + 'Bosnia', + 'Botswana', + 'Bouvet Island', + 'Brazil', + 'British Indian Ocean Territories', + 'Brunei Darussalam', + 'Bulgaria', + 'Burkina Faso', + 'Burundi', + 'Cambodia', + 'Cameroon', + 'Canada', + 'Cape Verde', + 'Cayman', + 'Central African Republic', + 'Chad', + 'Chile', + 'China', + 'Christmas Island', + 'Cocos Islands', + 'Colombia', + 'Comoros', + 'Congo', + 'Democratic Republic of the Congo', + 'Cook Islands', + 'Costa Rica', + 'Ivory Coast', + 'Croatia', + 'Cuba', + 'Cyprus', + 'Czech Republic', + 'Denmark', + 'Djibouti', + 'Dominica', + 'Dominican Republic', + 'Ecuador', + 'Egypt', + 'El Salvador', + 'Equatorial Guinea', + 'Eritrea', + 'Estonia', + 'Ethiopia', + 'Falkland Islands', + 'Faroe Islands', + 'Fiji', + 'Finland', + 'France', + 'French Guiana', + 'French Polynesia', + 'French Southern Territories', + 'Gabon', + 'Gambia', + 'Georgia', + 'Germany', + 'Ghana', + 'Gibraltar', + 'Greece', + 'Greenland', + 'Grenada', + 'Guadeloupe', + 'Guatemala', + 'Guinea', + 'Guinea-Bissau', + 'Guyana', + 'Haiti', + 'Heard Island', + 'Honduras', + 'Hong Kong', + 'Hungary', + 'Iceland', + 'India', + 'Indonesia', + 'Iran', + 'Iraq', + 'Ireland', + 'Israel', + 'Italy', + 'Jamaica', + 'Japan', + 'Jordan', + 'Kazakhstan', + 'Kenya', + 'Kiribati', + 'North Korea', + 'South Korea', + 'Kuwait', + 'Kyrgyzstan', + 'Laos', + 'Latvia', + 'Lebanon', + 'Lesotho', + 'Liberia', + 'Libya', + 'Liechtenstein', + 'Lithuania', + 'Luxembourg', + 'Macao', + 'Macedonia', + 'Madagascar', + 'Malawi', + 'Malaysia', + 'Maldives', + 'Mali', + 'Malta', + 'Martinique', + 'Mauritania', + 'Mauritius', + 'Mayotte', + 'Mexico', + 'Micronesia', + 'Moldova', + 'Monaco', + 'Mongolia', + 'Montserrat', + 'Morocco', + 'Mozambique', + 'Myanmar', + 'Namibia', + 'Nauru', + 'Nepal', + 'Netherlands Antilles', + 'Netherlands', + 'New Caledonia', + 'New Zealand', + 'Nicaragua', + 'Niger', + 'Nigeria', + 'Niue', + 'Norfolk', + 'Norway', + 'Oman', + 'Pakistan', + 'Panama', + 'Papua New Guinea', + 'Paraguay', + 'Peru', + 'Philippines', + 'Pitcairn', + 'Poland', + 'Portugal', + 'Qatar', + 'Reunion', + 'Romania', + 'Russia', + 'Rwanda', + 'Saint Helena', + 'Saint Kitts and Nevis', + 'Saint Lucia', + 'Saint Pierre and Miquelon', + 'Saint Vincent and the Grenadines', + 'San Marino', + 'Sao Tome and Principe', + 'Saudi Arabia', + 'Senegal', + 'Serbia', + 'Seychelles', + 'Sierra Leone', + 'Singapore', + 'Slovakia', + 'Slovenia', + 'Solomon Islands', + 'Somalia', + 'South Africa', + 'South Georgia and the South Sandwich Islands', + 'Spain', + 'Sri Lanka', + 'Sudan', + 'Suriname', + 'Swaziland', + 'Sweden', + 'Switzerland', + 'Syrian Arab Republic', + 'Taiwan', + 'Tajikistan', + 'Tanzania', + 'Thailand', + 'Timor-Leste', + 'Togo', + 'Tokelau', + 'Tonga', + 'Trinidad and Tobago', + 'Tunisia', + 'Turkey', + 'Turkmenistan', + 'Turks and Caicos Islands', + 'Tuvalu', + 'Uganda', + 'Ukraine', + 'United Arab Emirates', + 'United Kingdom', + 'Uruguay', + 'Uzbekistan', + 'Vanuatu', + 'Vatican', + 'Venezuela', + 'Vietnam', + 'British Virgin Islands', + 'Wallis and Futuna', + 'Western Sahara', + 'Yemen', + 'Zambia', + 'Zimbabwe', + ], + }, + state: { + title: 'State', + type: 'string', + maxLength: 51, + enum: [ + 'AL', + 'AK', + 'AS', + 'AZ', + 'AR', + 'AA', + 'AE', + 'AP', + 'CA', + 'CO', + 'CT', + 'DE', + 'DC', + 'FM', + 'FL', + 'GA', + 'GU', + 'HI', + 'ID', + 'IL', + 'IN', + 'IA', + 'KS', + 'KY', + 'LA', + 'ME', + 'MH', + 'MD', + 'MA', + 'MI', + 'MN', + 'MS', + 'MO', + 'MT', + 'NE', + 'NV', + 'NH', + 'NJ', + 'NM', + 'NY', + 'NC', + 'ND', + 'MP', + 'OH', + 'OK', + 'OR', + 'PW', + 'PA', + 'PR', + 'RI', + 'SC', + 'SD', + 'TN', + 'TX', + 'UT', + 'VT', + 'VI', + 'VA', + 'WA', + 'WV', + 'WI', + 'WY', + ], + enumNames: [ + 'Alabama', + 'Alaska', + 'American Samoa', + 'Arizona', + 'Arkansas', + 'Armed Forces Americas (AA)', + 'Armed Forces Europe (AE)', + 'Armed Forces Pacific (AP)', + 'California', + 'Colorado', + 'Connecticut', + 'Delaware', + 'District Of Columbia', + 'Federated States Of Micronesia', + 'Florida', + 'Georgia', + 'Guam', + 'Hawaii', + 'Idaho', + 'Illinois', + 'Indiana', + 'Iowa', + 'Kansas', + 'Kentucky', + 'Louisiana', + 'Maine', + 'Marshall Islands', + 'Maryland', + 'Massachusetts', + 'Michigan', + 'Minnesota', + 'Mississippi', + 'Missouri', + 'Montana', + 'Nebraska', + 'Nevada', + 'New Hampshire', + 'New Jersey', + 'New Mexico', + 'New York', + 'North Carolina', + 'North Dakota', + 'Northern Mariana Islands', + 'Ohio', + 'Oklahoma', + 'Oregon', + 'Palau', + 'Pennsylvania', + 'Puerto Rico', + 'Rhode Island', + 'South Carolina', + 'South Dakota', + 'Tennessee', + 'Texas', + 'Utah', + 'Vermont', + 'Virgin Islands', + 'Virginia', + 'Washington', + 'West Virginia', + 'Wisconsin', + 'Wyoming', + ], + }, + postalCode: { + title: 'Zip code', + type: 'string', + }, + }, + }, + }, + }, + }, + }, + editMode: false, + }, + chooseContactMethod: { + uiSchema: { + contactMethod: { + 'ui:title': + 'How should we contact you if we have questions on your application?', + 'ui:widget': 'radio', + 'ui:options': { + labels: { + email: 'Email', + mobilePhone: 'Mobile phone', + homePhone: 'Home phone', + mail: 'Mail', + }, + }, + }, + 'view:subHeadings': { + 'ui:description': { + key: null, + ref: null, + props: { + children: { + type: 'div', + key: null, + ref: null, + props: { + children: [ + { + type: 'h3', + key: null, + ref: null, + props: { + children: 'Choose how you want to get notifications', + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: + 'We recommend that you opt into text message notifications about your benefits. These include notifications that prompt you to verify your enrollment so you’ll receive your education payments. This is an easy way to verify your monthly enrollment.', + }, + _owner: null, + _store: {}, + }, + { + type: 'div', + key: null, + ref: null, + props: { + className: 'meb-list-label', + children: { + type: 'strong', + key: null, + ref: null, + props: { + children: 'What to know about text notifications:', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + { + type: 'ul', + key: null, + ref: null, + props: { + children: [ + { + type: 'li', + key: null, + ref: null, + props: { + children: 'We’ll send you 2 messages per month.', + }, + _owner: null, + _store: {}, + }, + { + type: 'li', + key: null, + ref: null, + props: { + children: 'Message and data rates may apply.', + }, + _owner: null, + _store: {}, + }, + { + type: 'li', + key: null, + ref: null, + props: { + children: 'If you want to opt out, text STOP.', + }, + _owner: null, + _store: {}, + }, + { + type: 'li', + key: null, + ref: null, + props: { + children: 'If you need help, text HELP.', + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: { + type: 'a', + key: null, + ref: null, + props: { + href: + 'https://www.va.gov/privacy-policy/digital-notifications-terms-and-conditions/', + rel: 'noopener noreferrer', + target: '_blank', + children: + 'Read our text notifications terms and conditions', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: { + type: 'a', + key: null, + ref: null, + props: { + href: 'https://www.va.gov/privacy-policy/', + rel: 'noopener noreferrer', + target: '_blank', + children: 'Read our privacy policy', + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: null, + ref: null, + props: { + children: [ + { + type: 'strong', + key: null, + ref: null, + props: { + children: 'Note', + }, + _owner: null, + _store: {}, + }, + ': At this time, we can only send text messages to U.S. mobile phone numbers.', + ], + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + }, + 'view:noMobilePhoneAlert': { + 'ui:description': { + type: 'va-alert', + key: null, + ref: null, + props: { + 'background-only': true, + 'close-btn-aria-label': 'Close notification', + 'show-icon': true, + status: 'warning', + visible: true, + children: { + type: 'div', + key: null, + ref: null, + props: { + children: [ + { + type: 'p', + key: null, + ref: null, + props: { + className: 'vads-u-margin-y--0', + children: + 'We can’t send you text message notifications because we don’t have a mobile phone number on file for you', + }, + _owner: null, + _store: {}, + }, + { + key: null, + ref: null, + props: { + 'aria-label': 'Go back and add a mobile phone number', + to: { + pathname: 'phone-email', + search: '?re', + }, + children: { + type: 'va-button', + key: null, + ref: null, + props: { + uswds: true, + secondary: true, + text: 'Go back and add a mobile phone number', + }, + _owner: null, + _store: {}, + }, + onlyActiveOnIndex: false, + style: {}, + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + _owner: null, + _store: {}, + }, + 'ui:options': {}, + }, + notificationMethod: { + 'ui:title': 'Choose how you want to get notifications?', + 'ui:widget': 'radio', + 'ui:options': { + labels: { + yes: 'Yes, send me text message notifications', + no: 'No, just send me email notifications', + }, + }, + 'ui:validations': [null], + }, + }, + schema: { + type: 'object', + required: ['contactMethod'], + properties: { + contactMethod: { + type: 'string', + enum: ['Email', 'Mobile Phone', 'Mail'], + }, + 'view:subHeadings': { + type: 'object', + properties: {}, + }, + 'view:noMobilePhoneAlert': { + type: 'object', + properties: {}, + 'ui:hidden': true, + }, + notificationMethod: { + type: 'string', + enum: ['yes', 'no'], + }, + }, + }, + editMode: false, + }, + directDeposit: { + uiSchema: { + 'view:directDeposit': { + 'ui:title': { + type: 'h4', + key: null, + ref: null, + props: { + className: 'vads-u-font-size--h5 vads-u-margin-top--0', + children: 'Direct deposit information', + }, + _owner: null, + _store: {}, + }, + 'ui:options': { + editTitle: 'Direct deposit information', + hideLabelText: true, + itemName: 'account information', + itemNameAction: 'Update', + reviewTitle: 'Direct deposit information', + showFieldLabel: false, + startInEdit: true, + volatileData: true, + }, + 'ui:description': { + type: 'p', + key: null, + ref: null, + props: { + children: [ + { + type: 'strong', + key: null, + ref: null, + props: { + children: 'Note:', + }, + _owner: null, + _store: {}, + }, + ' We make payments only through direct deposit, also called electronic funds transfer (EFT).', + ], + }, + _owner: null, + _store: {}, + }, + bankAccount: { + 'ui:order': ['accountType', 'routingNumber', 'accountNumber'], + accountType: { + 'ui:title': 'Account type', + 'ui:widget': 'radio', + 'ui:options': { + labels: { + checking: 'Checking', + savings: 'Savings', + }, + }, + }, + accountNumber: { + 'ui:title': 'Bank account number', + 'ui:errorMessages': { + pattern: 'Please enter a valid 5-17 digit bank account number', + }, + }, + routingNumber: { + 'ui:title': 'Bank routing number', + 'ui:validations': [null], + 'ui:errorMessages': { + pattern: 'Please enter a valid 9-digit routing number', + }, + }, + }, + }, + 'view:learnMore': { + 'ui:description': { + type: 'va-additional-info', + key: 'learn-more-btn', + ref: null, + props: { + trigger: 'Where can I find these numbers?', + children: [ + { + type: 'img', + key: 'check-image-src', + ref: null, + props: { + style: { + marginTop: '0.625rem', + }, + src: + 'https://staging-va-gov-assets.s3-us-gov-west-1.amazonaws.com/img/check-sample.png', + alt: + 'Example of a check showing where the account and routing numbers are', + }, + _owner: null, + _store: {}, + }, + { + type: 'br', + key: null, + ref: null, + props: {}, + _owner: null, + _store: {}, + }, + { + type: 'br', + key: null, + ref: null, + props: {}, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: 'learn-more-description', + ref: null, + props: { + children: + 'The bank routing number is the first 9 digits on the bottom left corner of a printed check. Your account number is the second set of numbers on the bottom of a printed check, just to the right of the bank routing number.', + }, + _owner: null, + _store: {}, + }, + { + type: 'br', + key: null, + ref: null, + props: {}, + _owner: null, + _store: {}, + }, + { + type: 'p', + key: 'learn-more-additional', + ref: null, + props: { + children: + 'If you don’t have a printed check, you can sign in to your online banking institution for this information', + }, + _owner: null, + _store: {}, + }, + ], + }, + _owner: null, + _store: {}, + }, + }, + }, + schema: { + type: 'object', + properties: { + 'view:directDeposit': { + type: 'object', + properties: { + bankAccount: { + type: 'object', + required: ['accountType', 'accountNumber', 'routingNumber'], + properties: { + accountNumber: { + type: 'string', + pattern: '^[*a-zA-Z0-9]{5,17}$', + }, + accountType: { + type: 'string', + enum: ['checking', 'savings'], + }, + routingNumber: { + type: 'string', + pattern: '^[\\d*]{5}\\d{4}$', + }, + }, + }, + }, + }, + 'view:learnMore': { + type: 'object', + properties: {}, + }, + }, + }, + editMode: false, + }, }, - gender: 'F', - veteranDateOfBirth: '1989-11-11', - veteranSocialSecurityNumber: '111111111', - privacyAgreementAccepted: true, - bankAccount: { - accountType: 'Savings', - accountNumber: '123456', - routingNumber: '322271627', + initialData: { + fullName: {}, + 'view:subHeading': {}, + 'view:fry': {}, + 'view:dea': {}, + 'view:benefitInfo': {}, + 'view:subHeadings': {}, + 'view:personalInformation': {}, + 'view:EmailAndphoneNumbers': {}, + mobilePhone: {}, + homePhone: {}, + 'view:confirmDuplicateData': {}, + mailingAddressInput: { + livesOnMilitaryBaseInfo: {}, + address: { + country: 'USA', + }, + }, + 'view:noMobilePhoneAlert': {}, + 'view:directDeposit': { + bankAccount: {}, + }, + 'view:learnMore': {}, }, + savedStatus: 'not-attempted', + autoSavedStatus: 'success', + loadedStatus: 'not-attempted', + version: 0, + lastSavedDate: 1729862456569, + expirationDate: 1735046457, + prefillStatus: 'not-attempted', + isStartingOver: false, + inProgressFormId: '', }; diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/actions.unit.spec.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/actions.unit.spec.js new file mode 100644 index 000000000000..b3feff8f35e1 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/actions.unit.spec.js @@ -0,0 +1,124 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; +import { mockApiRequest } from '@department-of-veterans-affairs/platform-testing/helpers'; +import { + ACKNOWLEDGE_DUPLICATE, + FETCH_PERSONAL_INFORMATION, + FETCH_DUPLICATE_CONTACT, + FETCH_CLAIM_STATUS, + CLAIM_STATUS_RESPONSE_IN_PROGRESS, + UPDATE_GLOBAL_EMAIL, + UPDATE_GLOBAL_PHONE_NUMBER, + TOGGLE_MODAL, + fetchPersonalInformation, + fetchDuplicateContactInfo, + updateGlobalEmail, + updateGlobalPhoneNumber, + acknowledgeDuplicate, + toggleModal, + fetchClaimStatus, +} from '../../actions'; + +describe('Personal Information action', () => { + it('should dispatch a fetch personal information action', () => { + const mockData = { data: { attributes: { name: 'John Doe' } } }; + mockApiRequest(mockData); + const dispatch = sinon.spy(); + return fetchPersonalInformation()(dispatch).then(() => { + expect(dispatch.firstCall.args[0].type).to.equal( + FETCH_PERSONAL_INFORMATION, + ); + }); + }); + it('should dispatch a fetch error', () => { + mockApiRequest({}, false); + const dispatch = sinon.spy(); + return fetchPersonalInformation()(dispatch).then(() => { + expect(typeof dispatch.firstCall.args[0]).to.equal('object'); + }); + }); +}); +describe('Duplicate Contact Info action', () => { + const email = 'test@example.com'; + const phoneNumber = '1234567890'; + it('should dispatch a fetch duplicate contact action', () => { + const mockData = { data: { duplicates: [] } }; + mockApiRequest(mockData); + const dispatch = sinon.spy(); + return fetchDuplicateContactInfo(email, phoneNumber)(dispatch).then(() => { + expect(dispatch.firstCall.args[0].type).to.equal(FETCH_DUPLICATE_CONTACT); + }); + }); + it('should dispatch a fetch error', () => { + mockApiRequest({}, false); + const dispatch = sinon.spy(); + return fetchDuplicateContactInfo(email, phoneNumber)(dispatch).then(() => { + expect(typeof dispatch.firstCall.args[0]).to.equal('object'); + }); + }); +}); +describe('Claim Status action', () => { + const selectedChapter = 'Chapter35'; + it('should dispatch a fetch claim status action', () => { + const mockData = { + data: { + attributes: { + claimStatus: 'COMPLETED', + receivedDate: '2024-01-01', + }, + }, + }; + mockApiRequest(mockData); + const dispatch = sinon.spy(); + return fetchClaimStatus(selectedChapter)(dispatch).then(() => { + expect(dispatch.firstCall.args[0].type).to.equal(FETCH_CLAIM_STATUS); + }); + }); + xit('should handle polling timeout', () => { + const mockData = { + data: { + attributes: { + claimStatus: CLAIM_STATUS_RESPONSE_IN_PROGRESS, + receivedDate: '2024-01-01', + }, + }, + }; + mockApiRequest(mockData); + const dispatch = sinon.spy(); + return fetchClaimStatus(selectedChapter)(dispatch).then(() => { + expect(dispatch.firstCall.args[0].type).to.equal(FETCH_CLAIM_STATUS); + }); + }); +}); +describe('Update Global Email action', () => { + it('should create an update global email action', () => { + const email = 'test@example.com'; + const action = updateGlobalEmail(email); + expect(action.type).to.equal(UPDATE_GLOBAL_EMAIL); + expect(action.email).to.equal(email); + }); +}); +describe('Update Global Phone Number action', () => { + it('should create an update global phone number action', () => { + const mobilePhone = '1234567890'; + const action = updateGlobalPhoneNumber(mobilePhone); + expect(action.type).to.equal(UPDATE_GLOBAL_PHONE_NUMBER); + expect(action.mobilePhone).to.equal(mobilePhone); + }); +}); +describe('Acknowledge Duplicate action', () => { + it('should create an acknowledge duplicate action', () => { + const contactInfo = { email: 'test@example.com', phone: '1234567890' }; + const action = acknowledgeDuplicate(contactInfo); + expect(action.type).to.equal(ACKNOWLEDGE_DUPLICATE); + expect(action.contactInfo).to.equal(contactInfo); + }); +}); +describe('Toggle Modal action', () => { + it('should create a toggle modal action', () => { + const toggle = true; + const action = toggleModal(toggle); + expect(action.type).to.equal(TOGGLE_MODAL); + expect(action.toggle).to.equal(toggle); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/components/PersonalInformation.unit.spec.jsx b/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/components/PersonalInformation.unit.spec.jsx deleted file mode 100644 index 4ae0b102911e..000000000000 --- a/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/components/PersonalInformation.unit.spec.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { Provider } from 'react-redux'; -import { render } from '@testing-library/react'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; -import { expect } from 'chai'; - -import { $ } from '@department-of-veterans-affairs/platform-forms-system/ui'; -import PersonalInformation from '../../../components/PersonalInformation'; - -const initialData = { - user: { - profile: { - userFullName: { - first: 'Michael', - middle: 'Thomas', - last: 'Wazowski', - suffix: 'Esq.', - }, - dob: '1990-02-03', - }, - }, - data: { - formData: { - data: { - attributes: { - claimant: { - firstName: 'john', - middleName: 'doe', - lastName: 'smith', - dateOfBirth: '1990-01-01', - }, - }, - }, - }, - }, -}; - -describe('PersonalInformation', () => { - const middleware = [thunk]; - const mockStore = configureStore(middleware); - - it('should render with data', () => { - const { container } = render( - - - , - ); - - expect($('.personal-info-header', container)).to.exist; - }); -}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/config/form.unit.spec.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/config/form.unit.spec.js index 52e3b4fe395f..5e0378a32e72 100644 --- a/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/config/form.unit.spec.js +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/unit/config/form.unit.spec.js @@ -1,52 +1,266 @@ import React from 'react'; -import { mount } from 'enzyme'; +import { mount, shallow } from 'enzyme'; import { expect } from 'chai'; -import { Provider } from 'react-redux'; -import { render } from '@testing-library/react'; -import configureStore from 'redux-mock-store'; -import thunk from 'redux-thunk'; import { fillData, - fillDate, selectRadio, DefinitionTester, } from 'platform/testing/unit/schemaform-utils'; import formConfig from '../../../config/form'; describe('Complex Form 22-5490 Detailed Interaction Tests', () => { - it('should fill out the applicant information fields', () => { - const { - schema, - uiSchema, - } = formConfig.chapters.applicantInformationChapter.pages.applicantInformation; - const form = mount( - , - ); + describe('applicantInformation', () => { + it('should fill out the applicant information fields', () => { + const { + schema, + uiSchema, + } = formConfig.chapters.applicantInformationChapter.pages.applicantInformation; + const form = mount( + , + ); - fillData(form, 'input#root_fullName_first', 'John'); - fillData(form, 'input#root_fullName_last', 'Doe'); - fillData(form, 'input#root_ssn', '123456789'); - selectRadio(form, 'root_relationShipToMember', 'spouse'); + fillData(form, 'input#root_fullName_first', 'John'); + fillData(form, 'input#root_fullName_last', 'Doe'); + fillData(form, 'input#root_ssn', '123456789'); + selectRadio(form, 'root_relationShipToMember', 'spouse'); - expect(form.find('input#root_fullName_first').props().value).to.equal( - 'John', - ); - expect(form.find('input#root_fullName_last').props().value).to.equal('Doe'); - expect(form.find('input#root_ssn').props().value).to.equal('123456789'); - expect( - form - .find('input[name="root_relationShipToMember"][value="spouse"]') - .props().checked, - ).to.be.true; - form.unmount(); + expect(form.find('input#root_fullName_first').props().value).to.equal( + 'John', + ); + expect(form.find('input#root_fullName_last').props().value).to.equal( + 'Doe', + ); + expect(form.find('input#root_ssn').props().value).to.equal('123456789'); + expect( + form + .find('input[name="root_relationShipToMember"][value="spouse"]') + .props().checked, + ).to.be.true; + form.unmount(); + }); + + it('should render an error when no first/last name are provided', () => { + const { + schema, + uiSchema, + } = formConfig.chapters.applicantInformationChapter.pages.applicantInformation; + + const form = mount( + , + ); + + form.find('input#root_fullName_first').simulate('change', { + target: { value: '' }, + }); + form.find('input#root_fullName_last').simulate('change', { + target: { value: '' }, + }); + form.find('input#root_ssn').simulate('change', { + target: { value: '123456789' }, + }); + form.find('select#root_dateOfBirthMonth').simulate('change', { + target: { value: '1' }, + }); + form.find('select#root_dateOfBirthDay').simulate('change', { + target: { value: '1' }, + }); + form.find('input#root_dateOfBirthYear').simulate('change', { + target: { value: '1990' }, + }); + selectRadio(form, 'root_relationShipToMember', 'spouse'); + + form.find('form').simulate('submit'); + const errorMessages = form.find('.usa-input-error-message'); + + expect(errorMessages.length).to.be.at.least(1); + expect(errorMessages.at(0).text()).to.include( + 'Error Please enter a first name', + ); + expect(errorMessages.at(1).text()).to.include( + 'Error Please enter a last name', + ); + + form.unmount(); + }); + + it('should render errors when names are too long', () => { + const { + schema, + uiSchema, + } = formConfig.chapters.applicantInformationChapter.pages.applicantInformation; + + const form = mount( + , + ); + + form.find('input#root_fullName_first').simulate('change', { + target: { value: 'abcdefghijklmnopqrstuvwxyz' }, + }); + form.find('input#root_fullName_middle').simulate('change', { + target: { value: 'abcdefghijklmnopqrstuvwxyz' }, + }); + form.find('input#root_fullName_last').simulate('change', { + target: { value: 'abcdefghijklmnopqrstuvwxyzabcd' }, + }); + form.find('input#root_ssn').simulate('change', { + target: { value: '123456789' }, + }); + form.find('select#root_dateOfBirthMonth').simulate('change', { + target: { value: '1' }, + }); + form.find('select#root_dateOfBirthDay').simulate('change', { + target: { value: '1' }, + }); + form.find('input#root_dateOfBirthYear').simulate('change', { + target: { value: '1990' }, + }); + selectRadio(form, 'root_relationShipToMember', 'spouse'); + + form.find('form').simulate('submit'); + const errorMessages = form.find('.usa-input-error-message'); + + expect(errorMessages.length).to.be.at.least(1); + expect(errorMessages.at(0).text()).to.include( + 'Error Must be 20 characters or less', + ); + expect(errorMessages.at(1).text()).to.include( + 'Error Must be 20 characters or less', + ); + expect(errorMessages.at(2).text()).to.include( + 'Error Must be 26 characters or less', + ); + + form.unmount(); + }); + + it('should render an error when last name is too short', () => { + const { + schema, + uiSchema, + } = formConfig.chapters.applicantInformationChapter.pages.applicantInformation; + + const form = mount( + , + ); + + form.find('input#root_fullName_first').simulate('change', { + target: { value: 'john' }, + }); + form.find('input#root_fullName_middle').simulate('change', { + target: { value: 'tesh' }, + }); + form.find('input#root_fullName_last').simulate('change', { + target: { value: 'a' }, + }); + form.find('input#root_ssn').simulate('change', { + target: { value: '123456789' }, + }); + form.find('select#root_dateOfBirthMonth').simulate('change', { + target: { value: '1' }, + }); + form.find('select#root_dateOfBirthDay').simulate('change', { + target: { value: '1' }, + }); + form.find('input#root_dateOfBirthYear').simulate('change', { + target: { value: '1990' }, + }); + selectRadio(form, 'root_relationShipToMember', 'spouse'); + + form.find('form').simulate('submit'); + + const errorMessages = form.find('.usa-input-error-message'); + + expect(errorMessages.length).to.be.at.least(1); + + expect(errorMessages.at(0).text()).to.include( + 'Must be 2 characters or more', + ); + + form.unmount(); + }); + + it('should render errors when first/middle/last names are invalid', () => { + const { + schema, + uiSchema, + } = formConfig.chapters.applicantInformationChapter.pages.applicantInformation; + + const form = mount( + , + ); + + form.find('input#root_fullName_first').simulate('change', { + target: { value: '((((9' }, + }); + form.find('input#root_fullName_middle').simulate('change', { + target: { value: '&&&&' }, + }); + form.find('input#root_fullName_last').simulate('change', { + target: { value: '&&&&' }, + }); + form.find('input#root_ssn').simulate('change', { + target: { value: '123456789' }, + }); + form.find('select#root_dateOfBirthMonth').simulate('change', { + target: { value: '1' }, + }); + form.find('select#root_dateOfBirthDay').simulate('change', { + target: { value: '1' }, + }); + form.find('input#root_dateOfBirthYear').simulate('change', { + target: { value: '1990' }, + }); + selectRadio(form, 'root_relationShipToMember', 'spouse'); + + form.find('form').simulate('submit'); + const errorMessages = form.find('.usa-input-error-message'); + + expect(errorMessages.length).to.be.at.least(1); + expect(errorMessages.at(0).text()).to.include( + 'Error Please enter a valid entry. Acceptable entries are letters, spaces and apostrophes.', + ); + expect(errorMessages.at(1).text()).to.include( + 'Error Please enter a valid entry. Acceptable entries are letters, spaces and apostrophes.', + ); + expect(errorMessages.at(2).text()).to.include( + 'Error Please enter a valid entry. Acceptable entries are letters, spaces, dashes and apostrophes.', + ); + + form.unmount(); + }); }); + it('should fill out the benefit selection fields', () => { const { schema, @@ -81,61 +295,23 @@ describe('Complex Form 22-5490 Detailed Interaction Tests', () => { uiSchema, } = formConfig.chapters.yourInformationChapter.pages.reviewPersonalInformation; - const initialData = { - user: { - profile: { - userFullName: { - first: 'Michael', - middle: 'Thomas', - last: 'Wazowski', - suffix: 'Esq.', - }, - dob: '1990-02-03', - }, - }, - form: { - data: {}, - }, - data: { - formData: { - data: { - attributes: { - claimant: { - firstName: 'john', - middleName: 'doe', - lastName: 'smith', - dateOfBirth: '1990-01-01', - }, - }, - }, - }, - }, - }; - const middleware = [thunk]; - const mockStore = configureStore(middleware); - - const { container } = render( - - - , + const form = shallow( + , ); - - selectRadio(container, 'root_highSchoolDiploma', 'yes'); - fillDate(container, 'root_graduationDate', '1992-07-07'); - expect( - container - .find('input[name="root_highSchoolDiploma"][value="yes"]') - .props().checked, - ).to.be.true; - container.unmount(); + expect(form.find('h3')).to.include('test'); + form.unmount(); }); + it('should fill out the marriage information fields', () => { + const baseData = { + relationShipToMember: 'spouse', + }; const { schema, uiSchema, @@ -145,8 +321,8 @@ describe('Complex Form 22-5490 Detailed Interaction Tests', () => { schema={schema} uiSchema={uiSchema} definitions={formConfig.defaultDefinitions} - data={{}} - formData={{}} + data={baseData} + formData={baseData} />, ); selectRadio(form, 'root_marriageStatus', 'married'); @@ -156,6 +332,7 @@ describe('Complex Form 22-5490 Detailed Interaction Tests', () => { ).to.be.true; form.unmount(); }); + it('should fill out the remarriage information fields', () => { const { schema, @@ -166,7 +343,11 @@ describe('Complex Form 22-5490 Detailed Interaction Tests', () => { schema={schema} uiSchema={uiSchema} definitions={formConfig.defaultDefinitions} - data={{ marriageStatus: 'divorced' }} + data={{ + marriageStatus: 'divorced', + relationShipToMember: 'spouse', + remarriageStatus: 'yes', + }} formData={{}} />, ); @@ -198,6 +379,8 @@ describe('Complex Form 22-5490 Detailed Interaction Tests', () => { ).to.be.true; form.unmount(); }); + + // @here fix this issue! xit('should fill out the contact information fields', () => { const { schema, @@ -230,7 +413,7 @@ describe('Complex Form 22-5490 Detailed Interaction Tests', () => { ); form.unmount(); }); - xit('should fill out the mailing address fields', () => { + it('should fill out the mailing address fields', () => { const { schema, uiSchema, @@ -244,25 +427,99 @@ describe('Complex Form 22-5490 Detailed Interaction Tests', () => { formData={{}} />, ); - fillData(form, '#root_mailingAddressInput_country', 'USA'); - fillData(form, '#root_mailingAddressInput_street', '123 Main St'); - fillData(form, '#root_mailingAddressInput_city', 'Anytown'); - fillData(form, '#root_mailingAddressInput_state', 'CA'); - fillData(form, '#root_mailingAddressInput_postalCode', '12345'); - expect( - form.find('#root_mailingAddressInput_street').prop('value'), - ).to.equal('123 Main St'); - expect(form.find('#root_mailingAddressInput_city').prop('value')).to.equal( - 'Anytown', + form + .find('select#root_mailingAddressInput_address_country') + .simulate('change', { + target: { value: 'USA' }, + }); + fillData( + form, + 'input#root_mailingAddressInput_address_street', + '123 Main St', ); - expect(form.find('#root_mailingAddressInput_state').prop('value')).to.equal( - 'CA', + fillData(form, 'input#root_mailingAddressInput_address_city', 'Anytown'); + form + .find('select#root_mailingAddressInput_address_state') + .simulate('change', { + target: { value: 'CA' }, + }); + fillData( + form, + 'input#root_mailingAddressInput_address_postalCode', + '12345', ); expect( - form.find('#root_mailingAddressInput_postalCode').prop('value'), + form.find('input#root_mailingAddressInput_address_street').prop('value'), + ).to.equal('123 Main St'); + expect( + form.find('input#root_mailingAddressInput_address_city').prop('value'), + ).to.equal('Anytown'); + expect( + form.find('select#root_mailingAddressInput_address_state').prop('value'), + ).to.equal('CA'); + expect( + form + .find('input#root_mailingAddressInput_address_postalCode') + .prop('value'), ).to.equal('12345'); form.unmount(); }); + + it('should fill render errors when comleting mailing address fields', () => { + const { + schema, + uiSchema, + } = formConfig.chapters.contactInformationChapter.pages.mailingAddress; + const form = mount( + , + ); + form + .find('select#root_mailingAddressInput_address_country') + .simulate('change', { + target: { value: 'USA' }, + }); + fillData(form, 'input#root_mailingAddressInput_address_street', ''); + fillData(form, 'input#root_mailingAddressInput_address_city', 'Anytown'); + form + .find('select#root_mailingAddressInput_address_state') + .simulate('change', { + target: { value: 'CA' }, + }); + fillData( + form, + 'input#root_mailingAddressInput_address_postalCode', + '12345', + ); + form.find('form').simulate('submit'); + const errorMessages = form.find('.usa-input-error-message'); + + expect(errorMessages.length).to.be.at.least(1); + expect(errorMessages.at(0).text()).to.include( + 'Please enter your full street address', + ); + + fillData(form, 'input#root_mailingAddressInput_address_street', 'SA'); + form.find('form').simulate('submit'); + + expect(errorMessages.at(0).text()).to.include('minimum of 3 characters'); + + fillData( + form, + 'input#root_mailingAddressInput_address_street', + 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', + ); + form.find('form').simulate('submit'); + + expect(errorMessages.at(0).text()).to.include('maximum of 40 characters'); + form.unmount(); + }); + it('should fill out the contact method fields', () => { const { schema, diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/utils/form-submit-transform.unit.spec.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/utils/form-submit-transform.unit.spec.js new file mode 100644 index 000000000000..dc384dab3c82 --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/utils/form-submit-transform.unit.spec.js @@ -0,0 +1,257 @@ +import { expect } from 'chai'; +import { submissionForm } from '../fixtures/data/form-submission-test-data'; +import { + transform5490Form, + getSchemaCountryCode, + getLTSCountryCode, + getAddressType, +} from '../../utils/form-submit-transform'; + +let mockSubmissionForm = {}; +let submissionObject = {}; + +describe('form submit transform', () => { + beforeEach(() => { + mockSubmissionForm = JSON.parse(JSON.stringify(submissionForm)); + submissionObject = JSON.parse(transform5490Form({}, mockSubmissionForm)); + }); + + describe('getSchemaCountryCode', () => { + it('returns default schema country code for null input', () => { + expect(getSchemaCountryCode(null)).to.eql('USA'); + }); + + it('returns default schema country code for non-string input', () => { + expect(getSchemaCountryCode(123)).to.eql('USA'); + }); + + it('returns correct schema country code for valid three-character code', () => { + expect(getSchemaCountryCode('CAN')).to.eql('CAN'); // Canada + }); + + it('returns correct schema country code for two-character LTS code', () => { + expect(getSchemaCountryCode('CA')).to.eql('CAN'); // Canada + }); + }); + + describe('getLTSCountryCode', () => { + it('returns LTS country code for a valid schema country code', () => { + expect(getLTSCountryCode('CAN')).to.eql('CA'); // Canada + }); + + it('returns LTS country code for a valid two-character input', () => { + expect(getLTSCountryCode('CA')).to.eql('CA'); // Canada + }); + + it('returns "ZZ" for an unrecognized country code', () => { + expect(getLTSCountryCode('XYZ')).to.eql('ZZ'); // Unknown country + }); + }); + + describe('getAddressType', () => { + it('returns null for undefined or null mailing address', () => { + expect(getAddressType(null)).to.eql(null); + expect(getAddressType(undefined)).to.eql(null); + }); + + it('returns "MILITARY_OVERSEAS" when livesOnMilitaryBase is true', () => { + const mailingAddress = { livesOnMilitaryBase: true }; + expect(getAddressType(mailingAddress)).to.eql('MILITARY_OVERSEAS'); + }); + + it('returns "DOMESTIC" for a United States address', () => { + const mailingAddress = { + address: { country: 'USA' }, + }; + expect(getAddressType(mailingAddress)).to.eql('DOMESTIC'); + }); + + it('returns "FOREIGN" for an address outside the United States', () => { + const mailingAddress = { + address: { country: 'CAN' }, + }; + expect(getAddressType(mailingAddress)).to.eql('FOREIGN'); + }); + }); + + describe('transform5490Form', () => { + describe('creates a type property', () => { + it('is set to Chapter35Submission', () => { + expect(submissionObject['@type']).to.eql('Chapter35Submission'); + }); + }); + + describe('creates claimant information', () => { + it('sets up first name', () => { + expect(submissionObject.claimant.firstName).to.eql('Hector'); + }); + it('sets up middle name', () => { + expect(submissionObject.claimant.middleName).to.eql('M'); + }); + it('sets up last name', () => { + expect(submissionObject.claimant.lastName).to.eql('Allen'); + }); + it('sets up suffix name', () => { + expect(submissionObject.claimant.suffix).to.eql('Sr.'); + }); + it('sets up birth date', () => { + expect(submissionObject.claimant.dateOfBirth).to.eql('1932-02-05'); + }); + it('sets up notificationMethod for text', () => { + mockSubmissionForm.data.notificationMethod = 'yes'; + submissionObject = JSON.parse( + transform5490Form({}, mockSubmissionForm), + ); + expect(submissionObject.claimant.notificationMethod).to.eql('TEXT'); + }); + it('sets up notificationMethod for email', () => { + expect(submissionObject.claimant.notificationMethod).to.eql('EMAIL'); + }); + it('sets up preferredContactMethod', () => { + expect(submissionObject.claimant.preferredContact).to.eql('Email'); + }); + + describe('sets up contact info', () => { + it('sets up address line 1', () => { + expect(submissionObject.claimant.contactInfo.addressLine1).to.eql( + '4000 Wilson Blvd', + ); + }); + it('sets up address line 2', () => { + expect(submissionObject.claimant.contactInfo.addressLine2).to.eql( + '#1', + ); + }); + it('sets up city', () => { + expect(submissionObject.claimant.contactInfo.city).to.eql( + 'ARLINGTON', + ); + }); + it('sets up zipcode', () => { + expect(submissionObject.claimant.contactInfo.zipcode).to.eql('22203'); + }); + it('sets up email address', () => { + expect(submissionObject.claimant.contactInfo.emailAddress).to.eql( + 'test@test.com', + ); + }); + it('sets up address type to domestic ', () => { + expect(submissionObject.claimant.contactInfo.addressType).to.eql( + 'DOMESTIC', + ); + }); + it('sets up address type to military overseas ', () => { + const form = { + data: { + mailingAddressInput: { + livesOnMilitaryBase: 'true', + }, + }, + }; + submissionObject = JSON.parse(transform5490Form({}, form)); + expect(submissionObject.claimant.contactInfo.addressType).to.eql( + 'MILITARY_OVERSEAS', + ); + }); + it('sets up mobile phone number', () => { + expect( + submissionObject.claimant.contactInfo.mobilePhoneNumber, + ).to.eql('5125554586'); + }); + it('sets up home phone number', () => { + expect(submissionObject.claimant.contactInfo.homePhoneNumber).to.eql( + '5125554585', + ); + }); + it('sets up country code', () => { + expect(submissionObject.claimant.contactInfo.countryCode).to.eql( + 'US', + ); + }); + it('sets up state code', () => { + expect(submissionObject.claimant.contactInfo.stateCode).to.eql('VA'); + }); + }); + }); + + describe('Creates highSchoolDiplomaInfo', () => { + it('should capture high school diploma info', () => { + expect( + submissionObject.highSchoolDiplomaInfo + ?.highSchoolDiplomaOrCertificate, + ).to.eql(true); + expect( + submissionObject.highSchoolDiplomaInfo + ?.highSchoolDiplomaOrCertificateDate, + ).to.eql('2010-01-02'); + }); + }); + + describe('creates service member information', () => { + it('sets up first name', () => { + expect(submissionObject.serviceMember.firstName).to.eql('test'); + }); + it('sets up middle name', () => { + expect(submissionObject.serviceMember.middleName).to.eql('t'); + }); + it('sets up last name', () => { + expect(submissionObject.serviceMember.lastName).to.eql('testerson'); + }); + it('sets up suffix name', () => { + expect(submissionObject.serviceMember.relationship).to.eql('spouse'); + }); + it('sets up birth date', () => { + expect(submissionObject.serviceMember.dateOfBirth).to.eql('1990-01-01'); + }); + it('sets up ssn', () => { + expect(submissionObject.serviceMember.ssn).to.eql('123123123'); + }); + }); + + describe('creates Direct Deposit information', () => { + it('sets up direct deposit account type', () => { + expect(submissionObject.directDeposit.directDepositAccountType).to.eql( + 'checking', + ); + }); + it('sets up direct deposit account number', () => { + expect( + submissionObject.directDeposit.directDepositAccountNumber, + ).to.eql('123123123'); + }); + it('sets up direct deposit routing number', () => { + expect( + submissionObject.directDeposit.directDepositRoutingNumber, + ).to.eql('123123124'); + }); + }); + + describe('Creates additionalConsiderations', () => { + it('sets up outstandingFelony', () => { + expect( + submissionObject.additionalConsiderations.outstandingFelony, + ).to.eql('no'); + }); + it('sets up marriageDate', () => { + expect(submissionObject.additionalConsiderations.marriageDate).to.eql( + '2015-01-01', + ); + }); + it('sets up marriageStatus', () => { + expect(submissionObject.additionalConsiderations.marriageStatus).to.eql( + 'divorced', + ); + }); + it('sets up remarriageDate', () => { + expect(submissionObject.additionalConsiderations.remarriageDate).to.eql( + '2020-01-01', + ); + }); + it('sets up remarriedSinceDivorce', () => { + expect( + submissionObject.additionalConsiderations.remarriedSinceDivorce, + ).to.eql('yes'); + }); + }); + }); +}); diff --git a/src/applications/survivor-dependent-education-benefit/22-5490/tests/utils/validations.unit.spec.js b/src/applications/survivor-dependent-education-benefit/22-5490/tests/utils/validations.unit.spec.js new file mode 100644 index 000000000000..1c9de1858d6f --- /dev/null +++ b/src/applications/survivor-dependent-education-benefit/22-5490/tests/utils/validations.unit.spec.js @@ -0,0 +1,59 @@ +import { expect } from 'chai'; +import sinon from 'sinon'; + +import { + validateHomePhone, + validateMobilePhone, + validateEmail, + validateEffectiveDate, +} from '../../utils/validations'; + +describe('Phone validation', () => { + it('validateHomePhone should add error', () => { + const errors = { + addError: sinon.spy(), + }; + + validateHomePhone(errors, '1234', { + homePhone: { + isInternational: false, + }, + }); + + expect(errors.addError.called).to.be.true; + }); + + it('validateMobilePhone should add error', () => { + const errors = { + addError: sinon.spy(), + }; + + validateMobilePhone(errors, '1234', { + mobilePhone: { + isInternational: false, + }, + }); + + expect(errors.addError.called).to.be.true; + }); + + it('validateEmail should add error', () => { + const errors = { + addError: sinon.spy(), + }; + + validateEmail(errors, 'test'); + + expect(errors.addError.called).to.be.true; + }); + + it('validateEffectiveDate should add error', () => { + const errors = { + addError: sinon.spy(), + }; + + validateEffectiveDate(errors, '1990-01-01'); + + expect(errors.addError.called).to.be.true; + }); +});