From ffa2265237d5d87ec89a60c29b161353251e37a0 Mon Sep 17 00:00:00 2001 From: Jerek Shoemaker Date: Thu, 16 Jan 2025 13:36:44 -0600 Subject: [PATCH 01/11] [CST] Remove cstUseLighthouse5103 code (#34080) * Removing cstUseLighthouse5103 code * Updating tests to mock the LH 5103 endpoint instead of the EVSS one --- .../claims-status/actions/index.js | 49 ---- .../Default5103EvidenceNotice.jsx | 23 +- .../claims-status/containers/AskVAPage.jsx | 30 +- .../claims-status/selectors/index.js | 29 -- .../tests/actions/index.unit.spec.js | 210 ------------- .../tests/components/AskVAPage.unit.spec.jsx | 49 +--- .../tests/components/ClaimPage.unit.spec.jsx | 4 + .../Default5103EvidenceNotice.unit.spec.jsx | 107 +------ ...laim-letters-keyboard-only.cypress.spec.js | 13 +- .../feature-toggle-5103-update-disabled.json | 15 + ...feature-toggle-5103-update-enabled-v2.json | 12 - .../feature-toggle-5103-update-enabled.json | 12 - ...eature-toggle-claim-detail-v2-enabled.json | 12 - .../feature-toggle-claim-phases-enabled.json | 12 - .../lighthouse/feature-toggle-enabled.json | 19 -- .../e2e/page-objects/TrackClaimsPageV2.js | 2 +- .../tests/selectors/index.unit.spec.js | 276 ------------------ 17 files changed, 46 insertions(+), 828 deletions(-) create mode 100644 src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-disabled.json delete mode 100644 src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-enabled.json diff --git a/src/applications/claims-status/actions/index.js b/src/applications/claims-status/actions/index.js index 884a2c4a15b0..587e33934a0a 100644 --- a/src/applications/claims-status/actions/index.js +++ b/src/applications/claims-status/actions/index.js @@ -225,55 +225,6 @@ export const getClaim = (id, navigate) => { export const clearClaim = () => ({ type: CLEAR_CLAIM_DETAIL }); -export function submitRequest(id, cstClaimPhasesEnabled = false) { - return dispatch => { - dispatch({ - type: SUBMIT_DECISION_REQUEST, - }); - - if (canUseMocks()) { - dispatch({ type: SET_DECISION_REQUESTED }); - dispatch( - setNotification({ - title: 'Request received', - body: - 'Thank you. We have your claim request and will make a decision.', - }), - ); - return Promise.resolve(); - } - - return makeAuthRequest( - `/v0/evss_claims/${id}/request_decision`, - { method: 'POST' }, - dispatch, - () => { - dispatch({ type: SET_DECISION_REQUESTED }); - if (cstClaimPhasesEnabled) { - dispatch( - setNotification({ - title: 'We received your evidence waiver', - body: - 'Thank you. We’ll move your claim to the next step as soon as possible.', - }), - ); - } else { - dispatch( - setNotification({ - title: 'Request received', - body: - 'Thank you. We have your claim request and will make a decision.', - }), - ); - } - }, - error => { - dispatch({ type: SET_DECISION_REQUEST_ERROR, error }); - }, - ); - }; -} - export function submit5103(id, trackedItemId, cstClaimPhasesEnabled = false) { return dispatch => { dispatch({ diff --git a/src/applications/claims-status/components/claim-document-request-pages/Default5103EvidenceNotice.jsx b/src/applications/claims-status/components/claim-document-request-pages/Default5103EvidenceNotice.jsx index 6749213f0fe6..9ca90e623dfe 100644 --- a/src/applications/claims-status/components/claim-document-request-pages/Default5103EvidenceNotice.jsx +++ b/src/applications/claims-status/components/claim-document-request-pages/Default5103EvidenceNotice.jsx @@ -15,12 +15,8 @@ import { import { // START ligthouse_migration submit5103 as submit5103Action, - submitRequest as submitRequestAction, // END lighthouse_migration } from '../../actions'; -// START lighthouse_migration -import { cstUseLighthouse } from '../../selectors'; -// END lighthouse_migration import { setUpPage } from '../../utils/page'; import withRouter from '../../utils/withRouter'; @@ -33,8 +29,6 @@ function Default5103EvidenceNotice({ navigate, params, submit5103, - submitRequest, - useLighthouse5103, }) { const [addedEvidence, setAddedEvidence] = useState(false); const [checkboxErrorMessage, setCheckboxErrorMessage] = useState(undefined); @@ -58,11 +52,7 @@ function Default5103EvidenceNotice({ const submit = () => { if (addedEvidence) { - if (useLighthouse5103) { - submit5103(params.id, params.trackedItemId, true); - } else { - submitRequest(params.id, true); - } + submit5103(params.id, params.trackedItemId, true); } else { setCheckboxErrorMessage( `You must confirm you’re done adding evidence before submitting the evidence waiver`, @@ -138,6 +128,7 @@ function Default5103EvidenceNotice({ review stage as quickly as possible.

+ {' '} Note: You can add evidence to support your claim at any time. However, if you add evidence later, your claim will move back to this step, so we encourage you to add all your evidence now. @@ -177,17 +168,11 @@ function mapStateToProps(state) { decisionRequested: claimsState.claimAsk.decisionRequested, decisionRequestError: claimsState.claimAsk.decisionRequestError, loadingDecisionRequest: claimsState.claimAsk.loadingDecisionRequest, - // START lighthouse_migration - useLighthouse5103: cstUseLighthouse(state, '5103'), - // END lighthouse_migration }; } const mapDispatchToProps = { - // START lighthouse_migration submit5103: submit5103Action, - submitRequest: submitRequestAction, - // END lighthouse_migration }; export default withRouter( @@ -204,11 +189,7 @@ Default5103EvidenceNotice.propTypes = { loadingDecisionRequest: PropTypes.bool, navigate: PropTypes.func, params: PropTypes.object, - // START lighthouse_migration submit5103: PropTypes.func, - submitRequest: PropTypes.func, - useLighthouse5103: PropTypes.bool, - // END lighthouse_migration }; export { Default5103EvidenceNotice }; diff --git a/src/applications/claims-status/containers/AskVAPage.jsx b/src/applications/claims-status/containers/AskVAPage.jsx index 9c08481576a0..4706761117e5 100644 --- a/src/applications/claims-status/containers/AskVAPage.jsx +++ b/src/applications/claims-status/containers/AskVAPage.jsx @@ -1,23 +1,18 @@ import React from 'react'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; + import { VaCheckbox } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; import { - // START ligthouse_migration - submit5103 as submit5103Action, - submitRequest as submitRequestAction, - // END lighthouse_migration getClaim as getClaimAction, + submit5103 as submit5103Action, } from '../actions'; -import NeedHelp from '../components/NeedHelp'; import ClaimsBreadcrumbs from '../components/ClaimsBreadcrumbs'; -// START lighthouse_migration -import { cstUseLighthouse } from '../selectors'; -// END lighthouse_migration +import NeedHelp from '../components/NeedHelp'; +import { setDocumentTitle } from '../utils/helpers'; import { setUpPage } from '../utils/page'; import withRouter from '../utils/withRouter'; -import { setDocumentTitle } from '../utils/helpers'; class AskVAPage extends React.Component { constructor() { @@ -54,11 +49,8 @@ class AskVAPage extends React.Component { decisionRequestError, params, submit5103, - submitRequest, - useLighthouse5103, } = this.props; - const submitFunc = useLighthouse5103 ? submit5103 : submitRequest; const submitDisabled = !this.state.submittedDocs || loadingDecisionRequest || @@ -127,7 +119,7 @@ class AskVAPage extends React.Component { submit class="button-primary vads-u-margin-top--1" text={buttonMsg} - onClick={() => submitFunc(params.id)} + onClick={() => submit5103(params.id)} /> {!loadingDecisionRequest ? ( toggleValues(state).loading; export const showClaimLettersFeature = state => toggleValues(state)[FEATURE_FLAG_NAMES.claimLettersAccess]; -// 'cst_use_lighthouse' -// endpoint - one of '5103', 'index', 'show' -export const cstUseLighthouse = (state, endpoint) => { - const flipperOverrideMode = sessionStorage.getItem('cstFlipperOverrideMode'); - if (flipperOverrideMode) { - switch (flipperOverrideMode) { - case 'featureToggle': - break; - case 'evss': - return false; - case 'lighthouse': - return true; - default: - break; - } - } - - // Returning true here because the feature toggle sometimes returns - // undefined and the feature toggle should always return true anyways - // Note: Checking for window.Cypress here because some of the Cypress - // tests are written for EVSS and will fail if this only returns true - - if (endpoint === 'show' && !window.Cypress) return true; - - return toggleValues(state)[ - FEATURE_FLAG_NAMES[`cstUseLighthouse#${endpoint}`] - ]; -}; - // 'cst_include_ddl_boa_letters' export const cstIncludeDdlBoaLetters = state => toggleValues(state)[FEATURE_FLAG_NAMES.cstIncludeDdlBoaLetters]; diff --git a/src/applications/claims-status/tests/actions/index.unit.spec.js b/src/applications/claims-status/tests/actions/index.unit.spec.js index 71ba09ab9f41..0a4180ea02ff 100644 --- a/src/applications/claims-status/tests/actions/index.unit.spec.js +++ b/src/applications/claims-status/tests/actions/index.unit.spec.js @@ -21,7 +21,6 @@ import { setFieldsDirty, setLastPage, setNotification, - submitRequest, updateField, submit5103, } from '../../actions'; @@ -445,215 +444,6 @@ describe('Actions', () => { global.window.dataLayer = oldDataLayer; }); }); - describe('submitRequest', () => { - context('when cstClaimPhasesEnabled is false', () => { - it('should submit request with canUseMocks true', done => { - const useMocksStub = sinon.stub(constants, 'canUseMocks').returns(true); - - const thunk = submitRequest(5); - const dispatch = sinon.spy(); - - thunk(dispatch) - .then(() => { - expect(dispatch.firstCall.args[0].type).to.equal( - SUBMIT_DECISION_REQUEST, - ); - expect(dispatch.secondCall.args[0].type).to.equal( - SET_DECISION_REQUESTED, - ); - }) - .then(() => useMocksStub.restore()) - .then(done, done); - }); - }); - - context('when cstClaimPhasesEnabled is true', () => { - it('should submit request with canUseMocks true', done => { - const useMocksStub = sinon.stub(constants, 'canUseMocks').returns(true); - - const thunk = submitRequest(5, true); - const dispatch = sinon.spy(); - - thunk(dispatch) - .then(() => { - expect(dispatch.firstCall.args[0].type).to.equal( - SUBMIT_DECISION_REQUEST, - ); - expect(dispatch.secondCall.args[0].type).to.equal( - SET_DECISION_REQUESTED, - ); - }) - .then(() => useMocksStub.restore()) - .then(done, done); - }); - }); - - context('', () => { - let expectedUrl; - const server = setupServer(); - - before(() => { - server.listen(); - }); - - beforeEach(() => { - server.events.on('request:start', req => { - expectedUrl = req.url.href; - }); - }); - - afterEach(() => { - server.resetHandlers(); - expectedUrl = undefined; - }); - - after(() => server.close()); - - context('when cstClaimPhasesEnabled is false', () => { - it('should submit request', done => { - const ID = 5; - server.use( - rest.post( - `https://dev-api.va.gov/v0/evss_claims/${ID}/request_decision`, - (req, res, ctx) => - res( - ctx.status(200), - ctx.json({ - // eslint-disable-next-line camelcase - job_id: ID, - }), - ), - ), - ); - - const thunk = submitRequest(ID); - const dispatchSpy = sinon.spy(); - const dispatch = action => { - dispatchSpy(action); - if (dispatchSpy.callCount === 3) { - expect(expectedUrl).to.contain('5/request_decision'); - expect(dispatchSpy.firstCall.args[0]).to.eql({ - type: SUBMIT_DECISION_REQUEST, - }); - expect(dispatchSpy.secondCall.args[0]).to.eql({ - type: SET_DECISION_REQUESTED, - }); - expect(dispatchSpy.thirdCall.args[0].type).to.eql( - SET_NOTIFICATION, - ); - expect(dispatchSpy.thirdCall.args[0].message.title).to.eql( - 'Request received', - ); - expect(dispatchSpy.thirdCall.args[0].message.body).to.eql( - 'Thank you. We have your claim request and will make a decision.', - ); - done(); - } - }; - - thunk(dispatch); - }); - it('should fail on error', done => { - const ID = 5; - server.use( - rest.post( - `https://dev-api.va.gov/v0/evss_claims/${ID}/request_decision`, - (req, res, ctx) => - res(ctx.status(400), ctx.json({ status: 400 })), - ), - ); - const thunk = submitRequest(ID); - const dispatchSpy = sinon.spy(); - const dispatch = action => { - dispatchSpy(action); - if (dispatchSpy.callCount === 2) { - expect(dispatchSpy.firstCall.args[0]).to.eql({ - type: SUBMIT_DECISION_REQUEST, - }); - expect(dispatchSpy.secondCall.args[0].type).to.eql( - SET_DECISION_REQUEST_ERROR, - ); - done(); - } - }; - - thunk(dispatch); - }); - }); - - context('when cstClaimPhasesEnabled is true', () => { - it('should submit request', done => { - const ID = 5; - server.use( - rest.post( - `https://dev-api.va.gov/v0/evss_claims/${ID}/request_decision`, - (req, res, ctx) => - res( - ctx.status(200), - ctx.json({ - // eslint-disable-next-line camelcase - job_id: ID, - }), - ), - ), - ); - - const thunk = submitRequest(ID, true); - const dispatchSpy = sinon.spy(); - const dispatch = action => { - dispatchSpy(action); - if (dispatchSpy.callCount === 3) { - expect(expectedUrl).to.contain('5/request_decision'); - expect(dispatchSpy.firstCall.args[0]).to.eql({ - type: SUBMIT_DECISION_REQUEST, - }); - expect(dispatchSpy.secondCall.args[0]).to.eql({ - type: SET_DECISION_REQUESTED, - }); - expect(dispatchSpy.thirdCall.args[0].type).to.eql( - SET_NOTIFICATION, - ); - expect(dispatchSpy.thirdCall.args[0].message.title).to.eql( - 'We received your evidence waiver', - ); - expect(dispatchSpy.thirdCall.args[0].message.body).to.eql( - 'Thank you. We’ll move your claim to the next step as soon as possible.', - ); - done(); - } - }; - - thunk(dispatch); - }); - it('should fail on error', done => { - const ID = 5; - server.use( - rest.post( - `https://dev-api.va.gov/v0/evss_claims/${ID}/request_decision`, - (req, res, ctx) => - res(ctx.status(400), ctx.json({ status: 400 })), - ), - ); - const thunk = submitRequest(ID); - const dispatchSpy = sinon.spy(); - const dispatch = action => { - dispatchSpy(action); - if (dispatchSpy.callCount === 2) { - expect(dispatchSpy.firstCall.args[0]).to.eql({ - type: SUBMIT_DECISION_REQUEST, - }); - expect(dispatchSpy.secondCall.args[0].type).to.eql( - SET_DECISION_REQUEST_ERROR, - ); - done(); - } - }; - - thunk(dispatch); - }); - }); - }); - }); describe('getStemClaims', () => { const server = setupServer(); diff --git a/src/applications/claims-status/tests/components/AskVAPage.unit.spec.jsx b/src/applications/claims-status/tests/components/AskVAPage.unit.spec.jsx index 4cc453f822e6..4e355e9a321b 100644 --- a/src/applications/claims-status/tests/components/AskVAPage.unit.spec.jsx +++ b/src/applications/claims-status/tests/components/AskVAPage.unit.spec.jsx @@ -37,13 +37,11 @@ describe('', () => { it('should render enabled submit button when va-checkbox checked', () => { const router = getRouter(); - const submitRequest = sinon.spy(); const { container, rerender } = renderWithRouter( , ); @@ -60,7 +58,6 @@ describe('', () => { , ); @@ -73,14 +70,12 @@ describe('', () => { const router = { push: sinon.spy(), }; - const submitRequest = sinon.spy(); const { container } = renderWithRouter( , ); @@ -93,15 +88,10 @@ describe('', () => { it('should update claims and redirect after success', () => { const navigate = sinon.spy(); - const submitRequest = sinon.spy(); const getClaim = sinon.spy(); const tree = SkinDeep.shallowRender( - , + , ); tree.getMountedInstance().UNSAFE_componentWillReceiveProps({ decisionRequested: true, @@ -111,8 +101,7 @@ describe('', () => { expect(navigate.calledWith('../status')).to.be.true; }); - // START lighthouse_migration - context('cst_use_lighthouse_5103 feature toggle', () => { + context('5103 Submission', () => { const params = { id: 1 }; const props = { @@ -121,38 +110,8 @@ describe('', () => { router: getRouter(), }; - it('calls submitRequest when disabled', () => { - props.submitRequest = sinon.spy(); - props.submit5103 = sinon.spy(); - props.useLighthouse5103 = false; - - const { container, rerender } = renderWithRouter( - - - , - ); - // Check the checkbox - $('va-checkbox', container).__events.vaChange({ - detail: { checked: true }, - }); - - rerenderWithRouter( - rerender, - - - , - ); - // Click submit button - fireEvent.click($('.button-primary', container)); - - expect(props.submitRequest.called).to.be.true; - expect(props.submit5103.called).to.be.false; - }); - - it('calls submit5103 when enabled', () => { - props.submitRequest = sinon.spy(); + it('calls submit5103 ', () => { props.submit5103 = sinon.spy(); - props.useLighthouse5103 = true; const { container, rerender } = renderWithRouter( @@ -173,9 +132,7 @@ describe('', () => { // Click submit button fireEvent.click($('.button-primary', container)); - expect(props.submitRequest.called).to.be.false; expect(props.submit5103.called).to.be.true; }); }); - // END lighthouse_migration }); diff --git a/src/applications/claims-status/tests/components/ClaimPage.unit.spec.jsx b/src/applications/claims-status/tests/components/ClaimPage.unit.spec.jsx index beec70264e4b..596d1862648b 100644 --- a/src/applications/claims-status/tests/components/ClaimPage.unit.spec.jsx +++ b/src/applications/claims-status/tests/components/ClaimPage.unit.spec.jsx @@ -8,6 +8,7 @@ import { renderWithRouter } from '../utils'; const params = { id: 1 }; const props = { + clearClaim: () => {}, params, }; @@ -24,13 +25,16 @@ describe('', () => { expect(props.getClaim.called).to.be.true; }); + it('calls clearClaim when it unmounts', () => { props.clearClaim = sinon.spy(); + const { unmount } = renderWithRouter(

, ); + unmount(); expect(props.clearClaim.called).to.be.true; }); diff --git a/src/applications/claims-status/tests/components/claim-document-request-pages/Default5103EvidenceNotice.unit.spec.jsx b/src/applications/claims-status/tests/components/claim-document-request-pages/Default5103EvidenceNotice.unit.spec.jsx index 9eafc5808a16..0fa68daab417 100644 --- a/src/applications/claims-status/tests/components/claim-document-request-pages/Default5103EvidenceNotice.unit.spec.jsx +++ b/src/applications/claims-status/tests/components/claim-document-request-pages/Default5103EvidenceNotice.unit.spec.jsx @@ -80,6 +80,7 @@ describe('', () => { ); expect($('#default-5103-notice-page', container)).to.not.exist; }); + it('link has the correct href to upload additional evidence', () => { const { getByText } = renderWithRouter( ', () => { ); }); - context('when useLighthouse5103 false', () => { - const props = { - item: automated5103, - params: { id: claimId }, - useLighthouse5103: false, - }; - - context('when checkbox is checked and submit button clicked', () => { - it('should submitRequest notice and redirect to files tab', () => { - const submitRequest = sinon.spy(); - const submit5103 = sinon.spy(); - const navigate = sinon.spy(); - - const { container, rerender } = renderWithRouter( - , - ); - - expect($('#default-5103-notice-page', container)).to.exist; - expect($('va-checkbox', container)).to.exist; - expect($('va-button', container)).to.exist; - - // Check the checkbox - $('va-checkbox', container).__events.vaChange({ - detail: { checked: true }, - }); - - rerenderWithRouter( - rerender, - , - ); - - // Click submit button - fireEvent.click($('#submit', container)); - - expect(submitRequest.called).to.be.true; - expect(submit5103.called).to.be.false; - expect(navigate.calledWith('../files')).to.be.true; - }); - }); - context('when checkbox is not checked and submit button clicked', () => { - it('should not submit 5103 notice and error message displayed', () => { - const submitRequest = sinon.spy(); - const submit5103 = sinon.spy(); - const navigate = sinon.spy(); - - const { container } = renderWithRouter( - , - ); - expect($('#default-5103-notice-page', container)).to.exist; - expect($('va-checkbox', container)).to.exist; - expect($('va-button', container)).to.exist; - expect($('va-checkbox', container).getAttribute('error')).to.be.null; - - // Click submit button - fireEvent.click($('#submit', container)); - - expect($('va-checkbox', container).getAttribute('checked')).to.equal( - 'false', - ); - expect($('va-checkbox', container).getAttribute('required')).to.equal( - 'true', - ); - expect(submitRequest.called).to.be.false; - expect(submit5103.called).to.be.false; - expect(navigate.calledWith('../files')).to.be.false; - expect($('va-checkbox', container).getAttribute('error')).to.equal( - 'You must confirm you’re done adding evidence before submitting the evidence waiver', - ); - }); - }); - }); - - context('when useLighthouse5103 true', () => { + context('submit5103', () => { const props = { item: automated5103, params: { id: claimId }, @@ -194,15 +104,13 @@ describe('', () => { context('when checkbox is checked and submit button clicked', () => { it('should submit5103 notice and redirect to files tab', () => { - const submitRequest = sinon.spy(); - const submit5103 = sinon.spy(); const navigate = sinon.spy(); + const submit5103 = sinon.spy(); const { container, rerender } = renderWithRouter( ', () => { ', () => { // Click submit button fireEvent.click($('#submit', container)); - expect(submitRequest.called).to.be.false; expect(submit5103.called).to.be.true; expect(navigate.calledWith('../files')).to.be.true; }); }); + context('when checkbox is not checked and submit button clicked', () => { it('should not submit 5103 notice and error message displayed', () => { - const submitRequest = sinon.spy(); - const submit5103 = sinon.spy(); const navigate = sinon.spy(); + const submit5103 = sinon.spy(); const { container } = renderWithRouter( , ); + expect($('#default-5103-notice-page', container)).to.exist; expect($('va-checkbox', container)).to.exist; expect($('va-button', container)).to.exist; @@ -262,7 +168,6 @@ describe('', () => { expect($('va-checkbox', container).getAttribute('checked')).to.equal( 'false', ); - expect(submitRequest.called).to.be.false; expect(submit5103.called).to.be.false; expect(navigate.calledWith('../files')).to.be.false; expect($('va-checkbox', container).getAttribute('error')).to.equal( diff --git a/src/applications/claims-status/tests/e2e/08.claim-letters-keyboard-only.cypress.spec.js b/src/applications/claims-status/tests/e2e/08.claim-letters-keyboard-only.cypress.spec.js index da5bca2cb30b..10e6d06fd94d 100644 --- a/src/applications/claims-status/tests/e2e/08.claim-letters-keyboard-only.cypress.spec.js +++ b/src/applications/claims-status/tests/e2e/08.claim-letters-keyboard-only.cypress.spec.js @@ -1,4 +1,3 @@ -import featureToggleEnabled from './fixtures/mocks/claim-letters/feature-toggle-enabled.json'; import claimLetters from './fixtures/mocks/claim-letters/list.json'; describe('Claim Letters Page', () => { @@ -6,9 +5,15 @@ describe('Claim Letters Page', () => { cy.intercept('GET', '/v0/claim_letters', claimLetters.data).as( 'claimLetters', ); - cy.intercept('GET', '/v0/feature_toggles?*', featureToggleEnabled).as( - 'featureToggleEnabled', - ); + + cy.intercept('GET', '/v0/feature_toggles*', { + data: { + features: [ + { name: 'cst_include_ddl_boa_letters', value: true }, + { name: 'claim_letters_access', value: true }, + ], + }, + }); cy.login(); }); diff --git a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-disabled.json b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-disabled.json new file mode 100644 index 000000000000..3637dcd01648 --- /dev/null +++ b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-disabled.json @@ -0,0 +1,15 @@ +{ + "data": { + "type": "feature_toggles", + "features": [ + { + "name": "cst_claim_phases", + "value": true + }, + { + "name": "cst_5103_update_enabled", + "value": false + } + ] + } +} diff --git a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-enabled-v2.json b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-enabled-v2.json index 2f7f386eb1b2..46e07b419285 100644 --- a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-enabled-v2.json +++ b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-enabled-v2.json @@ -2,18 +2,6 @@ "data": { "type": "feature_toggles", "features": [ - { - "name": "cst_use_lighthouse_5103", - "value": false - }, - { - "name": "cst_use_lighthouse_index", - "value": true - }, - { - "name": "cst_use_lighthouse_show", - "value": true - }, { "name": "cst_claim_phases", "value": true diff --git a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-enabled.json b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-enabled.json index 2f7f386eb1b2..46e07b419285 100644 --- a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-enabled.json +++ b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-5103-update-enabled.json @@ -2,18 +2,6 @@ "data": { "type": "feature_toggles", "features": [ - { - "name": "cst_use_lighthouse_5103", - "value": false - }, - { - "name": "cst_use_lighthouse_index", - "value": true - }, - { - "name": "cst_use_lighthouse_show", - "value": true - }, { "name": "cst_claim_phases", "value": true diff --git a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-claim-detail-v2-enabled.json b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-claim-detail-v2-enabled.json index 35c474035f0c..11a3a0ef5ab9 100644 --- a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-claim-detail-v2-enabled.json +++ b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-claim-detail-v2-enabled.json @@ -2,18 +2,6 @@ "data": { "type": "feature_toggles", "features": [ - { - "name": "cst_use_lighthouse_5103", - "value": false - }, - { - "name": "cst_use_lighthouse_index", - "value": true - }, - { - "name": "cst_use_lighthouse_show", - "value": true - }, { "name": "cst_use_claim_details_v2", "value": true diff --git a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-claim-phases-enabled.json b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-claim-phases-enabled.json index ee2d10685ff2..85b6e0fca419 100644 --- a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-claim-phases-enabled.json +++ b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-claim-phases-enabled.json @@ -2,18 +2,6 @@ "data": { "type": "feature_toggles", "features": [ - { - "name": "cst_use_lighthouse_5103", - "value": false - }, - { - "name": "cst_use_lighthouse_index", - "value": true - }, - { - "name": "cst_use_lighthouse_show", - "value": true - }, { "name": "cst_use_claim_details_v2", "value": true diff --git a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-enabled.json b/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-enabled.json deleted file mode 100644 index 5e1858427082..000000000000 --- a/src/applications/claims-status/tests/e2e/fixtures/mocks/lighthouse/feature-toggle-enabled.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "data": { - "type": "feature_toggles", - "features": [ - { - "name": "cst_use_lighthouse_5103", - "value": true - }, - { - "name": "cst_use_lighthouse_index", - "value": true - }, - { - "name": "cst_use_lighthouse_show", - "value": true - } - ] - } -} diff --git a/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPageV2.js b/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPageV2.js index 5e85eafa7277..96d14c3f9c51 100644 --- a/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPageV2.js +++ b/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPageV2.js @@ -19,7 +19,7 @@ class TrackClaimsPageV2 { cst5103UpdateEnabledV2 = false, ) { if (submitForm) { - cy.intercept('POST', `/v0/evss_claims/189685/request_decision`, { + cy.intercept('POST', `/v0/benefits_claims/189685/submit5103`, { body: {}, }).as('askVA'); } diff --git a/src/applications/claims-status/tests/selectors/index.unit.spec.js b/src/applications/claims-status/tests/selectors/index.unit.spec.js index 6276ee452d7e..cc7c3a20193d 100644 --- a/src/applications/claims-status/tests/selectors/index.unit.spec.js +++ b/src/applications/claims-status/tests/selectors/index.unit.spec.js @@ -1,6 +1,5 @@ import { expect } from 'chai'; -import FEATURE_FLAG_NAMES from '@department-of-veterans-affairs/platform-utilities/featureFlagNames'; import backendServices from '@department-of-veterans-affairs/platform-user/profile/backendServices'; import * as selectors from '../../selectors'; @@ -46,281 +45,6 @@ describe('selectors', () => { }); }); - describe('cstUseLighthouse', () => { - context('when endpoint is show', () => { - const endpoint = 'show'; - const cstUseLighthouseShow = - FEATURE_FLAG_NAMES[`cstUseLighthouse#${endpoint}`]; - context('when featureToggles are true', () => { - const state = { - featureToggles: { - [cstUseLighthouseShow]: true, - // eslint-disable-next-line camelcase - cst_use_lighthouse_5103: true, - }, - }; - context('when cstFlipperOverrideMode is set to featureToggle', () => { - it('should return true when window.cypress false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'featureToggle'); - window.Cypress = false; - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - - it('should return true when window.cypress true', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'featureToggle'); - window.Cypress = true; - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - - context('when cstFlipperOverrideMode is set to evss', () => { - it('should return false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'evss'); - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - - context('when cstFlipperOverrideMode is set to lighthouse', () => { - it('should return true', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'lighthouse'); - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - - context('when cstFlipperOverrideMode is null', () => { - it('should return true when window.cypress false', () => { - // sessionStorage.setItem('cstFlipperOverrideMode', ''); - window.Cypress = false; - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - - it('should return true when window.cypress true', () => { - // sessionStorage.setItem('cstFlipperOverrideMode', ''); - window.Cypress = true; - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - }); - - context('when featureToggles are false', () => { - const state = { - featureToggles: { - [cstUseLighthouseShow]: false, - // eslint-disable-next-line camelcase - cst_use_lighthouse_5103: false, - }, - }; - context('when cstFlipperOverrideMode is set to featureToggle', () => { - it('should return true when window.cypress false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'featureToggle'); - window.Cypress = false; - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - - it('should return true when window.cypress true', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'featureToggle'); - window.Cypress = true; - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - - context('when cstFlipperOverrideMode is set to evss', () => { - it('should return false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'evss'); - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - - context('when cstFlipperOverrideMode is set to lighthouse', () => { - it('should return true', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'lighthouse'); - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - - context('when cstFlipperOverrideMode is null', () => { - it('should return true when window.cypress false', () => { - // sessionStorage.setItem('cstFlipperOverrideMode', ''); - window.Cypress = false; - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - - it('should return true when window.cypress true', () => { - // sessionStorage.setItem('cstFlipperOverrideMode', ''); - window.Cypress = true; - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - }); - }); - - context('when endpoint is index', () => { - const endpoint = 'index'; - const cstUseLighthouseShow = - FEATURE_FLAG_NAMES[`cstUseLighthouse#${endpoint}`]; - context('when featureToggles are true', () => { - const state = { - featureToggles: { - [cstUseLighthouseShow]: true, - // eslint-disable-next-line camelcase - cst_use_lighthouse_5103: true, - }, - }; - context('when cstFlipperOverrideMode is set to featureToggle', () => { - it('should return true ', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'featureToggle'); - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - - context('when cstFlipperOverrideMode is set to evss', () => { - it('should return false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'evss'); - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - - context('when cstFlipperOverrideMode is set to lighthouse', () => { - it('should return true', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'lighthouse'); - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - - context('when cstFlipperOverrideMode is null', () => { - it('should return true', () => { - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - }); - - context('when featureToggles are false', () => { - const state = { - featureToggles: { - [cstUseLighthouseShow]: false, - // eslint-disable-next-line camelcase - cst_use_lighthouse_5103: false, - }, - }; - context('when cstFlipperOverrideMode is set to featureToggle', () => { - it('should return false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'featureToggle'); - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - - context('when cstFlipperOverrideMode is set to evss', () => { - it('should return false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'evss'); - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - - context('when cstFlipperOverrideMode is set to lighthouse', () => { - it('should return true', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'lighthouse'); - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - - context('when cstFlipperOverrideMode is null', () => { - it('should return false', () => { - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - }); - }); - - context('when endpoint is 5103', () => { - const endpoint = '5103'; - const cstUseLighthouseShow = - FEATURE_FLAG_NAMES[`cstUseLighthouse#${endpoint}`]; - context('when featureToggles are true', () => { - const state = { - featureToggles: { - [cstUseLighthouseShow]: true, - // eslint-disable-next-line camelcase - cst_use_lighthouse_5103: true, - }, - }; - context('when cstFlipperOverrideMode is set to featureToggle', () => { - it('should return true ', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'featureToggle'); - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - - context('when cstFlipperOverrideMode is set to evss', () => { - it('should return false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'evss'); - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - - context('when cstFlipperOverrideMode is set to lighthouse', () => { - it('should return true', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'lighthouse'); - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - - context('when cstFlipperOverrideMode is null', () => { - it('should return true', () => { - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - }); - - context('when featureToggles are false', () => { - const state = { - featureToggles: { - [cstUseLighthouseShow]: false, - // eslint-disable-next-line camelcase - cst_use_lighthouse_5103: false, - }, - }; - context('when cstFlipperOverrideMode is set to featureToggle', () => { - it('should return false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'featureToggle'); - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - - context('when cstFlipperOverrideMode is set to evss', () => { - it('should return false', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'evss'); - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - - context('when cstFlipperOverrideMode is set to lighthouse', () => { - it('should return true', () => { - sessionStorage.setItem('cstFlipperOverrideMode', 'lighthouse'); - - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.true; - }); - }); - - context('when cstFlipperOverrideMode is null', () => { - it('should return false', () => { - expect(selectors.cstUseLighthouse(state, endpoint)).to.be.false; - }); - }); - }); - }); - }); - describe('cstIncludeDdlBoaLetters', () => { const state = { featureToggles: { From dc613b26cfe9b1f4aafad079350cffee9a0b47ee Mon Sep 17 00:00:00 2001 From: Kyle Henson <145150351+khenson-oddball@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:58:55 -0700 Subject: [PATCH 02/11] Add veteran status confirmation integration (#34067) --- .../ProofOfVeteranStatus.jsx | 320 +++++++++++++----- .../ProofOfVeteranStatusNew.jsx | 280 +++++++++++---- .../ProofOfVeteranStatusNew.unit.spec.jsx | 129 ++++++- .../vet-verification-status/index.js | 29 ++ .../personalization/profile/mocks/server.js | 6 + .../MilitaryInformation.unit.spec.jsx | 23 +- .../PeriodOfServiceTypeText.unit.spec.jsx | 12 + .../proof-of-veteran-status.cypress.spec.js | 6 + 8 files changed, 660 insertions(+), 145 deletions(-) create mode 100644 src/applications/personalization/profile/mocks/endpoints/vet-verification-status/index.js diff --git a/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatus.jsx b/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatus.jsx index e5424c1625e0..0fe58bade12b 100644 --- a/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatus.jsx +++ b/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatus.jsx @@ -6,6 +6,8 @@ import { generatePdf } from '~/platform/pdf'; import { focusElement } from '~/platform/utilities/ui'; import { captureError } from '~/platform/user/profile/vap-svc/util/analytics'; import { CONTACTS } from '@department-of-veterans-affairs/component-library/contacts'; +import { useFeatureToggle } from '~/platform/utilities/feature-toggles'; +import { apiRequest } from '~/platform/utilities/api'; import { formatFullName } from '../../../common/helpers'; import { getServiceBranchDisplayName } from '../../helpers'; @@ -23,6 +25,8 @@ const ProofOfVeteranStatus = ({ mockUserAgent, }) => { const [errors, setErrors] = useState([]); + const [data, setData] = useState(null); + const [shouldFocusError, setShouldFocusError] = useState(false); const { first, middle, last, suffix } = userFullName; const userAgent = @@ -69,13 +73,45 @@ const ProofOfVeteranStatus = ({ }, }; + const { TOGGLE_NAMES, useToggleValue } = useFeatureToggle(); + const useLighthouseApi = useToggleValue( + TOGGLE_NAMES.veteranStatusCardUseLighthouseFrontend, + ); + + useEffect(() => { + let isMounted = true; + + const fetchVerificationStatus = async () => { + try { + const path = '/profile/vet_verification_status'; + const response = await apiRequest(path); + if (isMounted) { + setData(response.data); + } + } catch (error) { + if (isMounted) { + setErrors([ + "We're sorry. There's a problem with our system. We can't show your Veteran status card right now. Try again later.", + ]); + captureError(error, { eventName: 'vet-status-fetch-verification' }); + } + } + }; + fetchVerificationStatus(); + + return () => { + isMounted = false; + }; + }, []); + useEffect( () => { - if (errors?.length > 0) { + if (shouldFocusError && errors?.length > 0) { focusElement('.vet-status-pdf-download-error'); + setShouldFocusError(false); } }, - [errors], + [shouldFocusError, errors], ); const createPdf = async () => { @@ -93,6 +129,7 @@ const ProofOfVeteranStatus = ({ "We're sorry. Something went wrong on our end. Please try to download your Veteran status card later.", ]); captureError(error, { eventName: 'vet-status-pdf-download' }); + setShouldFocusError(true); } }; @@ -123,93 +160,216 @@ const ProofOfVeteranStatus = ({ ); }); + const contactInfoElements = data?.attributes?.message?.map(item => { + const contactNumber = `${CONTACTS.DS_LOGON.slice( + 0, + 3, + )}-${CONTACTS.DS_LOGON.slice(3, 6)}-${CONTACTS.DS_LOGON.slice(6)}`; + const startIndex = item.indexOf(contactNumber); + + if (startIndex === -1) { + return item; + } + + const before = item.slice(0, startIndex); + const telephone = item.slice( + startIndex, + startIndex + contactNumber.length + 11, + ); + const after = item.slice(startIndex + telephone.length); + + return ( + <> + {before} + ( + ){after} + + ); + }); + return ( <> -
-

- Proof of Veteran status -

-

- You can use your Veteran status card to get discounts offered to - Veterans at many restaurants, hotels, stores, and other businesses. -

-

- Note: - This card doesn’t entitle you to any VA benefits. -

- - {vetStatusEligibility.confirmed ? ( - <> -
- -
+ {useLighthouseApi ? ( +
+

+ Proof of Veteran status +

+

+ You can use your Veteran status card to get discounts offered to + Veterans at many restaurants, hotels, stores, and other businesses. +

+

+ Note: + This card doesn’t entitle you to any VA benefits. +

- {errors?.length > 0 ? ( -
- - {errors[0]} - + {data?.attributes?.veteranStatus === 'confirmed' ? ( + <> +
+
- ) : null} - -
-
-
- sample proof of veteran status card featuring name, date of birth, disability rating and period of service + + {errors?.length > 0 ? ( +
+ + {errors[0]} + +
+ ) : null} + +
+
+
+ sample proof of veteran status card featuring name, date of birth, disability rating and period of service +
-
-
- - You can use our mobile app to get proof of Veteran status. - To get started, download the{' '} - VA: Health and Benefits mobile app. - - } - /> -
- - ) : null} - - {!vetStatusEligibility.confirmed && - vetStatusEligibility.message.length > 0 ? ( - <> -
- - {componentizedMessage.map((message, i) => { - if (i === 0) { - return ( -

- {message} -

- ); +
+ + You can use our mobile app to get proof of Veteran status. + To get started, download the{' '} + VA: Health and Benefits mobile app. + } - return

{message}

; - })} + /> +
+ + ) : null} + + {data?.attributes?.veteranStatus !== 'confirmed' && + data?.attributes?.message.length > 0 ? ( + <> +
+ + {contactInfoElements.map((message, i) => { + if (i === 0) { + return ( +

+ {message} +

+ ); + } + return

{message}

; + })} +
+
+ + ) : null} + + {errors?.length > 0 ? ( +
+ + {errors[0]}
- - ) : null} -
+ ) : null} +
+ ) : ( +
+

+ Proof of Veteran status +

+

+ You can use your Veteran status card to get discounts offered to + Veterans at many restaurants, hotels, stores, and other businesses. +

+

+ Note: + This card doesn’t entitle you to any VA benefits. +

+ + {vetStatusEligibility.confirmed ? ( + <> +
+ +
+ + {errors?.length > 0 ? ( +
+ + {errors[0]} + +
+ ) : null} + +
+
+
+ sample proof of veteran status card featuring name, date of birth, disability rating and period of service +
+
+
+
+ + You can use our mobile app to get proof of Veteran status. + To get started, download the{' '} + VA: Health and Benefits mobile app. + + } + /> +
+ + ) : null} + + {!vetStatusEligibility.confirmed && + vetStatusEligibility.message.length > 0 ? ( + <> +
+ + {componentizedMessage.map((message, i) => { + if (i === 0) { + return ( +

+ {message} +

+ ); + } + return

{message}

; + })} +
+
+ + ) : null} +
+ )} ); }; diff --git a/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatusNew.jsx b/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatusNew.jsx index 81df2cad97f7..d2533e9c4ba9 100644 --- a/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatusNew.jsx +++ b/src/applications/personalization/profile/components/proof-of-veteran-status/ProofOfVeteranStatusNew.jsx @@ -6,6 +6,8 @@ import { generatePdf } from '~/platform/pdf'; import { focusElement } from '~/platform/utilities/ui'; import { captureError } from '~/platform/user/profile/vap-svc/util/analytics'; import { CONTACTS } from '@department-of-veterans-affairs/component-library/contacts'; +import { useFeatureToggle } from '~/platform/utilities/feature-toggles'; +import { apiRequest } from '~/platform/utilities/api'; import { formatFullName } from '../../../common/helpers'; import { getServiceBranchDisplayName } from '../../helpers'; import ProofOfVeteranStatusCard from './ProofOfVeteranStatusCard/ProofOfVeteranStatusCard'; @@ -24,6 +26,8 @@ const ProofOfVeteranStatusNew = ({ mockUserAgent, }) => { const [errors, setErrors] = useState([]); + const [data, setData] = useState(null); + const [shouldFocusError, setShouldFocusError] = useState(false); const { first, middle, last, suffix } = userFullName; const userAgent = @@ -59,6 +63,8 @@ const ProofOfVeteranStatusNew = ({ serviceHistory.length && formattedFullName ); + const hasConfirmationData = !!(data && data.attributes); + const pdfData = { title: `Veteran status card for ${formattedFullName}`, details: { @@ -86,13 +92,45 @@ const ProofOfVeteranStatusNew = ({ }, }; + const { TOGGLE_NAMES, useToggleValue } = useFeatureToggle(); + const useLighthouseApi = useToggleValue( + TOGGLE_NAMES.veteranStatusCardUseLighthouseFrontend, + ); + + useEffect(() => { + let isMounted = true; + + const fetchVerificationStatus = async () => { + try { + const path = '/profile/vet_verification_status'; + const response = await apiRequest(path); + if (isMounted) { + setData(response.data); + } + } catch (error) { + if (isMounted) { + setErrors([ + "We're sorry. There's a problem with our system. We can't show your Veteran status card right now. Try again later.", + ]); + captureError(error, { eventName: 'vet-status-fetch-verification' }); + } + } + }; + fetchVerificationStatus(); + + return () => { + isMounted = false; + }; + }, []); + useEffect( () => { - if (errors?.length > 0) { + if (shouldFocusError && errors?.length > 0) { focusElement('.vet-status-pdf-download-error'); + setShouldFocusError(false); } }, - [errors], + [shouldFocusError, errors], ); const createPdf = async () => { @@ -140,6 +178,33 @@ const ProofOfVeteranStatusNew = ({ ); }); + const contactInfoElements = data?.attributes?.message?.map(item => { + const contactNumber = `${CONTACTS.DS_LOGON.slice( + 0, + 3, + )}-${CONTACTS.DS_LOGON.slice(3, 6)}-${CONTACTS.DS_LOGON.slice(6)}`; + const startIndex = item.indexOf(contactNumber); + + if (startIndex === -1) { + return item; + } + + const before = item.slice(0, startIndex); + const telephone = item.slice( + startIndex, + startIndex + contactNumber.length + 11, + ); + const after = item.slice(startIndex + telephone.length); + + return ( + <> + {before} + ( + ){after} + + ); + }); + return ( <>
@@ -152,74 +217,165 @@ const ProofOfVeteranStatusNew = ({ {userHasRequiredCardData ? ( <> - {vetStatusEligibility.confirmed ? ( + {!useLighthouseApi ? ( <> - {errors?.length > 0 ? ( -
- - {errors[0]} - -
+ {vetStatusEligibility.confirmed ? ( + <> + {errors?.length > 0 ? ( +
+ + {errors[0]} + +
+ ) : null} +
+
+ +
+
+
+ +
+
+ + You can use our mobile app to get proof of Veteran + status. To get started, download the{' '} + VA: Health and Benefits mobile + app. + + } + /> +
+ + ) : null} + {!vetStatusEligibility.confirmed && + vetStatusEligibility.message.length > 0 ? ( + <> +
+ + {componentizedMessage.map((message, i) => { + if (i === 0) { + return ( +

+ {message} +

+ ); + } + return

{message}

; + })} +
+
+ ) : null} -
-
- -
-
-
- -
-
- - You can use our mobile app to get proof of Veteran - status. To get started, download the{' '} - VA: Health and Benefits mobile app. - - } - /> -
) : null} - {!vetStatusEligibility.confirmed && - vetStatusEligibility.message.length > 0 ? ( + {useLighthouseApi && hasConfirmationData ? ( <> -
- - {componentizedMessage.map((message, i) => { - if (i === 0) { - return ( -

- {message} -

- ); - } - return

{message}

; - })} -
-
+ {data?.attributes?.veteranStatus === 'confirmed' ? ( + <> + {errors?.length > 0 ? ( +
+ + {errors[0]} + +
+ ) : null} +
+
+ +
+
+
+ +
+
+ + You can use our mobile app to get proof of Veteran + status. To get started, download the{' '} + VA: Health and Benefits mobile + app. + + } + /> +
+ + ) : null} + + {data?.attributes?.veteranStatus !== 'confirmed' && + data?.attributes?.message.length > 0 ? ( + <> +
+ + {contactInfoElements.map((message, i) => { + if (i === 0) { + return ( +

+ {message} +

+ ); + } + return

{message}

; + })} +
+
+ + ) : null} ) : null} + + {useLighthouseApi && !hasConfirmationData ? ( + +

+ We’re sorry. There’s a problem with our system. We can’t show + your Veteran status card right now. Try again later. +

+
+ ) : null} ) : ( { }); }); + describe('should fetch verification status on render', () => { + let apiRequestStub; + const initialState = createBasicInitialState( + [eligibleServiceHistoryItem], + confirmedEligibility, + true, + ); + + beforeEach(() => { + apiRequestStub = sinon.stub(api, 'apiRequest'); + }); + + afterEach(() => { + apiRequestStub.restore(); + }); + + it('displays the card successfully', async () => { + const mockData = { + data: { + id: '', + type: 'veteran_status_confirmations', + attributes: { veteranStatus: 'confirmed' }, + }, + }; + + apiRequestStub.resolves(mockData); + + const view = renderWithProfileReducers(, { + initialState, + }); + + sinon.assert.calledWith( + apiRequestStub, + '/profile/vet_verification_status', + ); + await waitFor(() => { + expect( + view.queryByText( + /Get proof of Veteran status on your mobile device/i, + ), + ).to.exist; + expect( + view.queryByText( + /We’re sorry. There’s a problem with your discharge status records. We can’t provide a Veteran status card for you right now./, + ), + ).to.not.exist; + }); + }); + + it('displays the returned not confirmed message', async () => { + const mockData = { + data: { + id: '', + type: 'veteran_status_confirmations', + attributes: { + veteranStatus: 'not confirmed', + notConfirmedReason: 'PERSON_NOT_FOUND', + message: problematicEligibility.message, + }, + }, + }; + + apiRequestStub.resolves(mockData); + const view = renderWithProfileReducers(, { + initialState, + }); + + await waitFor(() => { + expect( + view.queryByText( + /Get proof of Veteran Status on your mobile device/i, + ), + ).to.not.exist; + expect( + view.queryByText( + /We’re sorry. There’s a problem with your discharge status records. We can’t provide a Veteran status card for you right now./, + ), + ).to.exist; + }); + }); + + it('handles empty API response', async () => { + const mockData = { + data: {}, + }; + apiRequestStub.resolves(mockData); + const view = renderWithProfileReducers(, { + initialState, + }); + + await waitFor(() => { + expect( + view.queryByText( + 'We’re sorry. There’s a problem with our system. We can’t show your Veteran status card right now. Try again later.', + ), + ).to.exist; + }); + }); + + it('handles API error', async () => { + apiRequestStub.rejects(new Error('API Error')); + const view = renderWithProfileReducers(, { + initialState, + }); + + await waitFor(() => { + expect( + view.getByText( + 'We’re sorry. There’s a problem with our system. We can’t show your Veteran status card right now. Try again later.', + ), + ).to.exist; + }); + }); + }); + describe('when eligible', () => { const initialState = createBasicInitialState( [ diff --git a/src/applications/personalization/profile/mocks/endpoints/vet-verification-status/index.js b/src/applications/personalization/profile/mocks/endpoints/vet-verification-status/index.js new file mode 100644 index 000000000000..ba62fadf4807 --- /dev/null +++ b/src/applications/personalization/profile/mocks/endpoints/vet-verification-status/index.js @@ -0,0 +1,29 @@ +const confirmed = { + data: { + id: '', + type: 'veteran_status_confirmations', + attributes: { + veteranStatus: 'confirmed', + }, + }, +}; + +const notConfirmed = { + data: { + id: null, + type: 'veteran_status_confirmations', + attributes: { + veteranStatus: 'not confirmed', + notConfirmedReason: 'PERSON_NOT_FOUND', + message: [ + 'We’re sorry. There’s a problem with your discharge status records. We can’t provide a Veteran status card for you right now.', + 'To fix the problem with your records, call the Defense Manpower Data Center at 800-538-9552 (TTY: 711). They’re open Monday through Friday, 8:00 a.m. to 8:00 p.m. ET.', + ], + }, + }, +}; + +module.exports = { + confirmed, + notConfirmed, +}; diff --git a/src/applications/personalization/profile/mocks/server.js b/src/applications/personalization/profile/mocks/server.js index 9177f471ca1d..fcb081e41bc3 100644 --- a/src/applications/personalization/profile/mocks/server.js +++ b/src/applications/personalization/profile/mocks/server.js @@ -22,6 +22,7 @@ const mockDisabilityCompensations = require('./endpoints/disability-compensation const directDeposits = require('./endpoints/direct-deposits'); const bankAccounts = require('./endpoints/bank-accounts'); const serviceHistory = require('./endpoints/service-history'); +const vetVerificationStatus = require('./endpoints/vet-verification-status'); const fullName = require('./endpoints/full-name'); const { baseUserTransitionAvailabilities, @@ -107,6 +108,7 @@ const responses = { profileShowPrivacyPolicy: true, veteranOnboardingContactInfoFlow: true, veteranStatusCardUseLighthouse: true, + veteranStatusCardUseLighthouseFrontend: true, }), ), secondsOfDelay, @@ -236,6 +238,10 @@ const responses = { // .status(200) // .json(serviceHistory.generateServiceHistoryError('403')); }, + 'GET /v0/profile/vet_verification_status': (_req, res) => { + return res.status(200).json(vetVerificationStatus.confirmed); + // return res.status(200).json(vetVerificationStatus.notConfirmed); + }, 'GET /v0/disability_compensation_form/rating_info': (_req, res) => { // return res.status(200).json(ratingInfo.success.serviceConnected0); return res.status(200).json(ratingInfo.success.serviceConnected40); diff --git a/src/applications/personalization/profile/tests/components/military-information/MilitaryInformation.unit.spec.jsx b/src/applications/personalization/profile/tests/components/military-information/MilitaryInformation.unit.spec.jsx index 3ac7fdcd0b6d..fcf3bb8ebf91 100644 --- a/src/applications/personalization/profile/tests/components/military-information/MilitaryInformation.unit.spec.jsx +++ b/src/applications/personalization/profile/tests/components/military-information/MilitaryInformation.unit.spec.jsx @@ -1,8 +1,8 @@ import React from 'react'; import { expect } from 'chai'; - +import * as api from '~/platform/utilities/api'; +import sinon from 'sinon'; import { renderWithProfileReducers } from '../../unit-test-helpers'; - import MilitaryInformation from '../../../components/military-information/MilitaryInformation'; function createBasicInitialState(toggles = {}) { @@ -61,6 +61,16 @@ function createBasicInitialState(toggles = {}) { describe('MilitaryInformation', () => { let initialState; let view; + let apiRequestStub; + + beforeEach(() => { + apiRequestStub = sinon.stub(api, 'apiRequest'); + }); + + afterEach(() => { + apiRequestStub.restore(); + }); + describe('when military history exists', () => { it('should render data for each entry of military history', () => { initialState = createBasicInitialState(); @@ -91,6 +101,15 @@ describe('MilitaryInformation', () => { initialState = createBasicInitialState(); initialState.vaProfile.militaryInformation.serviceHistory.serviceHistory[0].branchOfService = null; initialState.vaProfile.militaryInformation.serviceHistory.serviceHistory[1].branchOfService = undefined; + const mockData = { + data: { + id: '', + type: 'veteran_status_confirmations', + attributes: { veteranStatus: 'confirmed' }, + }, + }; + + apiRequestStub.resolves(mockData); view = renderWithProfileReducers(, { initialState, }); diff --git a/src/applications/personalization/profile/tests/components/military-information/PeriodOfServiceTypeText.unit.spec.jsx b/src/applications/personalization/profile/tests/components/military-information/PeriodOfServiceTypeText.unit.spec.jsx index 2cd5b272046a..3b6a2c289116 100644 --- a/src/applications/personalization/profile/tests/components/military-information/PeriodOfServiceTypeText.unit.spec.jsx +++ b/src/applications/personalization/profile/tests/components/military-information/PeriodOfServiceTypeText.unit.spec.jsx @@ -1,5 +1,7 @@ import React from 'react'; import { expect } from 'chai'; +import * as api from '~/platform/utilities/api'; +import sinon from 'sinon'; import { renderWithProfileReducers } from '../../unit-test-helpers'; import MilitaryInformation from '../../../components/military-information/MilitaryInformation'; @@ -58,6 +60,16 @@ function createBasicInitialState(toggles = {}) { } describe('MilitaryInformation - Period of Service Type Text', () => { + let apiRequestStub; + + beforeEach(() => { + apiRequestStub = sinon.stub(api, 'apiRequest'); + }); + + afterEach(() => { + apiRequestStub.restore(); + }); + describe('when military history exists', () => { it('should render periodOfServiceTypeText when present and when periodOfServiceTypeCode is A or V', () => { const initialState = createBasicInitialState(); diff --git a/src/applications/personalization/profile/tests/e2e/proof-of-veteran-status/proof-of-veteran-status.cypress.spec.js b/src/applications/personalization/profile/tests/e2e/proof-of-veteran-status/proof-of-veteran-status.cypress.spec.js index 5f007816d5c0..08cb01bf1704 100644 --- a/src/applications/personalization/profile/tests/e2e/proof-of-veteran-status/proof-of-veteran-status.cypress.spec.js +++ b/src/applications/personalization/profile/tests/e2e/proof-of-veteran-status/proof-of-veteran-status.cypress.spec.js @@ -8,6 +8,10 @@ import { dishonorableDischarge, unknownDischarge, } from '../../../mocks/endpoints/service-history'; +import { + confirmed, + notConfirmed, +} from '../../../mocks/endpoints/vet-verification-status'; import MilitaryInformation from '../military-information/MilitaryInformation'; describe('Proof of Veteran status', () => { @@ -16,6 +20,7 @@ describe('Proof of Veteran status', () => { cy.intercept('GET', '/v0/user', loa3User72); cy.intercept('GET', '/v0/profile/full_name', fullName.success); cy.intercept('GET', '/v0/profile/service_history', airForce); + cy.intercept('GET', '/v0/profile/vet_verification_status', confirmed); }); it('Should display the Proof of Veteran Status component', () => { @@ -31,6 +36,7 @@ const login = ({ dischargeCode }) => { cy.intercept('GET', '/v0/user', loa3User72); cy.intercept('GET', '/v0/profile/full_name', fullName.success); cy.intercept('GET', '/v0/profile/service_history', dischargeCode); + cy.intercept('GET', '/v0/profile/vet_verification_status', notConfirmed); }; describe('Veteran is not eligible', () => { From cc6bacea9bdee619c8347cb858d02b0c01ee4e8e Mon Sep 17 00:00:00 2001 From: Ian Magenta <59981318+ianmagenta@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:50:07 -0800 Subject: [PATCH 03/11] [VI-1007] Added Missing buttonContent Assignment (#34134) * added missing buttonContent assignment * fixed unit test ci --------- Co-authored-by: Caitlin <78328496+CaitHawk@users.noreply.github.com> --- .github/workflows/continuous-integration.yml | 2 +- src/applications/verify/components/UnifiedVerify.jsx | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 88c5d6b6196c..e27444d75b09 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -266,7 +266,7 @@ jobs: name: Unit Tests needs: [fetch-allow-lists, tests-prep] timeout-minutes: 30 - runs-on: ubuntu-16-cores-latest + runs-on: ubuntu-16-cores-22.04 outputs: app_folders: ${{ steps.get-changed-apps.outputs.folders }} changed-files: ${{ steps.get-changed-apps.outputs.changed_files }} diff --git a/src/applications/verify/components/UnifiedVerify.jsx b/src/applications/verify/components/UnifiedVerify.jsx index 19c590dff3e4..fbdf69f82322 100644 --- a/src/applications/verify/components/UnifiedVerify.jsx +++ b/src/applications/verify/components/UnifiedVerify.jsx @@ -19,10 +19,12 @@ const Verify = () => { let buttonContent; if (isAuthenticated) { - <> - - - ; + buttonContent = ( + <> + + + + ); } else if (isAuthenticatedOAuth) { // Use the loginServiceName to determine which button to show if (loginServiceName === 'idme') { From f59cfff1d74395ad9da8cd1186e9f0f356dfb996 Mon Sep 17 00:00:00 2001 From: Jami Gibbs Date: Thu, 16 Jan 2025 16:01:59 -0600 Subject: [PATCH 04/11] pin the 16 core runner to ubuntu 22.04 (#34139) From f5b716e4f64e9f5e05009314409941d124d895f3 Mon Sep 17 00:00:00 2001 From: Taras Kurilo Date: Thu, 16 Jan 2025 17:29:14 -0500 Subject: [PATCH 05/11] EDM-451 456 496 SOB Updates (#34133) * edm-451 456 496 sob updates * edm-456 update text --- .../components/UserInfoSection.jsx | 6 ++++- .../containers/StatusPage.jsx | 26 ++++++++++++++----- .../sass/post-911-gib-status.scss | 16 +++++++----- .../components/UserInfoSection.unit.spec.jsx | 25 ++++++++++++++++++ 4 files changed, 58 insertions(+), 15 deletions(-) diff --git a/src/applications/post-911-gib-status/components/UserInfoSection.jsx b/src/applications/post-911-gib-status/components/UserInfoSection.jsx index e30b9e3507d1..21e4d247f3c7 100644 --- a/src/applications/post-911-gib-status/components/UserInfoSection.jsx +++ b/src/applications/post-911-gib-status/components/UserInfoSection.jsx @@ -101,7 +101,11 @@ function UserInfoSection({ enrollmentData = {}, showCurrentAsOfAlert }) { You can print your statement and use it as a replacement for a Certificate of Eligibility (COE) to show that you qualify for - benefits. + benefits. This statement only includes entitlement earned through + your own military service. If you recently transferred entitlement, + it may not be reflected here.

+

+ The Supreme Court’s Rudisill decision may increase your months of + entitlement if you have two or more qualifying periods of active + duty. +

+
); printButton = ( -
+

How can I see my Post-9/11 GI Bill benefit payments?

-
+
If you've received education benefit payments through this program,{' '} @@ -66,10 +77,11 @@ class StatusPage extends React.Component {
-

- Call us at . We're here - Monday through Friday, 8:00 a.m to 9:00 p.m ET. If you have - hearing loss, call . +

+ Call 888-GI-BILL-1 ( + ). We're here from Monday through Friday, 8:00 a.m to 7:00 p.m + ET. If you have hearing loss, call{' '} + .

diff --git a/src/applications/post-911-gib-status/sass/post-911-gib-status.scss b/src/applications/post-911-gib-status/sass/post-911-gib-status.scss index 21206b6383ce..f28651b3da1a 100644 --- a/src/applications/post-911-gib-status/sass/post-911-gib-status.scss +++ b/src/applications/post-911-gib-status/sass/post-911-gib-status.scss @@ -5,8 +5,8 @@ margin-bottom: 5em; h3 { - margin: 1em 0 .5em 0; - padding: 0 0 .25em 0; + margin-bottom: 8.5px; + padding: 0; } hr { @@ -16,18 +16,20 @@ margin-top: 1em; margin-bottom: 1em; } + h2 { + margin-top: 0; + } .section { margin-bottom: 2em; } .section-line { - margin-bottom: .25em; + margin-bottom: 0.25em; + } + #benefit-level { + margin-top: 2em; } - - /*.usa-alert { - margin: 1em 0 1em 0; - }*/ .not-qualified h5 { margin-top: 1em; diff --git a/src/applications/post-911-gib-status/tests/components/UserInfoSection.unit.spec.jsx b/src/applications/post-911-gib-status/tests/components/UserInfoSection.unit.spec.jsx index 6f111e5aa446..fb00ccb41e83 100644 --- a/src/applications/post-911-gib-status/tests/components/UserInfoSection.unit.spec.jsx +++ b/src/applications/post-911-gib-status/tests/components/UserInfoSection.unit.spec.jsx @@ -136,4 +136,29 @@ describe('', () => { expect(benefitEndDate.text()).to.contain('Since you’re on active duty'); }); }); + describe('date of birth InfoPair', () => { + it('should display the formatted date of birth if present', () => { + const tree = SkinDeep.shallowRender(); + const dobInfoPair = tree + .everySubTree('InfoPair') + .find(pair => pair.props.label === 'Date of birth'); + expect(dobInfoPair).to.exist; + expect(dobInfoPair.props.value).to.equal('November 12, 1995'); + }); + + it('should display "Unavailable" if dateOfBirth is missing', () => { + // Create a copy of props but remove dateOfBirth + const noDobProps = _.merge({}, props, { + enrollmentData: { + dateOfBirth: null, + }, + }); + const tree = SkinDeep.shallowRender(); + const dobInfoPair = tree + .everySubTree('InfoPair') + .find(pair => pair.props.label === 'Date of birth'); + expect(dobInfoPair).to.exist; + expect(dobInfoPair.props.value).to.equal('Unavailable'); + }); + }); }); From 32fcd0b48636fb4ba300f01282e84450ed7442d3 Mon Sep 17 00:00:00 2001 From: Jami Gibbs Date: Thu, 16 Jan 2025 16:54:58 -0600 Subject: [PATCH 06/11] Disable no import linting error (#34143) * disable no import linting error * disable esline error --- .../vaos/referral-appointments/ChooseDateAndTime.jsx | 2 ++ .../vaos/referral-appointments/components/RequestsList.jsx | 1 + 2 files changed, 3 insertions(+) diff --git a/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx b/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx index 24b172a4758b..10f49b1b9bff 100644 --- a/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx +++ b/src/applications/vaos/referral-appointments/ChooseDateAndTime.jsx @@ -3,8 +3,10 @@ import PropTypes from 'prop-types'; import { useDispatch, useSelector, shallowEqual } from 'react-redux'; import { useLocation } from 'react-router-dom'; import ReferralLayout from './components/ReferralLayout'; +// eslint-disable-next-line import/no-restricted-paths import { getUpcomingAppointmentListInfo } from '../appointment-list/redux/selectors'; import { setFormCurrentPage, fetchProviderDetails } from './redux/actions'; +// eslint-disable-next-line import/no-restricted-paths import { fetchFutureAppointments } from '../appointment-list/redux/actions'; import { getProviderInfo } from './redux/selectors'; import { FETCH_STATUS } from '../utils/constants'; diff --git a/src/applications/vaos/referral-appointments/components/RequestsList.jsx b/src/applications/vaos/referral-appointments/components/RequestsList.jsx index d0dac5e7c89a..a88b485a1b71 100644 --- a/src/applications/vaos/referral-appointments/components/RequestsList.jsx +++ b/src/applications/vaos/referral-appointments/components/RequestsList.jsx @@ -3,6 +3,7 @@ import PropTypes from 'prop-types'; import RequestAppointmentLayout from '../../components/RequestAppointmentLayout'; import { APPOINTMENT_STATUS } from '../../utils/constants'; import InfoAlert from '../../components/InfoAlert'; +// eslint-disable-next-line import/no-restricted-paths import ScheduleAppointmentLink from '../../appointment-list/components/ScheduleAppointmentLink'; const RequestList = ({ appointments, requestsError }) => { From e00c7c5765c50003d5a70f5c189bbd01da57a26a Mon Sep 17 00:00:00 2001 From: Simi Adebowale <47654119+simiadebowale@users.noreply.github.com> Date: Thu, 16 Jan 2025 18:04:33 -0600 Subject: [PATCH 07/11] Add required indicator to DateTimeSelectPage heading and corresponding test (#34132) --- .../components/DateTimeSelectPage/index.jsx | 7 ++++- .../DateTimeSelectPage/index.unit.spec.js | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/applications/vaos/new-appointment/components/DateTimeSelectPage/index.jsx b/src/applications/vaos/new-appointment/components/DateTimeSelectPage/index.jsx index 41a46b1243a4..57d8c9334324 100644 --- a/src/applications/vaos/new-appointment/components/DateTimeSelectPage/index.jsx +++ b/src/applications/vaos/new-appointment/components/DateTimeSelectPage/index.jsx @@ -178,7 +178,12 @@ export default function DateTimeSelectPage() { return (
-

{pageTitle}

+

+ {pageTitle} + + (*Required) + +

{!loadingSlots && ( { }); expect(screen.history.push.called).to.be.false; }); + it('should show required text next to page heading', async () => { + const preferredDate = moment(); + const slot308Date = moment().add(6, 'days'); + + setDateTimeSelectMockFetches({ + typeOfCareId: 'outpatientMentalHealth', + slotDatesByClinicId: { + '308': [slot308Date], + }, + }); + + const store = createTestStore(initialState); + + await setTypeOfCare(store, /mental health/i); + await setVAFacility(store, '983', 'outpatientMentalHealth'); + await setClinic(store, '983_308'); + await setPreferredDate(store, preferredDate); + + const screen = renderWithStoreAndRouter( + , + { + store, + }, + ); + + expect(await screen.findByText(/Required/i)).to.exist; + }); }); From 2d7bdb8b5fa312fbf0a584d18dbf1a2c7480620e Mon Sep 17 00:00:00 2001 From: Colin <143013011+cosu419@users.noreply.github.com> Date: Fri, 17 Jan 2025 06:15:13 -0800 Subject: [PATCH 08/11] Appoint a rep - submission method (#34050) * Added feature toggle for appoint v2 features * Moving page depends logic into schema files to clean up form config * Made use of feature toggle for v2 features * Adjusted feature toggle + env checks. Added constant for local testing without vets-api * Created rep submission method screen * Updated routing for to include submission method * Added to submissionmethod page conditional * Added unit tests for submission method * Added return statement * Disabled feature toggle for e2e tests * comment * Corrected error state copy * Added unit test for error state * File + component naming * Updated pageDepends conditional * Make use of new pagedepends in representativeSubmissionMethod --- .../ContactAccreditedRepresentative.jsx | 9 + .../RepresentativeSubmissionMethod.jsx | 95 +++++++++++ .../representative-appoint/config/form.js | 32 ++-- .../constants/enableV2FeaturesLocally.js | 3 + .../representative-appoint/containers/App.jsx | 26 ++- .../hooks/useV2FeatureVisibility.js | 31 ++++ .../representative-appoint/pages/index.js | 2 + .../representativeSubmissionMethod.js | 43 +++++ .../selectAccreditedOrganization.js | 20 ++- .../tests/e2e/navigation/2122.cypress.spec.js | 4 + .../e2e/navigation/2122a.cypress.spec.js | 4 + ...presentativeSubmissionMethod.unit.spec.jsx | 159 ++++++++++++++++++ .../feature-toggles/featureFlagNames.json | 1 + 13 files changed, 396 insertions(+), 33 deletions(-) create mode 100644 src/applications/representative-appoint/components/RepresentativeSubmissionMethod.jsx create mode 100644 src/applications/representative-appoint/constants/enableV2FeaturesLocally.js create mode 100644 src/applications/representative-appoint/hooks/useV2FeatureVisibility.js create mode 100644 src/applications/representative-appoint/pages/representative/representativeSubmissionMethod.js create mode 100644 src/applications/representative-appoint/tests/pages/representative/representativeSubmissionMethod.unit.spec.jsx diff --git a/src/applications/representative-appoint/components/ContactAccreditedRepresentative.jsx b/src/applications/representative-appoint/components/ContactAccreditedRepresentative.jsx index cf3d6507433e..4602b79a158f 100644 --- a/src/applications/representative-appoint/components/ContactAccreditedRepresentative.jsx +++ b/src/applications/representative-appoint/components/ContactAccreditedRepresentative.jsx @@ -4,10 +4,12 @@ import FormNavButtons from 'platform/forms-system/src/js/components/FormNavButto import PropTypes from 'prop-types'; import { useReviewPage } from '../hooks/useReviewPage'; import { getEntityAddressAsObject } from '../utilities/helpers'; +import useV2FeatureToggle from '../hooks/useV2FeatureVisibility'; import AddressEmailPhone from './AddressEmailPhone'; const ContactAccreditedRepresentative = props => { + const v2IsEnabled = useV2FeatureToggle(); const { formData, goBack, goForward, goToPath } = props; const rep = props?.formData?.['view:selectedRepresentative']; const repAttributes = rep?.attributes; @@ -26,6 +28,9 @@ const ContactAccreditedRepresentative = props => { ) && representative.attributes?.accreditedOrganizations?.data?.length > 1; + // will need to update this when we can determine submission methods + const submissionMethodRequired = orgSelectionRequired && v2IsEnabled; + const handleGoBack = () => { if (isReviewPage) { goToPath('/representative-select?review=true'); @@ -36,6 +41,10 @@ const ContactAccreditedRepresentative = props => { const handleGoForward = () => { if (isReviewPage) { + if (submissionMethodRequired) { + goToPath('/representative-submission-method?review=true'); + return; + } if (orgSelectionRequired) { goToPath('/representative-organization?review=true'); } else { diff --git a/src/applications/representative-appoint/components/RepresentativeSubmissionMethod.jsx b/src/applications/representative-appoint/components/RepresentativeSubmissionMethod.jsx new file mode 100644 index 000000000000..8fd82cb1e45b --- /dev/null +++ b/src/applications/representative-appoint/components/RepresentativeSubmissionMethod.jsx @@ -0,0 +1,95 @@ +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { VaRadio } from '@department-of-veterans-affairs/component-library/dist/react-bindings'; +import FormNavButtons from 'platform/forms-system/src/js/components/FormNavButtons'; +import { scrollToFirstError } from 'platform/utilities/ui'; +import { useReviewPage } from '../hooks/useReviewPage'; + +const RepresentativeSubmissionMethod = props => { + const { formData, setFormData, goBack, goForward, goToPath } = props; + const [error, setError] = useState(null); + + const isReviewPage = useReviewPage(); + + const handleGoBack = () => { + if (isReviewPage) { + goToPath('representative-contact?review=true'); + } else { + goBack(formData); + } + }; + const handleGoForward = () => { + if (!formData?.representativeSubmissionMethod) { + setError('Choose how to submit your request by selecting an option'); + scrollToFirstError({ focusOnAlertRole: true }); + } else if (isReviewPage) { + goToPath('/representative-organization?review=true'); + } else { + goForward(formData); + } + }; + + const handleRadioSelect = e => { + setError(null); + setFormData({ + ...formData, + representativeSubmissionMethod: e.detail.value, + }); + }; + + return ( + <> + + + + + + + + ); +}; + +RepresentativeSubmissionMethod.propTypes = { + formData: PropTypes.object, + goBack: PropTypes.func, + goForward: PropTypes.func, + goToPath: PropTypes.func, + setFormData: PropTypes.func, +}; + +function mapStateToProps(state) { + return { + formData: state.form.data, + }; +} + +export { RepresentativeSubmissionMethod }; // Named export for testing + +export default connect( + mapStateToProps, + null, +)(RepresentativeSubmissionMethod); diff --git a/src/applications/representative-appoint/config/form.js b/src/applications/representative-appoint/config/form.js index 0add5f0207a0..d74bb684f16a 100644 --- a/src/applications/representative-appoint/config/form.js +++ b/src/applications/representative-appoint/config/form.js @@ -1,7 +1,6 @@ import commonDefinitions from 'vets-json-schema/dist/definitions.json'; -// import environment from '@department-of-veterans-affairs/platform-utilities/environment'; -// import profileContactInfo from 'platform/forms-system/src/js/definitions/profileContactInfo'; import FormFooter from 'platform/forms/components/FormFooter'; + import GetFormHelp from '../components/GetFormHelp'; import configService from '../utilities/configService'; import manifest from '../manifest.json'; @@ -33,6 +32,7 @@ import { replaceAccreditedRepresentative, selectedAccreditedOrganizationId, contactAccreditedRepresentative, + representativeSubmissionMethod, } from '../pages'; // import initialData from '../tests/fixtures/data/test-data.json'; @@ -41,6 +41,7 @@ import SelectAccreditedRepresentative from '../components/SelectAccreditedRepres import SelectedAccreditedRepresentativeReview from '../components/SelectAccreditedRepresentativeReview'; import ContactAccreditedRepresentative from '../components/ContactAccreditedRepresentative'; import SelectOrganization from '../components/SelectOrganization'; +import RepresentativeSubmissionMethod from '../components/RepresentativeSubmissionMethod'; import SubmissionError from '../components/SubmissionError'; @@ -136,29 +137,26 @@ const formConfig = { uiSchema: contactAccreditedRepresentative.uiSchema, schema: contactAccreditedRepresentative.schema, }, + RepresentativeSubmissionMethod: { + title: 'Representative Submission Method', + path: 'representative-submission-method', + CustomPage: RepresentativeSubmissionMethod, + depends: formData => + representativeSubmissionMethod.pageDepends(formData), + uiSchema: representativeSubmissionMethod.uiSchema, + schema: representativeSubmissionMethod.schema, + }, selectAccreditedOrganization: { path: 'representative-organization', title: 'Organization Select', hideOnReview: true, CustomPage: SelectOrganization, depends: formData => - !!formData['view:selectedRepresentative'] && - ['representative', 'veteran_service_officer'].includes( - formData['view:selectedRepresentative'].attributes - ?.individualType, - ) && - formData['view:selectedRepresentative'].attributes - ?.accreditedOrganizations?.data?.length > 1, + selectedAccreditedOrganizationId.pageDepends(formData), uiSchema: selectedAccreditedOrganizationId.uiSchema, - schema: { - type: 'object', - properties: { - selectedAccreditedOrganizationId: { - type: 'string', - }, - }, - }, + schema: selectedAccreditedOrganizationId.schema, }, + replaceAccreditedRepresentative: { title: 'Representative Replace', path: 'representative-replace', diff --git a/src/applications/representative-appoint/constants/enableV2FeaturesLocally.js b/src/applications/representative-appoint/constants/enableV2FeaturesLocally.js new file mode 100644 index 000000000000..c5c1800af150 --- /dev/null +++ b/src/applications/representative-appoint/constants/enableV2FeaturesLocally.js @@ -0,0 +1,3 @@ +// toggle this eg when testing PRs locally + +export const enableV2FeaturesForLocalTesting = true; diff --git a/src/applications/representative-appoint/containers/App.jsx b/src/applications/representative-appoint/containers/App.jsx index 0b397ca4d7f4..4d3cd330117d 100644 --- a/src/applications/representative-appoint/containers/App.jsx +++ b/src/applications/representative-appoint/containers/App.jsx @@ -15,6 +15,8 @@ import formConfig from '../config/form'; import configService from '../utilities/configService'; import { getFormSubtitle } from '../utilities/helpers'; +import useV2FeatureToggle from '../hooks/useV2FeatureVisibility'; + function App({ loggedIn, location, children, formData, setFormData }) { const subTitle = getFormSubtitle(formData); @@ -25,6 +27,7 @@ function App({ loggedIn, location, children, formData, setFormData }) { } = useFeatureToggle(); const appIsEnabled = useToggleValue(appToggleKey); + const v2FeatureToggle = useV2FeatureToggle(); const isProduction = window.Cypress || environment.isProduction(); const isAppToggleLoading = useToggleLoadingValue(appToggleKey); @@ -40,6 +43,7 @@ function App({ loggedIn, location, children, formData, setFormData }) { [pathname], ); + // dynamically updates the form subtitle to 21-22 or 21-22A useEffect( () => { configService.setFormConfig({ subTitle }); @@ -50,26 +54,18 @@ function App({ loggedIn, location, children, formData, setFormData }) { useEffect( () => { - const defaultViewFields = { + const updatedFormData = { + ...formData, + v2IsEnabled: v2FeatureToggle, 'view:isLoggedIn': loggedIn, + 'view:representativeQueryInput': '', + 'view:representativeSearchResults': [], }; - setFormData({ - ...formData, - ...defaultViewFields, - }); + setFormData(updatedFormData); }, - [loggedIn], + [v2FeatureToggle, loggedIn], ); - // resetting user query between sessions - useEffect(() => { - setFormData({ - ...formData, - 'view:representativeQueryInput': '', - 'view:representativeSearchResults': [], - }); - }, []); - if (isAppToggleLoading) { return (
diff --git a/src/applications/representative-appoint/hooks/useV2FeatureVisibility.js b/src/applications/representative-appoint/hooks/useV2FeatureVisibility.js new file mode 100644 index 000000000000..e58ffe290db5 --- /dev/null +++ b/src/applications/representative-appoint/hooks/useV2FeatureVisibility.js @@ -0,0 +1,31 @@ +import { useFeatureToggle } from '~/platform/utilities/feature-toggles'; +import environment from 'platform/utilities/environment'; +import { enableV2FeaturesForLocalTesting } from '../constants/enableV2FeaturesLocally'; + +const useV2FeatureToggle = () => { + const { + TOGGLE_NAMES: { appointARepresentativeEnableV2Features: appToggleKey }, + useToggleLoadingValue, + useToggleValue, + } = useFeatureToggle(); + + const appointV2FeaturesEnabled = useToggleValue(appToggleKey); + const toggleIsLoading = useToggleLoadingValue(appToggleKey); + + if (toggleIsLoading) { + return false; + } + + // can remove this after verifying the toggle in staging + if (environment.isProduction() || window.Cypress) { + return false; + } + + if (environment.isLocalhost()) { + return enableV2FeaturesForLocalTesting; + } + + return appointV2FeaturesEnabled; +}; + +export default useV2FeatureToggle; diff --git a/src/applications/representative-appoint/pages/index.js b/src/applications/representative-appoint/pages/index.js index ae5338060abf..dcbc97651470 100644 --- a/src/applications/representative-appoint/pages/index.js +++ b/src/applications/representative-appoint/pages/index.js @@ -20,6 +20,7 @@ import * as selectAccreditedRepresentative from './representative/selectAccredit import * as replaceAccreditedRepresentative from './representative/replaceAccreditedRepresentative'; import * as selectedAccreditedOrganizationId from './representative/selectAccreditedOrganization'; import * as contactAccreditedRepresentative from './representative/contactAccreditedRepresentative'; +import * as representativeSubmissionMethod from './representative/representativeSubmissionMethod'; export { authorizeMedical, @@ -44,4 +45,5 @@ export { replaceAccreditedRepresentative, selectedAccreditedOrganizationId, contactAccreditedRepresentative, + representativeSubmissionMethod, }; diff --git a/src/applications/representative-appoint/pages/representative/representativeSubmissionMethod.js b/src/applications/representative-appoint/pages/representative/representativeSubmissionMethod.js new file mode 100644 index 000000000000..7c4b6ac43c69 --- /dev/null +++ b/src/applications/representative-appoint/pages/representative/representativeSubmissionMethod.js @@ -0,0 +1,43 @@ +import RepresentativeSubmissionMethod from '../../components/RepresentativeSubmissionMethod'; + +export const uiSchema = { + representativeSubmissionMethod: { + 'ui:title': "Select how you'd like to submit your request", + 'ui:widget': RepresentativeSubmissionMethod, + 'ui:options': { + hideLabelText: true, + hideOnReview: true, + }, + 'ui:required': () => true, + }, +}; + +export const schema = { + type: 'object', + properties: { + representativeSubmissionMethod: { + type: 'string', + }, + }, +}; + +export const pageDepends = formData => { + const { v2IsEnabled } = formData; + // temporarily hardcoding these values + const repHasMultipleOrganizations = + !!formData['view:selectedRepresentative'] && + ['representative', 'veteran_service_officer'].includes( + formData['view:selectedRepresentative'].attributes?.individualType, + ) && + formData['view:selectedRepresentative'].attributes?.accreditedOrganizations + ?.data?.length > 1; + const userCanSubmitDigitally = true; + const representativeAcceptsDigitalSubmission = true; + + return ( + v2IsEnabled && + repHasMultipleOrganizations && + userCanSubmitDigitally && + representativeAcceptsDigitalSubmission + ); +}; diff --git a/src/applications/representative-appoint/pages/representative/selectAccreditedOrganization.js b/src/applications/representative-appoint/pages/representative/selectAccreditedOrganization.js index 21ddac36f596..47ef06e7bd84 100644 --- a/src/applications/representative-appoint/pages/representative/selectAccreditedOrganization.js +++ b/src/applications/representative-appoint/pages/representative/selectAccreditedOrganization.js @@ -12,4 +12,22 @@ export const uiSchema = { }, }; -export const schema = {}; +export const schema = { + type: 'object', + properties: { + selectedAccreditedOrganizationId: { + type: 'string', + }, + }, +}; + +export const pageDepends = formData => { + return ( + !!formData['view:selectedRepresentative'] && + ['representative', 'veteran_service_officer'].includes( + formData['view:selectedRepresentative'].attributes?.individualType, + ) && + formData['view:selectedRepresentative'].attributes?.accreditedOrganizations + ?.data?.length > 1 + ); +}; diff --git a/src/applications/representative-appoint/tests/e2e/navigation/2122.cypress.spec.js b/src/applications/representative-appoint/tests/e2e/navigation/2122.cypress.spec.js index 420d24a7644a..4312fd9fe670 100644 --- a/src/applications/representative-appoint/tests/e2e/navigation/2122.cypress.spec.js +++ b/src/applications/representative-appoint/tests/e2e/navigation/2122.cypress.spec.js @@ -17,6 +17,10 @@ describe('Authenticated', () => { data: { features: [ { name: 'appoint_a_representative_enable_frontend', value: true }, + { + name: 'appoint_a_representative_enable_v2_features', + value: false, + }, ], }, }); diff --git a/src/applications/representative-appoint/tests/e2e/navigation/2122a.cypress.spec.js b/src/applications/representative-appoint/tests/e2e/navigation/2122a.cypress.spec.js index 1c6df30fbb0c..c1ec9e80484d 100644 --- a/src/applications/representative-appoint/tests/e2e/navigation/2122a.cypress.spec.js +++ b/src/applications/representative-appoint/tests/e2e/navigation/2122a.cypress.spec.js @@ -18,6 +18,10 @@ describe('Unauthenticated', () => { data: { features: [ { name: 'appoint_a_representative_enable_frontend', value: true }, + { + name: 'appoint_a_representative_enable_v2_features', + value: false, + }, ], }, }); diff --git a/src/applications/representative-appoint/tests/pages/representative/representativeSubmissionMethod.unit.spec.jsx b/src/applications/representative-appoint/tests/pages/representative/representativeSubmissionMethod.unit.spec.jsx new file mode 100644 index 000000000000..8ec8f1784f96 --- /dev/null +++ b/src/applications/representative-appoint/tests/pages/representative/representativeSubmissionMethod.unit.spec.jsx @@ -0,0 +1,159 @@ +import React from 'react'; +import { Provider } from 'react-redux'; +import { expect } from 'chai'; +import { render, fireEvent, waitFor } from '@testing-library/react'; +import sinon from 'sinon'; +import { RepresentativeSubmissionMethod } from '../../../components/RepresentativeSubmissionMethod'; +import * as reviewPageHook from '../../../hooks/useReviewPage'; + +describe('', () => { + const getProps = () => { + return { + props: { + formData: {}, + goBack: sinon.spy(), + goForward: sinon.spy(), + goToPath: sinon.spy(), + }, + mockStore: { + getState: () => ({}), + subscribe: () => {}, + }, + }; + }; + + const renderContainer = (props, mockStore) => { + return render( + + + , + ); + }; + + it('should render component', () => { + const { props, mockStore } = getProps(); + + const { container } = render( + + + , + ); + expect(container).to.exist; + }); + + it('displays an error', async () => { + const { props, mockStore } = getProps(); + + const { container } = renderContainer(props, mockStore); + + const radioSelector = container.querySelector('va-radio'); + + const continueButton = container.querySelector('.usa-button-primary'); + + fireEvent.click(continueButton); + + await waitFor(() => { + expect(radioSelector).to.have.attr( + 'error', + 'Choose how to submit your request by selecting an option', + ); + }); + }); + + context('non-review mode', () => { + it('should call goBack with formData when handleGoBack is triggered and isReviewPage is false', () => { + const { props, mockStore } = getProps(); + + const useReviewPageStub = sinon + .stub(reviewPageHook, 'call') + .returns(false); + + const { getByText } = renderContainer(props, mockStore); + + fireEvent.click(getByText('Back')); + + expect(props.goBack.calledOnce).to.be.true; + expect(props.goBack.calledWith(props.formData)).to.be.true; + + useReviewPageStub.restore(); + }); + + 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(false); + + props.formData.representativeSubmissionMethod = 'mail'; + + const { getByText } = render( + + + , + ); + + fireEvent.click(getByText('Continue')); + + 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(); + }); + }); + + context('review mode', () => { + beforeEach(function() { + Object.defineProperty(window, 'location', { + value: { search: '?review=true' }, + writable: true, + }); + }); + + 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 { props, mockStore } = getProps(); + + const { getByText } = renderContainer(props, mockStore); + + fireEvent.click(getByText('Back')); + + 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; + + useReviewPageStub.restore(); + }); + + it('should call goToPath with /representative-organization?review=true when handleGoForward is triggered, isReviewPage is true, and isReplacingRep is true', () => { + const { props, mockStore } = getProps(); + + const useReviewPageStub = sinon + .stub(reviewPageHook, 'useReviewPage') + .returns(true); + + props.formData.representativeSubmissionMethod = 'mail'; + + const { getByText } = render( + + + , + ); + + fireEvent.click(getByText('Continue')); + + expect(props.goForward.called).to.be.false; + expect(props.goToPath.calledOnce).to.be.true; + expect( + props.goToPath.calledWith('/representative-organization?review=true'), + ).to.be.true; + + useReviewPageStub.restore(); + }); + }); +}); diff --git a/src/platform/utilities/feature-toggles/featureFlagNames.json b/src/platform/utilities/feature-toggles/featureFlagNames.json index fc321f66f4e1..327eeed7d3fa 100644 --- a/src/platform/utilities/feature-toggles/featureFlagNames.json +++ b/src/platform/utilities/feature-toggles/featureFlagNames.json @@ -5,6 +5,7 @@ "aedpPrefill": "aedp_prefill", "allClaimsAddDisabilitiesEnhancement": "all_claims_add_disabilities_enhancement", "appointARepresentativeEnableFrontend": "appoint_a_representative_enable_frontend", + "appointARepresentativeEnableV2Features": "appoint_a_representative_enable_v2_features", "askVaDashboardFeature": "ask_va_dashboard_feature", "askVaFormFeature": "ask_va_form_feature", "askVaIntroductionPageFeature": "ask_va_introduction_page_feature", From d3abcb7b709ec1f21e1266558697627e93b0f13d Mon Sep 17 00:00:00 2001 From: Brandon Cooper Date: Fri, 17 Jan 2025 09:16:07 -0500 Subject: [PATCH 09/11] fix typo (#34140) --- .../caregivers/components/FormFields/FacilitySearch.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/applications/caregivers/components/FormFields/FacilitySearch.jsx b/src/applications/caregivers/components/FormFields/FacilitySearch.jsx index 025a203c168a..d29e1483ec55 100644 --- a/src/applications/caregivers/components/FormFields/FacilitySearch.jsx +++ b/src/applications/caregivers/components/FormFields/FacilitySearch.jsx @@ -301,7 +301,7 @@ const FacilitySearch = props => {

You’ll need to find and select the VA medical center or clinic where - the Veteran receives or plans to recieve care. + the Veteran receives or plans to receive care.

The VA medical center or clinic may be in a different city, state, or From 96272b8316e06e31ac056e3ea44f8f11f30ad9c8 Mon Sep 17 00:00:00 2001 From: Peri-Ann McLaren <141954992+pmclaren19@users.noreply.github.com> Date: Fri, 17 Jan 2025 07:36:14 -0700 Subject: [PATCH 10/11] remove request by from breadcrumbs, tab title and document request page header (#34138) --- .../claim-document-request-pages/DefaultPage.jsx | 2 +- .../components/DocumentRequestPage.unit.spec.jsx | 6 +++--- .../DefaultPage.unit.spec.jsx | 15 +++++++++------ .../tests/e2e/page-objects/TrackClaimsPage.js | 2 +- .../tests/e2e/page-objects/TrackClaimsPageV2.js | 2 +- .../tests/utils/helpers.unit.spec.js | 4 ++-- src/applications/claims-status/utils/helpers.js | 3 ++- 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/applications/claims-status/components/claim-document-request-pages/DefaultPage.jsx b/src/applications/claims-status/components/claim-document-request-pages/DefaultPage.jsx index da9f6e9ae1a4..e22cc22c2632 100644 --- a/src/applications/claims-status/components/claim-document-request-pages/DefaultPage.jsx +++ b/src/applications/claims-status/components/claim-document-request-pages/DefaultPage.jsx @@ -21,7 +21,7 @@ export default function DefaultPage({ }) { return (

-

Request for {item.displayName}

+

{item.displayName}

{item.status === 'NEEDED_FROM_YOU' ? ( ) : null} diff --git a/src/applications/claims-status/tests/components/DocumentRequestPage.unit.spec.jsx b/src/applications/claims-status/tests/components/DocumentRequestPage.unit.spec.jsx index a777678891e4..0b8a3734d2a0 100644 --- a/src/applications/claims-status/tests/components/DocumentRequestPage.unit.spec.jsx +++ b/src/applications/claims-status/tests/components/DocumentRequestPage.unit.spec.jsx @@ -125,10 +125,10 @@ describe('', () => { `../document-request/${trackedItem.id}`, ); expect(breadcrumbs.breadcrumbList[3].label).to.equal( - `Request for ${trackedItem.displayName}`, + trackedItem.displayName, ); expect(document.title).to.equal( - `Request for ${trackedItem.displayName} | Veterans Affairs`, + `${trackedItem.displayName} | Veterans Affairs`, ); }); }); @@ -465,7 +465,7 @@ describe('', () => { , ); - expect(document.title).to.equal('Request for Testing | Veterans Affairs'); + expect(document.title).to.equal('Testing | Veterans Affairs'); expect(resetUploads.called).to.be.true; }); diff --git a/src/applications/claims-status/tests/components/claim-document-request-pages/DefaultPage.unit.spec.jsx b/src/applications/claims-status/tests/components/claim-document-request-pages/DefaultPage.unit.spec.jsx index bc17cec4197b..ae721b033752 100644 --- a/src/applications/claims-status/tests/components/claim-document-request-pages/DefaultPage.unit.spec.jsx +++ b/src/applications/claims-status/tests/components/claim-document-request-pages/DefaultPage.unit.spec.jsx @@ -1,6 +1,5 @@ import React from 'react'; import { expect } from 'chai'; -import moment from 'moment-timezone'; import { $ } from '@department-of-veterans-affairs/platform-forms-system/ui'; @@ -41,6 +40,12 @@ describe('', () => { documents: '[]', date: '2024-03-07', }; + const today = new Date(); + const past = new Date(item.suspenseDate); + const monthsDue = + today.getMonth() - + past.getMonth() + + 12 * (today.getFullYear() - past.getFullYear()); const { getByText, container } = renderWithRouter( , ); @@ -49,12 +54,10 @@ describe('', () => { expect($('.due-date-header', container)).to.exist; const formattedClaimDate = formatDate(item.suspenseDate); getByText( - `Needed from you by ${formattedClaimDate} - Due ${moment( - item.suspenseDate, - ).fromNow()}`, + `Needed from you by ${formattedClaimDate} - Due ${monthsDue} months ago`, ); expect($('.optional-upload', container)).to.not.exist; - getByText('Request for Submit buddy statement(s)'); + getByText('Submit buddy statement(s)'); getByText(scrubDescription(item.description)); expect($('va-additional-info', container)).to.exist; expect($('va-file-input', container)).to.exist; @@ -85,7 +88,7 @@ describe('', () => { getByText( '- We’ve asked others to send this to us, but you may upload it if you have it.', ); - getByText('Request for Submit buddy statement(s)'); + getByText('Submit buddy statement(s)'); getByText(scrubDescription(item.description)); expect($('va-additional-info', container)).to.exist; expect($('va-file-input', container)).to.exist; diff --git a/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPage.js b/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPage.js index 42c2f0cf3d85..229e4b5e6438 100644 --- a/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPage.js +++ b/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPage.js @@ -336,7 +336,7 @@ class TrackClaimsPage { } else { cy.get('.usa-breadcrumb__list > li:nth-child(4) a').should( 'contain', - 'Request for Submit Buddy Statement(s)', + 'Submit Buddy Statement(s)', ); } } diff --git a/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPageV2.js b/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPageV2.js index 96d14c3f9c51..307789b936fd 100644 --- a/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPageV2.js +++ b/src/applications/claims-status/tests/e2e/page-objects/TrackClaimsPageV2.js @@ -660,7 +660,7 @@ class TrackClaimsPageV2 { } else { cy.get('.usa-breadcrumb__list > li:nth-child(4) a').should( 'contain', - 'Request for Submit Buddy Statement(s)', + 'Submit Buddy Statement(s)', ); } } diff --git a/src/applications/claims-status/tests/utils/helpers.unit.spec.js b/src/applications/claims-status/tests/utils/helpers.unit.spec.js index d12c3c3b7588..07ec9782084b 100644 --- a/src/applications/claims-status/tests/utils/helpers.unit.spec.js +++ b/src/applications/claims-status/tests/utils/helpers.unit.spec.js @@ -1196,11 +1196,11 @@ describe('Disability benefits helpers: ', () => { 'Review evidence list (5103 notice)', ); }); - it('should display Request for Submit buddy statement(s)', () => { + it('should display Submit buddy statement(s)', () => { const displayName = 'Submit buddy statement(s)'; const documentRequestPageTitle = setDocumentRequestPageTitle(displayName); - expect(documentRequestPageTitle).to.equal(`Request for ${displayName}`); + expect(documentRequestPageTitle).to.equal(displayName); }); }); diff --git a/src/applications/claims-status/utils/helpers.js b/src/applications/claims-status/utils/helpers.js index 9ed672c0eaf2..b9138949573e 100644 --- a/src/applications/claims-status/utils/helpers.js +++ b/src/applications/claims-status/utils/helpers.js @@ -1190,10 +1190,11 @@ export const generateClaimTitle = (claim, placement, tab) => { }; // Use this function to set the Document Request Page Title, Page Tab and Page Breadcrumb Title +// It is also used to set the Document Request Page breadcrumb text export function setDocumentRequestPageTitle(displayName) { return isAutomated5103Notice(displayName) ? 'Review evidence list (5103 notice)' - : `Request for ${displayName}`; + : displayName; } // Used to set page title for the CST Tabs From fff065779f9296088747f20637c3de89024949a2 Mon Sep 17 00:00:00 2001 From: Kevin Suarez Date: Fri, 17 Jan 2025 09:56:23 -0500 Subject: [PATCH 11/11] 100539 Only hit AC-API if user is verified (#34082) --- .../mhv-landing-page/containers/LandingPageContainer.jsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/applications/mhv-landing-page/containers/LandingPageContainer.jsx b/src/applications/mhv-landing-page/containers/LandingPageContainer.jsx index b20605be4f21..c38f6212eb96 100644 --- a/src/applications/mhv-landing-page/containers/LandingPageContainer.jsx +++ b/src/applications/mhv-landing-page/containers/LandingPageContainer.jsx @@ -12,6 +12,7 @@ import { } from '../utilities/data'; import { isAuthenticatedWithSSOe, + isLOA3, isVAPatient, selectProfile, signInServiceEnabled, @@ -32,6 +33,7 @@ const LandingPageContainer = () => { const profile = useSelector(selectProfile); const ssoe = useSelector(isAuthenticatedWithSSOe); const useSiS = useSelector(signInServiceEnabled); + const userVerified = useSelector(isLOA3); const registered = useSelector(isVAPatient); const unreadMessageAriaLabel = resolveUnreadMessageAriaLabel( unreadMessageCount, @@ -77,7 +79,7 @@ const LandingPageContainer = () => { useEffect( () => { - if (!profile.loading) { + if (!profile.loading && userVerified) { if (userHasMhvAccount) { dispatch({ type: fetchAccountStatusSuccess, @@ -95,7 +97,7 @@ const LandingPageContainer = () => { } } }, - [userHasMhvAccount, profile.loading, dispatch], + [userHasMhvAccount, profile.loading, userVerified, dispatch], ); if (loading)