Merge branch 'develop' into feature/CSR-439

This commit is contained in:
FrankRua 2022-06-28 11:24:12 -04:00
commit 1fae6c5bde
3 changed files with 56 additions and 87 deletions

View file

@ -15,7 +15,7 @@ const maska = jest.fn();
describe("textboxQuestion.vue", () => {
it("Should render a text input", async () => {
// Arrange
// Arrange
const wrapper = shallowMount(textboxQuestion, {
global: {
directives: {
@ -53,35 +53,7 @@ describe("textboxQuestion.vue", () => {
expect(label.text()).toContain(questionText);
});
it("Should render the 'questionText' data value with '⁠' after the first character of each word in the label text when disableAutoFill is true.", async () => {
// Arrange
const wrapper = shallowMount(textboxQuestion, {
global: {
directives: {
maska: maska,
}
},
propsData: {
disableAutoFill: true,
},
mixins: [mockMixin]
});
// Mock CMS content ...
// Trust me, the below instance of the string "Question Text" actually has the ⁠ in it. You just can't see it
// Don't believe me? Copy and paste it into Google. Then inspect the search field element in Dev Tools,
// you will see "Q⁠uestion T⁠ext"
const expectedQuestionText = "Question Text";
// Act
const label = wrapper.find("label");
// Assert
expect(label.text()).toContain(expectedQuestionText);
});
it("Should return input id as the id of the input field", async () => {
// Arrange
const wrapper = shallowMount(textboxQuestion, {
@ -103,7 +75,7 @@ describe("textboxQuestion.vue", () => {
expect(input.attributes().id).toEqual("input ID");
});
it("Should render the 'questionText' data value as the aria-label attribute.", async () => {
// Arrange
@ -193,6 +165,6 @@ describe("textboxQuestion.vue", () => {
// Assert
expect(wrapper.vm.handleChange).toHaveBeenCalled;
});
});
});

View file

@ -1,7 +1,6 @@
<template>
<div class="textbox-question" :class="(errors && errors.length) || hasError ? 'has-error' : ''">
<label :for="inputId" :aria-label="questionText" class="form-label" v-html="labelText"></label>
<!-- See https://stackoverflow.com/a/30976223 for information about "do-not-autofill" -->
<input
v-model.trim="value"
v-maska="mask"
@ -14,7 +13,6 @@
:aria-disabled="isDisabled"
:disabled="isDisabled"
:aria-required="isRequired"
autocomplete="do-not-autofill"
:class="[hasIcon ? 'has-icon' : '', iconRight ? 'icon-right' : '']"
:validationRules="validationRules"
@change="handleChange"
@ -46,7 +44,6 @@ export default {
inputId: String,
isDisabled: Boolean,
isRequired: Boolean,
disableAutoFill: Boolean,
hasIcon: Boolean, // If input has an icon
iconRight: Boolean, // Place icon on right side of text input, otherwise default is left if hasIcon prop is used
hasError: Boolean,
@ -132,7 +129,7 @@ export default {
const result = await validate(newValue, this.validationRules); // do a test validation check, without triggering full validation
if (result.valid) {
this.handleChange(newValue); // trigger full validation on this field only
}
}
},
},
};

View file

@ -9,7 +9,7 @@
<loadingModal ref="loadingModal"/>
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" ref="vehicleBanner" :displayGenericVehicleImage=false />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" ref="funnelSubHeader" />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" ref="funnelSubHeader" />
<div class="fade-on-route-transition sub-container make-tall">
<customerQuestions ref="customerQuestions" v-model="customerQuestions" />
<alert ref="alertVinNotFound" v-if="displayVinNotFoundAlert"
@ -24,14 +24,14 @@
:manualCopy="AlertMatchedDifferentVehicleBody"
alertClass="alert-warning"
v-bind:isDismissible="false"
/>
/>
<alert ref="alertNonServiceableZip" v-if="displayNonServiceableZipAlert"
class="mb-4"
alertClass="alert-danger"
:manualHeadline="AlertNonServiceableZipHeader"
:manualCopy="AlertNonServiceableZipBody"
v-bind:isDismissible="false"
/>
/>
<alert ref="alertVinLookupsByHomeAddressNotAllowed" v-if="displayVinLookupByHomeAddressNotAllowedAlert"
class="mb-4"
cmsWidgetName="AlertVinLookupsByHomeAddressNotAllowedWidget"
@ -42,11 +42,11 @@
<div class="service-zip-field" v-if="showServiceZipField" aria-live="polite">
<div class="row mb-4">
<div class="col">
<textboxQuestion cmsWidgetName="ServiceZipQuestionWidget" v-model="serviceZipCode" ref="serviceZip" inputId="7add1b26df344f2caf1678de5797803f" aria-haspopup="" mask="#####" disableAutoFill validationRules="service-zip-required|service-zip-format" />
<textboxQuestion cmsWidgetName="ServiceZipQuestionWidget" v-model="serviceZipCode" ref="serviceZip" inputId="7add1b26df344f2caf1678de5797803f" aria-haspopup="" mask="#####" validationRules="service-zip-required|service-zip-format" />
</div>
</div>
</div>
</transition>
</transition>
<funnel-footer
cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter"
@ -55,7 +55,7 @@
@back-clicked="backButtonAction"
:isForwardActionDisabled="!meta.valid"
/>
</div>
</div>
</div>
</Form>
</template>
@ -122,14 +122,14 @@ export default {
firstName: this.getRegistrationFirstNameFromStore(),
lastName: this.getRegistrationLastNameFromStore(),
emailAddress: this.getEmailFromStore(),
},
},
serviceZipCode: this.getServiceZipFromStore(),
displayNonServiceableZipAlert: false,
displayVinNotFoundAlert: false,
displayMatchedDifferentVehicleAlert: false,
displayVinLookupByHomeAddressNotAllowedAlert: false,
previouslyEnteredCarId: "",
isSelectedGlassAvailableForVehicle: false,
isSelectedGlassAvailableForVehicle: false,
customAlertData: {},
showServiceZipField: this.getServiceZipFromStore(),
isZipServicable: false,
@ -150,7 +150,7 @@ export default {
this.$route
);
},
attachCustomEvents() {
attachCustomEvents() {
this.prependActionToMethod(this, this.forwardButtonAction, () => {
this.pushEventToGA(
this.$route.query[this.queryStrings.FMG_PAGE],
@ -177,7 +177,7 @@ export default {
},
getRegistrationLastNameFromStore() {
return store.getters.vehicle.registration.lastName;
},
},
getEmailFromStore() {
return store.getters.order.customer.emailAddress;
},
@ -189,9 +189,9 @@ export default {
// Lookup VIN(s) with the provided address
const vinLookupPromise = this.lookupVin(
this.customerQuestions.lastName,
this.customerQuestions.addressQuestions.streetAddress,
this.customerQuestions.addressQuestions.zipCode,
this.customerQuestions.lastName,
this.customerQuestions.addressQuestions.streetAddress,
this.customerQuestions.addressQuestions.zipCode,
this.customerQuestions.addressQuestions.state
);
@ -200,8 +200,8 @@ export default {
const vinLookupResponse = await vinLookupPromise;
const serviceZipValidationResponse = await serviceZipValidationPromise;
if (!vinLookupResponse.data.isStatePermissible) {
if (!vinLookupResponse.data.isStatePermissible) {
// State Restrictions forbid lookup by address
this.displayVinLookupByHomeAddressNotAllowedAlert = true;
this.$refs.funnelFooter.removeLoader();
@ -210,29 +210,29 @@ export default {
// if the neither the registration zip code or service zip code are not serviceable
this.isZipServicable = serviceZipValidationResponse.data.isServiceable;
if (!this.isZipServicable) {
if (!this.isZipServicable) {
this.displayNonServiceableZipAlert = true;
this.showServiceZipField = true;
this.$refs.funnelFooter.removeLoader();
this.$refs.funnelFooter.removeLoader();
} else if (!this.serviceZipCode) {
// if the registration zip code is servicable and nothing was entered for the service zip code
// then set the service zip code to the registration zip code
this.serviceZipCode = this.customerQuestions.addressQuestions.zipCode;
}
}
const carEntered = store.getters.vehicle;
const carsFound = vinLookupResponse.data.vinVehicles;
if (carsFound.length == 0) {
// No VINs found
// No VINs found
this.displayVinNotFoundAlert = true;
this.$refs.funnelFooter.removeLoader();
return;
} else if (carsFound.length == 1) {
return;
} else if (carsFound.length == 1) {
const carFound = carsFound[0].vehicle;
this.isCarIdDifferent = carFound.carId !== carEntered.carId;
if (this.isCarIdDifferent && carFound.carId !== this.previouslyEnteredCarId) {
if (this.isCarIdDifferent && carFound.carId !== this.previouslyEnteredCarId) {
// Display Alert
this.previouslyEnteredCarId = carFound.carId;
this.customAlertData.vehicleInfo = carFound;
@ -241,12 +241,12 @@ export default {
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(carFound.carId);
// Update button "Continue with..."
this.$refs.funnelFooter.updateButtonText(`Continue with ${carFound.year} ${carFound.make} ${carFound.model}`);
this.$refs.funnelFooter.updateButtonText(`Continue with ${carFound.year} ${carFound.make} ${carFound.model}`);
this.$refs.funnelFooter.removeLoader();
return;
}
}
if (!this.isZipServicable) {
return;
}
@ -254,21 +254,21 @@ export default {
// update data if the zip or service zip is servicable
this.updateVehicleInfo(carsFound[0].vin, carFound);
this.updateCustomerInfo(serviceZipValidationResponse.data.state);
} else if (carsFound.length > 1) {
if (!this.isZipServicable) {
return;
}
// if multiple cars were found
let matchingCars = carsFound.filter(car => car.vehicle.carId === carEntered.carId);
let matchingCars = carsFound.filter(car => car.vehicle.carId === carEntered.carId);
if (matchingCars.length === 1) {
// and one and only one of them matches the carId entered, save the vehicle info
// so we can go to the Heritage Funnel directly
const matchingCar = matchingCars[0];
this.updateVehicleInfo(matchingCar.vin, matchingCar.vehicle);
}
// update data if the zip or service zip is servicable
this.updateCustomerInfo(serviceZipValidationResponse.data.state);
}
@ -277,13 +277,13 @@ export default {
},
resetWarningsAndErrors() {
this.displayVinNotFoundAlert = false;
this.displayNonServiceableZipAlert = false;
this.displayNonServiceableZipAlert = false;
this.displayMatchedDifferentVehicleAlert = false;
this.displayVinLookupByHomeAddressNotAllowedAlert = false;
},
navigateForward(carEntered, carsFound) {
this.updateServiceLocationIfNecessary();
if (carsFound.length == 1) {
// if a different vehicle is found than the one entered and the selected glass
// is not available for that vehicle
@ -297,37 +297,37 @@ export default {
}, {}
);
} else {
// otherwise
// otherwise
this.navigateForwardWithSingleCarMatch();
}
}
} else if (carsFound.length > 1) {
// if multiple cars were found
let matchingCars = carsFound.filter(car => car.vehicle.carId === carEntered.carId);
let matchingCars = carsFound.filter(car => car.vehicle.carId === carEntered.carId);
if (matchingCars.length === 1) {
// and one and only of them matches the car id entered
const matchingCar = matchingCars[0];
const matchingCar = matchingCars[0];
this.updateVehicleInfo(matchingCar.vin, matchingCar.vehicle);
this.navigateForwardWithSingleCarMatch();
} else {
// if there are no matches or there are multiple matches, navigate to "address-vehicles" page
this.$router.navigate(this.navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES, this.$route, {}, {}, carsFound);
}
}
}
},
validateZip(zip) {
return this.dispatchStoreAction(
storeActions.VALIDATE_ZIP,
storeActions.VALIDATE_ZIP,
{ zip });
},
lookupVin(lastName, streetAddress, zip, state) {
return this.dispatchStoreAction(
storeActions.LOOKUP_VIN_BY_ADDRESS,
{
licenseLastName: lastName,
licenseStreetAddress: streetAddress,
licenseZip: zip,
licenseState: state
{
licenseLastName: lastName,
licenseStreetAddress: streetAddress,
licenseZip: zip,
licenseState: state
}, false
);
},
@ -342,7 +342,7 @@ export default {
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, vehicleInfo.imageUrl);
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, vehicleInfo.imageVifNumber);
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, vehicleInfo.imageColor);
},
},
updateCustomerInfo(serviceState) {
store.commit(storeMutations.UPDATE_REGISTRATION_ADDRESS, this.customerQuestions.addressQuestions.streetAddress);
store.commit(storeMutations.UPDATE_REGISTRATION_CITY, this.customerQuestions.addressQuestions.city);
@ -350,10 +350,10 @@ export default {
store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, this.customerQuestions.addressQuestions.zipCode);
store.commit(storeMutations.UPDATE_REGISTRATION_FIRST_NAME, this.customerQuestions.firstName);
store.commit(storeMutations.UPDATE_REGISTRATION_LAST_NAME, this.customerQuestions.lastName);
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP_CODE, this.serviceZipCode);
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_STATE, serviceState);
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP_CODE, this.serviceZipCode);
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_STATE, serviceState);
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, this.customerQuestions.emailAddress);
},
},
updateServiceLocationIfNecessary() {
const serviceLocation = store.getters.order.serviceLocation;
@ -373,7 +373,7 @@ export default {
},
AlertNonServiceableZipBody() {
return this.getCmsContent("AlertNonServiceableZipWidget", "BodyText");
},
},
AlertMatchedDifferentVehicleHeader() {
const text = this.getCmsContent("AlertMatchedDifferentVehicleWidget", "HeadlineText").replaceAll("{custom:glassText}", getDamageString());
return text;
@ -386,13 +386,13 @@ export default {
const content = this.getCmsContent("AlertMatchedDifferentVehicleWidget", "BodyText")
.replaceAll("{custom:glassText}", getDamageString())
.replaceAll("{custom:vinYmmFound}", vinYmmFound)
.replaceAll("{custom:vinYmmExpected}", vinYmmExpected);
.replaceAll("{custom:vinYmmExpected}", vinYmmExpected);
return content;
},
},
},
watch: {
customerQuestions: {
customerQuestions: {
handler(newValue) {
// if they modify one of the lookup fields (address, city, state, zipCode, or lastName), then modify the button text back to Get my personalized quote
this.$refs.funnelFooter.updateButtonText(this.getCmsContent("FunnelFooterWidget", "ForwardButtonText"));
@ -412,8 +412,8 @@ export default {
// if the Service Zip Code field is ever hidden, clear out it's value
if (!newValue) {
this.serviceZipCode = null;
}
},
}
},
}
},
components: {