From d897fbbad16acc057f794312b2e085d029026fb7 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 13 Nov 2023 16:18:52 -0500 Subject: [PATCH 1/6] Finished incorporating map into tpa search page --- public/index.html | 7 ++ src/constants/endpoints.js | 2 +- .../address-questions/address-questions.vue | 2 +- .../google-map/google-map.spec.js | 0 src/iss-components/google-map/google-map.vue | 89 +++++++++++++++ .../shop-list-button/shop-list-button.vue | 2 +- src/layouts/tpa-search/tpa-search.spec.js | 4 +- src/layouts/tpa-search/tpa-search.vue | 102 +++++++++++------- src/store/index.js | 86 ++++++++++----- 9 files changed, 224 insertions(+), 70 deletions(-) create mode 100644 src/iss-components/google-map/google-map.spec.js create mode 100644 src/iss-components/google-map/google-map.vue diff --git a/public/index.html b/public/index.html index 7edb81cc..d70b03cd 100644 --- a/public/index.html +++ b/public/index.html @@ -5,6 +5,13 @@ window.dataLayer = [{}]; + + diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index fb036dc7..6a69b4e9 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -136,7 +136,7 @@ const endpoints = Object.freeze({ method: 'GET' }, GooglePlaces: { - url: 'https://maps.googleapis.com/maps/api/js?key={apiKey}&libraries=places' + url: (apiKey) => `https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places` }, ValidateClientTag: { url: '/clientauth/api/v1/clientauth/validate-client-tag', diff --git a/src/iss-components/address-questions/address-questions.vue b/src/iss-components/address-questions/address-questions.vue index 39144ad2..665a3772 100644 --- a/src/iss-components/address-questions/address-questions.vue +++ b/src/iss-components/address-questions/address-questions.vue @@ -196,7 +196,7 @@ export default { const addressField1 = document.getElementById('autocomplete'); const self = this; - const url = endpoints.GooglePlaces.url.replace('{apiKey}', applicationConfig.GOOGLE_PLACES_API_KEY); + const url = endpoints.GooglePlaces.url(applicationConfig.GOOGLE_PLACES_API_KEY); this.$loadScript(url) .then(() => { diff --git a/src/iss-components/google-map/google-map.spec.js b/src/iss-components/google-map/google-map.spec.js new file mode 100644 index 00000000..e69de29b diff --git a/src/iss-components/google-map/google-map.vue b/src/iss-components/google-map/google-map.vue new file mode 100644 index 00000000..76d8af52 --- /dev/null +++ b/src/iss-components/google-map/google-map.vue @@ -0,0 +1,89 @@ + + + + + diff --git a/src/iss-components/shop-list-button/shop-list-button.vue b/src/iss-components/shop-list-button/shop-list-button.vue index 5b1b154f..935d11fc 100644 --- a/src/iss-components/shop-list-button/shop-list-button.vue +++ b/src/iss-components/shop-list-button/shop-list-button.vue @@ -17,7 +17,7 @@ {{ buttonLabelSubCopy }}
{ useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders)); // Act - const result = await wrapper.vm.getProviders(); + const result = await wrapper.vm.getProviderButtonData(); // Assert expect(result).toEqual([]); @@ -525,7 +525,7 @@ describe('TPA search page', () => { useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers)); // Act - const result = await wrapper.vm.getProviders(); + const result = await wrapper.vm.getProviderButtonData(); // Assert expect(result).toEqual(providers.data); diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index 817c0223..4f457cd5 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -36,18 +36,17 @@
+
-

- Placeholder for Map -

-
{ + next(async (vm) => { vm.setCmsContent(resultMap.cmsContent); - vm.setFilter(radius); - vm.setProviders(providers); + await vm.setInitialFilterAndProviders(); }); }, data() { const { zipCode } = useMainStore().order.customer.address; return { zipCode, + mapZipCode: zipCode, filter: '', providers: [], selectedProviderNumber: '', @@ -242,24 +224,56 @@ export default { this.widget.noNetworkShopsAlert, widgetFields.ALERT_WIDGET.HEADLINE_TEXT )?.replaceAll('{custom:radiusInMiles}', this.radiusInMiles); + }, + providerAddresses() { + return this.providers?.map((provider) => this.getProviderAddress(provider)) ?? []; + }, + providerButtonData() { + return this.providers?.map((provider) => this.getShopButtonDataFromProvider(provider)) ?? []; } }, watch: { async filter() { // TODO right now this results in getProviders being called once more than it needs to be - this.providers = await this.getProviders(); + this.providers = await this.getProviderButtonData(); + this.mapZipCode = this.zipCode; }, providers(newProviders) { this.selectedProviderNumber = newProviders?.length === 1 ?? false - ? newProviders[0].value + ? newProviders[0].providerNumber : ''; } }, methods: { - async getProviders() { + getProviderAddress(provider) { + const addressLine2 = `${provider.address.city}, ${provider.address.state} ${provider.address.zipCode}`; + return `${provider.address.streetAddress}, ${addressLine2}`; + }, + async setInitialFilterAndProviders() { + const radiusOptions = [25, 50, 100]; + const { zipCode } = useMainStore().order.customer.address; + + const tpaProvidersRadius25 = await useMainStore().getTpaProviders(zipCode, radiusOptions[0]); + const tpaProvidersRadius50 = await useMainStore().getTpaProviders(zipCode, radiusOptions[1]); + const tpaProvidersRadius100 = await useMainStore().getTpaProviders(zipCode, radiusOptions[2]); + let radius = '25 miles'; + + let providers = tpaProvidersRadius25; + if ((tpaProvidersRadius25?.data?.shopProviders ?? []).length === 0) { + radius = '50 miles'; + providers = tpaProvidersRadius50; + if ((tpaProvidersRadius50?.data?.shopProviders ?? []).length === 0) { + radius = '100 miles'; + providers = tpaProvidersRadius100; + } + } + this.filter = radius; + this.providers = providers.data.shopProviders; + }, + async getProviderButtonData() { const getTpaProvidersResult = await useMainStore().getTpaProviders(this.zipCode, this.radiusInMiles); - return getTpaProvidersResult?.data ?? []; + return getTpaProvidersResult?.data?.shopProviders ?? []; }, doNotSeeMyShopLinkClick() { this.$router.navigate( @@ -268,7 +282,8 @@ export default { ); }, async searchClick() { - this.providers = await this.getProviders(); + this.providers = await this.getProviderButtonData(); + this.mapZipCode = this.zipCode; }, backButtonAction() { this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); @@ -287,11 +302,16 @@ export default { return null; } }, - setFilter(filter) { - this.filter = filter; - }, - setProviders(providers) { - this.providers = providers; + getShopButtonDataFromProvider(provider) { + const { phoneNumber } = provider; + const distance = +provider.distanceInMiles.toFixed(1); + + return { + buttonLabel: provider.name, + buttonLabelSubCopy: `${distance} mi`, + buttonBodyCopy: `${this.getProviderAddress(provider)}
${phoneNumber}`, + value: provider.providerNumber + }; } } }; diff --git a/src/store/index.js b/src/store/index.js index 3e5e8cd3..804c5f28 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -806,38 +806,76 @@ export const useMainStore = defineStore({ }); }, - // TODO what info is returned here + // TODO update with real endpoint getTpaProviders(zipCode, radius) { return new Promise((resolve, reject) => { if (radius === 25) { - resolve({}); + resolve({ + data: { + mobileProviderNumber: null, + shopProviders: null + } + }); } else if (radius === 50) { resolve({ - data: [ - { - buttonLabel: 'USA Auto Glass', - buttonLabelSubCopy: '1.5 mi', - buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085
614-123-5555', - value: '000123' - } - ] + data: { + mobileProviderNumber: null, + shopProviders: [ + { + name: 'USA Auto Glass', + address: { + city: 'WESTERVILLE', + country: 'US', + state: 'OH', + streetAddress: '4403 EXECUTIVE PKWY', + streetAddress2: '', + zipCode: '43081', + zipCodeCtu: '01820' + }, + distanceInMiles: 8.393453111956896, + providerNumber: '003335', + phoneNumber: '614-123-5555' + } + ] + } }); } else { resolve({ - data: [ - { - buttonLabel: 'USA Auto Glass', - buttonLabelSubCopy: '1.5 mi', - buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085
614-123-5555', - value: '000123' - }, - { - buttonLabel: 'USA Auto Glass', - buttonLabelSubCopy: '1.5 mi', - buttonBodyCopy: '760 Dearborn Park Ln, Worthington, OH 43085
614-123-5555', - value: '000123' - } - ] + data: { + mobileProviderNumber: null, + shopProviders: [ + { + name: 'USA Auto Glass', + address: { + city: 'WESTERVILLE', + country: 'US', + state: 'OH', + streetAddress: '4403 EXECUTIVE PKWY', + streetAddress2: '', + zipCode: '43081', + zipCodeCtu: '01820' + }, + distanceInMiles: 8.393453111956896, + providerNumber: '003335', + phoneNumber: '614-123-5555' + }, + { + name: 'Safelite AutoGlass', + address: { + city: 'WORTHINGTON', + country: 'US', + state: 'OH', + streetAddress: '760 DEARBORN PARK LN', + streetAddress2: '', + zipCode: '43085', + zipCodeCtu: '01820' + }, + distanceInMiles: 8.704336770196678, + providerNumber: '001820', + phoneNumber: '740-555-1234' + } + ] + } }); } }); From 9d0f1a028abb3086f83bd54a680fcb8eb80a29a4 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Mon, 13 Nov 2023 16:21:16 -0500 Subject: [PATCH 2/6] fixing class test --- src/iss-components/shop-list-button/shop-list-button.spec.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/iss-components/shop-list-button/shop-list-button.spec.js b/src/iss-components/shop-list-button/shop-list-button.spec.js index f94e0585..0d29c552 100644 --- a/src/iss-components/shop-list-button/shop-list-button.spec.js +++ b/src/iss-components/shop-list-button/shop-list-button.spec.js @@ -152,7 +152,7 @@ describe('Shop list button', () => { expect(buttonLabelSubCopy.classes().length).toBe(4); expect(buttonLabelSubCopy.classes()).toContain('m-0'); expect(buttonLabelSubCopy.classes()).toContain('caption'); - expect(buttonLabelSubCopy.classes()).toContain('ms-1'); + expect(buttonLabelSubCopy.classes()).toContain('ms-2'); expect(buttonLabelSubCopy.classes()).toContain(textPosition); }); test('when "textPosition" prop not provided', async () => { @@ -176,7 +176,7 @@ describe('Shop list button', () => { expect(buttonLabelSubCopy.classes().length).toBe(3); expect(buttonLabelSubCopy.classes()).toContain('m-0'); expect(buttonLabelSubCopy.classes()).toContain('caption'); - expect(buttonLabelSubCopy.classes()).toContain('ms-1'); + expect(buttonLabelSubCopy.classes()).toContain('ms-2'); }); }); describe('availability indicator block with expected when displayAvailabilityIndicators true', () => { From 6005424f9ad3bc0d1ce06c8aed9d37dda27ad70f Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Tue, 14 Nov 2023 11:48:14 -0500 Subject: [PATCH 3/6] Fixing existing tests --- .../__snapshots__/google-map.spec.js.snap | 8 ++ .../google-map/google-map.spec.js | 27 +++++++ .../__snapshots__/tpa-search.spec.js.snap | 1 + src/layouts/tpa-search/tpa-search.spec.js | 79 +++++++++---------- src/layouts/tpa-search/tpa-search.vue | 40 ++++++++-- 5 files changed, 106 insertions(+), 49 deletions(-) create mode 100644 src/iss-components/google-map/__snapshots__/google-map.spec.js.snap diff --git a/src/iss-components/google-map/__snapshots__/google-map.spec.js.snap b/src/iss-components/google-map/__snapshots__/google-map.spec.js.snap new file mode 100644 index 00000000..b4ee066e --- /dev/null +++ b/src/iss-components/google-map/__snapshots__/google-map.spec.js.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Google Maps should render expected initial data 1`] = ` +Object { + "country": "USA", + "geocoder": null, +} +`; diff --git a/src/iss-components/google-map/google-map.spec.js b/src/iss-components/google-map/google-map.spec.js index e69de29b..fbaee73a 100644 --- a/src/iss-components/google-map/google-map.spec.js +++ b/src/iss-components/google-map/google-map.spec.js @@ -0,0 +1,27 @@ +import { shallowMount } from '@vue/test-utils'; +import googleMap from '@/iss-components/google-map/google-map.vue'; + +describe('Google Maps', () => { + describe('should render', () => { + test('expected initial data', async () => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.$data).toMatchSnapshot(); + }); + test('map', () => { + // Arrange + const mapReference = '#map'; + const wrapper = shallowMount(googleMap, {}); + // await wrapper.vm.$nextTick(); + + // Act + const map = wrapper.find(mapReference); + + // Assert + expect(map.exists()).toBeTruthy(); + }); + }); +}); diff --git a/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap b/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap index f0824765..b8e194a6 100644 --- a/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap +++ b/src/layouts/tpa-search/__snapshots__/tpa-search.spec.js.snap @@ -6,6 +6,7 @@ Object { "displayAvailabilityIndicators": false, }, "filter": "", + "mapZipCode": "12663", "providers": Array [], "rules": Object { "filter": "option-required", diff --git a/src/layouts/tpa-search/tpa-search.spec.js b/src/layouts/tpa-search/tpa-search.spec.js index cae0aaf1..5be0e59b 100644 --- a/src/layouts/tpa-search/tpa-search.spec.js +++ b/src/layouts/tpa-search/tpa-search.spec.js @@ -44,7 +44,7 @@ function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRu fetchCmsContentForPage.mockImplementation(() => Promise.resolve()); const wrapper = shallowMount(tpaSearch, mountOptions); - wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => cmsContent); + wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {}); wrapper.vm.setCmsContent = jest.fn(); return { wrapper }; } @@ -452,7 +452,7 @@ describe('TPA search page', () => { test('on filter calls getTpaProviders and sets providers', async () => { // Arrange const { wrapper } = getMountedComponent(); - const providers = { data: ['some data', 'some more data'] }; + const providers = { data: { shopProviders: ['some data', 'some more data']} }; useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers)); const newFilter = 'new filter'; @@ -461,7 +461,7 @@ describe('TPA search page', () => { // Assert expect(useMainStore().getTpaProviders).toBeCalled(); - expect(wrapper.vm.providers).toEqual(providers.data); + expect(wrapper.vm.providers).toEqual(providers.data.shopProviders); }); describe('on providers', () => { test.each([null, undefined, []])( @@ -481,7 +481,7 @@ describe('TPA search page', () => { // Arrange const { wrapper } = getMountedComponent(); const value = 'some value'; - const newProviders = [{ value }]; + const newProviders = [{ providerNumber: value }]; // Act wrapper.vm.$options.watch.providers.call(wrapper.vm, newProviders); @@ -503,7 +503,7 @@ describe('TPA search page', () => { }); }); describe('method', () => { - describe('getProviders', () => { + describe('getProviderButtonData', () => { test.each([null, undefined, {}])( 'returns empty list when getTpaProviders returns no data', async (newProviders) => { @@ -521,14 +521,18 @@ describe('TPA search page', () => { test('returns expected when getTpaProviders returns data', async () => { // Arrange const { wrapper } = getMountedComponent(); - const providers = { data: [{ name: 'foo' }] }; + const providers = { + data: { + shopProviders: [{ name: 'foo' }] + } + }; useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers)); // Act const result = await wrapper.vm.getProviderButtonData(); // Assert - expect(result).toEqual(providers.data); + expect(result).toEqual(providers.data.shopProviders); }); }); test('doNotSeeMyShopLinkClick invokes navigate method', () => { @@ -559,14 +563,16 @@ describe('TPA search page', () => { test('returns value from getProviders', async () => { // Arrange const { wrapper } = getMountedComponent(); - const newProviders = { data: [{ name: 'foo' }] }; - useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders)); + const shopProviders = [{ name: 'foo' }]; + useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => ({ + data: { shopProviders } + })); // Act await wrapper.vm.searchClick(); // Assert - expect(wrapper.vm.providers).toEqual(newProviders.data); + expect(wrapper.vm.providers).toEqual(shopProviders); }); }); test('backButtonAction invokes navigate method', () => { @@ -613,32 +619,6 @@ describe('TPA search page', () => { expect(result).toBeNull(); }); }); - test.each([undefined, null, 'some value', ''])( - 'setFilter sets value of filter', - (filter) => { - // Arrange - const { wrapper } = getMountedComponent(); - - // Act - wrapper.vm.setFilter(filter); - - // Assert - expect(wrapper.vm.filter).toEqual(filter); - } - ); - test.each([undefined, null, ['some value'], []])( - 'setProviders sets value of providers', - (providers) => { - // Arrange - const { wrapper } = getMountedComponent(); - - // Act - wrapper.vm.setProviders(providers); - - // Assert - expect(wrapper.vm.providers).toEqual(providers); - } - ); }); describe('before route enter', () => { test('when providers exist at 25 mile radius, filter is set to "25 miles" and providers set to expected', async () => { @@ -646,8 +626,9 @@ describe('TPA search page', () => { const { wrapper } = getMountedComponent(); const zipCode = '18394'; useMainStore().order.customer.address.zipCode = zipCode; - const providers = { data: [{ name: 'provider' }] }; - useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => (radius === 25 ? providers : [])); + const providers = [{ name: 'provider' }]; + useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => + ({ data: { shopProviders: radius === 25 ? providers : [] } })); const expectedFilter = '25 miles'; // Act @@ -658,6 +639,9 @@ describe('TPA search page', () => { (c) => c(wrapper.vm) ); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + // Assert expect(wrapper.vm.filter).toBe(expectedFilter); expect(wrapper.vm.providers).toEqual(providers); @@ -667,8 +651,9 @@ describe('TPA search page', () => { const { wrapper } = getMountedComponent(); const zipCode = '18394'; useMainStore().order.customer.address.zipCode = zipCode; - const providers = { data: [{ name: 'provider' }] }; - useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => (radius === 50 ? providers : [])); + const providers = [{ name: 'provider' }]; + useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => + ({ data: { shopProviders: radius === 50 ? providers : [] } })); const expectedFilter = '50 miles'; // Act @@ -679,6 +664,9 @@ describe('TPA search page', () => { (c) => c(wrapper.vm) ); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + // Assert expect(wrapper.vm.filter).toBe(expectedFilter); expect(wrapper.vm.providers).toEqual(providers); @@ -690,8 +678,9 @@ describe('TPA search page', () => { const { wrapper } = getMountedComponent(); const zipCode = '18394'; useMainStore().order.customer.address.zipCode = zipCode; - const providers = { data: [{ name: 'provider' }] }; - useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => (radius === 100 ? providers : [])); + const providers = [{ name: 'provider' }]; + useMainStore().getTpaProviders = jest.fn().mockImplementation((_, radius) => + ({ data: { shopProviders: radius === 100 ? providers : [] } })); const expectedFilter = '100 miles'; // Act @@ -702,6 +691,9 @@ describe('TPA search page', () => { (c) => c(wrapper.vm) ); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + // Assert expect(wrapper.vm.filter).toBe(expectedFilter); expect(wrapper.vm.providers).toEqual(providers); @@ -725,6 +717,9 @@ describe('TPA search page', () => { (c) => c(wrapper.vm) ); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + // Assert expect(wrapper.vm.filter).toBe(expectedFilter); expect(wrapper.vm.providers).toEqual([]); diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index 4f457cd5..a456d29f 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -247,8 +247,28 @@ export default { methods: { getProviderAddress(provider) { - const addressLine2 = `${provider.address.city}, ${provider.address.state} ${provider.address.zipCode}`; - return `${provider.address.streetAddress}, ${addressLine2}`; + const city = provider?.address?.city ?? ''; + const state = provider?.address?.state ?? ''; + const zipCode = provider?.address?.zipCode ?? ''; + let addressLine2 = ''; + if (city) { + addressLine2 = city; + if (state || zipCode) { + addressLine2 = ', '; + } + } + if (state) { + addressLine2 += `${state}`; + if (zipCode) { + addressLine2 += ' '; + } + } + if (zipCode) { + addressLine2 += `${zipCode}`; + } + return provider?.address?.streetAddress + ? `${provider.address.streetAddress}, ${addressLine2}` + : addressLine2; }, async setInitialFilterAndProviders() { const radiusOptions = [25, 50, 100]; @@ -268,8 +288,9 @@ export default { providers = tpaProvidersRadius100; } } + console.log(`radius: ${radius}`); this.filter = radius; - this.providers = providers.data.shopProviders; + this.providers = providers?.data?.shopProviders ?? []; }, async getProviderButtonData() { const getTpaProvidersResult = await useMainStore().getTpaProviders(this.zipCode, this.radiusInMiles); @@ -304,13 +325,18 @@ export default { }, getShopButtonDataFromProvider(provider) { const { phoneNumber } = provider; - const distance = +provider.distanceInMiles.toFixed(1); + + const distance = provider?.distanceInMiles + ? +provider.distanceInMiles.toFixed(1) + : null; return { - buttonLabel: provider.name, - buttonLabelSubCopy: `${distance} mi`, + buttonLabel: provider?.name ?? '', + buttonLabelSubCopy: distance == null + ? '' + : `${distance} mi`, buttonBodyCopy: `${this.getProviderAddress(provider)}
${phoneNumber}`, - value: provider.providerNumber + value: provider?.providerNumber ?? '' }; } } From 612a2f8b7bbef2a7656d56d14ace19d27c2cae8c Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Tue, 14 Nov 2023 16:38:07 -0500 Subject: [PATCH 4/6] Adding tests to tpa search --- src/layouts/tpa-search/tpa-search.spec.js | 382 +++++++++++++++++++++- src/layouts/tpa-search/tpa-search.vue | 15 +- 2 files changed, 383 insertions(+), 14 deletions(-) diff --git a/src/layouts/tpa-search/tpa-search.spec.js b/src/layouts/tpa-search/tpa-search.spec.js index 5be0e59b..1854c470 100644 --- a/src/layouts/tpa-search/tpa-search.spec.js +++ b/src/layouts/tpa-search/tpa-search.spec.js @@ -149,16 +149,31 @@ describe('TPA search page', () => { // Assert expect(providerSelectionForm.exists()).toBeTruthy(); }); - test('map', () => { + test('map', async () => { // Arrange - const wrapper = shallowMount(tpaSearch, getMountOptions()); + 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.find('#map'); + const map = wrapper.findComponent('#map'); // Assert expect(map.exists()).toBeTruthy(); expect(map.classes()).toContain('mb-4'); + expect(map.props().addresses).toEqual(expectedAddresses); + expect(map.props().zipCode).toBe(zipCode); }); test('search radius filter', () => { // Arrange @@ -173,9 +188,32 @@ describe('TPA search page', () => { expect(searchRadiusFilter.props().cmsWidgetName).toBe(expectedWidgetName); expect(searchRadiusFilter.props().validationRules).toBe(globalRules.OPTION_REQUIRED); }); - test('select provider question', () => { + test('select provider question', async () => { // Arrange const wrapper = shallowMount(tpaSearch, getMountOptions()); + await wrapper.setData({ + providers: [{ + name: '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 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'); @@ -188,6 +226,8 @@ describe('TPA search page', () => { 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', @@ -447,11 +487,142 @@ describe('TPA search page', () => { 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 () => { + // Arrange + const wrapper = shallowMount(tpaSearch, getMountOptions()); + await wrapper.setData({ + providers: [ + { + name: 'Shop Auto Glass', + phoneNumber: '740-555-1234', + distanceInMiles: 1.2345, + providerNumber: '232209', + address: { + streetAddress: '123 Lane Ave', + city: 'New York', + state: 'NY', + zipCode: '99230' + } + }, + { + name: '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; + + // Assert + expect(result).toEqual(expectedResult); + }); + }); + describe('providerButtonData', () => { + 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.providerButtonData; + + // Assert + expect(result).toEqual([]); + } + ); + test('returns expected when providers not empty', async () => { + // Arrange + const wrapper = shallowMount(tpaSearch, getMountOptions()); + await wrapper.setData({ + providers: [ + { + name: 'Shop Auto Glass', + phoneNumber: '740-555-1234', + distanceInMiles: 1.2345, + providerNumber: '232209', + address: { + streetAddress: '123 Lane Ave', + city: 'New York', + state: 'NY', + zipCode: '99230' + } + }, + { + name: 'Safelite AutoGlass', + phoneNumber: '676-555-0099', + distanceInMiles: 123.9452, + providerNumber: '123456', + address: { + streetAddress: '2003 Coral Ln', + city: 'Birmingham', + state: 'AL', + zipCode: '43001' + } + } + ] + }); + const expectedResult = [ + { + 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' + } + ]; + + // Act + const result = wrapper.vm.providerButtonData; + + // Assert + expect(result).toEqual(expectedResult); + }); + }); }); describe('watch', () => { test('on filter calls getTpaProviders and sets providers', async () => { // Arrange - const { wrapper } = getMountedComponent(); + const zipCode = '78220'; + const initialMapZipCode = '88800'; + const { wrapper } = getMountedComponent({}, { + zipCode, + mapZipCode: initialMapZipCode + }); const providers = { data: { shopProviders: ['some data', 'some more data']} }; useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (providers)); const newFilter = 'new filter'; @@ -462,6 +633,7 @@ describe('TPA search page', () => { // Assert expect(useMainStore().getTpaProviders).toBeCalled(); expect(wrapper.vm.providers).toEqual(providers.data.shopProviders); + expect(wrapper.vm.mapZipCode).toBe(zipCode); }); describe('on providers', () => { test.each([null, undefined, []])( @@ -503,6 +675,34 @@ describe('TPA search page', () => { }); }); 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, ''] + ])( + 'getProviderAddress returns expected', + (streetAddress, city, state, zipCode, expected) => { + // Arrange + const { wrapper } = getMountedComponent(); + useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders)); + const provider = { + address: { + streetAddress, + city, + state, + zipCode + } + }; + + // Act + const result = wrapper.vm.getProviderAddress(provider); + + // Assert + expect(result).toEqual(expected); + } + ); describe('getProviderButtonData', () => { test.each([null, undefined, {}])( 'returns empty list when getTpaProviders returns no data', @@ -550,7 +750,12 @@ describe('TPA search page', () => { 'sets providers to empty list when no data returned from getProviders', async (newProviders) => { // Arrange - const { wrapper } = getMountedComponent(); + const zipCode = '28331'; + const initialMapZipCode = '10022'; + const { wrapper } = getMountedComponent({}, { + zipCode, + mapZipCode: initialMapZipCode + }); useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => (newProviders)); // Act @@ -558,11 +763,17 @@ describe('TPA search page', () => { // Assert expect(wrapper.vm.providers).toEqual([]); + expect(wrapper.vm.mapZipCode).toEqual(zipCode); } ); test('returns value from getProviders', async () => { // Arrange - const { wrapper } = getMountedComponent(); + const zipCode = '12990'; + const initialMapZipCode = '00981'; + const { wrapper } = getMountedComponent({}, { + zipCode, + mapZipCode: initialMapZipCode + }); const shopProviders = [{ name: 'foo' }]; useMainStore().getTpaProviders = jest.fn().mockImplementationOnce(() => ({ data: { shopProviders } @@ -573,6 +784,7 @@ describe('TPA search page', () => { // Assert expect(wrapper.vm.providers).toEqual(shopProviders); + expect(wrapper.vm.mapZipCode).toEqual(zipCode); }); }); test('backButtonAction invokes navigate method', () => { @@ -619,6 +831,162 @@ describe('TPA search page', () => { expect(result).toBeNull(); }); }); + 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', () => { + // Arrange + const wrapper = shallowMount(tpaSearch, getMountOptions()); + const provider = { + name: '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' + }; + + // Act + const result = wrapper.vm.getShopButtonDataFromProvider(provider); + + // Assert + expect(result).toEqual(expectedResult); + }); + describe('returns result with button label', () => { + test.each(['', undefined, null])( + 'equal to empty string when name is empty', + (name) => { + // Arrange + const wrapper = shallowMount(tpaSearch, getMountOptions()); + const provider = { name }; + + // 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 name = 'Some Shop Name'; + const provider = { name }; + + // Act + const result = wrapper.vm.getShopButtonDataFromProvider(provider); + + // Assert + expect(result.buttonLabel).toEqual(name); + }); + }); + 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', () => { + // 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'; + + // Act + const result = wrapper.vm.getShopButtonDataFromProvider(provider); + + // 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); + }); + }); + }); }); describe('before route enter', () => { test('when providers exist at 25 mile radius, filter is set to "25 miles" and providers set to expected', async () => { diff --git a/src/layouts/tpa-search/tpa-search.vue b/src/layouts/tpa-search/tpa-search.vue index a456d29f..5e7259f5 100644 --- a/src/layouts/tpa-search/tpa-search.vue +++ b/src/layouts/tpa-search/tpa-search.vue @@ -254,7 +254,10 @@ export default { if (city) { addressLine2 = city; if (state || zipCode) { - addressLine2 = ', '; + if (state) { + addressLine2 += ','; + } + addressLine2 += ' '; } } if (state) { @@ -288,7 +291,6 @@ export default { providers = tpaProvidersRadius100; } } - console.log(`radius: ${radius}`); this.filter = radius; this.providers = providers?.data?.shopProviders ?? []; }, @@ -324,18 +326,17 @@ export default { } }, getShopButtonDataFromProvider(provider) { - const { phoneNumber } = provider; - - const distance = provider?.distanceInMiles + const cellNumber = provider?.phoneNumber ?? ''; + const distance = provider?.distanceInMiles !== null && !Number.isNaN(parseFloat(provider?.distanceInMiles)) ? +provider.distanceInMiles.toFixed(1) : null; return { buttonLabel: provider?.name ?? '', - buttonLabelSubCopy: distance == null + buttonLabelSubCopy: distance === null ? '' : `${distance} mi`, - buttonBodyCopy: `${this.getProviderAddress(provider)}
${phoneNumber}`, + buttonBodyCopy: `${this.getProviderAddress(provider)}
${cellNumber ?? ''}`, value: provider?.providerNumber ?? '' }; } From 352f3acb43fb103e47f20d0c1fd72173abac3337 Mon Sep 17 00:00:00 2001 From: Michaela Brydon Date: Wed, 15 Nov 2023 16:25:27 -0500 Subject: [PATCH 5/6] Finishing unit tests --- .../__snapshots__/google-map.spec.js.snap | 2 +- .../google-map/google-map.spec.js | 405 +++++++++++++++++- src/iss-components/google-map/google-map.vue | 18 +- src/layouts/tpa-search/tpa-search.vue | 4 +- 4 files changed, 418 insertions(+), 11 deletions(-) diff --git a/src/iss-components/google-map/__snapshots__/google-map.spec.js.snap b/src/iss-components/google-map/__snapshots__/google-map.spec.js.snap index b4ee066e..efab5bbf 100644 --- a/src/iss-components/google-map/__snapshots__/google-map.spec.js.snap +++ b/src/iss-components/google-map/__snapshots__/google-map.spec.js.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Google Maps should render expected initial data 1`] = ` +exports[`Google Map should render expected initial data 1`] = ` Object { "country": "USA", "geocoder": null, diff --git a/src/iss-components/google-map/google-map.spec.js b/src/iss-components/google-map/google-map.spec.js index fbaee73a..2c4ad709 100644 --- a/src/iss-components/google-map/google-map.spec.js +++ b/src/iss-components/google-map/google-map.spec.js @@ -1,7 +1,53 @@ import { shallowMount } from '@vue/test-utils'; import googleMap from '@/iss-components/google-map/google-map.vue'; -describe('Google Maps', () => { +const mockFitBounds = jest.fn(); +const mockMapInstance = { + fitBounds: mockFitBounds +}; +const mockMap = jest.fn(() => mockMapInstance); + +const mockGeocode = jest.fn(); +const mockGeocoderInstance = { + geocode: mockGeocode +}; +const mockGeocoder = jest.fn(() => (mockGeocoderInstance)); + +const mockAdvancedMarkerElement = jest.fn(); + +const mockImportLibrary = jest.fn().mockImplementation(() => (Promise.resolve({ + Map: mockMap, + Geocoder: mockGeocoder, + AdvancedMarkerElement: mockAdvancedMarkerElement +}))); + +const mockExtend = jest.fn(); +const mockLatLngBound = { extend: mockExtend }; + +const setupGoogleMock = () => { + global.window.google = { + maps: { + importLibrary: mockImportLibrary, + LatLngBounds: jest.fn(() => (mockLatLngBound)) + } + }; +}; + +beforeEach(() => { + // clear constructors + mockMap.mockClear(); + mockGeocoder.mockClear(); + mockAdvancedMarkerElement.mockClear(); + + // clear methods + mockExtend.mockClear(); + mockGeocode.mockClear(); + mockFitBounds.mockClear(); + + setupGoogleMock(); +}); + +describe('Google Map', () => { describe('should render', () => { test('expected initial data', async () => { // Arrange @@ -15,7 +61,6 @@ describe('Google Maps', () => { // Arrange const mapReference = '#map'; const wrapper = shallowMount(googleMap, {}); - // await wrapper.vm.$nextTick(); // Act const map = wrapper.find(mapReference); @@ -24,4 +69,360 @@ describe('Google Maps', () => { expect(map.exists()).toBeTruthy(); }); }); + test.each([ + ['13001', '13001 USA'], + ['9', '9 USA'], + ['', ' USA'], + [null, ' USA'] + ])( + 'given that zipcode set to "%p", computed zipCodeAddress should return "%p"', + async (zipCode, expected) => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + await wrapper.setData({ zipCode }); + + // Act + const result = wrapper.vm.zipCodeAddress; + + // Assert + expect(result).toBe(expected); + } + ); + test('watch addresses creates map with expected markers', async () => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + const zipCode = '00882'; + await wrapper.setData({ zipCode }); + + const mockGetCenter = jest.fn(); + const mockNorthEast = jest.fn(); + const mockSouthWest = jest.fn(); + const zipCodeGeocodeResult = { + results: [ + { + geometry: { + bounds: + { + getCenter: mockGetCenter, + getNorthEast: mockNorthEast, + getSouthWest: mockSouthWest + } + } + } + ] + }; + const [address1, address2, position1, position2] = ['a1', 'a2', 'p1', 'p2']; + mockGeocode.mockImplementation((obj) => { + let result = {}; + if (obj.address.includes(zipCode)) { + result = zipCodeGeocodeResult; + } else if (obj.address === address1) { + result = { + results: [ + { geometry: { location: position1 } } + ] + }; + } else if (obj.address === address2) { + result = { + results: [ + { geometry: { location: position2 } } + ] + }; + } + return Promise.resolve(result); + }); + const newAddresses = [address1, address2]; + + // Act + await wrapper.vm.$options.watch.addresses.call(wrapper.vm, newAddresses); + + for (let i = 0; i < 11; i++) { + // eslint-disable-next-line no-await-in-loop + await wrapper.vm.$nextTick(); + } + + // Assert + expect(mockMap).toBeCalledTimes(1); + expect(mockAdvancedMarkerElement).toBeCalledTimes(2); + expect(mockAdvancedMarkerElement).toBeCalledWith(expect.objectContaining({ + map: expect.anything(), + position: position1 + })); + expect(mockAdvancedMarkerElement).toBeCalledWith(expect.objectContaining({ + map: expect.anything(), + position: position2 + })); + expect(mockGeocoder).toBeCalledTimes(1); + + expect(mockExtend).toBeCalledTimes(4); + expect(mockFitBounds).toBeCalledTimes(1); + + expect(mockGetCenter).toBeCalledTimes(1); + expect(mockNorthEast).toBeCalledTimes(1); + expect(mockSouthWest).toBeCalledTimes(1); + }); + describe('method', () => { + test('getMap calls map constructor with expected parameters', async () => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + const center = { lat: 90, lng: -5 }; + + // Act + const result = await wrapper.vm.getMap(center); + + // Assert + expect(mockMap).toHaveBeenCalledWith( + null, + expect.objectContaining({ center, mapId: 'map_id', mapTypeControl: false }) + ); + expect(result).toBe(mockMapInstance); + }); + test.each([ + [undefined, 0], + [null, 0], + [[], 0], + [['loc1'], 1], + [['loc1', 'loc1', 'loc1', 'loc1'], 4] + ])( + 'given parameter %p, getBounds calls extend %p time(s)', + (locations, numberOfExtendCalls) => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + + // Act + const result = wrapper.vm.getBounds(locations); + + // Assert + expect(mockExtend).toHaveBeenCalledTimes(numberOfExtendCalls); + expect(result).toBe(mockLatLngBound); + } + ); + test.each([ + [1, null, mockGeocoderInstance], + [1, undefined, mockGeocoderInstance], + [0, {}, {}], + [0, { value: 'any value' }, { value: 'any value' }] + ])( + 'setGeocoder calls Geocoder constructor %p time(s) when original geocoder %p and geocoder set to %p', + async (numberOfCalls, originalGeocoder, newGeocoder) => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + await wrapper.setData({ geocoder: originalGeocoder }); + + // Act + await wrapper.vm.setGeocoder(); + + // Assert + expect(mockGeocoder).toHaveBeenCalledTimes(numberOfCalls); + expect(wrapper.vm.geocoder).toEqual(newGeocoder); + } + ); + test.each([ + [null, 0], + [undefined, 0], + [[], 0], + [['p1'], 1], + [['p1', 'p2', 'p3'], 3] + ])( + 'addMarkersToMap given positions argument %p, creates AdvancedMarkerElement %p time(s)', + async (positions, numberOfCalls) => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + const map = jest.fn(); + + // Act + await wrapper.vm.addMarkersToMap(map, positions); + + // Assert + expect(mockAdvancedMarkerElement).toHaveBeenCalledTimes(numberOfCalls); + } + ); + describe('getLocationsFromAddresses', () => { + test.each([ + [null, 0], + [undefined, 0], + [[], 0], + [['a1'], 1], + [['a1', 'a2', 'a3'], 3] + ])( + 'given address(es) %p calls geocode %p times', + async (addresses, numberOfCalls) => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + + // Act + await wrapper.vm.getLocationsFromAddresses(addresses); + + // Assert + expect(mockGeocode).toHaveBeenCalledTimes(numberOfCalls); + expect(wrapper.vm.geocoder).not.toBeNull(); + } + ); + test('only returns geocode results that are not null', async () => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + const addresses = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8']; + mockGeocode.mockImplementationOnce(() => null) + .mockImplementationOnce(() => ({ results: null })) + .mockImplementationOnce(() => ({ results: [] })) + .mockImplementationOnce(() => ({ results: [{}] })) + .mockImplementationOnce(() => ( + { results: [{ geometry: undefined }] } + )) + .mockImplementationOnce(() => ( + { results: [{ geometry: {} }] } + )) + .mockImplementationOnce(() => ( + { + results: [ + { geometry: { location: 'some value' } } + ] + } + )) + .mockImplementationOnce(() => ( + { + results: [ + { geometry: { location: 'some other value' } } + ] + } + )); + const expectedResult = ['some value', 'some other value']; + + // Act + const result = await wrapper.vm.getLocationsFromAddresses(addresses); + + // Assert + expect(result.length).toBe(2); + expect(result).toEqual(expectedResult); + }); + }); + describe('getBoundsFromAddress', () => { + test('returns expected when geocode returns valid result', async () => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + const address = 'random address'; + const expectedResult = 'bounds returned'; + const geocodeResult = { + results: [ + { + geometry: { bounds: expectedResult } + } + ] + }; + mockGeocode.mockImplementationOnce(() => Promise.resolve(geocodeResult)); + + // Act + const result = await wrapper.vm.getBoundsFromAddress(address); + + // Assert + expect(result).toBe(expectedResult); + expect(wrapper.vm.geocoder).not.toBeNull(); + }); + test('returns null when geocode returns invalid result', async () => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + const address = 'random address'; + mockGeocode.mockImplementationOnce(() => Promise.resolve({})); + + // Act + const result = await wrapper.vm.getBoundsFromAddress(address); + + // Assert + expect(result).toBeNull(); + expect(wrapper.vm.geocoder).not.toBeNull(); + }); + }); + describe('createMapWithMarkersForAddresses', () => { + test('calls expected when addresses provided', async () => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + const zipCode = '00882'; + await wrapper.setData({ zipCode }); + const mockGetCenter = jest.fn(); + const mockNorthEast = jest.fn(); + const mockSouthWest = jest.fn(); + const zipCodeGeocodeResult = { + results: [ + { + geometry: { + bounds: + { + getCenter: mockGetCenter, + getNorthEast: mockNorthEast, + getSouthWest: mockSouthWest + } + } + } + ] + }; + const shopGeocodeResult = { + results: [ + { geometry: { location: 'valid location' } } + ] + }; + mockGeocode.mockImplementation((obj) => + Promise.resolve(obj.address.includes(zipCode) + ? zipCodeGeocodeResult + : shopGeocodeResult)); + const addresses = ['a1', 'a2']; + + // Act + await wrapper.vm.createMapWithMarkersForAddresses(addresses); + + // Assert + expect(mockMap).toBeCalledTimes(1); + expect(mockAdvancedMarkerElement).toBeCalledTimes(2); + expect(mockGeocoder).toBeCalledTimes(1); + + expect(mockExtend).toBeCalledTimes(4); + expect(mockFitBounds).toBeCalledTimes(1); + + expect(mockGetCenter).toBeCalledTimes(1); + expect(mockNorthEast).toBeCalledTimes(1); + expect(mockSouthWest).toBeCalledTimes(1); + }); + test.each([null, undefined, []])( + 'calls expected when addresses argument is "%p"', + async (addresses) => { + // Arrange + const wrapper = shallowMount(googleMap, {}); + const zipCode = '00882'; + await wrapper.setData({ zipCode }); + const mockGetCenter = jest.fn(); + const mockNorthEast = jest.fn(); + const mockSouthWest = jest.fn(); + const zipCodeGeocodeResult = { + results: [ + { + geometry: { + bounds: + { + getCenter: mockGetCenter, + getNorthEast: mockNorthEast, + getSouthWest: mockSouthWest + } + } + } + ] + }; + mockGeocode.mockImplementation(() => Promise.resolve(zipCodeGeocodeResult)); + + // Act + await wrapper.vm.createMapWithMarkersForAddresses(addresses); + + // Assert + expect(mockMap).toBeCalledTimes(1); + expect(mockAdvancedMarkerElement).toBeCalledTimes(0); + expect(mockGeocoder).toBeCalledTimes(1); + + expect(mockExtend).toBeCalledTimes(2); + expect(mockFitBounds).toBeCalledTimes(1); + + expect(mockGetCenter).toBeCalledTimes(1); + expect(mockNorthEast).toBeCalledTimes(1); + expect(mockSouthWest).toBeCalledTimes(1); + } + ); + }); + }); }); diff --git a/src/iss-components/google-map/google-map.vue b/src/iss-components/google-map/google-map.vue index 76d8af52..fa5a57d8 100644 --- a/src/iss-components/google-map/google-map.vue +++ b/src/iss-components/google-map/google-map.vue @@ -7,12 +7,10 @@ - - + diff --git a/vue.config.js b/vue.config.js index a13ba34c..dcc30a25 100644 --- a/vue.config.js +++ b/vue.config.js @@ -9,6 +9,8 @@ process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY = "(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer','GTM-KKNWZ3');"; process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC = 'https://www.googletagmanager.com/ns.html?id=GTM-KKNWZ3>m_auth=amlAYNhxUxuskQo7jmjadg>m_preview=env-38>m_cookies_win=x'; +process.env.VUE_APP_GOOGLE_MAPS_API_SCRIPT = + "(g=>{var h,a,k,p='The Google Maps JavaScript API',c='google',l='importLibrary',q='__ib__',m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement('script'));e.set('libraries',[...r]+'');for(k in g)e.set(k.replace(/[A-Z]/g,t=>'_'+t[0].toLowerCase()),g[k]);e.set('callback',c+'.maps.'+q);a.src=`https://maps.googleapis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+' could not load.'));a.nonce=m.querySelector('script[nonce]')?.nonce||'';m.head.append(a)}));d[l]?console.warn(p+' only loads once. Ignoring:',g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({key: 'AIzaSyCuLhQcDdZTTb4JzpUFms1OCch2dk5lHF0', v: 'weekly'});"; module.exports = { publicPath: '/', diff --git a/vue.release.config.js b/vue.release.config.js index 20290580..c3c79a75 100644 --- a/vue.release.config.js +++ b/vue.release.config.js @@ -5,6 +5,7 @@ process.env.VUE_APP_GOOGLE_PLACES_API_KEY = "__VUE_APP_GOOGLE_PLACES_API_KEY__"; // GA & GTM process.env.VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY = "__VUE_APP_GOOGLE_TAG_MANAGER_SCRIPT_BODY__"; process.env.VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC = "__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__" +process.env.VUE_APP_GOOGLE_MAPS_API_SCRIPT = "__VUE_APP_GOOGLE_MAPS_API_SCRIPT__"; module.exports = { publicPath: "/",