diff --git a/src/digital-components/button-question/button-question.vue b/src/digital-components/button-question/button-question.vue index 3dfa4489f..e98ef2901 100644 --- a/src/digital-components/button-question/button-question.vue +++ b/src/digital-components/button-question/button-question.vue @@ -256,8 +256,10 @@ export default { }, }, watch: { - modelValue() { - this.resetField(); + modelValue(newValue) { + this.resetField({ + value: newValue, + }); }, answers() { //once we get the answers to display from parent, see if we need a GA event to log what we showed diff --git a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue index f1d684ffc..d6711f363 100644 --- a/src/layouts/service-location/appointment-type-question/appointment-type-question.vue +++ b/src/layouts/service-location/appointment-type-question/appointment-type-question.vue @@ -2,6 +2,7 @@
{ + it("Should include buttonLabel in html", async () => { + // Arrange + let { wrapper } = setupMocks({ + mountOptionsMockData: { + propsData: mockProps, + }, + }); + + // Act + const outputHtml = wrapper.html(); + + // Assert + expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabel"])); + }); + + it("Should include buttonLabelSubCopy in html", async () => { + // Arrange + let { wrapper } = setupMocks({ + mountOptionsMockData: { + propsData: mockProps, + }, + }); + + // Act + const outputHtml = wrapper.html(); + + // Assert + expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabelSubCopy"])); + }); + + it("Should include buttonLabelAuxillaryCopy in html", async () => { + // Arrange + let { wrapper } = setupMocks({ + mountOptionsMockData: { + propsData: mockProps, + }, + }); + + // Act + const outputHtml = wrapper.html(); + + // Assert + expect(outputHtml).toEqual(expect.stringContaining(mockProps["buttonLabelAuxillaryCopy"])); + }); +}); + +const mockProps = { + buttonLabel: "buttonLabel test copy", + buttonLabelSubCopy: "buttonLabelSubCopy test copy", + buttonBodyCopy: + "
  • buttonBodyCopy test copy
  • 2
  • 3
  • 4
  • 5
", + buttonLabelAuxillaryCopy: "buttonLabelAuxillaryCopy test copy", + value: 0, + modelValue: 0, + groupName: "mockGroup", +}; + +function setupMocks({ mountOptionsMockData = {} }) { + const wrapper = mount(shopListButton, { + ...mountOptionsMockData, + mixins: [inputButtonWrapperMixin], + }); + + return { wrapper }; +} diff --git a/src/layouts/service-location/shop-question/shop-question.spec.js b/src/layouts/service-location/shop-question/shop-question.spec.js index 3d0843e10..7f67737fa 100644 --- a/src/layouts/service-location/shop-question/shop-question.spec.js +++ b/src/layouts/service-location/shop-question/shop-question.spec.js @@ -1 +1,554 @@ -test.todo("some test to be written in the future"); +import { shallowMount } from "@vue/test-utils"; +import { storeActions } from "@/constants/store-actions"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import shopQuestion from "./shop-question"; + +jest.mock("@/mixins/base-mixin", () => ({ + methods: { + dispatchStoreAction(action, items, encode) { + if (action === mockGetProviderLocationsStoreAction) { + return new Promise((resolve) => { + resolve(mockNewShopList); + }); + } + }, + }, +})); + +const mockGetProviderLocationsStoreAction = storeActions.GET_PROVIDER_LOCATIONS; +const mockNewShopList = { + data: [ + { + city: "Far", + country: "United States", + distance: 100, + providerNumber: "129", + state: "OH", + streetAddress: "555 First Capital Ln", + zipCode: "45601", + }, + { + city: "Farther", + country: "United States", + distance: 200, + providerNumber: "130", + state: "OH", + streetAddress: "5486 N Grove Rd", + zipCode: "43215", + }, + { + city: "Farthest (Ever)", + country: "United States", + distance: 380.5, + providerNumber: "131", + state: "OH", + streetAddress: "1670 Bongo Ave D", + zipCode: "43223", + }, + ], +}; + +const mockCmsContent = { + QuestionText: "Select a shop:", +}; + +const cmsWidgetName = "AppointmentTypeQuestionWidget"; +const shopQuestionInitialData = [ + { + city: "Worthington", + country: "United States", + distance: 1.5, + providerNumber: "123", + state: "OH", + streetAddress: "760 Dearborn Park Ln", + zipCode: "43085", + }, + { + city: "Columbus", + country: "United States", + distance: 4.5, + providerNumber: "124", + state: "OH", + streetAddress: "5486 N Hamilton Rd", + zipCode: "43230", + }, + { + city: "Powell", + country: "United States", + distance: 7, + providerNumber: "125", + state: "OH", + streetAddress: "1670 Harmon Ave C", + zipCode: "43223", + }, + { + city: "Chillicothe", + country: "United States", + distance: 41.5, + providerNumber: "126", + state: "OH", + streetAddress: "555 First Capital Ln", + zipCode: "45601", + }, + { + city: "Grove City", + country: "United States", + distance: 4.5, + providerNumber: "127", + state: "OH", + streetAddress: "5486 N Grove Rd", + zipCode: "43215", + }, + { + city: "Dayton", + country: "United States", + distance: 80.5, + providerNumber: "128", + state: "OH", + streetAddress: "1670 Bongo Ave D", + zipCode: "43223", + }, + { + city: "Far", + country: "United States", + distance: 100, + providerNumber: "129", + state: "OH", + streetAddress: "555 First Capital Ln", + zipCode: "45601", + }, + { + city: "Farther", + country: "United States", + distance: 200, + providerNumber: "130", + state: "OH", + streetAddress: "5486 N Grove Rd", + zipCode: "43215", + }, + { + city: "Farthest (Ever)", + country: "United States", + distance: 380.5, + providerNumber: "131", + state: "OH", + streetAddress: "1670 Bongo Ave D", + zipCode: "43223", + }, +]; + +const mockMixin = { + methods: { + getCmsContent: jest.fn((widgetName, cmsFieldName) => { + if (widgetName === cmsWidgetName) { + return mockCmsContent[cmsFieldName]; + } + + return null; + }), + }, +}; + +describe("shop-question.vue", () => { + it("Should display first three shops when an appointment type has already been selected", async () => { + // Arrange/Act + const container = document.createElement("div"); + container.scrollTo = jest.fn(); + + container.classList.add("page-container-grouped-styles"); + document.body.appendChild(container); + + const { wrapper } = setupMocks({ + mixins: [mockMixin], + props: { + modelValue: null, + serviceZipCode: "43081", + selectedAppointmentType: "Dropoff", + cmsWidgetName: cmsWidgetName, + }, + mountOptions: { + attachTo: document.body, + }, + }); + + wrapper.vm.$refs.buttonQuestion.resetField = jest.fn(); + wrapper.vm.initializeComponent(shopQuestionInitialData); + + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.answers).toEqual([ + { + Name: "123", + buttonLabel: "Worthington", + buttonLabelSubCopy: "1.5 mi", + buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085", + }, + { + Name: "124", + buttonLabel: "Columbus", + buttonLabelSubCopy: "4.5 mi", + buttonBodyCopy: "5486 N Hamilton Rd, Columbus, OH 43230", + }, + { + Name: "125", + buttonLabel: "Powell", + buttonLabelSubCopy: "7 mi", + buttonBodyCopy: "1670 Harmon Ave C, Powell, OH 43223", + }, + ]); + }); + + it("Should display the 'Show more locations' link when there are more than three locations to chose from", async () => { + // Arrange/Act + const container = document.createElement("div"); + container.scrollTo = jest.fn(); + + container.classList.add("page-container-grouped-styles"); + document.body.appendChild(container); + + const { wrapper } = setupMocks({ + mixins: [mockMixin], + props: { + modelValue: null, + serviceZipCode: "43081", + selectedAppointmentType: "Dropoff", + cmsWidgetName: cmsWidgetName, + }, + mountOptions: { + attachTo: document.body, + }, + }); + + wrapper.vm.$refs.buttonQuestion.resetField = jest.fn(); + wrapper.vm.initializeComponent(shopQuestionInitialData); + + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" }); + + // Assert + expect(showMoreShopsLink.exists()).toBe(true); + }); + + it("Should not display the 'Show more locations' link when there are fewer than three locations to chose from", async () => { + // Arrange/Act + const container = document.createElement("div"); + container.scrollTo = jest.fn(); + + container.classList.add("page-container-grouped-styles"); + document.body.appendChild(container); + + const alsoShopQuestionInitialData = [ + { + city: "Worthington", + country: "United States", + distance: 1.5, + providerNumber: "123", + state: "OH", + streetAddress: "760 Dearborn Park Ln", + zipCode: "43085", + }, + { + city: "Columbus", + country: "United States", + distance: 4.5, + providerNumber: "124", + state: "OH", + streetAddress: "5486 N Hamilton Rd", + zipCode: "43230", + }, + { + city: "Powell", + country: "United States", + distance: 7, + providerNumber: "125", + state: "OH", + streetAddress: "1670 Harmon Ave C", + zipCode: "43223", + }, + ]; + + const { wrapper } = setupMocks({ + mixins: [mockMixin], + props: { + modelValue: null, + serviceZipCode: "43081", + selectedAppointmentType: "Dropoff", + cmsWidgetName: cmsWidgetName, + }, + mountOptions: { + attachTo: document.body, + }, + }); + + wrapper.vm.$refs.buttonQuestion.resetField = jest.fn(); + wrapper.vm.initializeComponent(alsoShopQuestionInitialData); + + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" }); + + // Assert + expect(showMoreShopsLink.exists()).toBe(false); + }); + + it("Should display the next three shops when the 'Show more location' link is clicked", async () => { + const container = document.createElement("div"); + container.scrollTo = jest.fn(); + + container.classList.add("page-container-grouped-styles"); + document.body.appendChild(container); + + const displayedAnswers = [ + { + Name: "123", + buttonLabel: "Worthington", + buttonLabelSubCopy: "1.5 mi", + buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085", + }, + { + Name: "124", + buttonLabel: "Columbus", + buttonLabelSubCopy: "4.5 mi", + buttonBodyCopy: "5486 N Hamilton Rd, Columbus, OH 43230", + }, + { + Name: "125", + buttonLabel: "Powell", + buttonLabelSubCopy: "7 mi", + buttonBodyCopy: "1670 Harmon Ave C, Powell, OH 43223", + }, + ]; + + // Arrange/Act + const { wrapper } = setupMocks({ + mixins: [mockMixin], + props: { + modelValue: null, + serviceZipCode: "43081", + selectedAppointmentType: "Dropoff", + cmsWidgetName: cmsWidgetName, + }, + mountOptions: { + attachTo: document.body, + }, + }); + + wrapper.vm.$refs.buttonQuestion.resetField = jest.fn(); + + wrapper.vm.shops = shopQuestionInitialData; + wrapper.vm.answers = displayedAnswers; + wrapper.vm.shopIndex = 3; + + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + const showMoreShopsLink = wrapper.findComponent({ ref: "showMoreShopsLink" }); + + showMoreShopsLink.trigger("click"); + + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.answers.length).toBe(6); + expect(wrapper.vm.answers).toEqual([ + { + Name: "123", + buttonLabel: "Worthington", + buttonLabelSubCopy: "1.5 mi", + buttonBodyCopy: "760 Dearborn Park Ln, Worthington, OH 43085", + }, + { + Name: "124", + buttonLabel: "Columbus", + buttonLabelSubCopy: "4.5 mi", + buttonBodyCopy: "5486 N Hamilton Rd, Columbus, OH 43230", + }, + { + Name: "125", + buttonLabel: "Powell", + buttonLabelSubCopy: "7 mi", + buttonBodyCopy: "1670 Harmon Ave C, Powell, OH 43223", + }, + { + Name: "126", + buttonLabel: "Chillicothe", + buttonLabelSubCopy: "41.5 mi", + buttonBodyCopy: "555 First Capital Ln, Chillicothe, OH 45601", + }, + { + Name: "127", + buttonLabel: "Grove City", + buttonLabelSubCopy: "4.5 mi", + buttonBodyCopy: "5486 N Grove Rd, Grove City, OH 43215", + }, + { + Name: "128", + buttonLabel: "Dayton", + buttonLabelSubCopy: "80.5 mi", + buttonBodyCopy: "1670 Bongo Ave D, Dayton, OH 43223", + }, + ]); + }); + + it("Should display the number of shops necessary to show a previously selected shop", async () => { + // Arrange/Act + const container = document.createElement("div"); + container.scrollTo = jest.fn(); + + container.classList.add("page-container-grouped-styles"); + document.body.appendChild(container); + + const { wrapper } = setupMocks({ + mixins: [mockMixin], + props: { + modelValue: "127", + serviceZipCode: "43081", + selectedAppointmentType: "Dropoff", + cmsWidgetName: cmsWidgetName, + }, + mountOptions: { + attachTo: document.body, + }, + }); + + wrapper.vm.$refs.buttonQuestion.resetField = jest.fn(); + wrapper.vm.initializeComponent(shopQuestionInitialData); + + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.answers.length).toEqual(5); + }); + + it("Should reset the answers when the selected appointment type changes", async () => { + // Arrange/Act + const container = document.createElement("div"); + container.scrollTo = jest.fn(); + + container.classList.add("page-container-grouped-styles"); + document.body.appendChild(container); + + const { wrapper } = setupMocks({ + mixins: [mockMixin], + props: { + modelValue: "127", + serviceZipCode: "43081", + selectedAppointmentType: "Dropoff", + cmsWidgetName: cmsWidgetName, + }, + mountOptions: { + attachTo: document.body, + }, + }); + + wrapper.vm.$refs.buttonQuestion.resetField = jest.fn(); + wrapper.vm.initializeComponent(shopQuestionInitialData); + + wrapper.setProps({ + selectedAppointmentType: "Inshop", + }); + + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.answers.length).toEqual(3); + }); + + it("Should reload the shops when the service zip code changes", async () => { + // Arrange/Act + const container = document.createElement("div"); + container.scrollTo = jest.fn(); + + container.classList.add("page-container-grouped-styles"); + document.body.appendChild(container); + + const { wrapper } = setupMocks({ + mixins: [mockMixin], + props: { + modelValue: "127", + serviceZipCode: "43081", + selectedAppointmentType: "Dropoff", + cmsWidgetName: cmsWidgetName, + }, + mountOptions: { + attachTo: document.body, + }, + }); + + wrapper.vm.$refs.buttonQuestion.resetField = jest.fn(); + wrapper.vm.shops = shopQuestionInitialData; + + await wrapper.vm.$nextTick(); + + wrapper.vm.$options.methods.loadInitialData = jest.fn().mockImplementation(() => { + return new Promise((resolve) => { + resolve(newShopList); + }); + }); + await wrapper.vm.$options.watch.serviceZipCode.handler.call(wrapper.vm, "43054"); + + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.shops.length).toEqual(3); + expect(wrapper.vm.shops).toEqual(mockNewShopList.data); + }); + + it("Should clear the existing answers when the service zip code changes", async () => { + // Arrange/Act + const container = document.createElement("div"); + container.scrollTo = jest.fn(); + + container.classList.add("page-container-grouped-styles"); + document.body.appendChild(container); + + const { wrapper } = setupMocks({ + mixins: [mockMixin], + props: { + modelValue: "127", + serviceZipCode: "43081", + selectedAppointmentType: "Dropoff", + cmsWidgetName: cmsWidgetName, + }, + mountOptions: { + attachTo: document.body, + }, + }); + + wrapper.vm.$refs.buttonQuestion.resetField = jest.fn(); + wrapper.vm.initializeComponent(shopQuestionInitialData); + + wrapper.setProps({ + selectedAppointmentType: "Inshop", + }); + + await wrapper.vm.$nextTick(); + + // Assert + expect(wrapper.vm.answers.length).toEqual(3); + }); +}); + +function setupMocks({ mountOptions, mixins, props, isShallowMount = true }) { + const resultingMountOptions = getMountOptions({ + ...mountOptions, + mixins, + }); + + if (props) resultingMountOptions.propsData = props; + + const wrapper = isShallowMount + ? shallowMount(shopQuestion, resultingMountOptions) + : mount(shopQuestion, resultingMountOptions); + + return { wrapper }; +} diff --git a/src/layouts/service-location/shop-question/shop-question.vue b/src/layouts/service-location/shop-question/shop-question.vue index 304651e81..2610fd787 100644 --- a/src/layouts/service-location/shop-question/shop-question.vue +++ b/src/layouts/service-location/shop-question/shop-question.vue @@ -67,14 +67,9 @@ export default { }, props: { modelValue: String, - isDropoff: Boolean, serviceZipCode: String, selectedAppointmentType: String, cmsWidgetName: String, - shopQuestionInitialData: { - type: Object, - required: true, - }, validationRules: String, }, computed: { @@ -90,7 +85,7 @@ export default { }, }, displayDropoffInformation() { - return this.isDropoff; + return this.selectedAppointmentType == "Dropoff"; }, showMoreShopsLinkText() { return this.getCmsContent("ShowMoreShopsLinkWidget", "Text"); @@ -105,7 +100,7 @@ export default { initializeComponent(shopQuestionInitialData) { this.shops = shopQuestionInitialData; }, - getNextShopsFromList(numberToGet = 3) { + async getNextShopsFromList(numberToGet = 3) { const shopIterator = (array, n) => { let l = array.length; return () => { @@ -125,7 +120,7 @@ export default { buttonLabel: shop.city, buttonLabelSubCopy: `${shop.distance} mi`, buttonBodyCopy: `${shop.streetAddress}, ${shop.city}, ${shop.state} ${shop.zipCode}`, - buttonAuxillaryCopy: `foo`, + //buttonAuxillaryCopy: `foo`, }; }); @@ -137,17 +132,17 @@ export default { }); } - this.$nextTick().then(() => { - if (this.shopIndex == this.shops.length) { - this.displaySeeMoreLocationsLink = false; - } else { - this.displaySeeMoreLocationsLink = true; - } + await this.$nextTick(); - this.$nextTick().then(() => { - this.scrollToBottom(); - }); - }); + if (this.shopIndex == this.shops.length) { + this.displaySeeMoreLocationsLink = false; + } else { + this.displaySeeMoreLocationsLink = true; + } + + await this.$nextTick(); + + this.scrollToBottom(); }, resetShopList() { this.answers = []; @@ -159,29 +154,29 @@ export default { const container = document.getElementsByClassName("page-container-grouped-styles")[0]; container.scrollTo({ top: container.scrollHeight, left: 0, behavior: "smooth" }); }, + async reloadShopData(serviceZipCode) { + const result = await this.loadInitialData(serviceZipCode); + this.initializeComponent(result.data); + this.resetShopList(); + }, }, watch: { serviceZipCode: { - handler(newValue) { - this.loadInitialData(newValue).then((result) => { - this.initializeComponent(result.data); - this.resetShopList(); - }); + async handler(newValue) { + await this.reloadShopData(newValue); }, }, selectedAppointmentType: { - handler(newValue) { + async handler(newValue) { // If the validation has been previously triggered, clear it before displaying the component this.$refs.buttonQuestion.resetField(); - if (newValue != null) { - this.resetShopList(); - this.getNextShopsFromList(); + this.resetShopList(); + await this.getNextShopsFromList(); - this.$nextTick().then(() => { - this.scrollToBottom(); - }); - } + await this.$nextTick(); + + this.scrollToBottom(); }, }, shops: { @@ -190,6 +185,7 @@ export default { const selectedShopIndex = newValue.findIndex( (provider) => provider.providerNumber == this.modelValue ); + if (selectedShopIndex >= 3) { this.getNextShopsFromList(selectedShopIndex + 1); } else { diff --git a/src/mixins/analytics-mixin.js b/src/mixins/analytics-mixin.js index 12fea69dc..29eb76c3d 100644 --- a/src/mixins/analytics-mixin.js +++ b/src/mixins/analytics-mixin.js @@ -24,35 +24,38 @@ import { applicationConfig } from "../constants/application-config"; export default { methods: { logPageView(pageEvent) { - // const currentPageName = getPageNameByQueryString(); - // var payload = { - // userId: getDeviceIdValue(), - // sessionKey: getSessionKeyValue(), - // pageName: currentPageName, - // sessionId: getSessionIdValue(), - // action: "", - // event: pageEvent, - // shouldUseSessionId: false, - // experimentsForUser: store.getters.applicationUser.experiments, - // }; - // baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false); + const currentPageName = getPageNameByQueryString(); + var payload = { + userId: getDeviceIdValue(), + sessionKey: getSessionKeyValue(), + pageName: currentPageName, + sessionId: getSessionIdValue(), + action: "", + event: pageEvent, + shouldUseSessionId: false, + experimentsForUser: store.getters.applicationUser.experiments, + }; + + baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false); }, logCustomEvent(category, action, label, value) { - // const currentPageName = getPageNameByQueryString(); - // var payload = { - // userId: getDeviceIdValue(), - // sessionKey: getSessionKeyValue(), - // pageName: currentPageName, - // sessionId: getSessionIdValue(), - // category: category, - // action: action, - // label: label, - // value: value, - // shouldUseSessionId: false, - // experimentsForUser: store.getters.applicationUser.experiments, - // }; - // baseMixin.methods.dispatchStoreAction(storeActions.LOG_CUSTOM_EVENT, payload, false); + const currentPageName = getPageNameByQueryString(); + + var payload = { + userId: getDeviceIdValue(), + sessionKey: getSessionKeyValue(), + pageName: currentPageName, + sessionId: getSessionIdValue(), + category: category, + action: action, + label: label, + value: value, + shouldUseSessionId: false, + experimentsForUser: store.getters.applicationUser.experiments, + }; + + baseMixin.methods.dispatchStoreAction(storeActions.LOG_CUSTOM_EVENT, payload, false); }, pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null) {