Merge branch 'develop' into feature/digital/SSR-179

This commit is contained in:
Kroell 2023-01-19 12:29:37 -05:00
commit a59440d823
15 changed files with 769 additions and 423 deletions

View file

@ -1,6 +1,5 @@
// Components // Components
import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions"; import addressQuestions from "@/common-components/address-questions/address-questions";
import alert from "@/ux-components/alert/alert";
// Supporting Files // Supporting Files
import { mount, shallowMount } from "@vue/test-utils"; import { mount, shallowMount } from "@vue/test-utils";

View file

@ -7,7 +7,6 @@ import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js"; import { getMountOptions } from "@/helpers/unit-test-helper.js";
import { useMainStore } from "@/store"; import { useMainStore } from "@/store";
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios"; import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
import e from "express";
jest.mock("@/helpers/damage-helper", () => ({ jest.mock("@/helpers/damage-helper", () => ({
isGlassAvailableForCarId: jest.fn().mockImplementation(() => true), isGlassAvailableForCarId: jest.fn().mockImplementation(() => true),
@ -21,48 +20,6 @@ jest.mock("@/helpers/layout-helper.js", () => ({
describe("address-lookup.vue", () => { describe("address-lookup.vue", () => {
describe("page level alerts", () => { describe("page level alerts", () => {
test("if the address is not serviceable display the Non-Serviceable Zip Alert", async () => {
// Arrange
const mockRegistrationAddress = {
streetAddress: "1234 Main St",
city: "Columbus",
state: "OH",
zipCode: "43215",
};
const { wrapper } = setupMocks({
isZipValid: true,
isZipServiceable: false,
vinVehicles: [
{
vin: "TEST_VIN",
vehicle: {
carId: "C0000",
},
},
{
vin: "TEST_VIN",
vehicle: {
carId: "C0000",
},
},
],
});
await wrapper.setData({
customerQuestions: {
addressQuestions: mockRegistrationAddress,
},
});
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe(true);
});
test("if the address matches a different vehicle display the Matched Different VehicleAlert", async () => { test("if the address matches a different vehicle display the Matched Different VehicleAlert", async () => {
// Arrange // Arrange
const mockRegistrationAddress = { const mockRegistrationAddress = {
@ -73,7 +30,6 @@ describe("address-lookup.vue", () => {
}; };
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
isZipServiceable: true,
vinVehicles: [ vinVehicles: [
{ {
vehicle: { vehicle: {
@ -110,7 +66,6 @@ describe("address-lookup.vue", () => {
}; };
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
isZipServiceable: true,
isStatePermissible: false, isStatePermissible: false,
lookupVinbyAddressResponse: { lookupVinbyAddressResponse: {
isStatePermissible: false, isStatePermissible: false,
@ -158,7 +113,6 @@ describe("address-lookup.vue", () => {
}; };
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
isZipServiceable: true,
lookupVinbyAddressResponse: { lookupVinbyAddressResponse: {
isStatePermissible: true, isStatePermissible: true,
vinVehicles: [], // Return no vehicles vinVehicles: [], // Return no vehicles
@ -188,7 +142,6 @@ describe("address-lookup.vue", () => {
test("if the back button is clicked, navigate back", async () => { test("if the back button is clicked, navigate back", async () => {
// Arrange // Arrange
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
isZipServiceable: true,
}); });
// Act // Act
@ -198,7 +151,7 @@ describe("address-lookup.vue", () => {
expect(wrapper.vm.$router.navigate).toHaveBeenCalled(); expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
}); });
test("if the car entered matches one of the vehicles found and the zip is serviceable, navigate forward", async () => { test("if the car entered matches one of the vehicles found navigate forward", async () => {
// Arrange // Arrange
const mockRegistrationAddress = { const mockRegistrationAddress = {
streetAddress: "1234 Main St", streetAddress: "1234 Main St",
@ -208,7 +161,6 @@ describe("address-lookup.vue", () => {
}; };
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
isZipServiceable: true,
vinVehicles: [ vinVehicles: [
{ {
vehicle: { vehicle: {
@ -244,7 +196,6 @@ describe("address-lookup.vue", () => {
}; };
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
isZipServiceable: true,
isStatePermissible: true, isStatePermissible: true,
vinVehicles: [ vinVehicles: [
{ {
@ -299,52 +250,7 @@ describe("address-lookup.vue", () => {
carsFound carsFound
); );
}); });
test("if the car entered matches one of the vehicles found but the zip is NOT serviceable, do not navigate forward", async () => {
// Arrange
const mockRegistrationAddress = {
streetAddress: "1234 Main St",
city: "Columbus",
state: "OH",
zipCode: "43215",
};
const { wrapper } = setupMocks({
isZipServiceable: false,
isStatePermissible: true,
vinVehicles: [
{
vin: "TEST_VIN",
vehicle: {
carId: "CARID",
},
},
{
vin: "TEST_VIN2",
vehicle: {
carId: "CARID2",
},
},
],
});
useMainStore().order.vehicle.carId = "CARID";
await wrapper.setData({
customerQuestions: {
addressQuestions: mockRegistrationAddress,
},
});
wrapper.vm.navigateForward = jest.fn();
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(wrapper.vm.navigateForward).toHaveBeenCalledTimes(0);
});
test("if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page", async () => { test("if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page", async () => {
// Arrange // Arrange
const mockRegistrationAddress = { const mockRegistrationAddress = {
@ -355,7 +261,6 @@ describe("address-lookup.vue", () => {
}; };
const { wrapper } = setupMocks({ const { wrapper } = setupMocks({
isZipServiceable: true,
isStatePermissible: true, isStatePermissible: true,
}); });
@ -450,135 +355,9 @@ describe("address-lookup.vue", () => {
}); });
}); });
describe("registration and service zips", () => {
describe("if registration zip is serviceable", () => {
test("if registration address is provided => update service address on successful continue", async () => {
// Arrange
const mockRegistrationAddress = {
streetAddress: "1234 Main St",
city: "Columbus",
state: "OH",
zipCode: "43215",
};
const { wrapper } = setupMocks({
isZipServiceable: true,
isStatePermissible: true,
vinVehicles: [
{
vin: "TEST_VIN",
vehicle: {
carId: "CARID",
},
},
],
route: { query: "address-lookup" },
});
useMainStore().order.vehicle.carId = "CARID";
useMainStore().saveRegistrationAddressLookup = jest.fn();
await wrapper.setData({
customerQuestions: {
addressQuestions: mockRegistrationAddress,
},
});
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(useMainStore().saveRegistrationAddressLookup).toHaveBeenCalled();
expect(useMainStore().validateZip).toHaveBeenCalledWith({ zip: "43215" });
});
});
describe("if registration zip is not serviceable", () => {
test("if registration address is provided and user clicks continue => show non-serviceable zip alert", async () => {
// Arrange
const mockRegistrationAddress = {
streetAddress: "1234 Main St",
city: "Columbus",
state: "OH",
zipCode: "43215",
};
const { wrapper } = setupMocks({
isZipValid: true,
isZipServiceable: false,
vinVehicles: [
{
vin: "TEST_VIN",
vehicle: {
carId: "C0000",
},
},
],
});
useMainStore().order.vehicle.carId = "C0000";
await wrapper.setData({
customerQuestions: {
addressQuestions: mockRegistrationAddress,
},
});
expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).exists()).toBe(
false
);
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(wrapper.vm.displayNonServiceableZipAlert).toBe(true);
expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).exists()).toBe(
true
);
expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe(
true
);
});
test("if registration address, service zip are provided, and user clicks continue => don't update service address", async () => {
// Arrange
const mockRegistrationAddress = {
streetAddress: "1234 Main St",
city: "Columbus",
state: "OH",
zipCode: "43215",
};
const { wrapper } = setupMocks({
isZipServiceable: false,
});
useMainStore().order.vehicle.carId = "CARID";
await wrapper.setData({
customerQuestions: {
addressQuestions: mockRegistrationAddress,
},
});
useMainStore().saveRegistrationAddressLookup = jest.fn();
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(useMainStore().saveRegistrationAddressLookup).not.toHaveBeenCalled();
});
});
});
}); });
function setupMocks({ function setupMocks({
isZipValid = true,
isZipServiceable = true,
lookupVinbyAddressResponse, lookupVinbyAddressResponse,
partsOrQuestions = [], partsOrQuestions = [],
isStatePermissible = true, isStatePermissible = true,
@ -587,16 +366,6 @@ function setupMocks({
route = null, route = null,
}) })
{ {
useMainStore().validateZip = jest.fn().mockImplementation(() => {
return Promise.resolve({
data: {
isValid: isZipValid,
isServiceable: isZipServiceable,
},
})
});
useMainStore().lookupVinByAddress = jest.fn().mockImplementation(() => { useMainStore().lookupVinByAddress = jest.fn().mockImplementation(() => {
return Promise.resolve({ return Promise.resolve({
data: lookupVinbyAddressResponse data: lookupVinbyAddressResponse
@ -652,10 +421,6 @@ function setupMocks({
); );
const apiResponses = { const apiResponses = {
serviceZipValidationResponse: {
isValid: isZipValid,
isServiceable: isZipServiceable,
},
vinLookupResponse: { vinLookupResponse: {
isStatePermissible: isStatePermissible, isStatePermissible: isStatePermissible,
vinVehicles: vinVehicles, vinVehicles: vinVehicles,

View file

@ -28,21 +28,6 @@
:manualCopy="AlertMatchedDifferentVehicleBody" :manualCopy="AlertMatchedDifferentVehicleBody"
alertClass="alert-warning" alertClass="alert-warning"
v-bind:isDismissible="false" /> v-bind:isDismissible="false" />
<alert
ref="alertInvalidZip"
v-if="displayInvalidZipAlert"
class="mb-4"
cmsWidgetName="AlertInvalidZipWidget"
alertClass="alert-danger"
v-bind:isDismissible="false" />
<alert
ref="alertNonServiceableZip"
v-if="displayNonServiceableZipAlert"
class="mb-4"
alertClass="alert-danger"
:manualHeadline="AlertNonServiceableZipHeader"
:manualCopy="AlertNonServiceableZipBody"
v-bind:isDismissible="false" />
<alert <alert
ref="alertVinLookupsByHomeAddressNotAllowed" ref="alertVinLookupsByHomeAddressNotAllowed"
v-if="displayVinLookupByHomeAddressNotAllowedAlert" v-if="displayVinLookupByHomeAddressNotAllowedAlert"
@ -50,23 +35,9 @@
cmsWidgetName="AlertVinLookupsByHomeAddressNotAllowedWidget" cmsWidgetName="AlertVinLookupsByHomeAddressNotAllowedWidget"
alertClass="alert-danger" alertClass="alert-danger"
v-bind:isDismissible="false" /> v-bind:isDismissible="false" />
<transition name="fade" mode="out-in">
<div class="service-zip-field" v-if="showServiceZipField" aria-live="polite">
<div class="row mb-4">
<div class="col">
<textboxQuestion
cmsWidgetName="ServiceZipQuestionWidget"
v-model="serviceZipCode"
ref="serviceZip"
inputId="7add1b26df344f2caf1678de5797803f"
aria-haspopup=""
mask="#####"
validationRules="service-zip-required|service-zip-format" />
</div>
</div>
</div>
</transition>
<customerQuestions ref="customerQuestions" v-model="customerQuestions" /> <customerQuestions ref="customerQuestions" v-model="customerQuestions" />
<siteFooter <siteFooter
cmsWidgetName="SiteFooterWidget" cmsWidgetName="SiteFooterWidget"
ref="siteFooter" ref="siteFooter"
@ -90,9 +61,7 @@ import customerQuestions from "@/layouts/address-lookup/customer-questions/custo
import alert from "@/ux-components/alert/alert"; import alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/common-components/textbox-question/textbox-question"; import textboxQuestion from "@/common-components/textbox-question/textbox-question";
import { Form, defineRule } from "vee-validate"; import { Form } from "vee-validate";
import { required, regex } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
// Supporting files // Supporting files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -103,13 +72,6 @@ import { getDamageString, isGlassAvailableForCarId } from "@/helpers/damage-help
import vinPagesMixin from "@/mixins/vin-pages-mixin"; import vinPagesMixin from "@/mixins/vin-pages-mixin";
import { useMainStore } from "@/store" import { useMainStore } from "@/store"
// DEFINE VALIDATION RULES - Note: Additional rules are defined in Customer Questions and Address Questions components
defineRule("service-zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
defineRule(
"service-zip-format",
regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT)
);
export default { export default {
name: "address-lookup", name: "address-lookup",
mixins: [baseFormMixin, vinPagesMixin], mixins: [baseFormMixin, vinPagesMixin],
@ -144,8 +106,6 @@ export default {
firstName: "", firstName: "",
lastName: "", lastName: "",
}, },
serviceZipCode: this.serviceZip,
displayNonServiceableZipAlert: false,
displayVinNotFoundAlert: false, displayVinNotFoundAlert: false,
displayMatchedDifferentVehicleAlert: false, displayMatchedDifferentVehicleAlert: false,
displayVinLookupByHomeAddressNotAllowedAlert: false, displayVinLookupByHomeAddressNotAllowedAlert: false,
@ -153,9 +113,6 @@ export default {
isCarIdDifferent: false, isCarIdDifferent: false,
isSelectedGlassAvailableForVehicle: true, isSelectedGlassAvailableForVehicle: true,
customAlertData: {}, customAlertData: {},
displayInvalidZipAlert: false,
showServiceZipField: this.serviceZip,
isZipServiceable: false,
}; };
}, },
methods: { methods: {
@ -201,13 +158,7 @@ export default {
{ {
resultKey: "vinLookupResponse", resultKey: "vinLookupResponse",
promise: vinLookupResponse, promise: vinLookupResponse,
}, }
{
resultKey: "serviceZipValidationResponse",
promise: this.serviceZipCode
? useMainStore().validateZip({zip: this.serviceZipCode})
: useMainStore().validateZip({zip: this.customerQuestions.addressQuestions.zipCode})
},
]; ];
const resultMap = await settleAllPromises(promiseResultMap); const resultMap = await settleAllPromises(promiseResultMap);
@ -219,14 +170,6 @@ export default {
return this.$refs.siteFooter.removeLoader(); return this.$refs.siteFooter.removeLoader();
} }
// If a Service Zip is entered and it is an invalid zip code (ex. 11111) then show an alert
const isZipValid = resultMap.serviceZipValidationResponse.isValid;
if (this.serviceZipCode && !isZipValid) {
this.displayInvalidZipAlert = true;
return this.$refs.siteFooter.removeLoader();
}
this.displayInvalidZipAlert = false;
const carsFound = resultMap.vinLookupResponse.vinVehicles; const carsFound = resultMap.vinLookupResponse.vinVehicles;
// Handle cases for different amounts of VINS found for the address. // Handle cases for different amounts of VINS found for the address.
@ -252,11 +195,11 @@ export default {
return this.$refs.siteFooter.removeLoader(); return this.$refs.siteFooter.removeLoader();
} }
// update data if the zip or service zip is serviceable // update data
vehicleInfoToCommit = Object.assign(carFound, { vin: carsFound[0].vin }); vehicleInfoToCommit = Object.assign(carFound, { vin: carsFound[0].vin });
} else if (carsFound.length > 1) { } else if (carsFound.length > 1) {
// If multiple cars were found and one and only one of them matches the carId entered, save the vehicle info // If multiple cars were found and one and only one of them matches the carId entered, save the vehicle info
// so we can go to the Heritage Funnel directly
const matchingCars = carsFound.filter( const matchingCars = carsFound.filter(
(vin) => vin.vehicle.carId === this.mainStore.order.vehicle.carId (vin) => vin.vehicle.carId === this.mainStore.order.vehicle.carId
); );
@ -273,52 +216,26 @@ export default {
return this.$refs.siteFooter.removeLoader(); return this.$refs.siteFooter.removeLoader();
} }
// If the either the registration zip code or service zip code are not serviceable
this.isZipServiceable = resultMap.serviceZipValidationResponse.isServiceable;
if (!this.isZipServiceable) {
this.displayNonServiceableZipAlert = true;
this.$refs.siteFooter.disableForwardButton();
this.showServiceZipField = true;
return this.$refs.siteFooter.removeLoader();
}
// If the registration zip code is serviceable and nothing was entered for the service zip code
// then set the service zip code to the registration zip code
if (!this.serviceZipCode) {
this.serviceZipCode = this.customerQuestions.addressQuestions.zipCode;
}
// Save vehicle, customer, service and registration information // Save vehicle, customer, service and registration information
await useMainStore().saveRegistrationAddressLookup( await useMainStore().saveRegistrationAddressLookup(
{ {
isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle, isSelectedGlassAvailableForVehicle: this.isSelectedGlassAvailableForVehicle,
vehicleInfo: vehicleInfo:
Object.keys(vehicleInfoToCommit).length === 0 Object.keys(vehicleInfoToCommit).length === 0
? this.mainStore.order.vehicle ? useMainStore().order.vehicle
: vehicleInfoToCommit, : vehicleInfoToCommit,
registrationInfo: { registrationInfo: {
firstName: this.customerQuestions.firstName, firstName: this.customerQuestions.firstName,
lastName: this.customerQuestions.lastName, lastName: this.customerQuestions.lastName,
address: this.customerQuestions.addressQuestions.streetAddress, address: this.customerQuestions.addressQuestions.streetAddress,
city: this.customerQuestions.addressQuestions.city, city: this.customerQuestions.addressQuestions.city,
state: resultMap.serviceZipValidationResponse.state, state: this.customerQuestions.addressQuestions.state,
zipCode: this.customerQuestions.addressQuestions.zipCode, zipCode: this.customerQuestions.addressQuestions.zipCode,
}, },
}, },
false false
); );
await useMainStore().saveServiceLocation(
{
address: this.customerQuestions.addressQuestions.streetAddress,
city: this.customerQuestions.addressQuestions.city,
zipCode: this.serviceZipCode,
state: resultMap.serviceZipValidationResponse.state,
zipCodeCtu: resultMap.serviceZipValidationResponse.zipCodeCtu,
},
false
);
return await this.navigateForward(carsFound); return await this.navigateForward(carsFound);
}, },
async navigateForward(carsFound) { async navigateForward(carsFound) {
@ -353,7 +270,6 @@ export default {
}, },
resetWarningsAndErrors() { resetWarningsAndErrors() {
this.displayVinNotFoundAlert = false; this.displayVinNotFoundAlert = false;
this.displayNonServiceableZipAlert = false;
this.displayMatchedDifferentVehicleAlert = false; this.displayMatchedDifferentVehicleAlert = false;
this.displayVinLookupByHomeAddressNotAllowedAlert = false; this.displayVinLookupByHomeAddressNotAllowedAlert = false;
this.$refs.siteFooter.enableForwardAction(); this.$refs.siteFooter.enableForwardAction();
@ -364,19 +280,6 @@ export default {
this.loadDefaultsFromStore(); this.loadDefaultsFromStore();
}, },
computed: { computed: {
AlertNonServiceableZipHeader() {
const zipCode = this.serviceZipCode
? this.serviceZipCode
: this.customerQuestions.addressQuestions.zipCode;
const text = this.getCmsContent(
"AlertNonServiceableZipWidget",
"HeadlineText"
).replaceAll("{custom:serviceZip}", zipCode);
return text;
},
AlertNonServiceableZipBody() {
return this.getCmsContent("AlertNonServiceableZipWidget", "BodyText");
},
AlertMatchedDifferentVehicleHeader() { AlertMatchedDifferentVehicleHeader() {
return this.getCmsContent( return this.getCmsContent(
"AlertMatchedDifferentVehicleWidget", "AlertMatchedDifferentVehicleWidget",
@ -400,25 +303,10 @@ export default {
this.$refs.siteFooter.updateButtonText( this.$refs.siteFooter.updateButtonText(
this.getCmsContent("siteFooterWidget", "ForwardButtonText") this.getCmsContent("siteFooterWidget", "ForwardButtonText")
); );
this.showServiceZipField = false;
this.resetWarningsAndErrors(); this.resetWarningsAndErrors();
}, },
deep: true, deep: true,
}, }
serviceZipCode: {
handler(newValue) {
// If they modify the service zip code, then hide the error message.
this.resetWarningsAndErrors();
},
},
showServiceZipField: {
handler(newValue) {
// If the Service Zip Code field is ever hidden, clear out it's value
if (!newValue) {
this.serviceZipCode = null;
}
},
},
}, },
components: { components: {
siteHeader, siteHeader,

View file

@ -25,7 +25,7 @@
</template> </template>
<script> <script>
import addressQuestions from "@/layouts/address-lookup/customer-questions/address-questions/address-questions"; import addressQuestions from "@/common-components/address-questions/address-questions";
import textboxQuestion from "@/common-components/textbox-question/textbox-question"; import textboxQuestion from "@/common-components/textbox-question/textbox-question";
import { defineRule } from "vee-validate"; import { defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules"; import { required } from "@/helpers/validation-rules";

View file

@ -0,0 +1,58 @@
/* eslint-env jest */
import { render } from '@testing-library/vue';
import userEvent from '@testing-library/user-event';
import '@testing-library/jest-dom';
import VinLocationInformationComponent from './vin-location-information.vue';
const mockText = Object.freeze({
HEADER: 'Mock Header',
BODY: 'Mock Body',
});
const mountOptions = {
global: {
mixins: [
{
methods: {
getCmsContent: jest.fn((cmsWidgetName, fieldName) => {
if (cmsWidgetName === 'WhereCanIFindMyVINToggle') {
if (fieldName === 'HeaderText') {
return mockText.HEADER;
}
if (fieldName === 'BodyText') {
return mockText.BODY;
}
}
return '';
}),
},
},
],
},
};
describe('vin-location-information.vue', () => {
test('VIN Location Detail is NOT displayed on the screen as a default.', () => {
const { container } = render(VinLocationInformationComponent, mountOptions);
const vinLocationDetailSection = container.querySelector('#vin-location-detail-wrapper');
expect(vinLocationDetailSection).not.toBeVisible();
});
test('Toggling VIN Location Detail', async () => {
const user = userEvent.setup();
const { container, getByText } = render(VinLocationInformationComponent, mountOptions);
const toggleLink = getByText(mockText.HEADER);
await user.click(toggleLink);
let vinLocationDetailSection = container.querySelector('#vin-location-detail-wrapper');
expect(vinLocationDetailSection).toBeVisible();
await user.click(toggleLink);
vinLocationDetailSection = container.querySelector('#vin-location-detail-wrapper');
expect(vinLocationDetailSection).not.toBeVisible();
});
});

View file

@ -3,6 +3,7 @@
alertClass="alert-danger" alertClass="alert-danger"
cmsWidgetName="AlertVinNotFoundWidget" cmsWidgetName="AlertVinNotFoundWidget"
id="vehicle-not-found-alert" id="vehicle-not-found-alert"
aria-label="vehicle-not-found-alert"
/> />
</template> </template>
<script> <script>

View file

@ -4,6 +4,7 @@
cmsWidgetName="AlertMatchedDifferentVehicleWidget" cmsWidgetName="AlertMatchedDifferentVehicleWidget"
:manualCopy="body" :manualCopy="body"
:manualHeadline="header" :manualHeadline="header"
aria-label="vehicle-not-matched-alert"
/> />
</template> </template>
<script> <script>

View file

@ -0,0 +1,57 @@
/* eslint-env jest */
import { getDamageString } from '@/helpers/damage-helper';
import vehicleLookupAlertTypes from '@/constants/vehicle-lookup-alert-types';
import { RouterLinkStub } from '@vue/test-utils';
import { render } from '@testing-library/vue';
import '@testing-library/jest-dom';
import VinLookupAlertsComponent from './vin-lookup-alerts.vue';
jest.mock('@/helpers/damage-helper');
const mountOptions = {
global: {
stubs: {
RouterLink: RouterLinkStub,
},
mixins: [
{
methods: {
getCmsContent: jest.fn(() => ''),
getFooterInfoBoxHeight: jest.fn(() => 80),
cssClassNameForCmsWidget: jest.fn(() => 'widget-name-mock-class'),
},
},
],
},
};
describe('vin-lookup-alerts.vue', () => {
test('VehicleNotFoundAlert is displayed on the screen', () => {
mountOptions.props = {
activeAlertType: vehicleLookupAlertTypes.NOT_FOUND,
};
const { queryByRole } = render(VinLookupAlertsComponent, mountOptions);
const vehicleNotFoundAlert = queryByRole('alert', { name: 'vehicle-not-found-alert' });
const vehicleNotMatchedAlert = queryByRole('alert', { name: 'vehicle-not-matched-alert' });
expect(vehicleNotFoundAlert).toBeVisible();
expect(vehicleNotMatchedAlert).toEqual(null);
});
test('VehicleNotMatchedAlert is displayed on the screen', () => {
getDamageString.mockImplementation(() => 'Mock Damage String');
mountOptions.props = {
activeAlertType: vehicleLookupAlertTypes.NOT_MATCHED,
};
mountOptions.global.provide = {
vehicleFromLookup: {},
};
const { queryByRole } = render(VinLookupAlertsComponent, mountOptions);
const vehicleNotFoundAlert = queryByRole('alert', { name: 'vehicle-not-found-alert' });
const vehicleNotMatchedAlert = queryByRole('alert', { name: 'vehicle-not-matched-alert' });
expect(vehicleNotFoundAlert).toEqual(null);
expect(vehicleNotMatchedAlert).toBeVisible();
});
});

View file

@ -0,0 +1,453 @@
/* eslint-env jest */
import '@testing-library/jest-dom';
import { flushPromises } from '@vue/test-utils';
import { render, waitFor } from '@testing-library/vue';
import { createTestingPinia } from '@pinia/testing';
import userEvent from '@testing-library/user-event';
import { errorMessages } from '@/constants/error-messages';
import { issPageValues } from '@/router/router-constants/issPage-values';
import { navigationScenarios } from '@/router/router-constants/navigation-scenarios';
import { routerParams } from '@/router/router-params';
import { useMainStore } from '@/store';
import VinLookupComponent from './vin-lookup.vue';
const continueButtonQuerySelector = '[data-test-id="site-footer-main-button"]';
const errorMessageWrapperElSelector = '#vin-question-wrapper .form-test-error';
const vinInputSelector = '#vin-question';
const mockValidVin = '12345678901234567';
const mockCarId = 'Mock Car Id';
const lookupVehicleByVin = {
methodName: 'lookupVehicleByVin',
mockResponse: {
data: {
carId: mockCarId,
},
},
};
const getPartsOrQuestions = {
methodName: 'getPartsOrQuestions',
mockResponse: null,
};
const vehicleWithPartQuestionsMockResponse = {
data: {
partsOrQuestions: [
{
partQuestions: [
{
id: 1,
},
],
},
],
},
};
const vehicleWithMultiplePartsMockResponse = {
data: {
partsOrQuestions: [
{
parts: [
{
id: 1,
},
{
id: 2,
},
],
},
],
},
};
const vehicleWithMoldingQuestionsMockResponse = {
data: {
partsOrQuestions: [
{
parts: [
{
childPartQuestions: [
{
id: 1,
},
],
},
],
},
],
},
};
const vehicleWithCapabilityQuestionsMockResponse = {
data: {
partsOrQuestions: [
{
parts: [
{
requiresCapabilityQuestions: true,
partNumber: 1,
},
],
},
],
},
};
const vehicleWithNoAdditionalPartsOrQuestionsMockResponse = {
data: {
partsOrQuestions: [
{
parts: [
{
childPartQuestions: [],
requiresCapabilityQuestions: false,
},
],
},
],
partQuestions: null,
},
};
const mockRoute = {
query: {
// Needed inside vehicle-questions-mixin
issPage: issPageValues.VIN_LOOKUP,
},
};
const mockRouter = {
navigate: jest.fn(),
};
const maska = jest.fn();
jest.mock('@/helpers/damage-helper', () => ({
isGlassAvailableForCarId: jest.fn().mockResolvedValue(false),
}));
const mountOptions = {
global: {
directives: {
maska,
},
mixins: [
{
methods: {
getCmsContent: jest.fn(() => ''),
getFooterInfoBoxHeight: jest.fn(() => 80),
cssClassNameForCmsWidget: jest.fn(() => 'widget-name-mock-class'),
getPageNameByQueryString: jest.fn(() => ''),
},
computed: {
navigationScenarios() {
return navigationScenarios;
},
},
},
],
mocks: {
$route: mockRoute,
$router: mockRouter,
},
plugins: [createTestingPinia({
initialState: {
main: {
order: {
vehicle: {
carId: mockCarId,
},
},
},
},
stubActions: false,
})],
stubs: {
siteHeader: true,
siteSubHeader: true,
vehicleBanner: true,
vinLocationInformation: true,
vinLookupAlerts: true,
vinQuestion: true,
},
},
};
beforeEach(() => {
jest.clearAllMocks();
});
describe('vin-lookup.vue', () => {
describe('Client-side validation', () => {
test.each([
{ vin: null },
{ vin: '' },
])('VIN Required error messsage is displayed.', async ({ vin }) => {
const user = userEvent.setup();
mountOptions.data = () => ({
vin,
});
mountOptions.global.stubs.vinQuestion = false;
const { container } = render(VinLookupComponent, mountOptions);
const continueButton = container.querySelector(continueButtonQuerySelector);
user.click(continueButton);
await flushPromises();
await waitFor(() => {
const errorMessageWrapperEl = container.querySelector(errorMessageWrapperElSelector);
expect(errorMessageWrapperEl).toBeVisible();
const errorMessageEl = errorMessageWrapperEl.querySelector('span');
expect(errorMessageEl.textContent).toBe(errorMessages.VIN_REQUIRED);
});
});
test.each([
{ vin: '11' },
{ vin: 'I1234567890123456' },
{ vin: 'O1234567890123456' },
{ vin: 'Q1234567890123456' },
])('VIN Invalid Format error messsage is displayed.', async ({ vin }) => {
const user = userEvent.setup();
mountOptions.data = () => ({
vin,
});
mountOptions.global.stubs.vinQuestion = false;
const { container } = render(VinLookupComponent, mountOptions);
const continueButton = container.querySelector(continueButtonQuerySelector);
user.click(continueButton);
await flushPromises();
await waitFor(() => {
const errorMessageWrapperEl = container.querySelector(errorMessageWrapperElSelector);
expect(errorMessageWrapperEl).toBeVisible();
const errorMessageEl = errorMessageWrapperEl.querySelector('span');
expect(errorMessageEl.textContent).toBe(errorMessages.VIN_FORMAT);
});
});
test('No error message is displayed when the vin is in the accepted format.', async () => {
const user = userEvent.setup();
mountOptions.data = () => ({
vin: '12345678901234567',
});
mountOptions.global.stubs.vinQuestion = false;
const { container } = render(VinLookupComponent, mountOptions);
const continueButton = container.querySelector(continueButtonQuerySelector);
user.click(continueButton);
await flushPromises();
await waitFor(() => {
const errorMessageWrapperEl = container.querySelector(errorMessageWrapperElSelector);
expect(errorMessageWrapperEl).not.toBeVisible();
const errorMessageEl = errorMessageWrapperEl.querySelector('span');
expect(errorMessageEl.textContent).toBeFalsy();
});
});
});
describe('Navigation', () => {
test('Click "Back", execute navigate with navigationScenario.CLICKED_BACK.', async () => {
const user = userEvent.setup();
const { container } = render(VinLookupComponent, mountOptions);
const backButton = container.querySelector('[data-test-id="site-footer-back-button"]');
await user.click(backButton);
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK, mockRoute);
});
test('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 () => {
const user = userEvent.setup();
mountOptions.global.stubs.vinQuestion = false;
getPartsOrQuestions.mockResponse = vehicleWithPartQuestionsMockResponse;
lookupVehicleByVin.mockResponse.data.carId = 'Different Car Id';
jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
mountOptions.data = () => ({
activeVehicleLookupAlertType: null,
needToLookupVehicle: false,
vehicleFromLookup: {
carId: 'Different Car Id',
},
vin: mockValidVin,
});
const { container } = render(VinLookupComponent, mountOptions);
const continueButton = container.querySelector(continueButtonQuerySelector);
await user.click(continueButton);
await flushPromises();
await waitFor(() => {
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate).toHaveBeenCalledWith(
navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
mockRoute,
{},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true },
);
});
});
describe('Succesful navigateForward', () => {
test('Vehicle with Part Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_PART_QUESTIONS', async () => {
const user = userEvent.setup();
mountOptions.global.stubs.vinQuestion = false;
getPartsOrQuestions.mockResponse = vehicleWithPartQuestionsMockResponse;
jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
const { container } = render(VinLookupComponent, mountOptions);
const vinInput = container.querySelector(vinInputSelector);
await user.type(vinInput, mockValidVin);
const continueButton = container.querySelector(continueButtonQuerySelector);
await user.click(continueButton);
await flushPromises();
await waitFor(() => {
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate)
.toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
mockRoute,
{},
{},
{ partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions },
);
});
});
test('Vehicle with Multiple Parts. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE', async () => {
const user = userEvent.setup();
mountOptions.global.stubs.vinQuestion = false;
getPartsOrQuestions.mockResponse = vehicleWithMultiplePartsMockResponse;
jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
const { container } = render(VinLookupComponent, mountOptions);
const vinInput = container.querySelector(vinInputSelector);
await user.type(vinInput, mockValidVin);
const continueButton = container.querySelector(continueButtonQuerySelector);
await user.click(continueButton);
await flushPromises();
await waitFor(() => {
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate)
.toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
mockRoute,
{},
{},
{ partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions },
);
});
});
test('Vehicle with Molding Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS', async () => {
const user = userEvent.setup();
mountOptions.global.stubs.vinQuestion = false;
getPartsOrQuestions.mockResponse = vehicleWithMoldingQuestionsMockResponse;
jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
const { container } = render(VinLookupComponent, mountOptions);
const vinInput = container.querySelector(vinInputSelector);
await user.type(vinInput, mockValidVin);
const continueButton = container.querySelector(continueButtonQuerySelector);
await user.click(continueButton);
await flushPromises();
await waitFor(() => {
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate)
.toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS,
mockRoute,
{},
{},
{ partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions },
);
});
});
test('Vehicle with Capability Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS', async () => {
const user = userEvent.setup();
const store = useMainStore();
mountOptions.global.stubs.vinQuestion = false;
getPartsOrQuestions.mockResponse = vehicleWithCapabilityQuestionsMockResponse;
jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
store.getCapabilityQuestions.mockResolvedValueOnce({ data: [] });
const { container } = render(VinLookupComponent, mountOptions);
const vinInput = container.querySelector(vinInputSelector);
await user.type(vinInput, mockValidVin);
const continueButton = container.querySelector(continueButtonQuerySelector);
await user.click(continueButton);
await flushPromises();
await waitFor(() => {
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate)
.toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS,
mockRoute,
{},
{},
{ partsOrQuestions: getPartsOrQuestions.mockResponse.data.partsOrQuestions },
);
});
});
test('Vehicle no additional Parts or Questions. Click "Continue", execute navigate with navigationScenario.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS', async () => {
const user = userEvent.setup();
mountOptions.global.stubs.vinQuestion = false;
getPartsOrQuestions.mockResponse = vehicleWithNoAdditionalPartsOrQuestionsMockResponse;
jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(VinLookupComponent.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
const { container } = render(VinLookupComponent, mountOptions);
const vinInput = container.querySelector(vinInputSelector);
await user.type(vinInput, mockValidVin);
const continueButton = container.querySelector(continueButtonQuerySelector);
await user.click(continueButton);
await flushPromises();
await waitFor(() => {
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate)
.toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
mockRoute,
);
});
});
});
});
});

View file

@ -40,15 +40,16 @@
<script> <script>
// Import Supporting Files // Import Supporting Files
import { computed } from 'vue'; import { computed } from 'vue';
import { endpoints } from '@/constants/endpoints';
import vehicleLookupAlertTypes from '@/constants/vehicle-lookup-alert-types'; import vehicleLookupAlertTypes from '@/constants/vehicle-lookup-alert-types';
import globalMethods from '@/global-methods';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import { isGlassAvailableForCarId } from '@/helpers/damage-helper';
import { settleAllPromises } from '@/helpers/layout-helper'; import { settleAllPromises } from '@/helpers/layout-helper';
import { routerParams } from '@/router/router-params';
import { useMainStore } from '@/store'; import { useMainStore } from '@/store';
// Import Component // Import Component
import baseFormMixin from '@/mixins/base-form-mixin'; import baseFormMixin from '@/mixins/base-form-mixin';
import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import { Form } from 'vee-validate'; import { Form } from 'vee-validate';
import siteFooter from '@/common-components/site-footer/site-footer.vue'; import siteFooter from '@/common-components/site-footer/site-footer.vue';
import siteHeader from '@/common-components/site-header/site-header.vue'; import siteHeader from '@/common-components/site-header/site-header.vue';
@ -60,7 +61,7 @@ import vinQuestion from './vin-question/vin-question.vue';
export default { export default {
name: 'vin-lookup', name: 'vin-lookup',
mixins: [baseFormMixin], mixins: [baseFormMixin, vehicleQuestionsMixin],
components: { components: {
siteFooter, siteFooter,
siteHeader, siteHeader,
@ -79,8 +80,9 @@ export default {
data() { data() {
return { return {
activeVehicleLookupAlertType: null, activeVehicleLookupAlertType: null,
needToLookupVehicle: true,
vehicleFromLookup: null, vehicleFromLookup: null,
vin: null, vin: null,
}; };
}, },
provide() { provide() {
@ -105,6 +107,14 @@ export default {
vm.setCmsContent(resultMap.cmsContent); vm.setCmsContent(resultMap.cmsContent);
}); });
}, },
computed: {
isCarIdDifferentFromTheStore() {
return (
this.vehicleFromLookup !== null
&& this.vehicleFromLookup.carId !== this.mainStore.vehicle.carId
);
}
},
methods: { methods: {
arePagePrerequisiteValid() { arePagePrerequisiteValid() {
return true; return true;
@ -115,40 +125,77 @@ export default {
*/ */
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route); this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
}, },
// NOTE: If form is not valid, this method is not called when 'Continue' button is clicked
async forwardButtonAction() { async forwardButtonAction() {
this.resetActiveAlert(); this.resetActiveAlert();
// Temp solution to reset the 'disabled' style on the Continue button
this.$refs.siteFooter.enableForwardAction();
if (this.needToLookupVehicle) {
const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin);
// NOTE: If form is not valid, this method is not called when 'Continue' button is clicked if (vehicleLookupResponse.error) {
const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin); this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND;
this.resetVehicleFromLookup();
this.$refs.siteFooter.removeLoader();
// Temp solution to turn on 'disabled' style on the Continue button
// because the form itself actually passes its client-side validation.
// SSR-189 Scenario #4.
this.$refs.siteFooter.disableForwardButton();
return;
}
if (vehicleLookupResponse.error) { // Add vin bcs the response from the service doesn't contain vin
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND; this.vehicleFromLookup = Object.assign(vehicleLookupResponse.data, { vin: this.vin });
this.resetVehicleFromLookup(); }
if (this.needToLookupVehicle && this.isCarIdDifferentFromTheStore) {
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_MATCHED;
const vehicleYearMakeModel = `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model}`;
this.$refs.siteFooter.updateButtonText(`Continue with ${vehicleYearMakeModel}`);
this.$refs.siteFooter.removeLoader();
this.needToLookupVehicle = false;
return;
}
let isSelectedGlassAvailableForVehicle = true;
if (this.isCarIdDifferentFromTheStore) {
isSelectedGlassAvailableForVehicle =
await isGlassAvailableForCarId(this.vehicleFromLookup.carId);
}
// navigate back to vehicle-damage
if (this.isCarIdDifferentFromTheStore && !isSelectedGlassAvailableForVehicle) {
this.mainStore.updateVehicle(this.vehicleFromLookup);
this.$router.navigate(
this.navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
this.$route,
{},
{ [routerParams.DISPLAY_VEHICLE_CHANGE_ALERT]: true }
);
// navigate() doesn't stop the processing flow
return;
}
this.mainStore.updateVehicle(this.vehicleFromLookup);
const partsOrQuestionsResponse = await this.getPartsOrQuestions();
if (partsOrQuestionsResponse.error) {
// To Do: Need requirement on what to do here
console.error('Error on retrieving PartsOrQuestions');
this.$refs.siteFooter.removeLoader(); this.$refs.siteFooter.removeLoader();
return; return;
} }
this.vehicleFromLookup = vehicleLookupResponse.data; // Comes from vehicleQuestionsMixin.navigateForward()
if (this.vehicleFromLookup.carId !== this.mainStore.vehicle.carId) { await this.navigateForward(partsOrQuestionsResponse.data.partsOrQuestions, this);
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_MATCHED;
const vehicleYearMakeModel = `${this.vehicleFromLookup.year} ${this.vehicleFromLookup.make} ${this.vehicleFromLookup.model}`;
this.$refs.siteFooter.updateButtonText(`Continue with ${vehicleYearMakeModel}`);
this.$refs.siteFooter.removeLoader();
}
// Continue
}, },
async lookupVehicleByVin(vin) { async getPartsOrQuestions() {
try { try {
const response = await globalMethods.callHttpClient({ const response = await this.mainStore.getPartsOrQuestions();
method: endpoints.LookupVehicleByVin.method,
endpoint: endpoints.LookupVehicleByVin.url,
payload: {
vin,
},
});
return response; return response;
} catch (responseError) { } catch (responseError) {
@ -159,6 +206,18 @@ export default {
}; };
} }
}, },
async lookupVehicleByVin(vin) {
try {
return await this.mainStore.lookupVehicleByVin(vin);
}
catch (responseError) {
return {
error: {
status: responseError.status,
},
};
}
},
resetActiveAlert() { resetActiveAlert() {
this.activeVehicleLookupAlertType = null; this.activeVehicleLookupAlertType = null;
}, },
@ -167,5 +226,15 @@ export default {
}, },
resetDependentState() {}, resetDependentState() {},
}, },
watch: {
vin() {
this.resetActiveAlert();
this.$refs.siteFooter.enableForwardAction();
this.needToLookupVehicle = true;
this.$refs.siteFooter.updateButtonText(
this.getCmsContent('SiteFooterWidget', 'ForwardButtonText')
);
}
},
}; };
</script> </script>

View file

@ -9,7 +9,7 @@
<textboxQuestion <textboxQuestion
inputId="policyNumberField" inputId="policyNumberField"
cmsWidgetName="PolicyNumberQuestion" cmsWidgetName="PolicyNumberQuestion"
v-model="policyNumber" v-model="welcomePageModel.policyNumber"
isRequired isRequired
ref="policyNumber" ref="policyNumber"
disableAutoFill disableAutoFill
@ -21,7 +21,7 @@
<textboxQuestion <textboxQuestion
type="date" type="date"
cmsWidgetName="DateOfLossQuestion" cmsWidgetName="DateOfLossQuestion"
v-model="dateOfLoss" v-model="welcomePageModel.dateOfLoss"
inputId="dateOfLossField" inputId="dateOfLossField"
isRequired isRequired
ref="dateOfLoss" ref="dateOfLoss"
@ -41,7 +41,7 @@
<div class="col"> <div class="col">
<dropdownQuestion <dropdownQuestion
cmsWidgetName="DamageCauseQuestion" cmsWidgetName="DamageCauseQuestion"
v-model="damageCause" v-model="welcomePageModel.damageCause"
ref="damageCause" ref="damageCause"
inputId="damageCauseQuestionField" inputId="damageCauseQuestionField"
:options="DamageCauseOptions" :options="DamageCauseOptions"
@ -55,7 +55,7 @@
<textboxQuestion <textboxQuestion
inputId="damageCityField" inputId="damageCityField"
cmsWidgetName="DamageCityQuestion" cmsWidgetName="DamageCityQuestion"
v-model="damageCity" v-model="welcomePageModel.damageCity"
isRequired isRequired
ref="damageCity" ref="damageCity"
disableAutoFill disableAutoFill
@ -66,7 +66,7 @@
<div class="col"> <div class="col">
<dropdownQuestion <dropdownQuestion
cmsWidgetName="DamageStateQuestion" cmsWidgetName="DamageStateQuestion"
v-model="damageState" v-model="welcomePageModel.damageState"
ref="state" ref="state"
inputId="8fdf9dc2e13e430eb57529499dceb3eb" inputId="8fdf9dc2e13e430eb57529499dceb3eb"
:options="stateOptions" :options="stateOptions"
@ -79,7 +79,7 @@
<div class="row mt-4"> <div class="row mt-4">
<buttonQuestion <buttonQuestion
cmsWidgetName="GlassOnlyQuestion" cmsWidgetName="GlassOnlyQuestion"
v-model="isDamageGlassOnly" v-model="welcomePageModel.isDamageGlassOnly"
inputId = "isDamageGlassOnly" inputId = "isDamageGlassOnly"
:answers="DamageGlassOnlyOptions" :answers="DamageGlassOnlyOptions"
:questionText="DamageGlassOnlyQuestion" :questionText="DamageGlassOnlyQuestion"
@ -94,7 +94,7 @@
<textboxQuestion <textboxQuestion
inputId="phoneNumberField" inputId="phoneNumberField"
cmsWidgetName="PhoneNumberQuestion" cmsWidgetName="PhoneNumberQuestion"
v-model="phoneNumber" v-model="welcomePageModel.phoneNumber"
validationRules="phone-number-required|phone-number-format" validationRules="phone-number-required|phone-number-format"
isRequired isRequired
ref="phoneNumber" ref="phoneNumber"
@ -107,7 +107,7 @@
<textboxQuestion <textboxQuestion
inputId="emailField" inputId="emailField"
cmsWidgetName="EmailAddressQuestion" cmsWidgetName="EmailAddressQuestion"
v-model="email" v-model="welcomePageModel.email"
ref="email" ref="email"
validationRules="email-address-required|email-address-format" validationRules="email-address-required|email-address-format"
isRequired isRequired
@ -145,6 +145,7 @@
import { required, regex } from "@/helpers/validation-rules"; import { required, regex } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages"; import { errorMessages } from "@/constants/error-messages";
import BaseFormMixin from '@/mixins/base-form-mixin.js'; import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store';
//define validation rules //define validation rules
defineRule("loss-date-required", required(errorMessages.LOSS_DATE_REQUIRED)); defineRule("loss-date-required", required(errorMessages.LOSS_DATE_REQUIRED));
@ -176,17 +177,13 @@
mixins: [BaseFormMixin], mixins: [BaseFormMixin],
data() { data() {
return { return {
policyNumber: "", welcomePageModel: this.getWelcomePageModelFromStore()
policyZipCode: "",
dateOfLoss: "",
damageCause: "",
damageState: "",
damageCity: "",
isDamageGlassOnly: "",
phoneNumber: "",
email: ""
}; };
}, },
setup() {
const mainStore = useMainStore();
return { mainStore };
},
async beforeRouteEnter(to, from, next) async beforeRouteEnter(to, from, next)
{ {
// Call APIs // Call APIs
@ -210,6 +207,7 @@
methods: methods:
{ {
async forwardButtonAction() { async forwardButtonAction() {
this.mainStore.updatePolicyData(this.welcomePageModel);
return this.navigateForward(); return this.navigateForward();
}, },
@ -219,20 +217,32 @@
this.$route this.$route
); );
}, },
getWelcomePageModelFromStore() {
return {
policyNumber : this.mainStore.order.policy.policyNumber,
dateOfLoss : this.mainStore.order.policy.dateOfLoss,
damageCause : this.mainStore.order.policy.damageCause,
damageState : this.mainStore.order.policy.damageState,
damageCity : this.mainStore.order.policy.damageCity,
isDamageGlassOnly : this.mainStore.order.policy.isDamageGlassOnly,
phoneNumber : this.mainStore.order.customer.phoneNumber,
email : this.mainStore.order.customer.emailAddress,
}
},
}, },
computed:{ computed:{
DamageCauseOptions() { DamageCauseOptions() {
const damageCauseAnswers = this.getCmsContent("DamageCauseQuestion", "Answers"); const damageCauseAnswers = this.getCmsContent("DamageCauseQuestion", "Answers");
const answerArray = []; const damageCauseAnswersObj ={};
if(damageCauseAnswers) if(damageCauseAnswers)
{ {
for (let answer of Object.values(damageCauseAnswers)) { for (let answer of Object.values(damageCauseAnswers)) {
if (answer?.Name) { if (answer?.Name) {
answerArray.push(answer.Name); damageCauseAnswersObj[answer.Name] = answer.Name;
} }
} }
} }
return answerArray; return damageCauseAnswersObj;
}, },
DamageGlassOnlyOptions() DamageGlassOnlyOptions()
{ {
@ -299,14 +309,6 @@
}; };
}, },
}, },
welcomePageModel: {
get: function () {
return this.modelValue;
},
set: function (newValue) {
this.$emit("update:modelValue", newValue);
},
},
}, },
components: { components: {
siteHeader, siteHeader,

View file

@ -100,7 +100,31 @@ const routingTable = function(store) {
{ {
scenario: navigationScenarios.CLICKED_BACK, scenario: navigationScenarios.CLICKED_BACK,
destinationIssPageValue: issPageValues.VEHICLE_LOOKUP, destinationIssPageValue: issPageValues.VEHICLE_LOOKUP,
}, },
{
scenario: navigationScenarios.SELECTED_VIN_WITH_MISMATCHED_GLASS,
destinationIssPageValue: issPageValues.VEHICLE_DAMAGE,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
destinationIssPageValue: issPageValues.PART_QUESTIONS,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
destinationIssPageValue: issPageValues.VEHICLE_PARTS,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS,
destinationIssPageValue: issPageValues.MOLDING_QUESTIONS,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS,
destinationIssPageValue: issPageValues.CAPABILITY_QUESTIONS,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
destinationIssPageValue: issPageValues.COVERAGE_STATEMENT,
},
], ],
}, },
{ {

View file

@ -1,5 +1,5 @@
const routerParams = { const routerParams = Object.freeze({
DISPLAY_VEHICLE_CHANGE_ALERT: "displayVehicleChangeAlert", DISPLAY_VEHICLE_CHANGE_ALERT: "displayVehicleChangeAlert",
}; });
export { routerParams }; export { routerParams };

View file

@ -41,6 +41,14 @@ const getDefaultState = () => {
moldingQuestionAnswers: null, moldingQuestionAnswers: null,
capabilityQuestionAnswers: null, capabilityQuestionAnswers: null,
}, },
policy: {
policyNumber: null,
dateOfLoss: null,
damageCause: null,
damageState: null,
damageCity: null,
isDamageGlassOnly: null,
},
customer: { customer: {
address: { address: {
streetAddress: null, streetAddress: null,
@ -51,6 +59,7 @@ const getDefaultState = () => {
firstName: null, firstName: null,
lastName: null, lastName: null,
emailAddress: null, emailAddress: null,
phoneNumber: null,
}, },
serviceLocation: { serviceLocation: {
address: null, address: null,
@ -389,6 +398,16 @@ export const useMainStore = defineStore({
}); });
}, },
lookupVehicleByVin(vin) {
return globalMethods.callHttpClient({
method: endpoints.LookupVehicleByVin.method,
endpoint: endpoints.LookupVehicleByVin.url,
payload: {
vin,
},
});
},
setVehicle() { setVehicle() {
return globalMethods return globalMethods
.callHttpClient({ .callHttpClient({
@ -635,7 +654,17 @@ export const useMainStore = defineStore({
updatePageData(pageData) { updatePageData(pageData) {
this.applicationUser.pageData[pageData.page] = pageData.data; this.applicationUser.pageData[pageData.page] = pageData.data;
}, },
updatePolicyData(welcomePageModel)
{
this.order.policy.policyNumber = welcomePageModel?.policyNumber;
this.order.policy.dateOfLoss = welcomePageModel?.dateOfLoss;
this.order.policy.damageCause = welcomePageModel?.damageCause;
this.order.policy.damageState = welcomePageModel?.damageState;
this.order.policy.damageCity = welcomePageModel?.damageCity;
this.order.policy.isDamageGlassOnly = welcomePageModel?.isDamageGlassOnly;
this.order.customer.phoneNumber = welcomePageModel?.phoneNumber;
this.order.customer.emailAddress = welcomePageModel?.email;
},
savePartQuestionAnswers(partQuestionAnswersArray) { savePartQuestionAnswers(partQuestionAnswersArray) {
// if part question answers have changed, reset subsequent question answers // if part question answers have changed, reset subsequent question answers
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue( const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(