department-of-veterans-affairs/vets-website

View on GitHub
src/applications/vaos/covid-19-vaccine/components/VAFacilityPage/index.unit.spec.js

Summary

Maintainability
F
4 days
Test Coverage
/* eslint-disable no-plusplus */
import React from 'react';
import { expect } from 'chai';

import {
  mockFetch,
  setFetchJSONFailure,
} from '@department-of-veterans-affairs/platform-testing/helpers';
import environment from '@department-of-veterans-affairs/platform-utilities/environment';
import { fireEvent, waitFor, within } from '@testing-library/dom';
import { cleanup } from '@testing-library/react';
import VAFacilityPage from '.';
import {
  createTestStore,
  renderWithStoreAndRouter,
} from '../../../tests/mocks/setup';
import { TYPE_OF_CARE_ID } from '../../utils';
import {
  createMockClinic,
  createMockFacility,
} from '../../../tests/mocks/data';
import {
  mockEligibilityFetches,
  mockFacilitiesFetch,
} from '../../../tests/mocks/fetch';
import {
  mockSchedulingConfigurations,
  mockGetCurrentPosition,
} from '../../../tests/mocks/helpers';
import { getSchedulingConfigurationMock } from '../../../tests/mocks/mock';

const facilityIds = ['983', '983GB', '983GC', '983HK', '983QA', '984'];

// TODO: Make sure this works in staging before removal
// const vhaIds = facilityIds.map(
//   id => `vha_${id.replace('983', '442').replace('984', '552')}`,
// );

const facilities = facilityIds.map((id, index) =>
  createMockFacility({
    id: id.replace('vha_', ''),
    name: `Fake facility name ${index + 1}`,
    lat: Math.random() * 90,
    long: Math.random() * 180,
    address: {
      line: [`Fake street ${index + 1}`],
      city: `Fake city ${index + 1}`,
      state: `Fake state ${index + 1}`,
      postalCode: `Fake zip ${index + 1}`,
    },
  }),
);

const closestFacility = facilities[2];
closestFacility.attributes.name = 'Closest facility';
closestFacility.attributes.lat = 39.50603012; // Dayton, OH
closestFacility.attributes.long = -84.3164749;

