+ // TODO https://github.com/department-of-veterans-affairs/vagov-claim-classification/issues/671:
+ // When remove allClaimsAddDisabilitiesEnhancement FF, remove the vads-u-flex--fill class
+
{typeof formData?.condition === 'string'
? formData.condition
: NULL_CONDITION_STRING}
diff --git a/src/applications/disability-benefits/all-claims/config/form.js b/src/applications/disability-benefits/all-claims/config/form.js
index ac6a50adae4e..0624267d45b0 100644
--- a/src/applications/disability-benefits/all-claims/config/form.js
+++ b/src/applications/disability-benefits/all-claims/config/form.js
@@ -40,6 +40,7 @@ import {
isUploadingSTR,
needsToEnter781,
needsToEnter781a,
+ onFormLoaded,
showAdditionalFormsChapter,
showPtsdCombat,
showPtsdNonCombat,
@@ -55,6 +56,7 @@ import { supportingEvidenceOrientation } from '../content/supportingEvidenceOrie
import {
adaptiveBenefits,
addDisabilities,
+ addDisabilitiesPrevious,
additionalBehaviorChanges,
additionalDocuments,
additionalRemarks781,
@@ -194,6 +196,7 @@ const formConfig = {
subTitle: 'VA Form 21-526EZ',
preSubmitInfo: getPreSubmitInfo(),
CustomReviewTopContent,
+ onFormLoaded,
chapters: {
veteranDetails: {
title: ({ onReviewPage }) =>
@@ -328,6 +331,24 @@ const formConfig = {
uiSchema: ratedDisabilities.uiSchema,
schema: ratedDisabilities.schema,
},
+ // TODO https://github.com/department-of-veterans-affairs/vagov-claim-classification/issues/671:
+ // When remove allClaimsAddDisabilitiesEnhancement FF, remove this page
+ addDisabilitiesPrevious: {
+ title: 'Add a new disability',
+ path: DISABILITY_SHARED_CONFIG.addDisabilitiesPrevious.path,
+ depends: formData =>
+ DISABILITY_SHARED_CONFIG.addDisabilitiesPrevious.depends(formData),
+ uiSchema: addDisabilitiesPrevious.uiSchema,
+ schema: addDisabilitiesPrevious.schema,
+ updateFormData: addDisabilitiesPrevious.updateFormData,
+ appStateSelector: state => ({
+ // needed for validateDisabilityName to work properly on the review
+ // & submit page. Validation functions are provided the pageData and
+ // not the formData on the review & submit page. For more details
+ // see https://dsva.slack.com/archives/CBU0KDSB1/p1614182869206900
+ newDisabilities: state.form?.data?.newDisabilities || [],
+ }),
+ },
addDisabilities: {
title: 'Add a new disability',
path: DISABILITY_SHARED_CONFIG.addDisabilities.path,
diff --git a/src/applications/disability-benefits/all-claims/constants.js b/src/applications/disability-benefits/all-claims/constants.js
index 9252d6c1aa44..efbaa0b3b39d 100644
--- a/src/applications/disability-benefits/all-claims/constants.js
+++ b/src/applications/disability-benefits/all-claims/constants.js
@@ -299,6 +299,11 @@ export const FORM_STATUS_BDD = 'formStatusBdd';
export const SHOW_8940_4192 = 'showSubforms';
+export const ADD_DISABILITIES_ENHANCEMENT_TOGGLE =
+ 'all_claims_add_disabilities_enhancement';
+export const ADD_DISABILITIES_ENHANCEMENT_DATA =
+ 'showAddDisabilitiesEnhancement';
+
export const SERVICE_BRANCHES = 'militaryServiceBranches';
// sessionStorage key used for the user entered separation date in the wizard
@@ -337,8 +342,6 @@ export const CHAR_LIMITS = [
export const MAX_HOUSING_STRING_LENGTH = 500;
export const OMB_CONTROL = '2900-0747';
-export const SHOW_ADD_DISABILITIES_ENHANCEMENT =
- 'showAddDisabilitiesEnhancement';
// used to save feature flag in form data for toxic exposure
export const SHOW_TOXIC_EXPOSURE = 'showToxicExposure';
@@ -419,6 +422,14 @@ export const OFFICIAL_REPORT_TYPES = Object.freeze({
none: 'No report',
});
+export const BEHAVIOR_LIST_BEHAVIOR_SUBTITLES = Object.freeze({
+ work: 'Behavioral changes related to work',
+ health: 'Behavioral changes related to health',
+ other: 'Other behavioral changes',
+ unlisted: 'Other behavioral changes not listed here:',
+ none: 'None',
+});
+
export const BEHAVIOR_CHANGES_WORK = Object.freeze({
reassignment:
'Request for a change in occupational series or duty assignment',
diff --git a/src/applications/disability-benefits/all-claims/content/addDisabilities.jsx b/src/applications/disability-benefits/all-claims/content/addDisabilities.jsx
index c3f35abba0b6..c938050ff960 100644
--- a/src/applications/disability-benefits/all-claims/content/addDisabilities.jsx
+++ b/src/applications/disability-benefits/all-claims/content/addDisabilities.jsx
@@ -1,8 +1,6 @@
import React from 'react';
import { Link } from 'react-router';
-import { SHOW_ADD_DISABILITIES_ENHANCEMENT } from '../constants';
-
export const addDisabilitiesInstructions = (
<>
Tell us the new conditions you want to claim
@@ -74,6 +72,3 @@ export const increaseAndNewAlertRevised = ({ formContext }) => {
);
};
-
-export const getShowAddDisabilitiesEnhancement = () =>
- window.sessionStorage.getItem(SHOW_ADD_DISABILITIES_ENHANCEMENT) === 'true';
diff --git a/src/applications/disability-benefits/all-claims/content/form0781/behaviorListPages.jsx b/src/applications/disability-benefits/all-claims/content/form0781/behaviorListPages.jsx
index b2ebcd32b7bb..835a769e5367 100644
--- a/src/applications/disability-benefits/all-claims/content/form0781/behaviorListPages.jsx
+++ b/src/applications/disability-benefits/all-claims/content/form0781/behaviorListPages.jsx
@@ -1,16 +1,127 @@
import React from 'react';
-export const BEHAVIOR_LIST_DESCRIPTION = (
-
- Did you experience any of these behavioral changes after your traumatic
- experiences? It’s also okay if you don’t report any behavioral changes. You
- can skip this question if you don’t feel comfortable answering.
-
+export const behaviorPageTitle = 'Behavioral changes';
+
+export const behaviorIntroDescription = (
+ <>
+
+ The next few questions are about behavioral changes you experienced after
+ your traumatic experiences.
+
+
+ These questions are optional. Any information you provide will help us
+ understand your situation and identify evidence to support your claim. You
+ can provide only details you’re comfortable sharing.
+
+
Information we’ll ask you for
+
+ We’ll ask you for this information:
+
+ -
+ The types of behavioral changes you experienced after your traumatic
+ events
+
+ -
+ A description of each behavioral change, including when it happened,
+ whether any records exist, and any other details you want to provide
+
+
+
+
You can take a break at any time
+
+ We understand that some of the questions may be difficult to answer. You
+ can take a break at any time and come back to continue your application
+ later. We’ll save the information you’ve entered so far.
+
+ >
+);
+
+export const behaviorListPageTitle = 'Types of behavioral changes';
+
+export const behaviorListDescription = (
+ <>
+
+ Did you experience any of these behavioral changes after your traumatic
+ experiences?
+
+
+ It’s also okay if you don’t report any behavioral changes. You can skip
+ this question if you don’t feel comfortable answering.
+
+ >
);
-export const BEHAVIOR_LIST_BEHAVIORS_TITLE =
- 'Behavioral changes related to work';
+export const behaviorListNoneLabel =
+ 'I didn’t experience any of these behavioral changes.';
+
+export const behaviorIntroCombatDescription = (
+ <>
+
+ The next few questions are about behavioral changes you experienced after
+ your traumatic experiences
+
+
+ Since you said your traumatic experiences were related to combat only,
+ these questions are optional. You don’t need to answer them. If we need
+ more information, we’ll contact you after you submit your claim.
+
+ >
+);
-export const BEHAVIOR_INTRO_COMBAT_DESCRIPTION = (
-
Placholder content for combat intro description
+export const behaviorListAdditionalInformation = (
+
+
+
+ We understand that traumatic events from your military service may not
+ have been reported or documented. In these situations, the information
+ you provide about your behavioral changes will help us understand your
+ situation and identify evidence to support your claim.
+
+
+
);
+
+/**
+ * Validates that a required selection is made and that the 'none' checkbox is not selected if behaviors are also selected
+ * @param {object} errors - Errors object from rjsf
+ * @param {object} formData
+ */
+
+export function validateBehaviorSelections(errors, formData) {
+ // returns true at first checkbox selection
+ const behaviorsSelected =
+ Object.values(formData.workBehaviors || {}).some(
+ selected => selected === true,
+ ) ||
+ Object.values(formData.healthBehaviors || {}).some(
+ selected => selected === true,
+ ) ||
+ Object.values(formData.otherBehaviors || {}).some(
+ selected => selected === true,
+ );
+
+ // returns true if text field has any input
+ const unlistedProvided = Object.values(formData.unlistedBehaviors || {}).some(
+ entry => !!entry,
+ );
+
+ // returns true if 'none' checkbox is selected
+ const optedOut = Object.values(formData['view:optOut'] || {}).some(
+ selected => selected === true,
+ );
+
+ if (!behaviorsSelected && !unlistedProvided && !optedOut) {
+ // when a user has not selected options nor opted out
+ errors['view:optOut'].addError(
+ 'PLACEHOLDER error message - selection required',
+ );
+ } else if (optedOut && (behaviorsSelected || unlistedProvided)) {
+ // when a user has selected options and opted out
+ errors['view:optOut'].addError(
+ 'If you didn’t experience any of these behavioral changes, unselect the other options you selected.',
+ );
+ }
+}
diff --git a/src/applications/disability-benefits/all-claims/pages/addDisabilities.js b/src/applications/disability-benefits/all-claims/pages/addDisabilities.js
index 703cd95e483b..c98e97260255 100644
--- a/src/applications/disability-benefits/all-claims/pages/addDisabilities.js
+++ b/src/applications/disability-benefits/all-claims/pages/addDisabilities.js
@@ -3,12 +3,9 @@ import set from 'platform/utilities/data/set';
import get from 'platform/utilities/data/get';
import omit from 'platform/utilities/data/omit';
import fullSchema from 'vets-json-schema/dist/21-526EZ-ALLCLAIMS-schema.json';
-import * as combobox from '../definitions/combobox';
import Autocomplete from '../components/Autocomplete';
import disabilityLabelsRevised from '../content/disabilityLabelsRevised';
import NewDisability from '../components/NewDisability';
-import ArrayField from '../components/ArrayField';
-import ConditionReviewField from '../components/ConditionReviewField';
import {
validateDisabilityName,
requireDisability,
@@ -24,7 +21,6 @@ import {
} from '../utils';
import {
addDisabilitiesInstructions,
- getShowAddDisabilitiesEnhancement,
increaseAndNewAlertRevised,
newOnlyAlertRevised,
} from '../content/addDisabilities';
@@ -52,65 +48,6 @@ const autocompleteUiSchema = {
},
};
-const comboboxUiSchema = combobox.uiSchema('Enter your condition', {
- 'ui:reviewField': ({ children }) => children,
- 'ui:options': {
- debounceRate: 200,
- freeInput: true,
- inputTransformers: [
- // Replace a bunch of things that aren't valid with valid equivalents
- input => input.replace(/["”’]/g, `'`),
- input => input.replace(/[;–]/g, ' -- '),
- input => input.replace(/[&]/g, ' and '),
- input => input.replace(/[\\]/g, '/'),
- // TODO: Remove the period replacer once permanent fix in place
- input => input.replace(/[.]/g, ' '),
- // Strip out everything that's not valid and doesn't need to be replaced
- // TODO: Add period back into allowed chars regex
- input => input.replace(/([^a-zA-Z0-9\-',/() ]+)/g, ''),
- // Get rid of extra whitespace characters
- input => input.trim(),
- input => input.replace(/\s{2,}/g, ' '),
- ],
- // options for the combobox dropdown
- listItems: Object.values(disabilityLabelsRevised),
- },
- // autoSuggest schema doesn't have any default validations as long as { `freeInput: true` }
- 'ui:validations': [validateDisabilityName, limitNewDisabilities],
- 'ui:required': () => true,
- 'ui:errorMessages': {
- required: missingConditionMessage,
- },
-});
-
-const allClaimsArrayFieldWithCombobox = {
- 'ui:description': addDisabilitiesInstructions,
- 'ui:field': ArrayField,
- 'ui:options': {
- viewField: NewDisability,
- reviewTitle: 'Conditions',
- duplicateKey: 'condition',
- itemName: 'Condition',
- itemAriaLabel: data => data.condition,
- includeRequiredLabelInTitle: true,
- classNames: 'cc-autocomplete-container',
- },
- useNewFocus: true,
- // Ideally, this would show the validation on the array itself (or the name
- // field in an array item), but that's not working.
- 'ui:validations': [requireDisability],
- items: {
- condition: comboboxUiSchema,
- // custom review & submit layout - see https://github.com/department-of-veterans-affairs/vets-website/pull/14091
- // disabled until design changes have been approved
- 'ui:objectViewField': ConditionReviewField,
- 'ui:options': {
- itemAriaLabel: data => data.condition,
- itemName: 'New condition',
- },
- },
-};
-
const platformArrayFieldWithAutocomplete = {
'ui:description': addDisabilitiesInstructions,
'ui:options': {
@@ -132,9 +69,7 @@ const platformArrayFieldWithAutocomplete = {
};
export const uiSchema = {
- newDisabilities: getShowAddDisabilitiesEnhancement()
- ? platformArrayFieldWithAutocomplete
- : allClaimsArrayFieldWithCombobox,
+ newDisabilities: platformArrayFieldWithAutocomplete,
// This object only shows up when the user tries to continue without claiming either a rated or new condition
'view:newDisabilityErrors': {
'view:newOnlyAlert': {
diff --git a/src/applications/disability-benefits/all-claims/pages/addDisabilitiesPrevious.js b/src/applications/disability-benefits/all-claims/pages/addDisabilitiesPrevious.js
new file mode 100644
index 000000000000..d7d79f6e1eaa
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/pages/addDisabilitiesPrevious.js
@@ -0,0 +1,253 @@
+// TODO https://github.com/department-of-veterans-affairs/vagov-claim-classification/issues/671:
+// When remove allClaimsAddDisabilitiesEnhancement FF, remove this page
+import set from 'platform/utilities/data/set';
+import get from 'platform/utilities/data/get';
+import omit from 'platform/utilities/data/omit';
+import fullSchema from 'vets-json-schema/dist/21-526EZ-ALLCLAIMS-schema.json';
+import * as combobox from '../definitions/combobox';
+import disabilityLabelsRevised from '../content/disabilityLabelsRevised';
+import NewDisability from '../components/NewDisability';
+import ArrayField from '../components/ArrayField';
+import ConditionReviewField from '../components/ConditionReviewField';
+import {
+ validateDisabilityName,
+ requireDisability,
+ limitNewDisabilities,
+ missingConditionMessage,
+} from '../validations';
+import {
+ newConditionsOnly,
+ newAndIncrease,
+ hasClaimedConditions,
+ claimingNew,
+ sippableId,
+} from '../utils';
+import {
+ addDisabilitiesInstructions,
+ increaseAndNewAlertRevised,
+ newOnlyAlertRevised,
+} from '../content/addDisabilities';
+
+const { condition } = fullSchema.definitions.newDisabilities.items.properties;
+
+const comboboxUiSchema = combobox.uiSchema('Enter your condition', {
+ 'ui:reviewField': ({ children }) => children,
+ 'ui:options': {
+ debounceRate: 200,
+ freeInput: true,
+ inputTransformers: [
+ // Replace a bunch of things that aren't valid with valid equivalents
+ input => input.replace(/["”’]/g, `'`),
+ input => input.replace(/[;–]/g, ' -- '),
+ input => input.replace(/[&]/g, ' and '),
+ input => input.replace(/[\\]/g, '/'),
+ // TODO: Remove the period replacer once permanent fix in place
+ input => input.replace(/[.]/g, ' '),
+ // Strip out everything that's not valid and doesn't need to be replaced
+ // TODO: Add period back into allowed chars regex
+ input => input.replace(/([^a-zA-Z0-9\-',/() ]+)/g, ''),
+ // Get rid of extra whitespace characters
+ input => input.trim(),
+ input => input.replace(/\s{2,}/g, ' '),
+ ],
+ // options for the combobox dropdown
+ listItems: Object.values(disabilityLabelsRevised),
+ },
+ // autoSuggest schema doesn't have any default validations as long as { `freeInput: true` }
+ 'ui:validations': [validateDisabilityName, limitNewDisabilities],
+ 'ui:required': () => true,
+ 'ui:errorMessages': {
+ required: missingConditionMessage,
+ },
+});
+
+const allClaimsArrayFieldWithCombobox = {
+ 'ui:description': addDisabilitiesInstructions,
+ 'ui:field': ArrayField,
+ 'ui:options': {
+ viewField: NewDisability,
+ reviewTitle: 'Conditions',
+ duplicateKey: 'condition',
+ itemName: 'Condition',
+ itemAriaLabel: data => data.condition,
+ includeRequiredLabelInTitle: true,
+ classNames: 'cc-autocomplete-container',
+ },
+ useNewFocus: true,
+ // Ideally, this would show the validation on the array itself (or the name
+ // field in an array item), but that's not working.
+ 'ui:validations': [requireDisability],
+ items: {
+ condition: comboboxUiSchema,
+ // custom review & submit layout - see https://github.com/department-of-veterans-affairs/vets-website/pull/14091
+ // disabled until design changes have been approved
+ 'ui:objectViewField': ConditionReviewField,
+ 'ui:options': {
+ itemAriaLabel: data => data.condition,
+ itemName: 'New condition',
+ },
+ },
+};
+
+export const uiSchema = {
+ newDisabilities: allClaimsArrayFieldWithCombobox,
+ // This object only shows up when the user tries to continue without claiming either a rated or new condition
+ 'view:newDisabilityErrors': {
+ 'view:newOnlyAlert': {
+ 'ui:description': newOnlyAlertRevised,
+ 'ui:options': {
+ hideIf: formData =>
+ !(newConditionsOnly(formData) && !claimingNew(formData)),
+ },
+ },
+ // Only show this alert if the veteran is claiming both rated and new
+ // conditions but no rated conditions were selected
+ 'view:increaseAndNewAlert': {
+ 'ui:description': increaseAndNewAlertRevised,
+ 'ui:options': {
+ hideIf: formData =>
+ !(newAndIncrease(formData) && !hasClaimedConditions(formData)),
+ },
+ },
+ },
+};
+
+export const schema = {
+ type: 'object',
+ properties: {
+ newDisabilities: {
+ type: 'array',
+ minItems: 1,
+ items: {
+ type: 'object',
+ properties: {
+ condition,
+ },
+ },
+ },
+ 'view:newDisabilityErrors': {
+ type: 'object',
+ properties: {
+ 'view:newOnlyAlert': { type: 'object', properties: {} },
+ 'view:increaseAndNewAlert': { type: 'object', properties: {} },
+ },
+ },
+ },
+};
+
+const indexOfFirstChange = (oldArr, newArr) => {
+ for (let i = 0; i < newArr.length; i += 1) {
+ if (oldArr[i] !== newArr[i]) return i;
+ }
+
+ // No difference found
+ return undefined;
+};
+
+const deleted = (oldArr, newArr) => {
+ const i = indexOfFirstChange(oldArr, newArr);
+ // If no difference was found, the last item was deleted
+ return i !== undefined ? oldArr[i] : oldArr[oldArr.length - 1];
+};
+
+const removeDisability = (deletedElement, formData) => {
+ const removeFromTreatedDisabilityNames = (disability, data) => {
+ const path = 'vaTreatmentFacilities';
+ const facilities = get(path, data);
+ if (!facilities) return data;
+
+ return set(
+ path,
+ facilities.map(f =>
+ set(
+ 'treatedDisabilityNames',
+ omit(
+ [sippableId(disability.condition)],
+ get('treatedDisabilityNames', f),
+ ),
+ f,
+ ),
+ ),
+ data,
+ );
+ };
+
+ const removeFromPow = (disability, data) => {
+ const path = 'view:isPow.powDisabilities';
+ const powDisabilities = get(path, data);
+ if (!powDisabilities) return data;
+
+ return set(
+ path,
+ omit([sippableId(disability.condition)], powDisabilities),
+ data,
+ );
+ };
+
+ return removeFromPow(
+ deletedElement,
+ removeFromTreatedDisabilityNames(deletedElement, formData),
+ );
+};
+
+// Find the old name -> change to new name
+const changeDisabilityName = (oldData, newData, changedIndex) => {
+ const oldId = sippableId(oldData.newDisabilities[changedIndex]?.condition);
+ const newId = sippableId(newData.newDisabilities[changedIndex]?.condition);
+
+ let result = removeDisability(oldData.newDisabilities[changedIndex], newData);
+
+ // Add in the new property with the old value
+ const facilitiesPath = 'vaTreatmentFacilities';
+ const facilities = get(facilitiesPath, result);
+ const oldFacilities = get(facilitiesPath, oldData);
+ if (facilities && oldFacilities) {
+ result = set(
+ facilitiesPath,
+ facilities.map((f, i) => {
+ const oldValue = oldFacilities[i].treatedDisabilityNames[oldId];
+ return oldValue !== undefined
+ ? set(['treatedDisabilityNames', newId], oldValue, f)
+ : f;
+ }),
+ result,
+ );
+ }
+
+ // And for the one view:isPow
+ const powDisabilitiesPath = 'view:isPow.powDisabilities';
+ const powDisabilities = get(powDisabilitiesPath, result);
+ const oldPowDisabilities = get(powDisabilitiesPath, oldData);
+ if (powDisabilities && oldPowDisabilities[oldId] !== undefined) {
+ result = set(
+ `${powDisabilitiesPath}.${newId}`,
+ oldPowDisabilities[oldId],
+ result,
+ );
+ }
+
+ return result;
+};
+
+export const updateFormData = (oldData, newData) => {
+ const oldArr = oldData.newDisabilities;
+ const newArr = newData.newDisabilities;
+ // Sanity check
+ if (!Array.isArray(oldArr) || !Array.isArray(newArr)) return newData;
+
+ // Disability was removed
+ if (oldArr.length > newArr.length) {
+ const deletedElement = deleted(oldArr, newArr);
+ return removeDisability(deletedElement, newData);
+ }
+
+ // Disability was modified
+ const changedIndex = indexOfFirstChange(oldArr, newArr);
+ if (oldArr.length === newArr.length && changedIndex !== undefined) {
+ // Update the disability name in treatedDisabilityNames and
+ // powDisabilities _if_ it exists already
+ return changeDisabilityName(oldData, newData, changedIndex);
+ }
+
+ return newData;
+};
diff --git a/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroCombatPage.js b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroCombatPage.js
index e9231ecfc5da..df8deae26faa 100644
--- a/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroCombatPage.js
+++ b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroCombatPage.js
@@ -2,20 +2,29 @@ import {
radioUI,
radioSchema,
} from 'platform/forms-system/src/js/web-component-patterns';
-
-import { BEHAVIOR_INTRO_COMBAT_DESCRIPTION } from '../../content/form0781/behaviorListPages';
+import {
+ titleWithTag,
+ form0781HeadingTag,
+ mentalHealthSupportAlert,
+} from '../../content/form0781';
+import {
+ behaviorPageTitle,
+ behaviorIntroCombatDescription,
+} from '../../content/form0781/behaviorListPages';
export const uiSchema = {
- 'ui:description': BEHAVIOR_INTRO_COMBAT_DESCRIPTION,
- 'view:answerCombatBehaviorQuestions': {
- ...radioUI({
- title: 'Do you want to answer additional questions?',
- required: () => true,
- labels: {
- true: 'true',
- false: 'false',
- },
- }),
+ 'ui:title': titleWithTag(behaviorPageTitle, form0781HeadingTag),
+ 'ui:description': behaviorIntroCombatDescription,
+ 'view:answerCombatBehaviorQuestions': radioUI({
+ title: 'Do you want to answer additional questions?',
+ required: () => true,
+ labels: {
+ true: 'Yes',
+ false: 'No',
+ },
+ }),
+ 'view:mentalHealthSupportAlert': {
+ 'ui:description': mentalHealthSupportAlert,
},
};
@@ -23,5 +32,9 @@ export const schema = {
type: 'object',
properties: {
'view:answerCombatBehaviorQuestions': radioSchema(['true', 'false']),
+ 'view:mentalHealthSupportAlert': {
+ type: 'object',
+ properties: {},
+ },
},
};
diff --git a/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroPage.js b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroPage.js
index 3a065fa325c7..5e0495a51b33 100644
--- a/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroPage.js
+++ b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorIntroPage.js
@@ -1,8 +1,27 @@
+import {
+ titleWithTag,
+ form0781HeadingTag,
+ mentalHealthSupportAlert,
+} from '../../content/form0781';
+import {
+ behaviorPageTitle,
+ behaviorIntroDescription,
+} from '../../content/form0781/behaviorListPages';
+
export const uiSchema = {
- 'ui:description': 'Placeholder Text for Behavior Intro',
+ 'ui:title': titleWithTag(behaviorPageTitle, form0781HeadingTag),
+ 'ui:description': behaviorIntroDescription,
+ 'view:mentalHealthSupportAlert': {
+ 'ui:description': mentalHealthSupportAlert,
+ },
};
export const schema = {
type: 'object',
- properties: {},
+ properties: {
+ 'view:mentalHealthSupportAlert': {
+ type: 'object',
+ properties: {},
+ },
+ },
};
diff --git a/src/applications/disability-benefits/all-claims/pages/form0781/behaviorListPage.js b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorListPage.js
index cfa34bc0c534..ebe6283ad53e 100644
--- a/src/applications/disability-benefits/all-claims/pages/form0781/behaviorListPage.js
+++ b/src/applications/disability-benefits/all-claims/pages/form0781/behaviorListPage.js
@@ -1,79 +1,91 @@
import {
checkboxGroupSchema,
checkboxGroupUI,
+ textUI,
} from 'platform/forms-system/src/js/web-component-patterns';
import {
- BEHAVIOR_LIST_DESCRIPTION,
- BEHAVIOR_LIST_BEHAVIORS_TITLE,
+ titleWithTag,
+ form0781HeadingTag,
+ mentalHealthSupportAlert,
+} from '../../content/form0781';
+import {
+ behaviorListDescription,
+ behaviorListNoneLabel,
+ behaviorListAdditionalInformation,
+ behaviorListPageTitle,
+ validateBehaviorSelections,
} from '../../content/form0781/behaviorListPages';
import {
+ BEHAVIOR_LIST_BEHAVIOR_SUBTITLES,
BEHAVIOR_CHANGES_WORK,
BEHAVIOR_CHANGES_HEALTH,
BEHAVIOR_CHANGES_OTHER,
} from '../../constants';
-const schemaKeys = Object.keys(BEHAVIOR_CHANGES_WORK).concat(
- Object.keys(BEHAVIOR_CHANGES_HEALTH),
- Object.keys(BEHAVIOR_CHANGES_OTHER),
-);
-
export const uiSchema = {
- 'ui:description': BEHAVIOR_LIST_DESCRIPTION,
- behaviors: checkboxGroupUI({
- title: BEHAVIOR_LIST_BEHAVIORS_TITLE,
+ 'ui:title': titleWithTag(behaviorListPageTitle, form0781HeadingTag),
+ 'ui:description': behaviorListDescription,
+ workBehaviors: checkboxGroupUI({
+ title: BEHAVIOR_LIST_BEHAVIOR_SUBTITLES.work,
+ labelHeaderLevel: '4',
labels: {
...BEHAVIOR_CHANGES_WORK,
+ },
+ required: false,
+ }),
+ healthBehaviors: checkboxGroupUI({
+ title: BEHAVIOR_LIST_BEHAVIOR_SUBTITLES.health,
+ labelHeaderLevel: '4',
+ labels: {
...BEHAVIOR_CHANGES_HEALTH,
+ },
+ required: false,
+ }),
+ otherBehaviors: checkboxGroupUI({
+ title: BEHAVIOR_LIST_BEHAVIOR_SUBTITLES.other,
+ labelHeaderLevel: '4',
+ labels: {
...BEHAVIOR_CHANGES_OTHER,
},
required: false,
}),
- otherBehaviors: {
- 'ui:title': 'placeholder title',
- 'ui:description': 'placeholde description',
- },
+ unlistedBehaviors: textUI({
+ title: BEHAVIOR_LIST_BEHAVIOR_SUBTITLES.unlisted,
+ }),
'view:optOut': checkboxGroupUI({
- title: 'None',
+ title: BEHAVIOR_LIST_BEHAVIOR_SUBTITLES.none,
+ labelHeaderLevel: '4',
labels: {
- none: 'no selection placeholder',
+ none: behaviorListNoneLabel,
},
required: false,
}),
- 'ui:validations': [
- (errors, field) => {
- const behaviorSelected = Object.values(field.behaviors || {}).some(
- selected => selected,
- );
- const otherProvided = Object.values(field.otherBehaviors || {}).some(
- entry => !!entry,
- );
- const optedOut = !!Object.values(field['view:optOut'] || {}).some(
- entry => !!entry,
- );
-
- if (!behaviorSelected && !otherProvided && !optedOut) {
- // when a user has not selected options nor opted out
- errors['view:optOut'].addError(
- 'selection required Error message placehoder',
- );
- } else if (optedOut && (behaviorSelected || otherProvided)) {
- // when a user has selected options and opted out
- errors['view:optOut'].addError(
- 'conflicting selections Error message placehoder',
- );
- }
- },
- ],
+ 'view:behaviorAdditionalInformation': {
+ 'ui:description': behaviorListAdditionalInformation,
+ },
+ 'view:mentalHealthSupportAlert': {
+ 'ui:description': mentalHealthSupportAlert,
+ },
+ 'ui:validations': [validateBehaviorSelections],
};
export const schema = {
type: 'object',
properties: {
- behaviors: checkboxGroupSchema(schemaKeys),
- otherBehaviors: {
+ workBehaviors: checkboxGroupSchema(Object.keys(BEHAVIOR_CHANGES_WORK)),
+ healthBehaviors: checkboxGroupSchema(Object.keys(BEHAVIOR_CHANGES_HEALTH)),
+ otherBehaviors: checkboxGroupSchema(Object.keys(BEHAVIOR_CHANGES_OTHER)),
+ unlistedBehaviors: {
type: 'string',
- properties: {},
},
'view:optOut': checkboxGroupSchema(['none']),
+ 'view:behaviorAdditionalInformation': {
+ type: 'object',
+ properties: {},
+ },
+ 'view:mentalHealthSupportAlert': {
+ type: 'object',
+ properties: {},
+ },
},
};
diff --git a/src/applications/disability-benefits/all-claims/pages/index.js b/src/applications/disability-benefits/all-claims/pages/index.js
index cf3ba7b7f166..91de9e443e18 100644
--- a/src/applications/disability-benefits/all-claims/pages/index.js
+++ b/src/applications/disability-benefits/all-claims/pages/index.js
@@ -114,10 +114,12 @@ import * as vaMedicalRecords from './vaMedicalRecords';
import * as veteranInfo from './veteranInfo';
import * as workBehaviorChanges from './workBehaviorChanges';
import * as addDisabilities from './addDisabilities';
+import * as addDisabilitiesPrevious from './addDisabilitiesPrevious';
export {
adaptiveBenefits,
addDisabilities,
+ addDisabilitiesPrevious,
additionalBehaviorChanges,
additionalDocuments,
additionalExposures,
diff --git a/src/applications/disability-benefits/all-claims/tests/cypress.helpers.js b/src/applications/disability-benefits/all-claims/tests/cypress.helpers.js
index 7e7d30c8a804..8394ea630fac 100644
--- a/src/applications/disability-benefits/all-claims/tests/cypress.helpers.js
+++ b/src/applications/disability-benefits/all-claims/tests/cypress.helpers.js
@@ -280,10 +280,12 @@ export const pageHooks = cy => ({
});
},
- 'new-disabilities/add': () => {
+ // TODO https://github.com/department-of-veterans-affairs/vagov-claim-classification/issues/671:
+ // When remove allClaimsAddDisabilitiesEnhancement FF, update this page to be 'new-disabilities/add'
+ 'new-disabilities/add-3': () => {
cy.get('@testData').then(data => {
data.newDisabilities.forEach((disability, index) => {
- const comboBox = `[id="root_newDisabilities_${index}_condition"]`;
+ const autocomplete = `[id="root_newDisabilities_${index}_condition"]`;
const input = '#inputField';
const option = '[role="option"]';
@@ -291,11 +293,11 @@ export const pageHooks = cy => ({
if (index > 0) {
cy.findByText(/add another condition/i).click();
- cy.findByText(/remove/i, { selector: 'button' }).should('be.visible');
+ cy.get('va-button[text="Remove"]').should('be.visible');
}
// click on input and type search text
- cy.get(comboBox)
+ cy.get(autocomplete)
.shadow()
.find(input)
.type(disability.condition, { force: true });
@@ -306,7 +308,7 @@ export const pageHooks = cy => ({
.first()
.click();
- cy.get(comboBox)
+ cy.get(autocomplete)
.shadow()
.find(input)
.should('have.value', disability.condition);
@@ -319,7 +321,7 @@ export const pageHooks = cy => ({
.eq(1)
.click();
- cy.get(comboBox)
+ cy.get(autocomplete)
.shadow()
.find(input)
.should('have.value', selectedOption);
@@ -327,7 +329,7 @@ export const pageHooks = cy => ({
}
// click save
- cy.findByText(/save/i, { selector: 'button' }).click();
+ cy.get('va-button[text="Save"]').click();
});
});
},
diff --git a/src/applications/disability-benefits/all-claims/tests/fixtures/mocks/feature-toggles.json b/src/applications/disability-benefits/all-claims/tests/fixtures/mocks/feature-toggles.json
index 537c352aaedd..29915c3bae0b 100644
--- a/src/applications/disability-benefits/all-claims/tests/fixtures/mocks/feature-toggles.json
+++ b/src/applications/disability-benefits/all-claims/tests/fixtures/mocks/feature-toggles.json
@@ -17,6 +17,10 @@
{
"name": "subform_8940_4192",
"value": true
+ },
+ {
+ "name": "all_claims_add_disabilities_enhancement",
+ "value": true
}
]
}
diff --git a/src/applications/disability-benefits/all-claims/tests/pages/addDisabilities.unit.spec.jsx b/src/applications/disability-benefits/all-claims/tests/pages/addDisabilities.unit.spec.jsx
index 7ff52f2fc56c..832c486d9379 100644
--- a/src/applications/disability-benefits/all-claims/tests/pages/addDisabilities.unit.spec.jsx
+++ b/src/applications/disability-benefits/all-claims/tests/pages/addDisabilities.unit.spec.jsx
@@ -1,7 +1,8 @@
-import { fireEvent, render } from '@testing-library/react';
+import { fireEvent, render, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { expect } from 'chai';
import { fullStringSimilaritySearch } from 'platform/forms-system/src/js/utilities/addDisabilitiesStringSearch';
+import { $, $$ } from 'platform/forms-system/src/js/utilities/ui';
import { DefinitionTester } from 'platform/testing/unit/schemaform-utils';
import set from 'platform/utilities/data/set';
import React from 'react';
@@ -44,6 +45,14 @@ const createScreen = (
);
};
+const getVaButtonByText = (text, container) => {
+ return $(`va-button[text="${text}"]`, container);
+};
+
+const getAllVaButtonsByText = (text, container) => {
+ return $$(`va-button[text="${text}"]`, container);
+};
+
const simulateInputChange = (selector, value) => {
const vaTextInput = selector;
vaTextInput.value = value;
@@ -55,50 +64,54 @@ const simulateInputChange = (selector, value) => {
vaTextInput.dispatchEvent(event);
};
-const addAConditionWithMouse = (
+const addAConditionWithMouse = async (
getAllByRole,
getByTestId,
- getByText,
searchTerm,
searchResult,
) => {
- const input = getByTestId('combobox-input');
+ const input = getByTestId('autocomplete-input');
simulateInputChange(input, searchTerm);
- const listboxItems = getAllByRole('option');
- const freeTextItem = listboxItems.find(
- item => item.textContent === searchResult,
- );
- fireEvent.click(freeTextItem);
+ await waitFor(() => {
+ const listResults = getAllByRole('option');
- const saveButton = getByText('Save');
- fireEvent.click(saveButton);
+ for (const result of listResults) {
+ if (result.textContent === searchResult) {
+ fireEvent.click(result);
+ const saveButton = getVaButtonByText('Save');
+ fireEvent.click(saveButton);
+ }
+ }
+ });
};
-const addAConditionWithKeyboard = (
+const addAConditionWithKeyboard = async (
getAllByRole,
getByTestId,
getByText,
searchTerm,
searchResult,
) => {
- const input = getByTestId('combobox-input');
+ const input = getByTestId('autocomplete-input');
simulateInputChange(input, searchTerm);
fireEvent.keyDown(input, { key: 'ArrowDown' });
- const listboxItems = getAllByRole('option');
+ await waitFor(() => {
+ const listResults = getAllByRole('option');
- for (const item of listboxItems) {
- if (item.textContent !== searchResult) {
- fireEvent.keyDown(item, { key: 'ArrowDown' });
- } else if (item.textContent === searchResult) {
- fireEvent.keyDown(input, { key: 'Enter' });
- break;
+ for (const result of listResults) {
+ if (result.textContent !== searchResult) {
+ fireEvent.keyDown(result, { key: 'ArrowDown' });
+ } else if (result.textContent === searchResult) {
+ fireEvent.keyDown(input, { key: 'Enter' });
+ break;
+ }
}
- }
+ });
- const saveButton = getByText('Save');
+ const saveButton = getVaButtonByText('Save');
fireEvent.click(saveButton);
};
@@ -158,14 +171,14 @@ describe('Add Disabilities Page', () => {
expect(example7).to.be.visible;
});
- it('should render "Your new conditions" subheading, ComboBox, save button, and add another condition button', () => {
- const { getByRole, getByTestId, getByText } = createScreen();
+ it('should render "Your new conditions" subheading, AutoComplete, save button, and add another condition button', () => {
+ const { container, getByRole, getByTestId, getByText } = createScreen();
const newConditionsSubHeading = getByRole('heading', {
name: 'Your new conditions',
});
- const input = getByTestId('combobox-input');
- const saveButton = getByText('Save');
+ const input = getByTestId('autocomplete-input');
+ const saveButton = getVaButtonByText('Save', container);
const addAnotherConditionButton = getByText('Add another condition');
expect(newConditionsSubHeading).to.be.visible;
@@ -175,29 +188,23 @@ describe('Add Disabilities Page', () => {
});
it('should render with no saved conditions by default', () => {
- const { queryByText } = createScreen();
+ const { container } = createScreen();
- const savedConditionEditButton = queryByText('Edit');
+ const savedConditionEditButton = getVaButtonByText('Edit', container);
expect(savedConditionEditButton).to.not.exist;
});
- it('should render ComboBox label with required, input, and listbox', () => {
- const { getByRole, getByText, getByTestId } = createScreen();
+ it('should render autocomplete label with required and input', () => {
+ const { getByTestId } = createScreen();
- const label = getByText('Enter your condition');
- const required = label.querySelector('span').textContent;
- const input = getByTestId('combobox-input');
- const listbox = getByRole('listbox');
+ const input = getByTestId('autocomplete-input');
- expect(label).to.be.visible;
- expect(required).to.eq('(*Required)');
- expect(input).to.be.visible;
- expect(listbox).to.be.visible;
- expect(listbox).to.have.length(0);
+ expect(input).to.have.attribute('label', 'Enter your condition');
+ expect(input).to.have.attribute('required');
});
- it('should render error message on item and alert on page if no new conditions are added', () => {
+ it('should render error message on result and alert on page if no new conditions are added', () => {
const { getByText } = createScreen();
const submitButton = getByText('Submit');
@@ -219,63 +226,73 @@ describe('Add Disabilities Page', () => {
describe('Updating State', () => {
it('should render with saved condition when there is initial formData', () => {
- const { getByText } = createScreen(true, false, [
+ const { container, getByText } = createScreen(true, false, [
{
condition: 'asthma',
},
]);
const savedCondition = getByText('asthma');
- const savedConditionEditButton = getByText('Edit');
+ const savedConditionEditButton = getVaButtonByText('Edit', container);
expect(savedCondition).to.be.visible;
expect(savedConditionEditButton).to.be.visible;
});
- it('should be able to add value to ComboBox input ', () => {
- const searchTerm = 'Typed value';
+ it('should be able to add value to AutoComplete input ', async () => {
+ const searchTerm = 'a';
const searchResults = fullStringSimilaritySearch(searchTerm, items);
const freeTextAndFilteredItemsCount = searchResults.length + 1;
- const { getByRole, getByTestId } = createScreen();
+ const { getByTestId, queryByTestId } = createScreen();
- const input = getByTestId('combobox-input');
+ const input = getByTestId('autocomplete-input');
simulateInputChange(input, searchTerm);
- const listbox = getByRole('listbox');
- expect(listbox).to.have.length(freeTextAndFilteredItemsCount);
+ await waitFor(() => {
+ const list = getByTestId('autocomplete-list');
+
+ expect(list).to.have.length(freeTextAndFilteredItemsCount);
+ });
- fireEvent.click(document);
+ fireEvent.mouseDown(document.body);
- expect(listbox).to.have.length(0);
- expect(input).to.have.value(searchTerm);
+ await waitFor(() => {
+ const list = queryByTestId('autocomplete-list');
+
+ expect(list).to.not.exist;
+ });
});
- it('should render ComboBox listbox items in alignment with string similarity search', () => {
+ it('should render AutoComplete list items in alignment with string similarity search', async () => {
const searchTerm = 'ACL';
const searchResults = fullStringSimilaritySearch(searchTerm, items);
const { getAllByRole, getByTestId } = createScreen();
- const input = getByTestId('combobox-input');
+ const input = getByTestId('autocomplete-input');
simulateInputChange(input, searchTerm);
- const listboxItems = getAllByRole('option');
-
- listboxItems.forEach((item, index) => {
- if (index === 0) {
- expect(item.textContent).to.eq(
- `Enter your condition as "${searchTerm}"`,
- );
- } else {
- const searchResult = searchResults[index - 1];
- expect(item.textContent).to.eq(searchResult);
- }
+
+ await waitFor(() => {
+ const listResults = getAllByRole('option');
+
+ listResults.forEach((result, index) => {
+ if (index === 0) {
+ expect(result.textContent).to.eq(
+ `Enter your condition as "${searchTerm}"`,
+ );
+ } else {
+ const searchResult = searchResults[index - 1];
+ expect(result.textContent).to.eq(searchResult);
+ }
+ });
});
});
});
describe('Mouse Interactions', () => {
- it('should be able to add a free-text condition', () => {
+ it('should be able to add a free-text condition', async () => {
const searchTerm = 'Tinnitus';
const {
+ container,
getAllByRole,
getByTestId,
getByText,
@@ -285,84 +302,99 @@ describe('Add Disabilities Page', () => {
addAConditionWithMouse(
getAllByRole,
getByTestId,
- getByText,
searchTerm,
`Enter your condition as "${searchTerm}"`,
);
- const savedConditionEditButton = getByText('Edit');
- const savedCondition = getByText(searchTerm);
+ await waitFor(() => {
+ const savedConditionEditButton = getVaButtonByText('Edit', container);
+ const savedCondition = getByText(searchTerm);
- expect(savedConditionEditButton).to.be.visible;
- expect(savedCondition).to.be.visible;
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
- const input = queryByTestId('combobox-input');
- expect(input).to.not.exist;
+ const input = queryByTestId('autocomplete-input');
+ expect(input).to.not.exist;
+ });
});
- it('should be able to select a condition', () => {
+ it('should be able to select a condition', async () => {
const searchTerm = 'Tinn';
const searchResult = 'tinnitus (ringing or hissing in ears)';
- const { getAllByRole, getByTestId, getByText } = createScreen();
+ const {
+ container,
+ getAllByRole,
+ getByTestId,
+ getByText,
+ } = createScreen();
addAConditionWithMouse(
getAllByRole,
getByTestId,
- getByText,
searchTerm,
searchResult,
);
- const savedConditionEditButton = getByText('Edit');
- const savedCondition = getByText(searchResult);
+ await waitFor(() => {
+ const savedConditionEditButton = getVaButtonByText('Edit', container);
+ const savedCondition = getByText(searchResult);
- expect(savedConditionEditButton).to.be.visible;
- expect(savedCondition).to.be.visible;
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
+ });
});
- it('should be able to edit a condition', () => {
+ it('should be able to edit a condition', async () => {
const searchTerm = 'Tinn';
const searchResult = 'tinnitus (ringing or hissing in ears)';
const newSearchTerm = 'Neck strain';
const newSearchResult = 'neck strain (cervical strain)';
- const { getAllByRole, getByTestId, getByText } = createScreen();
+ const {
+ container,
+ getAllByRole,
+ getByTestId,
+ getByText,
+ } = createScreen();
addAConditionWithMouse(
getAllByRole,
getByTestId,
- getByText,
searchTerm,
searchResult,
);
- const savedConditionEditButton = getByText('Edit');
- const savedCondition = getByText(searchResult);
+ await waitFor(() => {
+ const savedConditionEditButton = getVaButtonByText('Edit', container);
+ const savedCondition = getByText(searchResult);
- expect(savedConditionEditButton).to.be.visible;
- expect(savedCondition).to.be.visible;
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
- fireEvent.click(savedConditionEditButton);
+ fireEvent.click(savedConditionEditButton);
+ });
addAConditionWithMouse(
getAllByRole,
getByTestId,
- getByText,
newSearchTerm,
newSearchResult,
);
- const newCondition = getByText(newSearchResult);
- expect(newCondition).to.be.visible;
+ await waitFor(() => {
+ const newCondition = getByText(newSearchResult);
+
+ expect(newCondition).to.be.visible;
+ });
});
- it('should be able to select two conditions then remove one', () => {
+ it('should be able to select two conditions then remove one', async () => {
const searchTerm1 = 'Tinn';
const searchResult1 = 'tinnitus (ringing or hissing in ears)';
const searchTerm2 = 'Hear';
const searchResult2 = 'hearing loss';
const {
+ container,
getAllByRole,
- getAllByText,
getByTestId,
getByText,
queryByText,
@@ -371,40 +403,46 @@ describe('Add Disabilities Page', () => {
addAConditionWithMouse(
getAllByRole,
getByTestId,
- getByText,
searchTerm1,
searchResult1,
);
- const savedConditionEditButton1 = getByText('Edit');
- const savedCondition1 = getByText(searchResult1);
+ await waitFor(() => {
+ const savedConditionEditButton1 = getVaButtonByText('Edit', container);
+ const savedCondition1 = getByText(searchResult1);
- const addAnotherConditionButton = getByText('Add another condition');
- fireEvent.click(addAnotherConditionButton);
+ expect(savedConditionEditButton1).to.be.visible;
+ expect(savedCondition1).to.be.visible;
+
+ const addAnotherConditionButton = getByText('Add another condition');
+ fireEvent.click(addAnotherConditionButton);
+ });
addAConditionWithMouse(
getAllByRole,
getByTestId,
- getByText,
searchTerm2,
searchResult2,
);
- const savedConditionEditButton2 = getAllByText('Edit')[1];
- let savedCondition2 = getByText(searchResult2);
+ await waitFor(() => {
+ const savedConditionEditButton2 = getAllVaButtonsByText(
+ 'Edit',
+ container,
+ )[1];
+ let savedCondition2 = getByText(searchResult2);
- expect(savedConditionEditButton1).to.be.visible;
- expect(savedCondition1).to.be.visible;
- expect(savedConditionEditButton2).to.be.visible;
- expect(savedCondition2).to.be.visible;
+ expect(savedConditionEditButton2).to.be.visible;
+ expect(savedCondition2).to.be.visible;
- fireEvent.click(savedConditionEditButton2);
- const removeButton = getByText('Remove');
- fireEvent.click(removeButton);
+ fireEvent.click(savedConditionEditButton2);
+ const removeButton = getVaButtonByText('Remove', container);
+ fireEvent.click(removeButton);
- savedCondition2 = queryByText(searchResult2);
+ savedCondition2 = queryByText(searchResult2);
- expect(savedCondition2).not.to.exist;
+ expect(savedCondition2).not.to.exist;
+ });
});
it('should submit when form is completed', () => {
@@ -432,9 +470,10 @@ describe('Add Disabilities Page', () => {
});
describe('Keyboard Interactions', () => {
- it('should be able to add a free-text condition', () => {
+ it('should be able to add a free-text condition', async () => {
const searchTerm = 'Tinnitus';
const {
+ container,
getAllByRole,
getByTestId,
getByText,
@@ -449,20 +488,27 @@ describe('Add Disabilities Page', () => {
`Enter your condition as "${searchTerm}"`,
);
- const savedConditionEditButton = getByText('Edit');
- const savedCondition = getByText(searchTerm);
+ await waitFor(() => {
+ const savedConditionEditButton = getVaButtonByText('Edit', container);
+ const savedCondition = getByText(searchTerm);
- expect(savedConditionEditButton).to.be.visible;
- expect(savedCondition).to.be.visible;
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
- const input = queryByTestId('combobox-input');
- expect(input).to.not.exist;
+ const input = queryByTestId('autocomplete-input');
+ expect(input).to.not.exist;
+ });
});
- it('should be able to select a condition', () => {
+ it('should be able to select a condition', async () => {
const searchTerm = 'Tinn';
const searchResult = 'tinnitus (ringing or hissing in ears)';
- const { getAllByRole, getByTestId, getByText } = createScreen();
+ const {
+ container,
+ getAllByRole,
+ getByTestId,
+ getByText,
+ } = createScreen();
addAConditionWithKeyboard(
getAllByRole,
@@ -472,19 +518,26 @@ describe('Add Disabilities Page', () => {
searchResult,
);
- const savedConditionEditButton = getByText('Edit');
- const savedCondition = getByText(searchResult);
+ await waitFor(() => {
+ const savedConditionEditButton = getVaButtonByText('Edit', container);
+ const savedCondition = getByText(searchResult);
- expect(savedConditionEditButton).to.be.visible;
- expect(savedCondition).to.be.visible;
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
+ });
});
- it('should be able to edit a condition', () => {
+ it('should be able to edit a condition', async () => {
const searchTerm = 'Tinn';
const searchResult = 'tinnitus (ringing or hissing in ears)';
const newSearchTerm = 'Neck strain';
const newSearchResult = 'neck strain (cervical strain)';
- const { getAllByRole, getByTestId, getByText } = createScreen();
+ const {
+ container,
+ getAllByRole,
+ getByTestId,
+ getByText,
+ } = createScreen();
addAConditionWithKeyboard(
getAllByRole,
@@ -494,13 +547,15 @@ describe('Add Disabilities Page', () => {
searchResult,
);
- const savedConditionEditButton = getByText('Edit');
- const savedCondition = getByText(searchResult);
+ await waitFor(() => {
+ const savedConditionEditButton = getVaButtonByText('Edit', container);
+ const savedCondition = getByText(searchResult);
- expect(savedConditionEditButton).to.be.visible;
- expect(savedCondition).to.be.visible;
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
- userEvent.type(savedConditionEditButton, '{enter}');
+ userEvent.type(savedConditionEditButton, '{enter}');
+ });
addAConditionWithKeyboard(
getAllByRole,
@@ -509,19 +564,22 @@ describe('Add Disabilities Page', () => {
newSearchTerm,
newSearchResult,
);
- const newCondition = getByText(newSearchResult);
- expect(newCondition).to.be.visible;
+ await waitFor(() => {
+ const newCondition = getByText(newSearchResult);
+
+ expect(newCondition).to.be.visible;
+ });
});
- it('should be able to select two conditions then remove one', () => {
+ it('should be able to select two conditions then remove one', async () => {
const searchTerm1 = 'Tinn';
const searchResult1 = 'tinnitus (ringing or hissing in ears)';
const searchTerm2 = 'Hear';
const searchResult2 = 'hearing loss';
const {
+ container,
getAllByRole,
- getAllByText,
getByTestId,
getByText,
queryByText,
@@ -535,11 +593,16 @@ describe('Add Disabilities Page', () => {
searchResult1,
);
- const savedConditionEditButton1 = getByText('Edit');
- const savedCondition1 = getByText(searchResult1);
+ await waitFor(() => {
+ const savedConditionEditButton1 = getVaButtonByText('Edit', container);
+ const savedCondition1 = getByText(searchResult1);
- const addAnotherConditionButton = getByText('Add another condition');
- userEvent.type(addAnotherConditionButton, '{enter}');
+ expect(savedConditionEditButton1).to.be.visible;
+ expect(savedCondition1).to.be.visible;
+
+ const addAnotherConditionButton = getByText('Add another condition');
+ userEvent.type(addAnotherConditionButton, '{enter}');
+ });
addAConditionWithKeyboard(
getAllByRole,
@@ -549,21 +612,24 @@ describe('Add Disabilities Page', () => {
searchResult2,
);
- const savedConditionEditButton2 = getAllByText('Edit')[1];
- let savedCondition2 = getByText(searchResult2);
+ await waitFor(() => {
+ const savedConditionEditButton2 = getAllVaButtonsByText(
+ 'Edit',
+ container,
+ )[1];
+ let savedCondition2 = getByText(searchResult2);
- expect(savedConditionEditButton1).to.be.visible;
- expect(savedCondition1).to.be.visible;
- expect(savedConditionEditButton2).to.be.visible;
- expect(savedCondition2).to.be.visible;
+ expect(savedConditionEditButton2).to.be.visible;
+ expect(savedCondition2).to.be.visible;
- userEvent.type(savedConditionEditButton2, '{enter}');
- const removeButton = getByText('Remove');
- userEvent.type(removeButton, '{enter}');
+ userEvent.type(savedConditionEditButton2, '{enter}');
+ const removeButton = getVaButtonByText('Remove', container);
+ userEvent.type(removeButton, '{enter}');
- savedCondition2 = queryByText(searchResult2);
+ savedCondition2 = queryByText(searchResult2);
- expect(savedCondition2).not.to.exist;
+ expect(savedCondition2).not.to.exist;
+ });
});
it('should submit when form is completed', () => {
@@ -591,25 +657,10 @@ describe('Add Disabilities Page', () => {
});
describe('Accessibility', () => {
- it('should provide screen reader feedback when autocomplete results are available', () => {
- const searchTerm = 'asthma';
- const searchResults = fullStringSimilaritySearch(searchTerm, items);
- const resultsCount = searchResults.length + 1;
- const { getByTestId, getByText } = createScreen();
-
- const input = getByTestId('combobox-input');
- simulateInputChange(input, searchTerm);
-
- const screenReaderMessage = getByText(
- `${resultsCount} results available.`,
- );
- expect(screenReaderMessage).to.have.attribute('role', 'alert');
- });
-
it('should announce errors to screen readers when a required field is not filled', () => {
const { getByTestId, getByText } = createScreen();
- const input = getByTestId('combobox-input');
+ const input = getByTestId('autocomplete-input');
const submitButton = getByText('Submit');
simulateInputChange(input, '');
fireEvent.click(submitButton);
diff --git a/src/applications/disability-benefits/all-claims/tests/pages/addDisabilitiesPrevious.unit.spec.jsx b/src/applications/disability-benefits/all-claims/tests/pages/addDisabilitiesPrevious.unit.spec.jsx
new file mode 100644
index 000000000000..1e648e45aead
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/tests/pages/addDisabilitiesPrevious.unit.spec.jsx
@@ -0,0 +1,737 @@
+import { fireEvent, render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { expect } from 'chai';
+import { fullStringSimilaritySearch } from 'platform/forms-system/src/js/utilities/addDisabilitiesStringSearch';
+import { DefinitionTester } from 'platform/testing/unit/schemaform-utils';
+import set from 'platform/utilities/data/set';
+import React from 'react';
+import sinon from 'sinon';
+
+import formConfig from '../../config/form';
+import disabilityLabelsRevised from '../../content/disabilityLabelsRevised';
+import { updateFormData } from '../../pages/addDisabilitiesPrevious';
+
+const items = Object.values(disabilityLabelsRevised);
+
+const {
+ schema,
+ uiSchema,
+} = formConfig.chapters.disabilities.pages.addDisabilitiesPrevious;
+
+const createScreen = (
+ claimingNew = true,
+ claimingIncrease = false,
+ condition = null,
+) => {
+ const onSubmit = sinon.spy();
+
+ return render(
+
,
+ );
+};
+
+const simulateInputChange = (selector, value) => {
+ const vaTextInput = selector;
+ vaTextInput.value = value;
+
+ const event = new Event('input', {
+ bubbles: true,
+ });
+
+ vaTextInput.dispatchEvent(event);
+};
+
+const addAConditionWithMouse = (
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm,
+ searchResult,
+) => {
+ const input = getByTestId('combobox-input');
+ simulateInputChange(input, searchTerm);
+
+ const listboxItems = getAllByRole('option');
+ const freeTextItem = listboxItems.find(
+ item => item.textContent === searchResult,
+ );
+ fireEvent.click(freeTextItem);
+
+ const saveButton = getByText('Save');
+ fireEvent.click(saveButton);
+};
+
+const addAConditionWithKeyboard = (
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm,
+ searchResult,
+) => {
+ const input = getByTestId('combobox-input');
+ simulateInputChange(input, searchTerm);
+
+ fireEvent.keyDown(input, { key: 'ArrowDown' });
+
+ const listboxItems = getAllByRole('option');
+
+ for (const item of listboxItems) {
+ if (item.textContent !== searchResult) {
+ fireEvent.keyDown(item, { key: 'ArrowDown' });
+ } else if (item.textContent === searchResult) {
+ fireEvent.keyDown(input, { key: 'Enter' });
+ break;
+ }
+ }
+
+ const saveButton = getByText('Save');
+ fireEvent.click(saveButton);
+};
+
+describe('Add Disabilities Page', () => {
+ describe('Default Rendering', () => {
+ it('should render page heading and directions', () => {
+ const { getByRole, getByText } = createScreen();
+
+ const heading = getByRole('heading', {
+ name: 'Tell us the new conditions you want to claim',
+ });
+ const directions = getByText(
+ 'Enter the name of your condition. Then, select your condition from the list of possible matches.',
+ );
+
+ expect(heading).to.be.visible;
+ expect(directions).to.be.visible;
+ });
+
+ it('should render "If conditions aren\'t listed" subheading and details', () => {
+ const { getByRole, getByText } = createScreen();
+
+ const notListedSubHeading = getByRole('heading', {
+ name: 'If your conditions aren’t listed',
+ });
+ const notListedDetails = getByText(
+ 'You can claim a condition that isn’t listed. Enter your condition, diagnosis, or short description of your symptoms.',
+ );
+
+ expect(notListedSubHeading).to.be.visible;
+ expect(notListedDetails).to.be.visible;
+ });
+
+ it('should render examples subheading and list', () => {
+ const { getByRole, getByText } = createScreen();
+
+ const examplesSubHeading = getByRole('heading', {
+ name: 'Examples of conditions',
+ });
+ const examples = getByRole('list').children;
+ const example1 = getByText('Tinnitus (ringing or hissing in ears)');
+ const example2 = getByText('PTSD (post-traumatic stress disorder)');
+ const example3 = getByText('Hearing loss');
+ const example4 = getByText('Neck strain (cervical strain)');
+ const example5 = getByText('Ankylosis in knee, right');
+ const example6 = getByText('Hypertension (high blood pressure)');
+ const example7 = getByText('Migraines (headaches)');
+
+ expect(examplesSubHeading).to.be.visible;
+ expect(examples.length).to.eq(7);
+ expect(example1).to.be.visible;
+ expect(example2).to.be.visible;
+ expect(example3).to.be.visible;
+ expect(example4).to.be.visible;
+ expect(example5).to.be.visible;
+ expect(example6).to.be.visible;
+ expect(example7).to.be.visible;
+ });
+
+ it('should render "Your new conditions" subheading, ComboBox, save button, and add another condition button', () => {
+ const { getByRole, getByTestId, getByText } = createScreen();
+
+ const newConditionsSubHeading = getByRole('heading', {
+ name: 'Your new conditions',
+ });
+ const input = getByTestId('combobox-input');
+ const saveButton = getByText('Save');
+ const addAnotherConditionButton = getByText('Add another condition');
+
+ expect(newConditionsSubHeading).to.be.visible;
+ expect(input).to.be.visible;
+ expect(saveButton).to.be.visible;
+ expect(addAnotherConditionButton).to.be.visible;
+ });
+
+ it('should render with no saved conditions by default', () => {
+ const { queryByText } = createScreen();
+
+ const savedConditionEditButton = queryByText('Edit');
+
+ expect(savedConditionEditButton).to.not.exist;
+ });
+
+ it('should render ComboBox label with required, input, and listbox', () => {
+ const { getByRole, getByText, getByTestId } = createScreen();
+
+ const label = getByText('Enter your condition');
+ const required = label.querySelector('span').textContent;
+ const input = getByTestId('combobox-input');
+ const listbox = getByRole('listbox');
+
+ expect(label).to.be.visible;
+ expect(required).to.eq('(*Required)');
+ expect(input).to.be.visible;
+ expect(listbox).to.be.visible;
+ expect(listbox).to.have.length(0);
+ });
+
+ it('should render error message on item and alert on page if no new conditions are added', () => {
+ const { getByText } = createScreen();
+
+ const submitButton = getByText('Submit');
+ fireEvent.click(submitButton);
+
+ const errorMessage = getByText(
+ 'Enter a condition, diagnosis, or short description of your symptoms',
+ );
+ const alertHeading = getByText('Enter a condition to submit your claim');
+ const alertText = getByText(
+ 'You’ll need to enter a condition, diagnosis, or short description of your symptoms to submit your claim.',
+ );
+
+ expect(errorMessage).to.be.visible;
+ expect(alertHeading).to.be.visible;
+ expect(alertText).to.be.visible;
+ });
+ });
+
+ describe('Updating State', () => {
+ it('should render with saved condition when there is initial formData', () => {
+ const { getByText } = createScreen(true, false, [
+ {
+ condition: 'asthma',
+ },
+ ]);
+
+ const savedCondition = getByText('asthma');
+ const savedConditionEditButton = getByText('Edit');
+
+ expect(savedCondition).to.be.visible;
+ expect(savedConditionEditButton).to.be.visible;
+ });
+
+ it('should be able to add value to ComboBox input ', () => {
+ const searchTerm = 'Typed value';
+ const searchResults = fullStringSimilaritySearch(searchTerm, items);
+ const freeTextAndFilteredItemsCount = searchResults.length + 1;
+ const { getByRole, getByTestId } = createScreen();
+
+ const input = getByTestId('combobox-input');
+ simulateInputChange(input, searchTerm);
+ const listbox = getByRole('listbox');
+
+ expect(listbox).to.have.length(freeTextAndFilteredItemsCount);
+
+ fireEvent.click(document);
+
+ expect(listbox).to.have.length(0);
+ expect(input).to.have.value(searchTerm);
+ });
+
+ it('should render ComboBox listbox items in alignment with string similarity search', () => {
+ const searchTerm = 'ACL';
+ const searchResults = fullStringSimilaritySearch(searchTerm, items);
+ const { getAllByRole, getByTestId } = createScreen();
+
+ const input = getByTestId('combobox-input');
+ simulateInputChange(input, searchTerm);
+ const listboxItems = getAllByRole('option');
+
+ listboxItems.forEach((item, index) => {
+ if (index === 0) {
+ expect(item.textContent).to.eq(
+ `Enter your condition as "${searchTerm}"`,
+ );
+ } else {
+ const searchResult = searchResults[index - 1];
+ expect(item.textContent).to.eq(searchResult);
+ }
+ });
+ });
+ });
+
+ describe('Mouse Interactions', () => {
+ it('should be able to add a free-text condition', () => {
+ const searchTerm = 'Tinnitus';
+ const {
+ getAllByRole,
+ getByTestId,
+ getByText,
+ queryByTestId,
+ } = createScreen();
+
+ addAConditionWithMouse(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm,
+ `Enter your condition as "${searchTerm}"`,
+ );
+
+ const savedConditionEditButton = getByText('Edit');
+ const savedCondition = getByText(searchTerm);
+
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
+
+ const input = queryByTestId('combobox-input');
+ expect(input).to.not.exist;
+ });
+
+ it('should be able to select a condition', () => {
+ const searchTerm = 'Tinn';
+ const searchResult = 'tinnitus (ringing or hissing in ears)';
+ const { getAllByRole, getByTestId, getByText } = createScreen();
+
+ addAConditionWithMouse(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm,
+ searchResult,
+ );
+
+ const savedConditionEditButton = getByText('Edit');
+ const savedCondition = getByText(searchResult);
+
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
+ });
+
+ it('should be able to edit a condition', () => {
+ const searchTerm = 'Tinn';
+ const searchResult = 'tinnitus (ringing or hissing in ears)';
+ const newSearchTerm = 'Neck strain';
+ const newSearchResult = 'neck strain (cervical strain)';
+ const { getAllByRole, getByTestId, getByText } = createScreen();
+
+ addAConditionWithMouse(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm,
+ searchResult,
+ );
+
+ const savedConditionEditButton = getByText('Edit');
+ const savedCondition = getByText(searchResult);
+
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
+
+ fireEvent.click(savedConditionEditButton);
+
+ addAConditionWithMouse(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ newSearchTerm,
+ newSearchResult,
+ );
+ const newCondition = getByText(newSearchResult);
+
+ expect(newCondition).to.be.visible;
+ });
+
+ it('should be able to select two conditions then remove one', () => {
+ const searchTerm1 = 'Tinn';
+ const searchResult1 = 'tinnitus (ringing or hissing in ears)';
+ const searchTerm2 = 'Hear';
+ const searchResult2 = 'hearing loss';
+ const {
+ getAllByRole,
+ getAllByText,
+ getByTestId,
+ getByText,
+ queryByText,
+ } = createScreen();
+
+ addAConditionWithMouse(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm1,
+ searchResult1,
+ );
+
+ const savedConditionEditButton1 = getByText('Edit');
+ const savedCondition1 = getByText(searchResult1);
+
+ const addAnotherConditionButton = getByText('Add another condition');
+ fireEvent.click(addAnotherConditionButton);
+
+ addAConditionWithMouse(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm2,
+ searchResult2,
+ );
+
+ const savedConditionEditButton2 = getAllByText('Edit')[1];
+ let savedCondition2 = getByText(searchResult2);
+
+ expect(savedConditionEditButton1).to.be.visible;
+ expect(savedCondition1).to.be.visible;
+ expect(savedConditionEditButton2).to.be.visible;
+ expect(savedCondition2).to.be.visible;
+
+ fireEvent.click(savedConditionEditButton2);
+ const removeButton = getByText('Remove');
+ fireEvent.click(removeButton);
+
+ savedCondition2 = queryByText(searchResult2);
+
+ expect(savedCondition2).not.to.exist;
+ });
+
+ it('should submit when form is completed', () => {
+ const { getByText, queryByText } = createScreen(true, false, [
+ {
+ cause: 'NEW',
+ condition: 'asthma',
+ 'view:descriptionInfo': {},
+ },
+ ]);
+
+ const submitButton = getByText('Submit');
+ fireEvent.click(submitButton);
+
+ const errorMessage = queryByText(
+ 'Enter a condition, diagnosis, or short description of your symptoms',
+ );
+ const alertHeading = queryByText(
+ 'Enter a condition to submit your claim',
+ );
+
+ expect(errorMessage).not.to.exist;
+ expect(alertHeading).not.to.exist;
+ });
+ });
+
+ describe('Keyboard Interactions', () => {
+ it('should be able to add a free-text condition', () => {
+ const searchTerm = 'Tinnitus';
+ const {
+ getAllByRole,
+ getByTestId,
+ getByText,
+ queryByTestId,
+ } = createScreen();
+
+ addAConditionWithKeyboard(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm,
+ `Enter your condition as "${searchTerm}"`,
+ );
+
+ const savedConditionEditButton = getByText('Edit');
+ const savedCondition = getByText(searchTerm);
+
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
+
+ const input = queryByTestId('combobox-input');
+ expect(input).to.not.exist;
+ });
+
+ it('should be able to select a condition', () => {
+ const searchTerm = 'Tinn';
+ const searchResult = 'tinnitus (ringing or hissing in ears)';
+ const { getAllByRole, getByTestId, getByText } = createScreen();
+
+ addAConditionWithKeyboard(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm,
+ searchResult,
+ );
+
+ const savedConditionEditButton = getByText('Edit');
+ const savedCondition = getByText(searchResult);
+
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
+ });
+
+ it('should be able to edit a condition', () => {
+ const searchTerm = 'Tinn';
+ const searchResult = 'tinnitus (ringing or hissing in ears)';
+ const newSearchTerm = 'Neck strain';
+ const newSearchResult = 'neck strain (cervical strain)';
+ const { getAllByRole, getByTestId, getByText } = createScreen();
+
+ addAConditionWithKeyboard(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm,
+ searchResult,
+ );
+
+ const savedConditionEditButton = getByText('Edit');
+ const savedCondition = getByText(searchResult);
+
+ expect(savedConditionEditButton).to.be.visible;
+ expect(savedCondition).to.be.visible;
+
+ userEvent.type(savedConditionEditButton, '{enter}');
+
+ addAConditionWithKeyboard(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ newSearchTerm,
+ newSearchResult,
+ );
+ const newCondition = getByText(newSearchResult);
+
+ expect(newCondition).to.be.visible;
+ });
+
+ it('should be able to select two conditions then remove one', () => {
+ const searchTerm1 = 'Tinn';
+ const searchResult1 = 'tinnitus (ringing or hissing in ears)';
+ const searchTerm2 = 'Hear';
+ const searchResult2 = 'hearing loss';
+ const {
+ getAllByRole,
+ getAllByText,
+ getByTestId,
+ getByText,
+ queryByText,
+ } = createScreen();
+
+ addAConditionWithKeyboard(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm1,
+ searchResult1,
+ );
+
+ const savedConditionEditButton1 = getByText('Edit');
+ const savedCondition1 = getByText(searchResult1);
+
+ const addAnotherConditionButton = getByText('Add another condition');
+ userEvent.type(addAnotherConditionButton, '{enter}');
+
+ addAConditionWithKeyboard(
+ getAllByRole,
+ getByTestId,
+ getByText,
+ searchTerm2,
+ searchResult2,
+ );
+
+ const savedConditionEditButton2 = getAllByText('Edit')[1];
+ let savedCondition2 = getByText(searchResult2);
+
+ expect(savedConditionEditButton1).to.be.visible;
+ expect(savedCondition1).to.be.visible;
+ expect(savedConditionEditButton2).to.be.visible;
+ expect(savedCondition2).to.be.visible;
+
+ userEvent.type(savedConditionEditButton2, '{enter}');
+ const removeButton = getByText('Remove');
+ userEvent.type(removeButton, '{enter}');
+
+ savedCondition2 = queryByText(searchResult2);
+
+ expect(savedCondition2).not.to.exist;
+ });
+
+ it('should submit when form is completed', () => {
+ const { getByText, queryByText } = createScreen(true, false, [
+ {
+ cause: 'NEW',
+ condition: 'asthma',
+ 'view:descriptionInfo': {},
+ },
+ ]);
+
+ const submitButton = getByText('Submit');
+ userEvent.type(submitButton, '{enter}');
+
+ const errorMessage = queryByText(
+ 'Enter a condition, diagnosis, or short description of your symptoms',
+ );
+ const alertHeading = queryByText(
+ 'Enter a condition to submit your claim',
+ );
+
+ expect(errorMessage).not.to.exist;
+ expect(alertHeading).not.to.exist;
+ });
+ });
+
+ describe('Accessibility', () => {
+ it('should provide screen reader feedback when autocomplete results are available', () => {
+ const searchTerm = 'asthma';
+ const searchResults = fullStringSimilaritySearch(searchTerm, items);
+ const resultsCount = searchResults.length + 1;
+ const { getByTestId, getByText } = createScreen();
+
+ const input = getByTestId('combobox-input');
+ simulateInputChange(input, searchTerm);
+
+ const screenReaderMessage = getByText(
+ `${resultsCount} results available.`,
+ );
+ expect(screenReaderMessage).to.have.attribute('role', 'alert');
+ });
+
+ it('should announce errors to screen readers when a required field is not filled', () => {
+ const { getByTestId, getByText } = createScreen();
+
+ const input = getByTestId('combobox-input');
+ const submitButton = getByText('Submit');
+ simulateInputChange(input, '');
+ fireEvent.click(submitButton);
+
+ const errorMessage = getByText(
+ 'Enter a condition, diagnosis, or short description of your symptoms',
+ );
+ expect(errorMessage).to.have.attribute('role', 'alert');
+ });
+ });
+
+ describe('User is claiming a new condition AND claiming an increase', () => {
+ it('should display alert on page if no conditions exist', () => {
+ const { getByText } = createScreen(true, true, null);
+
+ const submitButton = getByText('Submit');
+ fireEvent.click(submitButton);
+
+ const alertHeading = getByText('We need you to add a condition');
+ const alertText = getByText(
+ 'You’ll need to add a new condition or choose a rated disability to claim. We can’t process your claim without a disability or new condition selected. Please add a new condition or choose a rated disability for increased compensation.',
+ );
+ const disabilityLink = getByText('Choose a rated disability');
+
+ expect(alertHeading).to.exist;
+ expect(alertText).to.exist;
+ expect(disabilityLink).to.exist;
+ });
+
+ it('should display helpful error if no new conditions are added', () => {
+ const { getByText } = createScreen(true, true, null);
+
+ const submitButton = getByText('Submit');
+ fireEvent.click(submitButton);
+
+ const alertHeading = getByText('We need you to add a condition');
+ const alertText = getByText(
+ 'You’ll need to add a new condition or choose a rated disability to claim. We can’t process your claim without a disability or new condition selected. Please add a new condition or choose a rated disability for increased compensation.',
+ );
+ const disabilityLink = getByText('Choose a rated disability');
+
+ expect(alertHeading).to.exist;
+ expect(alertText).to.exist;
+ expect(disabilityLink).to.exist;
+ });
+ });
+
+ describe('Update Form Data', () => {
+ const generateInitialData = () => ({
+ newDisabilities: [{ condition: 'Something with-hyphens and ALLCAPS' }],
+ vaTreatmentFacilities: [
+ {
+ treatedDisabilityNames: {
+ somethingwithhyphensandallcaps: true,
+ },
+ },
+ ],
+ 'view:isPow': {
+ powDisabilities: { somethingwithhyphensandallcaps: true },
+ },
+ });
+
+ it("if newDisabilities in initialData doesn't exist", () => {
+ const initialData = {};
+ const newData = { newDisabilities: ['asthma'] };
+
+ expect(updateFormData(initialData, newData)).to.eql(newData);
+ });
+
+ it("if newDisabilities in newData doesn't exist", () => {
+ const initialData = generateInitialData();
+ const newData = {};
+
+ expect(updateFormData(initialData, newData)).to.eql(newData);
+ });
+
+ it('if no disabilities changed', () => {
+ const initialData = generateInitialData();
+
+ expect(updateFormData(initialData, initialData)).to.eql(initialData);
+ });
+
+ it('should not modify initialData', () => {
+ const initialData = generateInitialData();
+
+ updateFormData(
+ initialData,
+ set(
+ 'newDisabilities[1]',
+ { condition: 'Something else' },
+ generateInitialData(),
+ ),
+ );
+
+ expect(initialData).to.eql(generateInitialData());
+ });
+
+ it('should change the property name in treatedDisabilityNames and powDisabilities when a disability name is changed', () => {
+ const initialData = generateInitialData();
+
+ const newData = set(
+ 'newDisabilities[0].condition',
+ 'Foo-with EXTRAz',
+ generateInitialData(),
+ );
+ const result = updateFormData(initialData, newData);
+
+ expect(
+ result.vaTreatmentFacilities[0].treatedDisabilityNames.foowithextraz,
+ ).to.be.true;
+ expect(result['view:isPow'].powDisabilities.foowithextraz).to.be.true;
+ });
+
+ it('should remove a deleted disability from treatedDisabilityNames and powDisabilities', () => {
+ const newData = Object.assign(generateInitialData(), {
+ newDisabilities: [],
+ });
+ const result = updateFormData(generateInitialData(), newData);
+
+ expect(result.vaTreatmentFacilities[0].treatedDisabilityNames).to.be
+ .empty;
+ expect(result['view:isPow'].powDisabilities).to.be.empty;
+ });
+ });
+});
diff --git a/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorListPage.unit.spec.js b/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorListPage.unit.spec.js
index 3150bfc4757f..d3ace6a6a5c7 100644
--- a/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorListPage.unit.spec.js
+++ b/src/applications/disability-benefits/all-claims/tests/pages/form0781/behaviorListPage.unit.spec.js
@@ -1,5 +1,7 @@
import { expect } from 'chai';
+import sinon from 'sinon';
import * as behaviorListPage from '../../../pages/form0781/behaviorListPage';
+import { validateBehaviorSelections } from '../../../content/form0781/behaviorListPages';
describe('Behavior List Page', () => {
it('should define a uiSchema object', () => {
@@ -10,3 +12,112 @@ describe('Behavior List Page', () => {
expect(behaviorListPage.schema).to.be.an('object');
});
});
+
+describe('validateBehaviorSelections', () => {
+ describe('invalid: selections required', () => {
+ it('should add error when nothing is selected', () => {
+ const errors = {
+ 'view:optOut': {
+ addError: sinon.spy(),
+ },
+ };
+ const formData = {
+ syncModern0781Flow: true,
+ workBehaviors: {
+ absences: false,
+ },
+ unlistedBehaviors: null,
+ 'view:optOut': { none: false },
+ };
+
+ validateBehaviorSelections(errors, formData);
+ expect(errors['view:optOut'].addError.called).to.be.true;
+ });
+ });
+
+ describe('invalid: conflicting selections', () => {
+ it('should add error when selecting none and selecting a behavior', () => {
+ const errors = {
+ 'view:optOut': {
+ addError: sinon.spy(),
+ },
+ };
+
+ const formData = {
+ syncModern0781Flow: true,
+ workBehaviors: {
+ reassignment: true,
+ absences: false,
+ },
+ 'view:optOut': { none: true },
+ };
+
+ validateBehaviorSelections(errors, formData);
+ expect(errors['view:optOut'].addError.called).to.be.true;
+ });
+
+ it('should add error when selecting none and entering an unlisted behavior', () => {
+ const errors = {
+ 'view:optOut': {
+ addError: sinon.spy(),
+ },
+ };
+
+ const formData = {
+ syncModern0781Flow: true,
+ workBehaviors: {
+ reassignment: false,
+ absences: false,
+ },
+ unlistedBehaviors: 'another behavior',
+ 'view:optOut': { none: true },
+ };
+
+ validateBehaviorSelections(errors, formData);
+ expect(errors['view:optOut'].addError.called).to.be.true;
+ });
+ });
+
+ describe('valid selections', () => {
+ it('should not add error when selecting none and with no other selected behaviors', () => {
+ const errors = {
+ 'view:optOut': {
+ addError: sinon.spy(),
+ },
+ };
+
+ const formData = {
+ syncModern0781Flow: true,
+ workBehaviors: {
+ reassignment: false,
+ absences: false,
+ },
+ 'view:optOut': { none: true },
+ };
+
+ validateBehaviorSelections(errors, formData);
+ expect(errors['view:optOut'].addError.called).to.be.false;
+ });
+
+ it('should not add error when behaviors are selected and none is unselected', () => {
+ const errors = {
+ 'view:optOut': {
+ addError: sinon.spy(),
+ },
+ };
+
+ const formData = {
+ syncModern0781Flow: true,
+ workBehaviors: {
+ reassignment: false,
+ absences: true,
+ },
+ unlistedBehaviors: 'another behavior',
+ 'view:optOut': { none: false },
+ };
+
+ validateBehaviorSelections(errors, formData);
+ expect(errors['view:optOut'].addError.called).to.be.false;
+ });
+ });
+});
diff --git a/src/applications/disability-benefits/all-claims/tests/property-names.unit.spec.js b/src/applications/disability-benefits/all-claims/tests/property-names.unit.spec.js
index 70f242ee59fe..dc88ea6cb552 100644
--- a/src/applications/disability-benefits/all-claims/tests/property-names.unit.spec.js
+++ b/src/applications/disability-benefits/all-claims/tests/property-names.unit.spec.js
@@ -60,6 +60,10 @@ describe('Root property names', () => {
'view:hasEvidence',
'view:selectableEvidenceTypes',
'view:evidenceTypeHelp',
+ // TODO https://github.com/department-of-veterans-affairs/vagov-claim-classification/issues/671:
+ // When remove allClaimsAddDisabilitiesEnhancement FF, remove 'newDisabilities' and 'view:newDisabilityErrors as properties
+ 'newDisabilities',
+ 'view:newDisabilityErrors',
];
Object.keys(pages).forEach(pageName => {
diff --git a/src/applications/disability-benefits/all-claims/utils/index.jsx b/src/applications/disability-benefits/all-claims/utils/index.jsx
index 5de386586933..f8eba1444cbc 100644
--- a/src/applications/disability-benefits/all-claims/utils/index.jsx
+++ b/src/applications/disability-benefits/all-claims/utils/index.jsx
@@ -16,6 +16,7 @@ import {
import FEATURE_FLAG_NAMES from '@department-of-veterans-affairs/platform-utilities/featureFlagNames';
import { VaBreadcrumbs } from '@department-of-veterans-affairs/component-library/dist/react-bindings';
import {
+ ADD_DISABILITIES_ENHANCEMENT_DATA,
DATA_PATHS,
DISABILITY_526_V2_ROOT_URL,
HOMELESSNESS_TYPES,
@@ -604,6 +605,9 @@ export const isUploadingSTR = formData =>
false,
);
+export const getAddDisabilitiesEnhancement = formData =>
+ formData[ADD_DISABILITIES_ENHANCEMENT_DATA];
+
export const DISABILITY_SHARED_CONFIG = {
orientation: {
path: 'disabilities/orientation',
@@ -614,9 +618,18 @@ export const DISABILITY_SHARED_CONFIG = {
path: 'disabilities/rated-disabilities',
depends: formData => isClaimingIncrease(formData),
},
- addDisabilities: {
+ // TODO https://github.com/department-of-veterans-affairs/vagov-claim-classification/issues/671:
+ // When remove allClaimsAddDisabilitiesEnhancement FF, move the content of '/add-3' to '/add'
+ // The 3 in the temporary URL '/add-3' is a reference to this new content being the 3rd major version of this page
+ addDisabilitiesPrevious: {
path: 'new-disabilities/add',
- depends: isClaimingNew,
+ depends: formData =>
+ isClaimingNew(formData) && !getAddDisabilitiesEnhancement(formData),
+ },
+ addDisabilities: {
+ path: 'new-disabilities/add-3',
+ depends: formData =>
+ isClaimingNew(formData) && getAddDisabilitiesEnhancement(formData),
},
};
@@ -854,3 +867,29 @@ export const formatFullName = (fullName = {}) => {
return res.trim();
};
+
+/**
+ * TODO https://github.com/department-of-veterans-affairs/vagov-claim-classification/issues/671:
+ * When remove allClaimsAddDisabilitiesEnhancement, update this function to route users from '/new-disabilities/add-3' to '/new-disabilities/add'
+ * Remove this function completely when there are no more save in progress forms remaining on the 'new-disabilities/add-3' page.
+ * @param {Object} formData - Form data from save-in-progress
+ * @param {String} returnUrl - URL of last saved page
+ * @param {Object} router - React router
+ */
+export const onFormLoaded = props => {
+ const { formData, returnUrl, router } = props;
+
+ if (
+ getAddDisabilitiesEnhancement(formData) &&
+ returnUrl === '/new-disabilities/add'
+ ) {
+ router?.push('/new-disabilities/add-3');
+ } else if (
+ !getAddDisabilitiesEnhancement(formData) &&
+ returnUrl === '/new-disabilities/add-3'
+ ) {
+ router?.push('/new-disabilities/add');
+ } else {
+ router?.push(returnUrl);
+ }
+};
diff --git a/src/applications/edu-benefits/10216/config/submit-transformer.js b/src/applications/edu-benefits/10216/config/submit-transformer.js
index def1db7983fc..ac1950498feb 100644
--- a/src/applications/edu-benefits/10216/config/submit-transformer.js
+++ b/src/applications/edu-benefits/10216/config/submit-transformer.js
@@ -11,6 +11,10 @@ export function transform(formConfig, form) {
...clonedData,
studentRatioCalcChapter: {
...clonedData.studentRatioCalcChapter,
+ beneficiaryStudent: Number(
+ clonedData.studentRatioCalcChapter.beneficiaryStudent,
+ ),
+ numOfStudent: Number(clonedData.studentRatioCalcChapter.numOfStudent),
VABeneficiaryStudentsPercentage: calculatedPercentage(clonedData),
},
};
@@ -23,6 +27,7 @@ export function transform(formConfig, form) {
(formData, transformer) => transformer(formData),
form.data,
);
+
return JSON.stringify({
educationBenefitsClaim: {
form: transformedData,
diff --git a/src/applications/edu-benefits/10216/tests/10216-keyboard-only.cypress.spec.js b/src/applications/edu-benefits/10216/tests/10216-keyboard-only.cypress.spec.js
index 160e6d1e2876..3a6777d7c17c 100644
--- a/src/applications/edu-benefits/10216/tests/10216-keyboard-only.cypress.spec.js
+++ b/src/applications/edu-benefits/10216/tests/10216-keyboard-only.cypress.spec.js
@@ -37,9 +37,11 @@ describe('22-10216 Edu form', () => {
);
cy.injectAxeThenAxeCheck();
cy.tabToElement('input[name="root_institutionDetails_institutionName"]');
- cy.typeInFocused('AVEDA INSTITUTE MARYLAND');
+ cy.typeInFocused(
+ 'DEPARTMENT OF VETERANS AFFAIRS-OFFICE OF INFORMATION AND TECHNOLOGY',
+ );
cy.tabToElement('input[name="root_institutionDetails_facilityCode"]');
- cy.typeInFocused(11111111);
+ cy.typeInFocused('10B35423');
cy.tabToElement(
'select[name="root_institutionDetails_termStartDateMonth"]',
);
diff --git a/src/applications/edu-benefits/10216/tests/config/submit-transformer.unit.spec.js b/src/applications/edu-benefits/10216/tests/config/submit-transformer.unit.spec.js
index dd4fc2319d58..c6b2e7fa4a19 100644
--- a/src/applications/edu-benefits/10216/tests/config/submit-transformer.unit.spec.js
+++ b/src/applications/edu-benefits/10216/tests/config/submit-transformer.unit.spec.js
@@ -30,8 +30,8 @@ describe('transform function', () => {
startDate: '2024-01-01',
},
studentRatioCalcChapter: {
- numOfStudent: '100',
- beneficiaryStudent: '75',
+ numOfStudent: 100,
+ beneficiaryStudent: 75,
VABeneficiaryStudentsPercentage: '75.0%',
},
}),
diff --git a/src/applications/mhv-medical-records/reducers/blueButton.js b/src/applications/mhv-medical-records/reducers/blueButton.js
index eba645e858df..c0b0619da8e6 100644
--- a/src/applications/mhv-medical-records/reducers/blueButton.js
+++ b/src/applications/mhv-medical-records/reducers/blueButton.js
@@ -65,8 +65,8 @@ export const convertMedication = med => {
id: med.id,
type: medicationTypes.VA,
prescriptionName: attributes.prescriptionName,
- lastFilledOn: attributes.dispensedDate
- ? formatDateLong(attributes.dispensedDate)
+ lastFilledOn: attributes.sortedDispensedDate
+ ? formatDateLong(attributes.sortedDispensedDate)
: 'Not filled yet',
status: attributes.refillStatus,
refillsLeft: attributes.refillRemaining ?? UNKNOWN,
diff --git a/src/applications/mhv-medical-records/tests/reducers/blueButton.unit.spec.js b/src/applications/mhv-medical-records/tests/reducers/blueButton.unit.spec.js
index 6e7de55fea55..71636be92e90 100644
--- a/src/applications/mhv-medical-records/tests/reducers/blueButton.unit.spec.js
+++ b/src/applications/mhv-medical-records/tests/reducers/blueButton.unit.spec.js
@@ -20,7 +20,7 @@ describe('convertMedication', () => {
id: '123',
attributes: {
prescriptionName: 'Aspirin',
- dispensedDate: '2021-01-01',
+ sortedDispensedDate: '2021-01-01',
refillStatus: 'Active',
refillRemaining: 2,
prescriptionNumber: 'RX123456',
@@ -347,7 +347,7 @@ describe('blueButtonReducer', () => {
id: 'med1',
attributes: {
prescriptionName: 'Medication1',
- dispensedDate: '2021-01-01',
+ sortedDispensedDate: '2021-01-01',
},
},
],
diff --git a/src/applications/proxy-rewrite/proxy-rewrite-whitelist.json b/src/applications/proxy-rewrite/proxy-rewrite-whitelist.json
index 0f140dcd4265..3c48fb889017 100644
--- a/src/applications/proxy-rewrite/proxy-rewrite-whitelist.json
+++ b/src/applications/proxy-rewrite/proxy-rewrite-whitelist.json
@@ -103,12 +103,12 @@
{
"hostname": "www.mirecc.va.gov",
"pathnameBeginning": "/",
- "cookieOnly": true
+ "cookieOnly": false
},
{
"hostname": "mirecc.va.gov",
"pathnameBeginning": "/",
- "cookieOnly": true
+ "cookieOnly": false
},
{
"hostname": "www.move.va.gov",
diff --git a/src/applications/simple-forms/20-10206/containers/IntroductionPage.jsx b/src/applications/simple-forms/20-10206/containers/IntroductionPage.jsx
index f7630b2913ff..6a2c78e2941a 100644
--- a/src/applications/simple-forms/20-10206/containers/IntroductionPage.jsx
+++ b/src/applications/simple-forms/20-10206/containers/IntroductionPage.jsx
@@ -4,8 +4,8 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { isLOA3, isLoggedIn } from 'platform/user/selectors';
+import VerifyAlert from 'platform/user/authorization/components/VerifyAlert';
import { IntroductionPageView } from '../../shared/components/IntroductionPageView';
-import manifest from '../manifest.json';
import { SUBTITLE, TITLE } from '../config/constants';
const ombInfo = {
@@ -81,26 +81,7 @@ export const IntroductionPage = ({ route, userIdVerified, userLoggedIn }) => {
{userLoggedIn &&
!userIdVerified /* If User's signed-in but not identity-verified [not LOA3] */ && (
-
-
- You’ll need to verify your identity to request your records
-
-
- We need to make sure you’re you — and not someone pretending to
- be you — before we can give you access to your personal
- information. This helps to keep your information safe, and to
- prevent fraud and identity theft.
-
- This one-time process takes about 5-10 minutes.
-
-
- Verify your identity
-
-
-
+
If you don’t want to verify your identity right now, you can still
download and complete the PDF version of this request.
diff --git a/src/applications/simple-forms/20-10206/tests/unit/containers/IntroductionPage.unit.spec.jsx b/src/applications/simple-forms/20-10206/tests/unit/containers/IntroductionPage.unit.spec.jsx
index fb0d71f8bf2d..fcb16429df73 100644
--- a/src/applications/simple-forms/20-10206/tests/unit/containers/IntroductionPage.unit.spec.jsx
+++ b/src/applications/simple-forms/20-10206/tests/unit/containers/IntroductionPage.unit.spec.jsx
@@ -13,11 +13,11 @@ const props = {
},
};
-const mockStore = {
+const generateStore = ({ loggedIn = false, loaCurrent = 3 } = {}) => ({
getState: () => ({
user: {
login: {
- currentlyLoggedIn: false,
+ currentlyLoggedIn: loggedIn,
},
profile: {
savedForms: [],
@@ -25,7 +25,7 @@ const mockStore = {
verified: false,
dob: '2000-01-01',
loa: {
- current: 3,
+ current: loaCurrent,
},
},
},
@@ -48,10 +48,11 @@ const mockStore = {
}),
subscribe: () => {},
dispatch: () => {},
-};
+});
describe('IntroductionPage', () => {
it('should render', () => {
+ const mockStore = generateStore();
const { container } = render(
@@ -59,4 +60,13 @@ describe('IntroductionPage', () => {
);
expect(container).to.exist;
});
+ it('should render the va-alert-sign-in for LOA1 users', () => {
+ const mockStore = generateStore({ loggedIn: true, loaCurrent: 1 });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.querySelector('va-alert-sign-in')).to.exist;
+ });
});
diff --git a/src/applications/simple-forms/20-10207/containers/IntroductionPage.jsx b/src/applications/simple-forms/20-10207/containers/IntroductionPage.jsx
index 6f8efece2fc9..fbf6aceabab9 100644
--- a/src/applications/simple-forms/20-10207/containers/IntroductionPage.jsx
+++ b/src/applications/simple-forms/20-10207/containers/IntroductionPage.jsx
@@ -6,10 +6,10 @@ import { setData } from '~/platform/forms-system/src/js/actions';
import { VA_FORM_IDS } from 'platform/forms/constants';
import { isLOA3, isLoggedIn } from 'platform/user/selectors';
+import VerifyAlert from 'platform/user/authorization/components/VerifyAlert';
import { IntroductionPageView } from '../../shared/components/IntroductionPageView';
import { TITLE, SUBTITLE } from '../config/constants';
-import manifest from '../manifest.json';
const IntroductionPage = props => {
const { route } = props;
@@ -212,26 +212,7 @@ const IntroductionPage = props => {
className="id-not-verified-content vads-u-margin-top--4"
data-testid="verifyIdAlert"
>
-
-
- You’ll need to verify your identity to request your records
-
-
- We need to make sure you’re you — and not someone pretending to
- be you — before we can give you access to your personal
- information. This helps to keep your information safe, and to
- prevent fraud and identity theft.
-
- This one-time process takes about 5-10 minutes.
-
-
- Verify your identity
-
-
-
+
If you don’t want to verify your identity right now, you can still
download and complete the PDF version of this request.
@@ -239,8 +220,8 @@ const IntroductionPage = props => {
diff --git a/src/applications/simple-forms/20-10207/tests/unit/containers/IntroductionPage.unit.spec.jsx b/src/applications/simple-forms/20-10207/tests/unit/containers/IntroductionPage.unit.spec.jsx
index 4a5da68f0db0..32c0dbaba3a0 100644
--- a/src/applications/simple-forms/20-10207/tests/unit/containers/IntroductionPage.unit.spec.jsx
+++ b/src/applications/simple-forms/20-10207/tests/unit/containers/IntroductionPage.unit.spec.jsx
@@ -41,20 +41,20 @@ const props = {
},
};
-const mockStore = {
+const generateStore = ({ loggedIn = false, loaCurrent = 3 } = {}) => ({
getState: () => ({
user: {
login: {
- currentlyLoggedIn: false,
+ currentlyLoggedIn: loggedIn,
},
profile: {
savedForms: [],
prefillsAvailable: ['20-10207'],
dob: '2000-01-01',
loa: {
- current: 3,
+ current: loaCurrent,
},
- verified: true,
+ verified: loaCurrent === 3,
},
},
form: {
@@ -76,10 +76,11 @@ const mockStore = {
}),
subscribe: () => {},
dispatch: () => {},
-};
+});
describe('IntroductionPage', () => {
it('renders successfully', () => {
+ const mockStore = generateStore();
const { container } = render(
@@ -89,6 +90,7 @@ describe('IntroductionPage', () => {
});
it('renders the correct title and subtitle', () => {
+ const mockStore = generateStore();
const { getByText } = render(
@@ -99,26 +101,9 @@ describe('IntroductionPage', () => {
});
it('renders {
- const userNotVerifiedMockStore = {
- ...mockStore,
- getState: () => ({
- ...mockStore.getState(),
- user: {
- login: {
- currentlyLoggedIn: true,
- },
- profile: {
- ...mockStore.getState().user.profile,
- loa: {
- current: 1,
- },
- verified: false,
- },
- },
- }),
- };
+ const mockStore = generateStore({ loggedIn: true, loaCurrent: 1 });
const { container } = render(
-
+
,
);
@@ -127,31 +112,16 @@ describe('IntroductionPage', () => {
'[data-testid=verifyIdAlert]',
);
const sipAlert = container.querySelector('va-alert[status=info]');
+ const verifyAlert = container.querySelector('va-alert-sign-in');
expect(userNotVerifiedDiv).to.exist;
+ expect(verifyAlert).to.exist;
expect(sipAlert).to.not.exist;
});
it('renders LOA3 content if user is logged-in and id-verified', () => {
- const userVerifiedMockStore = {
- ...mockStore,
- getState: () => ({
- ...mockStore.getState(),
- user: {
- login: {
- currentlyLoggedIn: true,
- },
- profile: {
- ...mockStore.getState().user.profile,
- loa: {
- current: 3,
- },
- verified: true,
- },
- },
- }),
- };
+ const mockStore = generateStore({ loggedIn: true });
const { container } = render(
-
+
,
);
diff --git a/src/applications/simple-forms/21-0845/containers/IntroductionPage.jsx b/src/applications/simple-forms/21-0845/containers/IntroductionPage.jsx
index ce52bf7790c9..19b39776fa54 100644
--- a/src/applications/simple-forms/21-0845/containers/IntroductionPage.jsx
+++ b/src/applications/simple-forms/21-0845/containers/IntroductionPage.jsx
@@ -6,12 +6,11 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { isLOA3, isLoggedIn } from 'platform/user/selectors';
+import VerifyAlert from 'platform/user/authorization/components/VerifyAlert';
import { focusElement } from 'platform/utilities/ui';
import FormTitle from 'platform/forms-system/src/js/components/FormTitle';
import SaveInProgressIntro from 'platform/forms/save-in-progress/SaveInProgressIntro';
-import manifest from '../manifest.json';
-
class IntroductionPage extends React.Component {
componentDidMount() {
focusElement('.va-nav-breadcrumbs-list');
@@ -75,27 +74,7 @@ class IntroductionPage extends React.Component {
{userLoggedIn &&
!userIdVerified /* If User's signed-in but not identity-verified [not LOA3] */ && (
-
-
- You’ll need to verify your identity to authorize the release
- of your information
-
-
- We need to make sure you’re you — and not someone pretending
- to be you — before we can give you access to your personal and
- health-related information. This helps to keep your
- information safe, and to prevent fraud and identity theft.
-
- This one-time process takes about 5-10 minutes.
-
-
- Verify your identity
-
-
-
+
If you don’t want to verify your identity right now, you can
still download and complete the PDF version of this
diff --git a/src/applications/simple-forms/21-0845/tests/containers/IntroductionPage.unit.spec.jsx b/src/applications/simple-forms/21-0845/tests/containers/IntroductionPage.unit.spec.jsx
index e132ec6debf9..4b166b3a972a 100644
--- a/src/applications/simple-forms/21-0845/tests/containers/IntroductionPage.unit.spec.jsx
+++ b/src/applications/simple-forms/21-0845/tests/containers/IntroductionPage.unit.spec.jsx
@@ -15,11 +15,11 @@ const props = {
userLoggedIn: false,
};
-const mockStore = {
+const generateStore = ({ loggedIn = false, loaCurrent = 3 } = {}) => ({
getState: () => ({
user: {
login: {
- currentlyLoggedIn: false,
+ currentlyLoggedIn: loggedIn,
},
profile: {
savedForms: [],
@@ -30,9 +30,10 @@ const mockStore = {
appeals: false,
},
loa: {
- current: 3,
+ current: loaCurrent,
highest: 3,
},
+ signIn: { serviceName: 'idme' },
},
},
form: {
@@ -54,10 +55,11 @@ const mockStore = {
}),
subscribe: () => {},
dispatch: () => {},
-};
+});
describe('IntroductionPage', () => {
it('should render', () => {
+ const mockStore = generateStore();
const { container } = render(
@@ -65,4 +67,13 @@ describe('IntroductionPage', () => {
);
expect(container).to.exist;
});
+ it('should render the va-alert-sign-in for LOA1 users', () => {
+ const mockStore = generateStore({ loggedIn: true, loaCurrent: 1 });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.querySelector('va-alert-sign-in')).to.exist;
+ });
});
diff --git a/src/applications/simple-forms/21-0966/containers/IntroductionPage.jsx b/src/applications/simple-forms/21-0966/containers/IntroductionPage.jsx
index 2cb06bd7d239..f553e584fcef 100644
--- a/src/applications/simple-forms/21-0966/containers/IntroductionPage.jsx
+++ b/src/applications/simple-forms/21-0966/containers/IntroductionPage.jsx
@@ -7,8 +7,7 @@ import { isLOA3, isLoggedIn } from 'platform/user/selectors';
import { focusElement } from 'platform/utilities/ui';
import FormTitle from 'platform/forms-system/src/js/components/FormTitle';
import SaveInProgressIntro from 'platform/forms/save-in-progress/SaveInProgressIntro';
-
-import manifest from '../manifest.json';
+import VerifyAlert from 'platform/user/authorization/components/VerifyAlert';
class IntroductionPage extends React.Component {
componentDidMount() {
@@ -121,31 +120,7 @@ class IntroductionPage extends React.Component {
{userLoggedIn &&
!userIdVerified /* If User's signed-in but not identity-verified [not LOA3] */ && (
-
-
- You’ll need to verify your identity to submit an intent to
- file
-
-
- We need to make sure you’re you — and not someone pretending
- to be you — before we can give you access to your personal
- information. This helps to keep your information safe, and to
- prevent fraud and identity theft.
-
-
-
- This one-time process takes about 5-10 minutes.
-
-
-
-
- Verify your identity
-
-
-
+
If you don’t want to verify your identity right now, you can
still download and complete the PDF version of this request.
diff --git a/src/applications/simple-forms/21-0966/tests/containers/IntroductionPage.unit.spec.jsx b/src/applications/simple-forms/21-0966/tests/containers/IntroductionPage.unit.spec.jsx
index d5aa541d1f5a..9a670bd38998 100644
--- a/src/applications/simple-forms/21-0966/tests/containers/IntroductionPage.unit.spec.jsx
+++ b/src/applications/simple-forms/21-0966/tests/containers/IntroductionPage.unit.spec.jsx
@@ -15,16 +15,16 @@ const props = {
userIdVerified: true,
};
-const mockStore = {
+const generateStore = ({ loggedIn = false, loaCurrent = 3 } = {}) => ({
getState: () => ({
user: {
login: {
- currentlyLoggedIn: false,
+ currentlyLoggedIn: loggedIn,
},
profile: {
savedForms: [],
prefillsAvailable: [],
- loa: { current: 3, highest: 3 },
+ loa: { current: loaCurrent, highest: 3 },
verified: true,
dob: '2000-01-01',
claims: {
@@ -51,10 +51,11 @@ const mockStore = {
}),
subscribe: () => {},
dispatch: () => {},
-};
+});
describe('IntroductionPage', () => {
it('should render', () => {
+ const mockStore = generateStore();
const { container } = render(
@@ -62,4 +63,13 @@ describe('IntroductionPage', () => {
);
expect(container).to.exist;
});
+ it('should render the va-alert-sign-in for LOA1 users', () => {
+ const mockStore = generateStore({ loggedIn: true, loaCurrent: 1 });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.querySelector('va-alert-sign-in')).to.exist;
+ });
});
diff --git a/src/applications/simple-forms/21-4142/containers/IntroductionPage.jsx b/src/applications/simple-forms/21-4142/containers/IntroductionPage.jsx
index 7096259cfcd2..1d3a5a0129b0 100644
--- a/src/applications/simple-forms/21-4142/containers/IntroductionPage.jsx
+++ b/src/applications/simple-forms/21-4142/containers/IntroductionPage.jsx
@@ -4,8 +4,7 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { isLOA3, isLoggedIn } from 'platform/user/selectors';
-
-import manifest from '../manifest.json';
+import VerifyAlert from 'platform/user/authorization/components/VerifyAlert';
import { IntroductionPageView } from '../../shared/components/IntroductionPageView';
const ombInfo = {
@@ -55,27 +54,7 @@ export const IntroductionPage = ({ route, userIdVerified, userLoggedIn }) => {
{userLoggedIn &&
!userIdVerified /* If User's signed-in but not identity-verified [not LOA3] */ && (
-
-
- You’ll need to verify your identity to authorize the release of
- non-VA medical records to VA
-
-
- We need to make sure you’re you — and not someone pretending to
- be you — before we can give you access to your personal and
- health-related information. This helps to keep your information
- safe, and to prevent fraud and identity theft.
-
- This one-time process takes about 5-10 minutes.
-
-
- Verify your identity
-
-
-
+
If you don’t want to verify your identity right now, you can still
download and complete the PDF version of this authorization.
diff --git a/src/applications/simple-forms/21-4142/tests/unit/containers/IntroductionPage.unit.spec.jsx b/src/applications/simple-forms/21-4142/tests/unit/containers/IntroductionPage.unit.spec.jsx
index 7dd0732dc9b9..e5fb8003afd3 100644
--- a/src/applications/simple-forms/21-4142/tests/unit/containers/IntroductionPage.unit.spec.jsx
+++ b/src/applications/simple-forms/21-4142/tests/unit/containers/IntroductionPage.unit.spec.jsx
@@ -15,17 +15,17 @@ const props = {
userIdVerified: true,
};
-const mockStore = {
+const generateStore = ({ loggedIn = false, loaCurrent = 3 } = {}) => ({
getState: () => ({
user: {
login: {
- currentlyLoggedIn: false,
+ currentlyLoggedIn: loggedIn,
},
profile: {
savedForms: [],
prefillsAvailable: [],
loa: {
- current: 3,
+ current: loaCurrent,
highest: 3,
},
verified: true,
@@ -54,10 +54,11 @@ const mockStore = {
}),
subscribe: () => {},
dispatch: () => {},
-};
+});
describe('IntroductionPage', () => {
it('should render', () => {
+ const mockStore = generateStore();
const { container } = render(
@@ -65,4 +66,13 @@ describe('IntroductionPage', () => {
);
expect(container).to.exist;
});
+ it('should render the va-alert-sign-in for LOA1 users', () => {
+ const mockStore = generateStore({ loggedIn: true, loaCurrent: 1 });
+ const { container } = render(
+
+
+ ,
+ );
+ expect(container.querySelector('va-alert-sign-in')).to.exist;
+ });
});
diff --git a/src/applications/simple-forms/form-upload/config/constants.js b/src/applications/simple-forms/form-upload/config/constants.js
index 2c17cb0eef57..10a46472b8fa 100644
--- a/src/applications/simple-forms/form-upload/config/constants.js
+++ b/src/applications/simple-forms/form-upload/config/constants.js
@@ -44,28 +44,15 @@ export const MUST_MATCH_ALERT = (variant, onCloseEvent, formData) => {
) : null}
{variant === 'name-and-zip-code' ? (
- <>
-
- Since you’re signed in to your account, we prefilled this page based
- on your account details.
-
-
-
- If the Veteran’s name and postal code here don’t match your uploaded
- pdf, it will cause processing delays.
-
- >
+
+ If the Veteran’s name and postal code here don’t match your uploaded
+ pdf, it will cause processing delays.
+
) : (
- <>
-
- Since you’re signed in to your account, we prefilled part of this
- page based on your account details.
-
-
- If the Veteran’s identification information you enter here doesn’t
- match your uploaded pdf, it will cause processing delays.
-
- >
+
+ If the Veteran’s identification information you enter here doesn’t
+ match your uploaded pdf, it will cause processing delays.
+
)}
);
diff --git a/src/applications/static-pages/BTSSS-login/App/index.jsx b/src/applications/static-pages/BTSSS-login/App/index.jsx
index 34eb8dc41052..34141bb7a6d7 100644
--- a/src/applications/static-pages/BTSSS-login/App/index.jsx
+++ b/src/applications/static-pages/BTSSS-login/App/index.jsx
@@ -1,13 +1,12 @@
-// Node modules.
import React from 'react';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
+import { useSelector } from 'react-redux';
import { isLoggedIn } from 'platform/user/selectors';
import AuthContext from '../AuthContext';
import UnauthContext from '../UnauthContext';
-export const App = ({ currentlyLoggedIn }) => {
+export const App = () => {
+ const currentlyLoggedIn = useSelector(isLoggedIn);
return (
<>
@@ -19,17 +18,4 @@ export const App = ({ currentlyLoggedIn }) => {
);
};
-const mapStateToProps = state => {
- return {
- currentlyLoggedIn: isLoggedIn(state),
- };
-};
-
-export default connect(
- mapStateToProps,
- null,
-)(App);
-
-App.propTypes = {
- currentlyLoggedIn: PropTypes.bool,
-};
+export default App;
diff --git a/src/applications/static-pages/BTSSS-login/App/index.unit.spec.js b/src/applications/static-pages/BTSSS-login/App/index.unit.spec.js
index 8773d035eb50..23e9e5b71bb1 100644
--- a/src/applications/static-pages/BTSSS-login/App/index.unit.spec.js
+++ b/src/applications/static-pages/BTSSS-login/App/index.unit.spec.js
@@ -1,25 +1,39 @@
-// Dependencies.
import React from 'react';
import { expect } from 'chai';
-import { shallow } from 'enzyme';
+import { fireEvent, cleanup } from '@testing-library/react';
+import { renderInReduxProvider } from 'platform/testing/unit/react-testing-library-helpers';
-// Relative imports.
-import { App } from '.';
-import AuthContext from '../AuthContext';
-import UnauthContext from '../UnauthContext';
+import BTSSSApp from '.';
describe('BTSSS Widget', () => {
- it('renders what we expect when unauthenticated', () => {
- const wrapper = shallow();
- expect(wrapper.find(UnauthContext)).to.have.lengthOf(1);
- expect(wrapper.find(AuthContext)).to.have.lengthOf(0);
- wrapper.unmount();
+ afterEach(cleanup);
+
+ it('renders va-alert-sign-in for unauthenticated user', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: {
+ user: { login: { currentLoggedIn: false } },
+ },
+ });
+ expect(container.querySelector('va-alert-sign-in')).to.exist;
+ });
+
+ it('opens sign-in modal when Sign in button is clicked', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: {
+ user: { login: { currentlyLoggedIn: false } },
+ },
+ });
+ const signInButton = container.querySelector('va-button');
+ fireEvent.click(signInButton);
+ expect(container.querySelector('va-button')).to.exist;
});
it('renders what we expect when authenticated', () => {
- const wrapper = shallow();
- expect(wrapper.find(UnauthContext)).to.have.lengthOf(0);
- expect(wrapper.find(AuthContext)).to.have.lengthOf(1);
- wrapper.unmount();
+ const { container } = renderInReduxProvider(, {
+ initialState: {
+ user: { login: { currentlyLoggedIn: true } },
+ },
+ });
+ expect(container.querySelector('va-alert-sign-in')).to.not.exist;
});
});
diff --git a/src/applications/static-pages/BTSSS-login/UnauthContext/index.jsx b/src/applications/static-pages/BTSSS-login/UnauthContext/index.jsx
index cb6286c4b02a..d91eae37cf02 100644
--- a/src/applications/static-pages/BTSSS-login/UnauthContext/index.jsx
+++ b/src/applications/static-pages/BTSSS-login/UnauthContext/index.jsx
@@ -12,30 +12,20 @@ const UnauthContext = () => {
};
return (
-
- Sign in to file a travel pay claim
-
-
- Sign in with your existing Login.gov,{' '}
- ID.me, or DS Logon account. If you
- don’t have any of these accounts, you can create a free{' '}
- Login.gov or ID.me account now.
-
+
-
-
+
+
);
};
diff --git a/src/applications/static-pages/medical-copays-cta/components/App/index.js b/src/applications/static-pages/medical-copays-cta/components/App/index.js
index 27a6b30f17cc..dedf011bf181 100644
--- a/src/applications/static-pages/medical-copays-cta/components/App/index.js
+++ b/src/applications/static-pages/medical-copays-cta/components/App/index.js
@@ -1,73 +1,39 @@
-// Node modules.
import React from 'react';
-import PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-// Relative imports.
-import { toggleLoginModal as toggleLoginModalAction } from 'platform/site-wide/user-nav/actions';
+import { useSelector, useDispatch } from 'react-redux';
+import { toggleLoginModal } from 'platform/site-wide/user-nav/actions';
-import ServiceProvidersText, {
- ServiceProvidersTextCreateAcct,
-} from 'platform/user/authentication/components/ServiceProvidersText';
-
-export const App = ({ loggedIn, toggleLoginModal }) => {
- return (
-
- {/* Title */}
-
- {loggedIn
- ? 'Review your VA copay balances'
- : 'Please sign in to review your VA copay balances'}
-
+export const App = () => {
+ const loggedIn = useSelector(
+ state => state?.user?.login?.currentlyLoggedIn || false,
+ );
+ const dispatch = useDispatch();
- {/* Explanation */}
- {loggedIn ? (
- With this tool, you can:
- ) : (
-
- Sign in with your existing account.{' '}
-
-
- )}
+ return loggedIn ? (
+
+ Review your VA copay balances
+ With this tool you can:
- Review your balances for each of your medical facilities
- Download your copay statements
- Find the right repayment option for you
-
- {/* Call to action button/link */}
- {loggedIn ? (
-
- Review your current copay balances
-
- ) : (
+
+ Review your current copay balances
+
+
+ ) : (
+
+
toggleLoginModal(true)}
+ onClick={() => dispatch(toggleLoginModal(true, 'medical-copays-cta'))}
text="Sign in or create an account"
/>
- )}
-
+
+
);
};
-App.propTypes = {
- // From mapDispatchToProps.
- toggleLoginModal: PropTypes.func.isRequired,
- // From mapStateToProps.
- loggedIn: PropTypes.bool,
-};
-
-const mapStateToProps = state => ({
- loggedIn: state?.user?.login?.currentlyLoggedIn || false,
-});
-
-const mapDispatchToProps = dispatch => ({
- toggleLoginModal: open => dispatch(toggleLoginModalAction(open)),
-});
-
-export default connect(
- mapStateToProps,
- mapDispatchToProps,
-)(App);
+export default App;
diff --git a/src/applications/static-pages/medical-copays-cta/components/App/index.unit.spec.js b/src/applications/static-pages/medical-copays-cta/components/App/index.unit.spec.js
index 33f81aa741d3..2fa125ac81ab 100644
--- a/src/applications/static-pages/medical-copays-cta/components/App/index.unit.spec.js
+++ b/src/applications/static-pages/medical-copays-cta/components/App/index.unit.spec.js
@@ -1,60 +1,45 @@
-// Dependencies.
import React from 'react';
import { expect } from 'chai';
-import { shallow, mount } from 'enzyme';
-import { Provider } from 'react-redux';
-// Relative imports.
+import { fireEvent, cleanup } from '@testing-library/react';
+import { renderInReduxProvider } from 'platform/testing/unit/react-testing-library-helpers';
+
import { App } from '.';
describe('Medical Copays CTA ', () => {
- it('renders what we expect when unauthenticated', () => {
- const mockStore = {
- getState: () => ({}),
- dispatch: () => {},
- subscribe: () => {},
- };
- const wrapper = mount(
-
-
- ,
- );
- expect(wrapper.type()).to.not.equal(null);
- expect(wrapper.text()).includes(
- 'Please sign in to review your VA copay balances',
- );
- expect(wrapper.text()).not.includes('Review your VA copay balances');
- expect(wrapper.text()).includes(
- 'If you don’t have any of these accounts, you can create a free Login.gov or ID.me account now. When you sign in or create an account, you’ll be able to:',
- );
- expect(wrapper.text()).not.includes('With this tool, you can:');
- expect(wrapper.text()).includes(
- 'Review your balances for each of your medical facilities',
- );
- expect(wrapper.text()).includes('Download your copay statements');
- expect(wrapper.text()).includes('Find the right repayment option for you');
- expect(wrapper.find('a.vads-c-action-link--blue')).to.have.lengthOf(0);
- expect(wrapper.find('va-button')).to.have.lengthOf(1);
- wrapper.unmount();
+ afterEach(cleanup);
+
+ it('renders va-alert-sign-in to unauthenticated user', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: {
+ user: { login: { currentlyLoggedIn: false } },
+ },
+ });
+ expect(container.querySelector('va-alert-sign-in')).to.exist;
+ });
+
+ it('opens sign-in modal when Sign in button is clicked', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: {
+ user: { login: { currentlyLoggedIn: false } },
+ },
+ });
+ const signInButton = container.querySelector('va-button');
+ fireEvent.click(signInButton);
+ expect(container.querySelector('va-button')).to.exist;
});
it('renders what we expect when authenticated', () => {
- const wrapper = shallow();
- expect(wrapper.type()).to.not.equal(null);
- expect(wrapper.text()).includes('Review your VA copay balances');
- expect(wrapper.text()).includes('With this tool, you can:');
- expect(wrapper.text()).not.includes(
- 'Please sign in to review your VA copay balances',
- );
- expect(wrapper.text()).not.includes(
- 'If you don’t have any of these accounts, you can create a free account now. When you sign in or create an account, you’ll be able to:',
- );
- expect(wrapper.text()).includes(
- 'Review your balances for each of your medical facilities',
+ const { container } = renderInReduxProvider(, {
+ initialState: {
+ user: { login: { currentlyLoggedIn: true } },
+ },
+ });
+ const vaAlert = container.querySelector('va-alert');
+ expect(container.querySelector('va-alert-sign-in')).to.not.exist;
+ expect(vaAlert).to.exist;
+ expect(vaAlert.getAttribute('status')).to.eql('info');
+ expect(container.querySelector('[slot="headline"]').textContent).to.eql(
+ 'Review your VA copay balances',
);
- expect(wrapper.text()).includes('Download your copay statements');
- expect(wrapper.text()).includes('Find the right repayment option for you');
- expect(wrapper.find('a.vads-c-action-link--blue')).to.have.lengthOf(1);
- expect(wrapper.find('va-button')).to.have.lengthOf(0);
- wrapper.unmount();
});
});
diff --git a/src/applications/static-pages/representative-status/api/RepresentativeStatusApi.js b/src/applications/static-pages/representative-status/api/RepresentativeStatusApi.js
index 894d0ec48ff2..a838cbdebec9 100644
--- a/src/applications/static-pages/representative-status/api/RepresentativeStatusApi.js
+++ b/src/applications/static-pages/representative-status/api/RepresentativeStatusApi.js
@@ -1,4 +1,4 @@
-import { fetchAndUpdateSessionExpiration as fetch } from '@department-of-veterans-affairs/platform-utilities/api';
+import { fetchAndUpdateSessionExpiration } from '@department-of-veterans-affairs/platform-utilities/api';
import environment from '@department-of-veterans-affairs/platform-utilities/environment';
class RepresentativeStatusApi {
@@ -7,8 +7,6 @@ class RepresentativeStatusApi {
environment.API_URL
}/representation_management/v0/power_of_attorney`;
const apiSettings = {
- 'Content-Type': 'application/json',
- mode: 'cors',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
@@ -18,7 +16,7 @@ class RepresentativeStatusApi {
const startTime = new Date().getTime();
return new Promise((resolve, reject) => {
- fetch(requestUrl, apiSettings)
+ fetchAndUpdateSessionExpiration(requestUrl, apiSettings)
.then(response => {
if (!response.ok && response.status !== 422) {
throw Error(response.statusText);
diff --git a/src/applications/static-pages/representative-status/components/App/index.jsx b/src/applications/static-pages/representative-status/components/App/index.jsx
index d801322e1d4d..268ab9d7692d 100644
--- a/src/applications/static-pages/representative-status/components/App/index.jsx
+++ b/src/applications/static-pages/representative-status/components/App/index.jsx
@@ -1,27 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
-import {
- isAuthenticatedWithSSOe,
- isAuthenticatedWithOAuth,
-} from '@department-of-veterans-affairs/platform-user/authentication/selectors';
import { toggleLoginModal as toggleLoginModalAction } from '@department-of-veterans-affairs/platform-site-wide/actions';
import { useFeatureToggle } from '~/platform/utilities/feature-toggles/useFeatureToggle';
import { Auth } from '../States/Auth';
import { Unauth } from '../States/Unauth';
import { useRepresentativeStatus } from '../../hooks/useRepresentativeStatus';
-export const App = ({
- baseHeader,
- toggleLoginModal,
- authenticatedWithSSOe,
- authenticatedWithOAuth,
- verbose,
-}) => {
+export const App = ({ baseHeader, toggleLoginModal, isLoggedIn }) => {
const DynamicHeader = `h${baseHeader}`;
const DynamicSubheader = `h${baseHeader + 1}`;
- const loggedIn = authenticatedWithSSOe || authenticatedWithOAuth;
+ const loggedIn = isLoggedIn;
const {
useToggleValue,
@@ -56,8 +46,7 @@ export const App = ({
<>
>
)}
@@ -67,17 +56,14 @@ export const App = ({
App.propTypes = {
toggleLoginModal: PropTypes.func.isRequired,
- authenticatedWithOAuth: PropTypes.bool,
- authenticatedWithSSOe: PropTypes.bool,
baseHeader: PropTypes.number,
hasRepresentative: PropTypes.bool,
- verbose: PropTypes.bool,
+ isLoggedIn: PropTypes.bool,
};
const mapStateToProps = state => ({
hasRepresentative: state?.user?.login?.hasRepresentative || null,
- authenticatedWithSSOe: isAuthenticatedWithSSOe(state),
- authenticatedWithOAuth: isAuthenticatedWithOAuth(state),
+ isLoggedIn: state?.user?.login?.currentlyLoggedIn || false,
});
const mapDispatchToProps = dispatch => ({
diff --git a/src/applications/static-pages/representative-status/components/States/Auth.jsx b/src/applications/static-pages/representative-status/components/States/Auth.jsx
index 09c110cbf595..7c31b3033b34 100644
--- a/src/applications/static-pages/representative-status/components/States/Auth.jsx
+++ b/src/applications/static-pages/representative-status/components/States/Auth.jsx
@@ -34,7 +34,7 @@ export const Auth = ({
focusElement('.poa-display');
}
},
- [id, isPostLogin],
+ [isPostLogin],
);
if (isLoading) {
diff --git a/src/applications/static-pages/representative-status/components/States/Unauth.jsx b/src/applications/static-pages/representative-status/components/States/Unauth.jsx
index c3cc603f465d..bb05f6d53f85 100644
--- a/src/applications/static-pages/representative-status/components/States/Unauth.jsx
+++ b/src/applications/static-pages/representative-status/components/States/Unauth.jsx
@@ -1,41 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
-export const Unauth = ({ toggleLoginModal, DynamicHeader, verbose }) => {
+export const Unauth = ({ toggleLoginModal, headingLevel }) => {
return (
<>
-
-
- Sign in to check if you have an accredited representative
-
-
- {verbose && (
-
- Sign in with your existing{' '}
- Login.gov, ID.me, DS Logon, or{' '}
- My HealtheVet account. If you don’t have any of
- these accounts, you can create a free Login.gov{' '}
- or ID.me account now.
-
- )}
+
toggleLoginModal(true)}
/>
-
-
+
+
>
);
};
Unauth.propTypes = {
- DynamicHeader: PropTypes.string,
+ headingLevel: PropTypes.number,
toggleLoginModal: PropTypes.func,
- verbose: PropTypes.bool,
};
diff --git a/src/applications/static-pages/representative-status/hooks/useRepresentativeStatus.js b/src/applications/static-pages/representative-status/hooks/useRepresentativeStatus.js
index ac8a3a27cfb7..48ebb6d34949 100644
--- a/src/applications/static-pages/representative-status/hooks/useRepresentativeStatus.js
+++ b/src/applications/static-pages/representative-status/hooks/useRepresentativeStatus.js
@@ -23,7 +23,7 @@ export function useRepresentativeStatus() {
contact,
extension,
vcfUrl,
- } = formatContactInfo(poaData.attributes);
+ } = await formatContactInfo(poaData.attributes);
setRepresentative({
id: poaData.id,
diff --git a/src/applications/static-pages/representative-status/tests/App.unit.spec.jsx b/src/applications/static-pages/representative-status/tests/App.unit.spec.jsx
index 9f185b37a064..f916d3437318 100644
--- a/src/applications/static-pages/representative-status/tests/App.unit.spec.jsx
+++ b/src/applications/static-pages/representative-status/tests/App.unit.spec.jsx
@@ -1,89 +1,161 @@
import React from 'react';
import { expect } from 'chai';
-import { Provider } from 'react-redux';
-import { mount } from 'enzyme';
+import { rest } from 'msw';
+import { setupServer } from 'msw/node';
+import { fireEvent, waitFor, cleanup } from '@testing-library/react';
+import { renderInReduxProvider } from 'platform/testing/unit/react-testing-library-helpers';
import App from '../components/App';
-const createFakeStore = state => {
- return {
- getState: () => state,
- subscribe: () => {},
- dispatch: () => {},
- };
-};
+const createFakeStore = ({
+ isLoading = false,
+ toggleEnabled = true,
+ hasRepresentative = false,
+ isLoggedIn = true,
+} = {}) => ({
+ featureToggles: {
+ loading: isLoading,
+ // eslint-disable-next-line camelcase
+ representative_status_enabled: toggleEnabled,
+ },
+ user: {
+ login: {
+ hasRepresentative,
+ currentlyLoggedIn: isLoggedIn,
+ },
+ },
+});
describe('App component', () => {
- const authNoRepState = {
- user: {
- login: {
- hasRepresentative: false,
- },
- profile: {
- session: {
- ssoe: true,
- oauth: false,
- },
- },
- },
- };
- const authWithRepState = {
- user: {
- login: {
- hasRepresentative: false,
- },
- profile: {
- session: {
- ssoe: true,
- oauth: false,
- },
- },
- },
- };
- const oAuthWithRepState = {
- user: {
- login: {
- hasRepresentative: false,
- },
- profile: {
- session: {
- ssoe: false,
- oauth: true,
- },
- },
- },
- };
-
- it('should render when authenticated (ssoe) with no rep', () => {
- const fakeStore = createFakeStore(authNoRepState);
- const wrapper = mount(
-
-
- ,
- );
-
- expect(wrapper.find('App').exists()).to.be.true;
- wrapper.unmount();
+ afterEach(cleanup);
+ it('should null when feature toggles is loading', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ isLoading: true }),
+ });
+
+ expect(container.querySelector('va-loading-indicator')).to.not.exist;
+ });
+
+ it('should be null when feature toggle is not enabled', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ toggleEnabled: false }),
+ });
+
+ expect(container.querySelector('va-loading-indicator')).to.not.exist;
});
- it('should render when authenticated (ssoe) with rep', () => {
- const fakeStore = createFakeStore(authWithRepState);
- const wrapper = mount(
-
-
- ,
- );
- expect(wrapper.find('App').exists()).to.be.true;
- wrapper.unmount();
+ context('unauthenticated', () => {
+ it('should render va-alert-sign-in', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ isLoggedIn: false }),
+ });
+ expect(container.querySelector('va-alert-sign-in')).to.exist;
+ });
+
+ it('should open sign-in modal when button is clicked', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ isLoggedIn: false }),
+ });
+ const signInButton = container.querySelector('va-button');
+ fireEvent.click(signInButton);
+ expect(signInButton).to.exist;
+ });
});
- it('should render when authenticated with oAuth', () => {
- const fakeStore = createFakeStore(oAuthWithRepState);
- const wrapper = mount(
-
-
- ,
- );
- expect(wrapper.find('App').exists()).to.be.true;
- wrapper.unmount();
+ context('authenticated', () => {
+ const server = setupServer();
+
+ before(() => {
+ server.listen();
+ });
+
+ after(() => {
+ server.close();
+ });
+
+ it('should render when no rep found', async () => {
+ server.use(
+ rest.get(
+ `https://dev-api.va.gov/representation_management/v0/power_of_attorney`,
+ (_, res, ctx) => res(ctx.status(200), ctx.json({})),
+ ),
+ );
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ hasRepresentative: false }),
+ });
+
+ await waitFor(() => {
+ const h2Tag = container.querySelector('h2');
+ expect(h2Tag).to.exist;
+ expect(h2Tag.textContent).to.eql(
+ 'You don’t have an accredited representative',
+ );
+ });
+ });
+
+ it('should render content when rep api fails', async () => {
+ server.use(
+ rest.get(
+ `https://dev-api.va.gov/representation_management/v0/power_of_attorney`,
+ (_, res, ctx) => res(ctx.status(400), ctx.json({})),
+ ),
+ );
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ hasRepresentative: false }),
+ });
+
+ await waitFor(() => {
+ const h2Tag = container.querySelector('h2');
+ expect(h2Tag).to.exist;
+ expect(h2Tag.textContent).to.eql(
+ 'We can’t check if you have an accredited representative.',
+ );
+ });
+ });
+
+ it('should render when rep is found', async () => {
+ server.use(
+ rest.get(
+ `https://dev-api.va.gov/representation_management/v0/power_of_attorney`,
+ (_, res, ctx) =>
+ res(
+ ctx.status(200),
+ ctx.json({
+ data: {
+ id: '074',
+ type: 'veteran_service_organizations',
+ attributes: {
+ addressLine1: '1608 K St NW',
+ addressLine2: null,
+ addressLine3: null,
+ addressType: 'Domestic',
+ city: 'Washington',
+ countryName: 'United States',
+ countryCodeIso3: 'USA',
+ province: 'District Of Columbia',
+ internationalPostalCode: null,
+ stateCode: 'DC',
+ zipCode: '20006',
+ zipSuffix: '2801',
+ phone: '202-861-2700',
+ type: 'organization',
+ name: 'American Legion',
+ email: 'sample@test.com',
+ },
+ },
+ }),
+ ),
+ ),
+ );
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ hasRepresentative: true }),
+ });
+
+ await waitFor(() => {
+ expect(container.querySelector('.auth-rep-subheader')).to.exist;
+ expect(
+ container.querySelector('.auth-rep-subheader h3').textContent,
+ ).to.contain('American Legion');
+ });
+ });
});
});
diff --git a/src/applications/static-pages/representative-status/tests/formatContactInfo.unit.spec.js b/src/applications/static-pages/representative-status/tests/formatContactInfo.unit.spec.js
index acd3ec135063..f8b7ae608c37 100644
--- a/src/applications/static-pages/representative-status/tests/formatContactInfo.unit.spec.js
+++ b/src/applications/static-pages/representative-status/tests/formatContactInfo.unit.spec.js
@@ -1,30 +1,8 @@
import { expect } from 'chai';
-import sinon from 'sinon';
import { formatContactInfo } from '../utilities/formatContactInfo';
describe('formatContactInfo', () => {
- let originalBlob;
- let createObjectURLStub;
-
- before(() => {
- originalBlob = global.Blob;
-
- global.Blob = sinon
- .stub()
- .callsFake((content, options) => ({ content, options }));
-
- createObjectURLStub = sinon
- .stub(URL, 'createObjectURL')
- .returns('mocked_vcf_url');
- });
-
- after(() => {
- global.Blob = originalBlob;
-
- createObjectURLStub.restore();
- });
-
- it('should format contact information correctly', () => {
+ it('should format contact information correctly', async () => {
const poaAttributes = {
addressLine1: '1608 K St NW',
addressLine2: '',
@@ -37,11 +15,11 @@ describe('formatContactInfo', () => {
phone: '202-861-2700 ext 123',
};
- const result = formatContactInfo(poaAttributes);
+ const result = await formatContactInfo(poaAttributes);
expect(result.concatAddress).to.equal('1608 K St NW Washington, DC 20006');
expect(result.contact).to.equal('2028612700');
expect(result.extension).to.equal('123');
- expect(result.vcfUrl).to.equal('mocked_vcf_url');
+ expect(result.vcfUrl).to.not.be.null;
});
});
diff --git a/src/applications/static-pages/representative-status/tests/repStatusApi.unit.spec.js b/src/applications/static-pages/representative-status/tests/repStatusApi.unit.spec.js
index 2eadbc5aeedf..9ca799b14e67 100644
--- a/src/applications/static-pages/representative-status/tests/repStatusApi.unit.spec.js
+++ b/src/applications/static-pages/representative-status/tests/repStatusApi.unit.spec.js
@@ -1,71 +1,66 @@
import { expect } from 'chai';
-import sinon from 'sinon';
-import environment from '@department-of-veterans-affairs/platform-utilities/environment';
+import { rest } from 'msw';
+import { setupServer } from 'msw/node';
import RepresentativeStatusApi from '../api/RepresentativeStatusApi';
describe('RepresentativeStatusApi', () => {
- let sandbox;
- let fetchStub;
+ const server = setupServer();
- beforeEach(() => {
- sandbox = sinon.createSandbox();
- global.fetch = sandbox.stub(global, 'fetch');
+ before(() => {
+ server.listen();
});
- afterEach(() => {
- sandbox.restore();
+ after(() => {
+ server.close();
});
+ const createResponse = ({
+ status = 200,
+ json = {},
+ networkError = false,
+ } = {}) => {
+ if (networkError) {
+ server.use(
+ rest.get(
+ `https://dev-api.va.gov/representation_management/v0/power_of_attorney`,
+ (_, res) => res.networkError(),
+ ),
+ );
+ }
+ server.use(
+ rest.get(
+ `https://dev-api.va.gov/representation_management/v0/power_of_attorney`,
+ (_, res, ctx) => res(ctx.status(status), ctx.json(json)),
+ ),
+ );
+ };
+
it('should fetch and return representative status successfully', async () => {
- const mockResponse = {
- ok: true,
- statusText: 'OK',
- json: () => Promise.resolve({ data: 'Mocked Data' }),
- };
- fetchStub = fetch.resolves(mockResponse);
+ createResponse({ json: { id: '074' } });
const result = await RepresentativeStatusApi.getRepresentativeStatus();
- const expectedUrl = `${
- environment.API_URL
- }/representation_management/v0/power_of_attorney`;
- sinon.assert.calledWith(fetchStub, expectedUrl, {
- 'Content-Type': 'application/json',
- mode: 'cors',
- credentials: 'include',
- headers: {
- 'Content-Type': 'application/json',
- 'X-Key-Inflection': 'camel',
- },
- });
- expect(result).to.have.nested.property('data', 'Mocked Data');
+ expect(result).to.contains({ id: '074' });
});
it('should throw an error if the response is not ok', async () => {
- const mockErrorResponse = {
- ok: false,
- statusText: 'Internal Server Error',
- json: () => Promise.resolve({ error: 'Some error' }),
- };
- fetchStub = fetch.resolves(mockErrorResponse);
+ createResponse({ status: 400, json: {} });
try {
await RepresentativeStatusApi.getRepresentativeStatus();
- throw new Error('Expected method to throw.');
} catch (error) {
- expect(error.message).to.equal('Internal Server Error');
+ expect(error.message).to.equal('Bad Request');
}
});
it('should handle errors', async () => {
const mockError = new Error('Network error');
- fetchStub = fetch.rejects(mockError);
+ createResponse({ networkError: true });
try {
await RepresentativeStatusApi.getRepresentativeStatus();
- throw new Error('Expected method to reject.');
} catch (error) {
- expect(error).to.equal(mockError);
+ expect(error.message).to.equal(mockError.message);
}
});
});
diff --git a/src/applications/static-pages/representative-status/utilities/formatContactInfo.js b/src/applications/static-pages/representative-status/utilities/formatContactInfo.js
index 61ac336ba66b..36a3714b3794 100644
--- a/src/applications/static-pages/representative-status/utilities/formatContactInfo.js
+++ b/src/applications/static-pages/representative-status/utilities/formatContactInfo.js
@@ -1,6 +1,14 @@
import { parsePhoneNumber } from './phoneNumbers';
-export function formatContactInfo(poaAttributes) {
+function blobToBase64(_blob) {
+ return new Promise(resolve => {
+ const reader = new FileReader();
+ reader.onloadend = () => resolve(reader.result);
+ reader.readAsDataURL(_blob);
+ });
+}
+
+export async function formatContactInfo(poaAttributes) {
const {
addressLine1,
addressLine2,
@@ -40,12 +48,9 @@ export function formatContactInfo(poaAttributes) {
].join('\n');
const blob = new Blob([vcfData], { type: 'text/vcard' });
- const vcfUrl = URL.createObjectURL(blob);
+ const vcfUrl = window?.Mocha
+ ? await blobToBase64(blob)
+ : URL.createObjectURL(blob);
- return {
- concatAddress,
- contact,
- extension,
- vcfUrl,
- };
+ return { concatAddress, contact, extension, vcfUrl };
}
diff --git a/src/applications/static-pages/view-education-letters-login/LoginWidget.jsx b/src/applications/static-pages/view-education-letters-login/LoginWidget.jsx
index e3dbbcf87482..209babb08611 100644
--- a/src/applications/static-pages/view-education-letters-login/LoginWidget.jsx
+++ b/src/applications/static-pages/view-education-letters-login/LoginWidget.jsx
@@ -1,102 +1,44 @@
import React from 'react';
-import { toggleLoginModal as toggleLoginModalAction } from 'platform/site-wide/user-nav/actions';
-import { connect } from 'react-redux';
-import PropTypes from 'prop-types';
+import { useDispatch, useSelector } from 'react-redux';
+import { toggleLoginModal } from 'platform/site-wide/user-nav/actions';
-const LoginInWidget = ({ toggleLoginModal, user }) => {
+const LoginInWidget = () => {
+ const dispatch = useDispatch();
+ const currentlyLoggedIn = useSelector(
+ state => state?.user?.login?.currentlyLoggedIn,
+ );
+ const isLoading = useSelector(state => state?.user?.profile?.loading);
const toggleLogin = e => {
e.preventDefault();
- toggleLoginModal(true, 'cta-form');
+ dispatch(toggleLoginModal(true, 'cta-form'));
};
- const visitorUI = (
-
-
- Sign in to download your VA education decision letter
-
-
- Sign in with your existing{' '}
- ID.me or{' '}
- Login.gov account. If
- you don’t have any of these accounts, you can create a free{' '}
-
- ID.me
- {' '}
- account or{' '}
-
- Login.gov
- {' '}
- account now.
-
-
-
- );
-
- const spinner = (
-
-
-
- );
-
- const loggedInUserUI = (
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ return currentlyLoggedIn ? (
Download your VA education decision letter
+ ) : (
+
+
+
+
+
);
-
- const renderUI = () => {
- if (!user?.login?.currentlyLoggedIn && !user?.login?.hasCheckedKeepAlive) {
- return spinner;
- }
- if (user?.login?.currentlyLoggedIn) {
- return loggedInUserUI;
- }
-
- return visitorUI;
- };
-
- return renderUI();
};
-LoginInWidget.propTypes = {
- toggleLoginModal: PropTypes.func,
- user: PropTypes.object,
-};
-
-const mapStateToProps = state => ({
- user: state.user || {},
-});
-
-const mapDispatchToProps = dispatch => ({
- toggleLoginModal: open => dispatch(toggleLoginModalAction(open)),
-});
-
-export default connect(
- mapStateToProps,
- mapDispatchToProps,
-)(LoginInWidget);
+export default LoginInWidget;
diff --git a/src/applications/static-pages/view-education-letters-login/LoginWidget.unit.spec.jsx b/src/applications/static-pages/view-education-letters-login/LoginWidget.unit.spec.jsx
new file mode 100644
index 000000000000..e30b4c25b03c
--- /dev/null
+++ b/src/applications/static-pages/view-education-letters-login/LoginWidget.unit.spec.jsx
@@ -0,0 +1,52 @@
+import React from 'react';
+import { expect } from 'chai';
+import { fireEvent, cleanup } from '@testing-library/react';
+import { renderInReduxProvider } from 'platform/testing/unit/react-testing-library-helpers';
+import App from './LoginWidget';
+
+const createFakeStore = ({ isLoading = false, isLoggedIn = false } = {}) => ({
+ user: {
+ profile: { loading: isLoading },
+ login: { currentlyLoggedIn: isLoggedIn },
+ },
+});
+
+describe('App component', () => {
+ afterEach(cleanup);
+
+ it('should render va-loading-indicator', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ isLoading: true }),
+ });
+ expect(container.querySelector('va-loading-indicator')).to.exist;
+ });
+
+ context('authenticated', () => {
+ it('should render an anchor tag', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ isLoggedIn: true }),
+ });
+ const anchorTag = container.querySelector('a');
+ expect(anchorTag).to.exist;
+ expect(anchorTag.href).to.contain('/education/download-letters/letters');
+ });
+ });
+
+ context('unauthenticated', () => {
+ it('should render va-alert-sign-in', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ isLoggedIn: false }),
+ });
+ expect(container.querySelector('va-alert-sign-in')).to.exist;
+ });
+
+ it('should open sign-in modal when button is clicked', () => {
+ const { container } = renderInReduxProvider(, {
+ initialState: createFakeStore({ isLoggedIn: false }),
+ });
+ const signInButton = container.querySelector('va-button');
+ fireEvent.click(signInButton);
+ expect(signInButton).to.exist;
+ });
+ });
+});
diff --git a/src/applications/travel-pay/components/HelpText.jsx b/src/applications/travel-pay/components/HelpText.jsx
index 48d84b0f99b3..8b346e03a817 100644
--- a/src/applications/travel-pay/components/HelpText.jsx
+++ b/src/applications/travel-pay/components/HelpText.jsx
@@ -19,9 +19,9 @@ export const HelpTextManage = () => {
.
- Or call the BTSSS call center at {' '}
- Monday through Friday, 8:00 a.m. to 8:00 p.m. ET. Have your claim number
- ready to share when you call.
+ Or call the BTSSS call center at (
+ ) Monday through Friday, 8:00 a.m. to
+ 8:00 p.m. ET. Have your claim number ready to share when you call.
);
@@ -31,8 +31,9 @@ export const HelpTextGeneral = () => {
return (
- Call the BTSSS call center at .
- We’re here Monday through Friday, 8:00 a.m. to 8:00 p.m. ET.
+ Call the BTSSS call center at (
+
+ ). We’re here Monday through Friday, 8:00 a.m. to 8:00 p.m. ET.
Or call your VA health facility’s Beneficiary Travel contact.
diff --git a/src/applications/travel-pay/components/submit-flow/pages/SubmissionErrorPage.jsx b/src/applications/travel-pay/components/submit-flow/pages/SubmissionErrorPage.jsx
index aa891adbcf06..28970b3105ab 100644
--- a/src/applications/travel-pay/components/submit-flow/pages/SubmissionErrorPage.jsx
+++ b/src/applications/travel-pay/components/submit-flow/pages/SubmissionErrorPage.jsx
@@ -1,9 +1,42 @@
-import React from 'react';
+import React, { useEffect } from 'react';
+import { focusElement, scrollToTop } from 'platform/utilities/ui';
+
+import { HelpTextGeneral, HelpTextModalities } from '../../HelpText';
const SubmissionErrorPage = () => {
+ useEffect(() => {
+ focusElement('h1');
+ scrollToTop('topScrollElement');
+ }, []);
+
return (
-
-
We couldn’t file your claim
+
+
We couldn’t file your claim
+
+ Something went wrong on our end
+
+ We’re sorry. We couldn’t file your travel reimbursement claim in this
+ tool right now. Please try again later.
+
+
+ Or you can still file within 30 days of the appointment through the
+ Beneficiary Travel Self Service System (BTSSS).
+
+
+
+
What happens next?
+
+
+ How can I get help with my claim?
+
+
);
};
diff --git a/src/applications/travel-pay/tests/components/submit-flow/pages/SubmissionErrorPage.unit.spec.jsx b/src/applications/travel-pay/tests/components/submit-flow/pages/SubmissionErrorPage.unit.spec.jsx
index 113115f944e9..be506f2c1a86 100644
--- a/src/applications/travel-pay/tests/components/submit-flow/pages/SubmissionErrorPage.unit.spec.jsx
+++ b/src/applications/travel-pay/tests/components/submit-flow/pages/SubmissionErrorPage.unit.spec.jsx
@@ -4,8 +4,42 @@ import { render } from '@testing-library/react';
import SubmissionErrorPage from '../../../../components/submit-flow/pages/SubmissionErrorPage';
-it('should render with back button', () => {
+it('should render submission error page with expected links', () => {
const screen = render(
);
-
expect(screen.getByText('We couldn’t file your claim')).to.exist;
+
+ // Text only found in HelpTextGeneral used in SubmissionErrorPage
+ expect(
+ screen.getByText(
+ 'Or call your VA health facility’s Beneficiary Travel contact.',
+ ),
+ ).to.exist;
+
+ expect(
+ screen.container.querySelector(
+ '[href="https://www.va.gov/health-care/get-reimbursed-for-travel-pay/"]',
+ '[text="Find out how to file for travel reimbursement"]',
+ ),
+ ).to.exist;
+
+ expect(
+ screen.container.querySelector(
+ '[href="https://dvagov-btsss.dynamics365portals.us/"]',
+ '[text="File a travel claim online"]',
+ ),
+ ).to.exist;
+
+ expect(
+ screen.container.querySelector(
+ '[href="/find-forms/about-form-10-3542/"]',
+ '[text="Learn more about VA Form 10-3542"]',
+ ),
+ ).to.exist;
+
+ expect(
+ screen.container.querySelector(
+ '[href="/HEALTHBENEFITS/vtp/beneficiary_travel_pocs.asp"]',
+ '[text="Find the travel contact for your facility"]',
+ ),
+ ).to.exist;
});
diff --git a/src/platform/forms-system/src/js/state/helpers.js b/src/platform/forms-system/src/js/state/helpers.js
index 82c897c6a4fc..1b5a44e6f707 100644
--- a/src/platform/forms-system/src/js/state/helpers.js
+++ b/src/platform/forms-system/src/js/state/helpers.js
@@ -115,7 +115,13 @@ export function isContentExpanded(data, matcher, formData) {
* The path parameter will contain the path, relative to formData, to the
* form data corresponding to the current schema object
*/
-export function setHiddenFields(schema, uiSchema, formData, path = []) {
+export function setHiddenFields(
+ schema,
+ uiSchema,
+ formData,
+ path = [],
+ fullData,
+) {
if (!uiSchema) {
return schema;
}
@@ -131,7 +137,7 @@ export function setHiddenFields(schema, uiSchema, formData, path = []) {
null,
);
- if (hideIf && hideIf(formData, index)) {
+ if (hideIf && hideIf(formData, index, fullData)) {
if (!updatedSchema['ui:hidden']) {
updatedSchema = set('ui:hidden', true, updatedSchema);
}
@@ -167,6 +173,7 @@ export function setHiddenFields(schema, uiSchema, formData, path = []) {
uiSchema[next],
formData,
path.concat(next),
+ fullData,
);
if (newSchema !== updatedSchema.properties[next]) {
@@ -187,7 +194,13 @@ export function setHiddenFields(schema, uiSchema, formData, path = []) {
// each item has its own schema, so we need to update the required fields on those schemas
// and then check for differences
const newItemSchemas = updatedSchema.items.map((item, idx) =>
- setHiddenFields(item, uiSchema.items, formData, path.concat(idx)),
+ setHiddenFields(
+ item,
+ uiSchema.items,
+ formData,
+ path.concat(idx),
+ fullData,
+ ),
);
if (
@@ -632,7 +645,7 @@ export function updateSchemasAndData(
newSchema = updateRequiredFields(newSchema, uiSchema, formData);
// Update the schema with any fields that are now hidden because of the data change
- newSchema = setHiddenFields(newSchema, uiSchema, formData);
+ newSchema = setHiddenFields(newSchema, uiSchema, formData, [], fullData);
// Update the uiSchema and schema with any general updates based on the new data
const newUiSchema = updateUiSchema(
diff --git a/src/platform/forms-system/test/js/state/helpers.unit.spec.js b/src/platform/forms-system/test/js/state/helpers.unit.spec.js
index 0b23b6d3020c..b1b5e50e3e6d 100644
--- a/src/platform/forms-system/test/js/state/helpers.unit.spec.js
+++ b/src/platform/forms-system/test/js/state/helpers.unit.spec.js
@@ -330,6 +330,7 @@ describe('Schemaform formState:', () => {
expect(newSchema).not.to.equal(schema);
});
it('should set hidden on array field', () => {
+ const hideIfSpy = sinon.stub().returns(true);
const schema = {
type: 'array',
items: [
@@ -351,17 +352,20 @@ describe('Schemaform formState:', () => {
items: {
field: {
'ui:options': {
- hideIf: () => true,
+ hideIf: hideIfSpy,
},
},
},
};
- const data = [{}];
+ const data = [{ test2: true }];
+ const path = ['test'];
+ const fullData = { test: data, test3: false };
- const newSchema = setHiddenFields(schema, uiSchema, data);
+ const newSchema = setHiddenFields(schema, uiSchema, data, path, fullData);
expect(newSchema).not.to.equal(schema);
expect(newSchema.items[0].properties.field['ui:hidden']).to.be.true;
+ expect(hideIfSpy.args[0]).to.deep.equal([data, 0, fullData]);
});
});
describe('removeHiddenData', () => {
diff --git a/src/platform/utilities/feature-toggles/featureFlagNames.json b/src/platform/utilities/feature-toggles/featureFlagNames.json
index f9f6d0ce95b6..8bc84a882152 100644
--- a/src/platform/utilities/feature-toggles/featureFlagNames.json
+++ b/src/platform/utilities/feature-toggles/featureFlagNames.json
@@ -73,8 +73,6 @@
"facilitiesPpmsSuppressPharmacies": "facilities_ppms_suppress_pharmacies",
"facilitiesUseAddressTypeahead": "facilities_use_address_typeahead",
"facilityLocatorPredictiveLocationSearch": "facility_locator_predictive_location_search",
- "facilityLocatorShowCommunityCares": "facility_locator_show_community_cares",
- "facilityLocatorShowOperationalHoursSpecialInstructions": "facility_locator_show_operational_hours_special_instructions",
"fileUploadShortWorkflowEnabled": "file_upload_short_workflow_enabled",
"financialStatusReportDebtsApiModule": "financial_status_report_debts_api_module",
"financialStatusReportExpensesUpdate": "financial_status_report_expenses_update",
diff --git a/yarn.lock b/yarn.lock
index 56c5d62aace8..11880cd172e0 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2840,10 +2840,10 @@
dependencies:
jsx-ast-utils "^3.3.3"
-"@department-of-veterans-affairs/generator-vets-website@^3.12.1":
- version "3.12.1"
- resolved "https://registry.npmjs.org/@department-of-veterans-affairs/generator-vets-website/-/generator-vets-website-3.12.1.tgz#6f0c749df4717bf08b7ec4ff27c9e04bff1c46d2"
- integrity sha512-4aTVkJuaWX6tRS9J03DVHRmRBfGLiyffLn7gvatE42eosmgRzCQ5okU/6UzC5GAl6R85EDwj3dFY+Djq8Y+Rag==
+"@department-of-veterans-affairs/generator-vets-website@^3.12.2":
+ version "3.12.2"
+ resolved "https://registry.npmjs.org/@department-of-veterans-affairs/generator-vets-website/-/generator-vets-website-3.12.2.tgz#ba7b3428931e3420114f22353350af0021ffd685"
+ integrity sha512-WH3/ytf+2GMMHw65o4kxg3jdnfAByKxof+N8YiHYPPbQika33kvpwn3scUV2EfAL8T7MuYdnAyPGvOYeqdp+ag==
dependencies:
chalk "^4.1.2"
yeoman-generator "^5.6.1"