From 9c6b2793c77bd263e2051a21ebeb3d47ba0eeb95 Mon Sep 17 00:00:00 2001 From: Mark Harris Date: Wed, 28 Dec 2022 09:37:37 -0500 Subject: [PATCH 1/2] Update azure-pipelines.yml for Azure Pipelines --- azure-pipelines.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f6d88a03d..777122777 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -117,7 +117,7 @@ stages: jobs: - deployment: testBuildDeployment displayName: Build and Deploy FMG - Test - environment: digitalCloud-sys + environment: NoApproval-All container: node workspace: clean: all From f11ffede1436867ebb20a3edf2649664c1f05eea Mon Sep 17 00:00:00 2001 From: Leah Schumann <91956385+lschumann1978@users.noreply.github.com> Date: Wed, 28 Dec 2022 13:20:25 -0500 Subject: [PATCH 2/2] Revert "Merge pull request #897 from Safelite/revert-934-defect" This reverts commit a03c05717f18a5c16170d6f593f74eca3a3183e4. --- .../address-questions.spec.js | 193 ++++++------------ .../address-questions/address-questions.vue | 159 ++++++++++----- 2 files changed, 167 insertions(+), 185 deletions(-) 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 3a7007a0f..c8d24c57e 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 @@ -5,7 +5,6 @@ import alert from "@/ux-components/alert/alert"; // Supporting Files 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"; @@ -59,35 +58,6 @@ describe("address-questions.vue", () => { expect(zipCode.exists()).toBe(true); }); - test("Should set this.displayNoMatchWarning to false when it is set to true, if the model if prepopulated", async () => { - // Arrange - const newAddressModel = { - streetAddress: "foo", - city: "foo", - state: "foo", - zipCode: "55555", - }; - const wrapper = shallowMount(addressQuestions, { - propsData: { - modelValue: newAddressModel, - }, - }); - - await wrapper.setData({ - displayNoMatchWarning: true, - }); - expect(wrapper.vm.displayNoMatchWarning).toBeTruthy(); - - // Act - wrapper.vm.$options.watch.addressModel.handler.call( - wrapper.vm, - wrapper.vm.addressModel - ); - - // Assert - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - }); - test("Should it set this.showAddressFields to true when the model is prepopulated", async () => { // Arrange // Act @@ -239,81 +209,13 @@ describe("address-questions.vue", () => { ); // Assert - const addressModel = wrapper.vm.addressModel; - expect(addressModel.streetAddress).toEqual("1234 Test Road"); - expect(addressModel.city).toEqual("Columbus"); - expect(addressModel.state).toEqual("OH"); - expect(addressModel.zipCode).toEqual("43215"); - }); - - test("street address is entered, user chooses good result from autocomplete results => alerts are cleared", async () => { - // Arrange - const { wrapper } = setupMocks({}); - await wrapper.setData({ - addressModel: { - streetAddress: "123 Test Street", - }, - displayVerificationWarning: true, - displayNoMatchWarning: true, + wrapper.vm.$nextTick(function () { + const addressModel = wrapper.vm.addressModel; + expect(addressModel.streetAddress).toEqual("1234 Test Road"); + expect(addressModel.city).toEqual("Columbus"); + expect(addressModel.state).toEqual("OH"); + expect(addressModel.zipCode).toEqual("43215"); }); - - let alerts = wrapper.findAllComponents(alert); - alerts.forEach((alert) => expect(alert.isVisible()).toBeTruthy()); - - const selectedPlace = { - address_components: [ - { - long_name: "1234", - short_name: "1234", - types: ["street_number"], - }, - { - long_name: "Test Road", - short_name: "Test Road", - types: ["route"], - }, - { - long_name: "East Columbus", - short_name: "Columbus", - types: ["neighborhood", "political"], - }, - { - long_name: "Columbus", - short_name: "Columbus", - types: ["locality", "political"], - }, - { - long_name: "Franklin County", - short_name: "Franklin County", - types: ["administrative_area_level_2", "political"], - }, - { - long_name: "Ohio", - short_name: "OH", - types: ["administrative_area_level_1", "political"], - }, - { - long_name: "United States", - short_name: "US", - types: ["country", "political"], - }, - { - long_name: "43215", - short_name: "43215", - types: ["postal_code"], - }, - ], - }; - - // Act - autocompleteElement.dispatchEvent( - new CustomEvent("place_changed", { detail: selectedPlace }) - ); - await wrapper.vm.$nextTick(); - - // Assert - alerts = wrapper.findAllComponents(alert); - alerts.forEach((alert) => expect(alert.exists()).toBeFalsy()); }); test("street address is entered, but user clicks away => first result is selected and other fields are filled in", async () => { @@ -413,13 +315,8 @@ describe("address-questions.vue", () => { addressModel: { streetAddress: "123 Test Street", }, - displayVerificationWarning: true, - displayNoMatchWarning: true, }); - let alerts = wrapper.findAllComponents(alert); - alerts.forEach((alert) => expect(alert.isVisible()).toBeTruthy()); - const selectedPlace = place; // Act @@ -434,6 +331,7 @@ describe("address-questions.vue", () => { }); expect(verificationAlert.exists()).toBe(true); expect(verificationAlert.isVisible()).toBe(true); + const noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); expect(noMatchAlert.exists()).toBe(false); } @@ -513,12 +411,12 @@ describe("address-questions.vue", () => { }); describe("noMatch alert is cleared on address change", () => { - test("user sees noMatch warning and enters city => noMatch warning is removed", async () => { + test("user sees noMatch warning and modifies street address => noMatch warning is removed", async () => { // Arrange const { wrapper } = setupMocks({}); - wrapper.setData({ - displayNoMatchWarning: true, + await wrapper.setData({ + matchFound: false, }); await wrapper.vm.$nextTick(); @@ -526,24 +424,53 @@ describe("address-questions.vue", () => { expect(noMatchAlert.exists()).toBeTruthy(); expect(noMatchAlert.isVisible()).toBeTruthy(); - // Act + // // Act wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { - city: "Somewhere", + streetAddress: "LS", + }); + + // Assert + wrapper.vm.$nextTick(function () { + expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); + + 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({}); + + await wrapper.setData({ + matchFound: false, }); 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 - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); + wrapper.vm.$nextTick(function () { + expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); + + 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({}); - wrapper.setData({ - displayNoMatchWarning: true, + await wrapper.setData({ + matchFound: false, }); await wrapper.vm.$nextTick(); @@ -551,24 +478,26 @@ describe("address-questions.vue", () => { expect(noMatchAlert.exists()).toBeTruthy(); expect(noMatchAlert.isVisible()).toBeTruthy(); - // Act + // // Act wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { state: "KO", }); - await wrapper.vm.$nextTick(); // Assert - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); + wrapper.vm.$nextTick(function () { + expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); + + 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({}); - wrapper.setData({ - displayNoMatchWarning: true, + await wrapper.setData({ + matchFound: false, }); await wrapper.vm.$nextTick(); @@ -576,16 +505,18 @@ describe("address-questions.vue", () => { expect(noMatchAlert.exists()).toBeTruthy(); expect(noMatchAlert.isVisible()).toBeTruthy(); - // Act + // // Act wrapper.vm.$options.watch.addressModel.handler.call(wrapper.vm, { zipCode: "12345", }); - await wrapper.vm.$nextTick(); // Assert - expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); - noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); - expect(noMatchAlert.exists()).toBeFalsy(); + wrapper.vm.$nextTick(function () { + expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); + + noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); + expect(noMatchAlert.exists()).toBeFalsy(); + }); }); }); }); 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 74c1866f1..4ad515fbb 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 @@ -12,7 +12,8 @@ aria-haspopup="" hasIcon disableAutoFill - validationRules="street-address-required" /> + validationRules="street-address-required" + @keydown.enter.prevent /> @@ -109,6 +110,10 @@ 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: { @@ -213,11 +218,11 @@ export default { 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", () => { + // 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.setAttribute("autocomplete", "do-not-autofill"); // Make place results box stick to the input on scroll @@ -229,12 +234,43 @@ export default { } }); + addressField1.addEventListener("keydown", (e) => { + if (e.code === "Enter" || e.code === "NumpadEnter" || e.code === "Tab") { + if (e.code === "Tab") { + self.matchingIndirectly = true; + } else { + self.enterPressed = true; + } + + addressField1.blur(); + } else { + return; + } + }); + addressField1.addEventListener("change", () => { - 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 - if (hover === null) { + // 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) { + return; + } + + // Get the address that the user clicked on (if any) + const clickedAddress = document.querySelector( + ".pac-container .pac-item:hover" + ); + + // 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( @@ -244,18 +280,12 @@ export default { function (results, status) { if (status === window.google.maps.GeocoderStatus.OK) { fillInAddress(results[0]); - self.displayVerificationWarning = true; - self.displayNoMatchWarning = false; } } ); } else { - self.addressModel.city = ""; - self.addressModel.state = ""; - self.addressModel.zipCode = ""; - self.showAddressFields = true; - self.displayVerificationWarning = false; - self.displayNoMatchWarning = true; + // No addresses found for the input + self.matchFound = false; } } }); @@ -266,50 +296,53 @@ export default { } if (place && place.address_components) { + self.matchFound = true; self.addressModel.streetAddress = ""; - self.showAddressFields = true; + self.$nextTick(function () { + self.showAddressFields = true; - for (const component of place.address_components) { - const componentType = component.types[0]; + for (const component of place.address_components) { + const componentType = component.types[0]; - switch (componentType) { - case "street_number": { - self.addressModel.streetAddress = component.long_name; - break; - } - case "route": { - self.addressModel.streetAddress += - " " + component.short_name; - break; - } - case "locality": { - self.addressModel.city = component.long_name; - break; - } - case "administrative_area_level_1": { - self.addressModel.state = component.short_name; - break; - } - case "postal_code": { - self.addressModel.zipCode = component.long_name; - break; + switch (componentType) { + case "street_number": { + self.addressModel.streetAddress = component.long_name; + break; + } + case "route": { + self.addressModel.streetAddress += + " " + component.short_name; + break; + } + case "locality": { + self.addressModel.city = component.long_name; + break; + } + case "administrative_area_level_1": { + self.addressModel.state = component.short_name; + break; + } + case "postal_code": { + self.addressModel.zipCode = component.long_name; + break; + } } } - } - self.displayVerificationWarning = false; - self.displayNoMatchWarning = false; + 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); + addressField1.onchange = null; + const pacContainer = document.querySelector(".pac-container"); + if (pacContainer) { + pacContainer.remove(); + } + }); } else { self.displayVerificationWarning = true; - self.displayNoMatchWarning = false; } - - // after showing the address fields, disable the address autocomplete - window.google.maps.event.removeListener(autocompleteListener); - window.google.maps.event.clearInstanceListeners(autocomplete); - addressField1.onchange = null; - const pacContainer = document.querySelector(".pac-container"); - pacContainer.remove(); } }) .catch(() => { @@ -322,11 +355,29 @@ export default { this.setupAddressLookup(); }, watch: { - addressModel: { + matchFound: { handler(newValue) { - // Clear no match warning on address change - if (newValue.city || newValue.state || newValue.zipCode) { + if (!newValue) { + this.displayNoMatchWarning = true; + + this.addressModel.city = ""; + this.addressModel.state = ""; + this.addressModel.zipCode = ""; + this.showAddressFields = true; + this.displayVerificationWarning = false; + + this.$nextTick(function () { + // Only deep watch the Address Model after a failed match + this.isAddressWatchActive = true; + }); + } + }, + }, + addressModel: { + handler() { + if (this.isAddressWatchActive) { this.displayNoMatchWarning = false; + this.isAddressWatchActive = false; } }, deep: true,