Merge branch 'develop' into feature/CSR-92 and fix / update unit tests

This commit is contained in:
Max 2022-05-04 13:21:05 -04:00
commit fef7a2227e
15 changed files with 530 additions and 140 deletions

View file

@ -26,7 +26,6 @@ module.exports = {
"!src/common-components/dropdown-question/dropdown-question.vue",
"!src/common-components/textbox-question/textbox-question.vue",
"!src/helpers/validation-rules.js",
"!src/helpers/damage-helper.js",
// END
], //! means exclude from coverage.
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],

View file

@ -1,7 +1,17 @@
import {getDamageString, isGlassAvailableForCarId} from "./damage-helper";
import baseMixin from "@/mixins/base-mixin.js";
import store from "@/store";
// Mock basemixin.
jest.mock("@/mixins/base-mixin.js", () => ({
methods: {
dispatchStoreAction: jest.fn().mockImplementation(() => { return {
data: {
windshieldOptions: {availableReplacementOptions: ["windshield"]}
}
} }),
},
}));
describe("damage-helper.js", () => {
it("Should return match when multiple selected damage options are in the store", () => {
@ -59,16 +69,7 @@ import store from "@/store";
// Arrange
store.getters.damage.glassToReplace = [{location: "Windshield", name: "windshield"}];
const updatedOptions = {
data: {
windshieldOptions: {availableReplacementOptions: ["windshield"]}
}
}
// Act
baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn().mockImplementation(()=> {
return updatedOptions;
});
const isGlassAvailable = await isGlassAvailableForCarId();
// Assert
@ -79,18 +80,8 @@ import store from "@/store";
describe("damage-helper.js", () => {
it("Should return false if any mismatches between each array exist", async () => {
// Arrange
store.getters.damage.glassToReplace = [{location: "Windshield", name: "windshield"}];
store.getters.damage.glassToReplace = [{location: "Windshield", name: "sideWindow"}];
const updatedOptions = {
data: {
windshieldOptions: {availableReplacementOptions: ["Crack"]}
}
}
// Act
baseMixin.methods.dispatchNonBlockingStoreAction = jest.fn().mockImplementation(()=> {
return updatedOptions;
});
const isGlassAvailable = await isGlassAvailableForCarId();
// Assert

View file

@ -95,7 +95,7 @@ async function getLatestPageForRedirection() {
if (store.getters.vehicle.vin) {
return fmgPageValues.LICENSE_PLATE_LOOKUP;
} else {
return fmgPageValues.VIN_LOOKUP;
return fmgPageValues.ESTIMATE;
}
}
}
@ -134,7 +134,7 @@ function overrideYmmsDirectionIfNeeded(toRoute) {
/* istanbul ignore next */
function isVinRelatedPage(toRoute) {
const fmgPageValue = toRoute.query[queryStrings.FMG_PAGE];
return fmgPageValue === fmgPageValues.VIN_LOOKUP ||
fmgPageValue === fmgPageValues.LICENSE_PLATE_LOOKUP ||
fmgPageValue === fmgPageValues.ADDRESS_LOOKUP ||

View file

@ -283,7 +283,7 @@ describe("getPageToRouteExistingOrderTo", () => {
const result = await getPageToRouteExistingOrderTo(toRoute, false);
//Assert
expect(result).toBe('vin-lookup');
expect(result).toBe('estimate');
});
test("getPageToRouteExistingOrderTo, existing order, should return heritage", async () => {

View file

@ -23,6 +23,7 @@ export function getMountOptions(mockData) {
mocks.pushPageViewToGA = jest.fn();
mocks.logEvent = jest.fn();
mocks.pushExperimentsToDataLayer = jest.fn();
mocks.prependActionToMethod = jest.fn();
mocks.dispatchStoreAction = jest.fn();
mocks.dispatchStoreAction.mockImplementation((actionName) => {

View file

@ -81,7 +81,7 @@ import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import { storeMutations } from "@/constants/store-mutations";
import baseMixin from "@/mixins/base-mixin";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { navigateAfterSaveToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import { getDamageString, isGlassAvailableForCarId } from "@/helpers/damage-helper";
defineRule("service-zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));

View file

@ -8,10 +8,7 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
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";
import { storeMutations } from "@/constants/store-mutations";
import store from "@/store";
import { validate } from "vee-validate";
// Mock our module for promises.
jest.mock("@/helpers/layout-helper.js", () => ({
@ -146,24 +143,19 @@ describe("license-plate-lookup.vue", () => {
});
describe("license-plate-lookup.vue", () => {
test("Navigate forward should be called when data entered matches store data", async () => {
test("Navigate forward should be called and isCarId should be set to false when data entered matches store data on forwardButtonAction click", async () => {
// Arrange
const { wrapper } = setupMocks({
pageHeaderWidgetHeaderText: "",
mountOptionsMockData: {
router: { navigateAfterSave: jest.fn(), },
},
});
const { wrapper } = setupMocks({});
wrapper.vm.licensePlate = "TESTPLATE";
wrapper.vm.registrationZip = "12345";
wrapper.vm.validateZip = jest.fn().mockImplementation(() => {
return {data: {isServiceable: true}};
});
const vinLookup = {data: {vehicle: {carId: "TESTID"}}}
wrapper.vm.lookupVin = jest.fn().mockImplementation(() => {
return {data: {vehicle: {carId: 'TESTID'}}, catch: () => "test"};
return {catch: () => vinLookup};
});
wrapper.vm.navigateForward = jest.fn();
//Act
licensePlateLookup.beforeRouteEnter.call(
@ -176,11 +168,255 @@ describe("license-plate-lookup.vue", () => {
await wrapper.vm.forwardButtonAction();
//Assert
expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalled();
expect(wrapper.vm.navigateForward).toHaveBeenCalled();
expect(wrapper.vm.isCarIdDifferent).toEqual(false);
});
});
describe("license-plate-lookup.vue", () => {
test("Function should stop and datam isRegistrationZipServicable should be set to false when service zip entered returns false on forwardButtonAction click", async () => {
// Arrange
const { wrapper } = setupMocks({});
wrapper.vm.validateZip = jest.fn().mockImplementation(() => {
return {data: {isServiceable: false}};
});
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
//Act
licensePlateLookup.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "license-plate-lookup" } },
undefined,
(c) => c(wrapper.vm)
);
await wrapper.vm.forwardButtonAction();
//Assert
expect(wrapper.vm.isRegistrationZipServicable).toEqual(false);
});
});
describe("license-plate-lookup.vue", () => {
test("Function should stop and datam isCarIdDifferent should be set to true when carId entered doesn't match store carId or previously entered carId on forwardButtonAction click", async () => {
// Arrange
const { wrapper } = setupMocks({});
wrapper.vm.validateZip = jest.fn().mockImplementation(() => {
return {data: {isServiceable: true}};
});
const vinLookup = {data: {vehicle: {carId: "TESTID1"}}}
wrapper.vm.lookupVin = jest.fn().mockImplementation(() => {
return {catch: () => vinLookup};
});
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
//Act
licensePlateLookup.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "license-plate-lookup" } },
undefined,
(c) => c(wrapper.vm)
);
await wrapper.vm.forwardButtonAction();
//Assert
expect(wrapper.vm.isCarIdDifferent).toEqual(true);
});
});
describe("license-plate-lookup.vue", () => {
test("Navigate forward should be called and isCarId should be set to true when carId entered matches previously entered carId and rest of data entered matches store data on forwardButtonAction click", async () => {
// Arrange
const { wrapper } = setupMocks({});
wrapper.vm.validateZip = jest.fn().mockImplementation(() => {
return {data: {isServiceable: true}};
});
const vinLookup = {data: {vehicle: {carId: "TESTID1"}}}
wrapper.vm.lookupVin = jest.fn().mockImplementation(() => {
return {catch: () => vinLookup};
});
wrapper.vm.previouslyEnteredCarId = "TESTID1";
wrapper.vm.navigateForward = jest.fn();
//Act
licensePlateLookup.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "license-plate-lookup" } },
undefined,
(c) => c(wrapper.vm)
);
await wrapper.vm.forwardButtonAction();
//Assert
expect(wrapper.vm.navigateForward).toHaveBeenCalled();
expect(wrapper.vm.isCarIdDifferent).toEqual(true);
});
});
describe("license-plate-lookup.vue", () => {
test("Button Text should revert to initial value when licensePlate textfield has new text", async () => {
// Arrange
const { wrapper } = setupMocks({});
//Act
wrapper.vm.licensePlate = "NEWPLATE";
wrapper.vm.getCmsContent = jest.fn();
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled();
});
});
describe("license-plate-lookup.vue", () => {
test("Button Text should revert to initial value when registrationZip textfield has new text", async () => {
// Arrange
const { wrapper } = setupMocks({});
//Act
wrapper.vm.registrationZip = "55555";
wrapper.vm.getCmsContent = jest.fn();
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled();
});
});
describe("license-plate-lookup.vue", () => {
test("Button Text should revert to initial value when serviceZip textfield has new text", async () => {
// Arrange
const { wrapper } = setupMocks({});
//Act
wrapper.vm.serviceZip = "55555";
wrapper.vm.getCmsContent = jest.fn();
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
await wrapper.vm.$nextTick();
//Assert
expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toHaveBeenCalled();
});
});
describe("license-plate-lookup.vue", () => {
test("Dispatch reset damage and dependencies should be called if isCarIdDifferent is true and isSelectedGlassAvailableForVehicle is false when updateCustomerInfo is called", async () => {
// Arrange
const { wrapper } = setupMocks({});
//Act
wrapper.vm.isCarIdDifferent = true;
wrapper.vm.isSelectedGlassAvailableForVehicle = false;
store.commit = jest.fn();
store.dispatch = jest.fn();
licensePlateLookup.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "license-plate-lookup" } },
undefined,
(c) => c(wrapper.vm)
);
const vehicleInfo = {year: "2020", make: "honda", model: "civic", style: "2 door", carId: "TestId", category: "testCat", imageUrl: "image.jpg", imageVifNumber: "123", imageColor: "blue"}
await wrapper.vm.updateCustomerInfo('vin', vehicleInfo, 'registrationState');
//Assert
expect(store.dispatch).toHaveBeenCalled();
});
});
describe("license-plate-lookup.vue", () => {
test("NavigateAfterSave should be called if isCarIdDifferent is true and isSelectedGlassAvailableForVehicle is false when navigateForward is called", async () => {
// Arrange
const { wrapper } = setupMocks({});
//Act
wrapper.vm.isCarIdDifferent = true;
wrapper.vm.isSelectedGlassAvailableForVehicle = false;
wrapper.vm.$router.navigateAfterSave = jest.fn();
store.dispatch = jest.fn();
licensePlateLookup.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "license-plate-lookup" } },
undefined,
(c) => c(wrapper.vm)
);
await wrapper.vm.navigateForward();
//Assert
expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalled();
});
});
describe("license-plate-lookup.vue", () => {
test("navigateAfterSaveToHeritageFunnel should be called if isCarIdDifferent is false or isSelectedGlassAvailableForVehicle is true when navigateForward is called", async () => {
// Arrange
const { wrapper } = setupMocks({});
//Act
wrapper.vm.isCarIdDifferent = false;
wrapper.vm.$router.navigateAfterSaveToHeritageFunnel = jest.fn();
licensePlateLookup.beforeRouteEnter.call(
wrapper.vm,
{ query: { fmgPage: "license-plate-lookup" } },
undefined,
(c) => c(wrapper.vm)
);
await wrapper.vm.navigateForward();
//Assert
expect(wrapper.vm.$router.navigateAfterSaveToHeritageFunnel).toHaveBeenCalled();
});
});
describe("license-plate-lookup.vue", () => {
test("dispatch non blocking store action called on validate zip", async () => {
// Arrange
const { wrapper } = setupMocks({});
//Act
await wrapper.vm.validateZip("12345");
//Assert
expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalled();
});
});
describe("license-plate-lookup.vue", () => {
test("dispatch non blocking store action called on lookup vin", async () => {
// Arrange
const { wrapper } = setupMocks({});
//Act
await wrapper.vm.lookupVin("zzz123fqsfwg");
//Assert
expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalled();
});
});
function setupMocks({
@ -189,6 +425,8 @@ function setupMocks({
router: {
navigate: jest.fn(),
},
licensePlate: "TESTPLATE",
registrationZip: "12345"
},
}) {
//Mock api responses

View file

@ -5,23 +5,45 @@
ref="theForm"
v-slot="{ meta }"
>
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall px-5">
<div
class="container-fluid shadow rounded-3 p-2 position-relative make-tall px-5"
>
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false"
/>
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<div class="row my-2">
<div class="col">
<textboxQuestion cmsWidgetName="LicensePlateNumber" v-model="licensePlate" isRequired inputId="license_plate" validationRules="license-plate-required" />
<textboxQuestion
cmsWidgetName="LicensePlateNumber"
v-model="licensePlate"
isRequired
inputId="license_plate"
validationRules="license-plate-required"
/>
</div>
</div>
<div class="row my-2">
<div class="col">
<textboxQuestion cmsWidgetName="RegistrationZip" v-model="registrationZip" isRequired inputId="zip" mask="#####" validationRules="zip-required" />
<textboxQuestion
cmsWidgetName="RegistrationZip"
v-model="registrationZip"
inputId="zip"
mask="#####"
validationRules="zip-required"
/>
</div>
</div>
<div class="row my-2">
<div class="col">
<textboxQuestion cmsWidgetName="EmailAddress" v-model="email" isRequired inputId="email" validationRules="email-address-required|email-address-format" />
<textboxQuestion
cmsWidgetName="EmailAddress"
v-model="email"
inputId="email"
validationRules="email-address-required|email-address-format"
/>
</div>
</div>
<alert
@ -33,7 +55,13 @@
/>
<div class="row my-2">
<div class="col">
<textboxQuestion v-if="!isRegistrationZipServicable" cmsWidgetName="ServiceZip" v-model="serviceZip" inputId="serviceZip" validationRules="zip-required" />
<textboxQuestion
v-if="!isRegistrationZipServicable"
cmsWidgetName="ServiceZip"
v-model="serviceZip"
inputId="serviceZip"
validationRules="zip-required"
/>
</div>
</div>
<alert
@ -68,7 +96,6 @@ 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";
@ -77,24 +104,35 @@ 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 { getDamageString, isGlassAvailableForCarId } from "@/helpers/damage-helper";
import {
getDamageString,
isGlassAvailableForCarId,
} from "@/helpers/damage-helper";
import { required, regex } from "@/helpers/validation-rules";
import { Form, defineRule } from "vee-validate";
import { navigateAfterSaveToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
// DEFINE VALIDATION RULES
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
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));
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 = [
{
@ -102,16 +140,14 @@ export default {
promise: cmsContentPromise,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
// Call the "next" function to complete the transition to this page.
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
props: {
validationRules: String,
validationRules: String,
},
data() {
return {
@ -122,25 +158,47 @@ export default {
registrationZip: this.getRegistrationZipFromStore(),
email: this.getEmailFromStore(),
serviceZip: this.getServiceZipFromStore(),
previouslyEnteredCarId: '',
previouslyEnteredCarId: "",
customAlertData: {},
isSelectedGlassAvailableForVehicle: true,
};
},
mounted() {
this.attachCustomEvents();
},
computed: {
MatchedDifferentVehicleAlertHeader() {
let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "HeadlineText").replaceAll("{custom:damage}", getDamageString());
let text = this.getCmsContent(
"MatchedDifferentVehicleAlertWidget",
"HeadlineText"
).replaceAll("{custom:damage}", getDamageString());
return text;
},
MatchedDifferentVehicleAlertBody() {
let text = this.getCmsContent("MatchedDifferentVehicleAlertWidget", "BodyText").replaceAll("{custom:damage}", getDamageString()).replaceAll("{custom:plateLookupYear}", this.customAlertData?.vehicleInfo?.year).replaceAll("{custom:plateLookupMake}", this.customAlertData?.vehicleInfo?.make).replaceAll("{custom:plateLookupModel}", this.customAlertData?.vehicleInfo?.model);
let text = this.getCmsContent(
"MatchedDifferentVehicleAlertWidget",
"BodyText"
)
.replaceAll("{custom:damage}", getDamageString())
.replaceAll(
"{custom:plateLookupYear}",
this.customAlertData?.vehicleInfo?.year
)
.replaceAll(
"{custom:plateLookupMake}",
this.customAlertData?.vehicleInfo?.make
)
.replaceAll(
"{custom:plateLookupModel}",
this.customAlertData?.vehicleInfo?.model
);
return text;
},
NoServiceZipHeader() {
let text = this.getCmsContent("NoServiceZipWidget", "HeadlineText").replaceAll("{custom:zip}", this.registrationZip);
let text = this.getCmsContent(
"NoServiceZipWidget",
"HeadlineText"
).replaceAll("{custom:zip}", this.registrationZip);
return text;
},
NoServiceZipBody() {
@ -158,26 +216,35 @@ export default {
store.commit(storeMutations.UPDATE_REGISTRATION_LAST_NAME, null);
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
},
attachCustomEvents() {
this.prependActionToMethod(this, this.forwardButtonAction, () => {
this.pushEventToGA(
this.$route.query[this.queryStrings.FMG_PAGE],
this.GaActions.SUBMITTED,
this.GaLabels.LICENSE_PLATE_LOOKUP,
true
);
});
},
getLicensePlateFromStore() {
return store.getters.vehicle.registration.licensePlate
return store.getters.vehicle.registration.licensePlate;
},
getRegistrationZipFromStore() {
return store.getters.vehicle.registration.zipCode
return store.getters.vehicle.registration.zipCode;
},
getEmailFromStore() {
return store.getters.order.customer.emailAddress
return store.getters.order.customer.emailAddress;
},
getServiceZipFromStore() {
return store.getters.order.serviceLocation.zip
return store.getters.order.serviceLocation.zip;
},
backButtonAction() {
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
},
async forwardButtonAction() {
this.pushEventToGA(this.$route.query[this.queryStrings.FMG_PAGE], this.GaActions.SUBMITTED, this.GaLabels.LICENSE_PLATE_LOOKUP , true);
const zipValidation = this.serviceZip ? await this.validateZip(this.serviceZip) : await this.validateZip(this.registrationZip);
const zipValidation = this.serviceZip
? await this.validateZip(this.serviceZip)
: await this.validateZip(this.registrationZip);
if (!zipValidation.data.isServiceable) {
this.$refs.funnelFooter.removeLoader();
this.isVinValid = true;
@ -185,44 +252,58 @@ export default {
this.isCarIdDifferent = false;
return;
}
const vinLookup = await this.lookupVin(this.licensePlate, zipValidation.data.state).catch(() => {
const vinLookup = await this.lookupVin(
this.licensePlate,
zipValidation.data.state
).catch(() => {
this.$refs.funnelFooter.removeLoader();
this.isVinValid = false;
this.isCarIdDifferent = false;
return;
});
this.isCarIdDifferent = vinLookup.data.vehicle.carId !== store.getters.vehicle.carId;
if (this.isCarIdDifferent && (vinLookup.data.vehicle.carId !== this.previouslyEnteredCarId)) {
this.isCarIdDifferent =
vinLookup.data.vehicle.carId !== store.getters.vehicle.carId;
if (
this.isCarIdDifferent &&
vinLookup.data.vehicle.carId !== this.previouslyEnteredCarId
) {
this.previouslyEnteredCarId = vinLookup.data.vehicle.carId;
this.customAlertData.vehicleInfo = vinLookup.data.vehicle;
this.$refs.funnelFooter.updateButtonText(`Continue with ${vinLookup.data.vehicle.year} ${vinLookup.data.vehicle.make} ${vinLookup.data.vehicle.model}`);
this.$refs.funnelFooter.updateButtonText(
`Continue with ${vinLookup.data.vehicle.year} ${vinLookup.data.vehicle.make} ${vinLookup.data.vehicle.model}`
);
this.isVinValid = true;
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(vinLookup.data.vehicle.carId);
this.isSelectedGlassAvailableForVehicle =
await isGlassAvailableForCarId(vinLookup.data.vehicle.carId);
this.$refs.funnelFooter.removeLoader();
return;
}
this.updateStoreInfo(vinLookup.data.vin, vinLookup.data.vehicle, zipValidation.data.state);
this.updateCustomerInfo(
vinLookup.data.vin,
vinLookup.data.vehicle,
zipValidation.data.state
);
this.navigateForward();
},
navigateForward() {
if(this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle){
this.$router.navigateAfterSave(this.navigationScenarios.CLICKED_FORWARD, this.$route, {}, { displayVehicleChangeAlert: true }, {});
return;
} else {
navigateAfterSaveToHeritageFunnel(this.$route);
return;
}
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
this.$router.navigateAfterSave(
this.navigationScenarios.CLICKED_FORWARD,
this.$route,
{},
{ displayVehicleChangeAlert: true },
{}
);
return;
} else {
this.$router.navigateAfterSaveToHeritageFunnel(this.$route);
return;
}
},
validateZip(zip) {
return baseMixin.methods.dispatchStoreAction(
storeActions.VALIDATE_ZIP,
{ zip }
);
return baseMixin.methods.dispatchStoreAction(storeActions.VALIDATE_ZIP, {
zip,
});
},
lookupVin(plate, state) {
return baseMixin.methods.dispatchStoreAction(
@ -230,9 +311,9 @@ export default {
{ licensePlate: plate, licenseState: state }
);
},
updateStoreInfo(vin, vehicleInfo, registrationState) {
if(this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle){
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
updateCustomerInfo(vin, vehicleInfo, registrationState) {
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
}
store.commit(storeMutations.UPDATE_VEHICLE_VIN, vin);
store.commit(storeMutations.UPDATE_YEAR, vehicleInfo.year);
@ -240,28 +321,34 @@ export default {
store.commit(storeMutations.UPDATE_MODEL, vehicleInfo.model);
store.commit(storeMutations.UPDATE_STYLE, vehicleInfo.style);
store.commit(storeMutations.UPDATE_CAR_ID, vehicleInfo.carId);
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY, vehicleInfo.category);
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);
store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE, this.licensePlate);
store.commit(storeMutations.UPDATE_VEHICLE_CATEGORY,vehicleInfo.category);
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);
store.commit(storeMutations.UPDATE_REGISTRATION_LICENSE_PLATE,this.licensePlate);
store.commit(storeMutations.UPDATE_REGISTRATION_STATE, registrationState);
store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE, this.registrationZip);
store.commit(storeMutations.UPDATE_REGISTRATION_ZIP_CODE,this.registrationZip);
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP, this.serviceZip);
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, this.email);
},
},
watch: {
licensePlate() {
this.$refs.funnelFooter.updateButtonText(this.getCmsContent("FunnelFooterWidget", "ForwardButtonText"));
},
registrationZip() {
this.$refs.funnelFooter.updateButtonText(this.getCmsContent("FunnelFooterWidget", "ForwardButtonText"));
},
serviceZip() {
this.$refs.funnelFooter.updateButtonText(this.getCmsContent("FunnelFooterWidget", "ForwardButtonText"));
}
licensePlate() {
this.$refs.funnelFooter.updateButtonText(
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
);
},
registrationZip() {
this.$refs.funnelFooter.updateButtonText(
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
);
},
serviceZip() {
this.$refs.funnelFooter.updateButtonText(
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
);
},
},
components: {
Form,
funnelHeader,
@ -272,4 +359,4 @@ export default {
funnelFooter,
},
};
</script>
</script>

View file

@ -134,6 +134,8 @@ export default {
vm.$refs.backGlassOptions.initializeComponent(
resultMap.damageOptions.backGlassOptions.availableReplacementOptions
);
});
},
data(){
@ -149,10 +151,7 @@ export default {
}
},
mounted(){
if(this.$store.getters.vehicle.imageVifNumber){
this.pushEventToGA(this.GaCategories.EVOX, `${this.GaActions.VIF}_${this.$store.getters.vehicle.imageVifNumber}`,
this.$store.getters.vehicle.carId, true);
}
this.attachCustomEvents();
},
methods: {
arePagePrerequisitesValid() {
@ -166,6 +165,13 @@ export default {
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
},
attachCustomEvents(){
if(this.$store.getters.vehicle.imageVifNumber){
this.pushEventToGA(this.GaCategories.EVOX, `${this.GaActions.VIF}_${this.$store.getters.vehicle.imageVifNumber}`,
this.$store.getters.vehicle.carId, true);
}
},
backButtonAction() {
// route to move backwards
this.$router.navigate(

View file

@ -24,7 +24,7 @@ import { settleAllPromises } from "@/helpers/layout-helper";
import { storeMutations } from "@/constants/store-mutations";
import { storeActions } from "@/constants/store-actions";
import { experimentUniverses } from "@/constants/experiments";
import { getDeviceIdValue, getSessionIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper";
import { getDeviceIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper";
import baseMixin from "@/mixins/base-mixin";
import store from "@/store";
@ -94,7 +94,6 @@ export default {
return true;
},
resetDependentState() {
// Set
store.commit(storeMutations.UPDATE_MAKE, null);
store.commit(storeMutations.UPDATE_MODEL, null);
@ -106,7 +105,7 @@ export default {
// Invokes
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
store.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
}
},
},
components: {
yearQuestion,

View file

@ -5,13 +5,26 @@
ref="theForm"
v-slot="{ meta }"
>
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall px-5">
<div
class="container-fluid shadow rounded-3 p-2 position-relative make-tall px-5"
>
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
<vehicleBanner
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false"
/>
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
<div class="row my-2">
<div class="col">
<textboxQuestion cmsWidgetName="VinNumber" v-model="vin" inputId="vin" isRequired disableAutoFill validationRules="vin-required|vin-format" :isDisabled=isVinFieldReadOnly />
<textboxQuestion
cmsWidgetName="VinNumber"
v-model="vin"
inputId="vin"
isRequired
disableAutoFill
validationRules="vin-required|vin-format"
:isDisabled="isVinFieldReadOnly"
/>
</div>
</div>
<div class="row my-2">
@ -21,12 +34,27 @@
</div>
<div class="row my-2">
<div class="col">
<textboxQuestion cmsWidgetName="ServiceZIP" v-model="zip" inputId="zip" mask="#####" isRequired disableAutoFill validationRules="zip-required" />
<textboxQuestion
cmsWidgetName="ServiceZIP"
v-model="zip"
inputId="zip"
mask="#####"
isRequired
disableAutoFill
validationRules="zip-required"
/>
</div>
</div>
<div class="row my-2">
<div class="col">
<textboxQuestion cmsWidgetName="EmailAddress" v-model="email" inputId="email" isRequired disableAutoFill validationRules="email-address-required|email-address-format" />
<textboxQuestion
cmsWidgetName="EmailAddress"
v-model="email"
inputId="email"
isRequired
disableAutoFill
validationRules="email-address-required|email-address-format"
/>
</div>
</div>
<alert
@ -76,7 +104,7 @@
<funnelFooter
cmsWidgetName="FunnelFooterWidget"
ref="funnelFooter"
:isDisabled="!meta.valid"
:isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction"
/>
@ -109,10 +137,22 @@ import { navigateAfterSaveToHeritageFunnel } from "@/helpers/heritage-integratio
// DEFINE VALIDATION RULES
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));
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
)
);
defineRule("vin-required", required(errorMessages.VIN_REQUIRED));
defineRule("vin-format", regex(/^[A-HJ-NPR-Z0-9]{17}$/, errorMessages.VIN_FORMAT));
defineRule(
"vin-format",
regex(/^[A-HJ-NPR-Z0-9]{17}$/, errorMessages.VIN_FORMAT)
);
export default {
name: "vin-lookup",
@ -120,7 +160,6 @@ export default {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
// Settle promises and get results
const promiseResultMap = [
{
@ -211,12 +250,20 @@ export default {
getZipFromStore(){
return store.getters.vehicle.registration.zipCode
},
attachCustomEvents() {
this.prependActionToMethod(this, this.forwardButtonAction, () => {
this.pushEventToGA(
this.$route.query[this.queryStrings.FMG_PAGE],
this.GaActions.SUBMITTED,
this.GaLabels.VINLOOKUP,
true
);
});
},
backButtonAction() {
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
},
async forwardButtonAction() {
this.pushEventToGA(this.$route.query[this.queryStrings.FMG_PAGE], this.GaActions.SUBMITTED, this.GaLabels.VINLOOKUP , true);
const zipValidation = await this.validateZip(this.zip);
if (!zipValidation.data.isServiceable) {
this.customAlertData.zip = this.zip;
@ -257,10 +304,9 @@ export default {
}
},
validateZip(zip) {
return baseMixin.methods.dispatchStoreAction(
storeActions.VALIDATE_ZIP,
{ zip }
);
return baseMixin.methods.dispatchStoreAction(storeActions.VALIDATE_ZIP, {
zip,
});
},
lookupVehicle(vin) {
return baseMixin.methods.dispatchStoreAction(

View file

@ -81,7 +81,15 @@ export default {
// Push to the data layer with the Google Custom Dimension Index.
pushToDataLayerIfDefined(experimentWithDimension);
});
}
},
prependActionToMethod(object, method, actionToPrepend) {
const baseMethod = object[method.name];
object[method.name] = function () {
actionToPrepend.apply(this, arguments);
return baseMethod.apply(object, arguments);
};
},
},
computed: {
analyticsPageEvents() {
@ -96,7 +104,7 @@ export default {
GaLabels() {
return GaLabels;
}
}
},
};
function pushToDataLayerIfDefined(data) {

View file

@ -79,5 +79,4 @@ describe("analyticsMixin.js", () => {
expect(window.dataLayer).toEqual([ { settings_5: {"Google Custom Dimension Index": "5"}, variationName_5: 'test', universeName_5: 'testUniverse' } ]);
});
});

View file

@ -126,6 +126,7 @@ const router = createRouter({
router.afterEach(async (to, from) => {
// Push page view to GA
analyticsMixin.methods.pushPageViewToGA(to.query[queryStrings.FMG_PAGE]);
const assignedExperiments = await baseMixin.methods.dispatchStoreAction(storeActions.GET_EXPERIMENTS_BY_USER_FOR_GA, { userId: getDeviceIdValue() });
analyticsMixin.methods.pushExperimentsToDataLayer(assignedExperiments);
});

View file

@ -47,6 +47,21 @@ describe("alert.vue", () => {
expect(wrapperDiv.classes()).toContain('warning')
});
it("Should update alert Headline to manualHeadline datam entered and alert copy to manualCopy datam entered when no cmsWidgetName entered", async () => {
// Arrange
const wrapper = shallowMount(alert, {
propsData: {
manualHeadline: 'testHeader',
manualCopy: 'testCopy'
},
mixins: [mockMixin]
});
// Assert
expect(wrapper.vm.alertHeadline).toBe("testHeader");
expect(wrapper.vm.alertCopy).toBe("testCopy");
});
});
const mockMixin = {