department-of-veterans-affairs/vets-website

View on GitHub
src/applications/vaos/new-appointment/components/CommunityCareProviderSelectionPage/index.unit.spec.js

Summary

Maintainability
F
4 days
Test Coverage
import React from 'react';
import { expect } from 'chai';
import userEvent from '@testing-library/user-event';
import { waitFor } from '@testing-library/dom';
import { cleanup } from '@testing-library/react';
import { mockFetch } from '@department-of-veterans-affairs/platform-testing/helpers';

import {
  createTestStore,
  renderWithStoreAndRouter,
  setClosestCity,
  setCommunityCareFlow,
  setTypeOfCare,
  setTypeOfFacility,
} from '../../../tests/mocks/setup';
import {
  mockCCProviderFetch,
  mockSchedulingConfigurations,
  mockV2CommunityCareEligibility,
  mockGetCurrentPosition,
} from '../../../tests/mocks/helpers';

import CommunityCareProviderSelectionPage from '.';
import { calculateBoundingBox } from '../../../utils/address';
import { CC_PROVIDERS_DATA } from '../../../tests/mocks/cc_providers_data';
import { FACILITY_SORT_METHODS, GA_PREFIX } from '../../../utils/constants';
import { mockFacilitiesFetch } from '../../../tests/mocks/fetch';
import { createMockFacility } from '../../../tests/mocks/data';
import { getSchedulingConfigurationMock } from '../../../tests/mocks/mock';

const initialState = {
  featureToggles: {
    vaOnlineSchedulingCommunityCare: true,
  },
  user: {
    profile: {
      facilities: [{ facilityId: '983', isCerner: false }],
      vapContactInfo: {
        residentialAddress: {
          addressLine1: '123 big sky st',
          city: 'Cincinnati',
          stateCode: 'OH',
          zipCode: '45220',
          latitude: 39.1,
          longitude: -84.6,
        },
      },
    },
  },
};

