Merge pull request #320 from Safelite/feature/CSR-92

Feature/csr 92
This commit is contained in:
max-dempsey 2022-04-06 10:02:03 -04:00 committed by GitHub
commit dbda924572
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 704 additions and 1086 deletions

View file

@ -13,6 +13,8 @@ module.exports = {
"!src/helpers/unit-test-helper.js",
"!src/layouts/component-test/component-test.vue",
"!src/layouts/form-test/form-test.vue",
"!src/layouts/license-plate-lookup/license-plate-lookup.vue",
"!src/layouts/vin-lookup/vin-lookup.vue",
"!src/layouts/vehicle-damage/windshield-damage-type-question/windshield-damage-type-question.vue",
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
"!src/layouts/address-poc/address-poc.vue",
@ -32,7 +34,8 @@ module.exports = {
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
coverageThreshold: {
global: {
statements: 89,
statements: 88,
// Got the go ahead from Mark to temporarily lower this. Taking out initialize component made the year,make,model and style coverage drop a bit. Once unit tests for license plate lookup, vin lookup and address lookup are in the coverage should go back up to 90
},
},
};

View file

@ -16,8 +16,8 @@ describe("funnel-footer.vue", () => {
});
const wrapper = mount(funnelFooter, {
mixins: [mockMixin]
});
wrapper.vm.initializeComponent(cmsContent);
// Assert
const link = wrapper.find("a");
@ -30,6 +30,7 @@ describe("funnel-footer.vue", () => {
it("Should emit ForwardClicked on button click", async () => {
// Act
const wrapper = mount(funnelFooter, {
mixins: [mockMixin]
});
wrapper.vm.buttonClick();
// Assert
@ -39,16 +40,28 @@ describe("funnel-footer.vue", () => {
it("Should emit BackClicked on link click", async () => {
// Act
const wrapper = mount(funnelFooter, {
mixins: [mockMixin]
});
wrapper.vm.linkClick();
// Assert
expect(wrapper.emitted()["BackClicked"][0]).toHaveBeenCalled;
});
it("Should change button text when update button text is called", async () => {
// Act
const wrapper = mount(funnelFooter, {
mixins: [mockMixin]
});
wrapper.vm.updateButtonText('newText');
// Assert
expect(wrapper.componentVM.customButtontext).toBe('newText');
});
});
//Mock CMS content
const cmsContent = {
backLink: "text",
buttonText: "text",
};
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}

View file

@ -18,6 +18,7 @@
<div class="row d-flex flex-row-reverse align-items-center">
<div class="col button-col d-flex" id="stacked">
<buttonMain
ref="buttonMain"
isPrimary
:buttonText="buttonText"
loaderColor="white"
@ -48,6 +49,7 @@ export default {
name: "funnelFooter",
props: {
isDisabled: Boolean,
cmsWidgetName: String,
},
components: {
textLink,
@ -56,8 +58,7 @@ export default {
data() {
return {
paddingHeight: 0,
backLink: "",
buttonText: "",
customButtontext: '',
}
},
mounted() {
@ -69,13 +70,23 @@ export default {
beforeUnmount() {
window.removeEventListener('resize', this.onResize);
},
computed: {
backLink(){
return this.getCmsContent(this.cmsWidgetName, 'BackButtonText');
},
buttonText(){
return this.customButtontext ? this.customButtontext : this.getCmsContent(this.cmsWidgetName, 'ForwardButtonText');
}
},
methods: {
onResize() {
this.paddingHeight = document.querySelector(".footer #infoBox").offsetHeight;
},
initializeComponent(cmsContent) {
this.backLink = cmsContent.BackButtonText;
this.buttonText = cmsContent.ForwardButtonText;
updateButtonText(newText) {
this.customButtontext = newText;
},
removeLoader(){
this.$refs.buttonMain.removeLoader();
},
buttonClick() {
this.$emit("ForwardClicked");

View file

@ -10,8 +10,8 @@ describe("funnelHeader", () => {
setData: {
imageSrc: "image_url",
},
mixins: [mockMixin]
});
wrapper.vm.initializeComponent(cmsContent);
// Assert
expect(wrapper.find("img")).toBeTruthy();
@ -19,7 +19,8 @@ describe("funnelHeader", () => {
});
});
//Mock CMS content
const cmsContent = {
imageSrc: "image_url",
};
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}

View file

@ -24,7 +24,6 @@ export default {
name: "funnel-header",
data() {
return {
imageSrc: "",
displayGlobalAlert: false,
globalAlertMessage: {
isDismissible: false,
@ -34,10 +33,13 @@ export default {
},
};
},
methods: {
initializeComponent(cmsContent) {
this.imageSrc = cmsContent.LogoImage;
},
props: {
cmsWidgetName: String,
},
computed: {
imageSrc(){
return this.getCmsContent(this.cmsWidgetName, 'LogoImage');
}
},
components: {
alert,

View file

@ -4,19 +4,18 @@ import FunnelSubHeader from "./funnel-sub-header";
describe("FunnelSubHeader.vue", () => {
it("Should render the 'text' data value as a span value for the header span text value.", async () => {
// Act
const wrapper = shallowMount(FunnelSubHeader);
await wrapper.setData({
text: "FunnelSubHeader Content",
const wrapper = shallowMount(FunnelSubHeader, {
mixins: [mockMixin]
});
wrapper.vm.initializeComponent(cmsContent);
wrapper.vm.clickEvent();
// Assert
expect(wrapper.find("h5").text()).toContain("FunnelSubHeader Content");
wrapper.unmount();
expect(wrapper.emitted()).toEqual({"click-event": [[]]});
});
});
//Mock CMS content
const cmsContent = {
HeaderText: "FunnelSubHeader Content",
};
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}

View file

@ -1,7 +1,7 @@
<template>
<div class="current_car_info-text">
<div class="d-flex align-items-center justify-content-center container-fluid overflow-hidden">
<h5 class="text-center fw-normal mb-0" :class="hasSubText ? 'dark-header' : 'light-header'">
<h5 class="text-center fw-normal mb-0" :class="headerColor">
<span>
{{ text }}
</span>
@ -27,32 +27,29 @@ import buttonBack from "@/common-components/funnel-sub-header/button-back/button
export default {
name: "FunnelSubHeader",
data() {
return {
text: "",
subText: "",
};
},
props: {
hasBackButton: Boolean,
backButtonAccessibleText: String
backButtonAccessibleText: String,
cmsWidgetName: String,
},
components: {
buttonBack,
},
computed: {
hasSubText(){
return this.subText.length > 0;
text(){
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
},
subText(){
return this.getCmsContent(this.cmsWidgetName, 'HeaderSubText');
},
headerColor(){
return this.subText ? 'dark-header' : 'light-header';
}
},
methods: {
clickEvent() {
this.$emit("click-event");
},
initializeComponent(cmsContent) {
this.text = cmsContent.HeaderText;
this.subText = cmsContent.HeaderSubText;
},
},
};
</script>

View file

@ -51,6 +51,7 @@ export default {
default: '',
},
validationRules: String,
cmsWidgetName: String,
},
setup(props) {
const fieldOptions = {
@ -74,17 +75,10 @@ export default {
meta,
};
},
data() {
return {
questionText: "",
}
},
methods: {
initializeComponent(cmsContent){
this.questionText = cmsContent;
},
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
value: {
get: function() {
return this.modelValue;

View file

@ -15,13 +15,10 @@ jest.mock(
describe("vehicleBanner", () => {
test("renders the blurrycar image", async () => {
// Arrange
const { wrapper, cmsContent } = setupMocks({ displayGenericVehicleImageProp: true, imageUrlValue: "NULL" });
const { wrapper } = setupMocks({ displayGenericVehicleImageProp: true, imageUrlValue: "NULL" });
//Act
wrapper.vm.initializeComponent(cmsContent);
// Assert
expect(wrapper.vm.vehicleImageToDisplay).toEqual( "image_url");
expect(wrapper.vm.vehicleImageToDisplay).toEqual(wrapper.vm.genericVehicleImage);
expect(wrapper.find("img").attributes("class")).toContain("vehicle-image");
wrapper.unmount();
});
@ -30,10 +27,7 @@ describe("vehicleBanner", () => {
describe("vehicleBanner", () => {
test("renders expected vehicle image", async () => {
// Arrange
const { wrapper, cmsContent } = setupMocks({ displayGenericVehicleImageProp: false, imageUrlValue: "url_to_vehicle_image" });
//Act
wrapper.vm.initializeComponent(cmsContent);
const { wrapper } = setupMocks({ displayGenericVehicleImageProp: false, imageUrlValue: "url_to_vehicle_image" });
// Assert
expect(wrapper.vm.vehicleImageToDisplay).toEqual( "url_to_vehicle_image");
@ -45,14 +39,12 @@ describe("vehicleBanner", () => {
describe("vehicleBanner", () => {
test("should render car icon when imageUrl is null and category is default", async () => {
// Arrange
const { wrapper, cmsContent } = setupMocks({ displayGenericVehicleImageProp: false, imageUrlValue: "NULL" });
const { wrapper } = setupMocks({ displayGenericVehicleImageProp: false, imageUrlValue: "NULL" });
//Act
wrapper.vm.initializeComponent(cmsContent);
var iconUrl = wrapper.vm.vehicleImageToDisplay;
// Assert
expect(iconUrl).toEqual( "car_icon_url");
expect(iconUrl).toEqual(undefined);
wrapper.unmount();
});
});
@ -68,20 +60,10 @@ describe("vehicleBanner", () => {
test.each(params)("renders an icon instead of an image for %s and %s", async (category, expectedIcon) => {
// Arrange
const { wrapper, cmsContent } = setupMocks({ displayGenericVehicleImageProp: false, imageUrlValue: expectedIcon, categoryValue: category });
//Act
wrapper.vm.initializeComponent(cmsContent);
var vehicleIcon = wrapper.vm.getUnmatchedVehicleIcon();
// Assert
expect(wrapper.vm.carUnmatchedVehicleIcon).toEqual(cmsContent.CarUnmatchedVehicleIcon);
expect(wrapper.vm.truckUnmatchedVehicleIcon).toEqual(cmsContent.TruckUnmatchedVehicleIcon);
expect(wrapper.vm.vanUnmatchedVehicleIcon).toEqual(cmsContent.VanUnmatchedVehicleIcon);
expect(wrapper.vm.commercialUnmatchedVehicleIcon).toEqual(cmsContent.CommercialVanUnmatchedVehicleIcon);
expect(wrapper.vm.suvUnmatchedVehicleIcon).toEqual(cmsContent.SuvUnmatchedVehicleIcon);
expect(store.getters.vehicle.category).toEqual(category);
expect(wrapper.vm.vehicleImageToDisplay).toEqual(vehicleIcon);
expect(wrapper.find("img").attributes("class")).toContain("vehicle-image");
wrapper.unmount();
});
@ -92,6 +74,15 @@ function setupMocks({
imageUrlValue,
categoryValue = "CAR"
}) {
const mockGetCmsContent = jest.fn();
mockGetCmsContent((cmsWidget, field) => {
return field
});
const mockMixin = {
methods: {
getCmsContent: mockGetCmsContent
}
}
//Mock store
store.dispatch = jest.fn(() => {});
store.getters = { vehicle: { category: categoryValue, imageUrl: imageUrlValue } };
@ -104,7 +95,7 @@ function setupMocks({
//Mock props
mountOptions.propsData = { displayGenericVehicleImage: displayGenericVehicleImageProp };
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(vehicleBanner, mountOptions);
//Mock CMS content

View file

@ -14,18 +14,9 @@ export default {
props: {
displayGenericVehicleImage: {
type: Boolean,
required: true
}
},
data() {
return {
genericVehicleImage: '',
carUnmatchedVehicleIcon: '',
truckUnmatchedVehicleIcon: '',
vanUnmatchedVehicleIcon: '',
commercialUnmatchedVehicleIcon: '',
suvUnmatchedVehicleIcon: ''
}
required: true,
},
cmsWidgetName: String,
},
computed: {
vehicleImageToDisplay(){
@ -38,17 +29,27 @@ export default {
}
return this.$store.getters.vehicle.imageUrl;
},
genericVehicleImage(){
return this.getCmsContent(this.cmsWidgetName, 'GenericVehicleImage');
},
carUnmatchedVehicleIcon(){
return this.getCmsContent(this.cmsWidgetName, 'CarUnmatchedVehicleIcon');
},
truckUnmatchedVehicleIcon(){
return this.getCmsContent(this.cmsWidgetName, 'TruckUnmatchedVehicleIcon');
},
vanUnmatchedVehicleIcon(){
return this.getCmsContent(this.cmsWidgetName, 'VanUnmatchedVehicleIcon');
},
commercialUnmatchedVehicleIcon(){
return this.getCmsContent(this.cmsWidgetName, 'CommercialVanUnmatchedVehicleIcon');
},
suvUnmatchedVehicleIcon(){
return this.getCmsContent(this.cmsWidgetName, 'SuvUnmatchedVehicleIcon');
}
},
},
methods: {
initializeComponent(cmsContent) {
this.genericVehicleImage = cmsContent.GenericVehicleImage;
this.carUnmatchedVehicleIcon = cmsContent.CarUnmatchedVehicleIcon;
this.truckUnmatchedVehicleIcon = cmsContent.TruckUnmatchedVehicleIcon;
this.vanUnmatchedVehicleIcon = cmsContent.VanUnmatchedVehicleIcon;
this.commercialUnmatchedVehicleIcon = cmsContent.CommercialVanUnmatchedVehicleIcon;
this.suvUnmatchedVehicleIcon = cmsContent.SuvUnmatchedVehicleIcon;
},
getUnmatchedVehicleIcon(){
switch(this.$store.getters.vehicle.category){
case this.vehicleCategories.CAR:

View file

@ -43,6 +43,10 @@ const endpoints = {
url: "/vehicle/api/v1/vehicle/Lookup",
method: "POST",
},
LookupVinByPlate: {
url: "/vehicle/api/v1/vehicle/lookup-vin-by-plate",
method: "POST",
},
GetPartsOrQuestions: {
url: "/parts/api/v1/parts/parts-or-questions",
method: "POST",
@ -54,7 +58,11 @@ const endpoints = {
LoadOrder: {
url: "/order/api/v1/order/load",
method: "POST",
}
},
ValidateZip: {
url: "/location/api/v1/location/zip",
method: "GET",
},
};
export { endpoints };

View file

@ -11,6 +11,7 @@ const errorMessages = {
CITY_REQUIRED: "Please enter your city",
STATE_REQUIRED: "Please enter your state",
ZIP_REQUIRED: "Please enter your ZIP",
LICENSE_PLATE_REQUIRED: "Please enter your license plate number",
FIRST_NAME_REQUIRED: "Please enter your first name",
LAST_NAME_REQUIRED: "Please enter your last name",
EMAIL_ADDRESS_REQUIRED: "Please enter your email address",

View file

@ -11,10 +11,12 @@ const storeActions = {
GET_EVOX_IMAGE: "getEvoxImage",
LOOKUP_VEHICLE_BY_YMMS: "lookupVehicleByYmms",
LOOKUP_VEHICLE_BY_VIN: "lookupVehicleByVin",
LOOKUP_VIN_BY_PLATE: "lookupVinByPlate",
GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions",
SAVE_ORDER: "saveOrder",
LOAD_ORDER: "loadOrder",
SET_REFERRAL_INFORMATION: "setReferralInformation",
VALIDATE_ZIP: "validateZip",
// DEPENDENCY MUTATIONS
RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies",

View file

@ -10,10 +10,20 @@ const storeMutations = {
UPDATE_VEHICLE_IMAGE_URL: "updateVehicleImageUrl",
UPDATE_VEHICLE_IMAGE_VIF_NUMBER: "updateVehicleImageVifNumber",
UPDATE_VEHICLE_IMAGE_COLOR: "updateVehicleImageColor",
UPDATE_VEHICLE_VIN: "updateVehicleVin",
UPDATE_IS_REPAIR: "updateIsRepair",
UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips",
UPDATE_GLASS_TO_REPLACE: "updateGlassToReplace",
UPDATE_PARTS: "updateParts",
UPDATE_REGISTRATION_LICENSE_PLATE : "updateRegistrationLicensePlate",
UPDATE_REGISTRATION_ADDRESS: "updateRegistrationAddress",
UPDATE_REGISTRATION_CITY: "updateRegistrationCity",
UPDATE_REGISTRATION_STATE: "updateRegistrationState",
UPDATE_REGISTRATION_ZIP_CODE: "updateRegistrationZipCode",
UPDATE_REGISTRATION_FIRST_NAME: "updateRegistrationFirstName",
UPDATE_REGISTRATION_LAST_NAME: "updateRegistrationLastName",
UPDATE_SERVICE_LOCATION_ZIP: "updateServiceLocationZip",
UPDATE_CUSTOMER_EMAIL_ADDRESS: "updateCustomerEmailAddress",
// ORDER MUTATIONS
UPDATE_REFERRAL_NUMBER: "updateReferralNumber",

View file

@ -4,6 +4,7 @@ import { navigationScenarios } from "@/router/router-constants/navigation-scenar
import { vehicleCategories } from "@/constants/vehicle-categories.js";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { cookieNames } from "@/constants/cookie-names";
import { Form } from "vee-validate";
import baseMixin from "@/mixins/base-mixin";
import { getCookieDomainValue } from "@/helpers/heritage-integration/cookie-helper";
@ -42,6 +43,7 @@ export function getMountOptions(mockData) {
const global = {
mocks: mocks,
stubs: { Form }
};
return { global };

View file

@ -0,0 +1,237 @@
<template>
<Form
@submit="onSubmit"
@invalid-submit="onInvalidSubmit"
ref="theForm"
v-slot="{ meta }"
>
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall px-5">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<div class="row my-2">
<div class="col">
<textboxQuestion cmsWidgetName="LicensePlateNumber" v-model="licensePlate" inputId="license_plate" disableAutoFill validationRules="license-plate-required" />
</div>
</div>
<div class="row my-2">
<div class="col">
<textboxQuestion cmsWidgetName="RegistrationZip" v-model="zip" inputId="zip" mask="#####" disableAutoFill validationRules="zip-required" />
</div>
</div>
<div class="row my-2">
<div class="col">
<textboxQuestion cmsWidgetName="EmailAddress" v-model="email" inputId="email" disableAutoFill validationRules="email-address-required|email-address-format" />
</div>
</div>
<alert
class="my-3"
v-if="newServiceZipRequired"
alertClass="alert-danger"
:alertHeadline="noServiceZipWidget.headline"
:alertCopy="noServiceZipWidget.copy"
/>
<alert
class="my-3"
v-if="vinNotValid"
alertClass="alert-danger"
:alertHeadline="noMatchAlertWidget.headline"
:alertCopy="noMatchAlertWidget.copy"
/>
<alert
class="my-3"
v-if="vinDoesNotMatchCarId"
alertClass="alert-warning"
:alertHeadline="matchedDifferentVehicleAlertWidget.headline"
:alertCopy="matchedDifferentVehicleAlertWidget.copy"
/>
<div class="row my-2">
<div class="col">
<textboxQuestion v-if="newServiceZipRequired" cmsWidgetName="ServiceZip" v-model="serviceZip" inputId="serviceZip" disableAutoFill validationRules="zip-required" />
</div>
</div>
<funnelFooter
ref="funnelFooter"
cmsWidgetName="FunnelFooterWidget"
:isDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction"
/>
</div>
</Form>
</template>
<script>
// Components
import funnelHeader from "@/common-components/funnel-header/funnel-header";
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 alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/common-components/textbox-question/textbox-question";
// Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import store from "@/store";
import baseMixin from "@/mixins/base-mixin.js";
import { storeActions } from "@/constants/store-actions";
import { storeMutations } from "@/constants/store-mutations";
import { errorMessages } from "@/constants/error-messages";
import { required, regex } from "@/helpers/validation-rules";
import { Form, defineRule } from "vee-validate";
// DEFINE VALIDATION RULES
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
defineRule("zip-required", required(errorMessages.ZIP_REQUIRED));
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED));
defineRule("email-address-format", regex(/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9_\-.]+).([a-zA-Z]{2,})$/, errorMessages.EMAIL_ADDRESS_FORMAT));
export default {
name: "license-plate-lookup",
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
vm.noServiceZipWidget = {
headline: resultMap.cmsContent.NoServiceZipWidget.HeadlineText,
copy: resultMap.cmsContent.NoServiceZipWidget.BodyText,
};
vm.noMatchAlertWidget = {
headline: resultMap.cmsContent.NoMatchAlertWidget.HeadlineText,
copy: resultMap.cmsContent.NoMatchAlertWidget.BodyText
};
vm.matchedDifferentVehicleAlertWidget = {
headline: resultMap.cmsContent.MatchedDifferentVehicleAlertWidget.HeadlineText,
copy: resultMap.cmsContent.MatchedDifferentVehicleAlertWidget.BodyText
};
});
},
props: {
validationRules: String,
},
data() {
return {
newServiceZipRequired: false,
vinNotValid: false,
vinDoesNotMatchCarId: false,
licensePlate: '',
zip: '',
email: '',
serviceZip: '',
};
},
methods: {
arePagePrerequisitesValid() {
return store.getters.vehicle.carId !== null;
},
resetDependentState() {
store.commit(storeMutations.UPDATE_REGISTRATION_ADDRESS, null);
store.commit(storeMutations.UPDATE_REGISTRATION_CITY, null);
store.commit(storeMutations.UPDATE_REGISTRATION_FIRST_NAME, null);
store.commit(storeMutations.UPDATE_REGISTRATION_LAST_NAME, null);
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
},
backButtonAction() {
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
},
async forwardButtonAction() {
const zipValidation = this.serviceZip ? await this.validateZip(this.serviceZip) : await this.validateZip(this.zip);
if (!zipValidation.data.isServiceable) {
this.$refs.funnelFooter.removeLoader();
this.newServiceZipRequired = true;
return;
}
const vinLookup = await this.lookupVin(this.licensePlate, zipValidation.data.state).catch(() => {
this.$refs.funnelFooter.removeLoader();
this.vinNotValid = true;
return;
});
if (vinLookup.data.vehicle.carId !== store.getters.vehicle.carId) {
this.$refs.funnelFooter.updateButtonText(`Continue with ${vinLookup.data.vin} ${vinLookup.data.vehicle.year} ${vinLookup.data.vehicle.make} ${vinLookup.data.vehicle.model}`);
this.$refs.funnelFooter.removeLoader();
this.vinDoesNotMatchCarId = true;
return;
}
const partsData = await baseMixin.methods.dispatchNonBlockingStoreAction(
this.storeActions.GET_PARTS_OR_QUESTIONS,
{
carId: store.getters.vehicle.carId,
glassArray: store.getters.damage.glassToReplace,
zipCode: this.zip,
vin: vinLookup.vin
},
false
);
this.navigateForward(partsData);
},
navigateForward(partsData){
if(partsData.data.partsOrQuestions[0].partQuestions && partsData.data.partsOrQuestions[0].partQuestions.length > 0){
this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_PARTS_QUESTION, this.$route, {}, {}, partsData.data);
return;
} else if((!partsData.data.partsOrQuestions[0].partQuestions || partsData.data.partsOrQuestions[0].partQuestions.length < 1) && partsData.data.partsOrQuestions[0].parts.length > 1) {
this.$router.navigateAfterSave(this.navigationScenarios.CONTINUING_WITH_MULTIPLE_PARTS, this.$route, {}, {}, partsData.data);
return;
} else {
this.$router.navigate(this.navigationScenarios.CONTINUING_WITH_SINGLE_PART, this.$route);
}
},
validateZip(zip) {
return baseMixin.methods.dispatchNonBlockingStoreAction(
storeActions.VALIDATE_ZIP,
{ zip }
);
},
lookupVin(plate, state) {
return baseMixin.methods.dispatchNonBlockingStoreAction(
storeActions.LOOKUP_VIN_BY_PLATE,
{ licensePlate: plate, licenseState: state }
);
},
updateStore() {
// if(vehicleDamage){
// store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
// }
store.commit(storeMutations.UPDATE_VEHICLE_VIN, null);
store.commit(storeMutations.UPDATE_YEAR, null);
store.commit(storeMutations.UPDATE_MAKE, null);
store.commit(storeMutations.UPDATE_MODEL, null);
store.commit(storeMutations.UPDATE_STYLE, null);
store.commit(storeMutations.UPDATE_CAR_ID, null);
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, null);
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_URL, null);
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_VIF_NUMBER, null);
store.commit(storeMutations.UPDATE_VEHICLE_IMAGE_COLOR, null);
store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, null);
store.commit(storeMutations.UPDATE_REGISTRATION_STATE, null);
store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, null);
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP, null);
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, null);
},
},
components: {
Form,
funnelHeader,
vehicleBanner,
funnelSubHeader,
textboxQuestion,
alert,
funnelFooter,
},
};
</script>

View file

@ -1,8 +1,8 @@
<template>
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
<funnelHeader ref="funnelHeader" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" ref="funnelHeader" />
<h1>Reveal Confetti Page Placeholder</h1>
<funnelFooter ref="funnelFooter" @back-clicked="backButtonAction" />
<funnelFooter cmsWidgetName="FunnelFooterWidget" ref="funnelFooter" @back-clicked="backButtonAction" />
</div>
</template>
@ -34,12 +34,7 @@ export default {
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.funnelFooter.initializeComponent(
resultMap.cmsContent.FunnelFooterWidget
);
vm.setCmsContent(resultMap.cmsContent);
});
},
methods: {

View file

@ -37,10 +37,10 @@ describe("damage-location-question.vue", () => {
});
//Act
damageLocationQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, damageOptions, "car-group");
damageLocationQuestion.methods.initializeComponent.call(wrapper.vm, damageOptions, "car-group");
//Assert
expect(wrapper.vm.answersToDisplay).toStrictEqual([{ Name: 'Windshield' }, { Name: 'SideDoor' }])
expect(wrapper.vm.damageOptions).toStrictEqual({"backGlassOptions": {"availableReplacementOptions": []}, "driverSideOptions": {"availableReplacementOptions": ["Quarter", "Front"]}, "windshieldOptions": {"availableReplacementOptions": ["Single"]}})
});
});
@ -64,10 +64,16 @@ function setupMocks({
});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
mountOptions.propsData = {
modelValue: modelValueProp,
isMultiSelect: isMultiSelect,
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(damageLocationQuestion, mountOptions);

View file

@ -26,8 +26,6 @@ export default ({
name: "damageLocationQuestion",
data(){
return {
questionText: String,
answersFromCms: Array,
damageOptions: Object,
}
},
@ -38,15 +36,20 @@ export default ({
filterByVehicleCategory: Boolean,
name: String,
groupName: String,
cmsWidgetName: String,
},
methods: {
initializeComponent(cmsContent, damageOptions){
this.questionText = cmsContent.QuestionText;
this.answersFromCms = cmsContent.Answers;
initializeComponent(damageOptions){
this.damageOptions = damageOptions;
},
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
answersFromCms(){
return this.getCmsContent(this.cmsWidgetName, 'Answers');
},
selectedValues: {
get: function() {
return this.modelValue;

View file

@ -28,10 +28,10 @@ describe("replace-options-question.vue", () => {
const { wrapper, cmsContent, replaceOptions } = setupMocks({ dataFromStoreApi: ["Windshield", "FrontDoor"], filterByVehicleCategory: true});
//Act
replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, replaceOptions, "car-group");
replaceOptionsQuestion.methods.initializeComponent.call(wrapper.vm, replaceOptions, "car-group");
//Assert
expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'Windshield' }, { Name: 'FrontDoor' } ])
expect(wrapper.vm.replaceOptions).toStrictEqual(["Windshield", "FrontDoor"])
});
});
@ -56,7 +56,7 @@ describe("replace-options-question.vue", () => {
//Act
wrapper.vm.$options.methods.updateSelectedValues.call(wrapper.vm);
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([['Stationary']]);
expect(wrapper.vm.selectedValues).toEqual([]);
});
});
@ -99,12 +99,18 @@ function setupMocks({
});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
mountOptions.propsData = {
modelValue: modelValueProp,
isAvailable: isAvailable,
isMultiSelect: isMultiSelect,
filterByVehicleCategory: filterByVehicleCategory
};
mountOptions.mixins = [mockMixin];
//Mock methods
methodsToMock.forEach((methodName) => {

View file

@ -24,9 +24,7 @@ export default ({
name: "replaceOptionsQuestion",
data(){
return {
questionText: String,
answersFromCms: Array,
replaceOptions: Array,
replaceOptions: [],
}
},
props: {
@ -37,11 +35,10 @@ export default ({
isMultiSelect: Boolean,
validationRules: String,
suppressError: Boolean,
cmsWidgetName: String,
},
methods: {
initializeComponent(cmsContent, replaceOptions){
this.questionText = cmsContent.QuestionText;
this.answersFromCms = cmsContent.Answers;
initializeComponent(replaceOptions){
this.replaceOptions = replaceOptions;
},
updateSelectedValues() {
@ -52,6 +49,12 @@ export default ({
},
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
answersFromCms(){
return this.getCmsContent(this.cmsWidgetName, 'Answers');
},
selectedValues: {
get: function() {
return this.modelValue;

View file

@ -77,7 +77,7 @@ describe("replace-options-question.vue", () => {
sideDoorOptions.methods.initializeComponent.call(wrapper.vm, cmsContent, driverSideReplaceOptions, passengerSideReplaceOptions, driverSideOptions, passengerSideOptions, "car-group");
//Assert
expect(wrapper.vm.answersToDisplay).toStrictEqual([ { Name: 'DriverSide' }, { Name: 'PassengerSide' } ])
expect(wrapper.vm.answersToDisplay).toStrictEqual([])
});
});
@ -105,11 +105,17 @@ describe("replace-options-question.vue", () => {
});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
mountOptions.propsData = {
modelValue: modelValueProp,
groupName: groupName,
selectedDamageLocations: selectedDamageLocations,
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(sideDoorOptions, mountOptions);

View file

@ -15,6 +15,7 @@
</transition>
<replaceOptionsQuestion
ref="driverSideOptions"
cmsWidgetName="DriverSideReplaceOptionsQuestion"
:isAvailable="isDriverSideReplaceOptionsQuestionAvailable"
groupName="driverSideOptions"
isMultiSelect
@ -24,6 +25,7 @@
/>
<replaceOptionsQuestion
ref="passengerSideOptions"
cmsWidgetName="PassengerSideReplaceOptionsQuestion"
:isAvailable="isPassengerSideReplaceOptionsQuestionAvailable"
groupName="passengerSideOptions"
isMultiSelect
@ -49,24 +51,16 @@ defineRule("passenger-side-options-required", required(errorMessages.PASSENGER_S
export default ({
name: "sideDoorOptions",
data(){
return {
questionText: String,
answersFromCms: Array,
}
},
props: {
groupName: String,
modelValue: Array,
selectedDamageLocations: Array,
cmsWidgetName: String,
},
methods: {
initializeComponent(cmsContent, driverSideReplaceOptions, passengerSideReplaceOptions, driverSideOptions, passengerSideOptions){
this.questionText = cmsContent.QuestionText;
this.answersFromCms = cmsContent.Answers;
// Child Components intialize
this.$refs.driverSideOptions.initializeComponent(driverSideReplaceOptions, driverSideOptions);
this.$refs.passengerSideOptions.initializeComponent(passengerSideReplaceOptions, passengerSideOptions);
initializeComponent(driverSideOptions, passengerSideOptions){
this.$refs.driverSideOptions.initializeComponent(driverSideOptions);
this.$refs.passengerSideOptions.initializeComponent(passengerSideOptions);
},
getSideDoorReplacementOptions(selectedDoorSides, selectedDriverSideReplaceOptions, selectedPassengerSideReplaceOptions){
return {
@ -77,6 +71,12 @@ export default ({
},
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
answersFromCms(){
return this.getCmsContent(this.cmsWidgetName, 'Answers');
},
selectedValues: {
get: function() {
return this.modelValue;

View file

@ -1,9 +1,5 @@
// Components
import vehicleDamage from "@/layouts/vehicle-damage/vehicle-damage.vue";
import funnelHeader from "@/common-components/funnel-header/funnel-header";
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 sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
@ -13,7 +9,7 @@ import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-que
import { settleAllPromises } from "@/helpers/layout-helper.js";
import baseMixin from "@/mixins/base-mixin";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { mount, flushPromises } from "@vue/test-utils";
import { shallowMount, flushPromises } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { nextTick } from "vue";
import { storeActions } from "@/constants/store-actions";
@ -48,90 +44,6 @@ jest.mock("@/store", () => ({
},
}));
describe("vehicle-damage.vue", () => {
test("Page header is initailized with api data", async (done) => {
//Arrange
const pageHeaderWidgetHeaderText = "Select Damage";
const { wrapper, apiPromise } = setupMocks({
pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText,
});
//Act
vehicleDamage.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-damage" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelSubHeader.methods.initializeComponent).toHaveBeenCalledWith(
pageHeaderWidgetHeaderText
);
done();
});
});
});
describe("vehicle-damage.vue", () => {
test("Page logo image is initailized with api data", async (done) => {
//Arrange
const SiteHeaderWidget = {
LogoImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
};
const { wrapper, apiPromise } = setupMocks({
SiteHeaderWidget: SiteHeaderWidget,
});
//Act
vehicleDamage.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-damage" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelHeader.methods.initializeComponent).toHaveBeenCalledWith(
SiteHeaderWidget
);
done();
});
});
});
describe("vehicle-damage.vue", () => {
test("Vehicle image is initailized with api data", async (done) => {
//Arrange
const VehicleBannerWidget = {
GenericVehicleImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
};
const { wrapper, apiPromise } = setupMocks({
VehicleBannerWidget: VehicleBannerWidget,
});
//Act
vehicleDamage.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-damage" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(vehicleBanner.methods.initializeComponent).toHaveBeenCalledWith(
VehicleBannerWidget
);
done();
});
});
});
describe("vehicle-damage.vue", () => {
test("CarId set, arePagePrerequisitesValid should be true ", async () => {
//Arrange
@ -800,7 +712,6 @@ describe("vehicle-damage.vue", () => {
);
const testNull = validate( "", "replace-options-required");
const testString = validate( "sldfj", "replace-options-required");
await flushPromises();
//Assert
testNull.then(function(data) {
@ -863,18 +774,6 @@ function setupMocks({
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
//Mock damage initialize methods
funnelHeader.methods = {
initializeComponent: jest.fn(),
};
vehicleBanner.methods = {
initializeComponent: jest.fn(),
};
funnelSubHeader.methods = {
initializeComponent: jest.fn(),
};
damageLocationQuestion.methods = {
initializeComponent: jest.fn(),
};
@ -892,22 +791,11 @@ function setupMocks({
updateSelectedValues: jest.fn(),
};
funnelFooter.methods = {
initializeComponent: jest.fn(),
}
const mountOptions = getMountOptions(mountOptionsMockData);
mountOptions['attachTo'] = document.body; // append wrapper to document.body to test DOM methods
const wrapper = mount(vehicleDamage, mountOptions);
const funnelHeaderWrapper = wrapper.findComponent({ name: "funnelHeader" });
funnelHeaderWrapper.vm.initializeComponent =
funnelHeader.methods.initializeComponent;
const vehicleBannerWrapper = wrapper.findComponent({ name: "vehicleBanner" });
vehicleBannerWrapper.vm.initializeComponent =
vehicleBanner.methods.initializeComponent;
const wrapper = shallowMount(vehicleDamage, mountOptions);
const sideDoorOptionsWrapper = wrapper.findComponent({ name: "sideDoorOptions" });
sideDoorOptionsWrapper.vm.initializeComponent =
@ -917,21 +805,15 @@ function setupMocks({
windshieldOptionsWrapper.vm.initializeComponent =
windshieldOptions.methods.initializeComponent;
const funnelSubHeaderWrapper = wrapper.findComponent({ name: "funnelSubHeader" });
funnelSubHeaderWrapper.vm.initializeComponent =
funnelSubHeader.methods.initializeComponent;
const backGlassOptionsWrapper = wrapper.findComponent({ name: "replaceOptionsQuestion" });
backGlassOptionsWrapper.vm.initializeComponent =
replaceOptionsQuestion.methods.initializeComponent;
replaceOptionsQuestion.methods.initializeComponent;
const damageLocationQuestionWrapper = wrapper.findComponent({ name: "damageLocationQuestion" });
damageLocationQuestionWrapper.vm.initializeComponent =
damageLocationQuestion.methods.initializeComponent;
damageLocationQuestion.methods.initializeComponent;
const funnelFooterWrapper = wrapper.findComponent({ name: "funnelFooter" });
funnelFooterWrapper.vm.initializeComponent =
funnelFooter.methods.initializeComponent;
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
return { wrapper, apiPromise };
}

View file

@ -5,12 +5,13 @@
ref="theForm"
v-slot="{ meta }"
>
<div class="container-fluid shadow rounded-3 px-5 p-2 position-relative make-tall">
<funnelHeader ref="funnelHeader" />
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false />
<funnelSubHeader ref="funnelSubHeader" />
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall px-5">
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<damageLocationQuestion
ref="damageLocation"
cmsWidgetName="DamageLocationQuestion"
v-model="selectedDamageLocations"
groupName="DamageLocationQuestion"
/>
@ -31,6 +32,7 @@
/>
<sideDoorOptions
ref="sideDoorOptions"
cmsWidgetName="SideDoorSideQuestion"
groupName="SideDoorSideQuestion"
v-model="sideDoorOptionsData"
v-show="!hasRepairReplaceConflict"
@ -38,13 +40,14 @@
/>
<replaceOptionsQuestion
ref="backGlassOptions"
cmsWidgetName="RearReplaceOptionsQuestion"
:isAvailable="isRearWindowDamageLocation && !hasRepairReplaceConflict"
v-model="selectedRearReplaceOptions"
groupName="BackGlassReplaceOptionsQuestion"
validationRules="replace-options-required"
/>
<funnel-footer
ref="funnelFooter"
cmsWidgetName="FunnelFooterWidget"
:isDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction"
@ -109,30 +112,18 @@ export default {
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.vehicleBanner.initializeComponent(
resultMap.cmsContent.VehicleBannerWidget
);
vm.$refs.funnelSubHeader.initializeComponent(
resultMap.cmsContent.FunnelSubHeaderWidget
);
vm.setCmsContent(resultMap.cmsContent);
vm.$refs.damageLocation.initializeComponent(
resultMap.cmsContent.DamageLocationQuestion, resultMap.damageOptions
resultMap.damageOptions
);
vm.$refs.sideDoorOptions.initializeComponent(
resultMap.cmsContent.SideDoorSideQuestion, resultMap.cmsContent.DriverSideReplaceOptionsQuestion, resultMap.cmsContent.PassengerSideReplaceOptionsQuestion, resultMap.damageOptions.driverSideOptions.availableReplacementOptions, resultMap.damageOptions.passengerSideOptions.availableReplacementOptions
resultMap.damageOptions.driverSideOptions.availableReplacementOptions, resultMap.damageOptions.passengerSideOptions.availableReplacementOptions
);
vm.$refs.windshieldOptions.initializeComponent(
resultMap.cmsContent.WindshieldDamageTypeQuestion, resultMap.cmsContent.WindshieldChipCountQuestion,
resultMap.cmsContent.WindshieldReplaceOptionsQuestion, resultMap.damageOptions.windshieldOptions.availableReplacementOptions
resultMap.damageOptions.windshieldOptions.availableReplacementOptions
);
vm.$refs.backGlassOptions.initializeComponent(
resultMap.cmsContent.RearReplaceOptionsQuestion, resultMap.damageOptions.backGlassOptions.availableReplacementOptions
);
vm.$refs.funnelFooter.initializeComponent(
resultMap.cmsContent.FunnelFooterWidget
resultMap.damageOptions.backGlassOptions.availableReplacementOptions
);
});
},

View file

@ -1,6 +1,7 @@
import { shallowMount } from "@vue/test-utils";
import windshieldChipCountQuestion from "@/layouts/vehicle-damage/windshield-options/windshield-chip-count-question/windshield-chip-count-question";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { nextTick } from "vue";
import store from "@/store";
jest.mock("@/store", () => { return {}; }, {virtual: true});
@ -22,17 +23,17 @@ describe("windshield-chip-count-question.vue", () => {
describe("Windshield-chip-count-question.vue", () => {
test("Should display question and answers from api.", async () => {
//Arrange
const { wrapper, cmsContent } = setupMocks({modelValueProp: ["One"]});
const { wrapper } = setupMocks({modelValueProp: ["One"]});
//Act
windshieldChipCountQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent);
wrapper.setProps({isAvailable: true});
wrapper.vm.updateSelectedValues = jest.fn();
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.vm.questionText).toStrictEqual("How many chips are we repairing?");
expect(wrapper.vm.answersFromCms).toStrictEqual([ { Name: 'One' }, { Name: 'Two' }, { Name: 'Three'} ]);
});
expect(wrapper.vm.updateSelectedValues).toBeCalled();
});
});
function setupMocks({
@ -54,9 +55,15 @@ describe("windshield-chip-count-question.vue", () => {
});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
mountOptions.propsData = {
modelValue: modelValueProp
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(windshieldChipCountQuestion, mountOptions);

View file

@ -18,24 +18,15 @@
import buttonQuestion from "@/common-components/button-question/button-question";
export default ({
name: "windshieldOptions",
data(){
return {
questionText: String,
answersFromCms: Array
}
},
name: "windshieldOptions",
props: {
modelValue: Array,
groupName: String,
isAvailable: Boolean,
validationRules: String,
cmsWidgetName: String,
},
methods: {
initializeComponent(cmsContent){
this.questionText = cmsContent.QuestionText;
this.answersFromCms = cmsContent.Answers;
},
updateSelectedValues() {
// UPDATE SELECTEDVALUES IF ONLY ONE ANSWER
if(Array.isArray(this.answersToDisplay) && this.answersToDisplay.length === 1 && this.selectedValues) {
@ -44,6 +35,12 @@ export default ({
},
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
answersFromCms(){
return this.getCmsContent(this.cmsWidgetName, 'Answers');
},
selectedChipCountValues: {
get: function() {
return this.modelValue;

View file

@ -20,21 +20,6 @@ describe("windshield-damage-type-question.vue", () => {
});
});
describe("windshield-damage-type-question.vue", () => {
test("Should display question and answers from api.", async () => {
//Arrange
const { wrapper, cmsContent } = setupMocks({modelValueProp: ["Repair"]});
//Act
windshieldDamageTypeQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent);
//Assert
expect(wrapper.vm.questionText).toStrictEqual("What's your windshield damage?");
expect(wrapper.vm.answersFromCms).toStrictEqual([ { Name: 'Repair' }, { Name: 'Replace' } ]);
});
});
function setupMocks({
modelValueProp = ["Two"],
groupName = "WindshieldDamageTypeQuestion",
@ -57,6 +42,12 @@ describe("windshield-damage-type-question.vue", () => {
mountOptions.propsData = {
modelValue: modelValueProp
};
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(windshieldDamageTypeQuestion, mountOptions);

View file

@ -20,26 +20,21 @@ import store from "@/store";
export default ({
name: "windshieldDamageTypeQuestion",
data(){
return {
questionText: String,
answersFromCms: Array
}
},
props: {
modelValue: Array,
groupName: String,
isAvailable: Boolean,
suppressError: Boolean,
validationRules: String,
},
methods: {
initializeComponent(cmsContent){
this.questionText = cmsContent.QuestionText;
this.answersFromCms = cmsContent.Answers;
},
cmsWidgetName: String,
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
answersFromCms(){
return this.getCmsContent(this.cmsWidgetName, 'Answers');
},
selectedValues: {
get: function() {
return this.modelValue;

View file

@ -1,6 +1,6 @@
<template>
<div class="windshield-options">
<windshieldDamageTypeQuestion ref="windshieldDamageTypeQuestion"
<windshieldDamageTypeQuestion cmsWidgetName="WindshieldDamageTypeQuestion"
:isAvailable=isWindshieldDamageLocation
:suppressError="hasRepairReplaceConflict || showNoReplacementAvailableError"
groupName="WindshieldDamageTypeQuestion"
@ -15,13 +15,13 @@
:alertCopy="['We\'re sorry, but we currently offer only repair service for your vehicle type.', 'Need help with next steps? Call us at 800-394-0288.']"
:isDismissible="false"
/>
<windshieldChipCountQuestion ref="windshieldChipCountQuestion"
<windshieldChipCountQuestion cmsWidgetName="WindshieldChipCountQuestion"
:isAvailable="isRepairOptionSelected && !hasRepairReplaceConflict"
groupName="WindshieldChipCountQuestion"
v-model="selectedWindshieldChipCountValues"
validationRules="windshield-chip-count-required"
/>
<replaceOptionsQuestion ref="replaceOptionsQuestion"
<replaceOptionsQuestion ref="replaceOptionsQuestion" cmsWidgetName="WindshieldReplaceOptionsQuestion"
:isAvailable=isReplaceOptionSelected
isMultiSelect
groupName="WindshieldReplaceOptions"
@ -99,13 +99,8 @@ export default ({
},
methods: {
initializeComponent(windshieldDamageTypeQuestionFromCms, windshieldChipCountQuestionFromCms,
windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions){
this.$refs.windshieldDamageTypeQuestion.initializeComponent(windshieldDamageTypeQuestionFromCms);
this.$refs.windshieldChipCountQuestion.initializeComponent(windshieldChipCountQuestionFromCms);
this.$refs.replaceOptionsQuestion.initializeComponent(windshieldReplaceOptionsQuestionFromCms, windshieldAvailableReplacementOptions);
initializeComponent(
windshieldAvailableReplacementOptions){
this.windshieldAvailableReplacementOptions = windshieldAvailableReplacementOptions;
},
getWindshieldOptions(selectedWindshieldDamageType, selectedWindshieldChipCount, selectedWindshieldReplaceOptions){

View file

@ -27,26 +27,6 @@ describe("make-question.vue", () => {
});
});
describe("make-question.vue", () => {
test("CMS question text is used as radio question text.", async () => {
//Arrange
const { wrapper, cmsContent } = setupMocks({
cmsQuestionText: "What make is your vehicle?",
});
//Act
makeQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, null);
//Assert
const buttonQuestionComponent = await wrapper.findComponent({
name: "buttonQuestion",
});
expect(buttonQuestionComponent.attributes("questiontext")).toBe(
"What make is your vehicle?"
);
});
});
describe("make-question.vue", () => {
test("Data from store api are used as radio question answers.", async () => {
//Arrange
@ -58,7 +38,6 @@ describe("make-question.vue", () => {
const initialData = makeQuestion.methods.loadInitialData.call(wrapper.vm);
makeQuestion.methods.initializeComponent.call(
wrapper.vm,
cmsContent,
initialData
);
@ -88,9 +67,15 @@ function setupMocks({
});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
mountOptions.propsData = {
modelValue: modelValueProp,
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(makeQuestion, mountOptions);
//Mock CMS content

View file

@ -23,14 +23,17 @@ export default {
name: "make-question",
data() {
return {
questionText: null,
makes: Array,
};
},
props: {
modelValue: String,
cmsWidgetName: String,
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
selectedValueAsArray: {
get: function() {
const modelValueAsArray = this.modelValue ? [this.modelValue] : [];
@ -52,8 +55,7 @@ export default {
{ year: store.getters.vehicle.year }
);
},
initializeComponent(cmsContent, initialData) {
this.questionText = cmsContent.QuestionText;
initializeComponent(initialData) {
this.makes = initialData;
},
},

View file

@ -6,13 +6,11 @@ import { nextTick } from "vue";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { storeMutations } from "@/constants/store-mutations";
import { storeActions } from "@/constants/store-actions";
import baseMixin from "@/mixins/base-mixin.js";
// Components
import vehicleMake from "@/layouts/vehicle-make/vehicle-make.vue";
import makeQuestion from "@/layouts/vehicle-make/make-question/make-question";
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import store from "@/store";
jest.mock("@/store", () => ({
@ -39,12 +37,8 @@ jest.mock("@/helpers/layout-helper.js", () => ({
describe("vehicle-make.vue", () => {
test("Make question component is initized with api data", async (done) => {
//Arrange
const vehicleMakeQuestionCmsContent = {
QuestionText: "What make is your vehicle?",
};
const makeQuestionInitialData = ["honda", "ford", "dodge"];
const { wrapper, apiPromise } = setupMocks({
vehicleMakeQuestionCmsContent: vehicleMakeQuestionCmsContent,
makeQuestionInitialData: makeQuestionInitialData,
});
@ -59,7 +53,6 @@ describe("vehicle-make.vue", () => {
//Assert
apiPromise.finally(() => {
expect(makeQuestion.methods.initializeComponent).toHaveBeenCalledWith(
vehicleMakeQuestionCmsContent,
makeQuestionInitialData
);
done();
@ -67,90 +60,6 @@ describe("vehicle-make.vue", () => {
});
});
describe("vehicle-make.vue", () => {
test("Page header is initailized with api data", async (done) => {
//Arrange
const pageHeaderWidgetHeaderText = "Select a make to get started";
const { wrapper, apiPromise } = setupMocks({
pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText,
});
//Act
vehicleMake.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-make" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelSubHeader.methods.initializeComponent).toHaveBeenCalledWith(
pageHeaderWidgetHeaderText
);
done();
});
});
});
describe("vehicle-make.vue", () => {
test("Page logo image is initailized with api data", async (done) => {
//Arrange
const SiteHeaderWidget = {
LogoImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
};
const { wrapper, apiPromise } = setupMocks({
SiteHeaderWidget: SiteHeaderWidget,
});
//Act
vehicleMake.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-make" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelHeader.methods.initializeComponent).toHaveBeenCalledWith(
SiteHeaderWidget
);
done();
});
});
});
describe("vehicle-make.vue", () => {
test("Vehicle image is initailized with api data", async (done) => {
//Arrange
const VehicleBannerWidget = {
GenericVehicleImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
};
const { wrapper, apiPromise } = setupMocks({
VehicleBannerWidget: VehicleBannerWidget,
});
//Act
vehicleMake.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-make" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(vehicleBanner.methods.initializeComponent).toHaveBeenCalledWith(
VehicleBannerWidget
);
done();
});
});
});
describe("vehicle-make.vue", () => {
test("BackButtonAction triggers a router.navigate change", async (done) => {
//Arrange
@ -262,32 +171,13 @@ function setupMocks({
loadInitialData: jest.fn(),
initializeComponent: jest.fn(),
};
funnelHeader.methods = {
initializeComponent: jest.fn(),
};
vehicleBanner.methods = {
initializeComponent: jest.fn(),
};
funnelSubHeader.methods = {
initializeComponent: jest.fn(),
};
const mountOptions = getMountOptions(mountOptionsMockData);
const wrapper = shallowMount(vehicleMake, mountOptions);
const makeQuestionWrapper = wrapper.findComponent({ name: "makeQuestion" });
makeQuestionWrapper.vm.initializeComponent =
makeQuestion.methods.initializeComponent;
const funnelHeaderWrapper = wrapper.findComponent({ name: "funnelHeader" });
funnelHeaderWrapper.vm.initializeComponent =
funnelHeader.methods.initializeComponent;
const vehicleBannerWrapper = wrapper.findComponent({ name: "vehicleBanner" });
vehicleBannerWrapper.vm.initializeComponent =
vehicleBanner.methods.initializeComponent;
const funnelSubHeaderWrapper = wrapper.findComponent({
name: "funnelSubHeader",
});
funnelSubHeaderWrapper.vm.initializeComponent =
funnelSubHeader.methods.initializeComponent;
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
return { wrapper, apiPromise };
}

View file

@ -1,16 +1,16 @@
<template>
<div class="container-fluid shadow rounded-3 p-0 position-relative make-tall">
<funnelHeader ref="funnelHeader" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<div class="select-car">
<div class="select-car-form rounded text-center">
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=true />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" displayGenericVehicleImage />
<funnelSubHeader
ref="funnelSubHeader"
cmsWidgetName="FunnelSubHeaderWidget"
:hasBackButton="true"
backButtonAccessibleText="Change Vehicle Year"
@click-event="backButtonAction"
/>
<makeQuestion v-model="selectedMake" ref="makeQuestion" />
<makeQuestion v-model="selectedMake" ref="makeQuestion" cmsWidgetName="VehicleMakeQuestion" />
</div>
</div>
</div>
@ -59,17 +59,8 @@ export default {
const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.$refs.funnelSubHeader.initializeComponent(
resultMap.cmsContent.FunnelSubHeaderWidget
);
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.vehicleBanner.initializeComponent(
resultMap.cmsContent.VehicleBannerWidget
);
vm.setCmsContent(resultMap.cmsContent);
vm.$refs.makeQuestion.initializeComponent(
resultMap.cmsContent.VehicleMakeQuestion,
resultMap.makeQuestionInitialData
);
});

View file

@ -27,29 +27,6 @@ describe("model-question.vue", () => {
});
});
describe("model-question.vue", () => {
test("CMS question text is used as radio question text.", async () => {
//Arrange
const { wrapper, cmsContent } = setupMocks({
cmsQuestionText: "What model is your vehicle?",
});
//Act
modelQuestion.methods.initializeComponent.call(
wrapper.vm,
cmsContent,
null
);
//Assert
const buttonQuestionComponent = await wrapper.findComponent({
name: "buttonQuestion",
});
expect(buttonQuestionComponent.attributes("questiontext")).toBe(
"What model is your vehicle?"
);
});
});
describe("model-question.vue", () => {
test("Data from store api are used as radio question answers.", async () => {
@ -62,7 +39,6 @@ describe("model-question.vue", () => {
const initialData = modelQuestion.methods.loadInitialData.call(wrapper.vm);
modelQuestion.methods.initializeComponent.call(
wrapper.vm,
cmsContent,
initialData
);
@ -92,9 +68,15 @@ function setupMocks({
});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
mountOptions.propsData = {
modelValue: modelValueProp,
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(modelQuestion, mountOptions);
//Mock CMS content

View file

@ -23,14 +23,17 @@ export default {
name: "model-question",
data() {
return {
questionText: null,
models: Array,
};
},
props: {
modelValue: String,
cmsWidgetName: String,
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
selectedValueAsArray: {
get: function() {
const modelValueAsArray = this.modelValue ? [this.modelValue] : [];
@ -52,8 +55,7 @@ export default {
{ year: store.getters.vehicle.year, make: store.getters.vehicle.make }
);
},
initializeComponent(cmsContent, initialData) {
this.questionText = cmsContent.QuestionText;
initializeComponent(initialData) {
this.models = initialData;
},
},

View file

@ -1,9 +1,6 @@
// Components
import vehicleModel from "@/layouts/vehicle-model/vehicle-model.vue";
import modelQuestion from "@/layouts/vehicle-model/model-question/model-question";
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
// Supporting files
import { settleAllPromises } from "@/helpers/layout-helper.js";
@ -13,6 +10,7 @@ import { nextTick } from "vue";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { storeMutations } from "@/constants/store-mutations";
import { storeActions } from "@/constants/store-actions";
import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store";
// Mock our module for promises.
@ -39,12 +37,8 @@ jest.mock("@/store", () => ({
describe("vehicle-model.vue", () => {
test("Model question component is initized with api data", async (done) => {
//Arange
const buttonQuestionContent = {
QuestionText: "What model is your vehicle?",
};
const modelQuestionInitialData = ["accord", "civic", "insight"];
const { wrapper, apiPromise } = setupMocks({
buttonQuestionContent: buttonQuestionContent,
modelQuestionInitialData: modelQuestionInitialData,
});
//Act
@ -57,88 +51,13 @@ describe("vehicle-model.vue", () => {
//Assert
apiPromise.finally(() => {
expect(modelQuestion.methods.initializeComponent).toHaveBeenCalledWith(
buttonQuestionContent,
modelQuestionInitialData
);
done();
});
});
});
describe("vehicle-model.vue", () => {
test("Page header is initailized with api data", async (done) => {
//Arrange
const pageHeaderWidgetHeaderText = "Select a model to get started";
const { wrapper, apiPromise } = setupMocks({
pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText,
});
//Act
vehicleModel.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-model" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelSubHeader.methods.initializeComponent).toHaveBeenCalledWith(
pageHeaderWidgetHeaderText
);
done();
});
});
});
describe("vehicle-model.vue", () => {
test("Page logo image is initailized with api data", async (done) => {
//Arrange
const SiteHeaderWidget = {
LogoImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
};
const { wrapper, apiPromise } = setupMocks({
SiteHeaderWidget: SiteHeaderWidget,
});
//Act
vehicleModel.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-model" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelHeader.methods.initializeComponent).toHaveBeenCalledWith(
SiteHeaderWidget
);
done();
});
});
});
describe("vehicle-model.vue", () => {
test("Vehicle image is initailized with api data", async (done) => {
//Arrange
const VehicleBannerWidget = {
GenericVehicleImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
};
const { wrapper, apiPromise } = setupMocks({
VehicleBannerWidget: VehicleBannerWidget,
});
//Act
vehicleModel.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-model" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(vehicleBanner.methods.initializeComponent).toHaveBeenCalledWith(
VehicleBannerWidget
);
done();
});
});
});
describe("vehicle-model.vue", () => {
test("BackButtonAction triggers a router.navigate change", async (done) => {
//Arrange
@ -246,35 +165,13 @@ function setupMocks({
loadInitialData: jest.fn(),
initializeComponent: jest.fn(),
};
funnelHeader.methods = {
initializeComponent: jest.fn(),
};
vehicleBanner.methods = {
initializeComponent: jest.fn(),
};
funnelSubHeader.methods = {
initializeComponent: jest.fn(),
};
const mountOptions = getMountOptions(mountOptionsMockData);
const wrapper = shallowMount(vehicleModel, mountOptions);
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
const modelQuestionWrapper = wrapper.findComponent({ name: "modelQuestion" });
modelQuestionWrapper.vm.initializeComponent =
modelQuestion.methods.initializeComponent;
const funnelHeaderWrapper = wrapper.findComponent({ name: "funnelHeader" });
funnelHeaderWrapper.vm.initializeComponent =
funnelHeader.methods.initializeComponent;
const vehicleBannerWrapper = wrapper.findComponent({ name: "vehicleBanner" });
vehicleBannerWrapper.vm.initializeComponent =
vehicleBanner.methods.initializeComponent;
const funnelSubHeaderWrapper = wrapper.findComponent({
name: "funnelSubHeader",
});
funnelSubHeaderWrapper.vm.initializeComponent =
funnelSubHeader.methods.initializeComponent;
return { wrapper, apiPromise };
}

View file

@ -1,16 +1,16 @@
<template>
<div class="container-fluid shadow rounded-3 p-0 position-relative">
<funnelHeader ref="funnelHeader" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<div class="select-car">
<div class="select-car-form rounded text-center">
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=true />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" displayGenericVehicleImage />
<funnelSubHeader
ref="funnelSubHeader"
cmsWidgetName="FunnelSubHeaderWidget"
:hasBackButton="true"
backButtonAccessibleText="Change Vehicle Make"
@click-event="backButtonAction"
/>
<modelQuestion v-model="selectedModel" ref="modelQuestion" />
<modelQuestion v-model="selectedModel" ref="modelQuestion" cmsWidgetName="VehicleModelQuestion" />
</div>
</div>
</div>
@ -60,17 +60,8 @@ export default {
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.$refs.funnelSubHeader.initializeComponent(
resultMap.cmsContent.FunnelSubHeaderWidget
);
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.vehicleBanner.initializeComponent(
resultMap.cmsContent.VehicleBannerWidget
);
vm.setCmsContent(resultMap.cmsContent);
vm.$refs.modelQuestion.initializeComponent(
resultMap.cmsContent.VehicleModelQuestion,
resultMap.modelQuestionInitialData
);
});

View file

@ -1,9 +1,5 @@
// Components
import vehicleParts from "@/layouts/vehicle-parts/vehicle-parts.vue";
import funnelHeader from "@/common-components/funnel-header/funnel-header";
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 glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass-part-question";
// Supporting Files
@ -12,6 +8,7 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { nextTick } from "vue";
import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store";
// Mock our module for promises.
@ -65,101 +62,13 @@ const basePartResponse = {
describe("vehicle-parts.vue", () => {
test("Page header is initialized with api data", async (done) => {
test("Set cms content called on load", async (done) => {
//Arrange
store.getters.pageData.mockReturnValueOnce(basePartResponse);
store.getters.lineItems = { glassParts: {} }
const pageHeaderWidgetHeaderText = "Select Parts";
const { wrapper, apiPromise } = setupMocks(
{
pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText,
mountOptionsMockData: {
router: {
navigateAfterSave: jest.fn()
},
route: {
query: {
fmgPage: 'vehicle-parts',
}
},
store: {
getters: store.getters
},
}
});
//Act
vehicleParts.beforeRouteEnter.call(wrapper.vm,
{ query: { fmgPage: "vehicle-parts" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelSubHeader.methods.initializeComponent).toHaveBeenCalledWith(pageHeaderWidgetHeaderText);
done();
});
});
test("Page logo image is initialized with api data", async (done) => {
//Arrange
const SiteHeaderWidget = {
LogoImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
};
store.getters.pageData.mockReturnValueOnce(basePartResponse);
store.getters.lineItems = { glassParts: {} }
const { wrapper, apiPromise } = setupMocks(
{
SiteHeaderWidget: SiteHeaderWidget,
mountOptionsMockData: {
router: {
navigateAfterSave: jest.fn()
},
route: {
query: {
fmgPage: 'vehicle-parts',
}
},
store: {
getters: store.getters
},
}
});
//Act
vehicleParts.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-parts" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelHeader.methods.initializeComponent).toHaveBeenCalledWith(SiteHeaderWidget);
done();
});
});
test("Vehicle image is initialized with api data", async (done) => {
//Arrange
const VehicleBannerWidget = {
GenericVehicleImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
};
store.getters.pageData.mockReturnValue(basePartResponse);
store.getters.lineItems = { glassParts: {} }
const { wrapper, apiPromise } = setupMocks(
{
VehicleBannerWidget: VehicleBannerWidget,
mountOptionsMockData: {
router: {
navigateAfterSave: jest.fn()
@ -182,10 +91,11 @@ describe("vehicle-parts.vue", () => {
undefined,
(c) => c(wrapper.vm)
);
wrapper.vm.setCmsContent = jest.fn();
//Assert
apiPromise.finally(() => {
expect(vehicleBanner.methods.initializeComponent).toHaveBeenCalledWith(VehicleBannerWidget);
expect(wrapper.vm.setCmsContent).toHaveBeenCalled();
done();
});
});
@ -375,29 +285,13 @@ function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {}
settleAllPromises.mockImplementation(() => apiPromise);
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
//Mock damage initialize methods
funnelHeader.methods = { initializeComponent: jest.fn() };
vehicleBanner.methods = { initializeComponent: jest.fn() };
funnelSubHeader.methods = { initializeComponent: jest.fn() };
funnelFooter.methods = { initializeComponent: jest.fn() }
const mountOptions = getMountOptions(mountOptionsMockData,);
const wrapper = shallowMount(vehicleParts, mountOptions);
const funnelHeaderWrapper = wrapper.findComponent({ name: "funnelHeader" });
funnelHeaderWrapper.vm.initializeComponent = funnelHeader.methods.initializeComponent;
const vehicleBannerWrapper = wrapper.findComponent({ name: "vehicleBanner" });
vehicleBannerWrapper.vm.initializeComponent = vehicleBanner.methods.initializeComponent;
const funnelSubHeaderWrapper = wrapper.findComponent({ name: "funnelSubHeader", });
funnelSubHeaderWrapper.vm.initializeComponent = funnelSubHeader.methods.initializeComponent;
const funnelFooterWrapper = wrapper.findComponent({ name: "funnelFooter", });
funnelFooterWrapper.vm.initializeComponent = funnelFooter.methods.initializeComponent;
const partQuestionRearWrapper = wrapper.findComponent({ name: "glassPartQuestion", });
partQuestionRearWrapper.vm.initializeComponent = glassPartQuestion.methods.initializeComponent;
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
return { wrapper, apiPromise };
}

View file

@ -1,8 +1,8 @@
<template>
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
<funnelHeader ref="funnelHeader" />
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage="false" />
<funnelSubHeader ref="funnelSubHeader" />
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
<funnelHeader ref="funnelHeader" cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner ref="vehicleBanner" cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
<funnelSubHeader ref="funnelSubHeader" cmsWidgetName="FunnelSubHeaderWidget" />
<div class="container-fluid prevent-squish my-5">
<div class="row">
<div class="col">
@ -31,12 +31,8 @@
:colorAnswers="item.colorAnswers"
/>
</div>
<funnelFooter
ref="funnelFooter"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction"
/>
</div>
<funnelFooter cmsWidgetName="FunnelFooterWidget" ref="funnelFooter" @back-clicked="backButtonAction" @ForwardClicked="forwardButtonAction" />
</div>
</template>
<script>
@ -55,43 +51,29 @@ import { storeMutations } from "@/constants/store-mutations";
import store from "@/store";
export default {
name: "vehicle-parts",
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
// Settle promises and get results
const promiseResultMap = [
{
resultKey: "cmsContent",
promise: cmsContentPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.vehicleBanner.initializeComponent(
resultMap.cmsContent.VehicleBannerWidget
);
vm.$refs.funnelSubHeader.initializeComponent(
resultMap.cmsContent.FunnelSubHeaderWidget
);
vm.$refs.funnelFooter.initializeComponent(
resultMap.cmsContent.FunnelFooterWidget
);
name: "vehicle-parts",
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
// Settle promises and get results
const promiseResultMap = [{
resultKey: "cmsContent",
promise: cmsContentPromise,
}, ];
const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
// Glass Part Question dynamic component
Object.keys(vm.$refs)
.filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined)
.forEach((c) =>
vm.$refs[c][0].initializeComponent({
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget,
})
);
// Glass Part Question dynamic component
Object.keys(vm.$refs)
.filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined)
.forEach((c) =>
vm.$refs[c][0].initializeComponent({
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget,
})
);
// Set alertData for the page alert. These use props so we don't call
// initializeComponent here.

View file

@ -27,30 +27,6 @@ describe("style-question.vue", () => {
});
});
describe("style-question.vue", () => {
test("CMS question text is used as radio question text.", async () => {
//Arrange
const { wrapper, cmsContent } = setupMocks({
cmsQuestionText: "What style is your vehicle?",
});
//Act
styleQuestion.methods.initializeComponent.call(
wrapper.vm,
cmsContent,
null
);
//Assert
const buttonQuestionComponent = await wrapper.findComponent({
name: "buttonQuestion",
});
expect(buttonQuestionComponent.attributes("questiontext")).toBe(
"What style is your vehicle?"
);
});
});
describe("style-question.vue", () => {
test("Data from store api are used as radio question answers.", async () => {
//Arrange
@ -62,7 +38,6 @@ describe("style-question.vue", () => {
const initialData = styleQuestion.methods.loadInitialData.call(wrapper.vm);
styleQuestion.methods.initializeComponent.call(
wrapper.vm,
cmsContent,
initialData
);
@ -90,9 +65,15 @@ function setupMocks({
});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
mountOptions.propsData = {
modelValue: modelValueProp,
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(styleQuestion, mountOptions);
//Mock CMS content

View file

@ -23,14 +23,17 @@ export default {
name: "style-question",
data() {
return {
questionText: null,
styles: Array,
};
},
props: {
modelValue: String,
cmsWidgetName: String,
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
selectedValueAsArray: {
get: function() {
const modelValueAsArray = this.modelValue ? [this.modelValue] : [];
@ -56,8 +59,7 @@ export default {
}
);
},
initializeComponent(cmsContent, initialData) {
this.questionText = cmsContent.QuestionText;
initializeComponent(initialData) {
this.styles = initialData;
},
},

View file

@ -1,8 +1,5 @@
// Components
import vehicleStyle from "@/layouts/vehicle-style/vehicle-style.vue";
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import styleQuestion from "@/layouts/vehicle-style/style-question/style-question";
// Supporting files
@ -12,6 +9,7 @@ import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { storeActions } from "@/constants/store-actions";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import baseMixin from "@/mixins/base-mixin.js";
// Mock our module for promises.
jest.mock("@/helpers/layout-helper.js", () => ({
@ -35,12 +33,8 @@ jest.mock("@/store", () => ({
describe("vehicle-style.vue", () => {
test("Style question component is initized with api data", async (done) => {
//Arrange
const vehicleStyleQuestionCmsContent = {
QuestionText: "What style is your vehicle?",
};
const styleQuestionInitialData = ["2 Door", "4 Door"];
const { wrapper, apiPromise } = setupMocks({
vehicleStyleQuestionCmsContent: vehicleStyleQuestionCmsContent,
styleQuestionInitialData: styleQuestionInitialData,
});
@ -55,7 +49,6 @@ describe("vehicle-style.vue", () => {
//Assert
apiPromise.finally(() => {
expect(styleQuestion.methods.initializeComponent).toHaveBeenCalledWith(
vehicleStyleQuestionCmsContent,
styleQuestionInitialData
);
done();
@ -63,90 +56,6 @@ describe("vehicle-style.vue", () => {
});
});
describe("vehicle-style.vue", () => {
test("Page header is initailized with api data", async (done) => {
//Arrange
const pageHeaderWidgetHeaderText = "Select a style to get started";
const { wrapper, apiPromise } = setupMocks({
pageHeaderWidgetHeaderText: pageHeaderWidgetHeaderText,
});
//Act
vehicleStyle.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-style" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelSubHeader.methods.initializeComponent).toHaveBeenCalledWith(
pageHeaderWidgetHeaderText
);
done();
});
});
});
describe("vehicle-style.vue", () => {
test("Page logo image is initailized with api data", async (done) => {
//Arrange
const SiteHeaderWidget = {
LogoImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
};
const { wrapper, apiPromise } = setupMocks({
SiteHeaderWidget: SiteHeaderWidget,
});
//Act
vehicleStyle.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-style" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelHeader.methods.initializeComponent).toHaveBeenCalledWith(
SiteHeaderWidget
);
done();
});
});
});
describe("vehicle-style.vue", () => {
test("Vehicle image is initailized with api data", async (done) => {
//Arrange
const VehicleBannerWidget = {
GenericVehicleImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
};
const { wrapper, apiPromise } = setupMocks({
VehicleBannerWidget: VehicleBannerWidget,
});
//Act
vehicleStyle.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-style" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(vehicleBanner.methods.initializeComponent).toHaveBeenCalledWith(
VehicleBannerWidget
);
done();
});
});
});
describe("vehicle-style.vue", () => {
test("BackButtonAction triggers a router.navigate change", async (done) => {
//Arrange
@ -270,38 +179,13 @@ function setupMocks({
initializeComponent: jest.fn(),
};
funnelHeader.methods = {
initializeComponent: jest.fn(),
};
vehicleBanner.methods = {
initializeComponent: jest.fn(),
};
funnelSubHeader.methods = {
initializeComponent: jest.fn(),
};
const mountOptions = getMountOptions(mountOptionsMockData);
const wrapper = shallowMount(vehicleStyle, mountOptions);
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
const styleQuestionWrapper = wrapper.findComponent({ name: "styleQuestion" });
styleQuestionWrapper.vm.initializeComponent =
styleQuestion.methods.initializeComponent;
const funnelHeaderWrapper = wrapper.findComponent({ name: "funnelHeader" });
funnelHeaderWrapper.vm.initializeComponent =
funnelHeader.methods.initializeComponent;
const vehicleBannerWrapper = wrapper.findComponent({ name: "vehicleBanner" });
vehicleBannerWrapper.vm.initializeComponent =
vehicleBanner.methods.initializeComponent;
const funnelSubHeaderWrapper = wrapper.findComponent({
name: "funnelSubHeader",
});
funnelSubHeaderWrapper.vm.initializeComponent =
funnelSubHeader.methods.initializeComponent;
return { wrapper, apiPromise };
}

View file

@ -1,16 +1,16 @@
<template>
<div class="container-fluid shadow rounded-3 p-0 position-relative">
<funnelHeader ref="funnelHeader" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<div class="select-car">
<div class="select-car-form rounded text-center">
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=true />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" displayGenericVehicleImage />
<funnelSubHeader
ref="funnelSubHeader"
cmsWidgetName="FunnelSubHeaderWidget"
:hasBackButton="true"
backButtonAccessibleText="Change Vehicle Model"
@click-event="backButtonAction"
/>
<styleQuestion v-model="selectedStyle" ref="styleQuestion" />
<styleQuestion v-model="selectedStyle" ref="styleQuestion" cmsWidgetName="VehicleStyleQuestion" />
</div>
</div>
</div>
@ -60,17 +60,8 @@ export default {
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.$refs.funnelSubHeader.initializeComponent(
resultMap.cmsContent.FunnelSubHeaderWidget
);
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.vehicleBanner.initializeComponent(
resultMap.cmsContent.VehicleBannerWidget
);
vm.setCmsContent(resultMap.cmsContent);
vm.$refs.styleQuestion.initializeComponent(
resultMap.cmsContent.VehicleStyleQuestion,
resultMap.styleQuestionInitialData
);
});

View file

@ -5,12 +5,10 @@ import { nextTick } from "vue";
import { storeMutations } from "@/constants/store-mutations";
import { storeActions } from "@/constants/store-actions";
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import baseMixin from "@/mixins/base-mixin.js";
import vehicleYear from "@/layouts/vehicle-year/vehicle-year.vue";
import yearQuestion from "@/layouts/vehicle-year/year-question/year-question";
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import store from "@/store";
@ -29,16 +27,11 @@ jest.mock("@/helpers/cms-content-helper", () => ({
fetchCmsContentForPage: jest.fn(),
}));
describe("vehicle-year.vue", () => {
test("Year question component is initized with api data", async (done) => {
//Arrange
const vehicleYearQuestionCmsContent = {
QuestionText: "What year is your vehicle?",
};
const yearQuestionInitialData = ["2023", "2022", "2021"];
const { wrapper, apiPromise } = setupMocks({
vehicleYearQuestionCmsContent: vehicleYearQuestionCmsContent,
yearQuestionInitialData: yearQuestionInitialData,
});
@ -53,7 +46,6 @@ describe("vehicle-year.vue", () => {
//Assert
apiPromise.finally(() => {
expect(yearQuestion.methods.initializeComponent).toHaveBeenCalledWith(
vehicleYearQuestionCmsContent,
yearQuestionInitialData
);
done();
@ -61,64 +53,6 @@ describe("vehicle-year.vue", () => {
});
});
describe("vehicle-year.vue", () => {
test("Page logo image is initailized with api data", async (done) => {
//Arrange
const SiteHeaderWidget = {
LogoImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
};
const { wrapper, apiPromise } = setupMocks({
SiteHeaderWidget: SiteHeaderWidget,
});
//Act
vehicleYear.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-year" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(funnelHeader.methods.initializeComponent).toHaveBeenCalledWith(
SiteHeaderWidget
);
done();
});
});
});
describe("vehicle-year.vue", () => {
test("Vehicle image is initailized with api data", async (done) => {
//Arrange
const VehicleBannerWidget = {
GenericVehicleImage:
"https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
};
const { wrapper, apiPromise } = setupMocks({
VehicleBannerWidget: VehicleBannerWidget,
});
//Act
vehicleYear.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "vehicle-year" } },
undefined,
(c) => c(wrapper.vm)
);
//Assert
apiPromise.finally(() => {
expect(vehicleBanner.methods.initializeComponent).toHaveBeenCalledWith(
VehicleBannerWidget
);
done();
});
});
});
describe("vehicle-year.vue", () => {
test("arePagePrerequisitesValid should be true ", async () => {
//Arrange
@ -200,32 +134,13 @@ function setupMocks({
loadInitialData: jest.fn(),
initializeComponent: jest.fn(),
};
funnelHeader.methods = {
initializeComponent: jest.fn(),
};
vehicleBanner.methods = {
initializeComponent: jest.fn(),
};
funnelSubHeader.methods = {
initializeComponent: jest.fn(),
};
const mountOptions = getMountOptions(mountOptionsMockData);
const wrapper = shallowMount(vehicleYear, mountOptions);
const yearQuestionWrapper = wrapper.findComponent({ name: "yearQuestion" });
yearQuestionWrapper.vm.initializeComponent =
yearQuestion.methods.initializeComponent;
const funnelHeaderWrapper = wrapper.findComponent({ name: "funnelHeader" });
funnelHeaderWrapper.vm.initializeComponent =
funnelHeader.methods.initializeComponent;
const vehicleBannerWrapper = wrapper.findComponent({ name: "vehicleBanner" });
vehicleBannerWrapper.vm.initializeComponent =
vehicleBanner.methods.initializeComponent;
const funnelSubHeaderWrapper = wrapper.findComponent({
name: "funnelSubHeader",
});
funnelSubHeaderWrapper.vm.initializeComponent =
funnelSubHeader.methods.initializeComponent;
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
return { wrapper, apiPromise };
}

View file

@ -1,11 +1,11 @@
<template>
<div class="container-fluid shadow rounded-3 p-0 position-relative">
<funnelHeader ref="funnelHeader" />
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<div class="select-car">
<div class="select-car-form rounded text-center">
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=true />
<funnelSubHeader ref="funnelSubHeader" />
<yearQuestion v-model="selectedYear" ref="yearQuestion" />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" displayGenericVehicleImage />
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<yearQuestion v-model="selectedYear" ref="yearQuestion" cmsWidgetName="VehicleYearQuestion" />
</div>
</div>
</div>
@ -55,17 +55,8 @@ export default {
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.$refs.funnelSubHeader.initializeComponent(
resultMap.cmsContent.FunnelSubHeaderWidget
);
vm.$refs.funnelHeader.initializeComponent(
resultMap.cmsContent.FunnelHeaderWidget
);
vm.$refs.vehicleBanner.initializeComponent(
resultMap.cmsContent.VehicleBannerWidget
);
vm.setCmsContent(resultMap.cmsContent);
vm.$refs.yearQuestion.initializeComponent(
resultMap.cmsContent.VehicleYearQuestion,
resultMap.yearQuestionInitialData
);
});

View file

@ -27,26 +27,6 @@ describe("year-question.vue", () => {
});
});
describe("year-question.vue", () => {
test("CMS question text is used as radio question text.", async () => {
//Arrange
const { wrapper, cmsContent } = setupMocks({
cmsQuestionText: "What year is your vehicle?",
});
//Act
yearQuestion.methods.initializeComponent.call(wrapper.vm, cmsContent, null);
//Assert
const buttonQuestionComponent = await wrapper.findComponent({
name: "buttonQuestion",
});
expect(buttonQuestionComponent.attributes("questiontext")).toBe(
"What year is your vehicle?"
);
});
});
describe("year-question.vue", () => {
test("Data from store api are used as radio question answers.", async () => {
//Arrange
@ -58,7 +38,6 @@ describe("year-question.vue", () => {
const initialData = yearQuestion.methods.loadInitialData.call(wrapper.vm);
yearQuestion.methods.initializeComponent.call(
wrapper.vm,
cmsContent,
initialData
);
@ -87,10 +66,16 @@ function setupMocks({
});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn()
}
}
mountOptions.propsData = {
modelValue: modelValueProp,
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(yearQuestion, mountOptions);
//Mock CMS content

View file

@ -21,18 +21,20 @@ export default {
name: "year-question",
data() {
return {
questionText: null,
years: Array,
};
},
props: {
modelValue: String,
cmsWidgetName: String,
},
emits: ['update:modelValue'],
components: {
buttonQuestion,
},
computed: {
questionText(){
return this.getCmsContent(this.cmsWidgetName, 'QuestionText');
},
selectedValueAsArray: {
get: function() {
const modelValueAsArray = this.modelValue ? [this.modelValue] : [];
@ -51,8 +53,7 @@ export default {
{}
);
},
initializeComponent(cmsContent, initialData) {
this.questionText = cmsContent.QuestionText;
initializeComponent(initialData) {
this.years = initialData;
},
},

View file

@ -6,9 +6,17 @@ import { vehicleCategories } from "@/constants/vehicle-categories.js";
export default {
data() {
return {};
return {
cmsContentByWidget: {}
};
},
methods: {
setCmsContent(cmsContent){
this.$root.cmsContentByWidget = cmsContent;
},
getCmsContent(widgetName, fieldName){
return this.$root.cmsContentByWidget?.[widgetName]?.[fieldName] ? this.$root.cmsContentByWidget[widgetName][fieldName] : '';
},
dispatchNonBlockingStoreAction(type, payload, encodePayload = true) {
// Encode the payload if required
if (encodePayload) {

View file

@ -8,7 +8,9 @@ const fmgPageValues = {
VIN_LOOKUP: "vin-lookup",
VEHICLE_PARTS: "vehicle-parts",
PART_QUESTIONS: "part-questions",
REVEAL : "reveal",
LICENSE_PLATE_LOOKUP: "license-plate-lookup",
REVEAL: "reveal",
ESTIMATE: "estimate",
};
export { fmgPageValues };

View file

@ -8,7 +8,10 @@ const navigationScenarios = {
SELECTED_PARTS: "SELECTED_PARTS",
SELECTED_DAMAGE_WITH_SINGLE_PART: "SELECTED_DAMAGE_WITH_SINGLE_PART",
SELECTED_DAMAGE_WITH_MULTIPLE_PARTS: "SELECTED_DAMAGE_WITH_MULTIPLE_PARTS",
SELECTED_DAMAGE_WITH_PART_QUESTIONS: "SELECTED_DAMAGE_WITH_PART_QUESTIONS"
SELECTED_DAMAGE_WITH_PART_QUESTIONS: "SELECTED_DAMAGE_WITH_PART_QUESTIONS",
CONTINUING_WITH_PARTS_QUESTION: "CONTINUING_WITH_PARTS_QUESTION",
CONTINUING_WITH_MULTIPLE_PARTS: "CONTINUING_WITH_MULTIPLE_PARTS",
CONTINUING_WITH_SINGLE_PART: "CONTINUING_WITH_SINGLE_PART"
};
export { navigationScenarios };

View file

@ -115,6 +115,27 @@ const routingTable = [
},
],
},
{
fmgPageValue: fmgPageValues.LICENSE_PLATE_LOOKUP,
maps: [
{
scenario: navigationScenarios.CLICKED_BACK,
destinationFmgPageValue: fmgPageValues.ESTIMATE,
},
{
scenario: navigationScenarios.CONTINUING_WITH_PARTS_QUESTION,
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
},
{
scenario: navigationScenarios.CONTINUING_WITH_MULTIPLE_PARTS,
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS,
},
{
scenario: navigationScenarios.CONTINUING_WITH_SINGLE_PART,
destinationFmgPageValue: fmgPageValues.REVEAL,
},
],
},
];
export { routingTable };

View file

@ -90,7 +90,10 @@ export const mutations = {
updateVehicleImageColor(state, imageColor) {
state.order.vehicle.imageColor = imageColor;
},
updateIsRepair(state, isRepair) {
updateVehicleVin(state, vin) {
state.order.vehicle.vin = vin;
},
updateIsRepair(state, isRepair){
state.order.damage.isRepair = isRepair;
},
updateNumberOfChips(state, numberOfChips) {
@ -240,6 +243,16 @@ export const actions = {
},
});
},
lookupVinByPlate(context, { licensePlate, licenseState }) {
return globalMethods.callHttpClient({
method: endpoints.LookupVinByPlate.method,
endpoint: endpoints.LookupVinByPlate.url,
payload: {
licensePlate: licensePlate,
licenseState: licenseState
},
});
},
getVehicleMakes(context, { year }) {
return globalMethods.callHttpClient({
method: endpoints.GetVehicleMakes.method,
@ -284,6 +297,12 @@ export const actions = {
payload: {},
});
},
validateZip(context, {zip}) {
return globalMethods.callHttpClient({
methods: endpoints.ValidateZip.method,
endpoint: `${endpoints.ValidateZip.url}/${zip}`
})
},
// DEPENDENCY ACTIONS
resetVehicleAndDependencies(context) {

View file

@ -283,6 +283,22 @@ describe("Actions", () => {
expect(response.data).toEqual({ carId: "C00000001" });
});
it("lookupVinByPlate action, should return car data", async () => {
// Arrange
const context = state;
// Act
globalMethods.callHttpClient.mockImplementation(() => {
return Promise.resolve({ data: { carId: "C00000001" } });
});
// Assert
const response = await actions.lookupVinByPlate(context, "12345678901234567")
expect(response.data).toEqual({ carId: "C00000001" });
});
it("getVehicleMakes action, should return makes list", async () => {
// Arrange
@ -368,6 +384,22 @@ describe("Actions", () => {
expect(response.data).toEqual(["Windshield", "DriversFrontDoor"]);
});
it("validateZip action", async () => {
// Arrange
const context = state;
// Act
globalMethods.callHttpClient.mockImplementation(() => {
return Promise.resolve({ data: "43201" });
});
const response = await actions.validateZip(context, "C00000000")
// Assert
expect(response.data).toEqual("43201");
});
it("resetVehicleAndDependencies action", async () => {
// Arrange
@ -432,6 +464,21 @@ describe("Actions", () => {
});
it("resetState action", async () => {
// Arrange
const context = state;
const commit = jest.fn();
context.commit = commit;
// Act
await actions.resetState(context)
expect(commit).toBeCalledWith(storeMutations.RESET_STATE);
});
it("getRouteInfo action, returns route info", async () => {
// Arrange

View file

@ -32,6 +32,9 @@ export default {
};
},
methods: {
removeLoader(){
this.isLoaderDisplayed = false;
},
clicked() {
if (!this.isDisabled) {
this.isLoaderDisplayed = true;