// Components import addressQuestions from "@/common-components/address-questions/address-questions"; // Supporting Files import { mount, shallowMount } from "@vue/test-utils"; import { getMountOptions } from "@/helpers/unit-test-helper.js"; let autocompleteElement; describe("address-questions.vue", () => { beforeEach(() => { // Create the `addressField1` element (autocomplete's input) autocompleteElement = document.createElement("input"); autocompleteElement.getPlace = jest.fn(); document.getElementById = jest.fn().mockReturnValue(autocompleteElement); }); describe("initial state", () => { test("Should render addressQuestions sub-components (textbox-questions and dropdown-questions)", async () => { // Arrange const { wrapper } = setupMocks({}); // 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); }); }); describe("happy paths", () => { test("full street address is passed in => address fields are displayed", async () => { // Arrange/Act const { wrapper } = setupMocks({ props: { modelValue: { streetAddress: "12345 Test Road", city: "Tests", state: "OH", zipCode: "12312", }, }, }); await wrapper.vm.$nextTick(); // Assert const cityField = wrapper.findComponent({ ref: "city" }); const stateField = wrapper.findComponent({ ref: "state" }); const zipField = wrapper.findComponent({ ref: "zipCode" }); expect(cityField.exists()).toBeTruthy(); expect(cityField.isVisible()).toBeTruthy(); expect(stateField.exists()).toBeTruthy(); expect(cityField.isVisible()).toBeTruthy(); expect(zipField.exists()).toBeTruthy(); expect(cityField.isVisible()).toBeTruthy(); }); test("street address is entered, user chooses good result from autocomplete results => other fields are filled in", async () => { // Arrange const { wrapper } = setupMocks({}); await wrapper.setData({ addressModel: { streetAddress: "123 Test Street", }, }); 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 }) ); // Assert 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"); }); }); test("street address is entered, but user clicks away => first result is selected and other fields are filled in", async () => { // Arrange let changeEventCallbackFunction; autocompleteElement.addEventListener = jest .fn() .mockImplementation((eventName, callbackFunction) => { if (eventName == "change") { changeEventCallbackFunction = callbackFunction; } }); const { wrapper } = setupMocks({ querySelectorFunction: function (query) { if (query == ".pac-container .pac-item") { let element = document.createElement("div"); element.textContent = "123 Test Street"; return element; } }, geocoderResult: { 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"], }, ], }, }); let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); let verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); expect(noMatchAlert.exists()).toBeFalsy(); expect(verificationAlert.exists()).toBeFalsy(); await wrapper.vm.$nextTick(); // Act changeEventCallbackFunction(); await wrapper.vm.$nextTick(); // 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"); }); }); 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; autocompleteElement.addEventListener = jest .fn() .mockImplementation((eventName, callbackFunction) => { if (eventName == "change") { changeEventCallbackFunction = callbackFunction; } }); const { wrapper } = setupMocks({}); let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); expect(noMatchAlert.exists()).toBeFalsy(); await wrapper.vm.$nextTick(); // Act changeEventCallbackFunction(); await wrapper.vm.$nextTick(); // Assert expect(wrapper.vm.displayNoMatchWarning).toBeTruthy(); noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); expect(noMatchAlert.exists()).toBeTruthy(); expect(noMatchAlert.isVisible()).toBeTruthy(); }); test("user enters address that yields autocomplete results, but doesn't select => show verification alert", async () => { // Arrange let changeEventCallbackFunction; autocompleteElement.addEventListener = jest .fn() .mockImplementation((eventName, callbackFunction) => { if (eventName == "change") { changeEventCallbackFunction = callbackFunction; } }); const { wrapper } = setupMocks({ querySelectorFunction: function (query) { if (query == ".pac-container .pac-item") { let element = document.createElement("div"); element.textContent = "123 Test Street"; return element; } }, }); let noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); let verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); expect(noMatchAlert.exists()).toBeFalsy(); expect(verificationAlert.exists()).toBeFalsy(); await wrapper.vm.$nextTick(); // Act changeEventCallbackFunction(); await wrapper.vm.$nextTick(); // Assert verificationAlert = wrapper.findComponent({ ref: "alertVerificationWarning" }); expect(wrapper.vm.displayVerificationWarning).toBeTruthy(); expect(verificationAlert.exists()).toBeTruthy(); expect(verificationAlert.isVisible()).toBeTruthy(); noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); expect(noMatchAlert.exists()).toBeFalsy(); }); 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({}); 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, { 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 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({}); 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, { state: "KO", }); // 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 zip code => 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, { zipCode: "12345", }); // Assert wrapper.vm.$nextTick(function () { expect(wrapper.vm.displayNoMatchWarning).toBeFalsy(); noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" }); expect(noMatchAlert.exists()).toBeFalsy(); }); }); }); }); }); function setupMocks({ mountOptions, props, isShallowMount = true, querySelectorFunction, geocoderResult = ["1234 Test Street"], }) { const resultingMountOptions = getMountOptions({ ...mountOptions, router: { navigate: jest.fn(), navigate: jest.fn(), }, loadScript: jest.fn().mockResolvedValue(), }); window.google = { maps: { event: { addListener: jest .fn() .mockImplementation((element, eventName, callbackFunction) => { function interceptedCallbackFunction(e) { callbackFunction(e.detail); } // selectedPlace = "Woogly"; element.addEventListener(eventName, interceptedCallbackFunction); }), removeListener: jest.fn(), clearInstanceListeners: jest.fn(), }, places: { Autocomplete: jest.fn().mockImplementation((el) => el), }, Geocoder: class Geocoder { // constructor(); geocode(request, callback) { callback([geocoderResult], true); } }, GeocoderStatus: { OK: true, }, }, }; if (props) resultingMountOptions.propsData = props; 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"); else if (querySelectorFunction) { result = querySelectorFunction(query); } return result ?? null; }); return { wrapper }; }