Add address-questions tests

This commit is contained in:
Katie 2022-06-07 16:07:58 -04:00
parent e181315e3f
commit f48d21f00d
3 changed files with 484 additions and 109 deletions

View file

@ -22,7 +22,6 @@ module.exports = {
// TODO REMOVE THESE AFTER WRITING UNIT TESTS // TODO REMOVE THESE AFTER WRITING UNIT TESTS
"!src/layouts/address-lookup/address-lookup.vue", "!src/layouts/address-lookup/address-lookup.vue",
"!src/layouts/address-lookup/customer-questions/customer-questions.vue", "!src/layouts/address-lookup/customer-questions/customer-questions.vue",
"!src/layouts/address-lookup/customer-questions/address-questions/address-questions.vue",
"!src/ux-components/alert\alert.vue", "!src/ux-components/alert\alert.vue",
"!src/helpers/validation-rules.js", "!src/helpers/validation-rules.js",
"!src/common-components/menu-modal/menu-modal.vue", "!src/common-components/menu-modal/menu-modal.vue",

View file

@ -1,53 +1,27 @@
// Components // Components
import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions"; import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions";
import alert from "@/ux-components/alert/alert"; 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 // 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 { mount, shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
import { storeMutations } from "@/constants/store-mutations"; import { storeMutations } from "@/constants/store-mutations";
import store from "@/store"; import store from "@/store";
// let fillInAddressFunction; let autocompleteElement;
// 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("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", () => { describe("initial state", () => {
test("only street address field is shown", () => { test("only street address field is shown", () => {
// Arrange // Arrange
const { wrapper } = setupMocks(); const { wrapper } = setupMocks({});
// Assert // Assert
const streetAddressField = wrapper.findComponent({ ref: "autocomplete" }); const streetAddressField = wrapper.findComponent({ ref: "autocomplete" });
@ -70,27 +44,467 @@ describe("address-questions.vue", () => {
}); });
describe("happy paths", () => { describe("happy paths", () => {
test.only("street address is entered, user chooses from autocomplete results => other fields are filled in", async () => { test("full street address is passed in => address fields are displayed", async () => {
// Arrange // Arrange/Act
// Create the `addressField1` element (autocomplete's input) const { wrapper } = setupMocks({
let autocompleteElement = document.createElement("input") props: {
autocompleteElement.getPlace = jest.fn(); modelValue: {
document.getElementById = jest.fn().mockReturnValue(autocompleteElement); streetAddress: "12345 Test Road",
city: "Tests",
state: "OH",
zipCode: "12312"
}
}
});
const { wrapper } = setupMocks({ autocompleteElement: autocompleteElement }); 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("full street address is passed in => don't load Google Autocomplete script", async () => {
// Arrange/Act
const { wrapper } = setupMocks({
props: {
modelValue: {
streetAddress: "12345 Test Road",
city: "Tests",
state: "OH",
zipCode: "12312"
}
}
});
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.$loadScript).not.toHaveBeenCalled();
});
test("address field is focused => disable autocomplete", async () => {
// Arrange
let focusEventCallbackFunction;
autocompleteElement.addEventListener = jest.fn().mockImplementation((eventName, callbackFunction) => {
if (eventName == "focus") {
focusEventCallbackFunction = callbackFunction;
}
});
const { wrapper } = setupMocks({});
await wrapper.vm.$nextTick();
// Act
focusEventCallbackFunction();
await wrapper.vm.$nextTick();
// Assert
expect(autocompleteElement.getAttribute("autocomplete")).toEqual("do-not-autofill");
});
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({ await wrapper.setData({
addressModel: { addressModel: {
streetAddress: "123 Test Street" 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 // Act
autocompleteElement.dispatchEvent(new CustomEvent("place_changed", { detail: "boogly" })); autocompleteElement.dispatchEvent(new CustomEvent("place_changed", { detail: selectedPlace }));
// 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
})
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 () => {
// 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"
},
displayVerificationWarning: true,
displayNoMatchWarning: true
})
let alerts = wrapper.findAllComponents(alert);
alerts.forEach(alert => expect(alert.isVisible()).toBeTruthy());
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 enters city => noMatch warning is removed", async () => {
// Arrange
const { wrapper } = setupMocks({});
wrapper.setData({
displayNoMatchWarning: true
});
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: "Somewhere"
})
await wrapper.vm.$nextTick();
// Assert
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.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"
})
await wrapper.vm.$nextTick();
// Assert
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.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"
})
await wrapper.vm.$nextTick();
// Assert
expect(wrapper.vm.displayNoMatchWarning).toBeFalsy();
noMatchAlert = wrapper.findComponent({ ref: "alertNoMatchWarning" });
expect(noMatchAlert.exists()).toBeFalsy();
});
});
}); });
}); });
function setupMocks({ mountOptions, selectedPlace, isShallowMount = true }) { function setupMocks({ mountOptions, props, isShallowMount = true, querySelectorFunction, geocoderResult = ["1234 Test Street"] }) {
store.commit(storeMutations.RESET_STATE); store.commit(storeMutations.RESET_STATE);
const resultingMountOptions = getMountOptions({ const resultingMountOptions = getMountOptions({
@ -102,59 +516,49 @@ function setupMocks({ mountOptions, selectedPlace, isShallowMount = true }) {
loadScript: jest.fn().mockResolvedValue() loadScript: jest.fn().mockResolvedValue()
}); });
// document.getElementById = jest.fn().mockImplementation(id => {
// if (id == "autocomplete") {
// console.log("get it")
// return autocompleteElement;
// }
// })
// fillInAddressFunction = null;
window.google = { window.google = {
maps: { maps: {
event: { event: {
addListener: jest.fn().mockImplementation((element, eventName, callbackFunction) => { addListener: jest.fn().mockImplementation((element, eventName, callbackFunction) => {
console.log("Here")
// console.log(element)
// console.log(eventName)
// console.log(callbackFunction)
// element.addEventListener(eventName, callbackFunction);
function interceptedCallbackFunction(e) { function interceptedCallbackFunction(e) {
console.log("intercepted")
console.log(e)
callbackFunction(e.detail); callbackFunction(e.detail);
} }
// selectedPlace = "Woogly"; // selectedPlace = "Woogly";
element.addEventListener(eventName, interceptedCallbackFunction); element.addEventListener(eventName, interceptedCallbackFunction);
// fillInAddressFunction = callbackFunction;
}), }),
removeListener: jest.fn(), removeListener: jest.fn(),
clearInstanceListeners: jest.fn() clearInstanceListeners: jest.fn()
}, },
places: { places: {
Autocomplete: jest.fn().mockImplementation((el) => el) 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); const wrapper = isShallowMount ? shallowMount(addressQuestions, resultingMountOptions) : mount(addressQuestions, resultingMountOptions);
// const wrapper = shallowMount(addressQuestions, resultingMountOptions); document.querySelector = jest.fn().mockImplementation(query => {
// wrapper.findComponent({ ref: "autocomplete" }).element = autocompleteElement; let result = null;
document.querySelector = jest.fn().mockReturnValue(document.createElement("div")); if (query == ".pac-container")
result = document.createElement("div");
else if (querySelectorFunction) {
result = querySelectorFunction(query);
}
// console.log(wrapper.$loadScript) return result ?? null;
// 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 }; return { wrapper };
} }

View file

@ -185,13 +185,10 @@ export default ({
}, },
methods: { methods: {
setupAddressLookup() { setupAddressLookup() {
console.log("setup")
console.log(this.addressModel)
if (this.addressModel.streetAddress && if (this.addressModel.streetAddress &&
this.addressModel.city && this.addressModel.city &&
this.addressModel.state && this.addressModel.state &&
this.addressModel.zipCode) { this.addressModel.zipCode) {
console.log("AHHHH")
this.showAddressFields = true; this.showAddressFields = true;
return; return;
@ -204,7 +201,6 @@ export default ({
this.$loadScript(`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`) this.$loadScript(`https://maps.googleapis.com/maps/api/js?key=${apiKey}&libraries=places`)
.then(() => { .then(() => {
console.log("A")
// Script is loaded, initialize the autocomplete textbox // Script is loaded, initialize the autocomplete textbox
const autocomplete = new window.google.maps.places.Autocomplete( const autocomplete = new window.google.maps.places.Autocomplete(
addressField1, addressField1,
@ -215,19 +211,9 @@ export default ({
} }
); );
console.log("B")
// Standard place_changed event handling // Standard place_changed event handling
const autocompleteListener = window.google.maps.event.addListener(autocomplete, 'place_changed', fillInAddress); const autocompleteListener = window.google.maps.event.addListener(autocomplete, 'place_changed', fillInAddress);
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 // Wrapping the addressField1 element in the Google Address Autocomplete object
// will cause "autocomplete='off'" which Chrome completely ignores. This event // will cause "autocomplete='off'" which Chrome completely ignores. This event
// handler will set the value to something arbitrary so autofill doesn't work. // handler will set the value to something arbitrary so autofill doesn't work.
@ -243,9 +229,7 @@ export default ({
} }
}) })
console.log("D") addressField1.addEventListener("change", () => {
addressField1.onchange = function() {
const hover = document.querySelector(".pac-container .pac-item:hover"); 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 an item has been clicked, do nothing, otherwise get first solution and use Geocoder to get the place
if (hover === null) { if (hover === null) {
@ -272,12 +256,9 @@ export default ({
self.displayNoMatchWarning = true; self.displayNoMatchWarning = true;
} }
} }
}; });
console.log("E")
function fillInAddress(place) { function fillInAddress(place) {
console.log("fillInAddress")
console.log(place)
if (!place) { if (!place) {
place = autocomplete.getPlace(); place = autocomplete.getPlace();
} }
@ -327,10 +308,7 @@ export default ({
addressField1.onchange = null; addressField1.onchange = null;
const pacContainer = document.querySelector(".pac-container"); const pacContainer = document.querySelector(".pac-container");
pacContainer.remove(); pacContainer.remove();
} }
console.log("F")
}) })
.catch(() => { .catch(() => {
// Failed to fetch script // Failed to fetch script
@ -344,14 +322,8 @@ export default ({
watch: { watch: {
addressModel: { addressModel: {
handler(newValue) { handler(newValue) {
// The first time the address model changes is w // Clear no match warning on address change
if (!newValue.city && if (newValue.city || newValue.state || newValue.zipCode) {
!newValue.state &&
!newValue.zipCode) {
return;
}
if (this.displayNoMatchWarning === true) {
this.displayNoMatchWarning = false; this.displayNoMatchWarning = false;
} }
}, },