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 c8d24c57e..cf620b15f 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 @@ -7,6 +7,7 @@ import { mount, shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { storeMutations } from "@/constants/store-mutations"; import store from "@/store"; +import { createImportSpecifier } from "typescript"; let autocompleteElement; describe("address-questions.vue", () => { @@ -18,7 +19,7 @@ describe("address-questions.vue", () => { }); describe("initial state", () => { - test("only street address field is shown", () => { + test("Should only show the street address field", () => { // Arrange const { wrapper } = setupMocks({}); @@ -58,7 +59,7 @@ describe("address-questions.vue", () => { expect(zipCode.exists()).toBe(true); }); - test("Should it set this.showAddressFields to true when the model is prepopulated", async () => { + test("Should set this.showAddressFields to true when the model is prepopulated", async () => { // Arrange // Act const newAddressModel = { @@ -305,38 +306,6 @@ describe("address-questions.vue", () => { }); describe("alerts", () => { - const places = [null, { address_components: null }, undefined, {}]; - test.each(places)( - "selected place/place properties is null => display verification alert", - async (place) => { - // Arrange - const { wrapper } = setupMocks({}); - await wrapper.setData({ - addressModel: { - streetAddress: "123 Test Street", - }, - }); - - const selectedPlace = place; - - // Act - autocompleteElement.dispatchEvent( - new CustomEvent("place_changed", { detail: selectedPlace }) - ); - await wrapper.vm.$nextTick(); - - // Assert - const verificationAlert = wrapper.findComponent({ - ref: "alertVerificationWarning", - }); - expect(verificationAlert.exists()).toBe(true); - expect(verificationAlert.isVisible()).toBe(true); - - const noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBe(false); - } - ); - test("user enters address that yields no autocomplete results => show noMatch alert", async () => { // Arrange let changeEventCallbackFunction; @@ -413,110 +382,110 @@ describe("address-questions.vue", () => { describe("noMatch alert is cleared on address change", () => { test("user sees noMatch warning and modifies street address => noMatch warning is removed", async () => { // Arrange - const { wrapper } = setupMocks({}); + const { wrapper } = setupMocks({ + props: { + modelValue: { + streetAddress: "LS", + city: "", + state: "", + zipCode: "", + }, + }, + }); + // Initial condition to display noMatch warning await wrapper.setData({ matchFound: false, }); + + // Act + wrapper.vm.addressModel.streetAddress = "LJS"; await wrapper.vm.$nextTick(); - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeTruthy(); - expect(noMatchAlert.isVisible()).toBeTruthy(); - - // // Act - wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { - streetAddress: "LS", - }); - // Assert - wrapper.vm.$nextTick(function () { - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - }); + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); }); test("user sees noMatch warning and enters city => noMatch warning is removed", async () => { // Arrange - const { wrapper } = setupMocks({}); + const { wrapper } = setupMocks({ + props: { + modelValue: { + streetAddress: "", + city: "LS", + state: "", + zipCode: "", + }, + }, + }); + // Initial condition to display noMatch warning await wrapper.setData({ matchFound: false, }); + + // Act + wrapper.vm.addressModel.city = "LJS"; await wrapper.vm.$nextTick(); - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeTruthy(); - expect(noMatchAlert.isVisible()).toBeTruthy(); - - // // Act - wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { - city: "LS", - }); - // Assert - wrapper.vm.$nextTick(function () { - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - }); + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); }); test("user sees noMatch warning and enters state => noMatch warning is removed", async () => { // Arrange - const { wrapper } = setupMocks({}); + const { wrapper } = setupMocks({ + props: { + modelValue: { + streetAddress: "", + city: "", + state: "LS", + zipCode: "", + }, + }, + }); + // Initial condition to display noMatch warning await wrapper.setData({ matchFound: false, }); + + // Act + wrapper.vm.addressModel.state = "LJS"; await wrapper.vm.$nextTick(); - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeTruthy(); - expect(noMatchAlert.isVisible()).toBeTruthy(); - - // // Act - wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { - state: "KO", - }); - // Assert - wrapper.vm.$nextTick(function () { - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - }); + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); }); test("user sees noMatch warning and enters zip code => noMatch warning is removed", async () => { // Arrange - const { wrapper } = setupMocks({}); + const { wrapper } = setupMocks({ + props: { + modelValue: { + streetAddress: "", + city: "", + state: "", + zipCode: "LS", + }, + }, + }); + // Initial condition to display noMatch warning await wrapper.setData({ matchFound: false, }); + + // Act + wrapper.vm.addressModel.zipCode = "LJS"; await wrapper.vm.$nextTick(); - let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeTruthy(); - expect(noMatchAlert.isVisible()).toBeTruthy(); - - // // Act - wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { - zipCode: "12345", - }); - // Assert - wrapper.vm.$nextTick(function () { - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); - }); + let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); }); }); }); @@ -576,6 +545,7 @@ function setupMocks({ const wrapper = isShallowMount ? shallowMount(addressQuestions, resultingMountOptions) : mount(addressQuestions, resultingMountOptions); + document.querySelector = jest.fn().mockImplementation((query) => { let result = null; if (query == ".pac-container") result = document.createElement("div"); 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 ba45ea133..23bc9280c 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 @@ -78,6 +78,7 @@ import { applicationConfig } from "@/constants/application-config.js"; import { defineRule } from "vee-validate"; import { required, regex } from "@/helpers/validation-rules"; import { errorMessages } from "@/constants/error-messages"; +import { fill } from "lodash"; // DEFINE VALIDATION RULES defineRule("street-address-required", required(errorMessages.STREET_ADDRESS_REQUIRED)); @@ -110,10 +111,7 @@ export default { alertCopyVerificationWarning: "", alertHeadlineNoMatchWarning: "", alertCopyNoMatchWarning: "", - matchingIndirectly: false, matchFound: null, // null = no attempted match, true = match was found, false = match was not found - enterPressed: false, - isAddressWatchActive: false, // Only deep watch the address model when a match was not found }; }, computed: { @@ -235,27 +233,25 @@ export default { }); addressField1.addEventListener("keydown", (e) => { - if (e.code === "Enter" || e.code === "NumpadEnter" || e.code === "Tab") { - if (e.code === "Tab") { - self.matchingIndirectly = true; + if (e.code === "Enter" || e.code === "NumpadEnter") { + const selectedItem = document.querySelector( + ".pac-container .pac-item-selected" + ); + if (selectedItem !== null) { + // Fill-in the address using selected item in the list. + fillInAddress(selectedItem); } else { - self.enterPressed = true; + // Fill-in the address using first item in the list. + fillInAddressUsingFirstItem(); } - - addressField1.blur(); } else { return; } }); addressField1.addEventListener("change", () => { - // NOTE: The "place_changed" event of the autocomplete fires after this and will use either the address the user had chosen - // using either the down / up arrows or the address the user was hovering over when they pressed "Enter." - - // If a match has been previously found then do nothing - // OR - // If the user pressed "Enter" then do nothing - if (self.matchFound || self.enterPressed) { + // If a match has been previously attempted then do nothing + if (self.matchFound !== null) { return; } @@ -267,29 +263,32 @@ export default { // If the Street Address field changed without clicking (i.e. by pressing Tab, or clicking outside the field) if (clickedAddress === null) { // Fill-in the address using first item in the list. - const item = document.querySelector(".pac-container .pac-item"); - if (item != null) { - self.matchingIndirectly = true; - - const firstResult = item.textContent; - const geocoder = new window.google.maps.Geocoder(); - geocoder.geocode( - { - address: firstResult, - }, - function (results, status) { - if (status === window.google.maps.GeocoderStatus.OK) { - fillInAddress(results[0]); - } - } - ); - } else { - // No addresses found for the input - self.matchFound = false; - } + fillInAddressUsingFirstItem(); } }); + function fillInAddressUsingFirstItem() { + // Fill-in the address using first item in the list. + const item = document.querySelector(".pac-container .pac-item"); + if (item != null) { + const firstResult = item.textContent; + const geocoder = new window.google.maps.Geocoder(); + geocoder.geocode( + { + address: firstResult, + }, + function (results, status) { + if (status === window.google.maps.GeocoderStatus.OK) { + fillInAddress(results[0]); + self.displayVerificationWarning = true; + } + } + ); + } else { + self.matchFound = false; + } + } + function fillInAddress(place) { if (!place) { place = autocomplete.getPlace(); @@ -297,6 +296,7 @@ export default { if (place && place.address_components) { self.matchFound = true; + self.addressModel.streetAddress = ""; self.$nextTick(function () { self.showAddressFields = true; @@ -329,8 +329,6 @@ export default { } } - self.displayVerificationWarning = self.matchingIndirectly; - // after showing the address fields, disable the address autocomplete window.google.maps.event.removeListener(autocompleteListener); window.google.maps.event.clearInstanceListeners(autocomplete); @@ -340,8 +338,6 @@ export default { pacContainer.remove(); } }); - } else { - self.displayVerificationWarning = true; } } }) @@ -357,6 +353,13 @@ export default { watch: { matchFound: { handler(newValue) { + if (newValue === null) { + this.displayNoMatchWarning = false; + this.displayVerificationWarning = false; + this.unwatchAddress(); + return; + } + if (!newValue) { this.displayNoMatchWarning = true; @@ -366,22 +369,19 @@ export default { this.showAddressFields = true; this.displayVerificationWarning = false; - this.$nextTick(function () { - // Only deep watch the Address Model after a failed match - this.isAddressWatchActive = true; - }); + // Only deep watch the Address Model after a failed match + this.unwatchAddress = this.$watch( + "addressModel", + () => { + // When the address model changes reset to "no attempted match" + this.matchFound = null; + this.$nextTick(); + }, + { deep: true, flush: "post" } + ); } }, }, - addressModel: { - handler() { - if (this.isAddressWatchActive) { - this.displayNoMatchWarning = false; - this.isAddressWatchActive = false; - } - }, - deep: true, - }, }, components: { textboxQuestion,