diff --git a/src/assets/img/tints/GlassTint.png b/src/assets/img/tints/GlassTint.png new file mode 100644 index 00000000..b3cf01ba Binary files /dev/null and b/src/assets/img/tints/GlassTint.png differ diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 45bceb0a..1c234ada 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -93,6 +93,10 @@ const endpoints = Object.freeze({ ) => `${LOCATION_BASE_URL}/providers/${zipCode}/${damageType}/${radius}/${parentAccountNumber}/${safeliteOnly}/${carId}/${recalPartNumber != null ? `${recalPartNumber}/` : ''}${isBigTruck}`, method: 'GET' }, + GetTpaAndSafeliteProviders: { + url: `${LOCATION_BASE_URL}/tpa-and-safelite-providers`, + method: 'GET' + }, GetCapabilityQuestions: { url: `${PARTS_BASE_URL}/capability-questions`, method: 'GET' diff --git a/src/constants/error-messages.js b/src/constants/error-messages.js index 224c6e30..1705ff1f 100644 --- a/src/constants/error-messages.js +++ b/src/constants/error-messages.js @@ -67,7 +67,10 @@ const errorMessages = Object.freeze({ DATE_REQUIRED: 'Please select a date', TIME_REQUIRED: 'Please select an appointment time.', PRIMARY_PHONE_REQUIRED: 'Primary phone number is required.', - BAILOUT_EMAIL_ADDRESS_REQUIRED: 'Email address is required.' + BAILOUT_EMAIL_ADDRESS_REQUIRED: 'Email address is required.', + TPA_SEARCH_ZIP_FORMAT: 'Please enter a valid Zip code.', + TPA_SEARCH_SHOP_FORMAT: 'Please enter a valid shop name.', + TPA_SEARCH_REQUIRED: 'Please enter a shop name or ZIP code.' }); export default errorMessages; diff --git a/src/constants/global-rules.js b/src/constants/global-rules.js index 878b7a27..840f8db3 100644 --- a/src/constants/global-rules.js +++ b/src/constants/global-rules.js @@ -28,7 +28,8 @@ const globalRules = Object.freeze({ ZIP_CODE_SEARCH_FORMAT: 'zip-code-search-format', OPTION_REQUIRED: 'option-required', PRIMARY_PHONE_REQUIRED: 'primary-phone-required', - BAILOUT_EMAIL_ADDRESS_REQUIRED: 'bailout-email-address-required' + BAILOUT_EMAIL_ADDRESS_REQUIRED: 'bailout-email-address-required', + TPA_SEARCH_FORMAT: 'tpa-search-format' }); export default globalRules; diff --git a/src/constants/tint-mapper.js b/src/constants/tint-mapper.js deleted file mode 100644 index 91fbd57b..00000000 --- a/src/constants/tint-mapper.js +++ /dev/null @@ -1,91 +0,0 @@ -// TintMap with array keys by location, lowercase to avoid as much string mismatching as possible. -// Src assumes you have a @/assets/img/tints/, making the final value @/assets/img/tints/{Src} in the markup. -// See vehicle-parts for implementation example. -const tintMap = Object.freeze({ - other: [ - // Blue Shade - { name: 'blue tint, blue shade', src: 'Glass-BlueShade-BlueTint.svg' }, - { name: 'brown tint, blue shade', src: 'Glass-BlueShade-BrownTint.svg' }, - { name: 'gray tint, blue shade', src: 'Glass-BlueShade-GrayTint.svg' }, - { name: 'green tint, blue shade', src: 'Glass-BlueShade-GreenTint.svg' }, - { name: 'clear, blue shade', src: 'Glass-BlueShade-NoTint.svg' }, - - // Brown Shade - { name: 'brown tint, brown shade', src: 'Glass-BrownShade-BrownTint.svg' }, - - // Gray Shade - { name: 'blue tint, gray shade', src: 'Glass-GrayShade-BlueTint.svg' }, - { name: 'brown tint, gray shade', src: 'Glass-GrayShade-BrownTint.svg' }, - { name: 'gray tint, gray shade', src: 'Glass-GrayShade-GrayTint.svg' }, - { name: 'green tint, gray shade', src: 'Glass-GrayShade-GreenTint.svg' }, - - // Green Shade - { name: 'blue tint, green shade', src: 'Glass-GreenShade-BlueTint.svg' }, - { name: 'brown tint, green shade', src: 'Glass-GreenShade-BrownTint.svg' }, - { name: 'green tint, green shade', src: 'Glass-GreenShade-GreenTint.svg' }, - - // Tints Only - { name: 'blue tint privacy', src: 'Glass-NoShade-BlueTint.svg' }, - { name: 'bronze tint', src: 'Glass-NoShade-BrownTint.svg' }, - { name: 'dark brown tint', src: 'Glass-NoShade-DarkBrownTint.svg' }, - { name: 'privacy, black frame', src: 'Glass-NoShade-Privacy.svg' }, - { name: 'gray tint', src: 'Glass-NoShade-GrayTint.svg' }, - { name: 'green tint', src: 'Glass-NoShade-GreenTint.svg' }, - { name: 'gray tint privacy', src: 'Glass-NoShade-GrayTint.svg' }, - { name: 'blue tint', src: 'Glass-NoShade-BlueTint.svg' }, - { name: 'green tint privacy', src: 'Glass-NoShade-GreenTint.svg' }, - - // No shade or tint - { name: 'clear', src: 'Glass-NoShade-NoTint.svg' } - ], - - windshield: [ - // Blue Shade - { name: 'blue tint, blue shade', src: 'Windshield-BlueShade-BlueTint.svg' }, - { name: 'bronze tint, blue shade', src: 'Windshield-BlueShade-BrownTint.svg' }, - { name: 'gray tint, blue shade', src: 'Windshield-BlueShade-GrayTint.svg' }, - { name: 'green tint, blue shade', src: 'Windshield-BlueShade-GreenTint.svg' }, - { name: 'clear, blue shade', src: 'Windshield-BlueShade-NoTint.svg' }, - - // Brown Shade - { name: 'bronze tint, bronze shade', src: 'Windshield-BrownShade-BrownTint.svg' }, - - // Gray Shade - { name: 'blue tint, gray shade', src: 'Windshield-GrayShade-BlueTint.svg' }, - { name: 'brown tint, gray shade', src: 'Windshield-GrayShade-BrownTint.svg' }, - { name: 'gray tint, gray shade', src: 'Windshield-GrayShade-GrayTint.svg' }, - { name: 'green tint, gray shade', src: 'Windshield-GrayShade-GreenTint.svg' }, - - // Green Shade - { name: 'blue tint, green shade', src: 'Windshield-GreenShade-BlueTint.svg' }, - { name: 'brown tint, green shade', src: 'Windshield-GreenShade-BrownTint.svg' }, - { name: 'green tint, green shade', src: 'Windshield-GreenShade-GreenTint.svg' }, - - // Tints Only - { name: 'blue tint', src: 'Windshield-NoShade-BlueTint.svg' }, - { name: 'bronze tint', src: 'Windshield-NoShade-BrownTint.svg' }, - { name: 'dark brown tint', src: 'Windshield-NoShade-DarkBrownTint.svg' }, - { name: 'privacy, black frame', src: 'Windshield-NoShade-Privacy.svg' }, - { name: 'gray tint', src: 'Windshield-NoShade-GrayTint.svg' }, - { name: 'green tint', src: 'Windshield-NoShade-GreenTint.svg' }, - { name: 'gray tint privacy', src: 'Windshield-NoShade-GrayTint.svg' }, - - // No shade or tint - { name: 'clear', src: 'Windshield-NoShade-NoTint.svg' } - ] -}); - -// Gets the tint image source string given the glass location, and the tint description (like 'Green Tint') -// Use lowered strings here to try to avoid mismatch. Returns an empty string if array key doesn't exist. -// Returns undefined if no items are found. -const getTintImage = (glassLocation, colorString) => { - // Windshield glass has special images, all other glass uses the same though. - const glassLoc = (glassLocation.toLowerCase() !== 'windshield') ? 'other' : 'windshield'; - if (tintMap[glassLoc] === undefined) { - return ''; - } - - return tintMap[glassLoc].find((item) => item.name.toLowerCase() === colorString.toLowerCase()); -}; - -export default getTintImage; diff --git a/src/digital-components/textbox-question/textbox-question.vue b/src/digital-components/textbox-question/textbox-question.vue index 85bef043..6bf89bb5 100644 --- a/src/digital-components/textbox-question/textbox-question.vue +++ b/src/digital-components/textbox-question/textbox-question.vue @@ -310,22 +310,31 @@ input::-webkit-date-and-time-value { .input-wrapper { position: relative; &.has-search-icon { + display:flex; + .form-control { + &:focus, &:focus-within { + border: 1px solid #0070d1; + } + max-height: 3rem; + } input[type='text'] { - border-radius: $border-radius; + border-color: #0070d1; + box-shadow: rgba(0, 0, 0, 0.2) 0rem 0.0625rem .25rem 0rem; + border-right: none; + border-radius: 4.5rem 0 0 4.5rem; + height: 3rem; + &:focus, &:focus-within { + border-right:none; + } } button[type='submit'] { - position: absolute; - top: 50%; - transform: translateY(-50%); - right: 0; background-image: url($svg-search-icon); background-repeat: no-repeat; background-position: center; - border-radius: 0 $border-radius-lg $border-radius-lg 0; + border-radius: 0 4.5rem 4.5rem 0; background-color: $blue-100; width: 2.75rem; - height: 100%; - border: 1px solid $gray-500; + border: 1px solid #0070d1; border-left: none; display: flex; } diff --git a/src/helpers/global-rule-definer.js b/src/helpers/global-rule-definer.js index b91f3dc0..58f52516 100644 --- a/src/helpers/global-rule-definer.js +++ b/src/helpers/global-rule-definer.js @@ -125,6 +125,28 @@ function defineGlobalZipCodeRules() { defineRule(globalRules.ZIP_CODE_SEARCH_FORMAT, regex(/^\d{5}$/, errorMessages.ZIP_FORMAT)); } +/** + * @summary Define global rules related to the zip code/shop name search on TPA Search page + */ +function defineGlobalTpaSearchRules() { + defineRule(globalRules.TPA_SEARCH_FORMAT, (value) => { + if (value.length === 0) { + return errorMessages.TPA_SEARCH_REQUIRED; + } + // Non Numeric + if (isNaN(value)) { + if (value.length < 3) { + return errorMessages.TPA_SEARCH_SHOP_FORMAT; + } + } else { + if (value.length != 5) { + return errorMessages.TPA_SEARCH_ZIP_FORMAT; + } + } + return true; + }); +} + /** * @function defineGlobalRules * @summary Define all global rules @@ -138,6 +160,6 @@ export default function defineGlobalRules() { defineGlobalPolicyNumberRules(); defineGlobalPolicyZipCodeRules(); defineGlobalZipCodeRules(); - + defineGlobalTpaSearchRules(); defineRule(globalRules.OPTION_REQUIRED, required(errorMessages.OPTION_REQUIRED)); } diff --git a/src/helpers/text-helper.js b/src/helpers/text-helper.js index b6fc5184..c05b870a 100644 --- a/src/helpers/text-helper.js +++ b/src/helpers/text-helper.js @@ -46,7 +46,7 @@ export function createUnorderedListFromStringOfParagraphs(stringOfParagraphs) { */ export function toTitleCase(text) { let temp = text?.toLowerCase() ?? ''; - return temp.replace(/(^|\s|-)\S/g, (letter) => letter.toUpperCase()); + return temp.replace(/(^|\s|-|\/)\S/g, (letter) => letter.toUpperCase()); } /** diff --git a/src/iss-components/questions-page-layout/questions-page-layout.vue b/src/iss-components/questions-page-layout/questions-page-layout.vue index 43ce4251..9a20bb12 100644 --- a/src/iss-components/questions-page-layout/questions-page-layout.vue +++ b/src/iss-components/questions-page-layout/questions-page-layout.vue @@ -6,7 +6,7 @@
diff --git a/src/iss-components/site-footer/site-footer.vue b/src/iss-components/site-footer/site-footer.vue index 9499c3c2..a694015e 100644 --- a/src/iss-components/site-footer/site-footer.vue +++ b/src/iss-components/site-footer/site-footer.vue @@ -14,14 +14,13 @@ id="stacked" class="col button-col d-flex px-0"> diff --git a/src/iss-components/tpa-shop-list-button/tpa-shop-list-button.vue b/src/iss-components/tpa-shop-list-button/tpa-shop-list-button.vue new file mode 100644 index 00000000..8fbe5146 --- /dev/null +++ b/src/iss-components/tpa-shop-list-button/tpa-shop-list-button.vue @@ -0,0 +1,119 @@ + + + + + \ No newline at end of file diff --git a/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap b/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap deleted file mode 100644 index 0c7e4c55..00000000 --- a/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap +++ /dev/null @@ -1,214 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`TPA search page returns the initial data 1`] = ` -Object { - "additionalButtonData": Object { - "displayAvailabilityIndicators": false, - }, - "dataLoaded": false, - "filter": "", - "mapZipCode": null, - "providers": Array [], - "reloadingProviders": false, - "rules": Object { - "filter": "option-required", - "provider": "option-required", - "zipCode": "zip-code-required|zip-code-search-format", - }, - "selectedProviderNumber": "", - "shopListButton": Object { - "beforeMount": [Function], - "components": Object { - "baseInputButton": Object { - "computed": Object { - "buttonId": [Function], - "eventTypes": [Function], - "inputType": [Function], - "isChecked": [Function], - "isValueSelectedOnClick": [Function], - }, - "data": [Function], - "emits": Array [ - "update:modelValue", - ], - "methods": Object { - "handleBlur": [Function], - "handleClick": [Function], - "handleEventAction": [Function], - "handleFocus": [Function], - "handleSelectionChange": [Function], - }, - "mounted": [Function], - "name": "base-input-button", - "props": Object { - "buttonWrapperClasses": Array [ - [Function], - [Function], - [Function], - ], - "groupName": Object { - "required": true, - "type": [Function], - }, - "inputClasses": Array [ - [Function], - [Function], - [Function], - ], - "isMultiSelect": [Function], - "isRequired": Object { - "default": true, - "type": [Function], - }, - "lastValuePushedToGa": Array [ - [Function], - [Function], - ], - "modelValue": Object { - "required": true, - "validator": [Function], - }, - "selectingInitiatesLoad": Object { - "default": false, - "type": [Function], - }, - "setLastValuePushedToGa": [Function], - "suppressError": [Function], - "validationRules": Object { - "default": "", - "type": [Function], - }, - "value": Object { - "required": true, - "type": Array [ - [Function], - [Function], - ], - }, - "valueToLogType": [Function], - }, - "render": [Function], - "setup": [Function], - }, - "loader": Object { - "computed": Object { - "cssProps": [Function], - }, - "methods": Object { - "captureClick": [Function], - }, - "name": "loader", - "props": Object { - "allowPageInteraction": Object { - "default": false - "type": [Function], - }, - "height": Object { - "default": 1, - "type": [Function], - }, - "loaderColor": Object { - "type": [Function], - }, - "loaderPosition": Object { - "type": [Function], - }, - "width": Object { - "default": 1, - "type": [Function], - }, - }, - "render": [Function], - }, - }, - "computed": Object { - "availabilityRatingClass": [Function], - "badgeText": [Function], - "isLoaderDisplayed": [Function], - }, - "data": [Function], - "mixins": Array [ - Object { - "computed": Object { - "selectedValue": Object { - "get": [Function], - "set": [Function], - }, - }, - "model": Object { - "event": "change", - "prop": "modelValue", - }, - "props": Object { - "additionalButtonData": [Function], - "additionalButtonStyling": [Function], - "altText": Object { - "default": "", - "type": [Function], - }, - "buttonAuxiliaryCopy": [Function], - "buttonBodyCopy": [Function], - "buttonFooterCopy": [Function], - "buttonImage": [Function], - "buttonImageId": [Function], - "buttonLabel": Array [ - [Function], - [Function], - ], - "buttonLabelSubCopy": [Function], - "groupName": Object { - "required": true, - "type": [Function], - }, - "isMultiSelect": [Function], - "isRequired": Object { - "default": true, - "type": [Function], - }, - "isWide": [Function], - "lastValuePushedToGa": Array [ - [Function], - [Function], - ], - "modelValue": Object { - "required": true, - "validator": [Function], - }, - "screenReaderOnlyText": [Function], - "selectingInitiatesLoad": Object { - "default": false, - "type": [Function], - }, - "setLastValuePushedToGa": [Function], - "suppressError": [Function], - "textPosition": [Function], - "validationRules": Object { - "default": "", - "type": [Function], - }, - "value": Object { - "required": true, - "type": Array [ - [Function], - [Function], - ], - }, - "valueToLogType": [Function], - }, - }, - ], - "name": "shop-list-button", - "render": [Function], - }, - "widget": Object { - "filterByQuestion": "FilterByQuestion", - "noNetworkShopsAlert": "NoNetworkShopsAlertWidget", - "searchInstructions": "SearchInstructions", - "shopNotListedLink": "ShopNotListedLink", - "siteFooter": "SiteFooterWidget", - "siteHeader": "SiteHeaderWidget", - "tpaSearchQuestion": "TPASearchQuestion", - }, - "zipCode": null, -} -`; diff --git a/src/layouts/tpa-search/tpa-search.spec.js b/src/layouts/tpa-search/tpa-search.spec.js index 393938b8..d2d5ad2e 100644 --- a/src/layouts/tpa-search/tpa-search.spec.js +++ b/src/layouts/tpa-search/tpa-search.spec.js @@ -1,1031 +1,503 @@ +/* eslint-disable max-len */ // Components -import { shallowMount } from '@vue/test-utils'; -import { createTestingPinia } from '@pinia/testing'; import tpaSearch from '@/layouts/tpa-search/tpa-search.vue'; // Supporting Files -import baseMixin from '@/mixins/base-mixin'; +import { nextTick } from 'vue'; +import { shallowMount } from '@vue/test-utils'; import { getMountOptions } from '@/helpers/unit-test-helper.js'; -import { useMainStore } from '@/store'; -import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; +import { createTestingPinia } from '@pinia/testing'; +import navigationScenarios from '@/router/router-constants/navigation-scenarios.js'; import settleAllPromises from '@/helpers/layout-helper.js'; +import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; +import { useMainStore } from '@/store'; +import { toTitleCase, toDisplayPhoneNumber } from '@/helpers/text-helper.js'; +import bailoutMessage from '@/constants/bailoutMessage'; import globalRules from '@/constants/global-rules.js'; -import widgetFields from '@/constants/cms-widget-fields.js'; -import navigationScenarios from '@/router/router-constants/navigation-scenarios'; -// Mock fetchCmsContentForPage -jest.mock('@/helpers/cms-content-helper', () => ({ - fetchCmsContentForPage: jest.fn(), - doesCopyContainRouterLink: jest.fn() -})); - -// Mock our module for promises. jest.mock('@/helpers/layout-helper.js', () => jest.fn()); -function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRunAfterInitializingStore = () => {}) { +jest.mock('@/helpers/cms-content-helper', () => ({ + fetchCmsContentForPage: jest.fn(), + setupModalLinks: jest.fn(), + processIfStatements: jest.fn() +})); + +jest.mock('@/helpers/text-helper', () => ({ + toTitleCase: jest.fn((text) => text?.toLowerCase()?.replace(/(^|\s|-)\S/g, (letter) => letter.toUpperCase()) ?? ''), + toDisplayPhoneNumber: jest.fn((phone) => phone ?? '') +})); + +const mockMixin = { + methods: { + getCmsContent: jest.fn().mockImplementation(() => ''), + setCmsContent: jest.fn(), + onSubmit: jest.fn(), + onInvalidSubmit: jest.fn(), + navigateBack: jest.fn(), + savePageDataToStore: jest.fn() + } +}; + +const footerStub = { + render: () => {}, + methods: { + updateButtonText: jest.fn() + } +}; + +const loaderStub = { + render: () => {}, + template: '
' +}; + +function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) { const mountOptions = getMountOptions({ router: { navigate: jest.fn() + }, + route: { + query: { issPage: 'tpa-search' } } }); + mountOptions.global.stubs = { + ...mountOptions.global.stubs, + siteFooter: footerStub, + siteHeader: true, + textboxQuestion: true, + dropdownQuestion: true, + buttonQuestion: true, + textLink: true, + alert: true, + googleMap: true, + loader: loaderStub + }; + const testingPinia = createTestingPinia({ initialState: { - main: mainInitialState + main: { + order: { + customer: { + address: { + zipCode: '43215' + } + }, + serviceLocation: { + provider: { + providerNumber: '' + } + } + }, + ...mainInitialState + } } }); useMainStore(testingPinia); - methodToRunAfterInitializingStore(); + methodToRun(); mountOptions.global.plugins = [testingPinia]; - mountOptions.data = () => (initialData); + mountOptions.mixins = [mockMixin]; + mountOptions.data = () => ({ + dataLoaded: true, + ...initialData + }); - const apiResponses = { cmsContent: {} }; - - settleAllPromises.mockImplementation(() => apiResponses); + const apiResponses = { + cmsContent: {}, + providers: [] + }; + const apiPromise = Promise.resolve(apiResponses); + settleAllPromises.mockImplementation(() => apiPromise); fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); const wrapper = shallowMount(tpaSearch, mountOptions); - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {}); - wrapper.vm.setCmsContent = jest.fn(); - wrapper.vm.$router.navigateWithSpinner = jest.fn(); - wrapper.vm.navigateBack = baseMixin.methods.navigateBack; - return { wrapper }; } -describe('TPA search page', () => { - test.skip('returns the initial data', () => { - // Arrange - const zipCode = '12663'; - const mainInitialState = { - order: { - customer: { - address: { zipCode } - } - } - }; - const { wrapper } = getMountedComponent(mainInitialState); - - // Assert - expect(wrapper.vm.$data).toMatchSnapshot(); +describe('tpa-search.vue', () => { + beforeEach(() => { + jest.clearAllMocks(); }); - describe('should render', () => { - test('site header', () => { + + describe('Rendering', () => { + test('Should render site header', () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const expectedWidgetName = 'SiteHeaderWidget'; + const { wrapper } = getMountedComponent(); // Act const siteHeader = wrapper.findComponent({ ref: 'siteHeader' }); // Assert - expect(siteHeader.exists()).toBeTruthy(); - expect(siteHeader.props().cmsWidgetName).toBe(expectedWidgetName); + expect(siteHeader.exists()).toBe(true); }); - test('search providers form', () => { + + test('Should render site footer', () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - - // Act - const searchProvidersForm = wrapper.findComponent('#searchProvidersForm'); - - // Assert - expect(searchProvidersForm.exists()).toBeTruthy(); - }); - test('search question label', () => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - - // Act - const searchQuestionLabel = wrapper.find('#tpaSearchQuestionLabel'); - - // Assert - expect(searchQuestionLabel.exists()).toBeTruthy(); - expect(searchQuestionLabel.attributes().for).toBe('tpaSearchQuestionField'); - expect(searchQuestionLabel.classes()).toContain('text-center'); - expect(searchQuestionLabel.classes()).toContain('mt-5'); - expect(searchQuestionLabel.classes()).toContain('mb-0'); - expect(searchQuestionLabel.classes()).toContain('text-black'); - expect(searchQuestionLabel.classes()).toContain('w-100'); - expect(searchQuestionLabel.classes()).toContain('fs-5'); - }); - test('search instructions', () => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - - // Act - const searchInstructions = wrapper.find('#searchInstructions'); - - // Assert - expect(searchInstructions.exists()).toBeTruthy(); - expect(searchInstructions.attributes().for).toBe('tpaSearchQuestionField'); - expect(searchInstructions.classes()).toContain('text-center'); - expect(searchInstructions.classes()).toContain('small'); - expect(searchInstructions.classes()).toContain('darker-gray'); - expect(searchInstructions.classes()).toContain('w-100'); - expect(searchInstructions.classes()).toContain('mb-4'); - }); - test('search question field', () => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const expectedWidgetName = 'TPASearchQuestion'; - - // Act - const searchQuestionField = wrapper.findComponent('#tpaSearchQuestionField'); - - // Assert - expect(searchQuestionField.exists()).toBeTruthy(); - expect(searchQuestionField.props().inputId).toBe('tpaSearchQuestionFieldInput'); - expect(searchQuestionField.props().isRequired).toBeTruthy(); - expect(searchQuestionField.props().cmsWidgetName).toBe(expectedWidgetName); - expect(searchQuestionField.props().includeSearchIcon).toBeTruthy(); - expect(searchQuestionField.props().displayQuestionText).toBeFalsy(); - expect(searchQuestionField.props().validationRules) - .toBe(`${globalRules.ZIP_CODE_REQUIRED}|${globalRules.ZIP_CODE_SEARCH_FORMAT}`); - }); - test('provider selection form', () => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - - // Act - const providerSelectionForm = wrapper.findComponent('#providerSelectionForm'); - - // Assert - expect(providerSelectionForm.exists()).toBeTruthy(); - }); - test('map', async () => { - // Arrange - const zipCode = '12345'; const { wrapper } = getMountedComponent(); - await wrapper.setData({ - providers: [{ - address: { - streetAddress: '123 Lane Ave', - city: 'New York', - state: 'NY', - zipCode: '99230' - } - }], - mapZipCode: zipCode - }); - const expectedAddresses = ['123 Lane Ave, New York, NY 99230']; // Act - const map = wrapper.findComponent('#map'); + const siteFooter = wrapper.findComponent({ ref: 'siteFooter' }); // Assert - expect(map.exists()).toBeTruthy(); - expect(map.classes()).toContain('mb-4'); - expect(map.props().markers.map((p) => p.fullAddress)).toEqual(expectedAddresses); - expect(map.props().zipCode).toBe(zipCode); + expect(siteFooter.exists()).toBe(true); }); - test('search radius filter', async () => { + + test('Should render tpa search question label', () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const expectedWidgetName = 'FilterByQuestion'; + const { wrapper } = getMountedComponent(); // Act - const searchRadiusFilter = wrapper.findComponent('#searchRadiusFilter'); + const label = wrapper.find('#tpaSearchQuestionLabel'); // Assert - expect(searchRadiusFilter.exists()).toBeTruthy(); - expect(searchRadiusFilter.props().cmsWidgetName).toBe(expectedWidgetName); - expect(searchRadiusFilter.props().validationRules).toBe(globalRules.OPTION_REQUIRED); + expect(label.exists()).toBe(true); }); - test('providers loader when reloadingProviders is true', async () => { + + test('Should render search instructions label', () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ reloadingProviders: true }); + const { wrapper } = getMountedComponent(); // Act - const providersLoader = wrapper.findComponent('#providersLoader'); + const label = wrapper.find('#searchInstructions'); // Assert - expect(providersLoader.exists()).toBeTruthy(); + expect(label.exists()).toBe(true); }); - test('select provider question when reloadingProviders are false', async () => { + + test('Should render textbox question for TPA search', () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ - providers: [{ - companyName: 'Shop Auto Glass', - phoneNumber: '740-555-1234', - distanceInMiles: 1.2345, - providerNumber: '232209', - address: { - streetAddress: '123 Lane Ave', - city: 'New York', - state: 'NY', - zipCode: '99230' - } - }], + const { wrapper } = getMountedComponent(); + + // Act + const textbox = wrapper.find('#tpaSearchQuestionField'); + + // Assert + expect(textbox.exists()).toBe(true); + }); + + test('Should render dropdown for radius filter', () => { + // Arrange + const { wrapper } = getMountedComponent(); + + // Act + const dropdown = wrapper.find('#searchRadiusFilter'); + + // Assert + expect(dropdown.exists()).toBe(true); + }); + + test('Should render google map', () => { + // Arrange + const { wrapper } = getMountedComponent(); + + // Act + const map = wrapper.find('#map'); + + // Assert + expect(map.exists()).toBe(true); + }); + + test('Should show loader state when reloadingProviders is true', () => { + // Arrange + const { wrapper } = getMountedComponent({}, { reloadingProviders: true }); + + // Assert + expect(wrapper.vm.reloadingProviders).toBe(true); + }); + + test('Should show provider selection when reloadingProviders is false', () => { + // Arrange + const { wrapper } = getMountedComponent({}, { reloadingProviders: false }); + + // Assert + expect(wrapper.vm.reloadingProviders).toBe(false); + }); + + test('Should render no network shops alert when providers is empty', () => { + // Arrange + const { wrapper } = getMountedComponent({}, { providers: [] }); + + // Act + const alert = wrapper.find('#alertNoNetworkProviders'); + + // Assert + expect(alert.exists()).toBe(true); + }); + + test('Should render preferred shop not listed link', () => { + // Arrange + const { wrapper } = getMountedComponent({}, { reloadingProviders: false }); + + // Act + const link = wrapper.find('#preferredShopNotListedLink'); + + // Assert + expect(link.exists()).toBe(true); + }); + + test('Should have needHelpLinkText computed property', () => { + // Arrange + const { wrapper } = getMountedComponent({}, { reloadingProviders: false }); - const expectedAnswers = [{ - buttonLabel: 'Shop Auto Glass', - buttonLabelSubCopy: '1.2 mi', - buttonBodyCopy: '123 Lane Ave, New York, NY 99230
740-555-1234', - value: '232209' - }]; - const expectedAdditionalButtonData = { - displayAvailabilityIndicators: false - }; - // Act - const selectProviderQuestion = wrapper.findComponent('#selectProviderQuestion'); - - // Assert - expect(selectProviderQuestion.exists()).toBeTruthy(); - expect(selectProviderQuestion.props().buttonTypeString).toBe('shopListButton'); - expect(selectProviderQuestion.classes()).toContain('radioQuestion'); - expect(selectProviderQuestion.props().groupName).toBe('chooseShop'); - expect(selectProviderQuestion.props().textPosition).toBe('text-start'); - expect(selectProviderQuestion.props().isRequired).toBeTruthy(); - expect(selectProviderQuestion.props().validationRules).toBe(globalRules.OPTION_REQUIRED); - expect(selectProviderQuestion.props().answers).toEqual(expectedAnswers); - expect(selectProviderQuestion.props().additionalButtonData).toEqual(expectedAdditionalButtonData); - }); - test.each([[], null, undefined])( - 'no network providers alert when providers length is 0, undefined, or null and reloadingProviders is false', - async (providers) => { - // Arrange - const initialData = { - providers - }; - const wrapper = shallowMount(tpaSearch, getMountOptions({}, initialData)); - await wrapper.setData({ reloadingProviders: false }); - - // Act - const noNetworkProvidersAlert = wrapper.findComponent('#alertNoNetworkProviders'); - - // Assert - expect(noNetworkProvidersAlert.exists()).toBeTruthy(); - expect(noNetworkProvidersAlert.props().cmsWidgetName).toBeTruthy(); - expect(noNetworkProvidersAlert.props().alertClass).toBe('alert-warning'); - expect(noNetworkProvidersAlert.props().isDismissible).toBeFalsy(); - } - ); - test('preferred shop not listed link when reloadingProviders is false', async () => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ reloadingProviders: false }); - - // Act - const preferredShopNotListedLink = wrapper.findComponent('#preferredShopNotListedLink'); - - // Assert - expect(preferredShopNotListedLink.exists()).toBeTruthy(); - expect(preferredShopNotListedLink.props().linkType).toBe('navigation'); - // eslint-disable-next-line no-script-url - expect(preferredShopNotListedLink.props().href).toBe('javascript:void(0)'); - }); - test('site footer', () => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - - // Act - const footer = wrapper.findComponent({ ref: 'siteFooter' }); - - // Assert - expect(footer.exists()).toBeTruthy(); - expect(footer.props().cmsWidgetName).toBe('SiteFooterWidget'); + // Assert - needHelpLinkText computed exists and returns a value (empty string from mock) + expect(wrapper.vm.needHelpLinkText).toBeDefined(); }); }); - describe('should not render', () => { - test('select provider question when reloadingProviders is true', async () => { - // Arrange - const { wrapper } = getMountedComponent(); - await wrapper.setData({ reloadingProviders: true }); - // Act - const selectProviderQuestion = wrapper.findComponent('#selectProviderQuestion'); - - // Assert - expect(selectProviderQuestion.exists()).toBeFalsy(); - }); - describe('no network providers alert', () => { - test('when providers length is not 0', async () => { + describe('Computed', () => { + describe('radiusFilterOptions', () => { + test('returns empty object when no options', () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ providers: ['p1', 'p2'] }); + const { wrapper } = getMountedComponent(); // Act - const noNetworkProvidersAlert = wrapper.findComponent('#alertNoNetworkProviders'); + const result = wrapper.vm.radiusFilterOptions; // Assert - expect(noNetworkProvidersAlert.exists()).toBeFalsy(); + expect(result).toEqual({}); }); - test('when reloadingProviders is true', async () => { + + test('returns formatted options when CMS content exists', () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ providers: [], reloadingProviders: true }); + const mockOptions = [ + { Name: '10', Text: '10 miles' }, + { Name: '25', Text: '25 miles' }, + { Name: '50', Text: '50 miles' } + ]; + const mountOptions = getMountOptions({ router: { navigate: jest.fn() } }); + mountOptions.global.stubs = { siteFooter: footerStub, siteHeader: true, loader: loaderStub }; + mountOptions.mixins = [{ + methods: { + getCmsContent: jest.fn().mockImplementation(() => mockOptions), + setCmsContent: jest.fn(), + onSubmit: jest.fn(), + onInvalidSubmit: jest.fn(), + navigateBack: jest.fn(), + savePageDataToStore: jest.fn() + } + }]; + mountOptions.data = () => ({ dataLoaded: true }); + const wrapper = shallowMount(tpaSearch, mountOptions); // Act - const noNetworkProvidersAlert = wrapper.findComponent('#alertNoNetworkProviders'); + const result = wrapper.vm.radiusFilterOptions; // Assert - expect(noNetworkProvidersAlert.exists()).toBeFalsy(); + expect(result).toEqual({ + '10': '10 miles', + '25': '25 miles', + '50': '50 miles' + }); }); }); - test('preferred shop not listed link when reloadingProviders is true', async () => { - // Arrange - const { wrapper } = getMountedComponent(); - await wrapper.setData({ reloadingProviders: true }); - // Act - const preferredShopNotListedLink = wrapper.findComponent('#preferredShopNotListedLink'); - - // Assert - expect(preferredShopNotListedLink.exists()).toBeFalsy(); - }); - }); - describe('computed', () => { - describe('filterOptions', () => { - test.each([[], null, undefined])( - 'returns empty object when cms content is empty, null or undefined', - (cmsContent) => { - // Arrange - const mountOptions = getMountOptions(); - mountOptions.mixins = [{ - methods: { - getCmsContent: jest.fn().mockImplementation((widget, field) => - (widget === 'FilterByQuestion' && field === widgetFields.INPUT_QUESTION_WIDGET.ANSWERS - ? cmsContent - : '')) - } - }]; - const wrapper = shallowMount(tpaSearch, mountOptions); - - // Act - const result = wrapper.vm.filterOptions; - - // Assert - expect(result).toEqual({}); - } - ); - test('returns expected when cms content is some non empty iterable', () => { + describe('radiusFilteredAndCappedProviders', () => { + test('filters providers by radius and caps at 5', () => { // Arrange - const mountOptions = getMountOptions(); - const cmsContent = [{ Name: 'foo' }, { Name: 'bar' }]; - mountOptions.mixins = [{ - methods: { - getCmsContent: jest.fn().mockImplementation((widget, field) => - (widget === 'FilterByQuestion' && field === widgetFields.INPUT_QUESTION_WIDGET.ANSWERS - ? cmsContent - : '')) - } - }]; - const wrapper = shallowMount(tpaSearch, mountOptions); - const expected = { foo: 'foo', bar: 'bar' }; + const providers = [ + { providerNumber: '1', distanceInMiles: 5, companyName: 'Shop 1', address: {} }, + { providerNumber: '2', distanceInMiles: 15, companyName: 'Shop 2', address: {} }, + { providerNumber: '3', distanceInMiles: 20, companyName: 'Shop 3', address: {} }, + { providerNumber: '4', distanceInMiles: 30, companyName: 'Shop 4', address: {} }, + { providerNumber: '5', distanceInMiles: 35, companyName: 'Shop 5', address: {} }, + { providerNumber: '6', distanceInMiles: 40, companyName: 'Shop 6', address: {} }, + { providerNumber: '7', distanceInMiles: 45, companyName: 'Shop 7', address: {} } + ]; + const { wrapper } = getMountedComponent({}, { providers, radiusFilter: 50 }); // Act - const result = wrapper.vm.filterOptions; + const result = wrapper.vm.radiusFilteredAndCappedProviders; // Assert - expect(result).toEqual(expected); + expect(result.length).toBe(5); + }); + + test('filters out providers beyond radius', () => { + // Arrange + const providers = [ + { providerNumber: '1', distanceInMiles: 5, companyName: 'Shop 1', address: {} }, + { providerNumber: '2', distanceInMiles: 15, companyName: 'Shop 2', address: {} }, + { providerNumber: '3', distanceInMiles: 30, companyName: 'Shop 3', address: {} } + ]; + const { wrapper } = getMountedComponent({}, { providers, radiusFilter: 25 }); + + // Act + const result = wrapper.vm.radiusFilteredAndCappedProviders; + + // Assert + expect(result.length).toBe(2); + expect(result.map(p => p.providerNumber)).toEqual(['1', '2']); }); }); - test.each([null, undefined, '', 'non empty string'])( - 'tpaSearchQuestionLabel returns "%p" from getCmsContent', - (cmsContent) => { - // Arrange - const mountOptions = getMountOptions(); - mountOptions.mixins = [{ - methods: { - getCmsContent: jest.fn().mockImplementation((widget, field) => - (widget === 'TPASearchQuestion' && field === widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT - ? cmsContent - : '')) - } - }]; - const wrapper = shallowMount(tpaSearch, mountOptions); - // Act - const result = wrapper.vm.tpaSearchQuestionLabel; - - // Assert - expect(result).toEqual(cmsContent); - } - ); - test.each([null, undefined, '', 'non empty string'])( - 'searchInstructionsText returns "%p" from getCmsContent', - (cmsContent) => { - // Arrange - const mountOptions = getMountOptions(); - mountOptions.mixins = [{ - methods: { - getCmsContent: jest.fn().mockImplementation((widget, field) => - (widget === 'SearchInstructions' && field === widgetFields.TEXT_BLOCK_WIDGET.TEXT - ? cmsContent - : '')) - } - }]; - const wrapper = shallowMount(tpaSearch, mountOptions); - - // Act - const result = wrapper.vm.searchInstructionsText; - - // Assert - expect(result).toEqual(cmsContent); - } - ); - test.each([null, undefined, '', 'non empty string'])( - 'shopNotListedModalLink returns "%p" from getCmsContent', - (cmsContent) => { - // Arrange - const mountOptions = getMountOptions(); - mountOptions.mixins = [{ - methods: { - getCmsContent: jest.fn().mockImplementation((widget, field) => - (widget === 'ShopNotListedLink' && field === widgetFields.TEXT_BLOCK_WIDGET.TEXT - ? cmsContent - : '')) - } - }]; - const wrapper = shallowMount(tpaSearch, mountOptions); - - // Act - const result = wrapper.vm.shopNotListedModalLink; - - // Assert - expect(result).toEqual(cmsContent); - } - ); - describe('radiusInMiles', () => { - test.each([ - [15, '15 miles'], - [25, '25 miles'], - [50, '50 miles'], - [100, '100 miles'] - ])( - 'returns %p when filter is "%p"', - async (expectedRadius, filter) => { - // Arrange - const { wrapper } = getMountedComponent(); - await wrapper.setData({ filter }); - - // Act - const result = wrapper.vm.radiusInMiles; - - // Assert - expect(result).toEqual(expectedRadius); - } - ); - test.each([null, undefined, 'some random string'])( - 'returns 0 when filter is "%p"', - async (filter) => { - // Arrange - const { wrapper } = getMountedComponent(); - await wrapper.setData({ filter }); - - // Act - const result = wrapper.vm.radiusInMiles; - - // Assert - expect(result).toEqual(0); - } - ); - }); - describe('noNetworkShopsAlertHeaderText', () => { - test('returns exact cmsContent value when "{custom:radiusInMiles}" is not a substring', async () => { - // Arrange - const cmsContent = 'content returned from cms'; - const mountOptions = getMountOptions(); - mountOptions.mixins = [{ - methods: { - getCmsContent: jest.fn().mockImplementation((widget, field) => - (widget === 'NoNetworkShopsAlertWidget' && field === widgetFields.ALERT_WIDGET.HEADLINE_TEXT - ? cmsContent - : '')) - } - }]; - const wrapper = shallowMount(tpaSearch, mountOptions); - - // Act - const result = wrapper.vm.noNetworkShopsAlertHeaderText; - - // Assert - expect(result).toEqual(cmsContent); - }); - test('returns cmsContent value with all instances of "{custom:radiusInMiles}" replaced with radiusInMiles', () => { - // Arrange - const cmsContent = '{custom:radiusInMiles} ret{custom:radiusInMiles}urned {custom:radiusInMiles}cms{custom:radiusInMiles}'; - const mountOptions = getMountOptions(); - mountOptions.mixins = [{ - methods: { - getCmsContent: jest.fn().mockImplementation((widget, field) => - (widget === 'NoNetworkShopsAlertWidget' && field === widgetFields.ALERT_WIDGET.HEADLINE_TEXT - ? cmsContent - : '')) - } - }]; - const wrapper = shallowMount(tpaSearch, mountOptions); - const expected = '0 ret0urned 0cms0'; - - // Act - const result = wrapper.vm.noNetworkShopsAlertHeaderText; - - // Assert - expect(result).toEqual(expected); - }); - }); describe('providerAddresses', () => { - test.each([null, undefined, []])( - 'returns empty list when providers empty', - async (providers) => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ providers }); - - // Act - const result = wrapper.vm.providerAddresses; - - // Assert - expect(result).toEqual([]); - } - ); - test('returns expected when providers not empty', async () => { + test('maps providers to address objects for google map', () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ - providers: [ - { - companyName: 'Shop Auto Glass', - phoneNumber: '740-555-1234', - distanceInMiles: 1.2345, - providerNumber: '232209', - address: { - streetAddress: '123 Lane Ave', - city: 'New York', - state: 'NY', - zipCode: '99230' - } - }, - { - companyName: 'Safelite AutoGlass', - phoneNumber: '676-555-0099', - distanceInMiles: 123.9452, - providerNumber: '123456', - address: { - streetAddress: '2003 Coral Ln', - city: 'Birmingham', - state: 'AL', - zipCode: '43001' - } - } - ] - }); - const expectedResult = [ - '123 Lane Ave, New York, NY 99230', - '2003 Coral Ln, Birmingham, AL 43001' - ]; - - // Act - const result = wrapper.vm.providerAddresses.map((pa) => pa.fullAddress); - - // Assert - expect(result).toEqual(expectedResult); - }); - }); - describe('providerButtonData', () => { - test.each([null, undefined, []])( - 'returns empty list when providers equals "%p"', - async (providers) => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ providers }); - - // Act - const result = wrapper.vm.providerButtonData; - - // Assert - expect(result).toEqual([]); - } - ); - test('returns expected when providers not empty', async () => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ - providers: [ - { - companyName: 'Shop Auto Glass', - phoneNumber: '740-555-1234', - distanceInMiles: 1.2345, - providerNumber: '232209', - address: { - streetAddress: '123 Lane Ave', - city: 'New York', - state: 'NY', - zipCode: '99230' - } - }, - { - companyName: 'Safelite AutoGlass', - phoneNumber: '676-555-0099', - distanceInMiles: 123.9452, - providerNumber: '123456', - address: { - streetAddress: '2003 Coral Ln', - city: 'Birmingham', - state: 'AL', - zipCode: '43001' - } - } - ] - }); - const expectedResult = [ + const providers = [ { - buttonLabel: 'Shop Auto Glass', - buttonLabelSubCopy: '1.2 mi', - buttonBodyCopy: '123 Lane Ave, New York, NY 99230
740-555-1234', - value: '232209' - }, - { - buttonLabel: 'Safelite AutoGlass', - buttonLabelSubCopy: '123.9 mi', - buttonBodyCopy: '2003 Coral Ln, Birmingham, AL 43001
676-555-0099', - value: '123456' + providerNumber: '1', + companyName: 'TEST SHOP', + distanceInMiles: 5, + address: { + streetAddress: '123 MAIN ST', + city: 'COLUMBUS', + state: 'OH', + zipCode: '43215' + } } ]; + const { wrapper } = getMountedComponent({}, { providers, radiusFilter: 25 }); + + // Act + const result = wrapper.vm.providerAddresses; + + // Assert + expect(result.length).toBe(1); + expect(result[0].title).toBe('test shop'); + expect(result[0].addressLines.length).toBe(2); + }); + + test('returns empty array when no providers', () => { + // Arrange + const { wrapper } = getMountedComponent({}, { providers: [], radiusFilter: 25 }); + + // Act + const result = wrapper.vm.providerAddresses; + + // Assert + expect(result).toEqual([]); + }); + }); + + describe('providerButtonData', () => { + test('maps providers to button data format', () => { + // Arrange + const providers = [ + { + providerNumber: '12345', + companyName: 'AUTO GLASS SHOP', + distanceInMiles: 5.75, + phoneNumber: '6145551234', + address: { + streetAddress: '456 OAK AVE', + city: 'DUBLIN', + state: 'OH', + zipCode: '43017' + } + } + ]; + const { wrapper } = getMountedComponent({}, { providers, radiusFilter: 25 }); // Act const result = wrapper.vm.providerButtonData; // Assert - expect(result).toEqual(expectedResult); + expect(result.length).toBe(1); + expect(result[0].buttonLabel).toBe('auto glass shop'); + expect(result[0].buttonLabelSubCopy).toBe('5.8 mi'); + expect(result[0].value).toBe('12345'); }); }); + describe('selectedProviderIsSafeliteShop', () => { - test('no issue when provider in providers with no provider number', async () => { + test('returns true when selected provider is Safelite shop', () => { // Arrange - const selectedProviderNumber = '10099'; const providers = [ - { isSafeliteShop: false }, - { isSafeliteShop: true } + { providerNumber: '123', isSafeliteShop: true }, + { providerNumber: '456', isSafeliteShop: false } ]; - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ selectedProviderNumber, providers }); + const { wrapper } = getMountedComponent({}, { + providers, + selectedProviderNumber: '123', + radiusFilter: 25 + }); // Act const result = wrapper.vm.selectedProviderIsSafeliteShop; // Assert - expect(result).toEqual(false); + expect(result).toBe(true); }); - test.each([null, undefined, []])( - 'returns false when providers %s', - async (providers) => { - // Arrange - const selectedProviderNumber = '10099'; - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ selectedProviderNumber, providers }); - // Act - const result = wrapper.vm.selectedProviderIsSafeliteShop; - - // Assert - expect(result).toEqual(false); - } - ); - test.each([ - [true, true], - [false, false] - ])( - 'returns %p when first instance of multiple matching providerNumbers has isSafeliteShop value %p', - async (expectedOutput, isSafeliteShop) => { - // Arrange - const selectedProviderNumber = '29987'; - const providers = [ - { - providerNumber: selectedProviderNumber, - isSafeliteShop - }, - { - providerNumber: selectedProviderNumber, - isSafeliteShop: !isSafeliteShop - } - ]; - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ providers }); - await wrapper.setData({ selectedProviderNumber }); - - // Act - const result = wrapper.vm.selectedProviderIsSafeliteShop; - - // Assert - expect(result).toEqual(expectedOutput); - } - ); - test.each([[false, false], [true, true]])( - 'returns %p when providerNumber match has isSafeliteShop equal to %p', - async (expectedOutput, isSafeliteShop) => { - // Arrange - const selectedProviderNumber = '19274'; - const providers = [{ - providerNumber: selectedProviderNumber, - isSafeliteShop - }]; - const wrapper = shallowMount(tpaSearch, getMountOptions()); - await wrapper.setData({ selectedProviderNumber, providers }); - - // Act - const result = wrapper.vm.selectedProviderIsSafeliteShop; - - // Assert - expect(result).toEqual(expectedOutput); - } - ); - }); - }); - describe('watch', () => { - // TODO update/add to filter watch tests - test('on filter calls getTpaProviders and sets providers', async () => { - // Arrange - const zipCode = '78220'; - const initialMapZipCode = '88800'; - const { wrapper } = getMountedComponent({}, { - zipCode, - mapZipCode: initialMapZipCode, - dataLoaded: true - }); - const providers = { data: { shopProviders: ['some data', 'some more data'] } }; - useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers)); - const newFilter = 'new filter'; - - // Act - await wrapper.vm.$options.watch.filter.call(wrapper.vm, newFilter); - - // Assert - expect(useMainStore().getTpaProviders).toBeCalled(); - expect(wrapper.vm.providers).toEqual(providers.data.shopProviders); - expect(wrapper.vm.mapZipCode).toBe(zipCode); - }); - describe('on filter', () => { - test('calls getTpaProviders once and sets providers when dataLoaded is true', async () => { + test('returns false when selected provider is not Safelite shop', () => { // Arrange - const zipCode = '78220'; - const initialMapZipCode = '88800'; + const providers = [ + { providerNumber: '123', isSafeliteShop: true }, + { providerNumber: '456', isSafeliteShop: false } + ]; const { wrapper } = getMountedComponent({}, { - zipCode, - mapZipCode: initialMapZipCode, - dataLoaded: true + providers, + selectedProviderNumber: '456', + radiusFilter: 25 }); - const providers = { data: { shopProviders: ['some data', 'some more data'] } }; - useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers)); - const newFilter = 'new filter'; // Act - await wrapper.vm.$options.watch.filter.call(wrapper.vm, newFilter); + const result = wrapper.vm.selectedProviderIsSafeliteShop; // Assert - expect(useMainStore().getTpaProviders).toBeCalled(); - expect(wrapper.vm.providers).toEqual(providers.data.shopProviders); - expect(wrapper.vm.mapZipCode).toBe(zipCode); + expect(result).toBe(false); }); - test('never calls getTpaProviders and does not set providers when dataLoaded is false', async () => { + + test('returns false when no provider is selected', () => { // Arrange - const zipCode = '78220'; - const initialMapZipCode = '88800'; + const providers = [ + { providerNumber: '123', isSafeliteShop: true } + ]; const { wrapper } = getMountedComponent({}, { - zipCode, - mapZipCode: initialMapZipCode, - dataLoaded: false + providers, + selectedProviderNumber: '', + radiusFilter: 25 }); - const providers = { data: { shopProviders: ['some data', 'some more data'] } }; - useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers)); - const newFilter = 'new filter'; // Act - await wrapper.vm.$options.watch.filter.call(wrapper.vm, newFilter); + const result = wrapper.vm.selectedProviderIsSafeliteShop; // Assert - expect(useMainStore().getTpaProviders).not.toBeCalled(); - expect(wrapper.vm.providers).not.toEqual(providers.data.shopProviders); - expect(wrapper.vm.mapZipCode).toBe(initialMapZipCode); + expect(result).toBe(false); }); }); - // TODO confirm tests appropriate. Also should this method be async? - describe('on providers', () => { - test('does not update provider number when dataLoaded false', () => { + + describe('noNetworkShopsAlertHeaderText', () => { + test('replaces radius placeholder with actual value', () => { // Arrange - const initialValue = 1234; - const { wrapper } = getMountedComponent({}, { - dataLoaded: false, - selectedProviderNumber: initialValue - }); - const value = 'some other value'; - const newProviders = [{ providerNumber: value }]; - - // Act - wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders); - - // Assert - expect(wrapper.vm.selectedProviderNumber).toBe(initialValue); - }); - test.each([null, undefined, []])( - 'sets selected provider number to "" when there are no providers', - (newProviders) => { - // Arrange - const { wrapper } = getMountedComponent({}, { - dataLoaded: true, - selectedProviderNumber: null - }); - - // Act - wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders); - - // Assert - expect(wrapper.vm.selectedProviderNumber).toBe(''); - } - ); - test('sets selected provider number to value of provider when there is one provider', () => { - // Arrange - const { wrapper } = getMountedComponent({}, { - dataLoaded: true, - selectedProviderNumber: null - }); - const value = 'some value'; - const newProviders = [{ providerNumber: value }]; - - // Act - wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders); - - // Assert - expect(wrapper.vm.selectedProviderNumber).toBe(value); - }); - test('sets selected provider number to "" when there are more than one provider', () => { - // Arrange - const { wrapper } = getMountedComponent({}, { - dataLoaded: true, - selectedProviderNumber: null - }); - const newProviders = [{ value: 'val1' }, { value: 'val2' }]; - - // Act - wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders); - - // Assert - expect(wrapper.vm.selectedProviderNumber).toBe(''); - }); - }); - describe('selectedProviderNumber', () => { - describe('does not call updateServiceLocation when', () => { - test('dataLoaded is false', () => { - // Arrange - const providerNumber = 38447; - const { wrapper } = getMountedComponent({}, { - providers: [{ - providerNumber, - address: { - streetAddress: '143 Average Lane', - city: 'Cambridge', - state: 'OH', - zipCode: '72983', - zipCodeCtu: '0390' - }, - companyName: "Sally's Auto", - phoneNumber: '1234567890' - }], - dataLoaded: false - }); - - // Act - wrapper.vm.$options.watch.selectedProviderNumber.call(wrapper.vm, providerNumber); - - // Assert - expect(wrapper.vm.mainStore.updateServiceLocation).not.toHaveBeenCalled(); - }); - test('providers list is null ', () => { - // Arrange - const { wrapper } = getMountedComponent({}, { - providers: null, - dataLoaded: true - }); - const newProviderNumber = 11235; - - // Act - wrapper.vm.$options.watch.selectedProviderNumber.call(wrapper.vm, newProviderNumber); - - // Assert - expect(wrapper.vm.mainStore.updateServiceLocation).not.toHaveBeenCalled(); - }); - test('providers list is empty ', () => { - // Arrange - const { wrapper } = getMountedComponent({}, { - providers: [], - dataLoaded: true - }); - const newProviderNumber = 11235; - - // Act - wrapper.vm.$options.watch.selectedProviderNumber.call(wrapper.vm, newProviderNumber); - - // Assert - expect(wrapper.vm.mainStore.updateServiceLocation).not.toHaveBeenCalled(); - }); - test('providers list does not contain match for provider number ', () => { - // Arrange - const { wrapper } = getMountedComponent({}, { - providers: [{ - providerNumber: 8374, - address: { - streetAddress: '143 Average Lane', - city: 'Cambridge', - state: 'OH', - zipCode: '72983', - zipCodeCtu: '0390' - }, - companyName: "Sally's Auto", - phoneNumber: '1234567890' - }], - dataLoaded: true - }); - const newProviderNumber = 11235; - - // Act - wrapper.vm.$options.watch.selectedProviderNumber.call(wrapper.vm, newProviderNumber); - - // Assert - expect(wrapper.vm.mainStore.updateServiceLocation).not.toHaveBeenCalled(); - }); - }); - test('Calls updateServiceLocation when matching provider found.', () => { - // Arrange - const newProviderNumber = 11235; - const streetAddress = '143 Average Lane'; - const city = 'Berlin'; - const state = 'MA'; - const zipCode = '12345'; - const zipCodeCtu = '0004'; - const companyName = "Sally's Auto"; - const phoneNumber = '3298479879'; - const provider = { - providerNumber: newProviderNumber, - address: { - streetAddress, - city, - state, - zipCode, - zipCodeCtu - }, - companyName, - phoneNumber - }; - const filter = '10 miles'; - const serviceZipCode = 10098; - const { wrapper } = getMountedComponent({}, { - providers: [ - provider, - { providerNumber: 328949832 } - ], - filter, - zipCode: serviceZipCode, - dataLoaded: true - }); - - // Act - wrapper.vm.$options.watch.selectedProviderNumber.call(wrapper.vm, newProviderNumber); - - // Assert - expect(wrapper.vm.mainStore.updateServiceLocation).toHaveBeenCalledTimes(1); - expect(wrapper.vm.mainStore.updateServiceLocation).toHaveBeenCalledWith({ - searchFilter: filter, - zipCode: serviceZipCode, - provider: { - providerNumber: newProviderNumber, - address: { - streetAddress, - city, - state, - zipCode, - zipCodeCtu - }, - companyName, - phoneNumber + const mountOptions = getMountOptions({ router: { navigate: jest.fn() } }); + mountOptions.global.stubs = { siteFooter: footerStub, siteHeader: true, loader: loaderStub }; + mountOptions.mixins = [{ + methods: { + getCmsContent: jest.fn().mockImplementation(() => 'No shops within {custom:radiusInMiles}'), + setCmsContent: jest.fn(), + onSubmit: jest.fn(), + onInvalidSubmit: jest.fn(), + navigateBack: jest.fn(), + savePageDataToStore: jest.fn() } - }); + }]; + mountOptions.data = () => ({ dataLoaded: true, radiusFilter: 25 }); + const wrapper = shallowMount(tpaSearch, mountOptions); + + // Act + const result = wrapper.vm.noNetworkShopsAlertHeaderText; + + // Assert + expect(result).toBe('No shops within 25 miles'); }); }); }); - describe('method', () => { - test.each([ - ['address', 'city', 'ST', 'zip', 'Address, City, ST zip'], - ['address', null, 'ST', 'zip', 'Address, ST zip'], - ['address', 'city', null, 'zip', 'Address, City zip'], - ['address', 'city', 'ST', null, 'Address, City, ST'], - [null, null, null, null, ''] - ])( - 'getFullProviderAddress returns expected', - (streetAddress, city, state, zipCode, expected) => { + + describe('Methods', () => { + describe('getFullProviderAddress', () => { + test('returns combined address lines', () => { // Arrange const { wrapper } = getMountedComponent(); const provider = { address: { - streetAddress, - city, - state, - zipCode + streetAddress: '123 MAIN ST', + city: 'COLUMBUS', + state: 'OH', + zipCode: '43215' } }; @@ -1033,26 +505,20 @@ describe('TPA search page', () => { const result = wrapper.vm.getFullProviderAddress(provider); // Assert - expect(result).toEqual(expected); - } - ); - test.each([ - ['address', 'city', 'ST', 'zip', 'Address'], - ['address', null, 'ST', 'zip', 'Address'], - ['address', 'city', null, 'zip', 'Address'], - ['address', 'city', 'ST', null, 'Address'], - [null, null, null, null, ''] - ])( - 'getProviderAddress returns expected', - (streetAddress, city, state, zipCode, expected) => { + expect(result).toContain('123 Main St'); + expect(result).toContain('Columbus'); + expect(result).toContain('OH'); + expect(result).toContain('43215'); + }); + }); + + describe('getProviderAddress', () => { + test('returns title cased street address', () => { // Arrange const { wrapper } = getMountedComponent(); const provider = { address: { - streetAddress, - city, - state, - zipCode + streetAddress: 'UPPERCASE STREET' } }; @@ -1060,26 +526,31 @@ describe('TPA search page', () => { const result = wrapper.vm.getProviderAddress(provider); // Assert - expect(result).toEqual(expected); - } - ); - test.each([ - ['address', 'city', 'ST', 'zip', 'City, ST zip'], - ['address', null, 'ST', 'zip', 'ST zip'], - ['address', 'city', null, 'zip', 'City zip'], - ['address', 'city', 'ST', null, 'City, ST'], - [null, null, null, null, ''] - ])( - 'getProviderCityZipState returns expected', - (streetAddress, city, state, zipCode, expected) => { + expect(toTitleCase).toHaveBeenCalledWith('UPPERCASE STREET'); + }); + + test('handles null address', () => { + // Arrange + const { wrapper } = getMountedComponent(); + const provider = { address: null }; + + // Act + const result = wrapper.vm.getProviderAddress(provider); + + // Assert + expect(result).toBe(''); + }); + }); + + describe('getProviderCityZipState', () => { + test('returns formatted city, state, zip', () => { // Arrange const { wrapper } = getMountedComponent(); const provider = { address: { - streetAddress, - city, - state, - zipCode + city: 'COLUMBUS', + state: 'OH', + zipCode: '43215' } }; @@ -1087,132 +558,174 @@ describe('TPA search page', () => { const result = wrapper.vm.getProviderCityZipState(provider); // Assert - expect(result).toEqual(expected); - } - ); - describe('getProviderButtonData', () => { - test.each([null, undefined, {}])( - 'returns empty list when getTpaProviders returns no data', - async (newProviders) => { - // Arrange - const { wrapper } = getMountedComponent(); - useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders)); + expect(result).toContain('OH'); + expect(result).toContain('43215'); + }); - // Act - const result = await wrapper.vm.getProviderButtonData(); - - // Assert - expect(result).toEqual([]); - } - ); - test('returns expected when getTpaProviders returns data', async () => { + test('handles missing city', () => { // Arrange const { wrapper } = getMountedComponent(); - const providers = { - data: { - shopProviders: [{ companyName: 'foo' }] + const provider = { + address: { + state: 'OH', + zipCode: '43215' } }; - useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers)); // Act - const result = await wrapper.vm.getProviderButtonData(); + const result = wrapper.vm.getProviderCityZipState(provider); // Assert - expect(result).toEqual(providers.data.shopProviders); + expect(result).toBe('OH 43215'); + }); + + test('handles missing state and zip', () => { + // Arrange + const { wrapper } = getMountedComponent(); + const provider = { + address: { + city: 'COLUMBUS' + } + }; + + // Act + const result = wrapper.vm.getProviderCityZipState(provider); + + // Assert + expect(result).toContain('Columbus'); }); }); - test('doNotSeeMyShopLinkClick invokes navigate method', () => { - // Arrange - const { wrapper } = getMountedComponent(); - // Act - wrapper.vm.doNotSeeMyShopLinkClick(); - - // Assert - expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); - }); - describe('searchClick', () => { - test.each([null, undefined, {}])( - 'sets providers to empty list when no data returned from getProviders', - async (newProviders) => { - // Arrange - const zipCode = '28331'; - const initialMapZipCode = '10022'; - const { wrapper } = getMountedComponent({}, { - zipCode, - mapZipCode: initialMapZipCode - }); - useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders)); - - // Act - await wrapper.vm.searchClick(); - - // Assert - expect(wrapper.vm.providers).toEqual([]); - expect(wrapper.vm.mapZipCode).toEqual(zipCode); - } - ); - test('returns value from getProviders', async () => { + describe('getShopButtonDataFromProvider', () => { + test('returns formatted button data with distance', () => { // Arrange - const zipCode = '12990'; - const initialMapZipCode = '00981'; - const { wrapper } = getMountedComponent({}, { - zipCode, - mapZipCode: initialMapZipCode - }); - const shopProviders = [{ companyName: 'foo' }]; - useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => ({ - data: { shopProviders } - })); + const { wrapper } = getMountedComponent(); + const provider = { + providerNumber: '12345', + companyName: 'TEST SHOP', + distanceInMiles: 5.789, + phoneNumber: '6145551234', + address: { + streetAddress: '123 MAIN ST', + city: 'COLUMBUS', + state: 'OH', + zipCode: '43215' + } + }; + + // Act + const result = wrapper.vm.getShopButtonDataFromProvider(provider); + + // Assert + expect(result.buttonLabel).toBe('test shop'); + expect(result.buttonLabelSubCopy).toBe('5.8 mi'); + expect(result.value).toBe('12345'); + expect(result.buttonBodyCopy).toContain('
'); + }); + + test('returns empty distance when distanceInMiles is null', () => { + // Arrange + const { wrapper } = getMountedComponent(); + const provider = { + providerNumber: '12345', + companyName: 'TEST SHOP', + distanceInMiles: null, + phoneNumber: '6145551234', + address: {} + }; + + // Act + const result = wrapper.vm.getShopButtonDataFromProvider(provider); + + // Assert + expect(result.buttonLabelSubCopy).toBe(''); + }); + + test('returns empty distance when distanceInMiles is NaN', () => { + // Arrange + const { wrapper } = getMountedComponent(); + const provider = { + providerNumber: '12345', + companyName: 'TEST SHOP', + distanceInMiles: NaN, + phoneNumber: '6145551234', + address: {} + }; + + // Act + const result = wrapper.vm.getShopButtonDataFromProvider(provider); + + // Assert + expect(result.buttonLabelSubCopy).toBe(''); + }); + }); + + describe('searchClick', () => { + test('searches by shop name when input is not a zip code', async () => { + // Arrange + const { wrapper } = getMountedComponent(); + wrapper.vm.tpaSearchValue = 'Auto Glass Shop'; + wrapper.vm.getProviderButtonData = jest.fn().mockResolvedValue([]); // Act await wrapper.vm.searchClick(); // Assert - expect(wrapper.vm.providers).toEqual(shopProviders); - expect(wrapper.vm.mapZipCode).toEqual(zipCode); + expect(wrapper.vm.getProviderButtonData).toHaveBeenCalledWith('Auto Glass Shop'); }); - }); - test('backButtonAction invokes navigate method', () => { - // Arrange - const { wrapper } = getMountedComponent(); - // Act - wrapper.vm.navigateBack(); - - // Assert - expect(wrapper.vm.$router.navigateWithSpinner).toHaveBeenCalled(); - }); - describe('forwardButtonAction', () => { - test('invokes navigate method with safelite shop scenario when selectedProviderIsSafeliteShop true', async () => { + test('updates mapZipCode when input is a zip code', async () => { // Arrange const { wrapper } = getMountedComponent(); - const providerNumber = '1109'; - await wrapper.setData({ - selectedProviderNumber: providerNumber, - providers: [{ - providerNumber, - isSafeliteShop: true - }] + wrapper.vm.tpaSearchValue = '43215'; + wrapper.vm.mapZipCode = '12345'; + wrapper.vm.getProviderButtonData = jest.fn().mockResolvedValue([]); + + // Act + await wrapper.vm.searchClick(); + + // Assert + expect(wrapper.vm.mapZipCode).toBe('43215'); + expect(wrapper.vm.getProviderButtonData).toHaveBeenCalledWith(); + }); + }); + + describe('forwardButtonAction', () => { + test('navigates with SAFELITE_SHOP scenario when provider is Safelite', () => { + // Arrange + const providers = [ + { providerNumber: '123', isSafeliteShop: true, distanceInMiles: 5, companyName: 'Safelite', address: {} } + ]; + const { wrapper } = getMountedComponent({}, { + providers, + selectedProviderNumber: '123', + radiusFilter: 25, + tpaSearchValue: 'test', + mapZipCode: '43215' }); // Act wrapper.vm.forwardButtonAction(); // Assert - expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP, undefined); + expect(wrapper.vm.mainStore.updateIsSafeliteProvider).toHaveBeenCalledWith(true); + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP, + expect.anything() + ); }); - test('invokes navigate method with non safelite shop scenario when selectedProviderIsSafeliteShop false', async () => { + + test('navigates with NON_SAFELITE_SHOP scenario when provider is not Safelite', () => { // Arrange - const { wrapper } = getMountedComponent(); - const providerNumber = '928'; - await wrapper.setData({ - selectedProviderNumber: providerNumber, - providers: [{ - providerNumber, - isSafeliteShop: false - }] + const providers = [ + { providerNumber: '456', isSafeliteShop: false, distanceInMiles: 5, companyName: 'Other Shop', address: {} } + ]; + const { wrapper } = getMountedComponent({}, { + providers, + selectedProviderNumber: '456', + radiusFilter: 25, + tpaSearchValue: 'test', + mapZipCode: '43215' }); // Act @@ -1221,421 +734,335 @@ describe('TPA search page', () => { // Assert expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP, - undefined + expect.anything() + ); + }); + + test('saves empty tpaSearchValue when it equals mapZipCode', () => { + // Arrange + const providers = [ + { providerNumber: '123', isSafeliteShop: false, distanceInMiles: 5, companyName: 'Test Shop', address: {} } + ]; + const { wrapper } = getMountedComponent({}, { + providers, + selectedProviderNumber: '123', + radiusFilter: 25, + tpaSearchValue: '43215', + mapZipCode: '43215' + }); + const savePageDataSpy = jest.spyOn(wrapper.vm, 'savePageDataToStore'); + + // Act + wrapper.vm.forwardButtonAction(); + + // Assert + expect(savePageDataSpy).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ tpaSearchValue: '' }) + ); + }); + + test('saves tpaSearchValue when different from mapZipCode', () => { + // Arrange + const providers = [ + { providerNumber: '123', isSafeliteShop: false, distanceInMiles: 5, companyName: 'Auto Shop', address: {} } + ]; + const { wrapper } = getMountedComponent({}, { + providers, + selectedProviderNumber: '123', + radiusFilter: 25, + tpaSearchValue: 'Auto Shop', + mapZipCode: '43215' + }); + const savePageDataSpy = jest.spyOn(wrapper.vm, 'savePageDataToStore'); + + // Act + wrapper.vm.forwardButtonAction(); + + // Assert + expect(savePageDataSpy).toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ tpaSearchValue: 'Auto Shop' }) ); }); }); - describe('getCustomValueFromString', () => { - test('returns radius in miles value if "radiusInMiles"', () => { + + describe('needHelpLinkClick', () => { + test('sets bailout and navigates', () => { // Arrange const { wrapper } = getMountedComponent(); - const str = 'radiusInMiles'; // Act - const result = wrapper.vm.getCustomValueFromString(str); + wrapper.vm.needHelpLinkClick(); // Assert - expect(result).toBe(0); - }); - test('returns radius in miles value if not "radiusInMiles"', () => { - // Arrange - const { wrapper } = getMountedComponent(); - const str = 'some other string'; - - // Act - const result = wrapper.vm.getCustomValueFromString(str); - - // Assert - expect(result).toBeNull(); + expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalled(); + expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( + navigationScenarios.CLICKED_NEED_HELP, + expect.anything() + ); }); }); - describe('getShopButtonDataFromProvider', () => { - test.each([null, undefined, {}])( - 'returns empty list given parameter with no provider', - (provider) => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const expectedResult = { - buttonLabel: '', - buttonLabelSubCopy: '', - buttonBodyCopy: '
', - value: '' - }; - // Act - const result = wrapper.vm.getShopButtonDataFromProvider(provider); - - // Assert - expect(result).toEqual(expectedResult); - } - ); - test('returns expected given provider', () => { + describe('getProviderButtonData', () => { + test('sets reloadingProviders to true then false', async () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const provider = { - companyName: 'Shop Auto Glass', - phoneNumber: '740-555-1234', - distanceInMiles: 1.2345, - providerNumber: '232209', - address: { - streetAddress: '123 Lane Ave', - city: 'New York', - state: 'NY', - zipCode: '99230' - } - }; - const expectedResult = { - buttonLabel: 'Shop Auto Glass', - buttonLabelSubCopy: '1.2 mi', - buttonBodyCopy: '123 Lane Ave, New York, NY 99230
740-555-1234', - value: '232209' - }; + const { wrapper } = getMountedComponent(); + wrapper.vm.mainStore.getTpaAndSafeliteProviders = jest.fn().mockResolvedValue({ data: [] }); // Act - const result = wrapper.vm.getShopButtonDataFromProvider(provider); + const promise = wrapper.vm.getProviderButtonData(); + expect(wrapper.vm.reloadingProviders).toBe(true); + await promise; // Assert - expect(result).toEqual(expectedResult); + expect(wrapper.vm.reloadingProviders).toBe(false); }); - describe('returns result with button label', () => { - test.each(['', undefined, null])( - 'equal to empty string when name is empty', - (companyName) => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const provider = { companyName }; - // Act - const result = wrapper.vm.getShopButtonDataFromProvider(provider); - - // Assert - expect(result.buttonLabel).toEqual(''); - } - ); - test('returns provider name when exists', () => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const companyName = 'Some Shop Name'; - const provider = { companyName }; - - // Act - const result = wrapper.vm.getShopButtonDataFromProvider(provider); - - // Assert - expect(result.buttonLabel).toEqual(companyName); - }); - }); - describe('returns result with button label sub copy', () => { - test.each([null, undefined, 'random string'])( - 'as empty string when distanceInMiles is not a number', - (distanceInMiles) => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const provider = { distanceInMiles }; - - // Act - const result = wrapper.vm.getShopButtonDataFromProvider(provider); - - // Assert - expect(result.buttonLabelSubCopy).toEqual(''); - } - ); - test.each([[0, 0], [0, 0.000], [-7.89, -7.9], [109, 109], [1.234, 1.2], [2.99, 3]])( - 'displays expected distance value', - (distanceInMiles, displayedDistance) => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const provider = { distanceInMiles }; - const expectedSubCopy = `${displayedDistance} mi`; - - // Act - const result = wrapper.vm.getShopButtonDataFromProvider(provider); - - // Assert - expect(result.buttonLabelSubCopy).toEqual(expectedSubCopy); - } - ); - }); - test('returns result with button body copy showing expected address and phone number', () => { + test('calls store method with zipCode and shopName', async () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const provider = { - phoneNumber: '123-555-0099', - address: { - streetAddress: '434 Up Down Blvd', - city: 'San Francisco', - state: 'CA', - zipCode: '90887' - } - }; - const expected = '434 Up Down Blvd, San Francisco, CA 90887
123-555-0099'; + const { wrapper } = getMountedComponent(); + wrapper.vm.mapZipCode = '43215'; + wrapper.vm.mainStore.getTpaAndSafeliteProviders = jest.fn().mockResolvedValue({ data: [] }); // Act - const result = wrapper.vm.getShopButtonDataFromProvider(provider); + await wrapper.vm.getProviderButtonData('Test Shop'); // Assert - expect(result.buttonBodyCopy).toEqual(expected); - }); - describe('returns result with value', () => { - test.each(['', undefined, null])( - 'equal to empty string when providerNumber is empty', - (providerNumber) => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const provider = { providerNumber }; - - // Act - const result = wrapper.vm.getShopButtonDataFromProvider(provider); - - // Assert - expect(result.value).toEqual(''); - } - ); - test('returns provider number when exists', () => { - // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); - const providerNumber = '123391'; - const provider = { providerNumber }; - - // Act - const result = wrapper.vm.getShopButtonDataFromProvider(provider); - - // Assert - expect(result.value).toEqual(providerNumber); - }); + expect(wrapper.vm.mainStore.getTpaAndSafeliteProviders).toHaveBeenCalledWith('43215', 'Test Shop'); }); }); }); - describe('before route enter', () => { - test('when service location zipCode set in store, it is used', async () => { - // Arrange - const { wrapper } = getMountedComponent(); - const customerZipCode = '18394'; - const serviceZipCode = '43982'; - useMainStore().order.customer.address.zipCode = customerZipCode; - useMainStore().order.serviceLocation.zipCode = serviceZipCode; - // Act - await tpaSearch.beforeRouteEnter.call( - wrapper.vm, - { query: { issPage: 'tpa-search' } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.$nextTick(); - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.zipCode).toBe(serviceZipCode); - }); - test('when service location zipCode not set in store, customer zipCode used', async () => { - // Arrange - const { wrapper } = getMountedComponent(); - const customerZipCode = '18394'; - useMainStore().order.customer.address.zipCode = customerZipCode; - useMainStore().order.serviceLocation.zipCode = null; - - // Act - await tpaSearch.beforeRouteEnter.call( - wrapper.vm, - { query: { issPage: 'tpa-search' } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.$nextTick(); - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.zipCode).toBe(customerZipCode); - }); - test('when search filter set in store, expected filter and provider number returned', async () => { - // Arrange - const { wrapper } = getMountedComponent(); - const customerZipCode = '18394'; - const searchFilter = '25 miles'; - const providerNumber = 749321; - useMainStore().order.customer.address.zipCode = customerZipCode; - useMainStore().order.serviceLocation.zipCode = null; - useMainStore().order.serviceLocation.searchFilter = searchFilter; - useMainStore().order.serviceLocation.provider.providerNumber = providerNumber; - const providers = [{ companyName: 'some provider' }]; - const expectedRadius = 25; - useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => - ({ data: { shopProviders: radius === expectedRadius ? providers : [] } })); - - // Act - await tpaSearch.beforeRouteEnter.call( - wrapper.vm, - { query: { issPage: 'tpa-search' } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.$nextTick(); - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.filter).toBe(searchFilter); - expect(wrapper.vm.providers).toEqual(providers); - expect(wrapper.vm.selectedProviderNumber).toBe(providerNumber); - expect(useMainStore().getTpaProviders).toBeCalledTimes(1); - expect(useMainStore().getTpaProviders).toBeCalledWith(customerZipCode, expectedRadius); - }); - test('when providers exist at 15 mile radius, filter is set to "15 miles" and providers set to expected', async () => { - // Arrange - const { wrapper } = getMountedComponent(); - const zipCode = '18394'; - useMainStore().order.customer.address.zipCode = '00001'; - useMainStore().order.serviceLocation.zipCode = zipCode; - useMainStore().order.serviceLocation.searchFilter = null; - const providers = [{ companyName: 'provider' }]; - useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => - ({ data: { shopProviders: radius === 15 ? providers : [] } })); - const expectedFilter = '15 miles'; - - // Act - await tpaSearch.beforeRouteEnter.call( - wrapper.vm, - { query: { issPage: 'tpa-search' } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.$nextTick(); - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.zipCode).toBe(zipCode); - expect(wrapper.vm.filter).toBe(expectedFilter); - expect(wrapper.vm.providers).toEqual(providers); - expect(wrapper.vm.selectedProviderNumber).toBeNull(); - expect(useMainStore().getTpaProviders).toBeCalledTimes(1); - }); - test('when providers exist at 25 mile radius, filter is set to "25 miles" and providers set to expected', async () => { - // Arrange - const { wrapper } = getMountedComponent(); - const zipCode = '18394'; - useMainStore().order.customer.address.zipCode = '00001'; - useMainStore().order.serviceLocation.zipCode = zipCode; - useMainStore().order.serviceLocation.searchFilter = null; - const providers = [{ companyName: 'provider' }]; - useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => - ({ data: { shopProviders: radius === 25 ? providers : [] } })); - const expectedFilter = '25 miles'; - - // Act - await tpaSearch.beforeRouteEnter.call( - wrapper.vm, - { query: { issPage: 'tpa-search' } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.$nextTick(); - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.zipCode).toBe(zipCode); - expect(wrapper.vm.filter).toBe(expectedFilter); - expect(wrapper.vm.providers).toEqual(providers); - expect(wrapper.vm.selectedProviderNumber).toBeNull(); - expect(useMainStore().getTpaProviders).toBeCalledTimes(2); - }); - test('when providers exist at 50 mile radius but not 25, filter is set to "50 miles" and providers set to expected', async () => { - // Arrange - const { wrapper } = getMountedComponent(); - const zipCode = '18394'; - useMainStore().order.customer.address.zipCode = '00001'; - useMainStore().order.serviceLocation.zipCode = zipCode; - useMainStore().order.serviceLocation.searchFilter = null; - const providers = [{ companyName: 'provider' }]; - useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => - ({ data: { shopProviders: radius === 50 ? providers : [] } })); - const expectedFilter = '50 miles'; - - // Act - await tpaSearch.beforeRouteEnter.call( - wrapper.vm, - { query: { issPage: 'tpa-search' } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.$nextTick(); - await wrapper.vm.$nextTick(); - - // Assert - expect(wrapper.vm.zipCode).toBe(zipCode); - expect(wrapper.vm.filter).toBe(expectedFilter); - expect(wrapper.vm.providers).toEqual(providers); - expect(wrapper.vm.selectedProviderNumber).toBeNull(); - expect(useMainStore().getTpaProviders).toBeCalledTimes(3); - }); - test( - 'when providers exist at 100 mile radius but not 50 or 25, filter is set to "100 miles" and providers set to expected', - async () => { + describe('Watchers', () => { + describe('providers watcher', () => { + test('auto-selects provider when only one provider exists', async () => { // Arrange - const { wrapper } = getMountedComponent(); - const zipCode = '18394'; - useMainStore().order.customer.address.zipCode = zipCode; - useMainStore().order.serviceLocation.zipCode = null; - useMainStore().order.serviceLocation.searchFilter = null; - const providers = [{ companyName: 'provider' }]; - useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => - ({ data: { shopProviders: radius === 100 ? providers : [] } })); - const expectedFilter = '100 miles'; + const { wrapper } = getMountedComponent({}, { dataLoaded: true }); + wrapper.vm.selectedProviderNumber = ''; // Act - await tpaSearch.beforeRouteEnter.call( - wrapper.vm, - { query: { issPage: 'tpa-search' } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.$nextTick(); - await wrapper.vm.$nextTick(); + wrapper.vm.providers = [{ providerNumber: '12345', distanceInMiles: 5, companyName: 'Test Shop', address: {} }]; + await nextTick(); // Assert - expect(wrapper.vm.zipCode).toBe(zipCode); - expect(wrapper.vm.filter).toBe(expectedFilter); - expect(wrapper.vm.providers).toEqual(providers); - expect(wrapper.vm.selectedProviderNumber).toBeNull(); - expect(useMainStore().getTpaProviders).toBeCalledTimes(4); - } - ); - test( - 'when no providers exist at 100, 50, or 25 mile radius, filter is set to "100 miles" and providers set to empty list', - async () => { + expect(wrapper.vm.selectedProviderNumber).toBe('12345'); + }); + + test('clears selection when multiple providers exist', async () => { // Arrange - const { wrapper } = getMountedComponent(); - const zipCode = '18394'; - useMainStore().order.customer.address.zipCode = '00001'; - useMainStore().order.serviceLocation.zipCode = zipCode; - useMainStore().order.serviceLocation.searchFilter = null; - useMainStore().getTpaProviders = jest.fn().mockImplementation(() => ([])); - const expectedFilter = '100 miles'; + const { wrapper } = getMountedComponent({}, { dataLoaded: true }); + wrapper.vm.selectedProviderNumber = '12345'; // Act - await tpaSearch.beforeRouteEnter.call( - wrapper.vm, - { query: { issPage: 'tpa-search' } }, - undefined, - (c) => c(wrapper.vm) - ); - - await wrapper.vm.$nextTick(); - await wrapper.vm.$nextTick(); + wrapper.vm.providers = [ + { providerNumber: '12345', distanceInMiles: 5, companyName: 'Shop 1', address: {} }, + { providerNumber: '67890', distanceInMiles: 10, companyName: 'Shop 2', address: {} } + ]; + await nextTick(); // Assert - expect(wrapper.vm.zipCode).toBe(zipCode); - expect(wrapper.vm.filter).toBe(expectedFilter); - expect(wrapper.vm.providers).toEqual([]); - expect(wrapper.vm.selectedProviderNumber).toBeNull(); - expect(useMainStore().getTpaProviders).toBeCalledTimes(4); - } - ); + expect(wrapper.vm.selectedProviderNumber).toBe(''); + }); + + test('does not auto-select when dataLoaded is false', async () => { + // Arrange + const { wrapper } = getMountedComponent({}, { dataLoaded: false }); + wrapper.vm.selectedProviderNumber = ''; + + // Act + wrapper.vm.providers = [{ providerNumber: '12345', distanceInMiles: 5, companyName: 'Test', address: {} }]; + await nextTick(); + + // Assert + expect(wrapper.vm.selectedProviderNumber).toBe(''); + }); + }); + + describe('selectedProviderNumber watcher', () => { + test('updates service location in store when provider is selected', async () => { + // Arrange + const providers = [ + { + providerNumber: '12345', + companyName: 'Test Shop', + phoneNumber: '6145551234', + address: { + streetAddress: '123 Main St', + city: 'Columbus', + state: 'OH', + zipCode: '43215', + zipCodeCtu: '01234' + }, + distanceInMiles: 5 + } + ]; + const { wrapper } = getMountedComponent({}, { dataLoaded: true, providers, radiusFilter: 25 }); + + // Act + wrapper.vm.selectedProviderNumber = '12345'; + await nextTick(); + + // Assert + expect(wrapper.vm.mainStore.updateServiceLocation).toHaveBeenCalledWith( + expect.objectContaining({ + provider: expect.objectContaining({ + providerNumber: '12345', + companyName: 'Test Shop' + }) + }) + ); + }); + + test('does not update store when provider not found', async () => { + // Arrange + const providers = [ + { providerNumber: '12345', distanceInMiles: 5, companyName: 'Test', address: {} } + ]; + const { wrapper } = getMountedComponent({}, { dataLoaded: true, providers, radiusFilter: 25 }); + wrapper.vm.mainStore.updateServiceLocation.mockClear(); + + // Act + wrapper.vm.selectedProviderNumber = '99999'; + await nextTick(); + + // Assert + expect(wrapper.vm.mainStore.updateServiceLocation).not.toHaveBeenCalled(); + }); + + test('does not update store when dataLoaded is false', async () => { + // Arrange + const providers = [ + { providerNumber: '12345', distanceInMiles: 5, companyName: 'Test', address: {} } + ]; + const { wrapper } = getMountedComponent({}, { dataLoaded: false, providers, radiusFilter: 25 }); + wrapper.vm.mainStore.updateServiceLocation.mockClear(); + + // Act + wrapper.vm.selectedProviderNumber = '12345'; + await nextTick(); + + // Assert + expect(wrapper.vm.mainStore.updateServiceLocation).not.toHaveBeenCalled(); + }); + }); + }); + + describe('beforeRouteEnter', () => { + test('fetches CMS content and providers on route enter', async () => { + // Arrange + const mockProviders = [ + { providerNumber: '123', distanceInMiles: 5 } + ]; + const { wrapper } = getMountedComponent(); + settleAllPromises.mockImplementation(() => ({ + cmsContent: {}, + providers: mockProviders + })); + + const to = { query: { issPage: 'tpa-search' } }; + const next = jest.fn((callback) => { + if (typeof callback === 'function') { + callback(wrapper.vm); + } + }); + + // Act + await tpaSearch.beforeRouteEnter(to, undefined, next); + + // Assert + expect(next).toHaveBeenCalled(); + }); + + test('restores page data from store if available', async () => { + // Arrange + const pageData = { + tpaSearchValue: 'Test Shop', + radiusFilter: 50 + }; + const { wrapper } = getMountedComponent(); + wrapper.vm.mainStore.pageData = jest.fn().mockReturnValue(pageData); + settleAllPromises.mockImplementation(() => ({ + cmsContent: {}, + providers: [] + })); + + const to = { query: { issPage: 'tpa-search' } }; + const next = jest.fn((callback) => { + if (typeof callback === 'function') { + callback(wrapper.vm); + } + }); + + // Act + await tpaSearch.beforeRouteEnter(to, undefined, next); + + // Assert + expect(next).toHaveBeenCalled(); + }); + }); + + describe('Data initialization', () => { + test('widget names are correctly defined', () => { + // Arrange + const { wrapper } = getMountedComponent(); + + // Assert + expect(wrapper.vm.widget.siteHeader).toBe('SiteHeaderWidget'); + expect(wrapper.vm.widget.tpaSearchQuestion).toBe('TPASearchQuestion'); + expect(wrapper.vm.widget.filterByQuestion).toBe('FilterByQuestion'); + expect(wrapper.vm.widget.noNetworkShopsAlert).toBe('NoNetworkShopsAlertWidget'); + expect(wrapper.vm.widget.siteFooter).toBe('SiteFooterWidget'); + }); + + test('validation rules are correctly defined', () => { + // Arrange + const { wrapper } = getMountedComponent(); + + // Assert + expect(wrapper.vm.rules.tpaSearch).toBe(globalRules.TPA_SEARCH_FORMAT); + expect(wrapper.vm.rules.filter).toBe(globalRules.OPTION_REQUIRED); + expect(wrapper.vm.rules.provider).toBe(globalRules.OPTION_REQUIRED); + }); + + test('additionalButtonData is correctly initialized', () => { + // Arrange + const { wrapper } = getMountedComponent(); + + // Assert + expect(wrapper.vm.additionalButtonData).toEqual({ + displayAvailabilityIndicators: false + }); + }); + }); + + describe('dataLoaded flag', () => { + test('dataLoaded can be initialized as false', () => { + // Arrange + const { wrapper } = getMountedComponent({}, { dataLoaded: false }); + + // Assert - verify initial state + expect(wrapper.vm.dataLoaded).toBe(false); + }); + + test('dataLoaded can be initialized as true', () => { + // Arrange + const { wrapper } = getMountedComponent({}, { dataLoaded: true }); + + // Assert + expect(wrapper.vm.dataLoaded).toBe(true); + }); + + test('dataLoaded affects provider watcher behavior', async () => { + // Arrange - with dataLoaded false, provider watcher should not auto-select + const { wrapper } = getMountedComponent({}, { dataLoaded: false, selectedProviderNumber: '' }); + + // Act - set providers (watcher should not auto-select because dataLoaded is false) + wrapper.vm.providers = [{ providerNumber: '12345', distanceInMiles: 5, companyName: 'Test', address: {} }]; + await nextTick(); + + // Assert - selectedProviderNumber should remain empty + expect(wrapper.vm.selectedProviderNumber).toBe(''); + }); }); }); diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index 83d211c1..13e9a767 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -15,26 +15,26 @@
@@ -52,10 +52,10 @@ @invalidSubmit="onInvalidSubmit"> @@ -73,7 +73,7 @@ id="selectProviderQuestion" v-model="selectedProviderNumber" buttonTypeString="shopListButton" - :buttonTypeObject="shopListButton" + :buttonTypeObject="tpaShopListButton" class="radioQuestion" :answers="providerButtonData" groupName="chooseShop" @@ -99,12 +99,23 @@ :text="shopNotListedModalLink" @clickEvent="doNotSeeMyShopLinkClick" />
+
+
@@ -123,7 +134,7 @@ import textLink from '@/ux-components/text-link/text-link.vue'; import alert from '@/ux-components/alert/alert.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue'; import googleMap from '@/iss-components/google-map/google-map.vue'; -import shopListButton from '@/iss-components/shop-list-button/shop-list-button.vue'; +import tpaShopListButton from '@/iss-components/tpa-shop-list-button/tpa-shop-list-button.vue'; import loader from '@/ux-components/loader/loader.vue'; // Supporting files @@ -135,53 +146,9 @@ import widgetFields from '@/constants/cms-widget-fields.js'; import { shallowRef } from 'vue'; import { toTitleCase, toDisplayPhoneNumber } from '@/helpers/text-helper.js'; import bailoutMessage from '@/constants/bailoutMessage'; +import settleAllPromises from '@/helpers/layout-helper'; +import issPageValues from '@/router/router-constants/issPage-values'; -const radiusFilterPairs = [ - { radius: 15, filter: '15 miles' }, - { radius: 25, filter: '25 miles' }, - { radius: 50, filter: '50 miles' }, - { radius: 100, filter: '100 miles' } -]; - -function convertRadiusFilterToInteger(filter) { - const pair = radiusFilterPairs.find((p) => p.filter === filter); - return pair?.radius ?? 0; -} - -async function getInitialSearchData() { - const customerZipCode = useMainStore().order.customer.address.zipCode; - const serviceLocationZipCode = useMainStore().order.serviceLocation.zipCode; - const zipCode = serviceLocationZipCode ?? customerZipCode; - - const { searchFilter } = useMainStore().order.serviceLocation; - let { providerNumber } = useMainStore().order.serviceLocation.provider; - - let providers = []; - let radius = 0; - if (searchFilter) { - radius = convertRadiusFilterToInteger(searchFilter); - const result = await useMainStore().getTpaProviders(zipCode, radius); - providers = result?.data?.shopProviders ?? []; - - return { zipCode, filter: searchFilter, providers, providerNumber }; - } - - providerNumber = null; - let pairIndex = 0; - let filter = ''; - while (providers.length === 0 && pairIndex < radiusFilterPairs.length) { - const pair = radiusFilterPairs[pairIndex]; - radius = pair.radius; - filter = pair.filter; - // eslint-disable-next-line no-await-in-loop - const result = await useMainStore().getTpaProviders(zipCode, radius); - providers = result?.data?.shopProviders ?? []; - - pairIndex += 1; - } - - return { zipCode, filter, providers, providerNumber }; -} export default { name: 'tpa-search', @@ -200,23 +167,40 @@ export default { }, mixins: [BaseFormMixin], async beforeRouteEnter(to, from, next) { - const { zipCode, filter, providers, providerNumber } = - await getInitialSearchData(); - const cmsContent = await fetchCmsContentForPage(to.query.issPage); - + const zipCode = useMainStore().order.customer.address.zipCode; + const pageData = useMainStore().pageData(to.query.issPage); + const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); + const providersPromise = useMainStore().getTpaAndSafeliteProviders(zipCode, pageData?.tpaSearchValue ?? ''); + const promiseResultMap = [ + { + resultKey: 'cmsContent', + promise: cmsContentPromise + }, + { + resultKey: 'providers', + promise: providersPromise + } + ]; + const resultMap = await settleAllPromises(promiseResultMap); next(async (vm) => { - vm.setCmsContent(cmsContent); - vm.setInitialSearchData(zipCode, filter, providers, providerNumber); + vm.setCmsContent(resultMap.cmsContent); + vm.mapZipCode = zipCode; + vm.providers = resultMap.providers; + if (pageData) { + vm.tpaSearchValue = pageData?.tpaSearchValue ?? vm.mapZipCode; + vm.radiusFilter = pageData?.radiusFilter ?? 25; + } }); }, data() { return { dataLoaded: false, - zipCode: null, + pageData: null, + tpaSearchValue: null, mapZipCode: null, - filter: '', + radiusFilter: null, providers: [], - selectedProviderNumber: '', + selectedProviderNumber: useMainStore().order.serviceLocation.provider.providerNumber, reloadingProviders: false, additionalButtonData: { displayAvailabilityIndicators: false @@ -228,28 +212,28 @@ export default { filterByQuestion: 'FilterByQuestion', noNetworkShopsAlert: 'NoNetworkShopsAlertWidget', shopNotListedLink: 'ShopNotListedLink', - siteFooter: 'SiteFooterWidget' + siteFooter: 'SiteFooterWidget', + needHelpLink: 'NeedHelpLink' }, rules: { - zipCode: `${globalRules.ZIP_CODE_REQUIRED}|${globalRules.ZIP_CODE_SEARCH_FORMAT}`, + tpaSearch: globalRules.TPA_SEARCH_FORMAT, filter: globalRules.OPTION_REQUIRED, // TODO do we even need this? provider: globalRules.OPTION_REQUIRED }, - shopListButton: shallowRef(shopListButton) + tpaShopListButton: shallowRef(tpaShopListButton) }; }, computed: { - filterOptions() { - const filterByAnswers = - this.getCmsContent( + radiusFilterOptions() { + const radiusFilterOptions = this.getCmsContent( this.widget.filterByQuestion, widgetFields.INPUT_QUESTION_WIDGET.ANSWERS ) ?? []; - const filterByAnswersObj = {}; - [...filterByAnswers].forEach((answer) => { - filterByAnswersObj[answer.Name] = answer.Name; - }); - return filterByAnswersObj; + const options = {}; + for (const option of radiusFilterOptions) { + options[option.Name] = option.Text; + } + return options; }, tpaSearchQuestionLabel() { return this.getCmsContent( @@ -257,6 +241,9 @@ export default { widgetFields.INPUT_QUESTION_WIDGET.QUESTION_TEXT ); }, + needHelpLinkText() { + return this.getCmsContent(this.widget.needHelpLink, widgetFields.TEXT_BLOCK_WIDGET.TEXT); + }, searchInstructionsText() { return this.getCmsContent( this.widget.searchInstructions, @@ -269,25 +256,25 @@ export default { widgetFields.TEXT_BLOCK_WIDGET.TEXT ); }, - radiusInMiles() { - return convertRadiusFilterToInteger(this.filter); - }, noNetworkShopsAlertHeaderText() { return this.getCmsContent( this.widget.noNetworkShopsAlert, widgetFields.ALERT_WIDGET.HEADLINE_TEXT - )?.replaceAll('{custom:radiusInMiles}', this.radiusInMiles); + )?.replaceAll('{custom:radiusInMiles}', this.radiusFilter + " miles"); + }, + radiusFilteredAndCappedProviders() { + return this.providers.filter((provider) => provider.distanceInMiles <= this.radiusFilter).slice(0, 5); }, providerAddresses() { - return this.providers?.map((provider) => ({ - title: provider.companyName, + return this.radiusFilteredAndCappedProviders?.map((provider) => ({ + title: provider.companyName.toLowerCase(), fullAddress: this.getFullProviderAddress(provider), addressLines: [this.getProviderAddress(provider), this.getProviderCityZipState(provider)] })) ?? []; }, providerButtonData() { return ( - this.providers?.map((provider) => + this.radiusFilteredAndCappedProviders?.map((provider) => this.getShopButtonDataFromProvider(provider)) ?? [] ); }, @@ -298,14 +285,6 @@ export default { } }, watch: { - async filter() { - if (this.dataLoaded) { - await this.getProviderButtonData().then((providers) => { - this.providers = providers; - }); - this.mapZipCode = this.zipCode; - } - }, providers(newProviders) { if (this.dataLoaded) { this.selectedProviderNumber = @@ -318,7 +297,6 @@ export default { const provider = this.providers?.find((p) => p.providerNumber === newNumber); if (provider && this.dataLoaded) { useMainStore().updateServiceLocation({ - searchFilter: this.filter, zipCode: this.zipCode, provider: { providerNumber: provider?.providerNumber, @@ -342,13 +320,6 @@ export default { } }, methods: { - setInitialSearchData(zipCode, filter, providers, providerNumber) { - this.zipCode = zipCode; - this.mapZipCode = zipCode; - this.filter = filter; - this.providers = providers; - this.selectedProviderNumber = providerNumber; - }, getFullProviderAddress(provider) { const addressLine1 = this.getProviderAddress(provider); const addressLine2 = this.getProviderCityZipState(provider); @@ -379,16 +350,16 @@ export default { } return addressLine2; }, - async getProviderButtonData() { + async getProviderButtonData(shopName = "") { this.reloadingProviders = true; - const getTpaProvidersResult = await useMainStore().getTpaProviders( - this.zipCode, - this.radiusInMiles + const getTpaProvidersResult = await useMainStore().getTpaAndSafeliteProviders( + this.mapZipCode, + shopName ); this.reloadingProviders = false; - return getTpaProvidersResult?.data?.shopProviders ?? []; + return getTpaProvidersResult?.data ?? []; }, - doNotSeeMyShopLinkClick() { + needHelpLinkClick() { this.mainStore.setBailout(bailoutMessage.RequestCallback()); this.$router.navigate( this.navigationScenarios.CLICKED_NEED_HELP, @@ -396,8 +367,12 @@ export default { ); }, async searchClick() { - this.providers = await this.getProviderButtonData(); - this.mapZipCode = this.zipCode; + if (isNaN(this.tpaSearchValue)) { + this.providers = await this.getProviderButtonData(this.tpaSearchValue); + } else { + this.mapZipCode = this.tpaSearchValue; + this.providers = await this.getProviderButtonData(); + } }, forwardButtonAction() { if (this.selectedProviderIsSafeliteShop) { @@ -407,16 +382,12 @@ export default { ? this.navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE_SHOP : this.navigationScenarios .CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP; + // Do not save the zip code as the tpa search value, prevents an outdated zipcode from being used + // as the default search value if customer goes back and changes zip earlier in the flow + const tpaSearchValueToSave = this.tpaSearchValue === this.mapZipCode ? '' : this.tpaSearchValue; + this.savePageDataToStore(issPageValues.TPA_SEARCH, {tpaSearchValue: tpaSearchValueToSave, radiusFilter: this.radiusFilter}); this.$router.navigate(scenario, this.$route); }, - getCustomValueFromString(str) { - switch (str) { - case 'radiusInMiles': - return this.radiusInMiles; - default: - return null; - } - }, getShopButtonDataFromProvider(provider) { const cellNumber = toDisplayPhoneNumber(provider?.phoneNumber); const distance = @@ -426,7 +397,7 @@ export default { : null; return { - buttonLabel: provider?.companyName ?? '', + buttonLabel: provider?.companyName.toLowerCase() ?? '', buttonLabelSubCopy: distance === null ? '' : `${distance} mi`, buttonBodyCopy: `${this.getFullProviderAddress(provider)}
${ cellNumber ?? '' @@ -442,4 +413,30 @@ export default { .darker-gray { color: map-get($colors, 'darker-gray'); } + +#tpaSearchQuestionLabel { + margin-bottom: .625rem; +} + +#map { + height: 27.1875rem; +} + +#providerSelectionForm { + #alertNoNetworkProviders { + :deep(.alert) { + margin-bottom: 0px; + } + } +} + +hr { + margin: 1.375rem 0; +} + +.need-help-link-container { + margin-top:1.875rem; + margin-bottom: 2.1875rem; +} + diff --git a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.spec.js b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.spec.js index 41a935b8..fd7afa32 100644 --- a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.spec.js +++ b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.spec.js @@ -41,13 +41,14 @@ function setupMocks({ glassNameProp, glassLocationProp, colorAnswersProp, modelV }; - const wrapper = shallowMount(glassPartQuestion, mountOptions); - // Mock store const partsOrQuestions = pageData ?? { partsOrQuestions: [{ glassName: 'Stationary', glassLocation: 'Rear', parts: [] }] }; useMainStore().pageData = jest.fn(); useMainStore().pageData.mockReturnValue(partsOrQuestions); document.querySelector = jest.fn().mockReturnValue({ clicked: false, click: jest.fn() }); + + const wrapper = shallowMount(glassPartQuestion, mountOptions); + return { wrapper }; } @@ -62,36 +63,23 @@ describe('glass-part-question.vue', () => { window.console.log(wrapper.vm.featureListData['Green Tint'][0].Text); // Assert expect(Object.keys(wrapper.vm.featureListData).length).toBe(2); - expect(wrapper.vm.featureListData['Green Tint'][0].Text).toBe('heated glass, solar, 1 hole'); + expect(wrapper.vm.featureListData['Green Tint'][0].Text).toBe('Heated glass, Solar, 1 Hole'); expect(wrapper.vm.featureListData['Green Tint'][0].Name).toBe('DB12209GTYN'); - expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Text).toBe('heated glass, solar, 1 hole'); + expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Text).toBe('Heated glass, Solar, 1 Hole'); expect(wrapper.vm.featureListData['Gray Tint Privacy'][0].Name).toBe('DB12209YPYN'); }); - test('Tint mapper, should get tint image by glassLocation and tintColor', async () => { + test('Tint mapper, should get return css class based on tint color', async () => { // Arrange const { wrapper } = setupMocks(featureListData); // Act await wrapper.vm.$nextTick(); - const tintSourceImage = wrapper.vm.getTintSourceImage('Rear', 'Green Tint'); + const tintClass = wrapper.vm.convertTintToCSSClass('Green Tint'); // Assert - expect(tintSourceImage).toBe('Glass-NoShade-GreenTint.svg'); - }); - - test('Tint mapper, should return empty string if no tint map found', async () => { - // Arrange - - const { wrapper } = setupMocks(featureListData); - - // Act - await wrapper.vm.$nextTick(); - const tintSourceImage = wrapper.vm.getTintSourceImage('Rear', 'Crazy Rainbow Tint'); - - // Assert - expect(tintSourceImage).toBe(''); + expect(tintClass).toBe('green-tint'); }); test('Should emit updateModelValue, and have correct attributes', async () => { @@ -122,7 +110,7 @@ describe('glass-part-question.vue', () => { { partNumber: 'DB12209GTYN', color: 'Green Tint' } ]); expect(listCard.attributes('groupname')).toBe('Rear-Stationary'); - expect(listCard.attributes('validationrules')).toBe('Rear-Stationary-tint-required'); + expect(listCard.attributes('validationrules')).toBe('Rear-Stationary-part-required'); }); test('default is selected if only one option', async () => { @@ -136,58 +124,42 @@ describe('glass-part-question.vue', () => { } ] }; + const updateSpy = jest.spyOn(glassPartQuestion.methods, 'updateSelectedPartNumber'); const { wrapper } = setupMocks(featureListData); - - // Act - await wrapper.vm.$nextTick(); - await wrapper.setData({ selectedTint: 'Green Tint' }); - // take emitted value, pass down as modelValue - // yes, yes, it's not ideal - await wrapper.setProps({ modelValue: wrapper.emitted()['update:modelValue'][0][0] }); await wrapper.vm.$nextTick(); // Assert - expect(wrapper.vm.selectedPartNumber).toBe('DB12209GTYN'); + expect(updateSpy).toHaveBeenCalledWith('DB12209GTYN'); }); - const partsForSelectedTintTestCases = [ + const partsForLocationTestCases = [ [ 'Rear', 'Stationary', - 'Green Tint', [ { partNumber: 'Glass1', color: 'Green Tint' }, + { partNumber: 'Glass2', color: 'Blue Tint' }, { partNumber: 'Glass3', color: 'Green Tint' }, { partNumber: 'Glass4', color: 'Green Tint' }, - { partNumber: 'Glass6', color: 'Green Tint' } + { partNumber: 'Glass5', color: 'Blue Tint' }, + { partNumber: 'Glass6', color: 'Green Tint' }, + { partNumber: 'Glass7', color: 'Red Tint' } ] ], - [ - 'Rear', - 'Stationary', - 'Blue Tint', - [ - { partNumber: 'Glass2', color: 'Blue Tint' }, - { partNumber: 'Glass5', color: 'Blue Tint' } - ] - ], - ['Rear', 'Stationary', 'Red Tint', [{ partNumber: 'Glass7', color: 'Red Tint' }]], [ 'Windshield', 'Single', - 'Green Tint', [ { partNumber: 'Windshield1', color: 'Green Tint' }, { partNumber: 'Windshield2', color: 'Green Tint' } ] ], - ['Windshield', 'Single', 'Blue Tint', []], - ['Driver', 'Quarter', 'Green Tint', []] + ['Driver', 'Quarter', []] ]; - test.each(partsForSelectedTintTestCases)( - 'partsForSelectedTint returns correct parts', - async (glassLocation, glassName, selectedTint, expectedResults) => { + test.each(partsForLocationTestCases)( + 'partsForLocation returns correct parts', + async (glassLocation, glassName, expectedResults) => { // Arrange const pageData = { partsOrQuestions: [ { @@ -219,11 +191,8 @@ describe('glass-part-question.vue', () => { pageData }); - // Act - await wrapper.setData({ selectedTint }); - // Assert - expect(expectedResults).toEqual(wrapper.vm.partsForSelectedTint); + expect(wrapper.vm.partsForLocation).toEqual(expectedResults); } ); }); diff --git a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue index 0fe2bec3..1d5c2115 100644 --- a/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue +++ b/src/layouts/vehicle-parts/glass-part-question/glass-part-question.vue @@ -1,44 +1,27 @@ diff --git a/src/layouts/vehicle-parts/vehicle-parts.vue b/src/layouts/vehicle-parts/vehicle-parts.vue index 5ceefd0d..4e75e55f 100644 --- a/src/layouts/vehicle-parts/vehicle-parts.vue +++ b/src/layouts/vehicle-parts/vehicle-parts.vue @@ -11,34 +11,36 @@
- -
- -
- +
+
+ +
+
@@ -51,7 +53,7 @@ import glassPartQuestion from '@/layouts/vehicle-parts/glass-part-question/glass import siteHeader from '@/iss-components/site-header/site-header.vue'; import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'; import siteFooter from '@/iss-components/site-footer/site-footer.vue'; -import alert from '@/ux-components/alert/alert.vue'; +import textLink from '@/ux-components/text-link/text-link.vue'; // Supporting Files import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; @@ -61,6 +63,7 @@ import { Form } from 'vee-validate'; import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; import BaseFormMixin from '@/mixins/base-form-mixin.js'; import { useMainStore } from '@/store'; +import widgetFields from '@/constants/cms-widget-fields'; export default { name: 'vehicle-parts', @@ -71,7 +74,7 @@ export default { siteHeader, siteSubHeader, siteFooter, - alert + textLink }, mixins: [BaseFormMixin, vehicleQuestionsMixin], async beforeRouteEnter(to, from, next) { @@ -88,17 +91,6 @@ export default { // Call the "next" function to complete the transition to this page. next((vm) => { vm.setCmsContent(resultMap.cmsContent); - - // Glass Part Question dynamic component - Object.keys(vm.$refs) - .filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined) - .forEach((c) => - vm.$refs[c][0].initializeComponent({ - ColorQuestionWidget: - resultMap.cmsContent.ColorQuestionWidget, - FeatureQuestionWidget: - resultMap.cmsContent.FeatureQuestionWidget - })); }); }, data() { @@ -156,6 +148,9 @@ export default { RefPrefix() { return 'partQuestion'; + }, + needHelpLinkText() { + return this.getCmsContent('NeedHelpLinkWidget', widgetFields.TEXT_BLOCK_WIDGET.TEXT); } }, mounted() { @@ -220,23 +215,47 @@ export default { }); }); }); + }, + requestCallbackBailout() { + this.mainStore.setBailout(bailoutMessage.RequestCallback()); + this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route); } } }; diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index 2698360f..c3075d3f 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -248,6 +248,10 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS, destinationIssPageValue: issPageValues.COVERAGE_STATEMENT + }, + { + scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, + destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, diff --git a/src/store/index.js b/src/store/index.js index 6759f16f..3b86e15a 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -142,7 +142,7 @@ export const getDefaultState = () => ({ appointmentType: null, isVehicleProtected: null, IsSafeliteProvider: null, - searchFilter: null, + tpaSearchRadius: null, provider: { providerNumber: null, address: { @@ -1038,8 +1038,24 @@ export const useMainStore = defineStore({ return this.getProviders(zipCode, radius, true) }, - getTpaProviders(zipCode, radius) { - return this.getProviders(zipCode, radius, false) + getTpaAndSafeliteProviders(zipCode, provderNameSearchString = "") { + const { parentAccountNumber } = this.order; + const carId = this.order.vehicle.carId; + const damageType = this.damage.isRepair ? 'Repair' : 'Install'; + + const parts = getLineItemsFlattened(this.order.lineItems.glassParts); + const partNumbers = parts.map((part) => part.partNumber).join(','); + + let url = `${endpoints.GetTpaAndSafeliteProviders.url}/${zipCode}?accountNumber=${parentAccountNumber}&carId=${carId}&damageType=${damageType}&partNumbers=${partNumbers}`; + if (provderNameSearchString) { + url += `&providerNameSearchString=${provderNameSearchString}`; + } + return new Promise((resolve, reject) => { + globalMethods.callHttpClient({ + method: endpoints.GetTpaAndSafeliteProviders.method, + endpoint: url + }).then((response) => resolve(response), (error) => reject(error)); + }); }, getProviders(zipcode, radius, safeliteOnly) { @@ -1844,7 +1860,7 @@ export const useMainStore = defineStore({ phoneNumber: serviceLocationInfo.provider?.phoneNumber }; } - this.order.serviceLocation.searchFilter = serviceLocationInfo.searchFilter; + this.order.serviceLocation.tpaSearchRadius = serviceLocationInfo.tpaSearchRadius; }, resetState() { diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 4f4173d8..f92fc95c 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -1966,53 +1966,65 @@ describe('Store', () => { }); }); - describe('getTpaProviders method', () => { + describe('getTpaAndSafeliteProviders method', () => { describe('successful method call', () => { - it.each([[true, 'Repair'], [false, 'Replace']])( - 'calls getProviders api endpoint', + it.each([[true, 'Repair'], [false, 'Install']])( + 'calls getProviders api endpoint with damageType %s', async (isRepair, damageType) => { // Arrange globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); const carId = getRandomString(10, 14); const parentAccountNumber = getRandomString(6, 6); + const partNumber = getRandomString(6, 6); store.order.parentAccountNumber = parentAccountNumber; store.order.damage.isRepair = isRepair; store.order.vehicle.carId = carId; - const zipCode = getRandomString(6, 6); - const radius = getRandomInt(5, 100); - const isBigTruck = false; - store.order.vehicle.isBigTruck = isBigTruck; - const expectedEndpoint = endpoints.GetProviders.url( - zipCode, - damageType, - radius, - parentAccountNumber, - 'false', - carId, - null, - isBigTruck - ); + store.order.lineItems.glassParts = [{ partNumber }]; + const zipCode = getRandomString(5, 5); + + const expectedEndpoint = `${endpoints.GetTpaAndSafeliteProviders.url}/${zipCode}?accountNumber=${parentAccountNumber}&carId=${carId}&damageType=${damageType}&partNumbers=${partNumber}`; // Act - await store.getTpaProviders(zipCode, radius); + await store.getTpaAndSafeliteProviders(zipCode); // Asserts expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining({ - method: endpoints.GetProviders.method, + method: endpoints.GetTpaAndSafeliteProviders.method, endpoint: expectedEndpoint })); } ); + it('includes providerNameSearchString in endpoint when provided', async () => { + // Arrange + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + const carId = getRandomString(10, 14); + const parentAccountNumber = getRandomString(6, 6); + const partNumber = getRandomString(6, 6); + store.order.parentAccountNumber = parentAccountNumber; + store.order.damage.isRepair = true; + store.order.vehicle.carId = carId; + store.order.lineItems.glassParts = [{ partNumber }]; + const zipCode = getRandomString(5, 5); + const providerNameSearchString = 'Test Shop'; + + // Act + await store.getTpaAndSafeliteProviders(zipCode, providerNameSearchString); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining({ + endpoint: expect.stringContaining(`&providerNameSearchString=${providerNameSearchString}`) + })); + }); it('Returns expected response object', async () => { // Arrange const response = { ReferralNumber: getRandomString(6, 6) }; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(response)); - const zipCode = getRandomString(6, 6); - const radius = getRandomInt(5, 100); + const zipCode = getRandomString(5, 5); // Act - const result = store.getTpaProviders(zipCode, radius); + const result = store.getTpaAndSafeliteProviders(zipCode); // Asserts await expect(result).resolves.toBe(response); @@ -2020,11 +2032,10 @@ describe('Store', () => { it('null response by api => no exceptions thrown', async () => { // Arrange globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(null)); - const zipCode = getRandomString(6, 6); - const radius = getRandomInt(5, 100); + const zipCode = getRandomString(5, 5); // Act - await store.getTpaProviders(zipCode, radius); + await store.getTpaAndSafeliteProviders(zipCode); }); it('no providers returned by api => promise resolves to empty list', async () => { // Arrange @@ -2034,11 +2045,10 @@ describe('Store', () => { } }; globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(responseNoProviders)); - const zipCode = getRandomString(6, 6); - const radius = getRandomInt(5, 100); + const zipCode = getRandomString(5, 5); // Act - const result = await store.getTpaProviders(zipCode, radius); + const result = await store.getTpaAndSafeliteProviders(zipCode); // Asserts expect(result.data.shopProviders).toHaveLength(0); @@ -2060,7 +2070,7 @@ describe('Store', () => { globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(responseProviders)); // Act - const result = await store.getTpaProviders(); + const result = await store.getTpaAndSafeliteProviders(); // Asserts expect(result.data.shopProviders).toHaveLength(2);