', () => {
});
});
- it('renders selected facility description text', () => {
- const { getByRole, getByText } = subject();
- expect(
- getByRole('heading', {
- level: 3,
- name: /Confirm your health care facilities/i,
- }),
- ).to.be.visible;
- expect(
- getByRole('heading', {
- level: 4,
- name: /The Veteran’s Facility you selected/i,
- }),
- ).to.be.visible;
- expect(
- getByText(
- /This is the facility where you told us the Veteran receives or plans to receive treatment/i,
- ),
- ).to.be.visible;
- });
-
- it('should render veteran selected facility name', () => {
- const { selectors } = subject();
- expect(selectors().selectedFacility.name).to.exist;
- });
-
- it('should render veteran selected facility address', () => {
- const { selectors } = subject();
-
- expect(selectors().selectedFacility.address1).to.exist;
- expect(selectors().selectedFacility.address2).to.exist;
- expect(selectors().selectedFacility.address3).to.exist;
- });
-
- it('renders caregive facility description text', () => {
- const { getByRole, getByText } = subject();
- expect(
- getByRole('heading', {
- level: 4,
- name: /Your assigned caregiver support facility/i,
- }),
- ).to.be.visible;
- expect(
- getByText(
- /This is the facility we’ve assigned to support you in the application process and has a caregiver support coordinator on staff. The coordinator at this facility will support you through the application process./i,
- ),
- ).to.be.visible;
- });
+ it('should render caregiver facility name and address', () => {
+ const { getByText } = subject();
+ const caregiverFacilityAddress = caregiverFacility.address.physical;
- it('should render caregiver facility name', () => {
- const { selectors } = subject();
- expect(selectors().caregiverFacility.name).to.exist;
+ expect(getByText(new RegExp(caregiverFacility.name))).to.exist;
+ expect(getByText(new RegExp(caregiverFacilityAddress.address1))).to.exist;
+ expect(getByText(new RegExp(caregiverFacilityAddress.address2))).to.exist;
+ expect(getByText(new RegExp(caregiverFacilityAddress.address3))).to.exist;
});
- it('should render caregiver facility address', () => {
- const { selectors } = subject();
+ it('should render veteran selected facility name and address', () => {
+ const { getByText } = subject();
+ const selectedFacilityAddress = selectedFacility.address.physical;
- expect(selectors().caregiverFacility.address1).to.exist;
- expect(selectors().caregiverFacility.address2).to.exist;
- expect(selectors().caregiverFacility.address3).to.exist;
+ expect(getByText(new RegExp(selectedFacility.name))).to.exist;
+ expect(getByText(new RegExp(selectedFacilityAddress.address1))).to.exist;
+ expect(getByText(new RegExp(selectedFacilityAddress.address2))).to.exist;
+ expect(getByText(new RegExp(selectedFacilityAddress.address3))).to.exist;
});
});
diff --git a/src/applications/combined-debt-portal/combined/components/AlertCard.jsx b/src/applications/combined-debt-portal/combined/components/AlertCard.jsx
index ea99e431f15e..70c13ebcf1ed 100644
--- a/src/applications/combined-debt-portal/combined/components/AlertCard.jsx
+++ b/src/applications/combined-debt-portal/combined/components/AlertCard.jsx
@@ -1,5 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { CONTACTS } from '@department-of-veterans-affairs/component-library/contacts';
import { APP_TYPES } from '../utils/helpers';
const AlertCard = ({ appType }) => {
@@ -22,11 +23,32 @@ const AlertCard = ({ appType }) => {
is unavailable because something went wrong on our end. Please check
back soon.
-
- If you continue having trouble viewing information about your{' '}
- {`${appType === APP_TYPES.DEBT ? 'debts' : 'copays'}`}, contact us
- online through Ask VA.
-
+ What you can do
+ {appType === APP_TYPES.DEBT ? (
+ <>
+
+ If you continue having trouble viewing information about your
+ current debts, contact us online through{' '}
+ Ask VA.
+
+
+ If you need immediate assistance call the Debt Management Center
+ at (
+
+ ). For international callers, use{' '}
+ .
+ We’re here Monday through Friday, 7:30 a.m. to 7:00 p.m. ET.
+
+ >
+ ) : (
+
+ If you continue having trouble viewing information about your
+ copays, call the VA Health Resource Center at{' '}
+ (
+
+ ). We’re here Monday through Friday, 8:00 a.m. to 8:00 p.m. ET.
+
+ )}
>
);
diff --git a/src/applications/combined-debt-portal/combined/components/ComboAlerts.jsx b/src/applications/combined-debt-portal/combined/components/ComboAlerts.jsx
index 2efe7121fad3..aaa4b2a63ebc 100644
--- a/src/applications/combined-debt-portal/combined/components/ComboAlerts.jsx
+++ b/src/applications/combined-debt-portal/combined/components/ComboAlerts.jsx
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import recordEvent from 'platform/monitoring/record-event';
+import { CONTACTS } from '@department-of-veterans-affairs/component-library/contacts';
import { ALERT_TYPES } from '../utils/helpers';
const ComboAlert = ({ children }) => children;
@@ -26,6 +27,20 @@ ComboAlert.Error = () => {
debts and bills, contact us online through{' '}
Ask VA.
+
+ If you need immediate assistance with overpayment debt, call the Debt
+ Management Center at (
+
+ ). For international callers, use{' '}
+ . We’re
+ here Monday through Friday, 7:30 a.m. to 7:00 p.m. ET.
+
+
+ If you need immediate assistance with copay bills, call the VA Health
+ Resource Center at (
+
+ ). We’re here Monday through Friday, 8:00 a.m. to 8:00 p.m. ET.
+
diff --git a/src/applications/combined-debt-portal/combined/utils/alert-messages.jsx b/src/applications/combined-debt-portal/combined/utils/alert-messages.jsx
index 3d108334a12c..7c1b096d9505 100644
--- a/src/applications/combined-debt-portal/combined/utils/alert-messages.jsx
+++ b/src/applications/combined-debt-portal/combined/utils/alert-messages.jsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { CONTACTS } from '@department-of-veterans-affairs/component-library/contacts';
import { ALERT_TYPES, APP_TYPES } from './helpers';
const alertMessage = (alertType, appType) => {
@@ -43,28 +44,38 @@ const alertMessage = (alertType, appType) => {
appType === APP_TYPES.DEBT ? 'debt' : 'copay'
} records right now`,
body: (
+
+ We’re sorry. Information about{' '}
+ {`${appType === APP_TYPES.DEBT ? 'debts' : 'copays'}`} you might
+ have is unavailable because something went wrong on our end. Please
+ check back soon.
+
+ ),
+ secondHeader: `What you can do`,
+ secondBody: (
<>
{appType === APP_TYPES.DEBT ? (
<>
-
- We’re sorry. Information about{' '}
- {`${appType === APP_TYPES.DEBT ? 'debts' : 'copays'}`} you
- might have is unavailable because something went wrong on our
- end. Please check back soon.
-
-
- If you continue having trouble viewing information about your{' '}
- {`${appType === APP_TYPES.DEBT ? 'debts' : 'copays'}`},
- contact us online through{' '}
+
+ If you continue having trouble viewing information about your
+ current debts, contact us online through{' '}
Ask VA.
+
+ If you need immediate assistance call the Debt Management
+ Center at (
+
+ ). For international callers, use{' '}
+
+ . We’re here Monday through Friday, 7:30 a.m. to 7:00 p.m. ET.
+
>
) : (
- Please check back soon. If you continue having trouble viewing
- information about your copays, call the VA Health Resource
- Center at (
-
+ If you continue having trouble viewing information about your
+ copays, call the VA Health Resource Center at{' '}
+ (
+
). We’re here Monday through Friday, 8:00 a.m. to 8:00 p.m. ET.
)}
@@ -121,12 +132,27 @@ const alertMessage = (alertType, appType) => {
current debts and bills, contact us online through{' '}
Ask VA.
-
+
+ If you need immediate assistance with overpayment debt, call the
+ Debt Management Center at {' '}
+ (
+ ). For international callers, use{' '}
+ .
+ We’re here Monday through Friday, 7:30 a.m. to 7:00 p.m. ET.
+
+
+ If you need immediate assistance with copay bills, call the VA
+ Health Resource Center at (
+
+ ). We’re here Monday through Friday, 8:00 a.m. to 8:00 p.m. ET.
+
+
+
+
>
),
};
diff --git a/src/applications/combined-debt-portal/debt-letters/containers/DebtLettersSummary.jsx b/src/applications/combined-debt-portal/debt-letters/containers/DebtLettersSummary.jsx
index a623854d9d28..35f572681f21 100644
--- a/src/applications/combined-debt-portal/debt-letters/containers/DebtLettersSummary.jsx
+++ b/src/applications/combined-debt-portal/debt-letters/containers/DebtLettersSummary.jsx
@@ -25,13 +25,12 @@ const renderAlert = (alertType, statements) => {
{alertInfo.header}
{alertInfo.body}
- {showOther && }
- {alertType === ALERT_TYPES.ALL_ERROR && (
+ {alertInfo.secondHeader ? (
<>
{alertInfo.secondHeader}
{alertInfo.secondBody}
>
- )}
+ ) : null}
{showVAReturnLink ? (
{
text="Return to VA.gov"
/>
) : null}
+ {showOther && }
);
};
@@ -58,7 +58,7 @@ const renderOtherVA = (mcpLength, mcpError) => {
{alertInfo.header}
- {alertInfo.body}
+ {alertInfo.secondBody}
>
);
@@ -189,6 +189,11 @@ const DebtLettersSummary = () => {
>
{title}
+
+ Check the details of debt you might have from VA education, disability
+ compensation, or pension programs. Find out how to pay your debt and
+ what to do if you need financial assistance.
+
Please note that payments may take up to 4 business days to reflect
after processing.
diff --git a/src/applications/combined-debt-portal/medical-copays/components/Balances.jsx b/src/applications/combined-debt-portal/medical-copays/components/Balances.jsx
index b52fb1473865..73cf3e5c87da 100644
--- a/src/applications/combined-debt-portal/medical-copays/components/Balances.jsx
+++ b/src/applications/combined-debt-portal/medical-copays/components/Balances.jsx
@@ -19,9 +19,8 @@ export const Balances = ({ statements }) => {
{statements?.length === 1 ? single : multiple}
- Any payments you may have made to your current copays will not be
- reflected here until our systems are updated with your next monthly
- statement.
+ Any payments you have made will not be reflected here until our systems
+ are updated with your next monthly statement.
{statements?.map((balance, idx) => {
diff --git a/src/applications/combined-debt-portal/medical-copays/containers/HTMLStatementPage.jsx b/src/applications/combined-debt-portal/medical-copays/containers/HTMLStatementPage.jsx
index 3f51145508dc..32b06f69efdc 100644
--- a/src/applications/combined-debt-portal/medical-copays/containers/HTMLStatementPage.jsx
+++ b/src/applications/combined-debt-portal/medical-copays/containers/HTMLStatementPage.jsx
@@ -34,9 +34,8 @@ const HTMLStatementPage = ({ match }) => {
const fullName = userFullName.middle
? `${userFullName.first} ${userFullName.middle} ${userFullName.last}`
: `${userFullName.first} ${userFullName.last}`;
- const acctNum = selectedCopay?.pHAccountNumber
- ? selectedCopay?.pHAccountNumber.toString()
- : selectedCopay?.pHCernerAccountNumber.toString();
+ const acctNum =
+ selectedCopay?.accountNumber || selectedCopay?.pHAccountNumber;
useHeaderPageTitle(title);
@@ -84,7 +83,7 @@ const HTMLStatementPage = ({ match }) => {
paymentsReceived={selectedCopay.pHTotCredits}
previousBalance={selectedCopay.pHPrevBal}
statementDate={statementDate}
- acctNum={selectedCopay.pHAccountNumber}
+ acctNum={acctNum}
/>
{
{alertInfo.header}
{alertInfo.body}
- {showOther && }
- {alertType === ALERT_TYPES.ALL_ERROR && (
+ {alertInfo.secondHeader ? (
<>
{alertInfo.secondHeader}
{alertInfo.secondBody}
>
- )}
+ ) : null}
{showVAReturnLink ? (
{
text="Return to VA.gov"
/>
) : null}
+ {showOther && }
);
};
@@ -62,7 +62,7 @@ const renderOtherVA = (debtLength, debtError) => {
{alertInfo.header}
- {alertInfo.body}
+ {alertInfo.secondBody}
>
);
diff --git a/src/applications/combined-debt-portal/medical-copays/tests/e2e/fixtures/mocks/copays.json b/src/applications/combined-debt-portal/medical-copays/tests/e2e/fixtures/mocks/copays.json
index 5f2fc1732787..ca0d16c634b2 100644
--- a/src/applications/combined-debt-portal/medical-copays/tests/e2e/fixtures/mocks/copays.json
+++ b/src/applications/combined-debt-portal/medical-copays/tests/e2e/fixtures/mocks/copays.json
@@ -2,6 +2,7 @@
"data": [
{
"id": "f4385298-08a6-42f8-a86f-50e97033fb85",
+ "accountNumber": "57 0000 0001 97750 IPOAD",
"pSSeqNum": 506,
"pSTotSeqNum": 588,
"pSFacilityNum": "534",
@@ -170,6 +171,7 @@
},
{
"id": "b381cc7b-ea3a-49dc-a982-7146416ed373",
+ "accountNumber": "60 0000 0001 97750 IPOAD",
"pSSeqNum": 1162,
"pSTotSeqNum": 1,
"pSFacilityNum": "757",
diff --git a/src/applications/disability-benefits/686c-674/containers/App.jsx b/src/applications/disability-benefits/686c-674/containers/App.jsx
index 6b6455fac903..dd1f44419824 100644
--- a/src/applications/disability-benefits/686c-674/containers/App.jsx
+++ b/src/applications/disability-benefits/686c-674/containers/App.jsx
@@ -1,7 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
import RoutedSavableApp from 'platform/forms/save-in-progress/RoutedSavableApp';
-import { VA_FORM_IDS } from '@department-of-veterans-affairs/platform-forms/constants';
import { useBrowserMonitoring } from '~/platform/utilities/real-user-monitoring';
import { useFeatureToggle } from '~/platform/utilities/feature-toggles';
import manifest from '../manifest.json';
@@ -15,7 +14,6 @@ function App({
isLoading,
vaFileNumber,
featureToggles,
- savedForms,
}) {
const { TOGGLE_NAMES } = useFeatureToggle();
useBrowserMonitoring({
@@ -31,16 +29,7 @@ function App({
return ;
}
- const flipperV2 = featureToggles.vaDependentsV2;
- const hasV1Form = savedForms.some(
- form => form.form === VA_FORM_IDS.FORM_21_686C,
- );
- const hasV2Form = savedForms.some(
- form => form.form === VA_FORM_IDS.FORM_21_686CV2,
- );
-
- const shouldUseV2 = hasV2Form || (flipperV2 && !hasV1Form);
- if (shouldUseV2) {
+ if (featureToggles.vaDependentsV2) {
window.location.href =
'/view-change-dependents/add-remove-form-21-686c-v2/';
return <>>;
@@ -52,6 +41,7 @@ function App({
);
+
// If on intro page, just return
if (location.pathname === '/introduction') {
return content;
diff --git a/src/applications/disability-benefits/all-claims/components/Autocomplete.jsx b/src/applications/disability-benefits/all-claims/components/Autocomplete.jsx
index 9f085ab5ad18..d582e2591289 100644
--- a/src/applications/disability-benefits/all-claims/components/Autocomplete.jsx
+++ b/src/applications/disability-benefits/all-claims/components/Autocomplete.jsx
@@ -179,6 +179,7 @@ const Autocomplete = ({
data-testid="autocomplete-list"
role="listbox"
tabIndex={-1}
+ aria-label="List of matching conditions"
>
{results.map((result, index) => (
Enter your condition as "
{option}"
@@ -341,7 +341,13 @@ export class ComboBox extends React.Component {
}
render() {
- const { searchTerm, ariaLive1, ariaLive2, filteredOptions } = this.state;
+ const {
+ searchTerm,
+ ariaLive1,
+ ariaLive2,
+ filteredOptions,
+ highlightedIndex,
+ } = this.state;
const autocompleteHelperText =
searchTerm?.length > 0
? null
@@ -351,6 +357,11 @@ export class ComboBox extends React.Component {
with swipe gestures.
`;
+ const activedescendant =
+ highlightedIndex === -1 || filteredOptions.length === 0
+ ? null
+ : `option-${highlightedIndex}`;
+
return (
0
- ? `option-${this.state.highlightedIndex}`
- : null
- }
+ aria-activedescendant={activedescendant}
tabIndex={-1}
>
{this.drawFreeTextOption(searchTerm)}
@@ -390,7 +397,7 @@ export class ComboBox extends React.Component {
filteredOptions.map((option, index) => {
const optionIndex = index + 1;
let classNameStr = 'cc-combobox__option';
- if (optionIndex === this.state.highlightedIndex) {
+ if (optionIndex === highlightedIndex) {
classNameStr += ' cc-combobox__option--active';
}
return (
@@ -408,11 +415,7 @@ export class ComboBox extends React.Component {
onKeyDown={this.handleKeyPress}
label={option}
role="option"
- aria-selected={
- optionIndex === this.state.highlightedIndex
- ? 'true'
- : 'false'
- }
+ aria-selected={optionIndex === highlightedIndex}
id={`option-${optionIndex}`}
>
{option}
diff --git a/src/applications/disability-benefits/all-claims/config/form0781/index.js b/src/applications/disability-benefits/all-claims/config/form0781/index.js
index 50fe50946be3..1f2bc6d83538 100644
--- a/src/applications/disability-benefits/all-claims/config/form0781/index.js
+++ b/src/applications/disability-benefits/all-claims/config/form0781/index.js
@@ -1,5 +1,8 @@
import * as workflowChoicePage from '../../pages/form0781/workflowChoicePage';
-import { showForm0781Pages } from '../../utils/form0781';
+import * as mentalHealthSupport from '../../pages/form0781/mentalHealthSupport';
+import * as traumaticEventsIntro from '../../pages/form0781/traumaticEventsIntro';
+import * as eventType from '../../pages/form0781/traumaticEventTypes';
+import { showForm0781Pages, isCompletingForm0781 } from '../../utils/form0781';
/**
* Configuration for our modern 0781 paper sync (2024/2025)
@@ -13,4 +16,25 @@ export const form0781PagesConfig = {
uiSchema: workflowChoicePage.uiSchema,
schema: workflowChoicePage.schema,
},
+ mentalHealthSupport: {
+ title: 'Mental health support',
+ path: 'additional-forms/mental-health-statement/support',
+ depends: formData => isCompletingForm0781(formData),
+ uiSchema: mentalHealthSupport.uiSchema,
+ schema: mentalHealthSupport.schema,
+ },
+ eventsIntro: {
+ title: 'Traumatic events',
+ path: 'additional-forms/mental-health-statement/events',
+ depends: formData => isCompletingForm0781(formData),
+ uiSchema: traumaticEventsIntro.uiSchema,
+ schema: traumaticEventsIntro.schema,
+ },
+ eventType: {
+ title: 'Types of traumatic events',
+ path: 'additional-forms/mental-health-statement/events-type',
+ depends: formData => isCompletingForm0781(formData),
+ uiSchema: eventType.uiSchema,
+ schema: eventType.schema,
+ },
};
diff --git a/src/applications/disability-benefits/all-claims/constants.js b/src/applications/disability-benefits/all-claims/constants.js
index dea9ccc4f157..987f6c61ccda 100644
--- a/src/applications/disability-benefits/all-claims/constants.js
+++ b/src/applications/disability-benefits/all-claims/constants.js
@@ -403,3 +403,11 @@ export const ADDITIONAL_EXPOSURES = Object.freeze({
none: 'None of these',
notsure: 'I’m not sure if I have been exposed to these hazards',
});
+
+export const TRAUMATIC_EVENT_TYPES = Object.freeze({
+ combat: 'Traumatic events related to combat',
+ mst:
+ 'Traumatic events related to sexual assault or harassment (also known as military sexual trauma or MST)',
+ nonMst: 'Traumatic events related to other personal interactions',
+ other: 'Other traumatic events',
+});
diff --git a/src/applications/disability-benefits/all-claims/content/form0781.jsx b/src/applications/disability-benefits/all-claims/content/form0781.jsx
index 837d76261baa..744b4620f473 100644
--- a/src/applications/disability-benefits/all-claims/content/form0781.jsx
+++ b/src/applications/disability-benefits/all-claims/content/form0781.jsx
@@ -1 +1,75 @@
+import React from 'react';
+
export const additionalFormsTitle = 'Additional Forms';
+
+export const form0781WorkflowChoices = {
+ COMPLETE_ONLINE_FORM: 'optForOnlineForm0781',
+ SUBMIT_PAPER_FORM: 'optForPaperForm0781Upload',
+ OPT_OUT_OF_FORM0781: 'optOutOfForm0781',
+};
+
+export const traumaticEventsExamples = (
+
+
+ Examples of traumatic events
+
+ Traumatic events related to combat
+
+
+ - You were engaged in combat with enemy forces
+ - You experienced fear of hostile military or terrorist activity
+ - You served in an imminent danger area
+ - You served as a drone aircraft crew member
+
+
+ Traumatic events related to sexual assault or harassment
+
+
+ -
+ You experienced pressure to engage in sexual activities (for example,
+ someone threatened you with bad treatment for refusing sex, or
+ promised you better treatment in exchange for sex)
+
+ -
+ You were pressured into sexual activities against your will (for
+ example, when you were asleep or intoxicated)
+
+ - You were physically forced into sexual activities
+ -
+ You experienced offensive comments about your body or sexual
+ activities
+
+ - You experienced unwanted sexual advances
+ -
+ You experienced someone touching or grabbing you against your will,
+ including during hazing
+
+
+
+ Traumatic events related to other personal interactions
+
+
+ -
+ You experienced physical assault, battery, robbery, mugging, stalking,
+ or harassment by a person who wasn’t part of an enemy force
+
+ - You experienced domestic intimate partner abuse or harassment
+
+ Other traumatic events
+
+ - You got into a car accident
+ - You witnessed a natural disaster, like a hurricane
+ - You worked on burn ward or graves registration
+ -
+ You witnessed the death, injury, or threat to another person or to
+ yourself, that was caused by something other than a hostile military
+ or terrorist activity
+
+ -
+ You experienced or witnessed friendly fire that occurred on a gunnery
+ range during a training mission
+
+
+
+
+);
diff --git a/src/applications/disability-benefits/all-claims/content/mentalHealth.jsx b/src/applications/disability-benefits/all-claims/content/mentalHealth.jsx
index 9c4af7f6dcdb..eb58115f96cb 100644
--- a/src/applications/disability-benefits/all-claims/content/mentalHealth.jsx
+++ b/src/applications/disability-benefits/all-claims/content/mentalHealth.jsx
@@ -1,4 +1,3 @@
-import React from 'react';
import {
isClaimingNew,
makeConditionsSchema,
@@ -14,72 +13,6 @@ export const conditionsQuestion =
export const examplesHint =
'Examples of mental health disorders include, but are not limited to, post-traumatic stress disorder (PTSD), depression, anxiety, and bipolar disorder.';
-export const traumaticEventsInfo = (
-
-
- Examples of traumatic events
-
- Traumatic events related to combat
-
-
- - You were engaged in combat with enemy forces
- - You experienced fear of hostile military or terrorist activity
- - You served in an imminent danger area
- - You served as a drone aircraft crew member
-
-
- Traumatic events related to sexual assault or harassment
-
-
- -
- You experienced pressure to engage in sexual activities (for example,
- someone threatened you with bad treatment for refusing sex, or
- promised you better treatment in exchange for sex)
-
- -
- You were pressured into sexual activities against your will (for
- example, when you were asleep or intoxicated)
-
- - You were physically forced into sexual activities
- -
- You experienced offensive comments about your body or sexual
- activities
-
- - You experienced unwanted sexual advances
- -
- You experienced someone touching or grabbing you against your will,
- including during hazing
-
-
-
- Traumatic events related to other personal interactions
-
-
- -
- You experienced physical assault, battery, robbery, mugging, stalking,
- or harassment by a person who wasn’t part of an enemy force
-
- - You experienced domestic intimate partner abuse or harassment
-
- Other traumatic events
-
- - You got into a car accident
- - You witnessed a natural disaster, like a hurricane
- - You worked on burn ward or graves registration
- -
- You witnessed the death, injury, or threat to another person or to
- yourself, that was caused by something other than a hostile military
- or terrorist activity
-
- -
- You experienced or witnessed friendly fire that occurred on a gunnery
- range during a training mission
-
-
-
-
-);
-
export const noneAndConditionError =
'If you’re not claiming any mental health conditions related to a traumatic event, unselect the other options you selected';
diff --git a/src/applications/disability-benefits/all-claims/content/mentalHealthSupport.jsx b/src/applications/disability-benefits/all-claims/content/mentalHealthSupport.jsx
new file mode 100644
index 000000000000..d9dd9e3c9d06
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/content/mentalHealthSupport.jsx
@@ -0,0 +1,4 @@
+// TODO: additional content will be added in ticket #97079
+
+/* ---------- content ----------*/
+export const mentalHealthSupportPageTitle = 'Mental health support';
diff --git a/src/applications/disability-benefits/all-claims/content/traumaticEventTypes.jsx b/src/applications/disability-benefits/all-claims/content/traumaticEventTypes.jsx
new file mode 100644
index 000000000000..bcbce47ab551
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/content/traumaticEventTypes.jsx
@@ -0,0 +1,8 @@
+// TODO: additional content will be added in ticket #97079
+
+/* ---------- content ----------*/
+export const eventTypesPageTitle = 'Types of traumatic events';
+export const eventTypesQuestion =
+ 'Which of these did you experience during your military service? Select all that you experienced.';
+export const eventTypesHint =
+ 'You can tell us about a single event, or a recurring or ongoing experience.';
diff --git a/src/applications/disability-benefits/all-claims/content/traumaticEventsIntro.jsx b/src/applications/disability-benefits/all-claims/content/traumaticEventsIntro.jsx
new file mode 100644
index 000000000000..d54933b0073a
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/content/traumaticEventsIntro.jsx
@@ -0,0 +1,4 @@
+// TODO: additional content will be added in ticket #97079
+
+/* ---------- content ----------*/
+export const eventsPageTitle = 'Traumatic events';
diff --git a/src/applications/disability-benefits/all-claims/pages/form0781/mentalHealthSupport.js b/src/applications/disability-benefits/all-claims/pages/form0781/mentalHealthSupport.js
new file mode 100644
index 000000000000..f1a708230f8a
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/pages/form0781/mentalHealthSupport.js
@@ -0,0 +1,13 @@
+// TODO: this is a placeholder; structure will be added in ticket #97079
+import { mentalHealthSupportPageTitle } from '../../content/mentalHealthSupport';
+
+import { formTitle } from '../../utils';
+
+export const uiSchema = {
+ 'ui:title': formTitle(mentalHealthSupportPageTitle),
+};
+
+export const schema = {
+ type: 'object',
+ properties: {},
+};
diff --git a/src/applications/disability-benefits/all-claims/pages/form0781/traumaticEventTypes.js b/src/applications/disability-benefits/all-claims/pages/form0781/traumaticEventTypes.js
new file mode 100644
index 000000000000..87b204e0af9b
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/pages/form0781/traumaticEventTypes.js
@@ -0,0 +1,43 @@
+import {
+ checkboxGroupUI,
+ checkboxGroupSchema,
+} from 'platform/forms-system/src/js/web-component-patterns';
+import {
+ eventTypesPageTitle,
+ eventTypesQuestion,
+ eventTypesHint,
+} from '../../content/traumaticEventTypes';
+import { formTitle } from '../../utils';
+import { TRAUMATIC_EVENT_TYPES } from '../../constants';
+import { traumaticEventsExamples } from '../../content/form0781';
+
+export const uiSchema = {
+ 'ui:title': formTitle(eventTypesPageTitle),
+ mentalHealth: {
+ eventTypes: checkboxGroupUI({
+ title: eventTypesQuestion,
+ hint: eventTypesHint,
+ labels: TRAUMATIC_EVENT_TYPES,
+ required: false,
+ }),
+ },
+ 'view:traumaticEventsInfo': {
+ 'ui:description': traumaticEventsExamples,
+ },
+};
+
+export const schema = {
+ type: 'object',
+ properties: {
+ mentalHealth: {
+ type: 'object',
+ properties: {
+ eventTypes: checkboxGroupSchema(Object.keys(TRAUMATIC_EVENT_TYPES)),
+ },
+ },
+ 'view:traumaticEventsInfo': {
+ type: 'object',
+ properties: {},
+ },
+ },
+};
diff --git a/src/applications/disability-benefits/all-claims/pages/form0781/traumaticEventsIntro.js b/src/applications/disability-benefits/all-claims/pages/form0781/traumaticEventsIntro.js
new file mode 100644
index 000000000000..0080b765186f
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/pages/form0781/traumaticEventsIntro.js
@@ -0,0 +1,13 @@
+// TODO: this is a placeholder; structure will be added in ticket #97079
+import { eventsPageTitle } from '../../content/traumaticEventsIntro';
+
+import { formTitle } from '../../utils';
+
+export const uiSchema = {
+ 'ui:title': formTitle(eventsPageTitle),
+};
+
+export const schema = {
+ type: 'object',
+ properties: {},
+};
diff --git a/src/applications/disability-benefits/all-claims/pages/mentalHealth/mentalHealthConditions.js b/src/applications/disability-benefits/all-claims/pages/mentalHealth/mentalHealthConditions.js
index 1c2e33805f59..846e12a01963 100644
--- a/src/applications/disability-benefits/all-claims/pages/mentalHealth/mentalHealthConditions.js
+++ b/src/applications/disability-benefits/all-claims/pages/mentalHealth/mentalHealthConditions.js
@@ -1,6 +1,5 @@
import { checkboxGroupSchema } from 'platform/forms-system/src/js/web-component-patterns';
import {
- traumaticEventsInfo,
conditionsPageTitle,
conditionsQuestion,
examplesHint,
@@ -8,6 +7,7 @@ import {
makeMHConditionsUISchema,
validateMHConditions,
} from '../../content/mentalHealth';
+import { traumaticEventsExamples } from '../../content/form0781';
import { formTitle, makeConditionsUI } from '../../utils';
export const uiSchema = {
@@ -21,7 +21,7 @@ export const uiSchema = {
}),
},
'view:traumaticEventsInfo': {
- 'ui:description': traumaticEventsInfo,
+ 'ui:description': traumaticEventsExamples,
},
'ui:validations': [validateMHConditions],
};
diff --git a/src/applications/disability-benefits/all-claims/tests/pages/form0781/mentalHealthSupport.unit.spec.js b/src/applications/disability-benefits/all-claims/tests/pages/form0781/mentalHealthSupport.unit.spec.js
new file mode 100644
index 000000000000..5e068605636c
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/tests/pages/form0781/mentalHealthSupport.unit.spec.js
@@ -0,0 +1,12 @@
+import { expect } from 'chai';
+import * as mentalHealthSupport from '../../../pages/form0781/mentalHealthSupport';
+
+describe('Mental health support', () => {
+ it('should define a uiSchema object', () => {
+ expect(mentalHealthSupport.uiSchema).to.be.an('object');
+ });
+
+ it('should define a schema object', () => {
+ expect(mentalHealthSupport.schema).to.be.an('object');
+ });
+});
diff --git a/src/applications/disability-benefits/all-claims/tests/pages/form0781/traumaticEventTypes.unit.spec.js b/src/applications/disability-benefits/all-claims/tests/pages/form0781/traumaticEventTypes.unit.spec.js
new file mode 100644
index 000000000000..fbef9cd48c60
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/tests/pages/form0781/traumaticEventTypes.unit.spec.js
@@ -0,0 +1,93 @@
+import React from 'react';
+import { render } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import sinon from 'sinon';
+import { expect } from 'chai';
+import {
+ $,
+ $$,
+} from '@department-of-veterans-affairs/platform-forms-system/ui';
+import { DefinitionTester } from '@department-of-veterans-affairs/platform-testing/schemaform-utils';
+import { checkVaCheckbox } from '@department-of-veterans-affairs/platform-testing/helpers';
+import formConfig from '../../../config/form';
+import * as eventType from '../../../pages/form0781/traumaticEventTypes';
+import {
+ eventTypesPageTitle,
+ eventTypesQuestion,
+} from '../../../content/traumaticEventTypes';
+import { TRAUMATIC_EVENT_TYPES } from '../../../constants';
+
+describe('Traumatic event types', () => {
+ const {
+ schema,
+ uiSchema,
+ } = formConfig.chapters.additionalForms.pages.eventType;
+
+ it('should define a uiSchema object', () => {
+ expect(eventType.uiSchema).to.be.an('object');
+ });
+
+ it('should define a schema object', () => {
+ expect(eventType.schema).to.be.an('object');
+ });
+
+ it('should render with all checkboxes', () => {
+ const { container, getByText } = render(
+ ,
+ );
+
+ getByText(eventTypesPageTitle);
+
+ expect($$('va-checkbox-group', container).length).to.equal(1);
+ expect($('va-checkbox-group', container).getAttribute('label')).to.equal(
+ eventTypesQuestion,
+ );
+
+ // fail fast - verify the correct number of checkboxes are present
+ expect($$('va-checkbox', container).length).to.equal(
+ Object.keys(TRAUMATIC_EVENT_TYPES).length,
+ );
+
+ // verify each checkbox exists with user facing label
+ Object.values(TRAUMATIC_EVENT_TYPES).forEach(option => {
+ expect($$(`va-checkbox[label="${option}"]`, container)).to.exist;
+ });
+ });
+
+ it('should submit without selecting any event types', () => {
+ const onSubmit = sinon.spy();
+
+ const { getByText } = render(
+ ,
+ );
+
+ userEvent.click(getByText('Submit'));
+ expect(onSubmit.calledOnce).to.be.true;
+ });
+
+ it('should submit when 1 or more event types are selected', async () => {
+ const formData = {};
+ const onSubmit = sinon.spy();
+
+ const { container, getByText } = render(
+ ,
+ );
+ const checkboxGroup = $('va-checkbox-group', container);
+
+ checkVaCheckbox(checkboxGroup, 'combat');
+ checkVaCheckbox(checkboxGroup, 'nonMst');
+
+ userEvent.click(getByText('Submit'));
+ expect(onSubmit.calledOnce).to.be.true;
+ });
+});
diff --git a/src/applications/disability-benefits/all-claims/tests/pages/form0781/traumaticEventsIntro.unit.spec.js b/src/applications/disability-benefits/all-claims/tests/pages/form0781/traumaticEventsIntro.unit.spec.js
new file mode 100644
index 000000000000..df2ef6baa319
--- /dev/null
+++ b/src/applications/disability-benefits/all-claims/tests/pages/form0781/traumaticEventsIntro.unit.spec.js
@@ -0,0 +1,12 @@
+import { expect } from 'chai';
+import * as traumaticEvents from '../../../pages/form0781/traumaticEventsIntro';
+
+describe('Traumatic events', () => {
+ it('should define a uiSchema object', () => {
+ expect(traumaticEvents.uiSchema).to.be.an('object');
+ });
+
+ it('should define a schema object', () => {
+ expect(traumaticEvents.schema).to.be.an('object');
+ });
+});
diff --git a/src/applications/disability-benefits/all-claims/utils/form0781.js b/src/applications/disability-benefits/all-claims/utils/form0781.js
index e7da8fbb0ee0..741dca2f5aa2 100644
--- a/src/applications/disability-benefits/all-claims/utils/form0781.js
+++ b/src/applications/disability-benefits/all-claims/utils/form0781.js
@@ -1,5 +1,6 @@
// All flippers for the 0781 Papersync should be added to this file
import { isClaimingNew } from '.';
+import { form0781WorkflowChoices } from '../content/form0781';
/**
* Checks if the modern 0781 flow should be shown if the flipper is active for this veteran
@@ -23,3 +24,19 @@ export function showForm0781Pages(formData) {
)
);
}
+
+/**
+ * Checks if
+ * 1. modern 0781 pages should be showing
+ * 2. the option to complete the online form is selected
+ *
+ * @param {object} formData
+ * @returns {boolean} true if COMPLETE_ONLINE_FORM is selected, false otherwise
+ */
+export function isCompletingForm0781(formData) {
+ return (
+ showForm0781Pages(formData) &&
+ formData['view:mentalHealthWorkflowChoice'] ===
+ form0781WorkflowChoices.COMPLETE_ONLINE_FORM
+ );
+}
diff --git a/src/applications/financial-status-report/mocks/responses.js b/src/applications/financial-status-report/mocks/responses.js
index 0024d049f17c..2226cc7bcc18 100644
--- a/src/applications/financial-status-report/mocks/responses.js
+++ b/src/applications/financial-status-report/mocks/responses.js
@@ -37,6 +37,7 @@ module.exports = {
data: [
{
id: 'f4385298-08a6-42f8-a86f-50e97033fb85',
+ accountNumber: '57 0000 0001 97750 IPOAD',
pSSeqNum: 506,
pSTotSeqNum: 588,
pSFacilityNum: '534',
@@ -311,6 +312,7 @@ module.exports = {
},
{
id: 'b381cc7b-ea3a-49dc-a982-7146416ed373',
+ accountNumber: '60 0000 0001 97750 IPOAD',
pSSeqNum: 1162,
pSTotSeqNum: 1,
pSFacilityNum: '757',
diff --git a/src/applications/financial-status-report/tests/e2e/fixtures/mocks/copays.json b/src/applications/financial-status-report/tests/e2e/fixtures/mocks/copays.json
index 5f2fc1732787..ca0d16c634b2 100644
--- a/src/applications/financial-status-report/tests/e2e/fixtures/mocks/copays.json
+++ b/src/applications/financial-status-report/tests/e2e/fixtures/mocks/copays.json
@@ -2,6 +2,7 @@
"data": [
{
"id": "f4385298-08a6-42f8-a86f-50e97033fb85",
+ "accountNumber": "57 0000 0001 97750 IPOAD",
"pSSeqNum": 506,
"pSTotSeqNum": 588,
"pSFacilityNum": "534",
@@ -170,6 +171,7 @@
},
{
"id": "b381cc7b-ea3a-49dc-a982-7146416ed373",
+ "accountNumber": "60 0000 0001 97750 IPOAD",
"pSSeqNum": 1162,
"pSTotSeqNum": 1,
"pSFacilityNum": "757",
diff --git a/src/applications/financial-status-report/tests/unit/cfsr-unit-maximal.json b/src/applications/financial-status-report/tests/unit/cfsr-unit-maximal.json
index fb6b715128e6..ffba4229c774 100644
--- a/src/applications/financial-status-report/tests/unit/cfsr-unit-maximal.json
+++ b/src/applications/financial-status-report/tests/unit/cfsr-unit-maximal.json
@@ -350,6 +350,7 @@
},
{
"id": "f4385298-08a6-42f8-a86f-50e97033fb85",
+ "accountNumber": "57 0000 0001 97750 IPOAD",
"pSSeqNum": 506,
"pSTotSeqNum": 588,
"pSFacilityNum": "534",
@@ -522,6 +523,7 @@
},
{
"id": "b381cc7b-ea3a-49dc-a982-7146416ed373",
+ "accountNumber": "57 0000 0001 97750 IPOAD",
"pSSeqNum": 1162,
"pSTotSeqNum": 1,
"pSFacilityNum": "757",
diff --git a/src/applications/financial-status-report/wizard/pages/Disagree.jsx b/src/applications/financial-status-report/wizard/pages/Disagree.jsx
index 505702e500c5..32e93d863a36 100644
--- a/src/applications/financial-status-report/wizard/pages/Disagree.jsx
+++ b/src/applications/financial-status-report/wizard/pages/Disagree.jsx
@@ -62,9 +62,9 @@ const Disagree = () => {
What to know about debt waivers
- You have 180 days from the date you received your
- first debt letter to request a debt waiver. A waiver is a request to
- ask us to stop collection on your debt.
+ You have 1 year from the date you received your first
+ debt letter to request a debt waiver. A waiver is a request to ask us
+ to stop collection on your debt.
If you’re worried that we won’t complete your appeal before the
diff --git a/src/applications/financial-status-report/wizard/pages/Error.jsx b/src/applications/financial-status-report/wizard/pages/Error.jsx
index e44e977aca5b..6f26c9b938d5 100644
--- a/src/applications/financial-status-report/wizard/pages/Error.jsx
+++ b/src/applications/financial-status-report/wizard/pages/Error.jsx
@@ -52,7 +52,7 @@ const DebtError = () => {
Note:
- You have 180 days from the date you received your first
+ You have 1 year from the date you received your first
debt letter to submit your dispute statement. After this time, we can’t
consider the request.
diff --git a/src/applications/ivc-champva/10-10D/tests/unit/helpers/helpers.unit.spec.js b/src/applications/ivc-champva/10-10D/tests/unit/helpers/helpers.unit.spec.js
index 7d61f0f7603f..49cc963be419 100644
--- a/src/applications/ivc-champva/10-10D/tests/unit/helpers/helpers.unit.spec.js
+++ b/src/applications/ivc-champva/10-10D/tests/unit/helpers/helpers.unit.spec.js
@@ -1,4 +1,5 @@
import { expect } from 'chai';
+import sinon from 'sinon';
import React from 'react';
import {
applicantWording,
@@ -32,16 +33,36 @@ describe('applicantWording helper', () => {
});
});
-describe('getAgeInYears helper', () => {
- const year = Number(
- new Date()
- .getFullYear()
- .toString()
- .slice(-2),
- );
+describe('getAgeInYears', () => {
+ let clock;
- it('should return the proper age in years', () => {
- expect(getAgeInYears('2000-01-01')).to.equal(year);
+ beforeEach(() => {
+ // Mock Date.now() to always return a fixed value in 2024
+ // (Similar to ReferralTaskCard.unit.spec.js)
+ const fixedTimestamp = new Date('2024-12-31T00:00:00Z').getTime();
+ clock = sinon.useFakeTimers({ now: fixedTimestamp, toFake: ['Date'] });
+ });
+
+ afterEach(() => {
+ clock.restore();
+ });
+
+ it('should correctly calculate age in years', () => {
+ const birthDate = '1990-07-01';
+ const age = getAgeInYears(birthDate);
+ expect(age).to.equal(34);
+ });
+
+ it('should correctly calculate age with a New Year’s Day birthdate', () => {
+ const birthDate = '2000-01-01';
+ const age = getAgeInYears(birthDate);
+ expect(age).to.equal(24);
+ });
+
+ it('should correctly calculate age with a leap day birthdate', () => {
+ const birthDate = '2004-02-29';
+ const age = getAgeInYears(birthDate);
+ expect(age).to.equal(20);
});
});
diff --git a/src/applications/ivc-champva/shared/utilities.js b/src/applications/ivc-champva/shared/utilities.js
index e9680a2b3ee4..7596b7c15321 100644
--- a/src/applications/ivc-champva/shared/utilities.js
+++ b/src/applications/ivc-champva/shared/utilities.js
@@ -77,7 +77,14 @@ export function getConditionalPages(pages, data, index) {
// Expects a date as a string in YYYY-MM-DD format
export function getAgeInYears(date) {
- const difference = Date.now() - Date.parse(date);
+ let difference = new Date(Date.now() - Date.parse(date));
+
+ // Get UTC offset to account for local TZ (See https://stackoverflow.com/a/9756226)
+ const utcOffsetSeconds =
+ (difference.getTime() + difference.getTimezoneOffset() * 60 * 1000) / 1000;
+
+ difference -= utcOffsetSeconds;
+
return Math.abs(new Date(difference).getUTCFullYear() - 1970);
}
diff --git a/src/applications/mhv-medications/containers/App.jsx b/src/applications/mhv-medications/containers/App.jsx
index 191a45add393..daaa95bdfcfe 100644
--- a/src/applications/mhv-medications/containers/App.jsx
+++ b/src/applications/mhv-medications/containers/App.jsx
@@ -40,10 +40,11 @@ const App = ({ children }) => {
sessionSampleRate: 100,
sessionReplaySampleRate: 50,
trackInteractions: true,
+ trackFrustrations: true,
trackUserInteractions: true,
trackResources: true,
trackLongTasks: true,
- defaultPrivacyLevel: 'mask',
+ defaultPrivacyLevel: 'mask-user-input',
};
useDatadogRum(datadogRumConfig);
diff --git a/src/applications/personalization/dashboard/mocks/medical-copays/index.js b/src/applications/personalization/dashboard/mocks/medical-copays/index.js
index 20031e938a49..674af3b5ad7d 100644
--- a/src/applications/personalization/dashboard/mocks/medical-copays/index.js
+++ b/src/applications/personalization/dashboard/mocks/medical-copays/index.js
@@ -460,6 +460,7 @@ const user81Copays = {
data: [
{
id: 'f4385298-08a6-42f8-a86f-50e97033fb85',
+ accountNumber: '57 0000 0001 97750 IPOAD',
pSSeqNum: 506,
pSTotSeqNum: 588,
pSFacilityNum: '534',
@@ -734,6 +735,7 @@ const user81Copays = {
},
{
id: 'b381cc7b-ea3a-49dc-a982-7146416ed373',
+ accountNumber: '60 0000 0001 97750 IPOAD',
pSSeqNum: 1162,
pSTotSeqNum: 1,
pSFacilityNum: '757',
diff --git a/src/applications/personalization/dashboard/tests/fixtures/test-copays-response.js b/src/applications/personalization/dashboard/tests/fixtures/test-copays-response.js
index 7308a2447304..b70e9c9a8d38 100644
--- a/src/applications/personalization/dashboard/tests/fixtures/test-copays-response.js
+++ b/src/applications/personalization/dashboard/tests/fixtures/test-copays-response.js
@@ -5,6 +5,7 @@ export const copaysSuccess = (hasRecentCopay = false) => {
data: [
{
id: 'f4385298-08a6-42f8-a86f-50e97033fb85',
+ accountNumber: '57 0000 0001 97750 IPOAD',
pSSeqNum: 506,
pSTotSeqNum: 588,
pSFacilityNum: '534',
@@ -182,6 +183,7 @@ export const copaysSuccess = (hasRecentCopay = false) => {
},
{
id: 'b381cc7b-ea3a-49dc-a982-7146416ed373',
+ accountNumber: '60 0000 0001 97750 IPOAD',
pSSeqNum: 1162,
pSTotSeqNum: 1,
pSFacilityNum: '757',
diff --git a/src/applications/user-testing/new-conditions/components/Autocomplete.jsx b/src/applications/user-testing/new-conditions/components/Autocomplete.jsx
index 96211ae9c817..2b64c902807c 100644
--- a/src/applications/user-testing/new-conditions/components/Autocomplete.jsx
+++ b/src/applications/user-testing/new-conditions/components/Autocomplete.jsx
@@ -180,6 +180,7 @@ const Autocomplete = ({
data-testid="autocomplete-list"
role="listbox"
tabIndex={-1}
+ aria-label="List of matching conditions"
>
{results.map((result, index) => (
{/* {!hideScheduleLink() && } */}
- {featureCCDirectScheduling && (
-
-
-
- )}
{featureCCDirectScheduling && }
{featureCCDirectScheduling && (
- {facility.name}
+
{facility.name}
@@ -143,7 +143,7 @@ export default function ClaimExamLayout({ data: appointment }) {
)}
{!!facility && (
<>
- {facility.name}
+
{facility.name}
{facilityPhone && (
diff --git a/src/applications/vaos/components/layout/ClaimExamLayout.unit.spec.js b/src/applications/vaos/components/layout/ClaimExamLayout.unit.spec.js
index 77abca6b97ab..b8c679f83ac0 100644
--- a/src/applications/vaos/components/layout/ClaimExamLayout.unit.spec.js
+++ b/src/applications/vaos/components/layout/ClaimExamLayout.unit.spec.js
@@ -25,6 +25,8 @@ describe('VAOS Component: ClaimExamLayout', () => {
value: '307-778-7550',
},
],
+ website:
+ 'https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/',
},
},
},
@@ -318,6 +320,11 @@ describe('VAOS Component: ClaimExamLayout', () => {
screen.getByRole('heading', { level: 2, name: /Where to attend/i }),
);
expect(screen.getByText(/Cheyenne VA Medical Center/i));
+ expect(
+ screen.container.querySelector(
+ 'a[href="https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/"]',
+ ),
+ ).to.be.ok;
expect(screen.getByText(/2360 East Pershing Boulevard/i));
expect(screen.container.querySelector('va-icon[icon="directions"]')).to.be
diff --git a/src/applications/vaos/components/layout/InPersonLayout.jsx b/src/applications/vaos/components/layout/InPersonLayout.jsx
index 96d95a575d55..fff4921d4fc1 100644
--- a/src/applications/vaos/components/layout/InPersonLayout.jsx
+++ b/src/applications/vaos/components/layout/InPersonLayout.jsx
@@ -108,7 +108,7 @@ export default function InPersonLayout({ data: appointment }) {
)}
{!!facility && (
<>
- {facility.name}
+
{facility.name}
diff --git a/src/applications/vaos/components/layout/InPersonLayout.unit.spec.js b/src/applications/vaos/components/layout/InPersonLayout.unit.spec.js
index 5e92ef024381..81567152008c 100644
--- a/src/applications/vaos/components/layout/InPersonLayout.unit.spec.js
+++ b/src/applications/vaos/components/layout/InPersonLayout.unit.spec.js
@@ -25,6 +25,8 @@ describe('VAOS Component: InPersonLayout', () => {
value: '307-778-7550',
},
],
+ website:
+ 'https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/',
},
},
},
@@ -320,6 +322,11 @@ describe('VAOS Component: InPersonLayout', () => {
screen.getByRole('heading', { level: 2, name: /Where to attend/i }),
);
expect(screen.getByText(/Cheyenne VA Medical Center/i));
+ expect(
+ screen.container.querySelector(
+ 'a[href="https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/"]',
+ ),
+ ).to.be.ok;
expect(screen.getByText(/2360 East Pershing Boulevard/i));
expect(screen.container.querySelector('va-icon[icon="directions"]')).to.be
diff --git a/src/applications/vaos/components/layout/PhoneLayout.jsx b/src/applications/vaos/components/layout/PhoneLayout.jsx
index 8666d20c0b98..d4bc20cd2b0c 100644
--- a/src/applications/vaos/components/layout/PhoneLayout.jsx
+++ b/src/applications/vaos/components/layout/PhoneLayout.jsx
@@ -86,7 +86,7 @@ export default function PhoneLayout({ data: appointment }) {
)}
{!!facility && (
<>
- {facility.name}
+
{facility.name}
>
diff --git a/src/applications/vaos/components/layout/PhoneLayout.unit.spec.js b/src/applications/vaos/components/layout/PhoneLayout.unit.spec.js
index ddb6002a9efe..ff1a42bec2fd 100644
--- a/src/applications/vaos/components/layout/PhoneLayout.unit.spec.js
+++ b/src/applications/vaos/components/layout/PhoneLayout.unit.spec.js
@@ -25,6 +25,8 @@ describe('VAOS Component: PhoneLayout', () => {
value: '307-778-7550',
},
],
+ website:
+ 'https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/',
},
},
},
@@ -243,6 +245,11 @@ describe('VAOS Component: PhoneLayout', () => {
}),
);
expect(screen.getByText(/Cheyenne VA Medical Center/i));
+ expect(
+ screen.container.querySelector(
+ 'a[href="https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/"]',
+ ),
+ ).to.be.ok;
expect(screen.getByText(/2360 East Pershing Boulevard/i));
expect(screen.container.querySelector('va-icon[icon="directions"]')).not
.to.exist;
diff --git a/src/applications/vaos/components/layout/VARequestLayout.jsx b/src/applications/vaos/components/layout/VARequestLayout.jsx
index ade88d77d815..22db90008698 100644
--- a/src/applications/vaos/components/layout/VARequestLayout.jsx
+++ b/src/applications/vaos/components/layout/VARequestLayout.jsx
@@ -89,7 +89,7 @@ export default function VARequestLayout({ data: appointment }) {
)}
{!!facility?.name && (
<>
- {facility.name}
+
{facility.name}
>
)}
diff --git a/src/applications/vaos/components/layout/VARequestLayout.unit.spec.js b/src/applications/vaos/components/layout/VARequestLayout.unit.spec.js
index 37328c3f5c05..1ebe60c7a0cb 100644
--- a/src/applications/vaos/components/layout/VARequestLayout.unit.spec.js
+++ b/src/applications/vaos/components/layout/VARequestLayout.unit.spec.js
@@ -24,6 +24,8 @@ describe('VAOS Component: VARequestLayout', () => {
value: '307-778-7550',
},
],
+ website:
+ 'https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/',
},
},
},
@@ -112,6 +114,11 @@ describe('VAOS Component: VARequestLayout', () => {
expect(screen.getByRole('heading', { level: 2, name: /Facility/i }));
expect(screen.getByText(/Cheyenne VA Medical Center/i));
+ expect(
+ screen.container.querySelector(
+ 'a[href="https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/"]',
+ ),
+ ).to.be.ok;
expect(screen.getByText(/2360 East Pershing Boulevard/i));
expect(screen.container.querySelector('va-icon[icon="directions"]')).to.be
diff --git a/src/applications/vaos/components/layout/VideoLayout.jsx b/src/applications/vaos/components/layout/VideoLayout.jsx
index f51aa8a04396..ea89e748a21f 100644
--- a/src/applications/vaos/components/layout/VideoLayout.jsx
+++ b/src/applications/vaos/components/layout/VideoLayout.jsx
@@ -91,7 +91,7 @@ export default function VideoLayout({ data: appointment }) {
{!!facility && (
<>
- {facility.name}
+ {facility.name}
{address.city},
@@ -140,7 +140,7 @@ export default function VideoLayout({ data: appointment }) {
{facility ? (
<>
- {facility.name}
+ {facility.name}
{address.city},
diff --git a/src/applications/vaos/components/layout/VideoLayout.unit.spec.js b/src/applications/vaos/components/layout/VideoLayout.unit.spec.js
index 441b52bfb32e..0be0fab6edc3 100644
--- a/src/applications/vaos/components/layout/VideoLayout.unit.spec.js
+++ b/src/applications/vaos/components/layout/VideoLayout.unit.spec.js
@@ -25,6 +25,8 @@ describe('VAOS Component: VideoLayout', () => {
value: '307-778-7550',
},
],
+ website:
+ 'https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/',
},
},
},
@@ -276,6 +278,11 @@ describe('VAOS Component: VideoLayout', () => {
}),
);
expect(screen.getByText(/Cheyenne VA Medical Center/i));
+ expect(
+ screen.container.querySelector(
+ 'a[href="https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/"]',
+ ),
+ ).to.be.ok;
expect(screen.queryByText(/2360 East Pershing Boulevard/i)).not.to.exist;
expect(screen.getByText(/Clinic: Clinic 1/i));
diff --git a/src/applications/vaos/components/layout/VideoLayoutAtlas.jsx b/src/applications/vaos/components/layout/VideoLayoutAtlas.jsx
index d9ae3f0dff89..41a49094ca8f 100644
--- a/src/applications/vaos/components/layout/VideoLayoutAtlas.jsx
+++ b/src/applications/vaos/components/layout/VideoLayoutAtlas.jsx
@@ -113,7 +113,7 @@ export default function VideoLayoutAtlas({ data: appointment }) {
)}
{!!facility && (
<>
- {facility.name}
+ {facility.name}
{facility ? (
<>
- {facility.name}
+ {facility.name}
{address.city},
diff --git a/src/applications/vaos/components/layout/VideoLayoutAtlas.unit.spec.js b/src/applications/vaos/components/layout/VideoLayoutAtlas.unit.spec.js
index 77afa9dcd8da..78e8a459c951 100644
--- a/src/applications/vaos/components/layout/VideoLayoutAtlas.unit.spec.js
+++ b/src/applications/vaos/components/layout/VideoLayoutAtlas.unit.spec.js
@@ -25,6 +25,8 @@ describe('VAOS Component: VideoLayoutAtlas', () => {
value: '307-778-7550',
},
],
+ website:
+ 'https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/',
},
},
},
@@ -354,6 +356,11 @@ describe('VAOS Component: VideoLayoutAtlas', () => {
}),
);
expect(screen.getByText(/Cheyenne VA Medical Center/i));
+ expect(
+ screen.container.querySelector(
+ 'a[href="https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/"]',
+ ),
+ ).to.be.ok;
expect(screen.queryByText(/2360 East Pershing Boulevard/i)).not.to.exist;
expect(screen.container.querySelector('va-icon[icon="directions"]')).to.be
.ok;
diff --git a/src/applications/vaos/components/layout/VideoLayoutVA.jsx b/src/applications/vaos/components/layout/VideoLayoutVA.jsx
index 5db538ac9775..c4739672a93e 100644
--- a/src/applications/vaos/components/layout/VideoLayoutVA.jsx
+++ b/src/applications/vaos/components/layout/VideoLayoutVA.jsx
@@ -106,7 +106,7 @@ export default function VideoLayoutVA({ data: appointment }) {
)}
{!!facility && (
<>
- {facility.name}
+ {facility.name}
diff --git a/src/applications/vaos/components/layout/VideoLayoutVA.unit.spec.js b/src/applications/vaos/components/layout/VideoLayoutVA.unit.spec.js
index 00c9afc6e79d..f18a68a2f963 100644
--- a/src/applications/vaos/components/layout/VideoLayoutVA.unit.spec.js
+++ b/src/applications/vaos/components/layout/VideoLayoutVA.unit.spec.js
@@ -25,6 +25,8 @@ describe('VAOS Component: VideoLayoutVA', () => {
value: '307-778-7550',
},
],
+ website:
+ 'https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/',
},
},
},
@@ -379,6 +381,11 @@ describe('VAOS Component: VideoLayoutVA', () => {
}),
);
expect(screen.getByText(/Cheyenne VA Medical Center/i));
+ expect(
+ screen.container.querySelector(
+ 'a[href="https://www.va.gov/cheyenne-health-care/locations/cheyenne-va-medical-center/"]',
+ ),
+ ).to.be.ok;
expect(screen.getByText(/2360 East Pershing Boulevard/i));
expect(screen.container.querySelector('va-icon[icon="directions"]')).to
.be.ok;
diff --git a/src/applications/vaos/referral-appointments/components/DateAndTimeContent.jsx b/src/applications/vaos/referral-appointments/components/DateAndTimeContent.jsx
index 59b2b298da08..bb274634c02f 100644
--- a/src/applications/vaos/referral-appointments/components/DateAndTimeContent.jsx
+++ b/src/applications/vaos/referral-appointments/components/DateAndTimeContent.jsx
@@ -93,6 +93,8 @@ export const DateAndTimeContent = props => {
routeToNextReferralPage(history, currentPage, currentReferral.UUID);
};
+ const noSlotsAvailable = !provider.slots.length;
+
return (
<>
@@ -120,51 +122,70 @@ export const DateAndTimeContent = props => {
{provider.driveTime} ({provider.driveDistance})
Choose a date and time
-
- Select an available date and time from the calendar below. Appointment
- times are displayed in{' '}
- {`${getTimezoneDescByFacilityId(
- currentReferral.ReferringFacilityInfo.FacilityCode,
- )}`}
- .
-
+ {!noSlotsAvailable && (
+
+ Select an available date and time from the calendar below.
+ Appointment times are displayed in{' '}
+ {`${getTimezoneDescByFacilityId(
+ currentReferral.ReferringFacilityInfo.FacilityCode,
+ )}`}
+ .
+
+ )}
-
-
+
+ We’re sorry. We couldn’t find any open time slots.
+
+ Please call this provider to schedule an appointment
+
+
+ )}
+ {!noSlotsAvailable && (
+ <>
+
+
+ }
+ onChange={onChange}
+ onNextMonth={null}
+ onPreviousMonth={null}
+ minDate={format(new Date(), 'yyyy-MM-dd')}
+ maxDate={format(latestAvailableSlot, 'yyyy-MM-dd')}
+ required
+ requiredMessage={error}
+ startMonth={format(new Date(), 'yyyy-MM')}
+ showValidation={error.length > 0}
+ showWeekends
+ overrideMaxDays
/>
- }
- onChange={onChange}
- onNextMonth={null}
- onPreviousMonth={null}
- minDate={format(new Date(), 'yyyy-MM-dd')}
- maxDate={format(latestAvailableSlot, 'yyyy-MM-dd')}
- required
- requiredMessage={error}
- startMonth={format(new Date(), 'yyyy-MM')}
- showValidation={error.length > 0}
- showWeekends
- overrideMaxDays
- />
-
- onBack()}
- onSubmit={() => onSubmit()}
- loadingText="Page change in progress"
- />
+
+
onBack()}
+ onSubmit={() => onSubmit()}
+ loadingText="Page change in progress"
+ />
+ >
+ )}
>
);
};
diff --git a/src/applications/vaos/referral-appointments/components/DateAndTimeContent.unit.spec.js b/src/applications/vaos/referral-appointments/components/DateAndTimeContent.unit.spec.js
index 1de20e16119e..7aaa1ae1658a 100644
--- a/src/applications/vaos/referral-appointments/components/DateAndTimeContent.unit.spec.js
+++ b/src/applications/vaos/referral-appointments/components/DateAndTimeContent.unit.spec.js
@@ -145,4 +145,17 @@ describe('VAOS Component: DateAndTimeContent', () => {
// Routes to next page if selection exists
expect(screen.history.push.called).to.be.true;
});
+ it('should show error if no slots available', async () => {
+ const screen = renderWithStoreAndRouter(
+ ,
+ {
+ initialState,
+ },
+ );
+ expect(screen.getByTestId('no-slots-alert')).to.exist;
+ });
});
diff --git a/src/applications/vaos/referral-appointments/components/ProviderAlert.jsx b/src/applications/vaos/referral-appointments/components/ProviderAlert.jsx
deleted file mode 100644
index b6f1527ab61b..000000000000
--- a/src/applications/vaos/referral-appointments/components/ProviderAlert.jsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import InfoAlert from '../../components/InfoAlert';
-
-export const ProviderAlert = ({ status }) => {
- return (
-
-
- You are not authorized to schedule with your indicated provider at this
- time. You can use this tool to schedule with another provider.
-
-
- );
-};
-
-ProviderAlert.propTypes = {
- status: PropTypes.oneOf(['info', 'error', 'success', 'warning', 'continue'])
- .isRequired,
-};
-
-export default ProviderAlert;
diff --git a/src/applications/vaos/referral-appointments/redux/actions.js b/src/applications/vaos/referral-appointments/redux/actions.js
index 430b95a3a82e..1df5ab7d1e02 100644
--- a/src/applications/vaos/referral-appointments/redux/actions.js
+++ b/src/applications/vaos/referral-appointments/redux/actions.js
@@ -5,10 +5,6 @@ import {
getPatientReferralById,
} from '../../services/referral';
-export const SET_FACILITY = 'SET_FACILITY';
-export const SET_APPOINTMENT_DETAILS = 'SET_APPOINTMENT_DETAILS';
-export const SET_SORT_PROVIDER_BY = 'SET_SORT_PROVIDER_BY';
-export const SET_SELECTED_PROVIDER = 'SET_SELECTED_PROVIDER';
export const SET_FORM_CURRENT_PAGE = 'SET_FORM_CURRENT_PAGE';
export const FETCH_PROVIDER_DETAILS = 'FETCH_PROVIDER_DETAILS';
export const FETCH_PROVIDER_DETAILS_SUCCEEDED =
@@ -23,37 +19,6 @@ export const FETCH_REFERRAL_FAILED = 'FETCH_REFERRAL_FAILED';
export const SET_SELECTED_SLOT = 'SET_SELECTED_SLOT';
export const SET_INIT_REFERRAL_FLOW = 'SET_INIT_REFERRAL_FLOW';
-export function setFacility(facility) {
- return {
- type: SET_FACILITY,
- payload: facility,
- };
-}
-
-export function setAppointmentDetails(dateTime, facility) {
- return {
- type: SET_APPOINTMENT_DETAILS,
- payload: {
- dateTime,
- facility,
- },
- };
-}
-
-export function setSortProviderBy(sortProviderBy) {
- return {
- type: SET_SORT_PROVIDER_BY,
- payload: sortProviderBy,
- };
-}
-
-export function setSelectedProvider(selectedProvider) {
- return {
- type: SET_SELECTED_PROVIDER,
- payload: selectedProvider,
- };
-}
-
export function setFormCurrentPage(currentPage) {
return {
type: SET_FORM_CURRENT_PAGE,
diff --git a/src/applications/vaos/referral-appointments/redux/reducers.js b/src/applications/vaos/referral-appointments/redux/reducers.js
index 7bb596317436..4a40359af160 100644
--- a/src/applications/vaos/referral-appointments/redux/reducers.js
+++ b/src/applications/vaos/referral-appointments/redux/reducers.js
@@ -1,8 +1,4 @@
import {
- SET_FACILITY,
- SET_APPOINTMENT_DETAILS,
- SET_SORT_PROVIDER_BY,
- SET_SELECTED_PROVIDER,
SET_FORM_CURRENT_PAGE,
FETCH_PROVIDER_DETAILS,
FETCH_PROVIDER_DETAILS_FAILED,
@@ -32,27 +28,6 @@ const initialState = {
function ccAppointmentReducer(state = initialState, action) {
switch (action.type) {
- case SET_FACILITY:
- return {
- ...state,
- facility: action.payload,
- };
- case SET_APPOINTMENT_DETAILS:
- return {
- ...state,
- dateTime: action.payload.dateTime,
- facility: action.payload.facility,
- };
- case SET_SORT_PROVIDER_BY:
- return {
- ...state,
- sortProviderBy: action.payload,
- };
- case SET_SELECTED_PROVIDER:
- return {
- ...state,
- selectedProvider: action.payload,
- };
case SET_FORM_CURRENT_PAGE:
return {
...state,
diff --git a/src/applications/vaos/referral-appointments/redux/selectors.js b/src/applications/vaos/referral-appointments/redux/selectors.js
index 5fbdf1619ab1..a32e3896bf26 100644
--- a/src/applications/vaos/referral-appointments/redux/selectors.js
+++ b/src/applications/vaos/referral-appointments/redux/selectors.js
@@ -1,6 +1,3 @@
-export const selectCCAppointment = state => state.ccAppointment;
-export const selectProvider = state => state.referral.selectedProvider;
-export const selectProviderSortBy = state => state.referral.sortProviderBy;
export const selectCurrentPage = state => state.referral.currentPage;
export const getSelectedSlot = state => state.referral.selectedSlot;
diff --git a/src/applications/vaos/referral-appointments/utils/provider.js b/src/applications/vaos/referral-appointments/utils/provider.js
index e5327582b3e8..1bf76b390816 100644
--- a/src/applications/vaos/referral-appointments/utils/provider.js
+++ b/src/applications/vaos/referral-appointments/utils/provider.js
@@ -3,6 +3,23 @@ const dateFns = require('date-fns');
const dateFnsTz = require('date-fns-tz');
const providers = {
+ '0': {
+ providerName: 'Dr. Perpetually Unavailable',
+ typeOfCare: 'Physical Therapy',
+ orgName: 'Ethereal Adjunct of Deferred Care',
+ orgAddress: {
+ street1: '421 Promethean Circuit',
+ street2: 'Suite 300',
+ street3: '',
+ city: 'Portland',
+ state: 'Oregon',
+ zip: '97214',
+ },
+ orgPhone: '555-687-6736',
+ driveTime: '1 hour drive',
+ driveDistance: '100 miles',
+ location: 'Hypothetical Adjunct Node, Sublime Care Complex',
+ },
'111': {
providerName: 'Dr. Bones',
typeOfCare: 'Physical Therapy',
diff --git a/src/applications/vaos/referral-appointments/utils/referrals.js b/src/applications/vaos/referral-appointments/utils/referrals.js
index 074f11642c02..b503725c1137 100644
--- a/src/applications/vaos/referral-appointments/utils/referrals.js
+++ b/src/applications/vaos/referral-appointments/utils/referrals.js
@@ -67,10 +67,7 @@ const createReferrals = (numberOfReferrals = 3, baseDate) => {
const baseDateObject = new Date(year, month - 1, day);
const referrals = [];
const baseUUID = 'add2f0f4-a1ea-4dea-a504-a54ab57c68';
- const providerIds = ['111', '222'];
- const isOdd = number => {
- return number % 2;
- };
+ const providerIds = ['111', '222', '0'];
for (let i = 0; i < numberOfReferrals; i++) {
const startDate = addDays(baseDateObject, i);
@@ -80,7 +77,7 @@ const createReferrals = (numberOfReferrals = 3, baseDate) => {
createReferral(
referralDate,
`${baseUUID}${i.toString().padStart(2, '0')}`,
- isOdd(i) ? providerIds[0] : providerIds[1],
+ providerIds[i % providerIds.length],
),
);
}
diff --git a/src/applications/vaos/services/location/transformers.js b/src/applications/vaos/services/location/transformers.js
index ab230326f403..bf8aeaf9fb64 100644
--- a/src/applications/vaos/services/location/transformers.js
+++ b/src/applications/vaos/services/location/transformers.js
@@ -48,6 +48,7 @@ export function transformFacilityV2(facility) {
state: facility.physicalAddress.state,
postalCode: facility.physicalAddress.postalCode,
},
+ website: facility.website,
};
}
diff --git a/src/applications/vaos/services/mocks/epsApi/appointments.json b/src/applications/vaos/services/mocks/epsApi/appointments.json
deleted file mode 100644
index 947230691c39..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/appointments.json
+++ /dev/null
@@ -1,61 +0,0 @@
-{
- "appointments": [
- {
- "appointmentDetails": {
- "cancelReason": {
- "id": "3b13a8cb-d4f3-4678-8bed-7fa3931c0c40",
- "name": "Patient"
- },
- "isLastest": true,
- "lastRetrieved": "2009-01-05T02:59:23Z",
- "phone": "555-555-1000",
- "start": "2026-01-01T17:00:00Z",
- "status": "booked"
- },
- "createdBy": {
- "byPatient": true,
- "system": {
- "id": "45604d54-7c49-4d9b-b669-53cbaf2a5189",
- "name": "The ACME Healthcare EPS Client",
- "type": "external"
- },
- "user": {
- "id": "a1b6673e-d409-4876-a0ff-a77bb4840aca"
- }
- },
- "error": "conflict",
- "id": "b79e46ba-1b09-4195-ae0a-ea42baedea6e",
- "networkId": "00eff3f3-ecfb-41ff-9ebc-78ed811e17f9",
- "patientId": "b6cc1875-5313-4ca8-af8b-74adac0c5d0c",
- "providerServiceId": "69cd9203-5e92-47a3-aa03-94b03752872a",
- "referral": {
- "id": "69cd9203-5e92-47a3-aa03-94b03752872a",
- "referralNumber": "12345"
- },
- "slotIds": [
- "dba0855e-5681-411d-801c-036f4f046988"
- ],
- "state": "submitted"
- },
- {
- "createdBy": {
- "byPatient": false,
- "system": {
- "type": "epsApi"
- },
- "user": {
- "id": "a1b6673e-d409-4876-a0ff-a77bb4840aca"
- }
- },
- "id": "bfd486ae-b04e-42a7-b049-8c689b6caef2",
- "patientId": "b6cc1875-5313-4ca8-af8b-74adac0c5d0c",
- "referral": {
- "id": "69cd9203-5e92-47a3-aa03-94b03752872a"
- },
- "state": "draft"
- }
- ],
- "count": 2,
- "nextToken": "YTI0NTM5NzUtNzA2My00NTU5LWJiYWYtZTg3MGY4ZWZlYzg5",
- "total": 5
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/epsApi/cancelReasons.json b/src/applications/vaos/services/mocks/epsApi/cancelReasons.json
deleted file mode 100644
index a57ddcb27f6a..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/cancelReasons.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "cancelReasons": [
- {
- "id": "930c8a9f-ce78-449c-8dda-d5428d183ec8",
- "name": "Patient"
- },
- {
- "id": "57b56149-10e8-41e9-8117-2abf67de46d0",
- "name": "Provider"
- },
- {
- "id": "c7a4fbdb-7f2f-45ad-b0b1-4d27f19fde19",
- "name": "Other"
- }
- ],
- "count": 3
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/epsApi/driveTime.json b/src/applications/vaos/services/mocks/epsApi/driveTime.json
deleted file mode 100644
index 5732b8c6507f..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/driveTime.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "destinations": {
- "00eff3f3-ecfb-41ff-9ebc-78ed811e17f9": {
- "latitude": -74.12870564772521,
- "longitude": -151.6240405624497
- },
- "69cd9203-5e92-47a3-aa03-94b03752872a": {
- "latitude": -1.7437745123171688,
- "longitude": -54.19187859370315
- }
- },
- "origin": {
- "latitude": 4.627174468915552,
- "longitude": -88.72187894562788
- }
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/epsApi/networks.json b/src/applications/vaos/services/mocks/epsApi/networks.json
deleted file mode 100644
index 92631606ebec..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/networks.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "count": 2,
- "networks": [
- {
- "id": "69cd9203-5e92-47a3-aa03-94b03752872a",
- "name": "The Acme Health Referral Network",
- "requiresReferralNumber": true
- },
- {
- "id": "00eff3f3-ecfb-41ff-9ebc-78ed811e17f9",
- "name": "Acme Health Internal Providers",
- "requiresReferralNumber": false
- }
- ],
- "nextToken": "YTI0NTM5NzUtNzA2My00NTU5LWJiYWYtZTg3MGY4ZWZlYzg5",
- "total": 5
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/epsApi/patients.json b/src/applications/vaos/services/mocks/epsApi/patients.json
deleted file mode 100644
index 49bd56793b29..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/patients.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "address": {
- "city": "Anytown",
- "country": "USA",
- "line": [
- "123 Main Street",
- "Suite 101"
- ],
- "postalCode": "12345",
- "state": "FL",
- "type": "both",
- "use": "home"
- },
- "birthDate": "1977-01-01",
- "contactInfo": [
- {
- "system": "phone",
- "use": "mobile",
- "value": "555-555-1111"
- },
- {
- "system": "email",
- "value": "jdoe@example.com"
- }
- ],
- "gender": "female",
- "id": "b6cc1875-5313-4ca8-af8b-74adac0c5d0c",
- "identifier": [
- {
- "system": "ssn",
- "value": "111-00-1111"
- }
- ],
- "name": [
- {
- "family": "Doe",
- "given": [
- "Jane",
- "Jill"
- ]
- }
- ]
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/epsApi/providerOrganizations.json b/src/applications/vaos/services/mocks/epsApi/providerOrganizations.json
deleted file mode 100644
index 8673e4be9b66..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/providerOrganizations.json
+++ /dev/null
@@ -1,31 +0,0 @@
-{
- "count": 4,
- "nextToken": "YTI0NTM5NzUtNzA2My00NTU5LWJiYWYtZTg3MGY4ZWZlYzg5",
- "providerOrganizations": [
- {
- "name": "Alpha Cardiology",
- "networkIds": [
- "69cd9203-5e92-47a3-aa03-94b03752872a"
- ]
- },
- {
- "name": "Beta Dental",
- "networkIds": [
- "69cd9203-5e92-47a3-aa03-94b03752872a"
- ]
- },
- {
- "name": "Acme Orthopaedic",
- "networkIds": [
- "00eff3f3-ecfb-41ff-9ebc-78ed811e17f9"
- ]
- },
- {
- "name": "Acme Urology",
- "networkIds": [
- "00eff3f3-ecfb-41ff-9ebc-78ed811e17f9"
- ]
- }
- ],
- "total": 11
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/epsApi/providerServices.json b/src/applications/vaos/services/mocks/epsApi/providerServices.json
deleted file mode 100644
index 1667e1a2f540..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/providerServices.json
+++ /dev/null
@@ -1,139 +0,0 @@
-{
- "count": 2,
- "nextToken": "YTI0NTM5NzUtNzA2My00NTU5LWJiYWYtZTg3MGY4ZWZlYzg5",
- "providerServices": [
- {
- "appointmentTypes": [
- {
- "id": "40cca3e9-cbf2-48d0-b088-7f4a2713fcae",
- "name": "Initial Consult"
- }
- ],
- "contactDetails": [
- {
- "system": "phone",
- "use": "for_patient",
- "value": "555-555-0001"
- },
- {
- "system": "phone",
- "use": "for_coordinator",
- "value": "555-555-1111"
- },
- {
- "system": "fax",
- "use": "for_coordinator",
- "value": "555-555-0101"
- }
- ],
- "features": {
- "directBooking": {
- "isEnabled": true,
- "requiredFields": [
- "name",
- "birthdate",
- "gender",
- "phone",
- "email"
- ]
- },
- "isDigital": true
- },
- "id": "69cd9203-5e92-47a3-aa03-94b03752872a",
- "individualProviders": [
- {
- "name": "Dr. Smith",
- "npi": "1245319599"
- }
- ],
- "isActive": true,
- "location": {
- "address": "123 Main Street, Suite 1, Anywhere USA 12345",
- "latitue": 90,
- "longitude": 180,
- "name": "Acme Cardiology - Anywhere, USA",
- "timezone": "America/New_York"
- },
- "name": "Dr. Smith @ Acme Cardiology - Anywhere, USA",
- "networkIds": [
- "69cd9203-5e92-47a3-aa03-94b03752872a",
- "00eff3f3-ecfb-41ff-9ebc-78ed811e17f9"
- ],
- "providerOrganization": {
- "name": "Acme Cardiology",
- "npi": "1245319599",
- "npiName": "The Smith Group"
- },
- "schedulingNotes": "Please inform the patient: - New patients should arrive 30 minutes prior to the appointment to complete new patient paperwork",
- "specialties": [
- {
- "id": "207XX0004X",
- "name": "Orthopaedic Surgery - Foot and Ankle Surgery"
- }
- ],
- "visitMode": "in-person"
- },
- {
- "appointmentTypes": [
- {
- "id": "40cca3e9-cbf2-48d0-b088-7f4a2713fcae",
- "name": "MRI Scan"
- }
- ],
- "contactDetails": [
- {
- "system": "phone",
- "use": "for_patient",
- "value": "555-555-0001"
- },
- {
- "system": "phone",
- "use": "for_coordinator",
- "value": "555-555-1111"
- },
- {
- "system": "fax",
- "use": "for_coordinator",
- "value": "555-555-0101"
- }
- ],
- "features": {
- "directBooking": {
- "isEnabled": true,
- "requiredFields": [
- "name",
- "birthdate",
- "gender",
- "phone"
- ]
- },
- "isDigital": true
- },
- "id": "00eff3f3-ecfb-41ff-9ebc-78ed811e17f9",
- "isActive": true,
- "location": {
- "address": "123 Main Street, Suite 100, Anywhere USA 12345",
- "latitue": 90,
- "longitude": 180,
- "name": "Acme Imaging - Anywhere, USA",
- "timezone": "America/New_York"
- },
- "name": "MRI @ Acme Imaging",
- "networkIds": [
- "69cd9203-5e92-47a3-aa03-94b03752872a"
- ],
- "providerOrganization": {
- "name": "Acme Imaging",
- "npi": "1245319588"
- },
- "specialties": [
- {
- "id": "261QM1200X",
- "name": "Clinic/Center - Magnetic Resonance Imaging (MRI)"
- }
- ],
- "visitMode": "in-person"
- }
- ],
- "total": 5
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/epsApi/providerServicesSlots.json b/src/applications/vaos/services/mocks/epsApi/providerServicesSlots.json
deleted file mode 100644
index 080d15441fd0..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/providerServicesSlots.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "count": 2,
- "nextToken": "YTI0NTM5NzUtNzA2My00NTU5LWJiYWYtZTg3MGY4ZWZlYzg5",
- "slots": [
- {
- "appointmentTypeId": "40cca3e9-cbf2-48d0-b088-7f4a2713fcae",
- "id": "dba0855e-5681-411d-801c-036f4f046988",
- "providerServiceId": "69cd9203-5e92-47a3-aa03-94b03752872a",
- "remaining": 1,
- "start": "2026-01-01T17:00:00Z"
- },
- {
- "appointmentTypeId": "40cca3e9-cbf2-48d0-b088-7f4a2713fcae",
- "id": "e3b29fa4-43b3-4b27-b5e2-62236d677abf",
- "providerServiceId": "69cd9203-5e92-47a3-aa03-94b03752872a",
- "remaining": 1,
- "start": "2026-01-01T18:00:00Z"
- }
- ],
- "total": 5
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/epsApi/specialties.json b/src/applications/vaos/services/mocks/epsApi/specialties.json
deleted file mode 100644
index c8d54486bb27..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/specialties.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "count": 2,
- "nextToken": "YTI0NTM5NzUtNzA2My00NTU5LWJiYWYtZTg3MGY4ZWZlYzg5",
- "specialties": [
- {
- "id": "207XX0004X",
- "name": "Orthopaedic Surgery - Foot and Ankle Surgery"
- },
- {
- "id": "208200000X",
- "name": "Orthopaedic Surgery - Hand Surgery"
- }
- ],
- "total": 5
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/epsApi/specialtyGroups.json b/src/applications/vaos/services/mocks/epsApi/specialtyGroups.json
deleted file mode 100644
index 65a59a661f7f..000000000000
--- a/src/applications/vaos/services/mocks/epsApi/specialtyGroups.json
+++ /dev/null
@@ -1,24 +0,0 @@
-{
- "count": 2,
- "nextToken": "YTI0NTM5NzUtNzA2My00NTU5LWJiYWYtZTg3MGY4ZWZlYzg5",
- "specialtyGroups": [
- {
- "id": "69cd9203-5e92-47a3-aa03-94b03752872a",
- "name": "Orthopaedic Surgery",
- "specialtyIds": [
- "207XX0004X",
- "208200000X"
- ]
- },
- {
- "id": "00eff3f3-ecfb-41ff-9ebc-78ed811e17f9",
- "name": "Dermatology",
- "specialtyIds": [
- "207ND0900X",
- "207NS0135X",
- "207NI0002X"
- ]
- }
- ],
- "total": 5
- }
\ No newline at end of file
diff --git a/src/applications/vaos/services/mocks/index.js b/src/applications/vaos/services/mocks/index.js
index b32d343d58a6..0659251ed1b8 100644
--- a/src/applications/vaos/services/mocks/index.js
+++ b/src/applications/vaos/services/mocks/index.js
@@ -48,16 +48,6 @@ const requestsV2 = require('./v2/requests.json');
// const meta = require('./v2/meta_failures.json');
// CC Direct Scheduling mocks
-const epsAppointments = require('./epsApi/appointments.json');
-const epsCancelReasons = require('./epsApi/cancelReasons.json');
-const driveTimes = require('./epsApi/driveTime.json');
-const patients = require('./epsApi/patients.json');
-const epsNetworks = require('./epsApi/networks.json');
-const specialties = require('./epsApi/specialties.json');
-const specialtyGroups = require('./epsApi/specialtyGroups.json');
-const providerOrgs = require('./epsApi/providerOrganizations.json');
-const providerServices = require('./epsApi/providerServices.json');
-const providerSlots = require('./epsApi/providerServicesSlots.json');
const referralUtils = require('../../referral-appointments/utils/referrals');
const providerUtils = require('../../referral-appointments/utils/provider');
@@ -69,8 +59,6 @@ const features = require('../../utils/featureFlags');
varSlots.data[0].attributes.appointmentTimeSlot = generateMockSlots();
const mockAppts = [];
let currentMockId = 1;
-const mockEpsAppts = [];
-let currentMockId_eps = 1;
// key: NPI, value: Provider Name
const providerMock = {
@@ -516,165 +504,17 @@ const responses = {
data: singleReferral ?? {},
});
},
- 'GET /vaos/v2/epsApi/appointments': (req, res) => {
- return res.json({
- data: epsAppointments.appointments.concat(mockEpsAppts),
- });
- },
- 'POST /vaos/v2/epsApi/appointments': (req, res) => {
- const { patientId, referral } = req.body;
- const createdAppt = {
- createdBy: {
- byPatient: true,
- },
- id: `mock${currentMockId_eps}`,
- patientId,
- referral: { id: referral.id },
- state: 'draft',
- };
- currentMockId_eps += 1;
- mockEpsAppts.push(createdAppt);
- return res.json({ data: createdAppt });
- },
- 'GET /vaos/v2/epsApi/appointments/:appointmentId': (req, res) => {
- const epsAppts = epsAppointments.appointments.concat(mockEpsAppts.data);
- return res.json({
- data: epsAppts.find(
- appointment => appointment?.id === req.params.appointmentId,
- ),
- });
- },
- 'GET /vaos/v2/epsApi/appointments/:appointmentId/cancel-reasons': (
- req,
- res,
- ) => {
- return res.json(epsCancelReasons);
- },
- 'POST /vaos/v2/epsApi/appointments/:appointmentId/cancel': (req, res) => {
- const { cancelReasonId } = req.body;
- const findApptToCancel = epsAppointments.appointments.find(
- appointment => appointment?.id === req.params.appointmentId,
- );
- const confirmCanceledAppts = {
- ...findApptToCancel,
- appointmentDetails: {
- cancelReason: {
- id: cancelReasonId,
- name: 'Patient',
- },
- },
- };
- return res.json({
- data: confirmCanceledAppts,
- });
- },
- 'POST /vaos/v2/epsApi/appointments/:appointmentId/submit': (req, res) => {
- const appointments = epsAppointments.appointments.concat(mockEpsAppts.data);
-
- const { additionalPatientAttributes } = req.body;
- const appt = appointments.find(
- item => item.id === req.params.appointmentId,
- );
- const submittedAppt = {
- ...appt,
- appointmentDetails: {
- ...additionalPatientAttributes,
- },
- state: 'submitted',
- };
- currentMockId_eps += 1;
- mockEpsAppts.push(submittedAppt);
- return res.json({ data: submittedAppt });
- },
- 'POST /vaos/v2/epsApi/drive-times': (req, res) => {
- return res.json({ driveTimes });
- },
- 'GET /vaos/v2/epsApi/patients/:patientId': (req, res) => {
- const epsPatients = [patients];
- return res.json({
- data: epsPatients.find(patient => patient?.id === req.params.patientId),
- });
- },
- 'GET /vaos/v2/epsApi/patients/:patientId/identifier/:system': (req, res) => {
- const epsPatients = [patients];
- const patientSystem = epsPatients
- .find(patient => patient?.id === req.params.patientId)
- .identifier.find(identifier => identifier.system === req.params.system);
- return res.json({
- data: patientSystem,
- });
- },
- 'GET /vaos/v2/epsApi/networks': (req, res) => {
- return res.json({ data: epsNetworks });
- },
- 'GET /vaos/v2/epsApi/networks/:networkId': (req, res) => {
- return res.json({
- data: epsNetworks.networks.find(
- network => network?.id === req.params.networkId,
- ),
- });
- },
- 'GET /vaos/v2/epsApi/specialties': (req, res) => {
- return res.json({ data: specialties });
- },
- 'GET /vaos/v2/epsApi/specialties/:specialtyId': (req, res) => {
- return res.json({
- data: specialties.specialties.find(
- specialty => specialty?.id === req.params.specialtyId,
- ),
- });
- },
- 'GET /vaos/v2/epsApi/specialty-groups': (req, res) => {
- return res.json({ data: specialtyGroups });
- },
- 'GET /vaos/v2/epsApi/specialty-groups/:specialtyGroupId': (req, res) => {
- return res.json({
- data: specialtyGroups.specialtyGroups.find(
- specialtyGroup => specialtyGroup?.id === req.params.specialtyGroupId,
- ),
- });
- },
- 'GET /vaos/v2/epsApi/provider-organization': (req, res) => {
- return res.json({ data: providerOrgs });
- },
- 'GET /vaos/v2/epsApi/provider-services': (req, res) => {
- return res.json({ data: providerServices });
- },
- 'GET /vaos/v2/epsApi/provider-services/:providerServiceId': (req, res) => {
- return res.json({
- data: providerServices.providerServices.find(
- providerService => providerService?.id === req.params.providerServiceId,
- ),
- });
- },
- 'GET /vaos/v2/epsApi/provider-services/:providerServiceId/slots': (
- req,
- res,
- ) => {
- return res.json({
- data: providerSlots.slots.find(
- slots => slots?.providerServiceId === req.params.providerServiceId,
- ),
- });
- },
- 'GET /vaos/v2/epsApi/provider-services/:providerServiceId/slots/:slotId': (
- req,
- res,
- ) => {
- const getSlot = [
- providerSlots.slots.find(
- slots => slots?.providerServiceId === req.params.providerServiceId,
- ),
- ];
- return res.json({
- data: getSlot.find(slot => slot?.id === req.params.slotId),
- });
- },
'GET /vaos/v2/epsApi/providerDetails/:providerId': (req, res) => {
// Provider 3 throws error
if (req.params.providerId === '3') {
return res.status(500).json({ error: true });
}
+ // Provider 0 has no available slots
+ if (req.params.providerId === '0') {
+ return res.json({
+ data: providerUtils.createProviderDetails(0, req.params.providerId),
+ });
+ }
return res.json({
data: providerUtils.createProviderDetails(5, req.params.providerId),
});
diff --git a/src/platform/startup/index.js b/src/platform/startup/index.js
index 74913794a01b..2adbc492ddfe 100644
--- a/src/platform/startup/index.js
+++ b/src/platform/startup/index.js
@@ -26,6 +26,10 @@ import setUpCommonFunctionality from './setup';
* @param {string} appInfo.url The base url for the React application
* @param {array} appInfo.analyticsEvents An array which contains analytics events to collect
* when the respective actions are fired.
+ * @param {boolean} preloadScheduledDowntimes Whether to fetch scheduled downtimes - when set
+ * to true, the maintenance_windows API request is made without having to wait for the
+ * DowntimeNotification component to mount. This can improve startup time for applications
+ * that use the DowntimeNotification component.
*/
export default function startApp({
routes,
@@ -35,12 +39,14 @@ export default function startApp({
url,
analyticsEvents,
entryName = 'unknown',
+ preloadScheduledDowntimes = false,
}) {
const store = setUpCommonFunctionality({
entryName,
url,
reducer,
analyticsEvents,
+ preloadScheduledDowntimes,
});
// If the build is not production, run an axe check in the browser
diff --git a/src/platform/startup/router.js b/src/platform/startup/router.js
index 88776a147c4f..203ba503c874 100644
--- a/src/platform/startup/router.js
+++ b/src/platform/startup/router.js
@@ -24,6 +24,10 @@ import setUpCommonFunctionality from './setup';
* @param {string} appInfo.url The base url for the React application
* @param {array} appInfo.analyticsEvents An array which contains analytics events to collect
* when the respective actions are fired.
+ * @param {boolean} preloadScheduledDowntimes Whether to fetch scheduled downtimes - when set
+ * to true, the maintenance_windows API request is made without having to wait for the
+ * DowntimeNotification component to mount. This can improve startup time for applications
+ * that use the DowntimeNotification component.
*/
export default function startApp({
routes,
@@ -33,12 +37,14 @@ export default function startApp({
url,
analyticsEvents,
entryName = 'unknown',
+ preloadScheduledDowntimes = false,
}) {
const store = setUpCommonFunctionality({
entryName,
url,
reducer,
analyticsEvents,
+ preloadScheduledDowntimes,
});
let content = component;
diff --git a/src/platform/startup/setup.js b/src/platform/startup/setup.js
index b65585a7547a..8c773142bbb9 100644
--- a/src/platform/startup/setup.js
+++ b/src/platform/startup/setup.js
@@ -1,6 +1,7 @@
import * as Sentry from '@sentry/browser';
import { connectFeatureToggle } from 'platform/utilities/feature-toggles';
+import { getScheduledDowntime } from 'platform/monitoring/DowntimeNotification/actions';
import createCommonStore from './store';
import startSitewideComponents from '../site-wide';
@@ -14,12 +15,14 @@ import startSitewideComponents from '../site-wide';
* @param {string} appInfo.url The base url for the React application
* @param {array} appInfo.analyticsEvents An array which contains analytics events to collect
* when the respective actions are fired.
+ * @param {boolean} preloadScheduledDowntimes Whether to fetch scheduled downtimes.
*/
export default function setUpCommonFunctionality({
entryName,
reducer,
analyticsEvents,
url,
+ preloadScheduledDowntimes,
}) {
// Set further errors to have the appropriate source tag
Sentry.setTag('source', entryName);
@@ -30,6 +33,11 @@ export default function setUpCommonFunctionality({
const store = createCommonStore(reducer, analyticsEvents);
connectFeatureToggle(store.dispatch);
+ if (preloadScheduledDowntimes) {
+ const actionCreator = getScheduledDowntime();
+ actionCreator(store.dispatch, store.getState());
+ }
+
if (url?.endsWith('/')) {
throw new Error(
'Root urls should not end with a slash. Check your manifest.json file and application entry file.',
diff --git a/src/platform/startup/tests/setup.unit.spec.js b/src/platform/startup/tests/setup.unit.spec.js
new file mode 100644
index 000000000000..a2b477e59533
--- /dev/null
+++ b/src/platform/startup/tests/setup.unit.spec.js
@@ -0,0 +1,92 @@
+import { expect } from 'chai';
+import sinon from 'sinon';
+import * as Sentry from '@sentry/browser';
+import * as featureToggles from 'platform/utilities/feature-toggles';
+import * as downtimeNotificationActions from 'platform/monitoring/DowntimeNotification/actions';
+import * as storeModule from '../store';
+import * as sitewideComponents from '../../site-wide';
+import setUpCommonFunctionality from '../setup';
+
+describe('setUpCommonFunctionality', () => {
+ let sandbox;
+ let storeStub;
+ let storeModuleStub;
+
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+ storeStub = {
+ dispatch: sinon.stub(),
+ getState: sinon.stub(),
+ };
+ sandbox.stub(featureToggles, 'connectFeatureToggle');
+ sandbox
+ .stub(downtimeNotificationActions, 'getScheduledDowntime')
+ .returns(sinon.stub());
+ sandbox.stub(sitewideComponents, 'default');
+ storeModuleStub = sandbox.stub(storeModule, 'default').returns(storeStub);
+ sandbox.stub(Sentry, 'setTag');
+ sandbox.stub(Sentry, 'withScope');
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ });
+
+ it('should set Sentry tag with entryName', () => {
+ setUpCommonFunctionality({ entryName: 'testApp' });
+ expect(Sentry.setTag.calledWith('source', 'testApp')).to.be.true;
+ });
+
+ it('should set window.appName with entryName', () => {
+ setUpCommonFunctionality({ entryName: 'testApp' });
+ expect(window.appName).to.equal('testApp');
+ });
+
+ it('should create a store with reducer and analyticsEvents', () => {
+ const reducer = {};
+ const analyticsEvents = [];
+ setUpCommonFunctionality({
+ entryName: 'testApp',
+ reducer,
+ analyticsEvents,
+ });
+ expect(storeModuleStub.calledWith(reducer, analyticsEvents)).to.be.true;
+ });
+
+ it('should connect feature toggle', () => {
+ setUpCommonFunctionality({ entryName: 'testApp' });
+ expect(featureToggles.connectFeatureToggle.calledWith(storeStub.dispatch))
+ .to.be.true;
+ });
+
+ it('should not fetch scheduled downtimes by default', () => {
+ setUpCommonFunctionality({
+ entryName: 'testApp',
+ });
+ expect(downtimeNotificationActions.getScheduledDowntime.called).to.be.false;
+ });
+
+ it('should fetch scheduled downtimes if preloadScheduledDowntimes is true', () => {
+ setUpCommonFunctionality({
+ entryName: 'testApp',
+ preloadScheduledDowntimes: true,
+ });
+ expect(downtimeNotificationActions.getScheduledDowntime.called).to.be.true;
+ });
+
+ it('should throw an error if url ends with a slash', () => {
+ expect(() =>
+ setUpCommonFunctionality({
+ entryName: 'testApp',
+ url: '/foo/',
+ }),
+ ).to.throw(
+ 'Root urls should not end with a slash. Check your manifest.json file and application entry file.',
+ );
+ });
+
+ it('should initialize sitewide components', () => {
+ setUpCommonFunctionality({ entryName: 'testApp' });
+ expect(Sentry.withScope.called).to.be.true;
+ });
+});
diff --git a/src/platform/startup/tests/startup.unit.spec.js b/src/platform/startup/tests/startup.unit.spec.js
new file mode 100644
index 000000000000..0615e8eebd38
--- /dev/null
+++ b/src/platform/startup/tests/startup.unit.spec.js
@@ -0,0 +1,86 @@
+import React from 'react';
+import { expect } from 'chai';
+import sinon from 'sinon';
+import * as reactRouter from 'react-router';
+import * as history from 'history';
+import * as navActions from 'platform/site-wide/user-nav/actions';
+import * as reactApp from '../react';
+import * as axeCheck from '../axe-check';
+import * as commonFunctionality from '../setup';
+import startApp from '../index';
+
+describe('startApp', () => {
+ let sandbox;
+ let storeStub;
+ let setUpCommonFunctionalityStub;
+ let runAxeCheckStub;
+ let startReactAppStub;
+ let updateRouteStub;
+ let useRouterHistoryStub;
+ let createHistoryStub;
+
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+ storeStub = {
+ dispatch: sinon.stub(),
+ getState: sinon.stub(),
+ };
+ setUpCommonFunctionalityStub = sandbox
+ .stub(commonFunctionality, 'default')
+ .returns(storeStub);
+ runAxeCheckStub = sandbox.stub(axeCheck, 'default');
+ startReactAppStub = sandbox.stub(reactApp, 'default');
+ updateRouteStub = sandbox.stub(navActions, 'updateRoute');
+ createHistoryStub = sandbox.stub(history, 'createHistory').returns({
+ getCurrentLocation: sinon.stub().returns({ pathname: '/' }),
+ listen: sinon.stub(),
+ });
+ useRouterHistoryStub = sandbox
+ .stub(reactRouter, 'useRouterHistory')
+ .returns(createHistoryStub);
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ });
+
+ it('should create a store with setUpCommonFunctionality', () => {
+ startApp({ entryName: 'testApp' });
+ expect(setUpCommonFunctionalityStub.called).to.be.true;
+ });
+
+ it('should run axe check in non-production environment', () => {
+ process.env.NODE_ENV = 'development';
+ startApp({ entryName: 'testApp' });
+ expect(runAxeCheckStub.called).to.be.true;
+ process.env.NODE_ENV = 'test';
+ });
+
+ it('should set up history with URL', () => {
+ startApp({ entryName: 'testApp', url: '/foo' });
+ expect(useRouterHistoryStub.calledWith(createHistoryStub)).to.be.true;
+ expect(createHistoryStub.calledWith({ basename: '/foo' })).to.be.true;
+ });
+
+ it('should dispatch route updates', () => {
+ startApp({ entryName: 'testApp', url: '/foo' });
+ expect(storeStub.dispatch.calledWith(updateRouteStub())).to.be.true;
+ });
+
+ it('should render React component with routes', () => {
+ const routes = Routes
;
+ startApp({ entryName: 'testApp', routes });
+ expect(startReactAppStub.called).to.be.true;
+ });
+
+ it('should render React component with component', () => {
+ const component = Component
;
+ startApp({ entryName: 'testApp', component });
+ expect(startReactAppStub.called).to.be.true;
+ });
+
+ it('should return the store', () => {
+ const result = startApp({ entryName: 'testApp' });
+ expect(result).to.equal(storeStub);
+ });
+});
diff --git a/yarn.lock b/yarn.lock
index 9966000e8fae..61be1b2e645f 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -5972,6 +5972,11 @@ babel-plugin-transform-class-properties@^6.24.1:
babel-runtime "^6.22.0"
babel-template "^6.24.1"
+babel-plugin-transform-import-ignore@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/babel-plugin-transform-import-ignore/-/babel-plugin-transform-import-ignore-1.1.0.tgz#542592e88f6a17dfcf410c85cda3a7dedc526d73"
+ integrity sha512-LRlRUiZE9FB/s2LZUnXyq1d1xmBeR2bOB/cN2GXS+rbcauYhazYHjvQoBKMJ2/e5MB/G9p2ywkkbZ/ru+oA+RA==
+
babel-plugin-transform-react-remove-prop-types@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz#f2edaf9b4c6a5fbe5c1d678bfb531078c1555f3a"