diff --git a/src/applications/representative-appoint/components/CurrentAccreditedRepresentative.jsx b/src/applications/representative-appoint/components/CurrentAccreditedRepresentative.jsx index 6e58b8a61dd7..7aa6d331891b 100644 --- a/src/applications/representative-appoint/components/CurrentAccreditedRepresentative.jsx +++ b/src/applications/representative-appoint/components/CurrentAccreditedRepresentative.jsx @@ -2,50 +2,28 @@ import React from 'react'; import PropTypes from 'prop-types'; import AddressEmailPhone from './AddressEmailPhone'; -import { getEntityAddressAsObject } from '../utilities/helpers'; +import { getEntityAddressAsObject, parseRepType } from '../utilities/helpers'; const CurrentAccreditedRepresentative = ({ repType, repAttributes }) => { const addressData = getEntityAddressAsObject(repAttributes); const repName = repAttributes.name || repAttributes.fullName; const { email, phone } = repAttributes; - const parseRepType = () => { - const parsedRep = {}; - - switch (repType) { - case 'Organization': - parsedRep.title = 'Veterans Service Organization (VSO)'; - parsedRep.subTitle = 'Veteran Service Organization'; - break; - case 'Attorney': - parsedRep.title = 'accredited attorney'; - parsedRep.subTitle = 'Accredited attorney'; - break; - case 'Claims Agent': - parsedRep.title = 'accredited claims agent'; - parsedRep.subTitle = 'Accredited claims agent'; - break; - default: - parsedRep.title = 'accredited representative'; - parsedRep.subTitle = 'Accredited representative'; - } - - return parsedRep; - }; - return (

- Your current {parseRepType().title} + Your current {parseRepType(repType).title}

{repName && ( <>

{repName}

-

{parseRepType().subTitle}

+

+ {parseRepType(repType).subTitle} +

)}
diff --git a/src/applications/representative-appoint/components/SelectAccreditedRepresentative.jsx b/src/applications/representative-appoint/components/SelectAccreditedRepresentative.jsx index b8c6b04964e0..663b4dfbb002 100644 --- a/src/applications/representative-appoint/components/SelectAccreditedRepresentative.jsx +++ b/src/applications/representative-appoint/components/SelectAccreditedRepresentative.jsx @@ -27,9 +27,7 @@ const SelectAccreditedRepresentative = props => { const representativeResults = formData?.['view:representativeSearchResults'] || null; - const currentSelectedRep = useRef( - formData?.['view:representativeSearchResults'], - ); + const currentSelectedRep = useRef(formData?.['view:selectedRepresentative']); const query = formData['view:representativeQuery']; const invalidQuery = query === undefined || !query.trim(); @@ -77,7 +75,7 @@ const SelectAccreditedRepresentative = props => { setError(noSearchError); scrollToFirstError({ focusOnAlertRole: true }); } else if (isReviewPage) { - if (selection === currentSelectedRep) { + if (selection === currentSelectedRep.current) { goToPath('/review-and-submit'); } else { goToPath('/representative-contact?review=true'); @@ -111,7 +109,7 @@ const SelectAccreditedRepresentative = props => { }; const handleSelectRepresentative = async selectedRepResult => { - if (selectedRepResult === currentSelectedRep) { + if (selectedRepResult === currentSelectedRep.current && isReviewPage) { goToPath('/review-and-submit'); } else { const repStatus = await getRepStatus(); @@ -141,18 +139,6 @@ const SelectAccreditedRepresentative = props => { } }; - // const continueError = () => { - // return ( - // - // Error - // {} - // - // ); - // }; - if (loadingPOA) { return ; } diff --git a/src/applications/representative-appoint/components/VeteranDetails.jsx b/src/applications/representative-appoint/components/VeteranDetails.jsx deleted file mode 100644 index ff6390d0e813..000000000000 --- a/src/applications/representative-appoint/components/VeteranDetails.jsx +++ /dev/null @@ -1,62 +0,0 @@ -import PropTypes from 'prop-types'; -import React from 'react'; -import { connect } from 'react-redux'; - -import { genderLabels } from 'platform/static-data/labels'; -import { srSubstitute, formatDate } from '../utilities/helpers'; -import { editNote } from '../content/common'; - -function VeteranDetails({ profile }) { - const { veteranSocialSecurityNumber, vaFileNumber, dob, gender } = profile; - const { first, middle, last, suffix } = profile.userFullName; - const mask = srSubstitute('●●●–●●–', 'ending with'); - - return ( -
-

- Confirm the personal information we have on file for you. -

-
- - {`${first || ''}${first && middle ? ' ' : ''}${middle || ''}${ - (first || middle) && last ? ' ' : '' - }${last || ''}`} - {suffix && `, ${suffix}`} - -

- Social Security number: {mask} - {veteranSocialSecurityNumber?.slice(5)} -

-

- VA file number: {mask} - {vaFileNumber?.slice(5)} -

-

Branch of service: {genderLabels[gender]}

- -

Date of birth: {dob ? formatDate(dob) : ''}

-
- {editNote('personal information')} -
- ); -} - -VeteranDetails.propTypes = { - profile: PropTypes.shape({ - dob: PropTypes.string, - gender: PropTypes.string, - ssn: PropTypes.string, - userFullName: PropTypes.shape({ - first: PropTypes.string, - middle: PropTypes.string, - last: PropTypes.string, - suffix: PropTypes.string, - }), - vaFileNumber: PropTypes.string, - }), -}; - -const mapStateToProps = state => ({ - profile: state.user.profile, -}); - -export default connect(mapStateToProps)(VeteranDetails); diff --git a/src/applications/representative-appoint/config/form.js b/src/applications/representative-appoint/config/form.js index 5635d995f4b8..21e90a798f34 100644 --- a/src/applications/representative-appoint/config/form.js +++ b/src/applications/representative-appoint/config/form.js @@ -22,7 +22,6 @@ import { authorizeOutsideVANames, claimantRelationship, claimantPersonalInformation, - // confirmClaimantPersonalInformation, claimantContactPhoneEmail, claimantContactMailing, veteranPersonalInformation, @@ -38,8 +37,6 @@ import { contactAccreditedRepresentative, } from '../pages'; -import { prefillTransformer } from '../prefill-transformer'; - import initialData from '../tests/fixtures/data/test-data.json'; import ClaimantType from '../components/ClaimantType'; import SelectAccreditedRepresentative from '../components/SelectAccreditedRepresentative'; @@ -88,7 +85,6 @@ const formConfig = { }, version: 0, prefillEnabled: true, - prefillTransformer, v3SegmentedProgressBar: true, additionalRoutes: [ { @@ -197,13 +193,6 @@ const formConfig = { uiSchema: claimantPersonalInformation.uiSchema, schema: claimantPersonalInformation.schema, }, - // confirmClaimantPersonalInformation: { - // path: 'confirm-claimant-personal-information', - // depends: formData => !preparerIsVeteran({ formData }), - // title: 'Your Personal Information', - // uiSchema: confirmClaimantPersonalInformation.uiSchema, - // schema: confirmClaimantPersonalInformation.schema, - // }, claimantContactMailing: { path: 'claimant-contact-mailing', depends: formData => !preparerIsVeteran({ formData }), diff --git a/src/applications/representative-appoint/pages/claimant/confirmClaimantPersonalInformation.js b/src/applications/representative-appoint/pages/claimant/confirmClaimantPersonalInformation.js deleted file mode 100644 index c8ea10ae3528..000000000000 --- a/src/applications/representative-appoint/pages/claimant/confirmClaimantPersonalInformation.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react'; -import VeteranDetails from '../../components/VeteranDetails'; - -/** @type {PageSchema} */ - -export const uiSchema = { - 'ui:title': 'Confirm the personal information we have on file for you', - 'ui:description': , - 'view:confirmVeteranPersonalInformation': { - 'ui:options': { - displayEmptyObjectOnReview: true, - }, - }, -}; -export const schema = { - type: 'object', - properties: { - 'view:confirmVeteranPersonalInformation': { - type: 'object', - properties: {}, - }, - }, -}; diff --git a/src/applications/representative-appoint/pages/index.js b/src/applications/representative-appoint/pages/index.js index e19eb477e79a..718d3ecebdf9 100644 --- a/src/applications/representative-appoint/pages/index.js +++ b/src/applications/representative-appoint/pages/index.js @@ -8,7 +8,6 @@ import * as claimantType from './claimant/claimantType'; import * as claimantContactPhoneEmail from './claimant/claimantContactPhoneEmail'; import * as claimantRelationship from './claimant/claimantRelationship'; import * as claimantPersonalInformation from './claimant/claimantPersonalInformation'; -import * as confirmClaimantPersonalInformation from './claimant/confirmClaimantPersonalInformation'; import * as claimantContactMailing from './claimant/claimantContactMailing'; import * as veteranPersonalInformation from './veteran/veteranPersonalInformation'; import * as veteranContactPhoneEmail from './veteran/veteranContactPhoneEmail'; @@ -33,7 +32,6 @@ export { claimantContactPhoneEmail, claimantRelationship, claimantPersonalInformation, - confirmClaimantPersonalInformation, claimantContactMailing, veteranPersonalInformation, veteranContactPhoneEmail, diff --git a/src/applications/representative-appoint/prefill-transformer.js b/src/applications/representative-appoint/prefill-transformer.js deleted file mode 100644 index d5c9ab38aa68..000000000000 --- a/src/applications/representative-appoint/prefill-transformer.js +++ /dev/null @@ -1,100 +0,0 @@ -import _ from 'platform/utilities/data'; - -export function prefillTransformer(pages, formData, metadata) { - const prefillPersonalInformation = data => { - const newData = _.omit(['personalInformation'], data); - const { personalInformation } = data; - - if (personalInformation) { - const { - first, - middle, - last, - suffix, - socialOrServiceNum, - dateOfBirth, - brandOfService, - } = personalInformation; - newData.aboutYourself = {}; - - if (first) newData.aboutYourself.first = first; - if (middle) newData.aboutYourself.middle = middle; - if (last) newData.aboutYourself.last = last; - if (suffix) newData.aboutYourself.suffix = suffix; - if (dateOfBirth) newData.aboutYourself.dateOfBirth = dateOfBirth; - if (brandOfService) newData.aboutYourself.brandOfService = brandOfService; - - if (socialOrServiceNum) { - const { ssn, serviceNumber } = socialOrServiceNum; - newData.aboutYourself.socialOrServiceNum = {}; - - if (ssn) newData.aboutYourself.socialOrServiceNum.ssn = ssn; - if (serviceNumber) - newData.aboutYourself.socialOrServiceNum.serviceNumber = serviceNumber; - } - } - - return newData; - }; - - const prefillContactInformation = data => { - const newData = _.omit(['contactInformation', 'avaProfile'], data); - const { contactInformation, avaProfile } = data; - - if (contactInformation) { - const { - preferredName, - onBaseOutsideUS, - country, - address, - phoneNumber, - emailAddress, - } = contactInformation; - const { schoolInfo, businessEmail, businessPhone } = avaProfile; - - if (preferredName) newData.contactPreferredName = preferredName; - if (onBaseOutsideUS) newData.onBaseOutsideUS = onBaseOutsideUS; - if (country) newData.country = country; - if (emailAddress) newData.emailAddress = emailAddress; - if (phoneNumber) newData.phoneNumber = phoneNumber; - if (schoolInfo) newData.school = schoolInfo; - if (businessEmail) newData.businessEmail = businessEmail; - if (businessPhone) newData.businessPhone = businessPhone; - - if (address) { - const { militaryAddress } = address; - newData.address = { - street: address.street || '', - unitNumber: address.unitNumber || '', - street2: address.street2 || '', - street3: address.street3 || '', - city: address.city || '', - state: address.state || '', - postalCode: address.postalCode || '', - }; - - if (militaryAddress) { - newData.address.militaryAddress = { - militaryPostOffice: militaryAddress.militaryPostOffice, - militaryState: militaryAddress.militaryState, - }; - } - } - } - - return newData; - }; - - const transformations = [ - prefillPersonalInformation, - prefillContactInformation, - ]; - - const applyTransformations = (data = {}, transformer) => transformer(data); - - return { - metadata, - formData: transformations.reduce(applyTransformations, formData), - pages, - }; -} diff --git a/src/applications/representative-appoint/tests/components/GetFormHelp.unit.spec.jsx b/src/applications/representative-appoint/tests/components/GetFormHelp.unit.spec.jsx new file mode 100644 index 000000000000..71180c42ee4d --- /dev/null +++ b/src/applications/representative-appoint/tests/components/GetFormHelp.unit.spec.jsx @@ -0,0 +1,12 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import { expect } from 'chai'; +import GetFormHelp from '../../components/GetFormHelp'; + +describe('GetFormHelp', () => { + it('should render', () => { + const { container } = render(); + + expect(container).to.exist; + }); +}); diff --git a/src/applications/representative-appoint/tests/components/SubmissionError.unit.spec.jsx b/src/applications/representative-appoint/tests/components/SubmissionError.unit.spec.jsx new file mode 100644 index 000000000000..7d90f5dee4f1 --- /dev/null +++ b/src/applications/representative-appoint/tests/components/SubmissionError.unit.spec.jsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { render, waitFor } from '@testing-library/react'; +import { expect } from 'chai'; + +import { $ } from 'platform/forms-system/src/js/utilities/ui'; + +import SubmissionError from '../../components/SubmissionError'; + +describe('SubmissionError', () => { + it('should render', async () => { + global.window.dataLayer = []; + const form = { inProgressFormId: '1234' }; + const heading = 'We couldn’t generate your form'; + + const { container, rerender } = render( +
+ +
, + ); + + expect($('va-alert[status="error"]', container)).to.exist; + + const h3 = $('h3', container); + expect(h3.textContent).to.eq(heading); + + // rerendering to test alertRef scroll & focus branch (not working?) + await rerender( +
+ +
, + ); + + await waitFor(() => { + expect(document.activeElement).to.eq(h3); + }); + }); +}); diff --git a/src/applications/representative-appoint/tests/config/formConfig.unit.spec.jsx b/src/applications/representative-appoint/tests/config/formConfig.unit.spec.jsx new file mode 100644 index 000000000000..c2eb6f4ca0b0 --- /dev/null +++ b/src/applications/representative-appoint/tests/config/formConfig.unit.spec.jsx @@ -0,0 +1,150 @@ +import { expect } from 'chai'; +import formConfig from '../../config/form'; +import mockFormData2122a from '../fixtures/data/21-22a/form-data.json'; +import mockFormData2122 from '../fixtures/data/form-data.json'; + +describe('FormConfig depends function', () => { + const { chapters } = formConfig; + + const authPages = chapters.authorization.pages; + const repPages = chapters.accreditedRepresentativeInformation.pages; + const claimantPages = chapters.claimantInfo.pages; + const veteranPages = chapters.veteranInfo.pages; + + context('21-22a', () => { + it('should show authorizeInsideVA', () => { + expect(authPages.authorizeInsideVA.depends(mockFormData2122a)).to.be.true; + }); + it('should show veteranServiceInformation', () => { + expect(veteranPages.veteranServiceInformation.depends(mockFormData2122a)) + .to.be.true; + }); + it('should show authorizeInsideVA', () => { + expect(authPages.authorizeInsideVA.depends(mockFormData2122a)).to.be.true; + }); + + it('should show authorizeOutsideVA', () => { + expect(authPages.authorizeOutsideVA.depends(mockFormData2122a)).to.be + .true; + }); + + it('should show authorizeOutsideVANames', () => { + expect(authPages.authorizeOutsideVANames.depends(mockFormData2122a)).to.be + .true; + }); + + it('should hide selectAccreditedOrganization', () => { + expect(repPages.selectAccreditedOrganization.depends(mockFormData2122a)) + .to.be.false; + }); + }); + + context('21-22', () => { + it('should hide authorizeInsideVA', () => { + expect(authPages.authorizeInsideVA.depends(mockFormData2122)).to.be.false; + }); + + it('should show selectAccreditedOrganization', () => { + expect(repPages.selectAccreditedOrganization.depends(mockFormData2122)).to + .be.true; + }); + + it('should hide authorizeInsideVA', () => { + expect(authPages.authorizeInsideVA.depends(mockFormData2122)).to.be.false; + }); + + it('should hide authorizeOutsideVA', () => { + expect(authPages.authorizeOutsideVA.depends(mockFormData2122)).to.be + .false; + }); + it('should hide authorizeOutsideVANames', () => { + expect(authPages.authorizeOutsideVANames.depends(mockFormData2122)).to.be + .false; + }); + + it('should hide veteranServiceInformation', () => { + expect(veteranPages.veteranServiceInformation.depends(mockFormData2122)) + .to.be.false; + }); + }); + + context('applicant is veteran', () => { + const mockData = { + 'view:applicantIsVeteran': 'Yes', + }; + it('should hide claimant pages', () => { + expect(claimantPages.claimantRelationship.depends(mockData)).to.be.false; + expect(claimantPages.claimantPersonalInformation.depends(mockData)).to.be + .false; + expect(claimantPages.claimantContactMailing.depends(mockData)).to.be + .false; + expect(claimantPages.claimantContactPhoneEmail.depends(mockData)).to.be + .false; + }); + + it('should show veteran pages', () => { + expect(claimantPages.veteranPersonalInformation.depends(mockData)).to.be + .true; + expect(claimantPages.veteranContactMailing.depends(mockData)).to.be.true; + expect(claimantPages.veteranContactPhoneEmail.depends(mockData)).to.be + .true; + expect(claimantPages.veteranIdentification.depends(mockData)).to.be.true; + }); + + it('should hide veteranInfo', () => { + expect(chapters.veteranInfo.depends(mockData)).to.be.false; + expect(veteranPages.veteranPersonalInformation.depends(mockData)).to.be + .false; + expect(veteranPages.veteranContactMailingClaimant.depends(mockData)).to.be + .false; + expect(veteranPages.veteranContactPhoneEmail.depends(mockData)).to.be + .false; + expect(veteranPages.veteranIdentification.depends(mockData)).to.be.false; + }); + }); + + context('applicant is not veteran', () => { + const mockData = { + 'view:applicantIsVeteran': 'No', + }; + + it('should show claimant pages', () => { + expect(claimantPages.claimantRelationship.depends(mockData)).to.be.true; + expect(claimantPages.claimantPersonalInformation.depends(mockData)).to.be + .true; + expect(claimantPages.claimantContactMailing.depends(mockData)).to.be.true; + expect(claimantPages.claimantContactPhoneEmail.depends(mockData)).to.be + .true; + }); + + it('should hide veteran pages', () => { + expect(claimantPages.veteranPersonalInformation.depends(mockData)).to.be + .false; + expect(claimantPages.veteranContactMailing.depends(mockData)).to.be.false; + expect(claimantPages.veteranContactPhoneEmail.depends(mockData)).to.be + .false; + expect(claimantPages.veteranIdentification.depends(mockData)).to.be.false; + }); + + it('should show veteranInfo', () => { + expect(chapters.veteranInfo.depends(mockData)).to.be.true; + expect(veteranPages.veteranPersonalInformation.depends(mockData)).to.be + .true; + expect(veteranPages.veteranContactMailingClaimant.depends(mockData)).to.be + .true; + expect(veteranPages.veteranContactPhoneEmail.depends(mockData)).to.be + .true; + expect(veteranPages.veteranIdentification.depends(mockData)).to.be.true; + }); + }); + + it('should hide selectAccreditedOrganization when rep status null', () => { + expect(repPages.replaceAccreditedRepresentative.depends(mockFormData2122)) + .to.be.false; + }); + + it('should show authorizeMedicalSelect when authorizationRadio is yes', () => { + expect(authPages.authorizeMedicalSelect.depends(mockFormData2122)).to.be + .true; + }); +}); diff --git a/src/applications/representative-appoint/tests/fixtures/data/21-22a/form-data.json b/src/applications/representative-appoint/tests/fixtures/data/21-22a/form-data.json new file mode 100644 index 000000000000..4ba9ee413403 --- /dev/null +++ b/src/applications/representative-appoint/tests/fixtures/data/21-22a/form-data.json @@ -0,0 +1,398 @@ +{ + "view:addressAuthorizationPolicy": {}, + "authorizeAddressRadio": "Yes, they can change my address if it’s incorrect or outdated", + "authorizeOutsideVARadio": "Yes", + "view:authorizationNote3": {}, + "view:authorizeRecordsSelect": {}, + "authorizeMedicalSelectCheckbox": { + "alcoholRecords": true, + "drugAbuseRecords": true + }, + "view:authorizationPolicy": {}, + "authorizationRadio": "Yes, but they can only access some of these types of records", + "view:authorizationNote4": {}, + "titleSchema": {}, + "profileNotUpdatedNote": {}, + "applicantPhone": "5347687980", + "homeAddress": { + "view:militaryBaseDescription": {}, + "country": "USA", + "street": "123 Any Street", + "city": "Town", + "state": "OH", + "postalCode": "43210" + }, + "applicantName": { "first": "Bob", "last": "Tester" }, + "applicantDOB": "1990-01-01", + "claimantRelationship": "CHILD", + "veteranFullName": { + "first": "John", + "middle": "Edmund", + "last": "Doe", + "suffix": "Sr." + }, + "veteranSocialSecurityNumber": "333224444", + "vaClaimsHistory": true, + "vaFileNumber": "123456789", + "veteranDateOfBirth": "1960-01-01", + "serviceBranch": { "army": true, "navy": true }, + "activeServiceDateRange": { "from": "2003-03-02", "to": "2007-03-20" }, + "serviceNumber": "123456789", + "serveUnderOtherNames": true, + "previousNames": [ + { "previousFullName": { "first": "Joseph", "last": "Doe" } } + ], + "placeOfSeparation": "Northampton, MA", + "powStatus": true, + "powDateRange": { "from": "1971-02-26", "to": "1973-03-02" }, + "socialSecurityDisability": false, + "medicalCondition": false, + "nursingHome": true, + "medicaidCoverage": false, + "medicaidStatus": true, + "specialMonthlyPension": false, + "vaTreatmentHistory": true, + "vaMedicalCenters": [ + { "medicalCenter": "Dallas Fort Worth VA Medical Center" } + ], + "federalTreatmentHistory": true, + "federalMedicalCenters": [{ "medicalCenter": "Memphis Health Care" }], + "currentEmployment": true, + "currentEmployers": [ + { "jobType": "Customer service", "jobHoursWeek": 20, "jobTitle": "Manager" } + ], + "maritalStatus": "Separated", + "marriages": [ + { + "spouseFullName": { + "first": "Jessica", + "middle": "Middle", + "last": "Doe" + }, + "view:pastMarriage": { + "reasonForSeparation": "Divorce", + "dateOfMarriage": "1989-03-02", + "dateOfSeparation": "1990-03-02", + "locationOfMarriage": "Dallas", + "locationOfSeparation": "San Antonio, TX" + } + }, + { + "spouseFullName": { "first": "Jane", "middle": "Middle", "last": "Doe" }, + "view:pastMarriage": { + "reasonForSeparation": "Spouse's death", + "dateOfMarriage": "1989-03-02", + "dateOfSeparation": "1990-03-02", + "locationOfMarriage": "Dallas", + "locationOfSeparation": "San Antonio, TX" + } + }, + { + "spouseFullName": { "first": "Meg", "middle": "Middle", "last": "Doe" }, + "view:currentMarriage": { + "dateOfMarriage": "1994-03-02", + "locationOfMarriage": "North Adams, MA", + "marriageType": "In a civil or religious ceremony with an officiant who signed my marriage license" + } + } + ], + "spouseDateOfBirth": "1960-01-01", + "spouseSocialSecurityNumber": "333224444", + "spouseIsVeteran": true, + "spouseVaFileNumber": "23423444", + "view:liveWithSpouse": false, + "spouseAddress": { + "street": "123 7th st", + "street2": "Apt 3", + "city": "Pittsfield", + "country": "USA", + "state": "MA", + "postalCode": "01050" + }, + "reasonForCurrentSeparation": "OTHER", + "otherExplanation": "Personal reason", + "currentSpouseMonthlySupport": 2444, + "currentSpouseMaritalHistory": "I’m not sure", + "spouseMarriages": [ + { + "dateOfMarriage": "1980-03-02", + "locationOfMarriage": "Seattle, WA", + "spouseFullName": { + "first": "Joe", + "middle": "F", + "last": "Generic", + "suffix": "Jr." + }, + "reasonForSeparation": "OTHER", + "otherExplanation": "Other reason", + "dateOfSeparation": "1990-03-02", + "locationOfSeparation": "Tacoma, WA" + } + ], + "view:hasDependents": true, + "dependents": [ + { + "childInHousehold": false, + "childAddress": { + "street": "123 8th st", + "city": "Hadley", + "country": "USA", + "state": "ME", + "postalCode": "01050" + }, + "personWhoLivesWithChild": { + "first": "Joe", + "middle": "Middle", + "last": "Smith" + }, + "monthlyPayment": 3444, + "childPlaceOfBirth": "Tallahassee, FL", + "childSocialSecurityNumber": "333224444", + "childRelationship": "biological", + "previouslyMarried": true, + "married": true, + "fullName": { "first": "Emily", "middle": "Anne", "last": "Doe" }, + "childDateOfBirth": "2000-03-03" + }, + { + "childInHousehold": true, + "childPlaceOfBirth": "Troy, MT", + "childSocialSecurityNumber": "333224444", + "childRelationship": "adopted", + "previouslyMarried": false, + "attendingCollege": true, + "fullName": { "first": "John", "middle": "Frederick", "last": "Doe" }, + "childDateOfBirth": "2005-09-22" + } + ], + "totalNetWorth": false, + "netWorthEstimation": 0, + "hasCareExpenses": true, + "careExpenses": [ + { + "recipients": "VETERAN", + "provider": "NYC Care Provider", + "careType": "CARE_FACILITY", + "ratePerHour": 100, + "hoursPerWeek": 20, + "careDateRange": { "from": "2020-08-01", "to": "2023-05-25" }, + "paymentFrequency": "ONCE_MONTH", + "paymentAmount": 2500 + }, + { + "recipients": "CHILD", + "childName": "Joe Doe", + "provider": "LA Care Provider", + "careType": "CARE_FACILITY", + "ratePerHour": 200, + "hoursPerWeek": 10, + "careDateRange": { "from": "2020-08-01" }, + "noCareEndDate": true, + "paymentFrequency": "ONCE_YEAR", + "paymentAmount": 22500 + } + ], + "hasMedicalExpenses": true, + "medicalExpenses": [ + { + "recipients": "VETERAN", + "provider": "Funeral Home", + "purpose": "Burial expenses", + "paymentDate": "2020-03-15", + "paymentFrequency": "ONE_TIME", + "paymentAmount": 10000 + }, + { + "recipients": "CHILD", + "childName": "Joe Doe", + "provider": "Health Provider", + "purpose": "Medical expenses", + "paymentDate": "2023-07-01", + "paymentFrequency": "ONE_TIME", + "paymentAmount": 10000 + } + ], + "transferredAssets": true, + "homeOwnership": true, + "homeAcreageMoreThanTwo": true, + "homeAcreageValue": 75000, + "landMarketable": true, + "receivesIncome": true, + "incomeSources": [ + { + "typeOfIncome": "SOCIAL_SECURITY", + "receiver": "Jane Doe", + "payer": "John Doe", + "amount": 278 + } + ], + "view:noDirectDeposit": false, + "bankAccount": { + "accountType": "checking", + "bankName": "Best Bank", + "accountNumber": "001122334455", + "routingNumber": "123123123" + }, + "veteranHomeAddress": { + "view:militaryBaseDescription": {}, + "country": "USA", + "street": "123 8th st", + "city": "Hadley", + "state": "ME", + "postalCode": "01050" + }, + "veteranEmail": "test@example.com", + "Primary phone": "5551234567", + "mobilePhone": "5551234567", + "internationalPhone": "001-555-123-4567", + "view:aidAttendance": true, + "isOver65": false, + "noRapidProcessing": true, + "statementOfTruthCertified": true, + "statementOfTruthSignature": "John Edmund Doe", + "descriptionSchema": {}, + "view:insideVAAuthorizationPolicy": {}, + "view:authorizationNote2": {}, + "view:outsideVAAuthorizationPolicy": {}, + "view:authorizationNote5": {}, + "view:unsureNote": {}, + "view:representativeQuery": "bob", + "view:representativeSearchResults": [ + { + "data": { + "id": "19731", + "type": "representative", + "attributes": { + "firstName": "Robert", + "lastName": "Name", + "fullName": "Robert Name", + "addressLine1": "175 W 5th St", + "addressLine2": null, + "addressLine3": null, + "addressType": "Domestic", + "city": "San Bernardino", + "countryName": "United States", + "countryCodeIso3": "USA", + "province": "California", + "internationalPostalCode": null, + "stateCode": "CA", + "zipCode": "54345", + "zipSuffix": "3243", + "email": null, + "phone": null, + "individualType": "veteran_service_officer", + "accreditedOrganizations": { + "data": [ + { + "id": "074", + "type": "organization", + "attributes": { + "name": "Am Legion", + "addressLine1": "13408 R St NW", + "addressLine2": null, + "addressLine3": null, + "addressType": "Domestic", + "city": "Washington", + "countryName": null, + "countryCodeIso3": null, + "province": null, + "internationalPostalCode": null, + "stateCode": "DC", + "zipCode": "20056", + "zipSuffix": "2401", + "phone": "555-841-2400", + "lat": null, + "long": null, + "poaCode": "074" + } + }, + { + "id": "064", + "type": "organization", + "attributes": { + "name": "National Association", + "addressLine1": "25 North Ave NW", + "addressLine2": "Ste 500", + "addressLine3": null, + "addressType": "Domestic", + "city": "Washington", + "countryName": null, + "countryCodeIso3": null, + "province": null, + "internationalPostalCode": null, + "stateCode": "DC", + "zipCode": "20003", + "zipSuffix": "1450", + "phone": "", + "lat": null, + "long": null, + "poaCode": "064" + } + } + ] + } + } + } + }, + { + "data": { + "id": "20087", + "type": "representative", + "attributes": { + "firstName": "John", + "lastName": "Adams", + "fullName": "John Adams", + "addressLine1": "2342 Main St", + "addressLine2": null, + "addressLine3": null, + "addressType": "Domestic", + "city": "Anytown", + "countryName": "United States", + "countryCodeIso3": "USA", + "province": "Connecticut", + "internationalPostalCode": null, + "stateCode": "CT", + "zipCode": "54837", + "zipSuffix": "2319", + "email": "attorney@law.com", + "phone": "585-878-0710ext.321", + "individualType": "attorney", + "accreditedOrganizations": { + "data": [] + } + } + } + } + ], + "view:applicantIsVeteran": "No", + "view:selectedRepresentative": { + "id": "20087", + "type": "representative", + "attributes": { + "firstName": "John", + "lastName": "Adams", + "fullName": "John Adams", + "addressLine1": "2342 Main St", + "addressLine2": null, + "addressLine3": null, + "addressType": "Domestic", + "city": "Anytown", + "countryName": "United States", + "countryCodeIso3": "USA", + "province": "Connecticut", + "internationalPostalCode": null, + "stateCode": "CT", + "zipCode": "54837", + "zipSuffix": "2319", + "email": "attorney@law.com", + "phone": "585-878-0710ext.321", + "individualType": "attorney", + "accreditedOrganizations": { + "data": [] + } + } + }, + "view:representativeStatus": null, + "selectedAccreditedOrganizationId": "074", + "selectedAccreditedOrganizationName": "Am Legion" +} diff --git a/src/applications/representative-appoint/tests/fixtures/data/initial-form-data.json b/src/applications/representative-appoint/tests/fixtures/data/initial-form-data.json new file mode 100644 index 000000000000..e4ebd4685a23 --- /dev/null +++ b/src/applications/representative-appoint/tests/fixtures/data/initial-form-data.json @@ -0,0 +1,69 @@ +{ + "titleSchema": {}, + "profileNotUpdatedNote": {}, + "applicantName": {}, + "veteranFullName": { + "first": "John", + "middle": "Edmund", + "last": "Doe", + "suffix": "Sr." + }, + "veteranSocialSecurityNumber": "333224444", + "vaClaimsHistory": true, + "vaFileNumber": "123456789", + "veteranDateOfBirth": "1960-01-01", + "serviceBranch": { + "army": true, + "navy": true + }, + "activeServiceDateRange": { + "from": "2003-03-02", + "to": "2007-03-20" + }, + "serviceNumber": "123456789", + "serveUnderOtherNames": true, + "previousNames": [ + { + "previousFullName": { + "first": "Joseph", + "last": "Doe" + } + } + ], + "placeOfSeparation": "Northampton, MA", + "powStatus": true, + "powDateRange": { + "from": "1971-02-26", + "to": "1973-03-02" + }, + + "veteranHomeAddress": { + "'view:militaryBaseDescription'": {}, + "country": "USA", + "street": "123 8th st", + "city": "Hadley", + "state": "ME", + "postalCode": "01050" + }, + "veteranEmail": "test@example.com", + "'Primary phone'": "5551234567", + "mobilePhone": "5551234567", + "internationalPhone": "001-555-123-4567", + "homeAddress": { + "'view:militaryBaseDescription'": {} + }, + "descriptionSchema": {}, + "'view:authorizationPolicy'": {}, + "'view:authorizationNote4'": {}, + "'view:authorizeRecordsSelect'": {}, + "authorizeMedicalSelectCheckbox": {}, + "'view:addressAuthorizationPolicy'": {}, + "'view:authorizationNote3'": {}, + "'view:insideVAAuthorizationPolicy'": {}, + "'view:authorizationNote2'": {}, + "'view:outsideVAAuthorizationPolicy'": {}, + "'view:authorizationNote5'": {}, + "'view:unsureNote'": {}, + "'view:representativeQuery'": "", + "'view:representativeSearchResults'": [] +} diff --git a/src/applications/representative-appoint/tests/fixtures/data/organization-type.json b/src/applications/representative-appoint/tests/fixtures/data/organization-type.json new file mode 100644 index 000000000000..0ce76205cb6c --- /dev/null +++ b/src/applications/representative-appoint/tests/fixtures/data/organization-type.json @@ -0,0 +1,23 @@ +{ + "id": "014", + "type": "organization", + "attributes": { + "name": "Gold Org", + "addressLine1": "300 N Winston Rd Rm 445", + "addressLine2": null, + "addressLine3": null, + "addressType": "Domestic", + "city": "Anytown", + "countryName": "United States", + "countryCodeIso3": "USA", + "province": "Ohio", + "internationalPostalCode": null, + "stateCode": "OH", + "zipCode": "44562", + "zipSuffix": "4324", + "phone": "1-855-721-6546", + "lat": 37.234432, + "long": -78.756847, + "poaCode": "014" + } +} diff --git a/src/applications/representative-appoint/tests/pages/claimantType.unit.spec.jsx b/src/applications/representative-appoint/tests/pages/claimantType.unit.spec.jsx new file mode 100644 index 000000000000..8d2762468497 --- /dev/null +++ b/src/applications/representative-appoint/tests/pages/claimantType.unit.spec.jsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { render } from '@testing-library/react'; +import { expect } from 'chai'; +import sinon from 'sinon'; +import ClaimantType from '../../components/ClaimantType'; +import initialFormData from '../fixtures/data/initial-form-data.json'; + +describe(' handlers', async () => { + const getProps = () => { + return { + props: { + setFormData: sinon.spy(), + formData: initialFormData, + }, + mockStore: { + getState: () => ({ + form: { + data: initialFormData, + }, + }), + subscribe: () => {}, + dispatch: () => ({ + setFormData: () => {}, + }), + }, + }; + }; + + const renderContainer = (props, mockStore) => { + return render( + + + , + ); + }; + + it('should render', async () => { + const { props, mockStore } = getProps(); + + const { container } = renderContainer(props, mockStore); + + const radioButtonYes = container.querySelector( + 'va-radio-option[value="Yes"]', + ); + + const radioButtonNo = container.querySelector( + 'va-radio-option[value="No"]', + ); + expect(radioButtonYes).to.exist; + expect(radioButtonNo).to.exist; + }); +}); diff --git a/src/applications/representative-appoint/tests/pages/representative/contactAccreditedRepresentative.unit.spec.jsx b/src/applications/representative-appoint/tests/pages/representative/contactAccreditedRepresentative.unit.spec.jsx new file mode 100644 index 000000000000..d9650206ef80 --- /dev/null +++ b/src/applications/representative-appoint/tests/pages/representative/contactAccreditedRepresentative.unit.spec.jsx @@ -0,0 +1,172 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { expect } from 'chai'; +import { render, waitFor, fireEvent } from '@testing-library/react'; +import sinon from 'sinon'; +import ContactAccreditedRepresentative from '../../../components/ContactAccreditedRepresentative'; +import selectedRep from '../../fixtures/data/representative.json'; + +describe('', () => { + const getProps = () => { + return { + props: { + loggedIn: true, + formData: { 'view:selectedRepresentative': selectedRep.data }, + }, + mockStore: { + getState: () => ({ + form: { + data: { 'view:selectedRepresentative': selectedRep.data }, + }, + }), + loggedIn: true, + subscribe: () => {}, + }, + }; + }; + + it('should render component', () => { + const { props, mockStore } = getProps(); + + const { container } = render( + + + , + ); + expect(container).to.exist; + }); + + context('non-review mode', () => { + beforeEach(function() { + Object.defineProperty(window, 'location', { + value: { search: '' }, + writable: true, + }); + }); + + it('should call goBack with formData', async () => { + const { props, mockStore } = getProps(); + + const goBackSpy = sinon.spy(); + + const { getByText } = render( + + + , + ); + + fireEvent.click(getByText('Back')); + + await waitFor(() => { + expect(goBackSpy.calledWith(props.formData)).to.be.true; + }); + }); + + it('should call goForward with formData', async () => { + const { props, mockStore } = getProps(); + + const goForwardSpy = sinon.spy(); + + Object.defineProperty(window, 'location', { + value: { search: '' }, + writable: true, + }); + + const { getByText } = render( + + + , + ); + + fireEvent.click(getByText('Continue')); + + await waitFor(() => { + expect(goForwardSpy.calledWith(props.formData)).to.be.true; + }); + }); + }); + + context('review mode', () => { + beforeEach(function() { + Object.defineProperty(window, 'location', { + value: { search: '?review=true' }, + writable: true, + }); + }); + + it('should navigate to the representative selection page when in review mode', async () => { + const { props, mockStore } = getProps(); + + const goToPathSpy = sinon.spy(); + + const { getByText } = render( + + + , + ); + + fireEvent.click(getByText('Back')); + + await waitFor(() => { + expect(goToPathSpy.calledWith('/representative-select?review=true')).to + .be.true; + }); + }); + + it('should navigate to the organization selection page when organization selection is required', async () => { + const { props, mockStore } = getProps(); + + const goToPathSpy = sinon.spy(); + + props.formData[ + 'view:selectedRepresentative' + ].attributes.accreditedOrganizations = { + data: [ + { attributes: { name: 'Organization 1' } }, + { attributes: { name: 'Organization 2' } }, + ], + }; + + const { getByText } = render( + + + , + ); + + fireEvent.click(getByText('Continue')); + + await waitFor(() => { + expect( + goToPathSpy.calledWith('/representative-organization?review=true'), + ).to.be.true; + }); + }); + + it('should navigate to the review-and-submit page when organization selection is not required', async () => { + const { props, mockStore } = getProps(); + + const goToPathSpy = sinon.spy(); + + props.formData[ + 'view:selectedRepresentative' + ].attributes.accreditedOrganizations = { + data: [{ attributes: { name: 'Organization 1' } }], // Only one organization + }; + + const { getByText } = render( + + + , + ); + + fireEvent.click(getByText('Continue')); + + await waitFor(() => { + expect(goToPathSpy.calledWith('/review-and-submit')).to.be.true; + }); + }); + }); +}); diff --git a/src/applications/representative-appoint/tests/pages/representative/selectAccreditedRepresentative.unit.spec.jsx b/src/applications/representative-appoint/tests/pages/representative/selectAccreditedRepresentative.unit.spec.jsx index 6ef24d09afc2..780eb7e7c60d 100644 --- a/src/applications/representative-appoint/tests/pages/representative/selectAccreditedRepresentative.unit.spec.jsx +++ b/src/applications/representative-appoint/tests/pages/representative/selectAccreditedRepresentative.unit.spec.jsx @@ -6,22 +6,28 @@ import { waitFor, fireEvent, getByTestId, + cleanup, } from '@testing-library/react'; import sinon from 'sinon'; import { SelectAccreditedRepresentative } from '../../../components/SelectAccreditedRepresentative'; import * as api from '../../../api/fetchRepStatus'; import repResults from '../../fixtures/data/representative-results.json'; +import * as reviewPageHook from '../../../hooks/useReviewPage'; describe('', () => { - const getProps = ({ submitted = false, setFormData = () => {} } = {}) => { + const getProps = ({ submitted = false } = {}) => { return { props: { formContext: { submitted, }, + loggedIn: true, formData: { 'view:representativeSearchResults': repResults }, - setFormData, + setFormData: sinon.spy(), + goToPath: sinon.spy(), + goBack: sinon.spy(), + goForward: sinon.spy(), }, mockStore: { getState: () => ({ @@ -29,40 +35,71 @@ describe('', () => { data: { 'view:representativeSearchResults': repResults }, }, }), - loggedIn: true, subscribe: () => {}, - dispatch: () => ({ - setFormData: () => {}, - }), }, }; }; - it('should render component', () => { - const { props, mockStore } = getProps(); + afterEach(() => { + cleanup(); + }); + const renderContainer = (props, mockStore) => { const { container } = render( , ); + + return container; + }; + + it('should render component', () => { + const { props, mockStore } = getProps(); + const container = renderContainer(props, mockStore); + expect(container).to.exist; }); - it('should call getRepStatus and update state accordingly', async () => { + it('calls getRepStatus and update state accordingly on search', async () => { const { props, mockStore } = getProps(); const fetchRepStatusStub = sinon.stub(api, 'fetchRepStatus').resolves({ data: { status: 'active' }, }); - const setFormDataSpy = sinon.spy(props, 'setFormData'); + const container = renderContainer(props, mockStore); - const { container } = render( - - - , - ); + const selectRepButton = getByTestId(container, 'rep-select-19731'); + + expect(selectRepButton).to.exist; + + fireEvent.click(selectRepButton); + + await waitFor(() => { + expect(fetchRepStatusStub.calledOnce).to.be.true; + expect(props.setFormData.called).to.be.true; + expect(props.goToPath.called).to.be.false; + expect(props.setFormData.args[0][0]).to.include({ + 'view:selectedRepresentative': repResults[0].data, + }); + }); + + fetchRepStatusStub.restore(); + }); + + it('calls getRepStatus and update state accordingly is review mode', async () => { + const { props, mockStore } = getProps(); + + const fetchRepStatusStub = sinon.stub(api, 'fetchRepStatus').resolves({ + data: { status: 'active' }, + }); + + const useReviewPageStub = sinon + .stub(reviewPageHook, 'useReviewPage') + .returns(true); + + const container = renderContainer(props, mockStore); const selectRepButton = getByTestId(container, 'rep-select-19731'); @@ -72,13 +109,107 @@ describe('', () => { await waitFor(() => { expect(fetchRepStatusStub.calledOnce).to.be.true; - expect(setFormDataSpy.called).to.be.true; - expect(setFormDataSpy.args[0][0]).to.include({ + expect(props.setFormData.called).to.be.true; + expect(props.goToPath.called).to.be.true; + expect(props.setFormData.args[0][0]).to.include({ 'view:selectedRepresentative': repResults[0].data, }); }); - // fetchRepStatusStub.restore(); - // setFormDataSpy.restore(); + useReviewPageStub.restore(); + }); + + it('displays query error when query is invalid', async () => { + const { mockStore, props } = getProps(); + + const container = renderContainer(props, mockStore); + + const forwardButton = container.querySelectorAll( + 'button[id*="continueButton"]', + )[1]; + + fireEvent.click(forwardButton); + + await waitFor(() => { + expect(container.textContent).to.contain('Enter the name'); + }); + }); + + it('displays selection error when query is valid but rep is null', async () => { + const { mockStore } = getProps(); + + const props = { + formData: { + 'view:representativeQuery': 'Valid Query', + 'view:selectedRepresentative': null, + }, + }; + + const container = renderContainer(props, mockStore); + + const forwardButton = container.querySelectorAll( + 'button[id*="continueButton"]', + )[1]; + fireEvent.click(forwardButton); + + await waitFor(() => { + expect(container.textContent).to.contain('Select the accredited'); + }); + }); + + it('goes back when not in review mode', async () => { + const { mockStore, props } = getProps(); + + const useReviewPageStub = sinon + .stub(reviewPageHook, 'useReviewPage') + .returns(false); + + const container = renderContainer(props, mockStore); + + const backButton = container.querySelectorAll( + 'button[id*="continueButton"]', + )[0]; + fireEvent.click(backButton); + + await waitFor(() => { + expect(props.goBack.called).to.be.true; + }); + + useReviewPageStub.restore(); + }); + + it('goes to path when in review mode', async () => { + const { mockStore, props } = getProps(); + + const useReviewPageStub = sinon + .stub(reviewPageHook, 'useReviewPage') + .returns(true); + + const container = renderContainer(props, mockStore); + + const backButton = container.querySelectorAll( + 'button[id*="continueButton"]', + )[0]; + fireEvent.click(backButton); + + await waitFor(() => { + expect(props.goToPath.called).to.be.true; + }); + + useReviewPageStub.restore(); + }); + + it('displays error on search when no query exists', async () => { + const { mockStore, props } = getProps(); + + const container = renderContainer(props, mockStore); + + const vaButton = container.querySelector('va-button[text="Search"]'); + + fireEvent.click(vaButton); + + await waitFor(() => { + expect(container.textContent).to.contain('Enter the name'); + }); }); }); diff --git a/src/applications/representative-appoint/tests/pages/representative/selectAccreditedRepresentativeReview.unit.spec.jsx b/src/applications/representative-appoint/tests/pages/representative/selectAccreditedRepresentativeReview.unit.spec.jsx index edfa8bb69c97..c79151de2e2a 100644 --- a/src/applications/representative-appoint/tests/pages/representative/selectAccreditedRepresentativeReview.unit.spec.jsx +++ b/src/applications/representative-appoint/tests/pages/representative/selectAccreditedRepresentativeReview.unit.spec.jsx @@ -4,6 +4,7 @@ import { expect } from 'chai'; import { render } from '@testing-library/react'; import SelectedAccreditedRepresentativeReview from '../../../components/SelectAccreditedRepresentativeReview'; import repResults from '../../fixtures/data/representative-results.json'; +import orgResult from '../../fixtures/data/organization-type.json'; describe('', () => { const getProps = () => { @@ -24,14 +25,49 @@ describe('', () => { }; }; - it('should render component', () => { - const { props, mockStore } = getProps(); - + const renderContainer = (props, mockStore) => { const { container } = render( , ); + + return container; + }; + + it('should render component', () => { + const { props, mockStore } = getProps(); + + const container = renderContainer(props, mockStore); + expect(container).to.exist; }); + + it('should display review-row when orgName is present', () => { + const { props, mockStore } = getProps(); + + const container = renderContainer(props, mockStore); + + const reviewRow = container.querySelector('.review-row'); + expect(reviewRow).to.not.be.null; + }); + + it('should render org for org type', () => { + const { mockStore } = getProps(); + + const props = { + data: { + 'view:selectedRepresentative': orgResult, + }, + }; + + const container = renderContainer(props, mockStore); + + const ddElement = Array.from(container.querySelectorAll('dd')).find( + dd => dd.textContent === 'Organization', + ); + + expect(ddElement).to.not.be.null; + expect(ddElement).to.have.text('Organization'); + }); }); diff --git a/src/applications/representative-appoint/tests/pages/representative/selectOrganization.unit.spec.jsx b/src/applications/representative-appoint/tests/pages/representative/selectOrganization.unit.spec.jsx index 3768cb7ee8ce..f91bb23b6f17 100644 --- a/src/applications/representative-appoint/tests/pages/representative/selectOrganization.unit.spec.jsx +++ b/src/applications/representative-appoint/tests/pages/representative/selectOrganization.unit.spec.jsx @@ -16,6 +16,9 @@ describe('', () => { }, formData: { 'view:selectedRepresentative': repResults[0].data }, setFormData, + goBack: sinon.spy(), + goForward: sinon.spy(), + goToPath: sinon.spy(), }, mockStore: { getState: () => ({ @@ -25,12 +28,20 @@ describe('', () => { }), subscribe: () => {}, dispatch: () => ({ - setFormData: () => {}, + setFormData, }), }, }; }; + const renderContainer = (props, mockStore) => { + return render( + + + , + ); + }; + it('should render component', () => { const { props, mockStore } = getProps(); @@ -42,147 +53,127 @@ describe('', () => { expect(container).to.exist; }); - it('should call goBack with formData when handleGoBack is triggered and isReviewPage is false', () => { - const goBackSpy = sinon.spy(); - const goToPathSpy = sinon.spy(); - - const { props, mockStore } = getProps({ setFormData: () => {} }); - props.goBack = goBackSpy; - props.goToPath = goToPathSpy; + context('non-review mode', () => { + it('should call goBack with formData when handleGoBack is triggered and isReviewPage is false', () => { + const { props, mockStore } = getProps({ setFormData: () => {} }); - const useReviewPageStub = sinon.stub(reviewPageHook, 'call').returns(false); + const useReviewPageStub = sinon + .stub(reviewPageHook, 'call') + .returns(false); - const { getByText } = render( - - - , - ); + const { getByText } = renderContainer(props, mockStore); - fireEvent.click(getByText('Back')); + fireEvent.click(getByText('Back')); - expect(goBackSpy.calledOnce).to.be.true; - expect(goBackSpy.calledWith(props.formData)).to.be.true; + expect(props.goBack.calledOnce).to.be.true; + expect(props.goBack.calledWith(props.formData)).to.be.true; - useReviewPageStub.restore(); - }); + useReviewPageStub.restore(); + }); - it('should call goToPath with the correct path when handleGoBack is triggered and isReviewPage is true', () => { - const goBackSpy = sinon.spy(); - const goToPathSpy = sinon.spy(); + it('should call goForward with formData when handleGoForward is triggered and isReviewPage is false', () => { + const { props, mockStore } = getProps(); - const useReviewPageStub = sinon - .stub(reviewPageHook, 'useReviewPage') - .returns(true); + const useReviewPageStub = sinon + .stub(reviewPageHook, 'useReviewPage') + .returns(false); - const { props, mockStore } = getProps({ setFormData: () => {} }); - props.goBack = goBackSpy; - props.goToPath = goToPathSpy; + props.formData.selectedAccreditedOrganizationId = '123'; - const { getByText } = render( - - - , - ); + const { getByText } = render( + + + , + ); - fireEvent.click(getByText('Back')); + fireEvent.click(getByText('Continue')); - expect(goBackSpy.called).to.be.false; - expect(goToPathSpy.calledOnce).to.be.true; - expect(goToPathSpy.calledWith('representative-contact?review=true')).to.be - .true; + expect(props.goForward.calledOnce).to.be.true; + expect(props.goForward.calledWith(props.formData)).to.be.true; + expect(props.goToPath.called).to.be.false; - useReviewPageStub.restore(); + useReviewPageStub.restore(); + }); }); - it('should call goForward with formData when handleGoForward is triggered and isReviewPage is false', () => { - const goForwardSpy = sinon.spy(); - const goToPathSpy = sinon.spy(); - - const { props, mockStore } = getProps(); - props.goForward = goForwardSpy; - props.goToPath = goToPathSpy; - - const useReviewPageStub = sinon - .stub(reviewPageHook, 'useReviewPage') - .returns(false); + context('review mode', () => { + beforeEach(function() { + Object.defineProperty(window, 'location', { + value: { search: '?review=true' }, + writable: true, + }); + }); - props.formData.selectedAccreditedOrganizationId = '123'; + it('should call goToPath with the correct path when handleGoBack is triggered and isReviewPage is true', () => { + const useReviewPageStub = sinon + .stub(reviewPageHook, 'useReviewPage') + .returns(true); - const { getByText } = render( - - - , - ); + const { props, mockStore } = getProps({ setFormData: () => {} }); - fireEvent.click(getByText('Continue')); + const { getByText } = renderContainer(props, mockStore); - expect(goForwardSpy.calledOnce).to.be.true; - expect(goForwardSpy.calledWith(props.formData)).to.be.true; - expect(goToPathSpy.called).to.be.false; + fireEvent.click(getByText('Back')); - useReviewPageStub.restore(); - }); - - it('should call goToPath with /representative-replace?review=true when handleGoForward is triggered, isReviewPage is true, and isReplacingRep is true', () => { - const goForwardSpy = sinon.spy(); - const goToPathSpy = sinon.spy(); + expect(props.goBack.called).to.be.false; + expect(props.goToPath.calledOnce).to.be.true; + expect(props.goToPath.calledWith('representative-contact?review=true')).to + .be.true; - const { props, mockStore } = getProps(); - props.goForward = goForwardSpy; - props.goToPath = goToPathSpy; + useReviewPageStub.restore(); + }); - const useReviewPageStub = sinon - .stub(reviewPageHook, 'useReviewPage') - .returns(true); + it('should call goToPath with /representative-replace?review=true when handleGoForward is triggered, isReviewPage is true, and isReplacingRep is true', () => { + const { props, mockStore } = getProps(); - props.formData['view:representativeStatus'] = { id: '123' }; - props.formData['view:selectedRepresentative'] = repResults[0].data; - props.formData.selectedAccreditedOrganizationId = '123'; + const useReviewPageStub = sinon + .stub(reviewPageHook, 'useReviewPage') + .returns(true); - const { getByText } = render( - - - , - ); + props.formData['view:representativeStatus'] = { id: '123' }; + props.formData['view:selectedRepresentative'] = repResults[0].data; + props.formData.selectedAccreditedOrganizationId = '123'; - fireEvent.click(getByText('Continue')); + const { getByText } = render( + + + , + ); - expect(goForwardSpy.called).to.be.false; - expect(goToPathSpy.calledOnce).to.be.true; - expect(goToPathSpy.calledWith('/representative-replace?review=true')).to.be - .true; + fireEvent.click(getByText('Continue')); - useReviewPageStub.restore(); - }); + expect(props.goForward.called).to.be.false; + expect(props.goToPath.calledOnce).to.be.true; + expect(props.goToPath.calledWith('/representative-replace?review=true')) + .to.be.true; - it('should call goToPath with /review-and-submit when handleGoForward is triggered, isReviewPage is true, and isReplacingRep is false', () => { - const goForwardSpy = sinon.spy(); - const goToPathSpy = sinon.spy(); + useReviewPageStub.restore(); + }); - const { props, mockStore } = getProps(); - props.goForward = goForwardSpy; - props.goToPath = goToPathSpy; + it('should call goToPath with /review-and-submit when handleGoForward is triggered, isReviewPage is true, and isReplacingRep is false', () => { + const { props, mockStore } = getProps(); - const useReviewPageStub = sinon - .stub(reviewPageHook, 'useReviewPage') - .returns(true); - props.formData['view:representativeStatus'] = null; - props.formData['view:selectedRepresentative'] = repResults[0].data; + const useReviewPageStub = sinon + .stub(reviewPageHook, 'useReviewPage') + .returns(true); + props.formData['view:representativeStatus'] = null; + props.formData['view:selectedRepresentative'] = repResults[0].data; - props.formData.selectedAccreditedOrganizationId = '123'; + props.formData.selectedAccreditedOrganizationId = '123'; - const { getByText } = render( - - - , - ); + const { getByText } = render( + + + , + ); - fireEvent.click(getByText('Continue')); + fireEvent.click(getByText('Continue')); - expect(goForwardSpy.called).to.be.false; - expect(goToPathSpy.calledOnce).to.be.true; - expect(goToPathSpy.calledWith('/review-and-submit')).to.be.true; + expect(props.goForward.called).to.be.false; + expect(props.goToPath.calledOnce).to.be.true; + expect(props.goToPath.calledWith('/review-and-submit')).to.be.true; - useReviewPageStub.restore(); + useReviewPageStub.restore(); + }); }); }); diff --git a/src/applications/representative-appoint/tests/utilities/parseRepType.unit.spec.jsx b/src/applications/representative-appoint/tests/utilities/parseRepType.unit.spec.jsx new file mode 100644 index 000000000000..f1a2cfda2242 --- /dev/null +++ b/src/applications/representative-appoint/tests/utilities/parseRepType.unit.spec.jsx @@ -0,0 +1,28 @@ +import { expect } from 'chai'; +import { parseRepType } from '../../utilities/helpers'; + +describe('parseRepType', () => { + it('should return correct title and subtitle for Organization', () => { + const result = parseRepType('Organization'); + expect(result.title).to.equal('Veterans Service Organization (VSO)'); + expect(result.subTitle).to.equal('Veteran Service Organization'); + }); + + it('should return correct title and subtitle for Attorney', () => { + const result = parseRepType('Attorney'); + expect(result.title).to.equal('accredited attorney'); + expect(result.subTitle).to.equal('Accredited attorney'); + }); + + it('should return correct title and subtitle for Claims Agent', () => { + const result = parseRepType('Claims Agent'); + expect(result.title).to.equal('accredited claims agent'); + expect(result.subTitle).to.equal('Accredited claims agent'); + }); + + it('should return default title and subtitle for unknown type', () => { + const result = parseRepType('Random Type'); + expect(result.title).to.equal('accredited representative'); + expect(result.subTitle).to.equal('Accredited representative'); + }); +}); diff --git a/src/applications/representative-appoint/utilities/helpers.js b/src/applications/representative-appoint/utilities/helpers.js index e9a2c80a0255..bd15ad999fbc 100644 --- a/src/applications/representative-appoint/utilities/helpers.js +++ b/src/applications/representative-appoint/utilities/helpers.js @@ -52,6 +52,30 @@ export const getFormSubtitle = formData => { return 'VA Forms 21-22 and 21-22a'; }; +export const parseRepType = repType => { + const parsedRep = {}; + + switch (repType) { + case 'Organization': + parsedRep.title = 'Veterans Service Organization (VSO)'; + parsedRep.subTitle = 'Veteran Service Organization'; + break; + case 'Attorney': + parsedRep.title = 'accredited attorney'; + parsedRep.subTitle = 'Accredited attorney'; + break; + case 'Claims Agent': + parsedRep.title = 'accredited claims agent'; + parsedRep.subTitle = 'Accredited claims agent'; + break; + default: + parsedRep.title = 'accredited representative'; + parsedRep.subTitle = 'Accredited representative'; + } + + return parsedRep; +}; + export const getEntityAddressAsObject = addressData => ({ addressLine1: (addressData?.addressLine1 || '').trim(), addressLine2: (addressData?.addressLine2 || '').trim(),