describe('VAOS Page: CommunityCareProviderSelectionPage', () => {
  beforeEach(() => {
    mockFetch();
    mockV2CommunityCareEligibility({
      parentSites: ['983', '983GJ', '983GC'],
      supportedSites: ['983', '983GJ'],
      careType: 'PrimaryCare',
    });
    mockCCProviderFetch(
      initialState.user.profile.vapContactInfo.residentialAddress,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        initialState.user.profile.vapContactInfo.residentialAddress.latitude,
        initialState.user.profile.vapContactInfo.residentialAddress.longitude,
        60,
      ),
      CC_PROVIDERS_DATA,
    );
    mockFacilitiesFetch({
      children: true,
      ids: ['983'],
      facilities: [
        createMockFacility({
          id: '983',
          address: {
            line: [],
            city: 'Belgrade',
          },
          lat: 39.1362562,
          long: -83.1804804,
        }),
      ],
    });
    mockSchedulingConfigurations(
      [
        getSchedulingConfigurationMock({
          id: '983',
          typeOfCareId: 'primaryCare',
          requestEnabled: true,
        }),
      ],
      true,
    );
  });
  it('should display closest city question when user has multiple supported sites', async () => {
    const store = createTestStore(initialState);
    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');
    const screen = renderWithStoreAndRouter(
      <CommunityCareProviderSelectionPage />,
      {
        store,
      },
    );

    // Trigger provider list loading
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );
    expect(await screen.findByText(/Displaying 5 of 16 providers/i)).to.be.ok;
    expect(screen.getAllByRole('radio').length).to.equal(5);

    expect(
      global.window.dataLayer.some(
        e => e === `${GA_PREFIX}-community-care-provider-selection-page`,
      ),
    );

    expect(screen.baseElement).to.contain.text('Primary care providers');

    // Continue without filling in required fields
    userEvent.click(screen.getByText(/Continue/i));
    await waitFor(() => expect(screen.history.push.called).to.be.true);
    expect(
      global.window.dataLayer.some(
        e => e === `${GA_PREFIX}-continue-without-provider`,
      ),
    );

    // Continue with filling in required fields with provider
    userEvent.click(
      await screen.findByRole('heading', {
        level: 2,
        name: /Primary care provider/i,
      }),
    );
    userEvent.click(await screen.findByText(/OH, JANICE/i));
    userEvent.click(
      await screen.getByRole('button', { name: /Select provider/i }),
    );
    expect(
      global.window.dataLayer.some(
        e => e === `${GA_PREFIX}-order-position-provider-selection`,
      ),
    );
    expect(await screen.baseElement).to.contain.text('OH, JANICEANNANDALE, VA');

    userEvent.click(screen.getByText(/Continue/i));
    expect(
      global.window.dataLayer.some(
        e => e === `${GA_PREFIX}-continue-with-provider`,
      ),
    );
    await waitFor(() => expect(screen.history.push.called).to.be.true);
  });

  it('should display list of providers when choose a provider clicked', async () => {
    const store = createTestStore(initialState);
    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');
    const screen = renderWithStoreAndRouter(
      <CommunityCareProviderSelectionPage />,
      {
        store,
      },
    );

    // Trigger provider list loading
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );

    expect(await screen.findByText(/Displaying 5 of 16 providers/i)).to.be.ok;
    expect(screen.getAllByRole('radio').length).to.equal(5);

    userEvent.click(await screen.findByText(/Show 5 more providers/i));
    expect((await screen.findAllByRole('radio')).length).to.equal(10);
    await waitFor(() => {
      expect(document.activeElement.id).to.equal(
        'root_communityCareProvider_6',
      );
    });

    userEvent.click(await screen.findByText(/Show 5 more providers/i));
    expect(await screen.findByText(/displaying 15 of 16 providers/i)).to.exist;
    expect((await screen.findAllByRole('radio')).length).to.equal(15);
    await waitFor(() => {
      expect(document.activeElement.id).to.equal(
        'root_communityCareProvider_11',
      );
    });

    userEvent.click(await screen.findByText(/Show 1 more provider/i));
    expect(await screen.findByText(/displaying 16 of 16 providers/i)).to.exist;
    expect((await screen.findAllByRole('radio')).length).to.equal(16);
    await waitFor(() => {
      expect(document.activeElement.id).to.equal(
        'root_communityCareProvider_16',
      );
    });
    // Choose Provider
    userEvent.click(await screen.findByText(/AJADI, ADEDIWURA/i));
    userEvent.click(
      await screen.getByRole('button', { name: /Select provider/i }),
    );
    expect(screen.baseElement).to.contain.text('Preferred provider');
    expect(screen.baseElement).to.contain.text(
      'AJADI, ADEDIWURAWASHINGTON, DC',
    );

    // Change Provider
    userEvent.click(
      await screen.findByRole('button', { name: /change provider/i }),
    );
    userEvent.click(await screen.findByText(/OH, JANICE/i));
    userEvent.click(
      await screen.findByRole('button', { name: /Select provider/i }),
    );

    expect(screen.baseElement).to.contain.text('OH, JANICEANNANDALE, VA');

    // Cancel Selection (not clearing of a selected provider)
    userEvent.click(
      await screen.findByRole('button', { name: /change provider/i }),
    );
    expect(await screen.findByText(/displaying 5 of 16 providers/i)).to.exist;
    userEvent.click(await screen.findByRole('button', { name: /cancel/i }));
    expect(screen.baseElement).to.contain.text('OH, JANICEANNANDALE, VA');
  });

  it('should display Select provider when remove provider clicked', async () => {
    const store = createTestStore(initialState);
    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');
    const screen = renderWithStoreAndRouter(
      <CommunityCareProviderSelectionPage />,
      {
        store,
      },
    );

    // Choose Provider that is buried 2 clicks deep
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );
    userEvent.click(await screen.findByText(/more providers$/i));
    userEvent.click(await screen.findByText(/more providers$/i));
    userEvent.click(await screen.findByText(/AJADI, ADEDIWURA/i));
    userEvent.click(
      await screen.getByRole('button', { name: /Select provider/i }),
    );

    // Remove Provider
    userEvent.click(await screen.findByRole('button', { name: /remove/i }));
    expect(await screen.findByTestId('removeProviderModal')).to.exist;
    userEvent.click(
      await screen.findByRole('button', { name: /Remove provider/i }),
    );
    expect(await screen.findByRole('button', { name: /Find a provider/i }));
    expect(screen.baseElement).not.to.contain.text(
      'AJADI, ADEDIWURAWASHINGTON, DC',
    );
  });

  it('should allow remove provider clicked when user has no residential address', async () => {
    // Given the CC iteration flag is on
    // And the user does not have a residential address
    const store = createTestStore({
      ...initialState,
      user: {
        profile: {
          facilities: [{ facilityId: '983', isCerner: false }],
          vapContactInfo: {
            residentialAddress: {},
          },
        },
      },
    });

    const facilityPosition = {
      latitude: 39.1362562,
      longitude: -83.1804804,
      fail: false,
    };

    mockGetCurrentPosition(facilityPosition);
    mockCCProviderFetch(
      facilityPosition,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        facilityPosition.latitude,
        facilityPosition.longitude,
        60,
      ),
      CC_PROVIDERS_DATA,
    );

    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');

    // Facility 983 is the 2nd of three options so the expectation is
    // that it should be selected when we get to the CommunityCareProviderSelectionPage.
    await setClosestCity(store, '983');
    const screen = renderWithStoreAndRouter(
      <CommunityCareProviderSelectionPage />,
      {
        store,
      },
    );
    // When the user tries to choose a provider
    // Trigger provider list loading
    userEvent.click(
      await screen.findByText(/Find a provider/i, {
        selector: 'button',
      }),
    );

    expect(await screen.findByTestId('providersSelect')).to.exist;

    // Choose Provider
    userEvent.click(await screen.findByText(/OH, JANICE/i));
    userEvent.click(
      await screen.getByRole('button', {
        name: /Select provider/i,
      }),
    );
    expect(screen.baseElement).to.contain.text('Preferred provider');
    expect(screen.baseElement).to.contain.text('OH, JANICEANNANDALE, VA');

    // Remove Provider
    userEvent.click(await screen.findByRole('button', { name: /remove/i }));
    expect(await screen.findByTestId('removeProviderModal')).to.exist;
    userEvent.click(
      await screen.findByRole('button', { name: /Remove provider/i }),
    );
    expect(await screen.findByRole('button', { name: /Find a provider/i }));
  });

  it('should display an error when choose a provider clicked and provider fetch error', async () => {
    const store = createTestStore(initialState);
    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');
    mockCCProviderFetch(
      initialState.user.profile.vapContactInfo.residentialAddress,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        initialState.user.profile.vapContactInfo.residentialAddress.latitude,
        initialState.user.profile.vapContactInfo.residentialAddress.longitude,
        60,
      ),
      CC_PROVIDERS_DATA,
      true,
    );
    const screen = renderWithStoreAndRouter(
      <CommunityCareProviderSelectionPage />,
      {
        store,
      },
    );

    // Trigger provider list loading
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );
    expect(
      await screen.findByRole('heading', {
        name: /We can’t load provider information/i,
      }),
    );
    expect(
      await screen.findByText(
        /We’re sorry. Something went wrong on our end. To request this appointment, you can:/i,
      ),
    );
  });

  it('should display an alert when no providers are available', async () => {
    const store = createTestStore(initialState);
    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');
    mockCCProviderFetch(
      initialState.user.profile.vapContactInfo.residentialAddress,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        initialState.user.profile.vapContactInfo.residentialAddress.latitude,
        initialState.user.profile.vapContactInfo.residentialAddress.longitude,
        60,
      ),
      [],
    );
    const screen = renderWithStoreAndRouter(
      <CommunityCareProviderSelectionPage />,
      {
        store,
      },
    );

    // Trigger provider list loading
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );
    expect(await screen.findByText(/To request this appointment, you can/i)).to
      .exist;
    expect(
      screen.findByRole('heading', {
        level: 3,
        name: /We .* find any Primary care providers close to you/i,
      }),
    );
    expect(
      await screen.findByRole('link', {
        name: /Find your health facility’s phone number/i,
      }),
    );
  });

  it('should notify user that the browser is blocked from using current location information', async () => {
    const store = createTestStore(initialState);

    mockGetCurrentPosition({ fail: true });

    mockCCProviderFetch(
      initialState.user.profile.vapContactInfo.residentialAddress,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        initialState.user.profile.vapContactInfo.residentialAddress.latitude,
        initialState.user.profile.vapContactInfo.residentialAddress.longitude,
        60,
      ),
      CC_PROVIDERS_DATA,
      // true,
    );

    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');

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

    // Choose Provider
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );
    // await waitFor(async () => {
    expect(await screen.findByText(/Displaying 5 of/i)).to.be.ok;
    // });
    const providersSelect = await screen.findByTestId('providersSelect');
    // call VaSelect custom event for onChange handling
    providersSelect.__events.vaSelect({
      detail: { value: FACILITY_SORT_METHODS.distanceFromCurrentLocation },
    });

    await waitFor(async () => {
      expect(
        await screen.findByRole('heading', {
          level: 3,
          name: /Your browser is blocked from finding your current location/,
        }),
      ).to.be.ok;
    });

    expect(
      screen.getByText(
        /Make sure your browser’s location feature is turned on./i,
      ),
    ).to.be.ok;
  });

  it('should sort provider addresses by distance from home address in ascending order', async () => {
    const store = createTestStore(initialState);

    mockCCProviderFetch(
      initialState.user.profile.vapContactInfo.residentialAddress,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        initialState.user.profile.vapContactInfo.residentialAddress.latitude,
        initialState.user.profile.vapContactInfo.residentialAddress.longitude,
        60,
      ),
      CC_PROVIDERS_DATA,
    );

    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');

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

    // Choose Provider based on home address
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );
    userEvent.click(await screen.findByText(/Show 5 more providers$/i));
    userEvent.click(await screen.findByText(/Show 5 more providers$/i));
    userEvent.click(await screen.findByText(/Show 1 more provider$/i));

    const miles = screen.queryAllByText(/miles$/);

    expect(miles.length).to.equal(16);
    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 sort provider addresses by distance from current location in ascending order and display distance from current location when chosen', async () => {
    const store = createTestStore(initialState);
    const currentPosition = {
      latitude: 37.5615,
      longitude: 121.9988,
      fail: false,
    };

    mockGetCurrentPosition(currentPosition);

    mockCCProviderFetch(
      currentPosition,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        currentPosition.latitude,
        currentPosition.longitude,
        60,
      ),
      CC_PROVIDERS_DATA,
    );

    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');

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

    // Choose Provider based on home address
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );

    // Choose Provider based on current location
    const currentLocButton = await screen.findByText(/your current location$/i);
    await screen.findByText(/Displaying 5 of /i);
    userEvent.click(currentLocButton);
    userEvent.click(await screen.findByText(/Show 5 more providers$/i));
    userEvent.click(await screen.findByText(/Show 5 more providers$/i));
    userEvent.click(await screen.findByText(/Show 1 more provider$/i));

    const miles = screen.queryAllByText(/miles$/);

    expect(miles.length).to.equal(16);
    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();

    userEvent.click(await screen.findByText(/OH, JANICE/i));
    userEvent.click(
      await screen.findByRole('button', { name: /Select provider/i }),
    );

    expect(screen.baseElement).to.contain.text('OH, JANICEANNANDALE, VA');
  });

  it('should reset provider selected when type of care changes', async () => {
    const store = createTestStore(initialState);

    mockCCProviderFetch(
      initialState.user.profile.vapContactInfo.residentialAddress,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        initialState.user.profile.vapContactInfo.residentialAddress.latitude,
        initialState.user.profile.vapContactInfo.residentialAddress.longitude,
        60,
      ),
      CC_PROVIDERS_DATA,
    );
    mockV2CommunityCareEligibility({
      parentSites: ['983', '983GJ', '983GC'],
      supportedSites: ['983', '983GJ'],
      careType: 'Podiatry',
    });

    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');

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

    // Choose Provider based on home address
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );

    userEvent.click(await screen.findByLabelText(/OH, JANICE/i));
    userEvent.click(
      await screen.findByRole('button', { name: /Select provider/i }),
    );

    // make sure it saves successfully
    await screen.findByRole('button', { name: /remove/i });

    // remove the page and change the type of care
    await cleanup();

    mockCCProviderFetch(
      initialState.user.profile.vapContactInfo.residentialAddress,
      ['213E00000X', '213EG0000X', '213EP1101X', '213ES0131X', '213ES0103X'],
      calculateBoundingBox(
        initialState.user.profile.vapContactInfo.residentialAddress.latitude,
        initialState.user.profile.vapContactInfo.residentialAddress.longitude,
        60,
      ),
      CC_PROVIDERS_DATA,
    );
    await setTypeOfCare(store, /podiatry/i);

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

    // the provider should no longer be set
    expect(await screen.findByRole('button', { name: /Find a provider/i })).to
      .exist;
    expect(screen.queryByText(/OH, JANICE/i)).to.not.exist;
  });

  it('should allow user to retry fetching location when it is blocked', async () => {
    const store = createTestStore(initialState);

    mockGetCurrentPosition({ fail: true });

    mockCCProviderFetch(
      initialState.user.profile.vapContactInfo.residentialAddress,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        initialState.user.profile.vapContactInfo.residentialAddress.latitude,
        initialState.user.profile.vapContactInfo.residentialAddress.longitude,
        60,
      ),
      CC_PROVIDERS_DATA,
    );

    await setTypeOfCare(store, /primary care/i);
    await setTypeOfFacility(store, 'communityCare');

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

    // Choose Provider
    userEvent.click(
      await screen.findByRole('button', { name: /Find a provider/i }),
    );
    await waitFor(() =>
      expect(screen.getAllByRole('radio').length).to.equal(5),
    );

    const providersSelect = await screen.findByTestId('providersSelect');
    // call VaSelect custom event for onChange handling
    providersSelect.__events.vaSelect({
      detail: { value: FACILITY_SORT_METHODS.distanceFromCurrentLocation },
    });

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

    expect(
      screen.getByText(
        /Make sure your browser’s location feature is turned on./i,
      ),
    ).to.be.ok;

    const currentPosition = {
      latitude: 37.5615,
      longitude: 121.9988,
      fail: false,
    };

    mockGetCurrentPosition(currentPosition);
    mockCCProviderFetch(
      currentPosition,
      ['207QA0505X', '363LP2300X', '363LA2200X', '261QP2300X'],
      calculateBoundingBox(
        currentPosition.latitude,
        currentPosition.longitude,
        60,
      ),
      // Only return one provider to distinguish from initial request
      // by residential address
      CC_PROVIDERS_DATA.slice(0, 1),
    );

    userEvent.click(
      screen.getByText(/Retry searching based on current location/i),
    );

    // should eventually be one provder
    await waitFor(() =>
      expect(screen.getAllByRole('radio').length).to.equal(1),
    );
  });

  it('should not display closest city question since iterations toggle is now the default', async () => {
    // Given a user with two supported sites
    // And the CC iterations toggle is on
    // And type of care is selected
    const store = await setCommunityCareFlow({
      parentSites: [
        { id: '983', address: { city: 'Bozeman', state: 'MT' } },
        { id: '984', address: { city: 'Belgrade', state: 'MT' } },
      ],
    });

    // Facility 984 is the 2nd of three options so the expectation is
    // that it should be selected when we get to the CommunityCareProviderSelectionPage.
    await setClosestCity(store, '984');

    // When the page is displayed
    const screen = renderWithStoreAndRouter(
      <CommunityCareProviderSelectionPage />,
      {
        store,
      },
    );

    // Then the heading will display type of provider requested
    expect(
      await screen.findByRole('heading', {
        level: 1,
        name: /Which provider do you prefer/i,
      }),
    ).to.be.ok;

    // And the closest city/state question is not shown
    expect(screen.queryByLabelText('Bozeman, MT')).not.to.exist;
    expect(screen.queryByLabelText('Belgrade, MT')).not.to.exist;
    expect(screen.queryByText(/closest city and state/i)).not.to.exist;
  });
});