Linting for first 10 layouts

This commit is contained in:
DavidAtSafelite 2023-07-26 13:47:35 -04:00
parent 5fc43ab548
commit 20cb89ed8d
23 changed files with 1268 additions and 1400 deletions

View file

@ -1,5 +1,5 @@
// Components
import addressLookup from '@/layouts/address-lookup/address-lookup.vue';
import addressLookup from '@/layouts/address-lookup/address-lookup';
// Supporting Files
import { settleAllPromises } from '@/helpers/layout-helper.js';
@ -18,6 +18,79 @@ jest.mock('@/helpers/layout-helper.js', () => ({
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('page level alerts', () => {
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();
// Assert
expect(wrapper.findComponent({ ref: 'alertMatchedDifferentVehicle' }).isVisible()).toBe(
true
);
expect(wrapper.findComponent({ ref: 'alertMatchedDifferentVehicle' }).isVisible()).toBe(true);
});
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();
// Assert
expect(wrapper.findComponent({ ref: 'alertMatchedTwoIdenticalYMMVehicle' }).isVisible()).toBe(
true
);
expect(wrapper.findComponent({ ref: 'alertMatchedTwoIdenticalYMMVehicle' }).isVisible()).toBe(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 () => {
@ -135,9 +204,7 @@ describe('address-lookup.vue', () => {
await wrapper.vm.forwardButtonAction();
// Assert
expect(
wrapper.findComponent({ ref: 'alertVinLookupsByHomeAddressNotAllowed' }).isVisible()
).toBe(true);
expect(wrapper.findComponent({ ref: 'alertVinLookupsByHomeAddressNotAllowed' }).isVisible()).toBe(true);
});
test('if no vehicles found, display Vin Not Found alert', async () => {
@ -278,13 +345,11 @@ describe('address-lookup.vue', () => {
await wrapper.vm.forwardButtonAction();
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES,
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 () => {
@ -310,7 +375,7 @@ describe('address-lookup.vue', () => {
useMainStore().order.vehicle.carId = 'CARID';
let carsFound = [
const carsFound = [
{
vin: 'TEST_VIN2',
vehicle: {
@ -323,12 +388,10 @@ describe('address-lookup.vue', () => {
await wrapper.vm.navigateForward(carsFound);
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
undefined,
{},
{ displayVehicleChangeAlert: true }
);
{ displayVehicleChangeAlert: true });
});
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>
<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="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" />
@ -9,51 +13,56 @@
<div class="col">
<div class="select-car-form rounded">
<vehicleBanner
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
ref="vehicleBanner"
:displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" ref="siteSubHeader" class="mb-5" />
ref="vehicleBanner"
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<siteSubHeader
ref="siteSubHeader"
cmsWidgetName="SiteSubHeaderWidget"
class="mb-5" />
<alert
ref="alertVinNotFound"
v-if="displayVinNotFoundAlert"
class="mb-4 mt-4"
cmsWidgetName="AlertVinNotFoundWidget"
alertClass="alert-danger"
v-bind:isDismissible="false" />
v-if="displayVinNotFoundAlert"
ref="alertVinNotFound"
class="mb-4 mt-4"
cmsWidgetName="AlertVinNotFoundWidget"
alertClass="alert-danger"
:isDismissible="false" />
<alert
ref="alertMatchedDifferentVehicle"
v-if="displayMatchedDifferentVehicleAlert"
class="mb-4 mt-4"
:manualHeadline="AlertMatchedDifferentVehicleHeader"
:manualCopy="AlertMatchedDifferentVehicleBody"
alertClass="alert-warning"
v-bind:isDismissible="false" />
v-if="displayMatchedDifferentVehicleAlert"
ref="alertMatchedDifferentVehicle"
class="mb-4 mt-4"
:manualHeadline="AlertMatchedDifferentVehicleHeader"
:manualCopy="AlertMatchedDifferentVehicleBody"
alertClass="alert-warning"
:isDismissible="false" />
<alert
ref="alertMatchedTwoIdenticalYMMVehicle"
v-if="displayMatchedTwoIdenticalYMMVehicleAlert"
class="mb-4 mt-4"
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader"
:manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody"
alertClass="alert-warning"
v-bind:isDismissible="false" />
v-if="displayMatchedTwoIdenticalYMMVehicleAlert"
ref="alertMatchedTwoIdenticalYMMVehicle"
class="mb-4 mt-4"
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader"
:manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody"
alertClass="alert-warning"
:isDismissible="false" />
<alert
ref="alertVinLookupsByHomeAddressNotAllowed"
v-if="displayVinLookupByHomeAddressNotAllowedAlert"
class="mt-4"
cmsWidgetName="AlertVinLookupsByHomeAddressNotAllowedWidget"
alertClass="alert-danger"
v-bind:isDismissible="false" />
<customerQuestions ref="customerQuestions" v-model="customerQuestions" />
v-if="displayVinLookupByHomeAddressNotAllowedAlert"
ref="alertVinLookupsByHomeAddressNotAllowed"
class="mt-4"
cmsWidgetName="AlertVinLookupsByHomeAddressNotAllowedWidget"
alertClass="alert-danger"
:isDismissible="false" />
<customerQuestions
ref="customerQuestions"
v-model="customerQuestions" />
<siteFooter
class="mt-5"
cmsWidgetName="SiteFooterWidget"
ref="siteFooter"
:isDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction"
@back-clicked="backButtonAction"
:isForwardActionDisabled="!meta.valid" />
ref="siteFooter"
class="mt-5"
cmsWidgetName="SiteFooterWidget"
:isDisabled="!meta.valid"
:isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction"
@back-clicked="backButtonAction" />
</div>
</div>
</div>
@ -67,7 +76,7 @@
<script>
// Components
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 vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
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 vinPagesMixin from '@/mixins/vin-pages-mixin';
import { useMainStore } from '@/store'
import { useMainStore } from '@/store';
export default {
name: 'address-lookup',
components: {
siteHeader,
siteFooter,
vehicleBanner,
siteSubHeader,
customerQuestions,
textboxQuestion,
alert,
// eslint-disable-next-line vue/no-reserved-component-names
Form
},
mixins: [baseFormMixin, vinPagesMixin],
async beforeRouteEnter(to, from, next) {
// Call APIs
@ -122,6 +142,52 @@ export default {
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: {
arePagePrerequisitesValid() {
return this.mainStore.order.vehicle.carId !== null;
@ -133,12 +199,10 @@ export default {
},
attachCustomEvents() {
this.prependActionToMethod(this, this.forwardButtonAction, () => {
this.pushEventToGA(
this.$route.query[this.queryStrings.ISS_PAGE],
this.pushEventToGA(this.$route.query[this.queryStrings.ISS_PAGE],
this.GaActions.SUBMITTED,
this.GaLabels.ADDRESS_LOOKUP,
true
);
true);
});
},
@ -175,7 +239,7 @@ export default {
const carsFound = resultMap.vinLookupResponse.vinVehicles;
// Handle cases for different amounts of VINS found for the address.
if (carsFound.length == 1) {
if (carsFound.length === 1) {
// Single VIN found
const carFound = carsFound[0].vehicle;
@ -186,19 +250,15 @@ export default {
this.customAlertData.vehicleInfo = carFound;
if (this.isTwoIdenticalYMMVehicleFound) {
this.displayMatchedTwoIdenticalYMMVehicleAlert = true;
this.forwardButtonCarStyle= carFound.style;
this.forwardButtonCarStyle = carFound.style;
} else {
this.displayMatchedDifferentVehicleAlert = true;
}
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(
carFound.carId
);
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(carFound.carId);
// Update button "Continue with..."
this.$refs.siteFooter.updateButtonText(
`Continue with ${carFound.year} ${carFound.make} ${carFound.model} ${this.forwardButtonCarStyle}`
);
this.$refs.siteFooter.updateButtonText(`Continue with ${carFound.year} ${carFound.make} ${carFound.model} ${this.forwardButtonCarStyle}`);
return this.$refs.siteFooter.removeLoader();
}
@ -206,9 +266,7 @@ export default {
vehicleInfoToCommit = Object.assign(carFound, { vin: carsFound[0].vin });
} 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
const matchingCars = carsFound.filter(
(vin) => vin.vehicle.carId === this.mainStore.order.vehicle.carId
);
const matchingCars = carsFound.filter((vin) => vin.vehicle.carId === this.mainStore.order.vehicle.carId);
if (matchingCars.length === 1) {
vehicleInfoToCommit = Object.assign(matchingCars[0].vehicle, {
@ -223,55 +281,47 @@ export default {
}
// Save vehicle, customer, service and registration information
await useMainStore().saveRegistrationAddressLookup(
{
isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle,
vehicleInfo:
await useMainStore().saveRegistrationAddressLookup({
isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle,
vehicleInfo:
Object.keys(vehicleInfoToCommit).length === 0
? null
: vehicleInfoToCommit,
registrationInfo: {
firstName: this.customerQuestions.firstName,
lastName: this.customerQuestions.lastName,
address: this.customerQuestions.addressQuestions.streetAddress,
city: this.customerQuestions.addressQuestions.city,
state: this.customerQuestions.addressQuestions.state,
zipCode: this.customerQuestions.addressQuestions.zipCode
}
},
false
);
registrationInfo: {
firstName: this.customerQuestions.firstName,
lastName: this.customerQuestions.lastName,
address: this.customerQuestions.addressQuestions.streetAddress,
city: this.customerQuestions.addressQuestions.city,
state: this.customerQuestions.addressQuestions.state,
zipCode: this.customerQuestions.addressQuestions.zipCode
}
},
false);
return await this.navigateForward(carsFound);
return this.navigateForward(carsFound);
},
async navigateForward(carsFound) {
// Match vehicles found to vehicles in state.
const matchingCars = carsFound.filter(
(car) => car.vehicle.carId === useMainStore().order.vehicle.carId
);
const matchingCars = carsFound.filter((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"
// display vehicle changed alert on that page.
if (
this.isCarIdDifferent &&
!this.isSelectedGlassAvailableForVehicle
this.isCarIdDifferent
&& !this.isSelectedGlassAvailableForVehicle
) {
this.$router.navigate(
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$router.navigate(this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$route,
{},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true }
);
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true });
} else if (matchingCars.length === 1) {
await this.navigateForwardWithSingleCarMatch();
} else {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES,
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_VEHICLES,
this.$route,
{},
{},
carsFound
);
carsFound);
}
},
resetWarningsAndErrors() {
@ -280,68 +330,6 @@ export default {
this.displayVinLookupByHomeAddressNotAllowedAlert = false;
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>

View file

@ -1,20 +1,21 @@
import { shallowMount } from '@vue/test-utils';
import customerQuestions from '@/layouts/address-lookup/customer-questions/customer-questions';
const customerModel = {
addressQuestions: {
streetAddress: '',
city: '',
state: '',
zipCode: ''
},
firstName: '',
lastName: '',
emailAddress: ''
};
// const customerModel = {
// addressQuestions: {
// streetAddress: '',
// city: '',
// state: '',
// zipCode: ''
// },
// firstName: '',
// lastName: '',
// emailAddress: ''
// };
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
const wrapper = shallowMount(customerQuestions);

View file

@ -1,11 +1,13 @@
<template>
<addressQuestions ref="addressQuestions" v-model="customerModel.addressQuestions" />
<addressQuestions
ref="addressQuestions"
v-model="customerModel.addressQuestions" />
<div class="row mb-4">
<div class="col">
<textboxQuestion
cmsWidgetName="FirstNameQuestionWidget"
v-model="customerModel.firstName"
ref="firstName"
v-model="customerModel.firstName"
cmsWidgetName="FirstNameQuestionWidget"
inputId="08497a2efd9a4a73a70360ab47b4838d"
disableAutoFill
:validationRules="rules.firstName" />
@ -14,9 +16,9 @@
<div class="row mb-4">
<div class="col">
<textboxQuestion
cmsWidgetName="LastNameQuestionWidget"
v-model="customerModel.lastName"
ref="lastName"
v-model="customerModel.lastName"
cmsWidgetName="LastNameQuestionWidget"
inputId="0030e56a57e74a4ab92de7fb8e97fec5"
disableAutoFill
:validationRules="rules.lastName" />
@ -25,13 +27,16 @@
</template>
<script>
import addressQuestions from '@/iss-components/address-questions/address-questions.vue';
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
import addressQuestions from '@/iss-components/address-questions/address-questions';
import textboxQuestion from '@/digital-components/textbox-question/textbox-question';
import globalRules from '@/constants/global-rules';
export default {
name: 'customer-questions',
emits: ['update:modelValue'], // The component emits an event
components: {
addressQuestions,
textboxQuestion
}, // The component emits an event
props: {
modelValue: {
type: Object,
@ -50,6 +55,7 @@ export default {
},
validationRules: String
},
emits: ['update:modelValue'],
data() {
return {
rules: {
@ -60,17 +66,13 @@ export default {
},
computed: {
customerModel: {
get: function () {
get() {
return this.modelValue;
},
set: function (newValue) {
set(newValue) {
this.$emit('update:modelValue', newValue);
}
}
},
components: {
addressQuestions,
textboxQuestion
}
};
</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 { 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', () => {
test('Selected vehicle is emitted upon selection', async () => {
// Arrange
@ -14,13 +49,13 @@ describe('address-vehicles-question.vue', () => {
// Assert
expect(wrapper.emitted()['update:modelValue'][0]).toEqual([
{ selectedVehicle: '2016 Jaguar F-Type' },
{ selectedVehicle: '2016 Jaguar F-Type' }
]);
});
test('Should return content for differentVehicleAlertHeader', () => {
// Arrange
const wrapper = shallowMount(addressVehiclesQuestion, {
mixins: [mockMixin],
mixins: [mockMixin]
});
// Assert
@ -62,40 +97,3 @@ describe('address-vehicles-question.vue', () => {
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>
<div>
<alert
ref="differentVehicleAlert"
v-if="displayMatchedDifferentVehicleAlert"
id="address-vehicles-question-alert"
ref="differentVehicleAlert"
class="my-4"
cmsWidgetName="AlertMatchedDifferentVehicleWidget"
:manualHeadline="differentVehicleAlertHeader"
:manualCopy="differentVehicleAlertBody"
alertClass="alert-warning"
v-bind:isDismissible="false"
id="address-vehicles-question-alert" />
:isDismissible="false" />
<alert
ref="alertMatchedTwoIdenticalYMMVehicle"
v-if="displayMatchedTwoIdenticalYMMVehicleAlert"
class="my-4"
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader"
:manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody"
alertClass="alert-warning"
v-bind:isDismissible="false"
id="address-vehicles-question-alert" />
v-if="displayMatchedTwoIdenticalYMMVehicleAlert"
id="address-vehicles-question-alert"
ref="alertMatchedTwoIdenticalYMMVehicle"
class="my-4"
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader"
:manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody"
alertClass="alert-warning"
:isDismissible="false" />
</div>
<buttonQuestion
class="address-vehicles-question"
ref="addressVehiclesQuestion"
v-model="selectedVehicleVin"
class="address-vehicles-question"
buttonTypeString="listButton"
groupName="ChooseAddressVehicle"
:questionText="questionText"
:answers="vehicles"
v-model="selectedVehicleVin"
isRequired
:validation-rules="validationRules" />
:validationRules="validationRules" />
</template>
<script>
@ -43,6 +41,10 @@ import { getDamageString } from '@/helpers/damage-helper';
export default {
name: 'address-vehicles-question',
components: {
buttonQuestion,
alert
},
props: {
vehicles: Array,
vehicleSelected: Object,
@ -53,12 +55,11 @@ export default {
displayMatchedDifferentVehicleAlert: Boolean,
displayMatchedTwoIdenticalYMMVehicleAlert: Boolean
},
emits: ['update: modelValue'],
computed: {
differentVehicleAlertHeader() {
return this.getCmsContent('AlertMatchedDifferentVehicleWidget', 'HeadlineText').replaceAll(
'{custom:damage}',
getDamageString()
);
return this.getCmsContent('AlertMatchedDifferentVehicleWidget', 'HeadlineText').replaceAll('{custom:damage}',
getDamageString());
},
differentVehicleAlertBody() {
const vinYmmFound = `${this.selectedVehicle?.vehicle.year} ${this.selectedVehicle?.vehicle.make} ${this.selectedVehicle?.vehicle.model}`;
@ -70,10 +71,8 @@ export default {
.replaceAll('{custom:vinYmmExpected}', vinYmmExpected);
},
AlertMatchedTwoIdenticalYMMVehicleHeader() {
return this.getCmsContent(
'AlertMatchedTwoIdenticalYMMVehicleWidget',
'HeadlineText'
).replaceAll('{custom:damage}', getDamageString());
return this.getCmsContent('AlertMatchedTwoIdenticalYMMVehicleWidget',
'HeadlineText').replaceAll('{custom:damage}', getDamageString());
},
AlertMatchedTwoIdenticalYMMVehicleBody() {
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');
},
selectedVehicleVin: {
get: function () {
get() {
return this.modelValue;
},
set: function (newValue) {
set(newValue) {
this.$emit('update:modelValue', newValue);
}
},
@ -99,17 +98,13 @@ export default {
// this computed is only needed for the computed differentVehicleAlertBody text above
return this.vehicles.find(({ vin }) => vin === this.selectedVehicleVin);
},
vehicleSelected(){
vehicleSelected() {
return this.vehicleSelected;
}
},
components: {
buttonQuestion,
alert
}
};
</script>
<style lang="scss">
<style lang="scss" scoped>
.address-vehicles-question {
.question-text {
margin-bottom: 0.5rem;

View file

@ -25,6 +25,104 @@ jest.mock('@/helpers/layout-helper.js', () => ({
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', () => {
test('Should navigate to CLICKED_BACK if backButtonAction is run', async () => {
// Arrange
@ -132,118 +230,14 @@ describe('address-vehicles.vue', () => {
useMainStore().order.vehicle.carId = 'CR00000395';
// Act
addressVehicles.beforeRouteEnter.call(
wrapper.vm,
addressVehicles.beforeRouteEnter.call(wrapper.vm,
{ query: { issPage: 'address-vehicles' } },
undefined,
(c) => c(wrapper.vm)
);
(c) => c(wrapper.vm));
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
// Assert
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>
<Form
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit"
ref="theForm"
v-slot="{ meta }">
v-slot="{ meta }"
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<div class="page-container-grouped-styles">
<siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="fade-on-route-transition sub-container overflow-scroll px-5">
@ -13,44 +13,51 @@
:displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<alert
cmsWidgetName="FoundMultipleVehicles"
id="multiple-vehicles-alert"
ref="alertFoundMultipleVehicles"
cmsWidgetName="FoundMultipleVehicles"
class="my-5"
alertClass="alert-warning"
:manualHeadline="AlertFoundMultipleVehiclesHeader"
manualCopy=""
v-bind:isDismissible="false"
id="multiple-vehicles-alert" />
:isDismissible="false" />
<addressVehiclesQuestion
ref="addressVehiclesQuestion"
v-model="selectedVehicleVin"
cmsWidgetName="VehicleConfirmationQuestion"
:vehicles="VehiclesForQuestions"
:vehicleSelected ="VehicleSelected"
:vehicleSelected="VehicleSelected"
validationRules="vehicle-required"
v-model="selectedVehicleVin"
:isCarIdDifferent="isCarIdDifferent"
:displayMatchedDifferentVehicleAlert = "displayMatchedDifferentVehicleAlert"
:displayMatchedTwoIdenticalYMMVehicleAlert = "displayMatchedTwoIdenticalYMMVehicleAlert" />
<div class="alert-provide-vin my-3"
v-if="splitAlertProvideVinBodyForLink.length">
<span v-for="copy in splitAlertProvideVinBodyForLink" :key="copy">
<span v-if="doesCopyContainRouterLink(copy)" class="text-body">
:displayMatchedDifferentVehicleAlert="displayMatchedDifferentVehicleAlert"
:displayMatchedTwoIdenticalYMMVehicleAlert="displayMatchedTwoIdenticalYMMVehicleAlert" />
<div
v-if="splitAlertProvideVinBodyForLink.length"
class="alert-provide-vin my-3">
<span
v-for="copy in splitAlertProvideVinBodyForLink"
:key="copy">
<span
v-if="doesCopyContainRouterLink(copy)"
class="text-body">
<router-link
:to="{
query: { issPage: `${getRouterLinkRouteFromCopy(copy)}` },
name: 'root',
}"
>{{ getRouterLinkDisplayTextFromCopy(copy) }}</router-link >
}">{{ getRouterLinkDisplayTextFromCopy(copy) }}</router-link>
</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>
</div>
<siteFooter
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@backClicked="backButtonAction"
@forwardClicked="forwardButtonAction"
ref="siteFooter" />
@forwardClicked="forwardButtonAction" />
</div>
</div>
</Form>
@ -58,7 +65,6 @@
<script>
// Import Supporting Files
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { settleAllPromises } from '@/helpers/layout-helper';
import { useMainStore } from '@/store';
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 { isGlassAvailableForCarId } from '@/helpers/damage-helper';
import {
fetchCmsContentForPage,
doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
getRouterLinkRouteFromCopy,
getRouterLinkDisplayTextFromCopy
} from '@/helpers/cms-content-helper';
} from '@/helpers/cms-content-helper.js';
import { routerParams } from '@/router/router-constants/router-params';
import vinPagesMixin from '@/mixins/vin-pages-mixin';
// Import Component
import baseFormMixin from '@/mixins/base-form-mixin';
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import siteHeader from '@/iss-components/site-header/site-header.vue';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue';
import siteFooter from '@/iss-components/site-footer/site-footer';
import siteHeader from '@/iss-components/site-header/site-header';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
import alert from '@/ux-components/alert/alert';
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 {
name: 'address-vehicles',
components: {
siteFooter,
siteHeader,
siteSubHeader,
// eslint-disable-next-line vue/no-reserved-component-names
Form,
vehicleBanner,
alert,
addressVehiclesQuestion
},
mixins: [baseFormMixin, vinPagesMixin],
async beforeRouteEnter(to, from, next) {
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -131,15 +148,13 @@ export default {
return this.VehiclesForQuestions.length;
},
AlertFoundMultipleVehiclesHeader() {
return this.getCmsContent(
'FoundMultipleVehicles',
'HeadlineText'
).replaceAll('{custom:vehicleCount}', this.vehicleCount);
return this.getCmsContent('FoundMultipleVehicles',
'HeadlineText').replaceAll('{custom:vehicleCount}', this.vehicleCount);
},
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}`;
return (vinYmmFound.toLowerCase()==vinYmmExpected.toLowerCase());
return (vinYmmFound.toLowerCase() === vinYmmExpected.toLowerCase());
},
AlertProvideVinBody() {
return this.getCmsContent('ProvideVinAlert', 'BodyText');
@ -157,9 +172,9 @@ export default {
return {
vin: v.vin,
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,
SubText: 'VIN ' + vinStart + vinEnd
SubText: `VIN ${vinStart}${vinEnd}`
};
});
return mappedData;
@ -168,14 +183,37 @@ export default {
return useMainStore().pageData(issPageValues.ADDRESS_VEHICLES);
},
selectedVehicle() {
return this.VehiclesForQuestions.find(
({ vin }) => vin === this.selectedVehicleVin
);
return this.VehiclesForQuestions.find(({ vin }) => vin === this.selectedVehicleVin);
},
VehicleSelected() {
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: {
doesCopyContainRouterLink,
splitCopyOnCMSPlaceHolder,
@ -199,19 +237,15 @@ export default {
if (!vinLookup) {
return;
}
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(
vinLookup.data.carId
);
await useMainStore().saveVin(
{
vehicleInfo: Object.assign(this.selectedVehicle.vehicle, {
vin: this.selectedVehicle.vin
}),
isSelectedGlassAvailableForVehicle:
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(vinLookup.data.carId);
await useMainStore().saveVin({
vehicleInfo: Object.assign(this.selectedVehicle.vehicle, {
vin: this.selectedVehicle.vin
}),
isSelectedGlassAvailableForVehicle:
this.isSelectedGlassAvailableForVehicle
},
false
);
},
false);
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
// for that vehicle, then navigate back to "vehicle-damage" and display vehicle changed alert on that page.
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
this.$router.navigate(
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$router.navigate(this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$route,
{},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true }
);
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true });
} else {
await this.navigateForwardWithSingleCarMatch();
}
@ -234,49 +266,11 @@ export default {
this.displayMatchedTwoIdenticalYMMVehicleAlert = false;
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>
<style lang="scss">
<style lang="scss" scoped>
.alert-provide-vin {
font-size: 0.875rem;
line-height: 1.4;
@ -290,8 +284,8 @@ export default {
margin-bottom: 0 !important; // Overrides extra margin-bottom on alert body text
}
.overflow-scroll {
height: calc(100% - 180px);
.overflow-scroll {
height: calc(100% - 180px);
overflow-X: hidden !important;
}
</style>

View file

@ -1,17 +1,20 @@
<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="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget"/>
<siteHeader cmsWidgetName="SiteHeaderWidget" />
<div class="container-fluid pb-2">
<p>Placeholder for bailout page</p>
<siteFooter
cmsWidgetName="SiteFooterWidget"
ref="siteFooter"
cmsWidgetName="SiteFooterWidget"
:isForwardActionDisabled="!meta.valid"
@ForwardClicked="forwardButtonAction"
@back-clicked="backButtonAction"
/>
@back-clicked="backButtonAction" />
</div>
</div>
</div>
@ -30,15 +33,14 @@ import { useMainStore } from '@/store';
export default {
name: 'bailout-page',
components: {
siteHeader,
siteFooter,
// eslint-disable-next-line vue/no-reserved-component-names
Form
},
mixins: [BaseFormMixin],
data() {
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
async beforeRouteEnter(to, from, next)
{
async beforeRouteEnter(to, from, next) {
// Call APIs
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
// Settle promises and get results
@ -48,11 +50,17 @@ export default {
promise: cmsContentPromise
}];
// use resultMap to populate layout content.
let resultMap = await settleAllPromises(promiseResultMap);
const resultMap = await settleAllPromises(promiseResultMap);
next((vm) => {
vm.setCmsContent(resultMap.cmsContent);
});
},
setup() {
const mainStore = useMainStore();
return { mainStore };
},
data() {
},
methods:
{
backButtonAction() {
@ -62,13 +70,6 @@ export default {
},
navigateForward() {
}
},
components: {
siteHeader,
siteFooter,
Form
}
}
}
};
</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 { getMountOptions } from '@/helpers/unit-test-helper.js';
import { useMainStore } from '@/store';
import baseMixin from '../../mixins/base-mixin';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import { nextTick } from 'vue';
import baseMixin from '@/mixins/base-mixin';
// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => ({
@ -19,78 +19,134 @@ jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: jest.fn()
}));
const baseStoreGettersPageData = () => {
return {
partsOrQuestions: [
{
parts: [
{
childPartQuestions: []
}
],
capabilityQuestions: [
{
questionSequence: 1,
questionText:
const baseStoreGettersPageData = () => ({
partsOrQuestions: [
{
parts: [
{
childPartQuestions: []
}
],
capabilityQuestions: [
{
questionSequence: 1,
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?',
answers: [
{
answerResult1: 'DYNAMIC',
answerResult2: '1',
answerText: 'Yes',
nextQuestionSequence: null,
answerResult: 'DYNAMIC'
},
{
answerResult1: 'Unknown',
answerResult2: '0',
answerText: 'No',
nextQuestionSequence: null,
answerResult: 'Unknown'
}
]
}
],
partQuestions: [],
questions: [],
glassLocation: 'Windshield',
glassName: 'Single',
answerKey: 'Windshield-Single',
answerData: null
}
]
};
};
const baseStoreGettersDamage = () => {
return {
partsQuestionAnswers: [
{
glassLocation: 'Windshield',
glassName: 'Single',
result: 'FW04848',
answeredQuestions: [
{
questionText:
answers: [
{
answerResult1: 'DYNAMIC',
answerResult2: '1',
answerText: 'Yes',
nextQuestionSequence: null,
answerResult: 'DYNAMIC'
},
{
answerResult1: 'Unknown',
answerResult2: '0',
answerText: 'No',
nextQuestionSequence: null,
answerResult: 'Unknown'
}
]
}
],
partQuestions: [],
questions: [],
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:
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
}
]
}
]
};
};
selectedAnswer: '2|nextQuestion|3|Yes',
selectedAnswerText: 'Yes',
questionNum: 2
}
]
}
]
});
useMainStore().pageData = baseStoreGettersPageData;
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('method arePagePrerequisitesValid...', () => {
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', () => {
// Arrange
const { wrapper } = setupMocks({});
useMainStore().pageData = jest.fn(() => {
return undefined;
});
useMainStore().pageData = jest.fn(() => undefined);
// Act
const result = wrapper.vm.arePagePrerequisitesValid();
@ -124,11 +178,9 @@ describe('capabilityQuestions.vue', () => {
test('Should be at least one item in partsOrQuestions', () => {
// Arrange
useMainStore().pageData = jest.fn(() => {
return {
partsOrQuestions: []
};
});
useMainStore().pageData = jest.fn(() => ({
partsOrQuestions: []
}));
useMainStore().damage = baseStoreGettersDamage;
const { wrapper } = setupMocks({});
@ -207,11 +259,9 @@ describe('capabilityQuestions.vue', () => {
]
}
];
wrapper.vm.dispatchStoreAction = jest.fn(() => {
return {
data: []
};
});
wrapper.vm.dispatchStoreAction = jest.fn(() => ({
data: []
}));
// Act
wrapper.vm.forwardButtonAction();
@ -242,13 +292,11 @@ describe('capabilityQuestions.vue', () => {
]
}
];
useMainStore().getPartsOrQuestions = jest.fn(() => {
return {
data: {
partsOrQuestions: []
}
};
});
useMainStore().getPartsOrQuestions = jest.fn(() => ({
data: {
partsOrQuestions: []
}
}));
// Act
wrapper.vm.forwardButtonAction();
@ -279,11 +327,9 @@ describe('capabilityQuestions.vue', () => {
]
}
];
wrapper.vm.dispatchStoreAction = jest.fn(() => {
return {
data: []
};
});
wrapper.vm.dispatchStoreAction = jest.fn(() => ({
data: []
}));
// Act
wrapper.vm.forwardButtonAction();
@ -316,13 +362,11 @@ describe('capabilityQuestions.vue', () => {
]
}
];
useMainStore().getPartsOrQuestions = jest.fn(() => {
return {
data: {
partsOrQuestions: []
}
};
});
useMainStore().getPartsOrQuestions = jest.fn(() => ({
data: {
partsOrQuestions: []
}
}));
wrapper.vm.navigateForward = jest.fn();
// 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>
<Form
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit" ref="theForm"
ref="theForm"
v-slot="{ meta }"
>
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<questionsPageLayout
isRequired
ref="questionsPageLayout"
:isMetaValid="meta.valid"
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
:questionsData="questionsData"
:validationRules="rules.optionRequired"
v-model="selectedAnswers"
@forwardButtonAction="forwardButtonAction"
@back-click="navigateBack"
:index="currentGlassIndex" />
ref="questionsPageLayout"
v-model="selectedAnswers"
isRequired
:isMetaValid="meta.valid"
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
:questionsData="questionsData"
:validationRules="rules.optionRequired"
:index="currentGlassIndex"
@forwardButtonAction="forwardButtonAction"
@back-click="navigateBack" />
</Form>
</template>
<script>
@ -30,25 +30,16 @@ import { Form } from 'vee-validate';
import { useMainStore } from '@/store';
import globalRules from '@/constants/global-rules';
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 {
name: 'capability-questions',
mixins: [baseFormMixin, vehicleQuestionsMixin],
components: {
// eslint-disable-next-line vue/no-reserved-component-names
Form,
questionsPageLayout
},
data() {
return {
questionsData: [],
selectedAnswers: {},
currentGlassIndex: 0,
rules: {
optionRequired: globalRules.OPTION_REQUIRED
}
};
},
mixins: [baseFormMixin, vehicleQuestionsMixin],
async beforeRouteEnter(to, from, next) {
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -66,6 +57,16 @@ export default {
vm.setCmsContent(resultMap.cmsContent);
});
},
data() {
return {
questionsData: [],
selectedAnswers: {},
currentGlassIndex: 0,
rules: {
optionRequired: globalRules.OPTION_REQUIRED
}
};
},
computed: {
AlertFewMoreQuestionsHeader() {
return this.getCmsContent('AdditionalPartsQuestionsAlert', 'HeadlineText');
@ -85,9 +86,8 @@ export default {
arePagePrerequisitesValid() {
const capabilityQuestionsFromPageData = useMainStore().pageData(issPageValues.CAPABILITY_QUESTIONS);
return (
capabilityQuestionsFromPageData?.partsOrQuestions?.some((part) => part?.glassName) &&
capabilityQuestionsFromPageData?.partsOrQuestions?.some(
(part) => part?.capabilityQuestions?.length > 0)
capabilityQuestionsFromPageData?.partsOrQuestions?.some((part) => part?.glassName)
&& capabilityQuestionsFromPageData?.partsOrQuestions?.some((part) => part?.capabilityQuestions?.length > 0)
);
},
getInitialQuestionData() {
@ -98,24 +98,20 @@ export default {
.map((glass, index) => {
// NOTE: questions for property "questions" can differ between layouts
glass.questions = glass.capabilityQuestions;
glass.answerKey = glass.glassLocation + '-' + glass.glassName;
glass.answerKey = `${glass.glassLocation}-${glass.glassName}`;
// reset selectedAnswers for this glass
this.selectedAnswers[glass.answerKey] = [];
const updatedGlass = this.setupInitialData(
glass,
const updatedGlass = this.setupInitialData(glass,
index,
alreadyAnsweredQuestions
);
alreadyAnsweredQuestions);
// Set up watch for each set of glass questions
this.$watch(
'selectedAnswers.' + glass.answerKey,
this.$watch(`selectedAnswers.${glass.answerKey}`,
(newValue) => {
if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates(newValue, glass.answerKey);
}
},
{ deep: true }
);
{ deep: true });
return updatedGlass;
});
},
@ -124,9 +120,7 @@ export default {
// get answerResult2 of returned answer
let selectedAnswerResult2;
glass.questions.forEach((q) => {
const idx = q.answers.findIndex(
(a) => a.answerResult === glass.answerData.answerResult
);
const idx = q.answers.findIndex((a) => a.answerResult === glass.answerData.answerResult);
if (idx !== -1) {
selectedAnswerResult2 = q.answers[idx].answerResult2;
}
@ -149,13 +143,11 @@ export default {
await this.mainStore.saveCapabilityQuestionAnswers(questionAnswersArray);
// get parts from the capabilityQuestionAnswers
const partsOrQuestions = this.partsOrQuestionsData;
for (let answer of questionAnswersArray) {
partsOrQuestions.find((partOrQuestion) => {
return (
partOrQuestion.glassLocation === answer.glassLocation &&
partOrQuestion.glassName === answer.glassName
);
}).parts[0].childParts = [
for (const answer of questionAnswersArray) {
partsOrQuestions.find((partOrQuestion) => (
partOrQuestion.glassLocation === answer.glassLocation
&& partOrQuestion.glassName === answer.glassName
)).parts[0].childParts = [
{
partNumber: answer.partNum
}

View file

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

View file

@ -1,15 +1,15 @@
describe ('test', () => {
test ('dummy test', () => {
describe('test', () => {
test('dummy test', () => {
// Arrange
let test = "test";
let test = 'test';
// Act
test = "test2"
test = 'test2';
// Assert
expect(test.length).toBe(5);
})
})
});
});
// *Testing to be completed on SSR-603
@ -60,7 +60,7 @@ describe ('test', () => {
// test("Should return false for valid page requisites if vin is missing", () => {
// // Arrange
// const { wrapper } = setupMocks({});
// useMainStore().order.vehicle.vin = null;
// // Act
@ -222,9 +222,9 @@ describe ('test', () => {
// mountOptions.global = {
// plugins: [createTestingPinia()]
// }
// const wrapper = shallowMount(coverageStatement, mountOptions);
// wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
// return { wrapper };

View file

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

View file

@ -1,5 +1,19 @@
import { shallowMount, mount } from '@vue/test-utils';
import recalModal from '@/layouts/coverage-statement/recal-modal/recal-modal.vue';
import { mount } from '@vue/test-utils';
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', () => {
it('Should display header text when HeaderText is defined in the CMS', async () => {
@ -11,7 +25,7 @@ describe('modal.vue', () => {
},
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 () => {
@ -23,7 +37,7 @@ describe('modal.vue', () => {
},
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 () => {
@ -35,7 +49,7 @@ describe('modal.vue', () => {
},
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 () => {
@ -47,22 +61,6 @@ describe('modal.vue', () => {
},
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"
:modalId="ModalName"
:footerButtonText="ModalCloseButtonText"
@footer-button-event="closeModal"
>
@footer-button-event="closeModal">
<div class="recal-modal-body ps-4 pe-4 pt-0 pb-5">
<h5 class="mb-4 text-center" v-html="ModalHeadline"></h5>
<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>
<h5
class="mb-4 text-center"
v-html="ModalHeadline"></h5>
<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
class="my-4 caption modal-sub-body"
v-if="ModalSubBodyText"
class="my-4 caption modal-sub-body"
v-html="ModalSubBodyText">
</p>
</div>
</modal>
</template>
<script>
@ -25,17 +32,12 @@ import modal from '@/digital-components/modal/modal';
export default {
name: 'recal-modal',
components: {
modal
},
props: {
cmsWidgetName: String
},
methods: {
openModal() {
this.$refs[this.ModalName].openModal();
},
closeModal() {
this.$refs[this.ModalName].closeModal();
}
},
computed: {
ModalName() {
return this.cmsWidgetName;
@ -59,13 +61,18 @@ export default {
return this.getCmsContent(this.cmsWidgetName, 'FooterText');
}
},
components: {
modal
methods: {
openModal() {
this.$refs[this.ModalName].openModal();
},
closeModal() {
this.$refs[this.ModalName].closeModal();
}
}
};
</script>
<style lang="scss">
<style lang="scss" scoped>
.recal-modal-body {
.modal-sub-body {
@ -83,6 +90,5 @@ export default {
color: $black;
}
}
</style>
</style>

View file

@ -1,11 +1,10 @@
// 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 { 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 { useMainStore } from '@/store';
// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => ({
@ -18,16 +17,6 @@ jest.mock('@/helpers/cms-content-helper', () => ({
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) {
const mountOptions = getMountOptions({
router: {
@ -36,15 +25,23 @@ function setupMocks(queryString) {
route: { queryString }
});
const wrapper = shallowMount(
entryPage,
mountOptions
);
const wrapper = shallowMount(entryPage,
mountOptions);
const apiResponses = {};
settleAllPromises.mockImplementation(() => apiResponses);
fetchCmsContentForPage.mockImplementation (() => {});
fetchCmsContentForPage.mockImplementation(() => { });
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>
<p id="message" v-show="unauthorized">Unauthorized Access.</p>
<p
v-show="unauthorized"
id="message">
Unauthorized Access.
</p>
</template>
<script>
@ -10,40 +14,42 @@ import { useMainStore } from '@/store';
export default {
name: 'entry-page',
mixins: [],
data() {
return {
unauthorized: false
}
components: {
},
mixins: [],
setup() {
const mainStore = useMainStore();
mainStore.resetISSConfigState();
mainStore.resetPageFields();
return { mainStore };
},
data() {
return {
unauthorized: false
};
},
computed: {
},
mounted() {
this.validateClientTagOnEntry();
},
methods:
{
navigateForward() {
this.$router.navigate(
this.navigationScenarios.MOVE_FORWARD_ENTRY_PAGE,
this.$route
);
this.$router.navigate(this.navigationScenarios.MOVE_FORWARD_ENTRY_PAGE,
this.$route);
},
parseQueryParms() {
// Dump the query string parameters into an array. Remove casing on the key for easy compare.
let queryStringParams = [];
for (let param in this.$route.query) {
const queryStringParams = [];
for (const param in this.$route.query) {
queryStringParams[param.toLowerCase()] = this.$route.query[param];
}
return queryStringParams;
},
async validateClientTagOnEntry() {
const queryStringParams = this.parseQueryParms();
const clientTag = queryStringParams['clienttag'];
const clientTag = queryStringParams.clienttag;
const clientTagPresent = !!clientTag;
let authorized = false;
@ -66,7 +72,7 @@ export default {
if (authorized) {
// 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) {
@ -76,9 +82,9 @@ export default {
this.mainStore.issConfig.styleSheet = data.styleSheet;
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.
if (data.authentication == 'RSAToken')
this.mainStore.issConfig.isAuthenticated = true;
// NOTE: This is only here for testing purposes.
// Will be removed and replaced by actual token / signature validation when work is completed.
if (data.authentication === 'RSAToken') this.mainStore.issConfig.isAuthenticated = true;
try {
if (data.clientFlags) {
@ -97,20 +103,20 @@ export default {
}
}
} catch (e) {
console.error('Error parsing client flags: ' + e);
console.error(`Error parsing client flags: ${e}`);
}
},
combineClientParameters(configParams, queryStringParams) {
let finalParams = [];
const finalParams = [];
try {
const clientParams = JSON.parse(configParams);
for (let cparam in clientParams) {
let cname = clientParams[cparam].toLowerCase();
for (const cparam in clientParams) {
const cname = clientParams[cparam].toLowerCase();
for (let qsparam in queryStringParams) {
let qsname = qsparam.toLowerCase();
for (const qsparam in queryStringParams) {
const qsname = qsparam.toLowerCase();
if (cname === qsname) {
finalParams[cname] = queryStringParams[cname];
@ -118,16 +124,16 @@ export default {
}
}
} catch (e) {
console.error('Error combining client parameters: ' + e);
console.error(`Error combining client parameters: ${e}`);
}
return finalParams;
},
populateStoreItemsFromParams(params) {
// Populate store items from parameters.
for (let param in params) {
let name = param.toLowerCase();
let value = params[param];
for (const param in params) {
const name = param.toLowerCase();
const value = params[param];
switch (name) {
case 'policynumber':
@ -158,18 +164,15 @@ export default {
case 'token':
case 'signature':
break;
default:
}
}
}
},
computed: {
},
components: {
}
}
}
};
</script>
<style lang="scss">
<style lang="scss" scoped>
#message {
text-align: center;
}

View file

@ -1,5 +1,5 @@
// Components
import licensePlateLookup from '@/layouts/license-plate-lookup/license-plate-lookup.vue';
import licensePlateLookup from '@/layouts/license-plate-lookup/license-plate-lookup';
// Supporting Files
import { settleAllPromises } from '@/helpers/layout-helper.js';
@ -23,6 +23,83 @@ jest.mock('@/helpers/cms-content-helper', () => ({
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('page level alerts', () => {
test('If license plate matches a different vehicle, display MatchedDifferentVehicleAlert', async () => {
@ -43,9 +120,7 @@ describe('license-plate-lookup.vue', () => {
await wrapper.vm.forwardButtonAction();
// Assert
expect(wrapper.findComponent({ ref: 'alertMatchedDifferentVehicle' }).isVisible()).toBe(
true
);
expect(wrapper.findComponent({ ref: 'alertMatchedDifferentVehicle' }).isVisible()).toBe(true);
});
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();
// Assert
expect(wrapper.findComponent({ ref: 'alertMatchedTwoIdenticalYMMVehicle' }).isVisible()).toBe(
true
);
expect(wrapper.findComponent({ ref: 'alertMatchedTwoIdenticalYMMVehicle' }).isVisible()).toBe(true);
});
test('If no vehicles found, display VinNotFound alert', async () => {
@ -101,7 +174,7 @@ describe('license-plate-lookup.vue', () => {
describe('navigation', () => {
test('Clicking back button navigates back', async () => {
// Arrange
const { wrapper, apiPromise } = setupMocks({
const { wrapper } = setupMocks({
mountOptionsMockData: {
router: {
navigate: jest.fn()
@ -109,12 +182,10 @@ describe('license-plate-lookup.vue', () => {
}
});
// Act
licensePlateLookup.beforeRouteEnter.call(
wrapper.vm,
licensePlateLookup.beforeRouteEnter.call(wrapper.vm,
{ query: { issPage: 'license-plate-lookup' } },
undefined,
(c) => c(wrapper.vm)
);
(c) => c(wrapper.vm));
await wrapper.vm.backButtonAction();
// Assert
@ -155,7 +226,7 @@ describe('license-plate-lookup.vue', () => {
useMainStore().order.vehicle.carId = 'CARID';
let carsFound = [
const carsFound = [
{
vin: 'TEST_VIN2',
vehicle: {
@ -168,12 +239,10 @@ describe('license-plate-lookup.vue', () => {
await wrapper.vm.navigateForward(carsFound);
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
undefined,
{},
{ displayVehicleChangeAlert: true }
);
{ displayVehicleChangeAlert: true });
});
});
@ -184,103 +253,15 @@ describe('license-plate-lookup.vue', () => {
useMainStore().order.vehicle.carId = 'CR00000395';
// Act
licensePlateLookup.beforeRouteEnter.call(
wrapper.vm,
licensePlateLookup.beforeRouteEnter.call(wrapper.vm,
{ query: { issPage: 'license-plate-lookup' } },
undefined,
(c) => c(wrapper.vm)
);
(c) => c(wrapper.vm));
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
const arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
// Assert
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>
<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="fade-on-route-transition position-relative">
<siteHeader cmsWidgetName="SiteHeaderWidget" />
@ -8,57 +12,60 @@
<div class="row px-3">
<div class="col">
<div class="select-car-form rounded">
<vehicleBanner class="mt-2 mb-4" cms-widget-name="VehicleBannerWidget" :display-generic-vehicle-image="false" />
<siteSubHeader cms-widget-name="SiteSubHeaderWidget" />
<vehicleBanner
class="mt-2 mb-4"
cmsWidgetName="VehicleBannerWidget"
:displayGenericVehicleImage="false" />
<siteSubHeader cmsWidgetName="SiteSubHeaderWidget" />
<alert
ref="alertVinNotFound"
v-if="displayVinNotFoundAlert"
class="mb-4"
cmsWidgetName="AlertVinNotFoundWidget"
alertClass="alert-danger"
v-bind:isDismissible="false" />
v-if="displayVinNotFoundAlert"
ref="alertVinNotFound"
class="mb-4"
cmsWidgetName="AlertVinNotFoundWidget"
alertClass="alert-danger"
:isDismissible="false" />
<alert
ref="alertMatchedDifferentVehicle"
v-if="displayMatchedDifferentVehicleAlert"
class="mb-4"
cmsWidgetName="AlertMatchedDifferentVehicleWidget"
:manualHeadline="AlertMatchedDifferentVehicleHeader"
:manualCopy="AlertMatchedDifferentVehicleBody"
alertClass="alert-warning"
v-bind:isDismissible="false" />
v-if="displayMatchedDifferentVehicleAlert"
ref="alertMatchedDifferentVehicle"
class="mb-4"
cmsWidgetName="AlertMatchedDifferentVehicleWidget"
:manualHeadline="AlertMatchedDifferentVehicleHeader"
:manualCopy="AlertMatchedDifferentVehicleBody"
alertClass="alert-warning"
:isDismissible="false" />
<alert
ref="alertMatchedTwoIdenticalYMMVehicle"
v-if="displayMatchedTwoIdenticalYMMVehicleAlert"
class="mb-4"
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader"
:manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody"
alertClass="alert-warning"
v-bind:isDismissible="false" />
v-if="displayMatchedTwoIdenticalYMMVehicleAlert"
ref="alertMatchedTwoIdenticalYMMVehicle"
class="mb-4"
cmsWidgetName="AlertMatchedTwoIdenticalYMMVehicleWidget"
:manualHeadline="AlertMatchedTwoIdenticalYMMVehicleHeader"
:manualCopy="AlertMatchedTwoIdenticalYMMVehicleBody"
alertClass="alert-warning"
:isDismissible="false" />
<textboxQuestion
cmsWidgetName="LicensePlateNumberQuestionWidget"
v-model="licensePlate"
isRequired
disableAutoFill
id="license-plate-question-wrapper"
inputId="license-plate-question"
validationRules="license-plate-required" />
id="license-plate-question-wrapper"
v-model="licensePlate"
cmsWidgetName="LicensePlateNumberQuestionWidget"
isRequired
disableAutoFill
inputId="license-plate-question"
validationRules="license-plate-required" />
<dropdownQuestion
cmsWidgetName="StateQuestionWidget"
v-model="licenseState"
ref="state"
inputId="8fdf9dc2e13e430eb57529499dceb3eb"
:options="stateOptions"
disableAutoFill
validationRules="state-required"
class="mt-4 mb-2"/>
ref="state"
v-model="licenseState"
cmsWidgetName="StateQuestionWidget"
inputId="8fdf9dc2e13e430eb57529499dceb3eb"
:options="stateOptions"
disableAutoFill
validationRules="state-required"
class="mt-4 mb-2" />
<siteFooter
class="mt-5"
:isForwardActionDisabled="!meta.valid"
cms-widget-name="SiteFooterWidget"
@back-clicked="backButtonAction"
@forward-clicked="forwardButtonAction"
ref="siteFooter" />
ref="siteFooter"
class="mt-5"
:isForwardActionDisabled="!meta.valid"
cmsWidgetName="SiteFooterWidget"
@back-clicked="backButtonAction"
@forward-clicked="forwardButtonAction" />
</div>
</div>
</div>
@ -75,21 +82,21 @@ import { settleAllPromises } from '@/helpers/layout-helper';
import { useMainStore } from '@/store';
import { errorMessages } from '@/constants/error-messages';
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 { routerParams } from '@/router/router-params.js'
import { routerParams } from '@/router/router-params.js';
import { states } from '@/constants/states';
// Import Component
import baseFormMixin from '@/mixins/base-form-mixin';
import vinPagesMixin from '@/mixins/vin-pages-mixin';
import { Form } from 'vee-validate';
import siteFooter from '@/iss-components/site-footer/site-footer.vue';
import siteHeader from '@/iss-components/site-header/site-header.vue';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header.vue';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner.vue';
import textboxQuestion from '@/digital-components/textbox-question/textbox-question.vue';
import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question.vue';
import siteFooter from '@/iss-components/site-footer/site-footer';
import siteHeader from '@/iss-components/site-header/site-header';
import siteSubHeader from '@/iss-components/site-sub-header/site-sub-header';
import vehicleBanner from '@/iss-components/vehicle-banner/vehicle-banner';
import textboxQuestion from '@/digital-components/textbox-question/textbox-question';
import dropdownQuestion from '@/digital-components/dropdown-question/dropdown-question';
import alert from '@/ux-components/alert/alert';
// Define Validation Rules
@ -98,6 +105,17 @@ defineRule('state-required', required(errorMessages.STATE_REQUIRED));
export default {
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],
async beforeRouteEnter(to, from, next) {
// Call APIs
@ -134,6 +152,56 @@ export default {
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: {
arePagePrerequisitesValid() {
return this.mainStore.order.vehicle.carId !== null;
@ -147,12 +215,10 @@ export default {
},
attachCustomEvents() {
this.prependActionToMethod(this, this.forwardButtonAction, () => {
this.pushEventToGA(
this.$route.query[this.queryStrings.ISS_PAGE],
this.pushEventToGA(this.$route.query[this.queryStrings.ISS_PAGE],
this.GaActions.SUBMITTED,
this.GaLabels.LICENSE_PLATE_LOOKUP,
true
);
true);
});
},
// NOTE: If form is not valid, this method is not called when 'Continue' button is clicked
@ -160,8 +226,7 @@ export default {
this.resetWarningsAndErrors();
// Lookup VIN
const vinLookupResponse = await useMainStore().lookupVinByPlate(
this.licensePlate, this.licenseState);
const vinLookupResponse = await useMainStore().lookupVinByPlate(this.licensePlate, this.licenseState);
// Settle promises and get results
const promiseResultMap = [
@ -178,33 +243,31 @@ export default {
this.displayVinNotFoundAlert = true;
this.$refs.siteFooter.disableForwardButton();
return this.$refs.siteFooter.removeLoader();
};
}
// Vehicle found from VIN lookup
const vehicleFromLookup = resultMap.vinLookupResponse.vehicle;
// Check if the CarId has changed
this.isCarIdDifferent =
vehicleFromLookup.carId !== useMainStore().order.vehicle.carId;
this.isCarIdDifferent
= vehicleFromLookup.carId !== useMainStore().order.vehicle.carId;
// Handle changing car
if (
this.isCarIdDifferent &&
vehicleFromLookup.carId !== this.previouslyEnteredCarId
this.isCarIdDifferent
&& vehicleFromLookup.carId !== this.previouslyEnteredCarId
) {
// Display Alert
this.previouslyEnteredCarId = vehicleFromLookup.carId;
this.customAlertData.vehicleInfo = vehicleFromLookup;
if (this.isTwoIdenticalYMMVehicleFound){
if (this.isTwoIdenticalYMMVehicleFound) {
this.displayMatchedTwoIdenticalYMMVehicleAlert = true;
this.forwardButtonCarStyle= vehicleFromLookup.style;
this.forwardButtonCarStyle = vehicleFromLookup.style;
} else {
this.displayMatchedDifferentVehicleAlert = true;
}
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(
vehicleFromLookup.carId
);
this.isSelectedGlassAvailableForVehicle = await isGlassAvailableForCarId(vehicleFromLookup.carId);
// Update button "Continue with..."
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
await useMainStore().saveRegistrationLicensePlateLookup(
{
isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle,
vehicleInfo: Object.assign(vinLookupResponse.data.vehicle, { vin: vinLookupResponse.data.vin }),
registrationInfo: {
licensePlate: this.licensePlate,
state: this.licenseState
}
},
false
);
await useMainStore().saveRegistrationLicensePlateLookup({
isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle,
vehicleInfo: Object.assign(vinLookupResponse.data.vehicle, { vin: vinLookupResponse.data.vin }),
registrationInfo: {
licensePlate: this.licensePlate,
state: this.licenseState
}
},
false);
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"
// display vehicle changed alert on that page.
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
this.$router.navigate(
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$router.navigate(this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$route,
{},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true }
);
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true });
} else {
await this.navigateForwardWithSingleCarMatch();
}
@ -244,79 +303,11 @@ export default {
this.displayVinNotFoundAlert = 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>
<style lang="scss">
<style lang="scss" scoped>
#license-plate-question-wrapper .form-test-error {
/**
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 { getMountOptions } from '@/helpers/unit-test-helper.js';
import { useMainStore } from '@/store';
import baseMixin from '../../mixins/base-mixin';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import { nextTick } from 'vue';
import baseMixin from '@/mixins/base-mixin';
// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => ({
@ -19,79 +19,136 @@ jest.mock('@/helpers/cms-content-helper', () => ({
fetchCmsContentForPage: jest.fn()
}));
const baseStoreGettersPageData = () => {
return {
partsOrQuestions: [
function setupMocks({
mountOptionsMockData = {
router: {
navigate: jest.fn()
},
actionList: [
{
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
actionName: 'saveMoldingQuestionAnswers',
data: {}
},
{
actionName: 'getPartsOrQuestions',
data: {}
}
]
};
};
const baseStoreGettersDamage = () => {
return {
partsQuestionAnswers: [
{
glassLocation: 'Windshield',
glassName: 'Single',
result: 'FW04848',
answeredQuestions: [
],
route: {
query: {
issPage: 'molding-questions'
}
},
data() {
return {
computedSwitcher: [
{
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
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(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().damage = baseStoreGettersDamage;
@ -114,9 +171,7 @@ describe('moldingQuestions.vue', () => {
test('Should return false for valid page requisites if partsOrQuestions in pageData is missing', () => {
// Arrange
const { wrapper } = setupMocks({});
useMainStore().pageData = jest.fn(() => {
return undefined;
});
useMainStore().pageData = jest.fn(() => undefined);
// Act
const result = wrapper.vm.arePagePrerequisitesValid();
@ -129,11 +184,9 @@ describe('moldingQuestions.vue', () => {
test('Should be at least one item in partsOrQuestions', () => {
// Arrange
useMainStore().pageData = jest.fn(() => {
return {
partsOrQuestions: []
};
});
useMainStore().pageData = jest.fn(() => ({
partsOrQuestions: []
}));
useMainStore().damage = baseStoreGettersDamage;
const { wrapper } = setupMocks({});
@ -207,13 +260,11 @@ describe('moldingQuestions.vue', () => {
}
}
];
wrapper.vm.dispatchStoreAction = jest.fn(() => {
return {
data: {
partsOrQuestions: []
}
};
});
wrapper.vm.dispatchStoreAction = jest.fn(() => ({
data: {
partsOrQuestions: []
}
}));
// Act
wrapper.vm.forwardButtonAction();
@ -238,13 +289,11 @@ describe('moldingQuestions.vue', () => {
}
}
];
useMainStore().getPartsOrQuestions = jest.fn(() => {
return {
data: {
partsOrQuestions: []
}
};
});
useMainStore().getPartsOrQuestions = jest.fn(() => ({
data: {
partsOrQuestions: []
}
}));
// Act
wrapper.vm.forwardButtonAction();
@ -270,13 +319,11 @@ describe('moldingQuestions.vue', () => {
}
}
];
useMainStore().getPartsOrQuestions = jest.fn(() => {
return {
data: {
partsOrQuestions: []
}
};
});
useMainStore().getPartsOrQuestions = jest.fn(() => ({
data: {
partsOrQuestions: []
}
}));
// Act
wrapper.vm.forwardButtonAction();
@ -303,13 +350,11 @@ describe('moldingQuestions.vue', () => {
}
}
];
useMainStore().getPartsOrQuestions = jest.fn(() => {
return {
data: {
partsOrQuestions: []
}
};
});
useMainStore().getPartsOrQuestions = jest.fn(() => ({
data: {
partsOrQuestions: []
}
}));
wrapper.vm.navigateForward = jest.fn();
// 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>
<Form
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit"
ref="theForm"
v-slot="{ meta }"
>
@submit="onSubmit"
@invalidSubmit="onInvalidSubmit">
<questionsPageLayout
isRequired
id="molding-question-wrapper"
ref="questionsPageLayout"
v-model="selectedAnswers"
isRequired
:isMetaValid="meta.valid"
:alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
:alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
:questionsData="questionsData"
:validationRules="rules.optionRequired"
v-model="selectedAnswers"
@forwardButtonAction="forwardButtonAction"
@back-click="navigateBack"
:index="currentGlassIndex"
id="molding-question-wrapper"
/>
@forwardButtonAction="forwardButtonAction"
@back-click="navigateBack" />
</Form>
</template>
<script>
@ -33,10 +31,15 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js';
// Import Component
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 {
name: 'molding-questions',
components: {
// eslint-disable-next-line vue/no-reserved-component-names
Form,
questionsPageLayout
},
mixins: [BaseFormMixin, vehicleQuestionsMixin],
async beforeRouteEnter(to, from, next) {
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
@ -67,10 +70,8 @@ export default {
},
computed: {
AlertFewMoreQuestionsHeader() {
return this.getCmsContent(
'AdditionalPartsQuestionsAlert',
'HeadlineText'
);
return this.getCmsContent('AdditionalPartsQuestionsAlert',
'HeadlineText');
},
AlertFewMoreQuestionsCopy() {
return this.getCmsContent('AdditionalPartsQuestionsAlert', 'BodyText');
@ -85,62 +86,48 @@ export default {
},
methods: {
arePagePrerequisitesValid() {
const moldingQuestionsFromPageData = useMainStore().pageData(
issPageValues.MOLDING_QUESTIONS
);
const moldingQuestionsFromPageData = useMainStore().pageData(issPageValues.MOLDING_QUESTIONS);
return (
// has childPartQuestions array and has glassName not null
moldingQuestionsFromPageData?.partsOrQuestions?.some(
(part) => part?.glassName
) &&
moldingQuestionsFromPageData.partsOrQuestions.some((glass) =>
glass.parts?.some((part) => part?.childPartQuestions?.length > 0)
)
moldingQuestionsFromPageData?.partsOrQuestions?.some((part) => part?.glassName)
&& moldingQuestionsFromPageData.partsOrQuestions.some((glass) => glass.parts?.some((part) => part?.childPartQuestions?.length > 0))
);
},
getInitialQuestionData() {
// get any questions that were already answered
const alreadyAnsweredQuestions =
useMainStore().damage.moldingQuestionAnswers;
const alreadyAnsweredQuestions
= useMainStore().damage.moldingQuestionAnswers;
this.questionsData = this.partsOrQuestionsData
.filter((x) => x.parts[0].childPartQuestions.length)
.map((glass, index) => {
// NOTE: questions for property "questions" can differ between layouts
glass.questions = glass.parts[0].childPartQuestions;
glass.answerKey = glass.glassLocation + '-' + glass.glassName;
glass.answerKey = `${glass.glassLocation}-${glass.glassName}`;
// reset selectedAnswers for this glass
this.selectedAnswers[glass.answerKey] = [];
const updatedGlass = this.setupInitialData(
glass,
const updatedGlass = this.setupInitialData(glass,
index,
alreadyAnsweredQuestions
);
alreadyAnsweredQuestions);
// Set up watch for each set of glass questions
this.$watch(
'selectedAnswers.' + glass.answerKey,
this.$watch(`selectedAnswers.${glass.answerKey}`,
(newValue) => {
if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates(
newValue,
glass.answerKey
);
this.handleAnswerUpdates(newValue,
glass.answerKey);
}
},
{ deep: true }
);
{ deep: true });
return updatedGlass;
});
},
async forwardButtonAction() {
const questionAnswersArray = this.questionsData.map((glass) => {
return {
glassLocation: glass.glassLocation,
glassName: glass.glassName,
partNum: glass.answerData.answerResult,
answeredQuestions: glass.answerData.answeredQuestions,
isSuppressedPart: glass.isSuppressedPart
};
});
const questionAnswersArray = this.questionsData.map((glass) => ({
glassLocation: glass.glassLocation,
glassName: glass.glassName,
partNum: glass.answerData.answerResult,
answeredQuestions: glass.answerData.answeredQuestions,
isSuppressedPart: glass.isSuppressedPart
}));
// clear out answerData for future page loads; must occur prior to store save
this.questionsData.forEach((glass) => {
glass.answerData = {};
@ -149,14 +136,12 @@ export default {
await this.mainStore.saveMoldingQuestionAnswers(questionAnswersArray);
// get parts from the questionAnswers
let partsOrQuestions = this.partsOrQuestionsData;
for (let answer of questionAnswersArray) {
partsOrQuestions.find((partOrQuestion) => {
return (
partOrQuestion.glassLocation === answer.glassLocation &&
partOrQuestion.glassName === answer.glassName
);
}).parts[0].childParts = [
const partsOrQuestions = this.partsOrQuestionsData;
for (const answer of questionAnswersArray) {
partsOrQuestions.find((partOrQuestion) => (
partOrQuestion.glassLocation === answer.glassLocation
&& partOrQuestion.glassName === answer.glassName
)).parts[0].childParts = [
{
partNumber: answer.partNum
}
@ -165,15 +150,11 @@ export default {
this.navigateForward(partsOrQuestions, null);
}
},
components: {
Form,
questionsPageLayout
}
};
</script>
<style lang="scss">
<style lang="scss" scoped>
#molding-question-wrapper p.text-body.small {
margin-bottom: 0 !important; // Overrides extra margin-bottom on alert body text
}

View file

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