From 11a4943e9f2abc4da0518d94fc62f2d2712b0500 Mon Sep 17 00:00:00 2001 From: Leah Schumann Date: Wed, 1 Jun 2022 08:10:19 -0400 Subject: [PATCH 01/13] addressQuestions component unit tests --- jest.config.js | 4 +- .../address-questions.spec.js | 130 ++++++++++++++++++ .../address-questions.spec.js1 | 0 .../address-questions/address-questions.vue | 16 +-- 4 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js delete mode 100644 src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js1 diff --git a/jest.config.js b/jest.config.js index f2c8b5f28..e93300967 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,7 +6,7 @@ module.exports = { transform: { "^.+\\.vue$": "vue-jest" }, moduleFileExtensions: ["js", "vue"], collectCoverageFrom: [ - "src/**/*.{js,vue}", + //"src/**/*.{js,vue}", "!src/main.js", "!src/constants/*.js", "!src/router/**/*.js", @@ -22,7 +22,7 @@ module.exports = { // TODO REMOVE THESE AFTER WRITING UNIT TESTS "!src/layouts/address-lookup/address-lookup.vue", "!src/layouts/address-lookup/customer-questions/customer-questions.vue", - "!src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue", + "src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue", "!src/layouts/address-vehicles/address-vehicles.vue", "!src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue", "!src/ux-components/alert\alert.vue", diff --git a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js new file mode 100644 index 000000000..11a0aa4ed --- /dev/null +++ b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js @@ -0,0 +1,130 @@ +import { shallowMount } from "@vue/test-utils"; +import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions"; + +const addressModel = { + streetAddress: "", + city: "", + state: "", + zipCode: "", +} + +describe("addressQuestions.vue", () => { + + it("Should render addressQuestions sub-components (textbox-questions and dropdown-questions)", async () => { + // Arrange + const wrapper = shallowMount(addressQuestions); + + // Act + const streetAddress = wrapper.findComponent({ ref: 'autocomplete' }); + const city = wrapper.findComponent({ ref: 'city' }); + const state = wrapper.findComponent({ ref: 'state' }); + const zipCode = wrapper.findComponent({ ref: 'zipCode' }); + + // Assert + expect(streetAddress.exists()).toBe(true); + expect(city.exists()).toBe(true); + expect(state.exists()).toBe(true); + expect(zipCode.exists()).toBe(true); + + }); + + it("Should call the watch handler for the address model when the addressModel is changed", async () => { + // Arrange + const wrapper = shallowMount(addressQuestions, { + propsData: { + modelValue: addressModel, + }, + }); + wrapper.vm.displayNoMatchWarning = true; + + // Act + const newAddressModel = { + streetAddress: "foo", + city: "foo", + state: "foo", + zipCode: "55555", + }; + + wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, newAddressModel); + + // Assert + expect(wrapper.vm.addressModel.handler).toHaveBeenCalled; + + }); + + it("Should set this.displayNoMatchWarning to false, if the model changes when it is set to true", async () => { + // Arrange + const wrapper = shallowMount(addressQuestions, { + propsData: { + modelValue: addressModel, + }, + }); + wrapper.vm.displayNoMatchWarning = true; + + // Act + const newAddressModel = { + streetAddress: "foo", + city: "", + state: "", + zipCode: "", + }; + + // Act + wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, newAddressModel); + + // Assert + expect(wrapper.vm.displayNoMatchWarning === false); + + }); + + it("Should it not should set this.displayNoMatchWarning to false when it is set to true, if the model if prepopulated", async () => { + // Arrange + const wrapper = shallowMount(addressQuestions, { + propsData: { + modelValue: addressModel, + }, + }); + wrapper.vm.displayNoMatchWarning = true; + + // Act + const newAddressModel = { + streetAddress: "foo", + city: "foo", + state: "foo", + zipCode: "55555", + }; + + // Act + wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, newAddressModel); + + // Assert + expect(wrapper.vm.displayNoMatchWarning === true); + + }); + + it("Should it set this.showAddressFields to true when the model is prepopulated", async () => { + // Arrange + const wrapper = shallowMount(addressQuestions, { + propsData: { + modelValue: addressModel, + }, + }); + wrapper.vm.displayNoMatchWarning = true; + + // Act + const newAddressModel = { + streetAddress: "foo", + city: "foo", + state: "foo", + zipCode: "55555", + }; + + // Act + wrapper.vm.setupAddressLookup(); + + // Assert + expect(wrapper.vm.showAddressFields).toBe(true); + + }); + +}) \ No newline at end of file diff --git a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js1 b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js1 deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue index f188cf214..61062ba47 100644 --- a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue +++ b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue @@ -215,12 +215,12 @@ export default ({ // Standard place_changed event handling const autocompleteListener = window.google.maps.event.addListener(autocomplete, 'place_changed', fillInAddress); - // Wrapping the addressField1 element in the Google Address Autocomplete object - // will cause "autocomplete='off'" which Chrome completely ignores. This event - // handler will set the value to something arbitrary so autofill doesn't work. - // https://stackoverflow.com/a/30976223 - addressField1.addEventListener("focus", () => { - addressField1.setAttribute("autocomplete", "do-not-autofill"); + // Wrapping the addressField1 element in the Google Address Autocomplete object + // will cause "autocomplete='off'" which Chrome completely ignores. This event + // handler will set the value to something arbitrary so autofill doesn't work. + // https://stackoverflow.com/a/30976223 + addressField1.addEventListener("focus", () => { + addressField1.setAttribute("autocomplete", "do-not-autofill"); // Make place results box stick to the input on scroll const streetAddressField = document.getElementById("streetAddressField"); @@ -228,7 +228,7 @@ export default ({ if (autocompleteResultsContainer) { streetAddressField.appendChild(autocompleteResultsContainer); } - + }) addressField1.onchange = function() { @@ -325,7 +325,7 @@ export default ({ watch: { addressModel: { handler(newValue) { - // The first time the address model changes is w + // The first time the address model changes is when the page first loads if (!newValue.city && !newValue.state && !newValue.zipCode) { From 6965f3febe22de39d9e9b5bced68e97866d03e82 Mon Sep 17 00:00:00 2001 From: Katie Date: Fri, 3 Jun 2022 14:12:49 -0400 Subject: [PATCH 02/13] Add basic setup for address-questions tests --- .../address-questions.spec.js | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js diff --git a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js new file mode 100644 index 000000000..2db456aa8 --- /dev/null +++ b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js @@ -0,0 +1,154 @@ +// Components +import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions"; +import alert from "@/ux-components/alert/alert"; +import funnelFooter from "@/common-components/funnel-footer/funnel-footer"; +import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner"; +import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header"; +import customerQuestions from "@/layouts/address-lookup/customer-questions/customer-questions"; +import textboxQuestion from "@/common-components/textbox-question/textbox-question"; +import router from "@/router"; + +// Supporting Files +import { settleAllPromises } from "@/helpers/layout-helper.js"; +import baseMixin from "@/mixins/base-mixin"; +import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; +import { mount, shallowMount } from "@vue/test-utils"; +import { getMountOptions } from "@/helpers/unit-test-helper.js"; +import { storeActions } from "@/constants/store-actions"; +import { storeMutations } from "@/constants/store-mutations"; +import store from "@/store"; + +let fillInAddressFunction; + +// Mock our module for promises. +// jest.mock("@/helpers/layout-helper.js", () => ({ +// settleAllPromises: jest.fn(), +// })); + +// // Mock fetchCmsContentForPage +// jest.mock("@/helpers/cms-content-helper", () => ({ +// fetchCmsContentForPage: jest.fn(), +// })); + +// jest.mock("@/mixins/base-mixin", () => { +// getCmsContent: jest.fn() +// }) + +// jest.mock("@/helpers/damage-helper", () => ({ +// isGlassAvailableForCarId: jest.fn().mockImplementation(() => true), +// getDamageString: jest.fn() +// })); + +// jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({ +// navigateAfterSaveToHeritageFunnel: jest.fn() +// })); + +describe("address-questions.vue", () => { + describe("initial state", () => { + test("only street address field is shown", () => { + // Arrange + const { wrapper } = setupMocks(); + + // Assert + const streetAddressField = wrapper.findComponent({ ref: "autocomplete" }); + const cityField = wrapper.findComponent({ ref: "city" }); + const stateField = wrapper.findComponent({ ref: "state" }); + const zipCodeField = wrapper.findComponent({ ref: "zipCode" }); + + expect(streetAddressField.exists()).toBe(true); + expect(streetAddressField.isVisible()).toBe(true); + expect(cityField.exists()).toBe(true); + expect(cityField.isVisible()).toBe(false); + expect(stateField.exists()).toBe(true); + expect(stateField.isVisible()).toBe(false); + expect(zipCodeField.exists()).toBe(true); + expect(zipCodeField.isVisible()).toBe(false); + + const alerts = wrapper.findAllComponents(alert); + expect(alerts.length).toEqual(0); + }) + }); + + describe("happy paths", () => { + test.only("street address is entered, user chooses from autocomplete results => other fields are filled in", async () => { + // Arrange + // Create the `addressField1` element (autocomplete's input) + let autocompleteElement = document.createElement("input") + autocompleteElement.getPlace = jest.fn(); + document.getElementById = jest.fn().mockReturnValue(autocompleteElement); + + const { wrapper } = setupMocks({ autocompleteElement: autocompleteElement }); + await wrapper.setData({ + addressModel: { + streetAddress: "123 Test Street" + } + }) + + + // Act + autocompleteElement.dispatchEvent(new CustomEvent("place_changed", { bubbles: true })); + }) + }); +}); + +function setupMocks({ mountOptions, autocompleteElement, isShallowMount = true }) { + store.commit(storeMutations.RESET_STATE); + + const resultingMountOptions = getMountOptions({ + ...mountOptions, + router: { + navigate: jest.fn(), + navigateAfterSave: jest.fn() + }, + loadScript: jest.fn().mockResolvedValue() + }); + + // document.getElementById = jest.fn().mockImplementation(id => { + // if (id == "autocomplete") { + // console.log("get it") + // return autocompleteElement; + // } + // }) + + fillInAddressFunction = null; + + window.google = { + maps: { + event: { + addListener: jest.fn().mockImplementation((element, eventName, callbackFunction) => { + console.log("Here") + // console.log(element) + // console.log(eventName) + // console.log(callbackFunction) + // element.addEventListener(eventName, callbackFunction); + element.addEventListener(eventName, callbackFunction); + fillInAddressFunction = callbackFunction; + // console.log("Hmm") + }), + removeListener: jest.fn(), + clearInstanceListeners: jest.fn() + }, + places: { + Autocomplete: jest.fn().mockImplementation((el) => el) + } + } + }; + + + const wrapper = isShallowMount ? shallowMount(addressQuestions, resultingMountOptions) : mount(addressQuestions, resultingMountOptions); + // const wrapper = shallowMount(addressQuestions, resultingMountOptions); + // wrapper.findComponent({ ref: "autocomplete" }).element = autocompleteElement; + document.querySelector = jest.fn().mockReturnValue(document.createElement("div")); + + // console.log(wrapper.$loadScript) + // console.log(wrapper.vm.$loadScript) + // wrapper.vm.$loadScript = jest.fn().mockImplementation(resolve => new Promise(resolve({}))); + + // wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => ""); + // wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent; + // wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn(); + // wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn(); + // wrapper.vm.$refs.loadingModal.showModal = jest.fn(); + + return { wrapper }; +} \ No newline at end of file From e181315e3f3fc64ef2abf35368e97df1d9d56543 Mon Sep 17 00:00:00 2001 From: Katie Date: Fri, 3 Jun 2022 15:30:56 -0400 Subject: [PATCH 03/13] Able to pass in 'place' into fillInAddress function --- src/helpers/unit-test-helper.js | 1 + .../address-questions.spec.js | 22 ++++++---- .../address-questions/address-questions.vue | 43 +++++++++++++------ 3 files changed, 46 insertions(+), 20 deletions(-) diff --git a/src/helpers/unit-test-helper.js b/src/helpers/unit-test-helper.js index 07632f5e5..f9b4d551b 100644 --- a/src/helpers/unit-test-helper.js +++ b/src/helpers/unit-test-helper.js @@ -55,6 +55,7 @@ export function getMountOptions(mockData) { mocks.$store = mockData.store; mocks.$router = mockData.router; mocks.$route = mockData.route; + mocks.$loadScript = mockData.loadScript; const global = { mocks: mocks, diff --git a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js index 2db456aa8..ec50cb30f 100644 --- a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js +++ b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.spec.js @@ -18,7 +18,7 @@ import { storeActions } from "@/constants/store-actions"; import { storeMutations } from "@/constants/store-mutations"; import store from "@/store"; -let fillInAddressFunction; +// let fillInAddressFunction; // Mock our module for promises. // jest.mock("@/helpers/layout-helper.js", () => ({ @@ -84,14 +84,13 @@ describe("address-questions.vue", () => { } }) - // Act - autocompleteElement.dispatchEvent(new CustomEvent("place_changed", { bubbles: true })); + autocompleteElement.dispatchEvent(new CustomEvent("place_changed", { detail: "boogly" })); }) }); }); -function setupMocks({ mountOptions, autocompleteElement, isShallowMount = true }) { +function setupMocks({ mountOptions, selectedPlace, isShallowMount = true }) { store.commit(storeMutations.RESET_STATE); const resultingMountOptions = getMountOptions({ @@ -110,7 +109,9 @@ function setupMocks({ mountOptions, autocompleteElement, isShallowMount = true } // } // }) - fillInAddressFunction = null; + // fillInAddressFunction = null; + + window.google = { maps: { @@ -121,9 +122,14 @@ function setupMocks({ mountOptions, autocompleteElement, isShallowMount = true } // console.log(eventName) // console.log(callbackFunction) // element.addEventListener(eventName, callbackFunction); - element.addEventListener(eventName, callbackFunction); - fillInAddressFunction = callbackFunction; - // console.log("Hmm") + function interceptedCallbackFunction(e) { + console.log("intercepted") + console.log(e) + callbackFunction(e.detail); + } + // selectedPlace = "Woogly"; + element.addEventListener(eventName, interceptedCallbackFunction); + // fillInAddressFunction = callbackFunction; }), removeListener: jest.fn(), clearInstanceListeners: jest.fn() diff --git a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue index f188cf214..4a61da56b 100644 --- a/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue +++ b/src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue @@ -185,11 +185,13 @@ export default ({ }, methods: { setupAddressLookup() { - - if (this.addressModel.streetAddress !== null & - this.addressModel.city !== null & - this.addressModel.state !== null & - this.addressModel.zipCode !== null) { + console.log("setup") + console.log(this.addressModel) + if (this.addressModel.streetAddress && + this.addressModel.city && + this.addressModel.state && + this.addressModel.zipCode) { + console.log("AHHHH") this.showAddressFields = true; return; @@ -202,6 +204,7 @@ export default ({ this.$loadScript(`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`) .then(() => { + console.log("A") // Script is loaded, initialize the autocomplete textbox const autocomplete = new window.google.maps.places.Autocomplete( addressField1, @@ -212,15 +215,25 @@ export default ({ } ); + console.log("B") // Standard place_changed event handling const autocompleteListener = window.google.maps.event.addListener(autocomplete, 'place_changed', fillInAddress); - // Wrapping the addressField1 element in the Google Address Autocomplete object - // will cause "autocomplete='off'" which Chrome completely ignores. This event - // handler will set the value to something arbitrary so autofill doesn't work. - // https://stackoverflow.com/a/30976223 - addressField1.addEventListener("focus", () => { - addressField1.setAttribute("autocomplete", "do-not-autofill"); + console.log("C") + // console.log(addressField1) + // console.log(addressField1.id) + // console.log(autocomplete) + // console.log(autocomplete.id) + // console.log(addressField1 == autocomplete) + + + + // Wrapping the addressField1 element in the Google Address Autocomplete object + // will cause "autocomplete='off'" which Chrome completely ignores. This event + // handler will set the value to something arbitrary so autofill doesn't work. + // https://stackoverflow.com/a/30976223 + addressField1.addEventListener("focus", () => { + addressField1.setAttribute("autocomplete", "do-not-autofill"); // Make place results box stick to the input on scroll const streetAddressField = document.getElementById("streetAddressField"); @@ -228,9 +241,10 @@ export default ({ if (autocompleteResultsContainer) { streetAddressField.appendChild(autocompleteResultsContainer); } - }) + console.log("D") + addressField1.onchange = function() { const hover = document.querySelector(".pac-container .pac-item:hover"); // if an item has been clicked, do nothing, otherwise get first solution and use Geocoder to get the place @@ -260,7 +274,10 @@ export default ({ } }; + console.log("E") function fillInAddress(place) { + console.log("fillInAddress") + console.log(place) if (!place) { place = autocomplete.getPlace(); } @@ -312,6 +329,8 @@ export default ({ pacContainer.remove(); } + + console.log("F") }) .catch(() => { // Failed to fetch script From 2160a158000d61253eabd50e2502acbbb47a47d0 Mon Sep 17 00:00:00 2001 From: bmauger Date: Mon, 6 Jun 2022 15:58:44 -0400 Subject: [PATCH 04/13] CSR-668 clean up code per Mark's request. --- .../funnel-header/menu-modal/menu-modal.vue | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/common-components/funnel-header/menu-modal/menu-modal.vue b/src/common-components/funnel-header/menu-modal/menu-modal.vue index 7017947e8..5f2a97327 100644 --- a/src/common-components/funnel-header/menu-modal/menu-modal.vue +++ b/src/common-components/funnel-header/menu-modal/menu-modal.vue @@ -7,7 +7,7 @@ -