describe('VAOS vaccine flow: VAFacilityPage', () => {
  describe('When there are more than 5 facilities', () => {
    const initialState = {
      user: {
        profile: {
          facilities: [
            { facilityId: '983', isCerner: false },
            { facilityId: '983GB', isCerner: false },
            { facilityId: '983GC', isCerner: false },
            { facilityId: '983HK', isCerner: false },
            { facilityId: '983QA', isCerner: false },
            { facilityId: '984', isCerner: false },
          ],
        },
      },
    };

    beforeEach(() => mockFetch());

    it('should display list of facilities with show more button', async () => {
      const configs = facilities.map(facility =>
        getSchedulingConfigurationMock({
          id: facility.id,
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
      );

      mockSchedulingConfigurations(configs);
      mockFacilitiesFetch({
        children: true,
        ids: facilityIds,
        facilities,
      });

      const store = createTestStore(initialState);

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      const buttons = await screen.findAllByRole('radio');

      expect(await screen.findByText(/Choose a VA location/i)).to.exist;
      await waitFor(() => {
        expect(global.document.title).to.equal(
          'Choose a VA location | Veterans Affairs',
        );
      });

      expect(screen.baseElement).to.contain.text(
        'Here’s a list of VA facilities where you’re registered that offer COVID-19 vaccine appointments',
      );

      // Should contain radio buttons
      facilities.slice(0, 5).forEach(f => {
        expect(screen.baseElement).to.contain.text(f.attributes.name);
      });

      // Should not show address
      expect(screen.baseElement).not.to.contain.text(
        'Facilities based on your home address',
      );

      // Should not show 6th facility
      expect(screen.baseElement).not.to.contain.text('Fake facility name 6');

      // Find show more button and fire click event
      const moreLocationsBtn = screen.getByText('Show 1 more location');
      expect(moreLocationsBtn).to.have.tagName('span');
      fireEvent.click(moreLocationsBtn);

      // Should show 6th facility
      expect(screen.baseElement).to.contain.text('Fake facility name 6');
      expect(screen.baseElement).to.contain.text('Fake street 6');
      expect(screen.baseElement).to.contain.text(
        'Fake city 6, Fake state 6 Fake zip 6',
      );
      await waitFor(() =>
        expect(document.activeElement.id).to.equal('root_vaFacility_6'),
      );

      // Should verify that all radio buttons have the same name (508 accessibility)
      buttons.forEach(button => {
        expect(button.name).to.equal('root_vaFacility');
      });

      // Should validation message if no facility selected
      fireEvent.click(screen.getByText(/Continue/));
      expect(
        await screen.findByText(
          'Please select a location for your appointment',
        ),
      ).to.be.ok;
    });

    it('should sort by distance from current location if user clicks "use current location"', async () => {
      const configs = facilities.map(facility =>
        getSchedulingConfigurationMock({
          id: facility.id,
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
      );

      mockSchedulingConfigurations(configs);
      mockFacilitiesFetch({
        children: true,
        ids: facilityIds,
        facilities,
      });
      mockGetCurrentPosition();

      const store = createTestStore({
        ...initialState,
        user: {
          ...initialState.user,
          profile: {
            ...initialState.user.profile,
            vapContactInfo: {
              residentialAddress: {
                addressLine1: '290 Ludlow Ave',
                city: 'Cincinatti',
                stateCode: 'OH',
                zipCode: '45220',
                latitude: 39.1362562, // Cincinatti, OH
                longitude: -84.6804804,
              },
            },
          },
        },
      });

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      // Radio buttons only show up after all the data is loaded, which
      // should mean all page rendering is finished
      await screen.findAllByRole('radio');
      fireEvent.click(screen.getByText('use your current location'));
      await screen.findAllByRole('radio');
      expect(screen.baseElement).to.contain.text(
        'Facilities based on your location',
      );
      expect(screen.baseElement).not.to.contain.text(
        'use your current location',
      );

      // Providers should be sorted.
      const miles = screen.queryAllByText(/miles$/);

      expect(miles.length).to.equal(5);
      expect(() => {
        for (let i = 0; i < miles.length - 1; i++) {
          if (
            Number.parseFloat(miles[i].textContent) >
            Number.parseFloat(miles[i + 1].textContent)
          )
            throw new Error();
        }
      }).to.not.throw();

      // Clicking use home address should revert sort back to distance from hoem address
      fireEvent.click(screen.getByText('use your home address on file'));
      expect(screen.baseElement).to.contain.text(
        'Facilities based on your home address',
      );
    });
  });

  describe('When there are less than 5 facilities', () => {
    const initialState = {
      user: {
        profile: {
          facilities: [
            { facilityId: '983', isCerner: false },
            { facilityId: '984', isCerner: false },
          ],
        },
      },
    };

    beforeEach(() => {
      mockFetch();

      mockEligibilityFetches({
        facilityId: '983',
        typeOfCareId: TYPE_OF_CARE_ID,
      });
      mockFacilitiesFetch({
        children: true,
        ids: ['983', '984'],
        facilities,
      });
      const configs = facilityIds.map(id =>
        getSchedulingConfigurationMock({
          id,
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
      );
      mockSchedulingConfigurations(configs);
    });

    it('should show residential address and sort by distance if we have coordinates', async () => {
      const store = createTestStore({
        ...initialState,
        user: {
          ...initialState.user,
          profile: {
            ...initialState.user.profile,
            vapContactInfo: {
              residentialAddress: {
                addressLine1: '290 Ludlow Ave',
                city: 'Cincinatti',
                stateCode: 'OH',
                zipCode: '45220',
                latitude: 39.1362562, // Cincinatti, OH
                longitude: -84.6804804,
              },
            },
          },
        },
      });

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      // Radio buttons only show up after all the data is loaded, which
      // should mean all page rendering is finished
      await screen.findAllByRole('radio');

      expect(screen.baseElement).to.contain.text(
        'Here’s a list of VA facilities where you’re registered that offer COVID-19 vaccine appointments',
      );
      expect(screen.baseElement).to.contain.text(
        'Locations closest to you are listed first',
      );
      expect(screen.baseElement).to.contain.text(
        'Facilities based on your home address',
      );
      expect(screen.baseElement).to.contain.text('290 Ludlow Ave');
      expect(screen.baseElement).to.contain.text('Cincinatti, OhioOH 45220');
      expect(screen.baseElement).to.contain.text(' miles');

      // It should sort by distance, making Closest facility the first facility
      const firstRadio = screen.container.querySelector('.form-radio-buttons');
      expect(firstRadio).to.contain.text('Closest facility');

      // Providers should be sorted.
      const miles = screen.queryAllByText(/miles$/);

      expect(miles.length).to.equal(5);

      expect(() => {
        for (let i = 0; i < miles.length - 1; i++) {
          if (
            Number.parseFloat(miles[i].textContent) >
            Number.parseFloat(miles[i + 1].textContent)
          )
            throw new Error();
        }
      }).to.not.throw();
    });

    it('should not display show more button if < 6 locations', async () => {
      const testState = {
        user: {
          profile: {
            facilities: [
              { facilityId: '983', isCerner: false },
              { facilityId: '983GB', isCerner: false },
              { facilityId: '983GC', isCerner: false },
              { facilityId: '983HK', isCerner: false },
              { facilityId: '983QA', isCerner: false },
            ],
          },
        },
      };
      const configs = facilities.slice(0, 5).map(facility =>
        getSchedulingConfigurationMock({
          id: facility.id,
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
      );

      mockSchedulingConfigurations(configs);
      mockFacilitiesFetch({
        children: true,
        ids: facilityIds.slice(0, 5),
        facilities: facilities.slice(0, 5),
      });

      const store = createTestStore(testState);
      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      // Radio buttons only show up after all the data is loaded, which
      // should mean all page rendering is finished
      await screen.findAllByRole('radio');

      expect(
        screen.getByText(
          /Here’s a list of VA facilities where you’re registered that offer COVID-19 vaccine appointments/i,
        ),
      ).to.exist;

      // Should contain radio buttons
      facilities.slice(0, 5).forEach(f => {
        expect(screen.baseElement).to.contain.text(f.attributes.name);
      });

      expect(screen.baseElement).not.to.contain.text('more location');
    });

    it('should display previous user choices when returning to page', async () => {
      const store = createTestStore(initialState);

      let screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      // Radio buttons only show up after all the data is loaded, which
      // should mean all page rendering is finished
      await screen.findAllByRole('radio');

      fireEvent.click(await screen.findByLabelText(/Fake facility name 1/i));
      fireEvent.click(await screen.findByRole('button', { name: /Continue/i }));

      await cleanup();

      screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      expect(
        await screen.findByLabelText(/Fake facility name 1/i),
      ).to.have.attribute('checked');

      expect(
        await screen.queryByText(
          /You’ve reached the limit for appointment request/i,
        ),
      ).to.be.null;
    });

    it('should show eligibility modal again if user closes it out and hits continue again with the same facility selected', async () => {
      const store = createTestStore(initialState);

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      await screen.findByText(/Here’s a list of VA facilities/i);

      fireEvent.click(await screen.findByLabelText(/Fake facility name 5/i));
      fireEvent.click(screen.getByText(/Continue/));
      await screen.findByTestId('eligibilityModal');
      expect(screen.findByTestId('eligibilityModal')).to.exist;
      fireEvent.click(screen.getByText(/Continue/));
      await screen.findByTestId('eligibilityModal');
    });
  });

  describe('When there are errors', () => {
    const initialState = {
      user: {
        profile: {
          facilities: [
            { facilityId: '983', isCerner: false },
            { facilityId: '984', isCerner: false },
          ],
        },
      },
    };

    const facility983 = createMockFacility({
      id: '983',
      name: 'Facility 983',
    });
    const facility984 = createMockFacility({
      id: '984',
      name: 'Facility 984',
    });

    beforeEach(() => {
      mockFetch();

      mockSchedulingConfigurations([
        getSchedulingConfigurationMock({
          id: '983',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
        getSchedulingConfigurationMock({
          id: '984',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
      ]);
    });

    it('should display error messaging if user denied location permissions', async () => {
      mockGetCurrentPosition({ fail: true });
      mockFacilitiesFetch({
        children: true,
        facilities: [facility983, facility984],
      });

      const store = createTestStore({
        ...initialState,
        user: {
          ...initialState.user,
          profile: {
            ...initialState.user.profile,
            vapContactInfo: {
              residentialAddress: {
                addressLine1: '290 Ludlow Ave',
                city: 'Cincinnati',
                stateCode: 'OH',
                zipCode: '45220',
                latitude: 39.1362562, // Cincinnati, OH
                longitude: -84.6804804,
              },
            },
          },
        },
      });

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      await screen.findAllByRole('radio');
      fireEvent.click(screen.getByText('use your current location'));
      await screen.findAllByRole('radio');
      expect(
        await screen.findByRole('heading', {
          level: 3,
          name: /Your browser is blocked from finding your current location/,
        }),
      ).to.be.ok;
    });

    it('should show no facilities message', async () => {
      const state = {
        ...initialState,
        user: {
          ...initialState.user,
          profile: {
            ...initialState.user.profile,
            vapContactInfo: {
              residentialAddress: {
                addressLine1: '290 Ludlow Ave',
                city: 'Cincinnati',
                stateCode: 'OH',
                zipCode: '45220',
                latitude: 39.1362562,
                longitude: -84.6804804,
              },
            },
          },
        },
      };
      const store = createTestStore(state);

      mockFacilitiesFetch({
        children: true,
        facilities: [facility983, facility984],
      });
      mockSchedulingConfigurations([
        getSchedulingConfigurationMock({
          id: '983',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: false,
        }),
        getSchedulingConfigurationMock({
          id: '984',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: false,
        }),
      ]);

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      expect(await screen.findByText(/We couldn’t find a VA facility/i)).to
        .exist;
      expect(screen.baseElement).to.contain.text(
        'We couldn’t find a VA facility where you receive care that accepts online appointments for COVID-19 vaccines',
      );
    });

    it('should display an error message when eligibility calls fail', async () => {
      const store = createTestStore(initialState);

      mockFacilitiesFetch({
        children: true,
        facilities: [facility983, facility984],
      });

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      fireEvent.click(await screen.findByLabelText(/Facility 983/i));
      fireEvent.click(screen.getByText(/Continue/));
      expect(await screen.findByText(/something went wrong on our end/i)).to
        .exist;
    });

    it('should display an error message when facilities call fails', async () => {
      const store = createTestStore(initialState);

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      expect(await screen.findByText(/something went wrong on our end/i)).to
        .exist;
    });

    it('should show alert when only one facility is supported', async () => {
      mockFacilitiesFetch({
        children: true,
        facilities: [facility983, facility984],
      });
      const clinic = createMockClinic({
        id: '1',
        stationId: '983',
        name: '',
      });
      mockEligibilityFetches({
        facilityId: '983',
        typeOfCareId: TYPE_OF_CARE_ID,
        clinics: [clinic, clinic],
      });
      mockSchedulingConfigurations([
        getSchedulingConfigurationMock({
          id: '983',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
        getSchedulingConfigurationMock({
          id: '984',
          typeOfCareId: 'primaryCare',
          requestEnabled: false,
        }),
      ]);

      const store = createTestStore(initialState);

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      expect(
        await screen.findByText(
          "We found one VA location where you're registered that offers COVID-19 vaccine appointments.",
        ),
      ).to.exist;

      expect(screen.baseElement).to.contain.text('Facility 983');

      fireEvent.click(await screen.findByText(/Continue/));
      await waitFor(() =>
        expect(screen.history.push.firstCall.args[0]).to.equal(
          '/new-covid-19-vaccine-appointment/choose-clinic',
        ),
      );
    });

    it('should show alert and not allow user to continue if only one facility and no clinics', async () => {
      mockFacilitiesFetch({
        children: true,
        facilities: [facility983, facility984],
      });
      mockEligibilityFetches({
        facilityId: '983',
        typeOfCareId: TYPE_OF_CARE_ID,
        clinics: [],
      });
      mockSchedulingConfigurations([
        getSchedulingConfigurationMock({
          id: '983',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
        getSchedulingConfigurationMock({
          id: '984',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: false,
        }),
      ]);

      const store = createTestStore(initialState);

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      // it should verify alert heading
      expect(
        await screen.findByRole('heading', {
          level: 2,
          name: 'We found one VA location for you',
        }),
      ).to.exist;

      expect(screen.baseElement).to.contain.text(
        'However, we couldn’t find any available slots right now',
      );

      expect(screen.getByText(/Continue/)).to.have.attribute('disabled');
    });

    it('should show error and not allow user to continue if only one facility and clinic call fails', async () => {
      mockFacilitiesFetch({
        children: true,
        facilities: [facility983, facility984],
      });
      mockSchedulingConfigurations([
        getSchedulingConfigurationMock({
          id: '983',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
        getSchedulingConfigurationMock({
          id: '984',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: false,
        }),
      ]);

      const store = createTestStore(initialState);

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      await screen.findByText(/We’re sorry. We’ve run into a problem/i);
    });

    it('should show eligibility modal with error if clinic call fails', async () => {
      mockFacilitiesFetch({
        children: true,
        facilities: [facility983, facility984],
      });
      mockSchedulingConfigurations([
        getSchedulingConfigurationMock({
          id: '983',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
        getSchedulingConfigurationMock({
          id: '984',
          typeOfCareId: TYPE_OF_CARE_ID,
          directEnabled: true,
        }),
      ]);

      setFetchJSONFailure(
        global.fetch.withArgs(
          `${environment.API_URL}
        /vaos/v2/locations/983/clinics?clinical_service=covid`,
        ),
        {
          errors: [],
        },
      );
      const store = createTestStore(initialState);

      const screen = renderWithStoreAndRouter(<VAFacilityPage />, {
        store,
      });

      await screen.findByText(/Here’s a list of VA facilities/i);

      fireEvent.click(await screen.findByLabelText(/Facility 983/i));
      fireEvent.click(screen.getByText(/Continue/));
      await screen.findByTestId('eligibilityModal');
      expect(
        within(screen.getByRole('alertdialog')).getByText(
          /Something went wrong/i,
        ),
      ).to.exist;
    });
  });
});