Merge pull request #381 from Safelite/refactor/linting6

Linting for first 10 layouts
This commit is contained in:
DavidAtSafelite 2023-07-26 14:05:46 -04:00 committed by GitHub
commit 8c39458d91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1268 additions and 1400 deletions

View file

@ -1,5 +1,5 @@
// Components // Components
import addressLookup from '@/layouts/address-lookup/address-lookup.vue'; import addressLookup from '@/layouts/address-lookup/address-lookup';
// Supporting Files // Supporting Files
import { settleAllPromises } from '@/helpers/layout-helper.js'; import { settleAllPromises } from '@/helpers/layout-helper.js';
@ -18,6 +18,79 @@ jest.mock('@/helpers/layout-helper.js', () => ({
settleAllPromises: jest.fn() settleAllPromises: jest.fn()
})); }));
function setupMocks({
lookupVinbyAddressResponse,
partsOrQuestions = [],
isStatePermissible = true,
vinVehicles = [],
carId = 'C0000',
route = null
}) {
useMainStore().lookupVinByAddress = jest.fn().mockImplementation(() => Promise.resolve({
data: lookupVinbyAddressResponse || {
isStatePermissible: true,
vinVehicles: [
{
vin: 'TEST_VIN',
vehicle: {
carId: 'CARID'
}
}
]
}
}));
useMainStore().getPartsOrQuestions = jest.fn().mockImplementation(() => Promise.resolve({
data: {
partsOrQuestions
}
}));
const wrapper = shallowMount(addressLookup,
getMountOptions({
route: route || undefined,
router: {
navigate: jest.fn()
},
mainStore: {
order: {
vehicle: {
carId,
registration: {
licensePlate: 'TESTPLATE',
zipCode: '12345'
}
},
customer: {
emailAddress: 'test@test.com'
},
serviceLocation: {
zipCode: '11111'
}
}
}
}));
const apiResponses = {
vinLookupResponse: {
isStatePermissible,
vinVehicles
}
};
settleAllPromises.mockImplementation(() => apiResponses);
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => '');
wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.$refs.siteFooter.updateButtonText = jest.fn();
wrapper.vm.$refs.siteFooter.removeLoader = jest.fn();
wrapper.vm.$refs.siteFooter.disableForwardButton = jest.fn();
wrapper.vm.$refs.siteFooter.enableForwardAction = jest.fn();
return { wrapper };
}
describe('address-lookup.vue', () => { describe('address-lookup.vue', () => {
describe('page level alerts', () => { describe('page level alerts', () => {
test('if the address matches a different vehicle display the Matched Different VehicleAlert', async () => { test('if the address matches a different vehicle display the Matched Different VehicleAlert', async () => {
@ -51,9 +124,7 @@ describe('address-lookup.vue', () => {
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect(wrapper.findComponent({ ref: 'alertMatchedDifferentVehicle' }).isVisible()).toBe( expect(wrapper.findComponent({ ref: 'alertMatchedDifferentVehicle' }).isVisible()).toBe(true);
true
);
}); });
test('if the address matches two identical YMM vehicles, then display Two Identical YMM Vehicle alert', async () => { test('if the address matches two identical YMM vehicles, then display Two Identical YMM Vehicle alert', async () => {
@ -88,9 +159,7 @@ describe('address-lookup.vue', () => {
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect(wrapper.findComponent({ ref: 'alertMatchedTwoIdenticalYMMVehicle' }).isVisible()).toBe( expect(wrapper.findComponent({ ref: 'alertMatchedTwoIdenticalYMMVehicle' }).isVisible()).toBe(true);
true
);
}); });
test('if the looking up VIN by address is not allowed in the state selected display the Vin Lookup By HomeAddress Not Allowed Alert', async () => { test('if the looking up VIN by address is not allowed in the state selected display the Vin Lookup By HomeAddress Not Allowed Alert', async () => {
@ -135,9 +204,7 @@ describe('address-lookup.vue', () => {
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect( expect(wrapper.findComponent({ ref: 'alertVinLookupsByHomeAddressNotAllowed' }).isVisible()).toBe(true);
wrapper.findComponent({ ref: 'alertVinLookupsByHomeAddressNotAllowed' }).isVisible()
).toBe(true);
}); });
test('if no vehicles found, display Vin Not Found alert', async () => { test('if no vehicles found, display Vin Not Found alert', async () => {
@ -278,13 +345,11 @@ describe('address-lookup.vue', () => {
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES,
navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES,
undefined, undefined,
{}, {},
{}, {},
carsFound carsFound);
);
}); });
test('if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page', async () => { test('if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page', async () => {
@ -310,7 +375,7 @@ describe('address-lookup.vue', () => {
useMainStore().order.vehicle.carId = 'CARID'; useMainStore().order.vehicle.carId = 'CARID';
let carsFound = [ const carsFound = [
{ {
vin: 'TEST_VIN2', vin: 'TEST_VIN2',
vehicle: { vehicle: {
@ -323,12 +388,10 @@ describe('address-lookup.vue', () => {
await wrapper.vm.navigateForward(carsFound); await wrapper.vm.navigateForward(carsFound);
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
undefined, undefined,
{}, {},
{ displayVehicleChangeAlert: true } { displayVehicleChangeAlert: true });
);
}); });
test('single car was found and matches entered vehicle => navigateForwardWithSingleCarMatch', async () => { test('single car was found and matches entered vehicle => navigateForwardWithSingleCarMatch', async () => {
@ -391,84 +454,3 @@ describe('address-lookup.vue', () => {
}); });
}); });
}); });
function setupMocks({
lookupVinbyAddressResponse,
partsOrQuestions = [],
isStatePermissible = true,
vinVehicles = [],
carId = 'C0000',
route = null
}) {
useMainStore().lookupVinByAddress = jest.fn().mockImplementation(() => {
return Promise.resolve({
data: lookupVinbyAddressResponse
? lookupVinbyAddressResponse
: {
isStatePermissible: true,
vinVehicles: [
{
vin: 'TEST_VIN',
vehicle: {
carId: 'CARID'
}
}
]
}
});
});
useMainStore().getPartsOrQuestions = jest.fn().mockImplementation(() => {
return Promise.resolve({
data: {
partsOrQuestions: partsOrQuestions
}
});
});
const wrapper = shallowMount(
addressLookup,
getMountOptions({
route: route ? route : undefined,
router: {
navigate: jest.fn()
},
mainStore: {
order: {
vehicle: {
carId: carId,
registration: {
licensePlate: 'TESTPLATE',
zipCode: '12345'
}
},
customer: {
emailAddress: 'test@test.com'
},
serviceLocation: {
zipCode: '11111'
}
}
}
})
);
const apiResponses = {
vinLookupResponse: {
isStatePermissible: isStatePermissible,
vinVehicles: vinVehicles
}
};
settleAllPromises.mockImplementation(() => apiResponses);
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => '');
wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.$refs.siteFooter.updateButtonText = jest.fn();
wrapper.vm.$refs.siteFooter.removeLoader = jest.fn();
wrapper.vm.$refs.siteFooter.disableForwardButton = jest.fn();
wrapper.vm.$refs.siteFooter.enableForwardAction = jest.fn();
return { wrapper };
}

View file

@ -1,5 +1,9 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <siteHeader cmsWidgetName="SiteHeaderWidget" />
@ -9,51 +13,56 @@
<div class="col"> <div class="col">
<div class="select-car-form rounded"> <div class="select-car-form rounded">
<vehicleBanner <vehicleBanner
class="mt-2 mb-4" ref="vehicleBanner"
cmsWidgetName="VehicleBannerWidget" class="mt-2 mb-4"
ref="vehicleBanner" cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" /> :displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" ref="siteSubHeader" class="mb-5" /> <siteSubHeader
ref="siteSubHeader"
cmsWidgetName="SiteSubHeaderWidget"
class="mb-5" />
<alert <alert
ref="alertVinNotFound" v-if="displayVinNotFoundAlert"
v-if="displayVinNotFoundAlert" ref="alertVinNotFound"
class="mb-4 mt-4" class="mb-4 mt-4"
cmsWidgetName="AlertVinNotFoundWidget" cmsWidgetName="AlertVinNotFoundWidget"
alertClass="alert-danger" alertClass="alert-danger"
v-bind:isDismissible="false" /> :isDismissible="false" />
<alert <alert
ref="alertMatchedDifferentVehicle" v-if="displayMatchedDifferentVehicleAlert"
v-if="displayMatchedDifferentVehicleAlert" ref="alertMatchedDifferentVehicle"
class="mb-4 mt-4" class="mb-4 mt-4"
:manualHeadline="AlertMatchedDifferentVehicleHeader" :manualHeadline="AlertMatchedDifferentVehicleHeader"
:manualCopy="AlertMatchedDifferentVehicleBody" :manualCopy="AlertMatchedDifferentVehicleBody"
alertClass="alert-warning" alertClass="alert-warning"
v-bind:isDismissible="false" /> :isDismissible="false" />
<alert <alert
ref="alertMatchedTwoIdenticalYMMVehicle" v-if="displayMatchedTwoIdenticalYMMVehicleAlert"
v-if="displayMatchedTwoIdenticalYMMVehicleAlert" ref="alertMatchedTwoIdenticalYMMVehicle"
class="mb-4 mt-4" class="mb-4 mt-4"
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget" cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader" :manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader"
:manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody" :manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody"
alertClass="alert-warning" alertClass="alert-warning"
v-bind:isDismissible="false" /> :isDismissible="false" />
<alert <alert
ref="alertVinLookupsByHomeAddressNotAllowed" v-if="displayVinLookupByHomeAddressNotAllowedAlert"
v-if="displayVinLookupByHomeAddressNotAllowedAlert" ref="alertVinLookupsByHomeAddressNotAllowed"
class="mt-4" class="mt-4"
cmsWidgetName="AlertVinLookupsByHomeAddressNotAllowedWidget" cmsWidgetName="AlertVinLookupsByHomeAddressNotAllowedWidget"
alertClass="alert-danger" alertClass="alert-danger"
v-bind:isDismissible="false" /> :isDismissible="false" />
<customerQuestions ref="customerQuestions" v-model="customerQuestions" /> <customerQuestions
ref="customerQuestions"
v-model="customerQuestions" />
<siteFooter <siteFooter
class="mt-5" ref="siteFooter"
cmsWidgetName="SiteFooterWidget" class="mt-5"
ref="siteFooter" cmsWidgetName="SiteFooterWidget"
:isDisabled="!meta.valid" :isDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction" :isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction" @ForwardClicked="forwardButtonAction"
:isForwardActionDisabled="!meta.valid" /> @back-clicked="backButtonAction" />
</div> </div>
</div> </div>
</div> </div>
@ -67,7 +76,7 @@
<script> <script>
// Components // Components
import baseFormMixin from '@/mixins/base-form-mixin'; import baseFormMixin from '@/mixins/base-form-mixin';
import siteHeader from '@/iss-components/site-header/site-header.vue'; import siteHeader from '@/iss-components/site-header/site-header';
import siteFooter from '@/iss-components/site-footer/site-footer'; import siteFooter from '@/iss-components/site-footer/site-footer';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner'; import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header'; import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
@ -84,10 +93,21 @@ import { routerParams } from '@/router/router-constants/router-params';
import { getDamageString, isGlassAvailableForCarId } from '@/helpers/damage-helper'; import { getDamageString, isGlassAvailableForCarId } from '@/helpers/damage-helper';
import vinPagesMixin from '@/mixins/vin-pages-mixin'; import vinPagesMixin from '@/mixins/vin-pages-mixin';
import { useMainStore } from '@/store' import { useMainStore } from '@/store';
export default { export default {
name: 'address-lookup', name: 'address-lookup',
components: {
siteHeader,
siteFooter,
vehicleBanner,
siteSubHeader,
customerQuestions,
textboxQuestion,
alert,
// eslint-disable-next-line vue/no-reserved-component-names
Form
},
mixins: [baseFormMixin, vinPagesMixin], mixins: [baseFormMixin, vinPagesMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
@ -122,6 +142,52 @@ export default {
forwardButtonCarStyle: '' forwardButtonCarStyle: ''
}; };
}, },
computed: {
AlertMatchedDifferentVehicleHeader() {
return this.getCmsContent('AlertMatchedDifferentVehicleWidget',
'HeadlineText').replaceAll('{custom:damage}', getDamageString());
},
AlertMatchedDifferentVehicleBody() {
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return this.getCmsContent('AlertMatchedDifferentVehicleWidget', 'BodyText')
.replaceAll('{custom:damage}', getDamageString())
.replaceAll('{custom:vinYmmFound}', vinYmmFound)
.replaceAll('{custom:vinYmmExpected}', vinYmmExpected);
},
AlertMatchedTwoIdenticalYMMVehicleHeader() {
return this.getCmsContent('AlertMatchedTwoIdenticalYMMVehicleWidget',
'HeadlineText').replaceAll('{custom:damage}', getDamageString());
},
AlertMatchedTwoIdenticalYMMVehicleBody() {
const vinYmmsFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model} ${this.customAlertData?.vehicleInfo?.style}`;
const vinYmmsExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model} ${this.mainStore.order.vehicle.style}`;
return this.getCmsContent('AlertMatchedTwoIdenticalYMMVehicleWidget', 'BodyText')
.replaceAll('{custom:damage}', getDamageString())
.replaceAll('{custom:vinYmmsFound}', vinYmmsFound)
.replaceAll('{custom:vinYmmsExpected}', vinYmmsExpected);
},
isTwoIdenticalYMMVehicleFound() {
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return (vinYmmFound.toLowerCase() == vinYmmExpected.toLowerCase());
}
},
watch: {
customerQuestions: {
handler() {
// if they modify one of the lookup fields (address, city, state, zipCode, or lastName), then modify the button text back to "Get my personalized quote"
this.$refs.siteFooter.updateButtonText(this.getCmsContent('siteFooterWidget', 'ForwardButtonText'));
this.resetWarningsAndErrors();
},
deep: true
}
},
mounted() {
this.attachCustomEvents();
},
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
return this.mainStore.order.vehicle.carId !== null; return this.mainStore.order.vehicle.carId !== null;
@ -133,12 +199,10 @@ export default {
}, },
attachCustomEvents() { attachCustomEvents() {
this.prependActionToMethod(this, this.forwardButtonAction, () => { this.prependActionToMethod(this, this.forwardButtonAction, () => {
this.pushEventToGA( this.pushEventToGA(this.$route.query[this.queryStrings.ISS_PAGE],
this.$route.query[this.queryStrings.ISS_PAGE],
this.GaActions.SUBMITTED, this.GaActions.SUBMITTED,
this.GaLabels.ADDRESS_LOOKUP, this.GaLabels.ADDRESS_LOOKUP,
true true);
);
}); });
}, },
@ -175,7 +239,7 @@ export default {
const carsFound = resultMap.vinLookupResponse.vinVehicles; const carsFound = resultMap.vinLookupResponse.vinVehicles;
// Handle cases for different amounts of VINS found for the address. // Handle cases for different amounts of VINS found for the address.
if (carsFound.length == 1) { if (carsFound.length === 1) {
// Single VIN found // Single VIN found
const carFound = carsFound[0].vehicle; const carFound = carsFound[0].vehicle;
@ -186,19 +250,15 @@ export default {
this.customAlertData.vehicleInfo = carFound; this.customAlertData.vehicleInfo = carFound;
if (this.isTwoIdenticalYMMVehicleFound) { if (this.isTwoIdenticalYMMVehicleFound) {
this.displayMatchedTwoIdenticalYMMVehicleAlert = true; this.displayMatchedTwoIdenticalYMMVehicleAlert = true;
this.forwardButtonCarStyle= carFound.style; this.forwardButtonCarStyle = carFound.style;
} else { } else {
this.displayMatchedDifferentVehicleAlert = true; this.displayMatchedDifferentVehicleAlert = true;
} }
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId( this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(carFound.carId);
carFound.carId
);
// Update button "Continue with..." // Update button "Continue with..."
this.$refs.siteFooter.updateButtonText( this.$refs.siteFooter.updateButtonText(`Continue with ${carFound.year} ${carFound.make} ${carFound.model} ${this.forwardButtonCarStyle}`);
`Continue with ${carFound.year} ${carFound.make} ${carFound.model} ${this.forwardButtonCarStyle}`
);
return this.$refs.siteFooter.removeLoader(); return this.$refs.siteFooter.removeLoader();
} }
@ -206,9 +266,7 @@ export default {
vehicleInfoToCommit = Object.assign(carFound, { vin: carsFound[0].vin }); vehicleInfoToCommit = Object.assign(carFound, { vin: carsFound[0].vin });
} else if (carsFound.length > 1) { } else if (carsFound.length > 1) {
// If multiple cars were found and one and only one of them matches the carId entered, save the vehicle info // If multiple cars were found and one and only one of them matches the carId entered, save the vehicle info
const matchingCars = carsFound.filter( const matchingCars = carsFound.filter((vin) => vin.vehicle.carId === this.mainStore.order.vehicle.carId);
(vin) => vin.vehicle.carId === this.mainStore.order.vehicle.carId
);
if (matchingCars.length === 1) { if (matchingCars.length === 1) {
vehicleInfoToCommit = Object.assign(matchingCars[0].vehicle, { vehicleInfoToCommit = Object.assign(matchingCars[0].vehicle, {
@ -223,55 +281,47 @@ export default {
} }
// Save vehicle, customer, service and registration information // Save vehicle, customer, service and registration information
await useMainStore().saveRegistrationAddressLookup( await useMainStore().saveRegistrationAddressLookup({
{ isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle,
isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle, vehicleInfo:
vehicleInfo:
Object.keys(vehicleInfoToCommit).length === 0 Object.keys(vehicleInfoToCommit).length === 0
? null ? null
: vehicleInfoToCommit, : vehicleInfoToCommit,
registrationInfo: { registrationInfo: {
firstName: this.customerQuestions.firstName, firstName: this.customerQuestions.firstName,
lastName: this.customerQuestions.lastName, lastName: this.customerQuestions.lastName,
address: this.customerQuestions.addressQuestions.streetAddress, address: this.customerQuestions.addressQuestions.streetAddress,
city: this.customerQuestions.addressQuestions.city, city: this.customerQuestions.addressQuestions.city,
state: this.customerQuestions.addressQuestions.state, state: this.customerQuestions.addressQuestions.state,
zipCode: this.customerQuestions.addressQuestions.zipCode zipCode: this.customerQuestions.addressQuestions.zipCode
} }
}, },
false false);
);
return await this.navigateForward(carsFound); return this.navigateForward(carsFound);
}, },
async navigateForward(carsFound) { async navigateForward(carsFound) {
// Match vehicles found to vehicles in state. // Match vehicles found to vehicles in state.
const matchingCars = carsFound.filter( const matchingCars = carsFound.filter((car) => car.vehicle.carId === useMainStore().order.vehicle.carId);
(car) => car.vehicle.carId === useMainStore().order.vehicle.carId
);
// If a different vehicle is found than the one entered and the selected glass is not available for that vehicle then navigate back to "vehicle-damage" // If a different vehicle is found than the one entered and the selected glass is not available for that vehicle then navigate back to "vehicle-damage"
// display vehicle changed alert on that page. // display vehicle changed alert on that page.
if ( if (
this.isCarIdDifferent && this.isCarIdDifferent
!this.isSelectedGlassAvailableForVehicle && !this.isSelectedGlassAvailableForVehicle
) { ) {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$route, this.$route,
{}, {},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true } { [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true });
);
} else if (matchingCars.length === 1) { } else if (matchingCars.length === 1) {
await this.navigateForwardWithSingleCarMatch(); await this.navigateForwardWithSingleCarMatch();
} else { } else {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES,
this.navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES,
this.$route, this.$route,
{}, {},
{}, {},
carsFound carsFound);
);
} }
}, },
resetWarningsAndErrors() { resetWarningsAndErrors() {
@ -280,68 +330,6 @@ export default {
this.displayVinLookupByHomeAddressNotAllowedAlert = false; this.displayVinLookupByHomeAddressNotAllowedAlert = false;
this.$refs.siteFooter.enableForwardAction(); this.$refs.siteFooter.enableForwardAction();
} }
},
mounted() {
this.attachCustomEvents();
},
computed: {
AlertMatchedDifferentVehicleHeader() {
return this.getCmsContent(
'AlertMatchedDifferentVehicleWidget',
'HeadlineText'
).replaceAll('{custom:damage}', getDamageString());
},
AlertMatchedDifferentVehicleBody() {
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return this.getCmsContent('AlertMatchedDifferentVehicleWidget', 'BodyText')
.replaceAll('{custom:damage}', getDamageString())
.replaceAll('{custom:vinYmmFound}', vinYmmFound)
.replaceAll('{custom:vinYmmExpected}', vinYmmExpected);
},
AlertMatchedTwoIdenticalYMMVehicleHeader() {
return this.getCmsContent(
'AlertMatchedTwoIdenticalYMMVehicleWidget',
'HeadlineText'
).replaceAll('{custom:damage}', getDamageString());
},
AlertMatchedTwoIdenticalYMMVehicleBody() {
const vinYmmsFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model} ${this.customAlertData?.vehicleInfo?.style}`;
const vinYmmsExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model} ${this.mainStore.order.vehicle.style}`;
return this.getCmsContent('AlertMatchedTwoIdenticalYMMVehicleWidget', 'BodyText')
.replaceAll('{custom:damage}', getDamageString())
.replaceAll('{custom:vinYmmsFound}', vinYmmsFound)
.replaceAll('{custom:vinYmmsExpected}', vinYmmsExpected);
},
isTwoIdenticalYMMVehicleFound() {
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return (vinYmmFound.toLowerCase()==vinYmmExpected.toLowerCase());
}
},
watch: {
customerQuestions: {
handler(newValue) {
// if they modify one of the lookup fields (address, city, state, zipCode, or lastName), then modify the button text back to "Get my personalized quote"
this.$refs.siteFooter.updateButtonText(
this.getCmsContent('siteFooterWidget', 'ForwardButtonText')
);
this.resetWarningsAndErrors();
},
deep: true
}
},
components: {
siteHeader,
siteFooter,
vehicleBanner,
siteSubHeader,
customerQuestions,
textboxQuestion,
alert,
Form,
} }
}; };
</script> </script>

View file

@ -1,20 +1,21 @@
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import customerQuestions from '@/layouts/address-lookup/customer-questions/customer-questions'; import customerQuestions from '@/layouts/address-lookup/customer-questions/customer-questions';
const customerModel = { // const customerModel = {
addressQuestions: { // addressQuestions: {
streetAddress: '', // streetAddress: '',
city: '', // city: '',
state: '', // state: '',
zipCode: '' // zipCode: ''
}, // },
firstName: '', // firstName: '',
lastName: '', // lastName: '',
emailAddress: '' // emailAddress: ''
}; // };
describe('customerQuestions.vue', () => { describe('customerQuestions.vue', () => {
it('Should render customerQuestions sub-components (addressQuestions, first name, last name, and email textbox-questions)', async () => { it('Should render customerQuestions sub-components '
+ 'addressQuestions, first name, last name, and email textbox - questions)', async () => {
// Arrange // Arrange
const wrapper = shallowMount(customerQuestions); const wrapper = shallowMount(customerQuestions);

View file

@ -1,11 +1,13 @@
<template> <template>
<addressQuestions ref="addressQuestions" v-model="customerModel.addressQuestions" /> <addressQuestions
ref="addressQuestions"
v-model="customerModel.addressQuestions" />
<div class="row mb-4"> <div class="row mb-4">
<div class="col"> <div class="col">
<textboxQuestion <textboxQuestion
cmsWidgetName="FirstNameQuestionWidget"
v-model="customerModel.firstName"
ref="firstName" ref="firstName"
v-model="customerModel.firstName"
cmsWidgetName="FirstNameQuestionWidget"
inputId="08497a2efd9a4a73a70360ab47b4838d" inputId="08497a2efd9a4a73a70360ab47b4838d"
disableAutoFill disableAutoFill
:validationRules="rules.firstName" /> :validationRules="rules.firstName" />
@ -14,9 +16,9 @@
<div class="row mb-4"> <div class="row mb-4">
<div class="col"> <div class="col">
<textboxQuestion <textboxQuestion
cmsWidgetName="LastNameQuestionWidget"
v-model="customerModel.lastName"
ref="lastName" ref="lastName"
v-model="customerModel.lastName"
cmsWidgetName="LastNameQuestionWidget"
inputId="0030e56a57e74a4ab92de7fb8e97fec5" inputId="0030e56a57e74a4ab92de7fb8e97fec5"
disableAutoFill disableAutoFill
:validationRules="rules.lastName" /> :validationRules="rules.lastName" />
@ -25,13 +27,16 @@
</template> </template>
<script> <script>
import addressQuestions from '@/iss-components/address-questions/address-questions.vue'; import addressQuestions from '@/iss-components/address-questions/address-questions';
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue'; import textboxQuestion from '@/digital-components/textbox-question/textbox-question';
import globalRules from '@/constants/global-rules'; import globalRules from '@/constants/global-rules';
export default { export default {
name: 'customer-questions', name: 'customer-questions',
emits: ['update:modelValue'], // The component emits an event components: {
addressQuestions,
textboxQuestion
}, // The component emits an event
props: { props: {
modelValue: { modelValue: {
type: Object, type: Object,
@ -50,6 +55,7 @@ export default {
}, },
validationRules: String validationRules: String
}, },
emits: ['update:modelValue'],
data() { data() {
return { return {
rules: { rules: {
@ -60,17 +66,13 @@ export default {
}, },
computed: { computed: {
customerModel: { customerModel: {
get: function () { get() {
return this.modelValue; return this.modelValue;
}, },
set: function (newValue) { set(newValue) {
this.$emit('update:modelValue', newValue); this.$emit('update:modelValue', newValue);
} }
} }
},
components: {
addressQuestions,
textboxQuestion
} }
}; };
</script> </script>

View file

@ -1,7 +1,42 @@
import addressVehiclesQuestion from '@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue'; import addressVehiclesQuestion from '@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { getMountOptions } from '@/helpers/unit-test-helper.js';
function setupMocks({
modelValueProp = 'TESTCAR',
cmsQuestionText = 'CMS text goes here'
}) {
const mountOptions = getMountOptions();
// Mock props
mountOptions.propsData = {
modelValue: modelValueProp
};
const wrapper = shallowMount(addressVehiclesQuestion, mountOptions);
// Mock CMS content
const cmsContent = {
QuestionText: cmsQuestionText
};
return { wrapper, cmsContent };
}
const mockMixin = {
methods: {
getCmsContent: jest.fn((contentName) => {
if (contentName === 'AlertMatchedDifferentVehicleWidget') {
return 'AlertMatchedDifferentVehicleWidgetTestReturn';
}
if (contentName === 'AlertMatchedTwoIdenticalYMMVehicleWidget') {
return 'AlertMatchedTwoIdenticalYMMVehicleWidgetTestReturn';
}
return null;
}),
vehicles: jest.fn(() => [{ vehicle: 'test' }])
}
};
describe('address-vehicles-question.vue', () => { describe('address-vehicles-question.vue', () => {
test('Selected vehicle is emitted upon selection', async () => { test('Selected vehicle is emitted upon selection', async () => {
// Arrange // Arrange
@ -14,13 +49,13 @@ describe('address-vehicles-question.vue', () => {
// Assert // Assert
expect(wrapper.emitted()['update:modelValue'][0]).toEqual([ expect(wrapper.emitted()['update:modelValue'][0]).toEqual([
{ selectedVehicle: '2016 Jaguar F-Type' }, { selectedVehicle: '2016 Jaguar F-Type' }
]); ]);
}); });
test('Should return content for differentVehicleAlertHeader', () => { test('Should return content for differentVehicleAlertHeader', () => {
// Arrange // Arrange
const wrapper = shallowMount(addressVehiclesQuestion, { const wrapper = shallowMount(addressVehiclesQuestion, {
mixins: [mockMixin], mixins: [mockMixin]
}); });
// Assert // Assert
@ -62,40 +97,3 @@ describe('address-vehicles-question.vue', () => {
expect(wrapper.vm.AlertMatchedTwoIdenticalYMMVehicleBody).toEqual('AlertMatchedTwoIdenticalYMMVehicleWidgetTestReturn'); expect(wrapper.vm.AlertMatchedTwoIdenticalYMMVehicleBody).toEqual('AlertMatchedTwoIdenticalYMMVehicleWidgetTestReturn');
}); });
}); });
const mockMixin = {
methods: {
getCmsContent: jest.fn((contentName) => {
if (contentName === 'AlertMatchedDifferentVehicleWidget') {
return 'AlertMatchedDifferentVehicleWidgetTestReturn';
}
if (contentName === 'AlertMatchedTwoIdenticalYMMVehicleWidget') {
return 'AlertMatchedTwoIdenticalYMMVehicleWidgetTestReturn';
}
return null;
}),
vehicles: jest.fn(() => {
return [{ vehicle: 'test' }];
})
}
};
function setupMocks({
modelValueProp = 'TESTCAR',
cmsQuestionText = 'CMS text goes here'
}) {
const mountOptions = getMountOptions();
// Mock props
mountOptions.propsData = {
modelValue: modelValueProp
};
const wrapper = shallowMount(addressVehiclesQuestion, mountOptions);
// Mock CMS content
const cmsContent = {
QuestionText: cmsQuestionText
};
return { wrapper, cmsContent };
}

View file

@ -1,38 +1,36 @@
<template> <template>
<div> <div>
<alert <alert
ref="differentVehicleAlert"
v-if="displayMatchedDifferentVehicleAlert" v-if="displayMatchedDifferentVehicleAlert"
id="address-vehicles-question-alert"
ref="differentVehicleAlert"
class="my-4" class="my-4"
cmsWidgetName="AlertMatchedDifferentVehicleWidget" cmsWidgetName="AlertMatchedDifferentVehicleWidget"
:manualHeadline="differentVehicleAlertHeader" :manualHeadline="differentVehicleAlertHeader"
:manualCopy="differentVehicleAlertBody" :manualCopy="differentVehicleAlertBody"
alertClass="alert-warning" alertClass="alert-warning"
v-bind:isDismissible="false" :isDismissible="false" />
id="address-vehicles-question-alert" />
<alert <alert
ref="alertMatchedTwoIdenticalYMMVehicle" v-if="displayMatchedTwoIdenticalYMMVehicleAlert"
v-if="displayMatchedTwoIdenticalYMMVehicleAlert" id="address-vehicles-question-alert"
class="my-4" ref="alertMatchedTwoIdenticalYMMVehicle"
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget" class="my-4"
:manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader" cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody" :manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader"
alertClass="alert-warning" :manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody"
v-bind:isDismissible="false" alertClass="alert-warning"
id="address-vehicles-question-alert" /> :isDismissible="false" />
</div> </div>
<buttonQuestion <buttonQuestion
class="address-vehicles-question"
ref="addressVehiclesQuestion" ref="addressVehiclesQuestion"
v-model="selectedVehicleVin"
class="address-vehicles-question"
buttonTypeString="listButton" buttonTypeString="listButton"
groupName="ChooseAddressVehicle" groupName="ChooseAddressVehicle"
:questionText="questionText" :questionText="questionText"
:answers="vehicles" :answers="vehicles"
v-model="selectedVehicleVin"
isRequired isRequired
:validation-rules="validationRules" /> :validationRules="validationRules" />
</template> </template>
<script> <script>
@ -43,6 +41,10 @@ import { getDamageString } from '@/helpers/damage-helper';
export default { export default {
name: 'address-vehicles-question', name: 'address-vehicles-question',
components: {
buttonQuestion,
alert
},
props: { props: {
vehicles: Array, vehicles: Array,
vehicleSelected: Object, vehicleSelected: Object,
@ -53,12 +55,11 @@ export default {
displayMatchedDifferentVehicleAlert: Boolean, displayMatchedDifferentVehicleAlert: Boolean,
displayMatchedTwoIdenticalYMMVehicleAlert: Boolean displayMatchedTwoIdenticalYMMVehicleAlert: Boolean
}, },
emits: ['update: modelValue'],
computed: { computed: {
differentVehicleAlertHeader() { differentVehicleAlertHeader() {
return this.getCmsContent('AlertMatchedDifferentVehicleWidget', 'HeadlineText').replaceAll( return this.getCmsContent('AlertMatchedDifferentVehicleWidget', 'HeadlineText').replaceAll('{custom:damage}',
'{custom:damage}', getDamageString());
getDamageString()
);
}, },
differentVehicleAlertBody() { differentVehicleAlertBody() {
const vinYmmFound = `${this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model}`; const vinYmmFound = `${this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model}`;
@ -70,10 +71,8 @@ export default {
.replaceAll('{custom:vinYmmExpected}', vinYmmExpected); .replaceAll('{custom:vinYmmExpected}', vinYmmExpected);
}, },
AlertMatchedTwoIdenticalYMMVehicleHeader() { AlertMatchedTwoIdenticalYMMVehicleHeader() {
return this.getCmsContent( return this.getCmsContent('AlertMatchedTwoIdenticalYMMVehicleWidget',
'AlertMatchedTwoIdenticalYMMVehicleWidget', 'HeadlineText').replaceAll('{custom:damage}', getDamageString());
'HeadlineText'
).replaceAll('{custom:damage}', getDamageString());
}, },
AlertMatchedTwoIdenticalYMMVehicleBody() { AlertMatchedTwoIdenticalYMMVehicleBody() {
const vinYmmsFound = `${this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model} ${this.selectedVehicle?.vehicle.style}`; const vinYmmsFound = `${this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model} ${this.selectedVehicle?.vehicle.style}`;
@ -88,10 +87,10 @@ export default {
return this.getCmsContent('VehicleConfirmationQuestion', 'QuestionText'); return this.getCmsContent('VehicleConfirmationQuestion', 'QuestionText');
}, },
selectedVehicleVin: { selectedVehicleVin: {
get: function () { get() {
return this.modelValue; return this.modelValue;
}, },
set: function (newValue) { set(newValue) {
this.$emit('update:modelValue', newValue); this.$emit('update:modelValue', newValue);
} }
}, },
@ -99,17 +98,13 @@ export default {
// this computed is only needed for the computed differentVehicleAlertBody text above // this computed is only needed for the computed differentVehicleAlertBody text above
return this.vehicles.find(({ vin }) => vin === this.selectedVehicleVin); return this.vehicles.find(({ vin }) => vin === this.selectedVehicleVin);
}, },
vehicleSelected(){ vehicleSelected() {
return this.vehicleSelected; return this.vehicleSelected;
} }
},
components: {
buttonQuestion,
alert
} }
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.address-vehicles-question { .address-vehicles-question {
.question-text { .question-text {
margin-bottom: 0.5rem; margin-bottom: 0.5rem;

View file

@ -25,6 +25,104 @@ jest.mock('@/helpers/layout-helper.js', () => ({
settleAllPromises: jest.fn() settleAllPromises: jest.fn()
})); }));
function setupMocks({
route = null,
lookupVehicleByVinResponse
}) {
useMainStore().applicationUser = {
pageData: {
'address-vehicles':
[
{
vehicle: {
carId: 'CR00069309',
category: 'SUV',
imageUrl:
'https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13769/13769_cc0320_032_WW8.jpg',
imageVifColor: 'white',
imageVifNumber: '13769',
make: 'Hyundai',
model: 'Santa Fe',
style: '4 door utility',
year: 2020
},
vin: '5NMS3CADXLH233004'
}
]
}
};
useMainStore().lookupVehicleByVin = jest.fn().mockImplementation(() => Promise.resolve({
data: lookupVehicleByVinResponse || {
vehicle: {
carId: 'CARID'
}
}
}));
const mountOptions = getMountOptions({
route: route || undefined,
router: {
navigate: jest.fn()
},
mainStore: {
order: {
vehicle: {
carId: 'CR00069309',
category: 'SUV',
imageUrl:
'https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13769/13769_cc0320_032_WW8.jpg',
imageVifColor: 'white',
imageVifNumber: '13769',
make: 'Hyundai',
model: 'Santa Fe',
style: '4 door utility',
year: 2020
},
vin: '5NMS3CADXLH233004'
}
}
});
const apiResponses = {
cmsContent: {}
};
settleAllPromises.mockImplementation(() => apiResponses);
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
// Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn((contentName) => {
if (contentName === 'FoundMultipleVehicles') {
return 'FoundMultipleVehiclesTestReturn';
}
if (contentName === 'ProvideVinAlert') {
return 'ProvideVinAlertTestReturn';
}
return null;
})
},
computed: {
dynamicStrings() {
return { ROUTER_LINK: 'routerLink:' };
}
}
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(addressVehicles, mountOptions);
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => '');
wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.$refs.siteFooter.updateButtonText = jest.fn();
wrapper.vm.$refs.siteFooter.removeLoader = jest.fn();
return { wrapper };
}
describe('address-vehicles.vue', () => { describe('address-vehicles.vue', () => {
test('Should navigate to CLICKED_BACK if backButtonAction is run', async () => { test('Should navigate to CLICKED_BACK if backButtonAction is run', async () => {
// Arrange // Arrange
@ -132,118 +230,14 @@ describe('address-vehicles.vue', () => {
useMainStore().order.vehicle.carId = 'CR00000395'; useMainStore().order.vehicle.carId = 'CR00000395';
// Act // Act
addressVehicles.beforeRouteEnter.call( addressVehicles.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'address-vehicles' } }, { query: { issPage: 'address-vehicles' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
// Assert // Assert
expect(arePagePrerequisitesValid).toBe(true); expect(arePagePrerequisitesValid).toBe(true);
}); });
}); });
function setupMocks({
route = null,
lookupVehicleByVinResponse
}) {
useMainStore().applicationUser = {
pageData: {
'address-vehicles':
[
{
vehicle: {
carId: 'CR00069309',
category: 'SUV',
imageUrl:
'https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13769/13769_cc0320_032_WW8.jpg',
imageVifColor: 'white',
imageVifNumber: '13769',
make: 'Hyundai',
model: 'Santa Fe',
style: '4 door utility',
year: 2020
},
vin: '5NMS3CADXLH233004'
}
]
}
};
useMainStore().lookupVehicleByVin = jest.fn().mockImplementation(() => {
return Promise.resolve({
data: lookupVehicleByVinResponse
? lookupVehicleByVinResponse : {
vehicle: {
carId: 'CARID'
}
}
});
});
const mountOptions = getMountOptions({
route: route ? route : undefined,
router: {
navigate: jest.fn()
},
mainStore: {
order: {
vehicle: {
carId: 'CR00069309',
category: 'SUV',
imageUrl:
'https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13769/13769_cc0320_032_WW8.jpg',
imageVifColor: 'white',
imageVifNumber: '13769',
make: 'Hyundai',
model: 'Santa Fe',
style: '4 door utility',
year: 2020
},
vin: '5NMS3CADXLH233004'
}
}
}
);
const apiResponses = {
cmsContent: {}
};
settleAllPromises.mockImplementation(() => apiResponses);
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
// Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn((contentName) => {
if (contentName === 'FoundMultipleVehicles') {
return 'FoundMultipleVehiclesTestReturn';
}
if (contentName === 'ProvideVinAlert') {
return 'ProvideVinAlertTestReturn';
}
return null;
})
},
computed: {
dynamicStrings() {
return { ROUTER_LINK: 'routerLink:' };
}
}
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(addressVehicles, mountOptions);
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => '');
wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.$refs.siteFooter.updateButtonText = jest.fn();
wrapper.vm.$refs.siteFooter.removeLoader = jest.fn();
return { wrapper };
}

View file

@ -1,9 +1,9 @@
<template> <template>
<Form <Form
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit"
ref="theForm" ref="theForm"
v-slot="{ meta }"> v-slot="{ meta }"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="fade-on-route-transition sub-container overflow-scroll px-5"> <div class="fade-on-route-transition sub-container overflow-scroll px-5">
@ -13,44 +13,51 @@
:displayGenericVehicleImage="false" /> :displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" /> <siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<alert <alert
cmsWidgetName="FoundMultipleVehicles" id="multiple-vehicles-alert"
ref="alertFoundMultipleVehicles" ref="alertFoundMultipleVehicles"
cmsWidgetName="FoundMultipleVehicles"
class="my-5" class="my-5"
alertClass="alert-warning" alertClass="alert-warning"
:manualHeadline="AlertFoundMultipleVehiclesHeader" :manualHeadline="AlertFoundMultipleVehiclesHeader"
manualCopy="" manualCopy=""
v-bind:isDismissible="false" :isDismissible="false" />
id="multiple-vehicles-alert" />
<addressVehiclesQuestion <addressVehiclesQuestion
ref="addressVehiclesQuestion" ref="addressVehiclesQuestion"
v-model="selectedVehicleVin"
cmsWidgetName="VehicleConfirmationQuestion" cmsWidgetName="VehicleConfirmationQuestion"
:vehicles="VehiclesForQuestions" :vehicles="VehiclesForQuestions"
:vehicleSelected ="VehicleSelected" :vehicleSelected="VehicleSelected"
validationRules="vehicle-required" validationRules="vehicle-required"
v-model="selectedVehicleVin"
:isCarIdDifferent="isCarIdDifferent" :isCarIdDifferent="isCarIdDifferent"
:displayMatchedDifferentVehicleAlert = "displayMatchedDifferentVehicleAlert" :displayMatchedDifferentVehicleAlert="displayMatchedDifferentVehicleAlert"
:displayMatchedTwoIdenticalYMMVehicleAlert = "displayMatchedTwoIdenticalYMMVehicleAlert" /> :displayMatchedTwoIdenticalYMMVehicleAlert="displayMatchedTwoIdenticalYMMVehicleAlert" />
<div class="alert-provide-vin my-3" <div
v-if="splitAlertProvideVinBodyForLink.length"> v-if="splitAlertProvideVinBodyForLink.length"
<span v-for="copy in splitAlertProvideVinBodyForLink" :key="copy"> class="alert-provide-vin my-3">
<span v-if="doesCopyContainRouterLink(copy)" class="text-body"> <span
v-for="copy in splitAlertProvideVinBodyForLink"
:key="copy">
<span
v-if="doesCopyContainRouterLink(copy)"
class="text-body">
<router-link <router-link
:to="{ :to="{
query: { issPage: `${getRouterLinkRouteFromCopy(copy)}` }, query: { issPage: `${getRouterLinkRouteFromCopy(copy)}` },
name: 'root', name: 'root',
}" }">{{ getRouterLinkDisplayTextFromCopy(copy) }}</router-link>
>{{ getRouterLinkDisplayTextFromCopy(copy) }}</router-link >
</span> </span>
<span v-else class="m-0 text-body" v-html="copy"></span> <span
v-else
class="m-0 text-body"
v-html="copy"></span>
</span> </span>
</div> </div>
<siteFooter <siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget" cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@backClicked="backButtonAction" @backClicked="backButtonAction"
@forwardClicked="forwardButtonAction" @forwardClicked="forwardButtonAction" />
ref="siteFooter" />
</div> </div>
</div> </div>
</Form> </Form>
@ -58,7 +65,6 @@
<script> <script>
// Import Supporting Files // Import Supporting Files
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper'; import { settleAllPromises } from '@/helpers/layout-helper';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import { issPageValues } from '@/router/router-constants/issPage-values'; import { issPageValues } from '@/router/router-constants/issPage-values';
@ -67,20 +73,21 @@ import { required } from '@/helpers/validation-rules';
import { Form, defineRule } from 'vee-validate'; import { Form, defineRule } from 'vee-validate';
import { isGlassAvailableForCarId } from '@/helpers/damage-helper'; import { isGlassAvailableForCarId } from '@/helpers/damage-helper';
import { import {
fetchCmsContentForPage,
doesCopyContainRouterLink, doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder, splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy, getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy getRouterLinkDisplayTextFromCopy
} from '@/helpers/cms-content-helper'; } from '@/helpers/cms-content-helper.js';
import { routerParams } from '@/router/router-constants/router-params'; import { routerParams } from '@/router/router-constants/router-params';
import vinPagesMixin from '@/mixins/vin-pages-mixin'; import vinPagesMixin from '@/mixins/vin-pages-mixin';
// Import Component // Import Component
import baseFormMixin from '@/mixins/base-form-mixin'; import baseFormMixin from '@/mixins/base-form-mixin';
import siteFooter from '@/iss-components/site-footer/site-footer.vue'; import siteFooter from '@/iss-components/site-footer/site-footer';
import siteHeader from '@/iss-components/site-header/site-header.vue'; import siteHeader from '@/iss-components/site-header/site-header';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'; import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
import alert from '@/ux-components/alert/alert'; import alert from '@/ux-components/alert/alert';
import addressVehiclesQuestion from '@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question'; import addressVehiclesQuestion from '@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question';
@ -89,6 +96,16 @@ defineRule('vehicle-required', required(errorMessages.VEHICLE_REQUIRED));
export default { export default {
name: 'address-vehicles', name: 'address-vehicles',
components: {
siteFooter,
siteHeader,
siteSubHeader,
// eslint-disable-next-line vue/no-reserved-component-names
Form,
vehicleBanner,
alert,
addressVehiclesQuestion
},
mixins: [baseFormMixin, vinPagesMixin], mixins: [baseFormMixin, vinPagesMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -131,15 +148,13 @@ export default {
return this.VehiclesForQuestions.length; return this.VehiclesForQuestions.length;
}, },
AlertFoundMultipleVehiclesHeader() { AlertFoundMultipleVehiclesHeader() {
return this.getCmsContent( return this.getCmsContent('FoundMultipleVehicles',
'FoundMultipleVehicles', 'HeadlineText').replaceAll('{custom:vehicleCount}', this.vehicleCount);
'HeadlineText'
).replaceAll('{custom:vehicleCount}', this.vehicleCount);
}, },
isTwoIdenticalYMMVehicleFound() { isTwoIdenticalYMMVehicleFound() {
const vinYmmFound = `${ this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model}`; const vinYmmFound = `${this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`; const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return (vinYmmFound.toLowerCase()==vinYmmExpected.toLowerCase()); return (vinYmmFound.toLowerCase() === vinYmmExpected.toLowerCase());
}, },
AlertProvideVinBody() { AlertProvideVinBody() {
return this.getCmsContent('ProvideVinAlert', 'BodyText'); return this.getCmsContent('ProvideVinAlert', 'BodyText');
@ -157,9 +172,9 @@ export default {
return { return {
vin: v.vin, vin: v.vin,
vehicle: v.vehicle, vehicle: v.vehicle,
Text: v.vehicle.year + ' ' + v.vehicle.make + ' ' + v.vehicle.model, Text: `${v.vehicle.year} ${v.vehicle.make} ${v.vehicle.model}`,
Name: v.vin, Name: v.vin,
SubText: 'VIN ' + vinStart + vinEnd SubText: `VIN ${vinStart}${vinEnd}`
}; };
}); });
return mappedData; return mappedData;
@ -168,14 +183,37 @@ export default {
return useMainStore().pageData(issPageValues.ADDRESS_VEHICLES); return useMainStore().pageData(issPageValues.ADDRESS_VEHICLES);
}, },
selectedVehicle() { selectedVehicle() {
return this.VehiclesForQuestions.find( return this.VehiclesForQuestions.find(({ vin }) => vin === this.selectedVehicleVin);
({ vin }) => vin === this.selectedVehicleVin
);
}, },
VehicleSelected() { VehicleSelected() {
return this.mainStore.order.vehicle; return this.mainStore.order.vehicle;
} }
}, },
watch: {
selectedVehicleVin: {
handler() {
this.resetWarningsAndErrors();
// does this vehicle match the previously selected carId?
this.isCarIdDifferent
= this.selectedVehicle?.vehicle.carId !== useMainStore().vehicle.carId;
if (this.isCarIdDifferent
&& this.selectedVehicle?.vehicle.carId !== this.previouslyEnteredCarId) {
this.previouslyEnteredCarId = this.selectedVehicle?.vehicle.carId;
if (this.isTwoIdenticalYMMVehicleFound) {
const carStyle = this.selectedVehicle?.vehicle.style;
this.displayMatchedTwoIdenticalYMMVehicleAlert = true;
this.forwardButtonCarStyle = carStyle;
} else {
this.displayMatchedDifferentVehicleAlert = true;
}
this.$refs.siteFooter.updateButtonText(`Continue with ${this.selectedVehicle.vehicle.year} ${this.selectedVehicle.vehicle.make} ${this.selectedVehicle.vehicle.model} ${this.forwardButtonCarStyle}`);
} else {
this.$refs.siteFooter.updateButtonText(this.getCmsContent('SiteFooterWidget', 'ForwardButtonText'));
}
},
deep: true
}
},
methods: { methods: {
doesCopyContainRouterLink, doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder, splitCopyOnCMSPlaceHolder,
@ -199,19 +237,15 @@ export default {
if (!vinLookup) { if (!vinLookup) {
return; return;
} }
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId( this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(vinLookup.data.carId);
vinLookup.data.carId await useMainStore().saveVin({
); vehicleInfo: Object.assign(this.selectedVehicle.vehicle, {
await useMainStore().saveVin( vin: this.selectedVehicle.vin
{ }),
vehicleInfo: Object.assign(this.selectedVehicle.vehicle, { isSelectedGlassAvailableForVehicle:
vin: this.selectedVehicle.vin
}),
isSelectedGlassAvailableForVehicle:
this.isSelectedGlassAvailableForVehicle this.isSelectedGlassAvailableForVehicle
}, },
false false);
);
return await this.navigateForward(); return await this.navigateForward();
}, },
@ -219,12 +253,10 @@ export default {
// If the vehicle selected on this page is different from the one originally entered and the selected glass is not available // If the vehicle selected on this page is different from the one originally entered and the selected glass is not available
// for that vehicle, then navigate back to "vehicle-damage" and display vehicle changed alert on that page. // for that vehicle, then navigate back to "vehicle-damage" and display vehicle changed alert on that page.
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) { if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$route, this.$route,
{}, {},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true } { [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true });
);
} else { } else {
await this.navigateForwardWithSingleCarMatch(); await this.navigateForwardWithSingleCarMatch();
} }
@ -234,49 +266,11 @@ export default {
this.displayMatchedTwoIdenticalYMMVehicleAlert = false; this.displayMatchedTwoIdenticalYMMVehicleAlert = false;
this.forwardButtonCarStyle = ''; this.forwardButtonCarStyle = '';
} }
},
watch: {
selectedVehicleVin: {
handler() {
this.resetWarningsAndErrors();
// does this vehicle match the previously selected carId?
this.isCarIdDifferent =
this.selectedVehicle?.vehicle.carId !== useMainStore().vehicle.carId;
if (this.isCarIdDifferent &&
this.selectedVehicle?.vehicle.carId !== this.previouslyEnteredCarId) {
this.previouslyEnteredCarId = this.selectedVehicle?.vehicle.carId;
if(this.isTwoIdenticalYMMVehicleFound){
const carStyle = this.selectedVehicle?.vehicle.style;
this.displayMatchedTwoIdenticalYMMVehicleAlert = true;
this.forwardButtonCarStyle= carStyle;
} else {
this.displayMatchedDifferentVehicleAlert = true;
}
this.$refs.siteFooter.updateButtonText(
`Continue with ${this.selectedVehicle.vehicle.year} ${this.selectedVehicle.vehicle.make} ${this.selectedVehicle.vehicle.model} ${this.forwardButtonCarStyle}`
);
} else {
this.$refs.siteFooter.updateButtonText(
this.getCmsContent('SiteFooterWidget', 'ForwardButtonText')
);
}
},
deep: true
}
},
components: {
siteFooter,
siteHeader,
siteSubHeader,
Form,
vehicleBanner,
alert,
addressVehiclesQuestion
} }
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.alert-provide-vin { .alert-provide-vin {
font-size: 0.875rem; font-size: 0.875rem;
line-height: 1.4; line-height: 1.4;

View file

@ -1,17 +1,20 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget"/> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="container-fluid pb-2"> <div class="container-fluid pb-2">
<p>Placeholder for bailout page</p> <p>Placeholder for bailout page</p>
<siteFooter <siteFooter
cmsWidgetName="SiteFooterWidget"
ref="siteFooter" ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction" @ForwardClicked="forwardButtonAction"
@back-clicked="backButtonAction" @back-clicked="backButtonAction" />
/>
</div> </div>
</div> </div>
</div> </div>
@ -30,15 +33,14 @@ import { useMainStore } from '@/store';
export default { export default {
name: 'bailout-page', name: 'bailout-page',
components: {
siteHeader,
siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names
Form
},
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
data() { async beforeRouteEnter(to, from, next) {
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
async beforeRouteEnter(to, from, next)
{
// Call APIs // Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results // Settle promises and get results
@ -48,11 +50,17 @@ export default {
promise: cmsContentPromise promise: cmsContentPromise
}]; }];
// use resultMap to populate layout content. // use resultMap to populate layout content.
let resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
}, },
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() {
},
methods: methods:
{ {
backButtonAction() { backButtonAction() {
@ -62,13 +70,6 @@ export default {
}, },
navigateForward() { navigateForward() {
} }
}, }
components: { };
siteHeader,
siteFooter,
Form
}
}
</script> </script>
<style lang="scss">
</style>

View file

@ -5,9 +5,9 @@ import capabilityQuestions from '@/layouts/capability-questions/capability-quest
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import baseMixin from '../../mixins/base-mixin';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import baseMixin from '@/mixins/base-mixin';
// Mock our module for promises. // Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => ({ jest.mock('@/helpers/layout-helper.js', () => ({
@ -19,78 +19,134 @@ jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: jest.fn() fetchCmsContentForPage: jest.fn()
})); }));
const baseStoreGettersPageData = () => { const baseStoreGettersPageData = () => ({
return { partsOrQuestions: [
partsOrQuestions: [ {
{ parts: [
parts: [ {
{ childPartQuestions: []
childPartQuestions: [] }
} ],
], capabilityQuestions: [
capabilityQuestions: [ {
{ questionSequence: 1,
questionSequence: 1, questionText:
questionText:
'Is your vehicle equipped with the optional Lane-Keeping System which tugs on the steering wheel and/or beeps to alert you if you drift too close to the edge of the lane?', 'Is your vehicle equipped with the optional Lane-Keeping System which tugs on the steering wheel and/or beeps to alert you if you drift too close to the edge of the lane?',
answers: [ answers: [
{ {
answerResult1: 'DYNAMIC', answerResult1: 'DYNAMIC',
answerResult2: '1', answerResult2: '1',
answerText: 'Yes', answerText: 'Yes',
nextQuestionSequence: null, nextQuestionSequence: null,
answerResult: 'DYNAMIC' answerResult: 'DYNAMIC'
}, },
{ {
answerResult1: 'Unknown', answerResult1: 'Unknown',
answerResult2: '0', answerResult2: '0',
answerText: 'No', answerText: 'No',
nextQuestionSequence: null, nextQuestionSequence: null,
answerResult: 'Unknown' answerResult: 'Unknown'
} }
] ]
} }
], ],
partQuestions: [], partQuestions: [],
questions: [], questions: [],
glassLocation: 'Windshield', glassLocation: 'Windshield',
glassName: 'Single', glassName: 'Single',
answerKey: 'Windshield-Single', answerKey: 'Windshield-Single',
answerData: null answerData: null
} }
] ]
}; });
}; const baseStoreGettersDamage = () => ({
const baseStoreGettersDamage = () => { partsQuestionAnswers: [
return { {
partsQuestionAnswers: [ glassLocation: 'Windshield',
{ glassName: 'Single',
glassLocation: 'Windshield', result: 'FW04848',
glassName: 'Single', answeredQuestions: [
result: 'FW04848', {
answeredQuestions: [ questionText:
{
questionText:
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?', 'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
selectedAnswer: '1|nextQuestion|3|Yes', selectedAnswer: '1|nextQuestion|3|Yes',
selectedAnswerText: 'Yes', selectedAnswerText: 'Yes',
questionNum: 1 questionNum: 1
}, },
{ {
questionText: questionText:
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?', 'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
selectedAnswer: '2|nextQuestion|3|Yes', selectedAnswer: '2|nextQuestion|3|Yes',
selectedAnswerText: 'Yes', selectedAnswerText: 'Yes',
questionNum: 2 questionNum: 2
} }
] ]
} }
] ]
}; });
};
useMainStore().pageData = baseStoreGettersPageData; useMainStore().pageData = baseStoreGettersPageData;
useMainStore().damage = baseStoreGettersDamage; useMainStore().damage = baseStoreGettersDamage;
function setupMocks({
mountOptionsMockData = {
router: {
navigate: jest.fn()
},
actionList: [
{
actionName: 'saveCapabilityQuestionAnswers',
data: {}
},
{
actionName: 'getPartsOrQuestions',
data: {}
}
],
route: {
query: {
issPage: 'capability-questions'
}
},
data() {
return {
computedSwitcher: [
{
glassLocation: 'Windshield',
glassName: 'Single',
answerData: {
answerResult: 'FW04848',
answeredQuestions: []
}
}
]
};
},
questionsData: {
get() {
return this.computedSwitcher;
},
set(val) {
this.computedSwitcher = val;
}
}
}
}) {
useMainStore().getPartsOrQuestions = jest.fn(() => ({
data: {
partsOrQuestions: []
}
}));
const mountOptions = getMountOptions({
...mountOptionsMockData,
mixins: [baseMixin, vehicleQuestionsMixin]
});
mountOptions.attachTo = document.body;
const wrapper = shallowMount(capabilityQuestions, mountOptions);
return { wrapper };
}
describe('capabilityQuestions.vue', () => { describe('capabilityQuestions.vue', () => {
describe('method arePagePrerequisitesValid...', () => { describe('method arePagePrerequisitesValid...', () => {
test('Should return true for valid page requisites if pageData exists', () => { test('Should return true for valid page requisites if pageData exists', () => {
@ -109,9 +165,7 @@ describe('capabilityQuestions.vue', () => {
test('Should return false for valid page requisites if partsOrQuestions in pageData is missing', () => { test('Should return false for valid page requisites if partsOrQuestions in pageData is missing', () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
useMainStore().pageData = jest.fn(() => { useMainStore().pageData = jest.fn(() => undefined);
return undefined;
});
// Act // Act
const result = wrapper.vm.arePagePrerequisitesValid(); const result = wrapper.vm.arePagePrerequisitesValid();
@ -124,11 +178,9 @@ describe('capabilityQuestions.vue', () => {
test('Should be at least one item in partsOrQuestions', () => { test('Should be at least one item in partsOrQuestions', () => {
// Arrange // Arrange
useMainStore().pageData = jest.fn(() => { useMainStore().pageData = jest.fn(() => ({
return { partsOrQuestions: []
partsOrQuestions: [] }));
};
});
useMainStore().damage = baseStoreGettersDamage; useMainStore().damage = baseStoreGettersDamage;
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
@ -207,11 +259,9 @@ describe('capabilityQuestions.vue', () => {
] ]
} }
]; ];
wrapper.vm.dispatchStoreAction = jest.fn(() => { wrapper.vm.dispatchStoreAction = jest.fn(() => ({
return { data: []
data: [] }));
};
});
// Act // Act
wrapper.vm.forwardButtonAction(); wrapper.vm.forwardButtonAction();
@ -242,13 +292,11 @@ describe('capabilityQuestions.vue', () => {
] ]
} }
]; ];
useMainStore().getPartsOrQuestions = jest.fn(() => { useMainStore().getPartsOrQuestions = jest.fn(() => ({
return { data: {
data: { partsOrQuestions: []
partsOrQuestions: [] }
} }));
};
});
// Act // Act
wrapper.vm.forwardButtonAction(); wrapper.vm.forwardButtonAction();
@ -279,11 +327,9 @@ describe('capabilityQuestions.vue', () => {
] ]
} }
]; ];
wrapper.vm.dispatchStoreAction = jest.fn(() => { wrapper.vm.dispatchStoreAction = jest.fn(() => ({
return { data: []
data: [] }));
};
});
// Act // Act
wrapper.vm.forwardButtonAction(); wrapper.vm.forwardButtonAction();
@ -316,13 +362,11 @@ describe('capabilityQuestions.vue', () => {
] ]
} }
]; ];
useMainStore().getPartsOrQuestions = jest.fn(() => { useMainStore().getPartsOrQuestions = jest.fn(() => ({
return { data: {
data: { partsOrQuestions: []
partsOrQuestions: [] }
} }));
};
});
wrapper.vm.navigateForward = jest.fn(); wrapper.vm.navigateForward = jest.fn();
// Act // Act
@ -335,65 +379,3 @@ describe('capabilityQuestions.vue', () => {
}); });
}); });
}); });
function setupMocks({
mountOptionsMockData = {
router: {
navigate: jest.fn()
},
actionList: [
{
actionName: 'saveCapabilityQuestionAnswers',
data: {}
},
{
actionName: 'getPartsOrQuestions',
data: {}
}
],
route: {
query: {
issPage: 'capability-questions'
}
},
data() {
return {
computedSwitcher: [
{
glassLocation: 'Windshield',
glassName: 'Single',
answerData: {
answerResult: 'FW04848',
answeredQuestions: []
}
}
]
};
},
questionsData: {
get() {
return this.computedSwitcher;
},
set(val) {
this.computedSwitcher = val;
}
}
}
}) {
useMainStore().getPartsOrQuestions = jest.fn(() => {
return {
data: {
partsOrQuestions: []
}
};
});
const mountOptions = getMountOptions({
...mountOptionsMockData,
mixins: [baseMixin, vehicleQuestionsMixin]
});
mountOptions['attachTo'] = document.body;
const wrapper = shallowMount(capabilityQuestions, mountOptions);
return { wrapper };
}

View file

@ -1,21 +1,21 @@
<template> <template>
<Form <Form
@submit="onSubmit" ref="theForm"
@invalidSubmit="onInvalidSubmit" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
> @submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<questionsPageLayout <questionsPageLayout
isRequired ref="questionsPageLayout"
ref="questionsPageLayout" v-model="selectedAnswers"
:isMetaValid="meta.valid" isRequired
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader" :isMetaValid="meta.valid"
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy" :alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
:questionsData="questionsData" :alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
:validationRules="rules.optionRequired" :questionsData="questionsData"
v-model="selectedAnswers" :validationRules="rules.optionRequired"
@forwardButtonAction="forwardButtonAction" :index="currentGlassIndex"
@back-click="navigateBack" @forwardButtonAction="forwardButtonAction"
:index="currentGlassIndex" /> @back-click="navigateBack" />
</Form> </Form>
</template> </template>
<script> <script>
@ -30,25 +30,16 @@ import { Form } from 'vee-validate';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import globalRules from '@/constants/global-rules'; import globalRules from '@/constants/global-rules';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import questionsPageLayout from '@/iss-components/questions-page-layout/questions-page-layout.vue'; import questionsPageLayout from '@/iss-components/questions-page-layout/questions-page-layout';
export default { export default {
name: 'capability-questions', name: 'capability-questions',
mixins: [baseFormMixin, vehicleQuestionsMixin],
components: { components: {
// eslint-disable-next-line vue/no-reserved-component-names
Form, Form,
questionsPageLayout questionsPageLayout
}, },
data() { mixins: [baseFormMixin, vehicleQuestionsMixin],
return {
questionsData: [],
selectedAnswers: {},
currentGlassIndex: 0,
rules: {
optionRequired: globalRules.OPTION_REQUIRED
}
};
},
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -66,6 +57,16 @@ export default {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
}, },
data() {
return {
questionsData: [],
selectedAnswers: {},
currentGlassIndex: 0,
rules: {
optionRequired: globalRules.OPTION_REQUIRED
}
};
},
computed: { computed: {
AlertFewMoreQuestionsHeader() { AlertFewMoreQuestionsHeader() {
return this.getCmsContent('AdditionalPartsQuestionsAlert', 'HeadlineText'); return this.getCmsContent('AdditionalPartsQuestionsAlert', 'HeadlineText');
@ -85,9 +86,8 @@ export default {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
const capabilityQuestionsFromPageData = useMainStore().pageData(issPageValues.CAPABILITY_QUESTIONS); const capabilityQuestionsFromPageData = useMainStore().pageData(issPageValues.CAPABILITY_QUESTIONS);
return ( return (
capabilityQuestionsFromPageData?.partsOrQuestions?.some((part) => part?.glassName) && capabilityQuestionsFromPageData?.partsOrQuestions?.some((part) => part?.glassName)
capabilityQuestionsFromPageData?.partsOrQuestions?.some( && capabilityQuestionsFromPageData?.partsOrQuestions?.some((part) => part?.capabilityQuestions?.length > 0)
(part) => part?.capabilityQuestions?.length > 0)
); );
}, },
getInitialQuestionData() { getInitialQuestionData() {
@ -98,24 +98,20 @@ export default {
.map((glass, index) => { .map((glass, index) => {
// NOTE: questions for property "questions" can differ between layouts // NOTE: questions for property "questions" can differ between layouts
glass.questions = glass.capabilityQuestions; glass.questions = glass.capabilityQuestions;
glass.answerKey = glass.glassLocation + '-' + glass.glassName; glass.answerKey = `${glass.glassLocation}-${glass.glassName}`;
// reset selectedAnswers for this glass // reset selectedAnswers for this glass
this.selectedAnswers[glass.answerKey] = []; this.selectedAnswers[glass.answerKey] = [];
const updatedGlass = this.setupInitialData( const updatedGlass = this.setupInitialData(glass,
glass,
index, index,
alreadyAnsweredQuestions alreadyAnsweredQuestions);
);
// Set up watch for each set of glass questions // Set up watch for each set of glass questions
this.$watch( this.$watch(`selectedAnswers.${glass.answerKey}`,
'selectedAnswers.' + glass.answerKey,
(newValue) => { (newValue) => {
if (newValue && Object.keys(newValue).length > 0) { if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates(newValue, glass.answerKey); this.handleAnswerUpdates(newValue, glass.answerKey);
} }
}, },
{ deep: true } { deep: true });
);
return updatedGlass; return updatedGlass;
}); });
}, },
@ -124,9 +120,7 @@ export default {
// get answerResult2 of returned answer // get answerResult2 of returned answer
let selectedAnswerResult2; let selectedAnswerResult2;
glass.questions.forEach((q) => { glass.questions.forEach((q) => {
const idx = q.answers.findIndex( const idx = q.answers.findIndex((a) => a.answerResult === glass.answerData.answerResult);
(a) => a.answerResult === glass.answerData.answerResult
);
if (idx !== -1) { if (idx !== -1) {
selectedAnswerResult2 = q.answers[idx].answerResult2; selectedAnswerResult2 = q.answers[idx].answerResult2;
} }
@ -149,13 +143,11 @@ export default {
await this.mainStore.saveCapabilityQuestionAnswers(questionAnswersArray); await this.mainStore.saveCapabilityQuestionAnswers(questionAnswersArray);
// get parts from the capabilityQuestionAnswers // get parts from the capabilityQuestionAnswers
const partsOrQuestions = this.partsOrQuestionsData; const partsOrQuestions = this.partsOrQuestionsData;
for (let answer of questionAnswersArray) { for (const answer of questionAnswersArray) {
partsOrQuestions.find((partOrQuestion) => { partsOrQuestions.find((partOrQuestion) => (
return ( partOrQuestion.glassLocation === answer.glassLocation
partOrQuestion.glassLocation === answer.glassLocation && && partOrQuestion.glassName === answer.glassName
partOrQuestion.glassName === answer.glassName )).parts[0].childParts = [
);
}).parts[0].childParts = [
{ {
partNumber: answer.partNum partNumber: answer.partNum
} }

View file

@ -76,8 +76,7 @@ describe('contactDetails.vue', () => {
const checkboxLabel = getRandomString(50, 100); const checkboxLabel = getRandomString(50, 100);
const mockMixin = { const mockMixin = {
methods: { methods: {
getCmsContent: jest.fn().mockImplementation(() => getCmsContent: jest.fn().mockImplementation(() => checkboxLabel),
checkboxLabel),
setCmsContent: jest.fn() setCmsContent: jest.fn()
} }
}; };
@ -107,8 +106,7 @@ describe('contactDetails.vue', () => {
const disclaimerText = getRandomString(50, 100); const disclaimerText = getRandomString(50, 100);
const mockMixin = { const mockMixin = {
methods: { methods: {
getCmsContent: jest.fn().mockImplementation(() => getCmsContent: jest.fn().mockImplementation(() => disclaimerText),
disclaimerText),
setCmsContent: jest.fn() setCmsContent: jest.fn()
} }
}; };

View file

@ -1,15 +1,15 @@
describe ('test', () => { describe('test', () => {
test ('dummy test', () => { test('dummy test', () => {
// Arrange // Arrange
let test = "test"; let test = 'test';
// Act // Act
test = "test2" test = 'test2';
// Assert // Assert
expect(test.length).toBe(5); expect(test.length).toBe(5);
}) });
}) });
// *Testing to be completed on SSR-603 // *Testing to be completed on SSR-603

View file

@ -283,8 +283,7 @@ export default {
if (!store.order.damage.isRepair) { if (!store.order.damage.isRepair) {
const parts = store.order.lineItems.glassParts; const parts = store.order.lineItems.glassParts;
if (parts != null && parts.filter((part) => if (parts != null && parts.filter((part) => part.requiresRecalibration).length > 0) {
part.requiresRecalibration).length > 0) {
return true; return true;
} }
} }
@ -427,7 +426,7 @@ export default {
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.body-text { .body-text {
p { p {
font-size: 14px; font-size: 14px;

View file

@ -1,5 +1,19 @@
import { shallowMount, mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import recalModal from '@/layouts/coverage-statement/recal-modal/recal-modal.vue'; import recalModal from '@/layouts/coverage-statement/recal-modal/recal-modal';
const mockCmsContent = {
HeaderText: 'Sample header text here.',
SubheaderText: 'Sample subheader text here.',
Image: 'https://www.sampleImage.sample',
BodyText: 'Sample body text here.',
FooterText: 'Sample footer text here.'
};
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => mockCmsContent[cmsFieldName])
}
};
describe('modal.vue', () => { describe('modal.vue', () => {
it('Should display header text when HeaderText is defined in the CMS', async () => { it('Should display header text when HeaderText is defined in the CMS', async () => {
@ -11,7 +25,7 @@ describe('modal.vue', () => {
}, },
attachTo: document.body attachTo: document.body
}); });
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['HeaderText'])); expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent.HeaderText));
}); });
it('Should display subheader text when SubheaderText is defined in the CMS', async () => { it('Should display subheader text when SubheaderText is defined in the CMS', async () => {
@ -23,7 +37,7 @@ describe('modal.vue', () => {
}, },
attachTo: document.body attachTo: document.body
}); });
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['SubheaderText'])); expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent.SubheaderText));
}); });
it('Should insert image url when Image is defined in the CMS', async () => { it('Should insert image url when Image is defined in the CMS', async () => {
@ -35,7 +49,7 @@ describe('modal.vue', () => {
}, },
attachTo: document.body attachTo: document.body
}); });
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['Image'])); expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent.Image));
}); });
it('Should display body text when BodyText is defined in the CMS', async () => { it('Should display body text when BodyText is defined in the CMS', async () => {
@ -47,22 +61,6 @@ describe('modal.vue', () => {
}, },
attachTo: document.body attachTo: document.body
}); });
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['BodyText'])); expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent.BodyText));
}); });
}); });
const mockMixin = {
methods: {
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
return mockCmsContent[cmsFieldName];
})
}
};
const mockCmsContent = {
HeaderText: 'Sample header text here.',
SubheaderText: 'Sample subheader text here.',
Image: 'https://www.sampleImage.sample',
BodyText: 'Sample body text here.',
FooterText: 'Sample footer text here.'
};

View file

@ -3,21 +3,28 @@
:ref="ModalName" :ref="ModalName"
:modalId="ModalName" :modalId="ModalName"
:footerButtonText="ModalCloseButtonText" :footerButtonText="ModalCloseButtonText"
@footer-button-event="closeModal" @footer-button-event="closeModal">
>
<div class="recal-modal-body ps-4 pe-4 pt-0 pb-5"> <div class="recal-modal-body ps-4 pe-4 pt-0 pb-5">
<h5 class="mb-4 text-center" v-html="ModalHeadline"></h5> <h5
<img :src="ModalImage" class="mw-100 d-flex mx-auto mb-4" alt="" /> class="mb-4 text-center"
<p class="fw-bold mb-2 subheader-text" v-html="ModalSubheadertext"></p> v-html="ModalHeadline"></h5>
<p class="mb-0 small" v-html="ModalBodyText"></p> <img
:src="ModalImage"
class="mw-100 d-flex mx-auto mb-4"
alt="" />
<p
class="fw-bold mb-2 subheader-text"
v-html="ModalSubheadertext"></p>
<p
class="mb-0 small"
v-html="ModalBodyText"></p>
<p <p
class="my-4 caption modal-sub-body"
v-if="ModalSubBodyText" v-if="ModalSubBodyText"
class="my-4 caption modal-sub-body"
v-html="ModalSubBodyText"> v-html="ModalSubBodyText">
</p> </p>
</div> </div>
</modal> </modal>
</template> </template>
<script> <script>
@ -25,17 +32,12 @@ import modal from '@/digital-components/modal/modal';
export default { export default {
name: 'recal-modal', name: 'recal-modal',
components: {
modal
},
props: { props: {
cmsWidgetName: String cmsWidgetName: String
}, },
methods: {
openModal() {
this.$refs[this.ModalName].openModal();
},
closeModal() {
this.$refs[this.ModalName].closeModal();
}
},
computed: { computed: {
ModalName() { ModalName() {
return this.cmsWidgetName; return this.cmsWidgetName;
@ -59,13 +61,18 @@ export default {
return this.getCmsContent(this.cmsWidgetName, 'FooterText'); return this.getCmsContent(this.cmsWidgetName, 'FooterText');
} }
}, },
components: { methods: {
modal openModal() {
this.$refs[this.ModalName].openModal();
},
closeModal() {
this.$refs[this.ModalName].closeModal();
}
} }
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
.recal-modal-body { .recal-modal-body {
.modal-sub-body { .modal-sub-body {
@ -84,5 +91,4 @@ export default {
} }
} }
</style> </style>

View file

@ -1,11 +1,10 @@
// Components // Components
import entryPage from '@/layouts/entry-page/entry-page.vue'; import entryPage from '@/layouts/entry-page/entry-page';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { settleAllPromises } from '@/helpers/layout-helper.js'; import { settleAllPromises } from '@/helpers/layout-helper.js';
import { fetchCmsContentForPage, setupModalLinks } from '@/helpers/cms-content-helper'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { useMainStore } from '@/store';
// Mock our module for promises. // Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => ({ jest.mock('@/helpers/layout-helper.js', () => ({
@ -18,16 +17,6 @@ jest.mock('@/helpers/cms-content-helper', () => ({
setupModalLinks: jest.fn() setupModalLinks: jest.fn()
})); }));
describe('entry-page.vue', () => {
test('should render', () => {
const queryString = 'policynumber="123456"'
const { wrapper } = setupMocks(queryString);
console.log(wrapper.vm.$route.query);
expect(wrapper).toBeTruthy();
})
});
function setupMocks(queryString) { function setupMocks(queryString) {
const mountOptions = getMountOptions({ const mountOptions = getMountOptions({
router: { router: {
@ -36,15 +25,23 @@ function setupMocks(queryString) {
route: { queryString } route: { queryString }
}); });
const wrapper = shallowMount( const wrapper = shallowMount(entryPage,
entryPage, mountOptions);
mountOptions
);
const apiResponses = {}; const apiResponses = {};
settleAllPromises.mockImplementation(() => apiResponses); settleAllPromises.mockImplementation(() => apiResponses);
fetchCmsContentForPage.mockImplementation (() => {}); fetchCmsContentForPage.mockImplementation(() => { });
return { wrapper }; return { wrapper };
}; }
describe('entry-page.vue', () => {
test('should render', () => {
const queryString = 'policynumber="123456"';
const { wrapper } = setupMocks(queryString);
console.log(wrapper.vm.$route.query);
expect(wrapper).toBeTruthy();
});
});

View file

@ -1,5 +1,9 @@
<template> <template>
<p id="message" v-show="unauthorized">Unauthorized Access.</p> <p
v-show="unauthorized"
id="message">
Unauthorized Access.
</p>
</template> </template>
<script> <script>
@ -10,40 +14,42 @@ import { useMainStore } from '@/store';
export default { export default {
name: 'entry-page', name: 'entry-page',
mixins: [], components: {
data() {
return {
unauthorized: false
}
}, },
mixins: [],
setup() { setup() {
const mainStore = useMainStore(); const mainStore = useMainStore();
mainStore.resetISSConfigState(); mainStore.resetISSConfigState();
mainStore.resetPageFields(); mainStore.resetPageFields();
return { mainStore }; return { mainStore };
}, },
data() {
return {
unauthorized: false
};
},
computed: {
},
mounted() { mounted() {
this.validateClientTagOnEntry(); this.validateClientTagOnEntry();
}, },
methods: methods:
{ {
navigateForward() { navigateForward() {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.MOVE_FORWARD_ENTRY_PAGE,
this.navigationScenarios.MOVE_FORWARD_ENTRY_PAGE, this.$route);
this.$route
);
}, },
parseQueryParms() { parseQueryParms() {
// Dump the query string parameters into an array. Remove casing on the key for easy compare. // Dump the query string parameters into an array. Remove casing on the key for easy compare.
let queryStringParams = []; const queryStringParams = [];
for (let param in this.$route.query) { for (const param in this.$route.query) {
queryStringParams[param.toLowerCase()] = this.$route.query[param]; queryStringParams[param.toLowerCase()] = this.$route.query[param];
} }
return queryStringParams; return queryStringParams;
}, },
async validateClientTagOnEntry() { async validateClientTagOnEntry() {
const queryStringParams = this.parseQueryParms(); const queryStringParams = this.parseQueryParms();
const clientTag = queryStringParams['clienttag']; const clientTag = queryStringParams.clienttag;
const clientTagPresent = !!clientTag; const clientTagPresent = !!clientTag;
let authorized = false; let authorized = false;
@ -66,7 +72,7 @@ export default {
if (authorized) { if (authorized) {
// Forced full location redirect here. We do not want the entry page as part of the router/flow/path history. // Forced full location redirect here. We do not want the entry page as part of the router/flow/path history.
window.location = '/?issPage=' + issPageValues.WELCOME_PAGE; window.location = `/?issPage=${issPageValues.WELCOME_PAGE}`;
} }
}, },
populateISSConfigValues(data) { populateISSConfigValues(data) {
@ -76,9 +82,9 @@ export default {
this.mainStore.issConfig.styleSheet = data.styleSheet; this.mainStore.issConfig.styleSheet = data.styleSheet;
this.mainStore.issConfig.isCoverageEnabled = data.coverageEnabled; this.mainStore.issConfig.isCoverageEnabled = data.coverageEnabled;
// NOTE: This is only here for testing purposes. Will be removed and replaced by actual token/signature validation when work is completed. // NOTE: This is only here for testing purposes.
if (data.authentication == 'RSAToken') // Will be removed and replaced by actual token / signature validation when work is completed.
this.mainStore.issConfig.isAuthenticated = true; if (data.authentication === 'RSAToken') this.mainStore.issConfig.isAuthenticated = true;
try { try {
if (data.clientFlags) { if (data.clientFlags) {
@ -97,20 +103,20 @@ export default {
} }
} }
} catch (e) { } catch (e) {
console.error('Error parsing client flags: ' + e); console.error(`Error parsing client flags: ${e}`);
} }
}, },
combineClientParameters(configParams, queryStringParams) { combineClientParameters(configParams, queryStringParams) {
let finalParams = []; const finalParams = [];
try { try {
const clientParams = JSON.parse(configParams); const clientParams = JSON.parse(configParams);
for (let cparam in clientParams) { for (const cparam in clientParams) {
let cname = clientParams[cparam].toLowerCase(); const cname = clientParams[cparam].toLowerCase();
for (let qsparam in queryStringParams) { for (const qsparam in queryStringParams) {
let qsname = qsparam.toLowerCase(); const qsname = qsparam.toLowerCase();
if (cname === qsname) { if (cname === qsname) {
finalParams[cname] = queryStringParams[cname]; finalParams[cname] = queryStringParams[cname];
@ -118,16 +124,16 @@ export default {
} }
} }
} catch (e) { } catch (e) {
console.error('Error combining client parameters: ' + e); console.error(`Error combining client parameters: ${e}`);
} }
return finalParams; return finalParams;
}, },
populateStoreItemsFromParams(params) { populateStoreItemsFromParams(params) {
// Populate store items from parameters. // Populate store items from parameters.
for (let param in params) { for (const param in params) {
let name = param.toLowerCase(); const name = param.toLowerCase();
let value = params[param]; const value = params[param];
switch (name) { switch (name) {
case 'policynumber': case 'policynumber':
@ -158,18 +164,15 @@ export default {
case 'token': case 'token':
case 'signature': case 'signature':
break; break;
default:
} }
} }
} }
}, }
computed: { };
},
components: {
}
}
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
#message { #message {
text-align: center; text-align: center;
} }

View file

@ -1,5 +1,5 @@
// Components // Components
import licensePlateLookup from '@/layouts/license-plate-lookup/license-plate-lookup.vue'; import licensePlateLookup from '@/layouts/license-plate-lookup/license-plate-lookup';
// Supporting Files // Supporting Files
import { settleAllPromises } from '@/helpers/layout-helper.js'; import { settleAllPromises } from '@/helpers/layout-helper.js';
@ -23,6 +23,83 @@ jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: jest.fn() fetchCmsContentForPage: jest.fn()
})); }));
function setupMocks({
isZipValid = true,
isZipServiceable = true,
lookupVinByPlateResponse,
partsOrQuestions = [],
vehicle = {},
vin = null,
carId = 'C0000',
vinLookupResponseError = null,
route = null
}) {
useMainStore().validateZip = jest.fn().mockImplementation(() => Promise.resolve({
data: {
isValid: isZipValid,
isServiceable: isZipServiceable
}
}));
useMainStore().lookupVinByPlate = jest.fn().mockImplementation(() => Promise.resolve({
data: lookupVinByPlateResponse || {
vin: 'TEST_VIN',
vehicle: {
carId: 'CARID'
}
}
}));
useMainStore().getPartsOrQuestions = jest.fn().mockImplementation(() => Promise.resolve({
data: {
partsOrQuestions
}
}));
const wrapper = shallowMount(licensePlateLookup,
getMountOptions({
route: route || undefined,
router: {
navigate: jest.fn()
},
mainStore: {
order: {
vehicle: {
carId,
registration: {
licensePlate: 'TESTPLATE',
zipCode: '12345'
}
}
}
}
}));
const apiResponses = {
serviceZipValidationResponse: {
isValid: isZipValid,
isServiceable: isZipServiceable
},
vinLookupResponse: {
vin,
vehicle,
error: vinLookupResponseError
}
};
settleAllPromises.mockImplementation(() => apiResponses);
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => '');
wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.$refs.siteFooter.updateButtonText = jest.fn();
wrapper.vm.$refs.siteFooter.removeLoader = jest.fn();
wrapper.vm.$refs.siteFooter.disableForwardButton = jest.fn();
wrapper.vm.$refs.siteFooter.enableForwardButton = jest.fn();
return { wrapper };
}
describe('license-plate-lookup.vue', () => { describe('license-plate-lookup.vue', () => {
describe('page level alerts', () => { describe('page level alerts', () => {
test('If license plate matches a different vehicle, display MatchedDifferentVehicleAlert', async () => { test('If license plate matches a different vehicle, display MatchedDifferentVehicleAlert', async () => {
@ -43,9 +120,7 @@ describe('license-plate-lookup.vue', () => {
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect(wrapper.findComponent({ ref: 'alertMatchedDifferentVehicle' }).isVisible()).toBe( expect(wrapper.findComponent({ ref: 'alertMatchedDifferentVehicle' }).isVisible()).toBe(true);
true
);
}); });
test('If license plate matches two identical YMM vehicles, display alertMatchedTwoIdenticalYMMVehicle', async () => { test('If license plate matches two identical YMM vehicles, display alertMatchedTwoIdenticalYMMVehicle', async () => {
@ -67,9 +142,7 @@ describe('license-plate-lookup.vue', () => {
await wrapper.vm.forwardButtonAction(); await wrapper.vm.forwardButtonAction();
// Assert // Assert
expect(wrapper.findComponent({ ref: 'alertMatchedTwoIdenticalYMMVehicle' }).isVisible()).toBe( expect(wrapper.findComponent({ ref: 'alertMatchedTwoIdenticalYMMVehicle' }).isVisible()).toBe(true);
true
);
}); });
test('If no vehicles found, display VinNotFound alert', async () => { test('If no vehicles found, display VinNotFound alert', async () => {
@ -101,7 +174,7 @@ describe('license-plate-lookup.vue', () => {
describe('navigation', () => { describe('navigation', () => {
test('Clicking back button navigates back', async () => { test('Clicking back button navigates back', async () => {
// Arrange // Arrange
const { wrapper, apiPromise } = setupMocks({ const { wrapper } = setupMocks({
mountOptionsMockData: { mountOptionsMockData: {
router: { router: {
navigate: jest.fn() navigate: jest.fn()
@ -109,12 +182,10 @@ describe('license-plate-lookup.vue', () => {
} }
}); });
// Act // Act
licensePlateLookup.beforeRouteEnter.call( licensePlateLookup.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'license-plate-lookup' } }, { query: { issPage: 'license-plate-lookup' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
await wrapper.vm.backButtonAction(); await wrapper.vm.backButtonAction();
// Assert // Assert
@ -155,7 +226,7 @@ describe('license-plate-lookup.vue', () => {
useMainStore().order.vehicle.carId = 'CARID'; useMainStore().order.vehicle.carId = 'CARID';
let carsFound = [ const carsFound = [
{ {
vin: 'TEST_VIN2', vin: 'TEST_VIN2',
vehicle: { vehicle: {
@ -168,12 +239,10 @@ describe('license-plate-lookup.vue', () => {
await wrapper.vm.navigateForward(carsFound); await wrapper.vm.navigateForward(carsFound);
// Assert // Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
undefined, undefined,
{}, {},
{ displayVehicleChangeAlert: true } { displayVehicleChangeAlert: true });
);
}); });
}); });
@ -184,103 +253,15 @@ describe('license-plate-lookup.vue', () => {
useMainStore().order.vehicle.carId = 'CR00000395'; useMainStore().order.vehicle.carId = 'CR00000395';
// Act // Act
licensePlateLookup.beforeRouteEnter.call( licensePlateLookup.beforeRouteEnter.call(wrapper.vm,
wrapper.vm,
{ query: { issPage: 'license-plate-lookup' } }, { query: { issPage: 'license-plate-lookup' } },
undefined, undefined,
(c) => c(wrapper.vm) (c) => c(wrapper.vm));
);
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid(); const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
// Assert // Assert
expect(arePagePrerequisitesValid).toBe(true); expect(arePagePrerequisitesValid).toBe(true);
}); });
}); });
}); });
function setupMocks({
isZipValid = true,
isZipServiceable = true,
lookupVinByPlateResponse,
partsOrQuestions = [],
vehicle = {},
vin = null,
carId = 'C0000',
vinLookupResponseError = null,
route = null
}) {
useMainStore().validateZip = jest.fn().mockImplementation(() => {
return Promise.resolve({
data: {
isValid: isZipValid,
isServiceable: isZipServiceable
}
});
});
useMainStore().lookupVinByPlate = jest.fn().mockImplementation(() => {
return Promise.resolve({
data: lookupVinByPlateResponse
? lookupVinByPlateResponse : {
vin: 'TEST_VIN',
vehicle: {
carId: 'CARID'
}
}
});
});
useMainStore().getPartsOrQuestions = jest.fn().mockImplementation(() => {
return Promise.resolve({
data: {
partsOrQuestions: partsOrQuestions
}
});
});
const wrapper = shallowMount(
licensePlateLookup,
getMountOptions({
route: route ? route : undefined,
router: {
navigate: jest.fn()
},
mainStore: {
order: {
vehicle: {
carId: carId,
registration: {
licensePlate: 'TESTPLATE',
zipCode: '12345'
}
}
}
}
})
);
const apiResponses = {
serviceZipValidationResponse: {
isValid: isZipValid,
isServiceable: isZipServiceable
},
vinLookupResponse: {
vin: vin,
vehicle: vehicle,
error: vinLookupResponseError
}
};
settleAllPromises.mockImplementation(() => apiResponses);
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => '');
wrapper.vm.setCmsContent = jest.fn();
wrapper.vm.$refs.siteFooter.updateButtonText = jest.fn();
wrapper.vm.$refs.siteFooter.removeLoader = jest.fn();
wrapper.vm.$refs.siteFooter.disableForwardButton = jest.fn();
wrapper.vm.$refs.siteFooter.enableForwardButton = jest.fn();
return { wrapper };
}

View file

@ -1,5 +1,9 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" /> <siteHeader cmsWidgetName="SiteHeaderWidget" />
@ -8,57 +12,60 @@
<div class="row px-3"> <div class="row px-3">
<div class="col"> <div class="col">
<div class="select-car-form rounded"> <div class="select-car-form rounded">
<vehicleBanner class="mt-2 mb-4" cms-widget-name="VehicleBannerWidget" :display-generic-vehicle-image="false" /> <vehicleBanner
<siteSubHeader cms-widget-name="SiteSubHeaderWidget" /> class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<alert <alert
ref="alertVinNotFound" v-if="displayVinNotFoundAlert"
v-if="displayVinNotFoundAlert" ref="alertVinNotFound"
class="mb-4" class="mb-4"
cmsWidgetName="AlertVinNotFoundWidget" cmsWidgetName="AlertVinNotFoundWidget"
alertClass="alert-danger" alertClass="alert-danger"
v-bind:isDismissible="false" /> :isDismissible="false" />
<alert <alert
ref="alertMatchedDifferentVehicle" v-if="displayMatchedDifferentVehicleAlert"
v-if="displayMatchedDifferentVehicleAlert" ref="alertMatchedDifferentVehicle"
class="mb-4" class="mb-4"
cmsWidgetName="AlertMatchedDifferentVehicleWidget" cmsWidgetName="AlertMatchedDifferentVehicleWidget"
:manualHeadline="AlertMatchedDifferentVehicleHeader" :manualHeadline="AlertMatchedDifferentVehicleHeader"
:manualCopy="AlertMatchedDifferentVehicleBody" :manualCopy="AlertMatchedDifferentVehicleBody"
alertClass="alert-warning" alertClass="alert-warning"
v-bind:isDismissible="false" /> :isDismissible="false" />
<alert <alert
ref="alertMatchedTwoIdenticalYMMVehicle" v-if="displayMatchedTwoIdenticalYMMVehicleAlert"
v-if="displayMatchedTwoIdenticalYMMVehicleAlert" ref="alertMatchedTwoIdenticalYMMVehicle"
class="mb-4" class="mb-4"
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget" cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader" :manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader"
:manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody" :manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody"
alertClass="alert-warning" alertClass="alert-warning"
v-bind:isDismissible="false" /> :isDismissible="false" />
<textboxQuestion <textboxQuestion
cmsWidgetName="LicensePlateNumberQuestionWidget" id="license-plate-question-wrapper"
v-model="licensePlate" v-model="licensePlate"
isRequired cmsWidgetName="LicensePlateNumberQuestionWidget"
disableAutoFill isRequired
id="license-plate-question-wrapper" disableAutoFill
inputId="license-plate-question" inputId="license-plate-question"
validationRules="license-plate-required" /> validationRules="license-plate-required" />
<dropdownQuestion <dropdownQuestion
cmsWidgetName="StateQuestionWidget" ref="state"
v-model="licenseState" v-model="licenseState"
ref="state" cmsWidgetName="StateQuestionWidget"
inputId="8fdf9dc2e13e430eb57529499dceb3eb" inputId="8fdf9dc2e13e430eb57529499dceb3eb"
:options="stateOptions" :options="stateOptions"
disableAutoFill disableAutoFill
validationRules="state-required" validationRules="state-required"
class="mt-4 mb-2"/> class="mt-4 mb-2" />
<siteFooter <siteFooter
class="mt-5" ref="siteFooter"
:isForwardActionDisabled="!meta.valid" class="mt-5"
cms-widget-name="SiteFooterWidget" :isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction" cmsWidgetName="SiteFooterWidget"
@forward-clicked="forwardButtonAction" @back-clicked="backButtonAction"
ref="siteFooter" /> @forward-clicked="forwardButtonAction" />
</div> </div>
</div> </div>
</div> </div>
@ -75,21 +82,21 @@ import { settleAllPromises } from '@/helpers/layout-helper';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import { errorMessages } from '@/constants/error-messages'; import { errorMessages } from '@/constants/error-messages';
import { required } from '@/helpers/validation-rules'; import { required } from '@/helpers/validation-rules';
import { defineRule } from 'vee-validate'; import { defineRule, Form } from 'vee-validate';
import { getDamageString, isGlassAvailableForCarId } from '@/helpers/damage-helper.js'; import { getDamageString, isGlassAvailableForCarId } from '@/helpers/damage-helper.js';
import { routerParams } from '@/router/router-params.js' import { routerParams } from '@/router/router-params.js';
import { states } from '@/constants/states'; import { states } from '@/constants/states';
// Import Component // Import Component
import baseFormMixin from '@/mixins/base-form-mixin'; import baseFormMixin from '@/mixins/base-form-mixin';
import vinPagesMixin from '@/mixins/vin-pages-mixin'; import vinPagesMixin from '@/mixins/vin-pages-mixin';
import { Form } from 'vee-validate';
import siteFooter from '@/iss-components/site-footer/site-footer.vue'; import siteFooter from '@/iss-components/site-footer/site-footer';
import siteHeader from '@/iss-components/site-header/site-header.vue'; import siteHeader from '@/iss-components/site-header/site-header';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue'; import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue'; import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue'; import textboxQuestion from '@/digital-components/textbox-question/textbox-question';
import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question.vue'; import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question';
import alert from '@/ux-components/alert/alert'; import alert from '@/ux-components/alert/alert';
// Define Validation Rules // Define Validation Rules
@ -98,6 +105,17 @@ defineRule('state-required', required(errorMessages.STATE_REQUIRED));
export default { export default {
name: 'license-plate-lookup', name: 'license-plate-lookup',
components: {
// eslint-disable-next-line vue/no-reserved-component-names
Form,
siteFooter,
siteHeader,
siteSubHeader,
vehicleBanner,
textboxQuestion,
dropdownQuestion,
alert
},
mixins: [baseFormMixin, vinPagesMixin], mixins: [baseFormMixin, vinPagesMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
@ -134,6 +152,56 @@ export default {
forwardButtonCarStyle: '' forwardButtonCarStyle: ''
}; };
}, },
computed: {
AlertMatchedDifferentVehicleHeader() {
return this.getCmsContent('AlertMatchedDifferentVehicleWidget',
'HeadlineText').replaceAll('{custom:damage}', getDamageString());
},
AlertMatchedDifferentVehicleBody() {
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return this.getCmsContent('AlertMatchedDifferentVehicleWidget', 'BodyText')
.replaceAll('{custom:damage}', getDamageString())
.replaceAll('{custom:vinYmmFound}', vinYmmFound)
.replaceAll('{custom:vinYmmExpected}', vinYmmExpected);
},
AlertMatchedTwoIdenticalYMMVehicleHeader() {
return this.getCmsContent('AlertMatchedTwoIdenticalYMMVehicleWidget',
'HeadlineText').replaceAll('{custom:damage}', getDamageString());
},
AlertMatchedTwoIdenticalYMMVehicleBody() {
const vinYmmsFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model} ${this.customAlertData?.vehicleInfo?.style}`;
const vinYmmsExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model} ${this.mainStore.order.vehicle.style}`;
return this.getCmsContent('AlertMatchedTwoIdenticalYMMVehicleWidget', 'BodyText')
.replaceAll('{custom:damage}', getDamageString())
.replaceAll('{custom:vinYmmsFound}', vinYmmsFound)
.replaceAll('{custom:vinYmmsExpected}', vinYmmsExpected);
},
isTwoIdenticalYMMVehicleFound() {
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return (vinYmmFound.toLowerCase() === vinYmmExpected.toLowerCase());
},
stateOptions: {
get() {
return states;
}
}
},
watch: {
licensePlate() {
this.$refs.siteFooter.updateButtonText(this.getCmsContent('SiteFooterWidget', 'ForwardButtonText'));
},
registrationZipCode() {
this.$refs.siteFooter.updateButtonText(this.getCmsContent('SiteFooterWidget', 'ForwardButtonText'));
}
},
mounted() {
this.attachCustomEvents();
this.loadDefaultsFromStore();
},
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
return this.mainStore.order.vehicle.carId !== null; return this.mainStore.order.vehicle.carId !== null;
@ -147,12 +215,10 @@ export default {
}, },
attachCustomEvents() { attachCustomEvents() {
this.prependActionToMethod(this, this.forwardButtonAction, () => { this.prependActionToMethod(this, this.forwardButtonAction, () => {
this.pushEventToGA( this.pushEventToGA(this.$route.query[this.queryStrings.ISS_PAGE],
this.$route.query[this.queryStrings.ISS_PAGE],
this.GaActions.SUBMITTED, this.GaActions.SUBMITTED,
this.GaLabels.LICENSE_PLATE_LOOKUP, this.GaLabels.LICENSE_PLATE_LOOKUP,
true true);
);
}); });
}, },
// NOTE: If form is not valid, this method is not called when 'Continue' button is clicked // NOTE: If form is not valid, this method is not called when 'Continue' button is clicked
@ -160,8 +226,7 @@ export default {
this.resetWarningsAndErrors(); this.resetWarningsAndErrors();
// Lookup VIN // Lookup VIN
const vinLookupResponse = await useMainStore().lookupVinByPlate( const vinLookupResponse = await useMainStore().lookupVinByPlate(this.licensePlate, this.licenseState);
this.licensePlate, this.licenseState);
// Settle promises and get results // Settle promises and get results
const promiseResultMap = [ const promiseResultMap = [
@ -178,33 +243,31 @@ export default {
this.displayVinNotFoundAlert = true; this.displayVinNotFoundAlert = true;
this.$refs.siteFooter.disableForwardButton(); this.$refs.siteFooter.disableForwardButton();
return this.$refs.siteFooter.removeLoader(); return this.$refs.siteFooter.removeLoader();
}; }
// Vehicle found from VIN lookup // Vehicle found from VIN lookup
const vehicleFromLookup = resultMap.vinLookupResponse.vehicle; const vehicleFromLookup = resultMap.vinLookupResponse.vehicle;
// Check if the CarId has changed // Check if the CarId has changed
this.isCarIdDifferent = this.isCarIdDifferent
vehicleFromLookup.carId !== useMainStore().order.vehicle.carId; = vehicleFromLookup.carId !== useMainStore().order.vehicle.carId;
// Handle changing car // Handle changing car
if ( if (
this.isCarIdDifferent && this.isCarIdDifferent
vehicleFromLookup.carId !== this.previouslyEnteredCarId && vehicleFromLookup.carId !== this.previouslyEnteredCarId
) { ) {
// Display Alert // Display Alert
this.previouslyEnteredCarId = vehicleFromLookup.carId; this.previouslyEnteredCarId = vehicleFromLookup.carId;
this.customAlertData.vehicleInfo = vehicleFromLookup; this.customAlertData.vehicleInfo = vehicleFromLookup;
if (this.isTwoIdenticalYMMVehicleFound){ if (this.isTwoIdenticalYMMVehicleFound) {
this.displayMatchedTwoIdenticalYMMVehicleAlert = true; this.displayMatchedTwoIdenticalYMMVehicleAlert = true;
this.forwardButtonCarStyle= vehicleFromLookup.style; this.forwardButtonCarStyle = vehicleFromLookup.style;
} else { } else {
this.displayMatchedDifferentVehicleAlert = true; this.displayMatchedDifferentVehicleAlert = true;
} }
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId( this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(vehicleFromLookup.carId);
vehicleFromLookup.carId
);
// Update button "Continue with..." // Update button "Continue with..."
this.$refs.siteFooter.updateButtonText(`Continue with ${vehicleFromLookup.year} ${vehicleFromLookup.make} ${vehicleFromLookup.model} ${this.forwardButtonCarStyle}`); this.$refs.siteFooter.updateButtonText(`Continue with ${vehicleFromLookup.year} ${vehicleFromLookup.make} ${vehicleFromLookup.model} ${this.forwardButtonCarStyle}`);
@ -212,17 +275,15 @@ export default {
} }
// Save vehicle, license plate, and registration information // Save vehicle, license plate, and registration information
await useMainStore().saveRegistrationLicensePlateLookup( await useMainStore().saveRegistrationLicensePlateLookup({
{ isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle,
isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle, vehicleInfo: Object.assign(vinLookupResponse.data.vehicle, { vin: vinLookupResponse.data.vin }),
vehicleInfo: Object.assign(vinLookupResponse.data.vehicle, { vin: vinLookupResponse.data.vin }), registrationInfo: {
registrationInfo: { licensePlate: this.licensePlate,
licensePlate: this.licensePlate, state: this.licenseState
state: this.licenseState }
} },
}, false);
false
);
return await this.navigateForward(); return await this.navigateForward();
}, },
@ -230,12 +291,10 @@ export default {
// If a different vehicle is found than the one entered and the selected glass is not available for that vehicle then navigate back to "vehicle-damage" // If a different vehicle is found than the one entered and the selected glass is not available for that vehicle then navigate back to "vehicle-damage"
// display vehicle changed alert on that page. // display vehicle changed alert on that page.
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) { if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$route, this.$route,
{}, {},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true } { [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true });
);
} else { } else {
await this.navigateForwardWithSingleCarMatch(); await this.navigateForwardWithSingleCarMatch();
} }
@ -244,79 +303,11 @@ export default {
this.displayVinNotFoundAlert = false; this.displayVinNotFoundAlert = false;
this.displayMatchedDifferentVehicleAlert = false; this.displayMatchedDifferentVehicleAlert = false;
} }
},
mounted() {
this.attachCustomEvents();
this.loadDefaultsFromStore();
},
computed: {
AlertMatchedDifferentVehicleHeader() {
return this.getCmsContent(
'AlertMatchedDifferentVehicleWidget',
'HeadlineText'
).replaceAll('{custom:damage}', getDamageString());
},
AlertMatchedDifferentVehicleBody() {
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return this.getCmsContent('AlertMatchedDifferentVehicleWidget', 'BodyText')
.replaceAll('{custom:damage}', getDamageString())
.replaceAll('{custom:vinYmmFound}', vinYmmFound)
.replaceAll('{custom:vinYmmExpected}', vinYmmExpected);
},
AlertMatchedTwoIdenticalYMMVehicleHeader() {
return this.getCmsContent(
'AlertMatchedTwoIdenticalYMMVehicleWidget',
'HeadlineText'
).replaceAll('{custom:damage}', getDamageString());
},
AlertMatchedTwoIdenticalYMMVehicleBody() {
const vinYmmsFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model} ${this.customAlertData?.vehicleInfo?.style}`;
const vinYmmsExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model} ${this.mainStore.order.vehicle.style}`;
return this.getCmsContent('AlertMatchedTwoIdenticalYMMVehicleWidget', 'BodyText')
.replaceAll('{custom:damage}', getDamageString())
.replaceAll('{custom:vinYmmsFound}', vinYmmsFound)
.replaceAll('{custom:vinYmmsExpected}', vinYmmsExpected);
},
isTwoIdenticalYMMVehicleFound() {
const vinYmmFound = `${this.customAlertData?.vehicleInfo?.year} ${this.customAlertData?.vehicleInfo?.make} ${this.customAlertData?.vehicleInfo?.model}`;
const vinYmmExpected = `${this.mainStore.order.vehicle.year} ${this.mainStore.order.vehicle.make} ${this.mainStore.order.vehicle.model}`;
return (vinYmmFound.toLowerCase()==vinYmmExpected.toLowerCase());
},
stateOptions: {
get: function () {
return states;
}
}
},
watch: {
licensePlate() {
this.$refs.siteFooter.updateButtonText(
this.getCmsContent('SiteFooterWidget', 'ForwardButtonText')
);
},
registrationZipCode() {
this.$refs.siteFooter.updateButtonText(
this.getCmsContent('SiteFooterWidget', 'ForwardButtonText')
);
}
},
components: {
Form,
siteFooter,
siteHeader,
siteSubHeader,
vehicleBanner,
textboxQuestion,
dropdownQuestion,
alert
} }
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
#license-plate-question-wrapper .form-test-error { #license-plate-question-wrapper .form-test-error {
/** /**
Override extra margin-bottom in the error message in TextboxQuestion Override extra margin-bottom in the error message in TextboxQuestion

View file

@ -5,9 +5,9 @@ import moldingQuestions from '@/layouts/molding-questions/molding-questions';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { getMountOptions } from '@/helpers/unit-test-helper.js'; import { getMountOptions } from '@/helpers/unit-test-helper.js';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
import baseMixin from '../../mixins/base-mixin';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin'; import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import baseMixin from '@/mixins/base-mixin';
// Mock our module for promises. // Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => ({ jest.mock('@/helpers/layout-helper.js', () => ({
@ -19,79 +19,136 @@ jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: jest.fn() fetchCmsContentForPage: jest.fn()
})); }));
const baseStoreGettersPageData = () => { function setupMocks({
return { mountOptionsMockData = {
partsOrQuestions: [ router: {
navigate: jest.fn()
},
actionList: [
{ {
parts: [ actionName: 'saveMoldingQuestionAnswers',
{ data: {}
childPartQuestions: [ },
{ {
questionSequence: 1, actionName: 'getPartsOrQuestions',
questionText: data: {}
'Does the rubber seal around your windshield have a chrome strip running through it?',
answers: [
{
answerResult: 'WKT D1106 C',
answerText: 'Yes',
nextQuestionSequence: null
},
{
answerResult: 'WKT D1106 B',
answerText: 'No',
nextQuestionSequence: null
}
]
}
],
basePartNumber: 'DW01105',
color: 'Green Tint, Blue Shade',
requiresRecalibration: false,
recalibrationType: '',
canSafeliteRecalibrate: false,
requiresCapabilityQuestions: false,
childParts: null,
partNumber: 'DW01105GBNN',
description: 'solar',
partType: 'WINDSHIELD'
}
],
partQuestions: [],
glassLocation: 'Windshield',
glassName: 'Single',
answerKey: 'Windshield-Single',
answerData: null
} }
] ],
}; route: {
}; query: {
const baseStoreGettersDamage = () => { issPage: 'molding-questions'
return { }
partsQuestionAnswers: [ },
{ data() {
glassLocation: 'Windshield', return {
glassName: 'Single', computedSwitcher: [
result: 'FW04848',
answeredQuestions: [
{ {
questionText: glassLocation: 'Windshield',
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?', glassName: 'Single',
selectedAnswer: '1|nextQuestion|3|Yes', answerData: {
selectedAnswerText: 'Yes', answerResult: 'FW04848',
questionNum: 1 answeredQuestions: []
}, }
{
questionText:
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
selectedAnswer: '2|nextQuestion|3|Yes',
selectedAnswerText: 'Yes',
questionNum: 2
} }
] ]
};
},
questionsData: {
get() {
return this.computedSwitcher;
},
set(val) {
this.computedSwitcher = val;
} }
] }
}; }
}; }) {
useMainStore().getPartsOrQuestions = jest.fn(() => ({
data: {
partsOrQuestions: []
}
}));
const mountOptions = getMountOptions({
...mountOptionsMockData,
mixins: [baseMixin, vehicleQuestionsMixin]
});
mountOptions.attachTo = document.body;
const wrapper = shallowMount(moldingQuestions, mountOptions);
return { wrapper };
}
const baseStoreGettersPageData = () => ({
partsOrQuestions: [
{
parts: [
{
childPartQuestions: [
{
questionSequence: 1,
questionText:
'Does the rubber seal around your windshield have a chrome strip running through it?',
answers: [
{
answerResult: 'WKT D1106 C',
answerText: 'Yes',
nextQuestionSequence: null
},
{
answerResult: 'WKT D1106 B',
answerText: 'No',
nextQuestionSequence: null
}
]
}
],
basePartNumber: 'DW01105',
color: 'Green Tint, Blue Shade',
requiresRecalibration: false,
recalibrationType: '',
canSafeliteRecalibrate: false,
requiresCapabilityQuestions: false,
childParts: null,
partNumber: 'DW01105GBNN',
description: 'solar',
partType: 'WINDSHIELD'
}
],
partQuestions: [],
glassLocation: 'Windshield',
glassName: 'Single',
answerKey: 'Windshield-Single',
answerData: null
}
]
});
const baseStoreGettersDamage = () => ({
partsQuestionAnswers: [
{
glassLocation: 'Windshield',
glassName: 'Single',
result: 'FW04848',
answeredQuestions: [
{
questionText:
'Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?',
selectedAnswer: '1|nextQuestion|3|Yes',
selectedAnswerText: 'Yes',
questionNum: 1
},
{
questionText:
'Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?',
selectedAnswer: '2|nextQuestion|3|Yes',
selectedAnswerText: 'Yes',
questionNum: 2
}
]
}
]
});
useMainStore().pageData = baseStoreGettersPageData; useMainStore().pageData = baseStoreGettersPageData;
useMainStore().damage = baseStoreGettersDamage; useMainStore().damage = baseStoreGettersDamage;
@ -114,9 +171,7 @@ describe('moldingQuestions.vue', () => {
test('Should return false for valid page requisites if partsOrQuestions in pageData is missing', () => { test('Should return false for valid page requisites if partsOrQuestions in pageData is missing', () => {
// Arrange // Arrange
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
useMainStore().pageData = jest.fn(() => { useMainStore().pageData = jest.fn(() => undefined);
return undefined;
});
// Act // Act
const result = wrapper.vm.arePagePrerequisitesValid(); const result = wrapper.vm.arePagePrerequisitesValid();
@ -129,11 +184,9 @@ describe('moldingQuestions.vue', () => {
test('Should be at least one item in partsOrQuestions', () => { test('Should be at least one item in partsOrQuestions', () => {
// Arrange // Arrange
useMainStore().pageData = jest.fn(() => { useMainStore().pageData = jest.fn(() => ({
return { partsOrQuestions: []
partsOrQuestions: [] }));
};
});
useMainStore().damage = baseStoreGettersDamage; useMainStore().damage = baseStoreGettersDamage;
const { wrapper } = setupMocks({}); const { wrapper } = setupMocks({});
@ -207,13 +260,11 @@ describe('moldingQuestions.vue', () => {
} }
} }
]; ];
wrapper.vm.dispatchStoreAction = jest.fn(() => { wrapper.vm.dispatchStoreAction = jest.fn(() => ({
return { data: {
data: { partsOrQuestions: []
partsOrQuestions: [] }
} }));
};
});
// Act // Act
wrapper.vm.forwardButtonAction(); wrapper.vm.forwardButtonAction();
@ -238,13 +289,11 @@ describe('moldingQuestions.vue', () => {
} }
} }
]; ];
useMainStore().getPartsOrQuestions = jest.fn(() => { useMainStore().getPartsOrQuestions = jest.fn(() => ({
return { data: {
data: { partsOrQuestions: []
partsOrQuestions: [] }
} }));
};
});
// Act // Act
wrapper.vm.forwardButtonAction(); wrapper.vm.forwardButtonAction();
@ -270,13 +319,11 @@ describe('moldingQuestions.vue', () => {
} }
} }
]; ];
useMainStore().getPartsOrQuestions = jest.fn(() => { useMainStore().getPartsOrQuestions = jest.fn(() => ({
return { data: {
data: { partsOrQuestions: []
partsOrQuestions: [] }
} }));
};
});
// Act // Act
wrapper.vm.forwardButtonAction(); wrapper.vm.forwardButtonAction();
@ -303,13 +350,11 @@ describe('moldingQuestions.vue', () => {
} }
} }
]; ];
useMainStore().getPartsOrQuestions = jest.fn(() => { useMainStore().getPartsOrQuestions = jest.fn(() => ({
return { data: {
data: { partsOrQuestions: []
partsOrQuestions: [] }
} }));
};
});
wrapper.vm.navigateForward = jest.fn(); wrapper.vm.navigateForward = jest.fn();
// Act // Act
@ -322,66 +367,3 @@ describe('moldingQuestions.vue', () => {
}); });
}); });
}); });
function setupMocks({
mountOptionsMockData = {
router: {
navigate: jest.fn()
},
actionList: [
{
actionName: 'saveMoldingQuestionAnswers',
data: {}
},
{
actionName: 'getPartsOrQuestions',
data: {}
}
],
route: {
query: {
issPage: 'molding-questions'
}
},
data() {
return {
computedSwitcher: [
{
glassLocation: 'Windshield',
glassName: 'Single',
answerData: {
answerResult: 'FW04848',
answeredQuestions: []
}
}
]
};
},
questionsData: {
get() {
return this.computedSwitcher;
},
set(val) {
this.computedSwitcher = val;
}
}
}
}) {
useMainStore().getPartsOrQuestions = jest.fn(() => {
return {
data: {
partsOrQuestions: []
}
};
});
const mountOptions = getMountOptions({
...mountOptionsMockData,
mixins: [baseMixin, vehicleQuestionsMixin]
});
mountOptions['attachTo'] = document.body;
const wrapper = shallowMount(moldingQuestions, mountOptions);
return { wrapper };
}

View file

@ -1,24 +1,22 @@
<template> <template>
<Form <Form
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit"
ref="theForm" ref="theForm"
v-slot="{ meta }" v-slot="{ meta }"
> @submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<questionsPageLayout <questionsPageLayout
isRequired id="molding-question-wrapper"
ref="questionsPageLayout" ref="questionsPageLayout"
v-model="selectedAnswers"
isRequired
:isMetaValid="meta.valid" :isMetaValid="meta.valid"
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader" :alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy" :alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
:questionsData="questionsData" :questionsData="questionsData"
:validationRules="rules.optionRequired" :validationRules="rules.optionRequired"
v-model="selectedAnswers"
@forwardButtonAction="forwardButtonAction"
@back-click="navigateBack"
:index="currentGlassIndex" :index="currentGlassIndex"
id="molding-question-wrapper" @forwardButtonAction="forwardButtonAction"
/> @back-click="navigateBack" />
</Form> </Form>
</template> </template>
<script> <script>
@ -33,10 +31,15 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js';
// Import Component // Import Component
import { Form } from 'vee-validate'; import { Form } from 'vee-validate';
import questionsPageLayout from '@/iss-components/questions-page-layout/questions-page-layout.vue'; import questionsPageLayout from '@/iss-components/questions-page-layout/questions-page-layout';
export default { export default {
name: 'molding-questions', name: 'molding-questions',
components: {
// eslint-disable-next-line vue/no-reserved-component-names
Form,
questionsPageLayout
},
mixins: [BaseFormMixin, vehicleQuestionsMixin], mixins: [BaseFormMixin, vehicleQuestionsMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage); const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -67,10 +70,8 @@ export default {
}, },
computed: { computed: {
AlertFewMoreQuestionsHeader() { AlertFewMoreQuestionsHeader() {
return this.getCmsContent( return this.getCmsContent('AdditionalPartsQuestionsAlert',
'AdditionalPartsQuestionsAlert', 'HeadlineText');
'HeadlineText'
);
}, },
AlertFewMoreQuestionsCopy() { AlertFewMoreQuestionsCopy() {
return this.getCmsContent('AdditionalPartsQuestionsAlert', 'BodyText'); return this.getCmsContent('AdditionalPartsQuestionsAlert', 'BodyText');
@ -85,62 +86,48 @@ export default {
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
const moldingQuestionsFromPageData = useMainStore().pageData( const moldingQuestionsFromPageData = useMainStore().pageData(issPageValues.MOLDING_QUESTIONS);
issPageValues.MOLDING_QUESTIONS
);
return ( return (
// has childPartQuestions array and has glassName not null // has childPartQuestions array and has glassName not null
moldingQuestionsFromPageData?.partsOrQuestions?.some( moldingQuestionsFromPageData?.partsOrQuestions?.some((part) => part?.glassName)
(part) => part?.glassName && moldingQuestionsFromPageData.partsOrQuestions.some((glass) => glass.parts?.some((part) => part?.childPartQuestions?.length > 0))
) &&
moldingQuestionsFromPageData.partsOrQuestions.some((glass) =>
glass.parts?.some((part) => part?.childPartQuestions?.length > 0)
)
); );
}, },
getInitialQuestionData() { getInitialQuestionData() {
// get any questions that were already answered // get any questions that were already answered
const alreadyAnsweredQuestions = const alreadyAnsweredQuestions
useMainStore().damage.moldingQuestionAnswers; = useMainStore().damage.moldingQuestionAnswers;
this.questionsData = this.partsOrQuestionsData this.questionsData = this.partsOrQuestionsData
.filter((x) => x.parts[0].childPartQuestions.length) .filter((x) => x.parts[0].childPartQuestions.length)
.map((glass, index) => { .map((glass, index) => {
// NOTE: questions for property "questions" can differ between layouts // NOTE: questions for property "questions" can differ between layouts
glass.questions = glass.parts[0].childPartQuestions; glass.questions = glass.parts[0].childPartQuestions;
glass.answerKey = glass.glassLocation + '-' + glass.glassName; glass.answerKey = `${glass.glassLocation}-${glass.glassName}`;
// reset selectedAnswers for this glass // reset selectedAnswers for this glass
this.selectedAnswers[glass.answerKey] = []; this.selectedAnswers[glass.answerKey] = [];
const updatedGlass = this.setupInitialData( const updatedGlass = this.setupInitialData(glass,
glass,
index, index,
alreadyAnsweredQuestions alreadyAnsweredQuestions);
);
// Set up watch for each set of glass questions // Set up watch for each set of glass questions
this.$watch( this.$watch(`selectedAnswers.${glass.answerKey}`,
'selectedAnswers.' + glass.answerKey,
(newValue) => { (newValue) => {
if (newValue && Object.keys(newValue).length > 0) { if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates( this.handleAnswerUpdates(newValue,
newValue, glass.answerKey);
glass.answerKey
);
} }
}, },
{ deep: true } { deep: true });
);
return updatedGlass; return updatedGlass;
}); });
}, },
async forwardButtonAction() { async forwardButtonAction() {
const questionAnswersArray = this.questionsData.map((glass) => { const questionAnswersArray = this.questionsData.map((glass) => ({
return { glassLocation: glass.glassLocation,
glassLocation: glass.glassLocation, glassName: glass.glassName,
glassName: glass.glassName, partNum: glass.answerData.answerResult,
partNum: glass.answerData.answerResult, answeredQuestions: glass.answerData.answeredQuestions,
answeredQuestions: glass.answerData.answeredQuestions, isSuppressedPart: glass.isSuppressedPart
isSuppressedPart: glass.isSuppressedPart }));
};
});
// clear out answerData for future page loads; must occur prior to store save // clear out answerData for future page loads; must occur prior to store save
this.questionsData.forEach((glass) => { this.questionsData.forEach((glass) => {
glass.answerData = {}; glass.answerData = {};
@ -149,14 +136,12 @@ export default {
await this.mainStore.saveMoldingQuestionAnswers(questionAnswersArray); await this.mainStore.saveMoldingQuestionAnswers(questionAnswersArray);
// get parts from the questionAnswers // get parts from the questionAnswers
let partsOrQuestions = this.partsOrQuestionsData; const partsOrQuestions = this.partsOrQuestionsData;
for (let answer of questionAnswersArray) { for (const answer of questionAnswersArray) {
partsOrQuestions.find((partOrQuestion) => { partsOrQuestions.find((partOrQuestion) => (
return ( partOrQuestion.glassLocation === answer.glassLocation
partOrQuestion.glassLocation === answer.glassLocation && && partOrQuestion.glassName === answer.glassName
partOrQuestion.glassName === answer.glassName )).parts[0].childParts = [
);
}).parts[0].childParts = [
{ {
partNumber: answer.partNum partNumber: answer.partNum
} }
@ -165,15 +150,11 @@ export default {
this.navigateForward(partsOrQuestions, null); this.navigateForward(partsOrQuestions, null);
} }
},
components: {
Form,
questionsPageLayout
} }
}; };
</script> </script>
<style lang="scss"> <style lang="scss" scoped>
#molding-question-wrapper p.text-body.small { #molding-question-wrapper p.text-body.small {
margin-bottom: 0 !important; // Overrides extra margin-bottom on alert body text margin-bottom: 0 !important; // Overrides extra margin-bottom on alert body text
} }

View file

@ -1,17 +1,20 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }" > <Form
ref="theForm"
v-slot="{ meta }"
@submit="onSubmit"
@invalid-submit="onInvalidSubmit">
<div class="page-container-grouped-styles"> <div class="page-container-grouped-styles">
<div class="fade-on-route-transition position-relative"> <div class="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget"/> <siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="container-fluid pb-2"> <div class="container-fluid pb-2">
<p>Placeholder for order confirmation page</p> <p>Placeholder for order confirmation page</p>
<siteFooter <siteFooter
cmsWidgetName="SiteFooterWidget"
ref="siteFooter" ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid" :isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction" @ForwardClicked="forwardButtonAction"
@back-clicked="backButtonAction" @back-clicked="backButtonAction" />
/>
</div> </div>
</div> </div>
</div> </div>
@ -26,8 +29,15 @@ import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper'; import { settleAllPromises } from '@/helpers/layout-helper';
import { Form } from 'vee-validate'; import { Form } from 'vee-validate';
import BaseFormMixin from '@/mixins/base-form-mixin.js'; import BaseFormMixin from '@/mixins/base-form-mixin.js';
export default { export default {
name: 'order-confirmation', name: 'order-confirmation',
components: {
siteHeader,
siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names
Form
},
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
async beforeRouteEnter(to, from, next) { async beforeRouteEnter(to, from, next) {
// Call APIs // Call APIs
@ -39,7 +49,7 @@ export default {
promise: cmsContentPromise promise: cmsContentPromise
}]; }];
// use resultMap to populate layout content. // use resultMap to populate layout content.
let resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => { next((vm) => {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
@ -53,16 +63,9 @@ export default {
return this.navigateForward(); return this.navigateForward();
}, },
navigateForward() { navigateForward() {
this.$router.navigate( this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD,
this.navigationScenarios.CLICKED_FORWARD, this.$route);
this.$route
);
} }
}, }
components: { };
siteHeader,
siteFooter,
Form
}
}
</script> </script>