Merge branch 'develop' into feature/Digital/SSR-139
This commit is contained in:
commit
a8a1c849e3
10 changed files with 3438 additions and 27 deletions
398
src/layouts/part-questions/part-questions.spec.js
Normal file
398
src/layouts/part-questions/part-questions.spec.js
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
// Components
|
||||
import partQuestions from "@/layouts/part-questions/part-questions";
|
||||
|
||||
// Supporting Files
|
||||
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";
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||
settleAllPromises: jest.fn(),
|
||||
}));
|
||||
|
||||
// Mock fetchCmsContentForPage
|
||||
jest.mock("@/helpers/cms-content-helper", () => ({
|
||||
fetchCmsContentForPage: jest.fn(),
|
||||
}));
|
||||
|
||||
|
||||
const baseStoreGettersPageData = () => {
|
||||
return {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
parts: null,
|
||||
partQuestions: [
|
||||
{
|
||||
questionSequence: 1,
|
||||
questionText:
|
||||
"Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?",
|
||||
answers: [
|
||||
{
|
||||
answerResult: "",
|
||||
answerText: "Yes",
|
||||
nextQuestionSequence: 2,
|
||||
},
|
||||
{
|
||||
answerResult: "",
|
||||
answerText: "No",
|
||||
nextQuestionSequence: 3,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
answerKey: "Windshield-Single",
|
||||
answerData: null,
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
const baseStoreGettersDamage = () => {
|
||||
return {
|
||||
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?",
|
||||
selectedAnswerText: "Yes",
|
||||
questionNum: 1,
|
||||
},
|
||||
{
|
||||
questionText:
|
||||
"Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?",
|
||||
selectedAnswerText: "Yes",
|
||||
questionNum: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
useMainStore().pageData = baseStoreGettersPageData;
|
||||
useMainStore().damage = baseStoreGettersDamage;
|
||||
|
||||
describe("partQuestions.vue...", () => {
|
||||
describe("method arePagePrerequisitesValid...", () => {
|
||||
test("Should return true for valid page requisites if pageData exists", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
//Assert
|
||||
expect(result).toBe(true);
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("Should return false for valid page requisites if partsOrQuestions in pageData is missing", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
useMainStore().pageData = jest.fn(() => {
|
||||
return undefined;
|
||||
});
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
//Assert
|
||||
expect(result).toBeFalsy();
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("Should be at least one item in partsOrQuestions", () => {
|
||||
// Arrange
|
||||
useMainStore().pageData = jest.fn(() => {
|
||||
return {
|
||||
partsOrQuestions: [],
|
||||
};
|
||||
});
|
||||
useMainStore().damage = baseStoreGettersDamage;
|
||||
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
//Assert
|
||||
expect(result).toBeFalsy();
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("watch on selectedAnswers should be set up...", () => {
|
||||
test("Should trigger handleAnswerUpdates if watched data changes", async () => {
|
||||
// Arrange
|
||||
useMainStore().pageData = baseStoreGettersPageData;
|
||||
useMainStore().damage = baseStoreGettersDamage;
|
||||
|
||||
const { wrapper } = setupMocks({});
|
||||
const spy = jest.spyOn(wrapper.vm, "handleAnswerUpdates");
|
||||
|
||||
// Act
|
||||
wrapper.setData({
|
||||
selectedAnswers: {
|
||||
"Windshield-Single": {
|
||||
answerResult: "FW04848",
|
||||
answeredQuestions: [
|
||||
{
|
||||
questionText: "One?",
|
||||
selectedAnswerText: "Yes",
|
||||
questionNum: 1,
|
||||
},
|
||||
{
|
||||
questionText: "Two?",
|
||||
selectedAnswerText: "Yes",
|
||||
questionNum: 2,
|
||||
},
|
||||
],
|
||||
index: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await nextTick();
|
||||
|
||||
wrapper.setData({
|
||||
selectedAnswers: {
|
||||
"Windshield-Single": {
|
||||
answerResult: "NEW-ANSWER",
|
||||
answeredQuestions: [
|
||||
{
|
||||
questionText: "One?",
|
||||
selectedAnswerText: "Yes",
|
||||
questionNum: 1,
|
||||
},
|
||||
{
|
||||
questionText: "Two?",
|
||||
selectedAnswerText: "Yes",
|
||||
questionNum: 2,
|
||||
},
|
||||
],
|
||||
index: 0,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
//Assert
|
||||
expect(spy).toHaveBeenCalled();
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
|
||||
describe("forwardButtonAction", () => {
|
||||
test("Should clear out answerData", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
await wrapper.setData({
|
||||
questionsData: [
|
||||
{
|
||||
glassLocation: "fbfWindshield",
|
||||
glassName: "fbfSingle",
|
||||
answerData: {
|
||||
answerResult: "FW04848",
|
||||
answeredQuestions: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
wrapper.vm.getParts = jest.fn(() => {
|
||||
return {
|
||||
data: {
|
||||
glassPieceParts: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.forwardButtonAction();
|
||||
|
||||
await nextTick();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.questionsData[0].answerData).toEqual({});
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("Should save to pinia store", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.questionsData = [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
answerData: {
|
||||
answerResult: "FW04848",
|
||||
answeredQuestions: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
useMainStore().getParts = jest.fn(() => {
|
||||
return {
|
||||
data: {
|
||||
glassPieceParts: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.forwardButtonAction();
|
||||
|
||||
await nextTick();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.savePartQuestionAnswers).toHaveBeenCalled;
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("Should call GET_PARTS API", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.questionsData = [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
answerData: {
|
||||
answerResult: "FW04848",
|
||||
answeredQuestions: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
useMainStore().getParts = jest.fn(() => {
|
||||
return {
|
||||
data: {
|
||||
glassPieceParts: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
// Act
|
||||
wrapper.vm.forwardButtonAction();
|
||||
|
||||
await nextTick();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.getParts).toHaveBeenCalled;
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("Should trigger navigateForward", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
wrapper.vm.questionsData = [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
answerData: {
|
||||
answerResult: "FW04848",
|
||||
answeredQuestions: [],
|
||||
},
|
||||
},
|
||||
];
|
||||
useMainStore().getParts = jest.fn(() => {
|
||||
return {
|
||||
data: {
|
||||
glassPieceParts: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.navigateForward).toHaveBeenCalled();
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setupMocks({
|
||||
mountOptionsMockData = {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
navigateWithSaving: jest.fn(),
|
||||
navigateWithoutSaving: jest.fn(),
|
||||
},
|
||||
actionList: [
|
||||
{
|
||||
actionName: "savePartQuestionAnswers",
|
||||
data: {},
|
||||
},
|
||||
{
|
||||
actionName: "getParts",
|
||||
data: {},
|
||||
},
|
||||
],
|
||||
route: {
|
||||
query: {
|
||||
fmgPage: "part-questions",
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
computedSwitcher: [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
answerData: {
|
||||
answerResult: "FW04848",
|
||||
answeredQuestions: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
},
|
||||
questionsData: {
|
||||
get() {
|
||||
return this.computedSwitcher;
|
||||
},
|
||||
set(val) {
|
||||
this.computedSwitcher = val;
|
||||
},
|
||||
},
|
||||
},
|
||||
}) {
|
||||
|
||||
useMainStore().getParts = jest.fn(() => {
|
||||
return {
|
||||
data: {
|
||||
glassPieceParts: [],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const mountOptions = getMountOptions({
|
||||
...mountOptionsMockData,
|
||||
mixins: [baseMixin, vehicleQuestionsMixin],
|
||||
});
|
||||
mountOptions["attachTo"] = document.body;
|
||||
|
||||
const wrapper = shallowMount(partQuestions, mountOptions);
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
name: "make-question",
|
||||
data() {
|
||||
return {
|
||||
makes: Array,
|
||||
makes: [],
|
||||
};
|
||||
},
|
||||
props: {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,8 @@
|
|||
<div class="select-car-form rounded text-center">
|
||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="true"/>
|
||||
<siteSubHeader
|
||||
cmsWidgetName="SiteSubHeaderWidget"
|
||||
cmsWidgetName="SiteSubHeaderWidget"
|
||||
:hasBackButton="true"
|
||||
:backButtonAccessibleText="backButtonAccessibleText"
|
||||
@click-event="backButtonAction"
|
||||
/>
|
||||
<div class="fade-on-route-transition">
|
||||
|
|
@ -97,12 +96,6 @@
|
|||
this.$route
|
||||
);
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
backButtonAccessibleText()
|
||||
{
|
||||
return this.getCmsContent(this.cmsWidgetName, "BackButtonAccessibleText")
|
||||
},
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
425
src/layouts/vehicle-parts/vehicle-parts.spec.js
Normal file
425
src/layouts/vehicle-parts/vehicle-parts.spec.js
Normal file
|
|
@ -0,0 +1,425 @@
|
|||
// Components
|
||||
import vehicleParts from "@/layouts/vehicle-parts/vehicle-parts.vue";
|
||||
import glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass-part-question";
|
||||
|
||||
// Supporting Files
|
||||
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import { nextTick } from "vue";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import { useMainStore } from "@/store";
|
||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||
settleAllPromises: jest.fn(),
|
||||
}));
|
||||
|
||||
// Mock fetchCmsContentForPage
|
||||
jest.mock("@/helpers/cms-content-helper", () => ({
|
||||
fetchCmsContentForPage: jest.fn(),
|
||||
}));
|
||||
|
||||
useMainStore().getters = {
|
||||
pageData: jest.fn(),
|
||||
damage: {
|
||||
isRepair: false,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
||||
const basePartResponse = {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassName: "Stationary",
|
||||
glassLocation: "Rear",
|
||||
parts: [
|
||||
{
|
||||
partNumber: "DB12209GTYN",
|
||||
description: "heated glass, solar, 1 hole",
|
||||
color: "Green Tint",
|
||||
requiresRecalibration: false,
|
||||
requiresCapabilityQuestions: false,
|
||||
childParts: null,
|
||||
},
|
||||
{
|
||||
partNumber: "DB12209YPYNOEM",
|
||||
description: "heated glass, solar, 1 hole",
|
||||
color: "Gray Tint Privacy",
|
||||
requiresRecalibration: false,
|
||||
requiresCapabilityQuestions: false,
|
||||
childParts: null,
|
||||
},
|
||||
],
|
||||
partQuestions: null,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
describe("vehicle-parts.vue", () => {
|
||||
test("Set cms content called on load", async () => {
|
||||
//Arrange
|
||||
useMainStore().pageData = jest.fn();
|
||||
useMainStore().pageData.mockReturnValue(basePartResponse);
|
||||
useMainStore().lineItems = { glassParts: null };
|
||||
|
||||
const { wrapper, apiPromise } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
navigate: jest.fn()
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
issPage: "vehicle-parts",
|
||||
},
|
||||
},
|
||||
useMainStore: {
|
||||
getters: useMainStore().getters,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: "vehicle-parts" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
wrapper.vm.setCmsContent = jest.fn();
|
||||
|
||||
//Assert
|
||||
apiPromise.finally(() => {
|
||||
expect(wrapper.vm.setCmsContent).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test("PageData / isRepair populated in Vuex. arePagePrerequisitesValid should be true ", async () => {
|
||||
//Arrange
|
||||
useMainStore().pageData = jest.fn();
|
||||
useMainStore().pageData.mockReturnValue(basePartResponse);
|
||||
useMainStore().lineItems = { glassParts: null };
|
||||
useMainStore().damage.isRepair = false;
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
issPage: "vehicle-parts",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: "vehicle-parts" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
await nextTick();
|
||||
|
||||
//Assert
|
||||
expect(arePagePrerequisitesValid).toBe(true);
|
||||
});
|
||||
|
||||
test("Initial data, should populate this.selectedGlassParts", async () => {
|
||||
//Arrange
|
||||
useMainStore().pageData = jest.fn();
|
||||
useMainStore().pageData.mockReturnValue(basePartResponse);
|
||||
useMainStore().lineItems = { glassParts: [{ partNumber: "DB12209YPYNOEM" }] };
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
issPage: "vehicle-parts",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: "vehicle-parts" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
await nextTick();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.selectedGlassParts).toEqual({
|
||||
"Rear-Stationary": {
|
||||
partNumber: "DB12209YPYNOEM",
|
||||
description: "heated glass, solar, 1 hole",
|
||||
color: "Gray Tint Privacy",
|
||||
requiresRecalibration: false,
|
||||
requiresCapabilityQuestions: false,
|
||||
childParts: null,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("User had part questions > navigateBack triggers a router.navigateWithoutSaving change with correct scenario", async () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
issPage: "vehicle-parts",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: "vehicle-parts" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
wrapper.vm.navigateBack();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
|
||||
navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS,
|
||||
wrapper.vm.$route
|
||||
);
|
||||
});
|
||||
|
||||
test("User did not have part questions > navigateBack triggers a router.navigate change with correct scenario", async () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
issPage: "vehicle-parts",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: "vehicle-parts" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
wrapper.vm.navigateBack();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
|
||||
navigationScenarios.CLICKED_BACK_WITH_NO_VIN_NOR_QUESTIONS,
|
||||
wrapper.vm.$route
|
||||
);
|
||||
});
|
||||
|
||||
test("ForwardButtonAction triggers a router.navigate change if there are child part questions", async () => {
|
||||
//Arrange
|
||||
useMainStore().pageData = jest.fn();
|
||||
useMainStore().pageData.mockReturnValue({
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassName: "Single",
|
||||
glassLocation: "Windshield",
|
||||
parts: [
|
||||
{
|
||||
partNumber: "FW03861GTYN",
|
||||
description:
|
||||
"rain sensor, heated glass, auto dimming mirror, solar, 3rd visor band, condensation sensor",
|
||||
color: "Green Tint",
|
||||
requiresRecalibration: false,
|
||||
requiresCapabilityQuestions: false,
|
||||
recalibrationType: null,
|
||||
childParts: null,
|
||||
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,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
partQuestions: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
useMainStore().lineItems = { glassParts: {} };
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
issPage: "vehicle-parts",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.setData({
|
||||
selectedGlassParts: { "Rear-Stationary": { partNumber: "FW03861GTYN" } },
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: "vehicle-parts" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("ForwardButtonAction triggers a router.navigate change if there are capability questions", async () => {
|
||||
//Arrange
|
||||
useMainStore().pageData = jest.fn();
|
||||
useMainStore().pageData.mockReturnValue({
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassName: "Single",
|
||||
glassLocation: "Windshield",
|
||||
parts: [
|
||||
{
|
||||
partNumber: "DB12209GTYN",
|
||||
description: "heated glass, solar, 1 hole",
|
||||
color: "Green Tint",
|
||||
requiresRecalibration: false,
|
||||
requiresCapabilityQuestions: true,
|
||||
childParts: null,
|
||||
},
|
||||
],
|
||||
partQuestions: null,
|
||||
},
|
||||
],
|
||||
});
|
||||
useMainStore().lineItems = { glassParts: {} };
|
||||
useMainStore().vehicle = { carId: "TEST_CAR_ID" };
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
issPage: "vehicle-parts",
|
||||
},
|
||||
},
|
||||
actionList: [
|
||||
{
|
||||
actionName: "getCapabilityQuestions",
|
||||
data: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.setData({
|
||||
selectedGlassParts: { "Rear-Stationary": { partNumber: "DB12209GTYN" } },
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { issPage: "vehicle-parts" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {} }) {
|
||||
//Mock api responses
|
||||
const apiResponses = {
|
||||
cmsContent: {
|
||||
SiteSubHeaderWidget: pageHeaderWidgetHeaderText,
|
||||
VehicleBannerWidget: {
|
||||
GenericVehicleImage:
|
||||
"https://digitalisscms-dev.safelite.com/images/default-source/default-album/blurred-image.jpg?sfvrsn=a6ce3034_3",
|
||||
},
|
||||
FunnelHeaderWidget: {
|
||||
LogoImage:
|
||||
"https://digitalisscms-dev.safelite.com/images/default-source/default-album/safelite-logo.svg?sfvrsn=45e7ed06_3",
|
||||
},
|
||||
ColorQuestionWidget: "Please choose your rear window tint color",
|
||||
FeatureQuestionWidget: "Ok no choose features",
|
||||
AlertWidget: {
|
||||
BodyText: "Please choose your tint color",
|
||||
HeadlineText: "Just a few more steps to go",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const store = useMainStore();
|
||||
let actionResult = [];
|
||||
store.getCapabilityQuestions.mockImplementation(() => Promise.resolve({data: actionResult,}));
|
||||
|
||||
const apiPromise = Promise.resolve(apiResponses);
|
||||
|
||||
settleAllPromises.mockImplementation(() => apiPromise);
|
||||
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
|
||||
|
||||
|
||||
const mountOptions = getMountOptions(mountOptionsMockData);
|
||||
const wrapper = shallowMount(vehicleParts, mountOptions);
|
||||
|
||||
const partQuestionRearWrapper = wrapper.findComponent({ name: "glassPartQuestion" });
|
||||
partQuestionRearWrapper.vm.initializeComponent = glassPartQuestion.methods.initializeComponent;
|
||||
|
||||
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
||||
wrapper.vm.$refs.siteFooter.removeLoader = jest.fn();
|
||||
|
||||
return { wrapper, apiPromise };
|
||||
}
|
||||
222
src/layouts/vehicle-parts/vehicle-parts.vue
Normal file
222
src/layouts/vehicle-parts/vehicle-parts.vue
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
<template>
|
||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
|
||||
<div class="page-container-grouped-styles vehicle-parts">
|
||||
<siteHeader ref="siteHeader" cmsWidgetName="SiteHeaderWidget" />
|
||||
<vehicleBanner
|
||||
ref="vehicleBanner"
|
||||
cmsWidgetName="VehicleBannerWidget"
|
||||
:displayGenericVehicleImage="false" />
|
||||
<siteSubHeader ref="siteSubHeader" cmsWidgetName="SiteSubHeaderWidget" />
|
||||
<div class="fade-on-route-transition sub-container make-tall">
|
||||
<div class="prevent-squish my-5">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<alert
|
||||
class="rounded border-0 shadow-sm"
|
||||
alertClass="alert-warning"
|
||||
cmsWidgetName="AlertWidget"
|
||||
:isDismissible="false" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(item, i) in PartsOrQuestions" :key="i">
|
||||
<!-- Render horizontal lines if there is multi-glass (aka if i > 0) -->
|
||||
<hr v-if="i > 0" />
|
||||
<glassPartQuestion
|
||||
:ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`"
|
||||
v-model="selectedGlassParts[item.glassLocation + '-' + item.glassName]"
|
||||
:glassLocation="item.glassLocation"
|
||||
:glassName="item.glassName"
|
||||
:colorAnswers="item.colorAnswers"
|
||||
:alreadyPopulatedPartsData="alreadyPopulatedPartsData" />
|
||||
</div>
|
||||
<siteFooter
|
||||
cmsWidgetName="siteFooterWidget"
|
||||
ref="siteFooter"
|
||||
:isForwardActionDisabled="isForwardActionDisabled"
|
||||
@tempButtonClicked="() => handleTempButtonClicked(this)"
|
||||
@back-click="navigateBack"
|
||||
@ForwardClicked="forwardButtonAction" />
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Components
|
||||
import glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass-part-question";
|
||||
import siteHeader from "@/common-components/site-header/site-header";
|
||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||
import siteSubHeader from "@/common-components/site-sub-header/site-sub-header";
|
||||
import siteFooter from "@/common-components/site-footer/site-footer";
|
||||
import alert from "@/ux-components/alert/alert";
|
||||
|
||||
// Supporting Files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { issPageValues } from "@/router/router-constants/issPage-values";
|
||||
import { Form } from "vee-validate";
|
||||
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||
|
||||
export default {
|
||||
name: "vehicle-parts",
|
||||
mixins: [vehicleQuestionsMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.issPage);
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: "cmsContent",
|
||||
promise: cmsContentPromise,
|
||||
},
|
||||
];
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
|
||||
// Glass Part Question dynamic component
|
||||
Object.keys(vm.$refs)
|
||||
.filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined)
|
||||
.forEach((c) =>
|
||||
vm.$refs[c][0].initializeComponent({
|
||||
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
|
||||
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget,
|
||||
})
|
||||
);
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedGlassParts: {},
|
||||
alertWidgetData: Object,
|
||||
alreadyPopulatedPartsData: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
isForwardActionDisabled() {
|
||||
return (
|
||||
this.selectedGlassPartNumbers.length !== this.PartsFromApi.partsOrQuestions.length
|
||||
);
|
||||
},
|
||||
selectedGlassPartNumbers() {
|
||||
// Compile all selected parts from the page.
|
||||
const numberArray = [];
|
||||
for (let glassPart of Object.values(this.selectedGlassParts)) {
|
||||
if (glassPart?.partNumber) {
|
||||
numberArray.push(glassPart.partNumber);
|
||||
}
|
||||
}
|
||||
return numberArray;
|
||||
},
|
||||
PartsOrQuestions() {
|
||||
const partsData = this.PartsFromApi;
|
||||
|
||||
// Map API result data, to vehicle-parts data structure
|
||||
const mappedData = partsData.partsOrQuestions.map((g) => {
|
||||
return {
|
||||
glassName: g.glassName,
|
||||
glassLocation: g.glassLocation,
|
||||
colorAnswers: g.parts?.reduce((arr, p) => {
|
||||
arr.push({
|
||||
ColorAnswerText: p.color,
|
||||
FeatureAnswers: [
|
||||
{
|
||||
FeatureAnswerText:
|
||||
p.description === "" ? p.color : p.description,
|
||||
PartNumber: p.partNumber,
|
||||
},
|
||||
],
|
||||
});
|
||||
return arr;
|
||||
}, []),
|
||||
};
|
||||
});
|
||||
|
||||
return mappedData;
|
||||
},
|
||||
|
||||
PartsFromApi() {
|
||||
return this.mainStore.pageData(issPageValues.VEHICLE_PARTS);
|
||||
},
|
||||
|
||||
RefPrefix() {
|
||||
return "partQuestion";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
// Check if isRepair is populated and if the pageData we need is here (Parts data)
|
||||
return (
|
||||
this.mainStore.damage.isRepair != null &&
|
||||
this.mainStore.pageData(issPageValues.VEHICLE_PARTS) &&
|
||||
Object.keys(this.mainStore.pageData(issPageValues.VEHICLE_PARTS)).length !== 0
|
||||
);
|
||||
},
|
||||
async forwardButtonAction() {
|
||||
const matchedParts = [];
|
||||
|
||||
// Match them to the parts from the API.
|
||||
for (let [key, value] of Object.entries(this.PartsFromApi.partsOrQuestions)) {
|
||||
for (let [partKey, partValue] of Object.entries(value.parts)) {
|
||||
const currentPart = this.PartsFromApi.partsOrQuestions[key].parts[partKey];
|
||||
|
||||
const isMatched = this.selectedGlassPartNumbers.some(
|
||||
(p) => p === currentPart.partNumber
|
||||
);
|
||||
|
||||
if (isMatched) {
|
||||
matchedParts.push({
|
||||
glassLocation: value.glassLocation,
|
||||
glassName: value.glassName,
|
||||
parts: [currentPart],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// If no parts could be matched, throw an error (isForwardActionDisabled is based off of matchedParts)
|
||||
if (this.isForwardActionDisabled) {
|
||||
this.$refs.funnelFooter.removeLoader();
|
||||
throw new Error("Could not match any parts to the selected parts");
|
||||
}
|
||||
|
||||
await this.mainStore.resetMoldingAndCapabilityQuestionAnswersIfNeeded(matchedParts);
|
||||
|
||||
this.navigateForward(matchedParts, null);
|
||||
},
|
||||
|
||||
LoadInitialPartsData() {
|
||||
const partsData = this.PartsFromApi;
|
||||
this.alreadyPopulatedPartsData =
|
||||
this.mainStore.lineItems.glassParts === null
|
||||
? []
|
||||
: this.mainStore.lineItems.glassParts;
|
||||
|
||||
partsData.partsOrQuestions.map((g) => {
|
||||
// If the part is already populated, use the value from the store and populate the v-model.
|
||||
Object.keys(this.alreadyPopulatedPartsData).forEach((key) => {
|
||||
const partNumber = this.alreadyPopulatedPartsData[key].partNumber;
|
||||
g.parts.forEach((p) => {
|
||||
if (p.partNumber === partNumber) {
|
||||
this.selectedGlassParts[g.glassLocation + "-" + g.glassName] = p;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.LoadInitialPartsData();
|
||||
},
|
||||
components: {
|
||||
Form,
|
||||
glassPartQuestion,
|
||||
siteHeader,
|
||||
vehicleBanner,
|
||||
siteSubHeader,
|
||||
siteFooter,
|
||||
alert,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -6,8 +6,8 @@ import { useMainStore } from "@/store";
|
|||
describe("year-question.vue", () => {
|
||||
test("Selected year is emitted upon selection.", async () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({ modelValueProp: "2020" });
|
||||
const yearToSelect = "2021";
|
||||
const { wrapper } = setupMocks({ modelValueProp: 2020 });
|
||||
const yearToSelect = 2021;
|
||||
|
||||
//Act
|
||||
wrapper.setValue({ modelValue: yearToSelect });
|
||||
|
|
@ -15,7 +15,7 @@ describe("year-question.vue", () => {
|
|||
|
||||
//Assert
|
||||
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([
|
||||
{ modelValue: "2021" },
|
||||
{ modelValue: 2021 },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -24,7 +24,7 @@ describe("year-question.vue", () => {
|
|||
test("Data from store api are used as radio question answers.", async () => {
|
||||
//Arrange
|
||||
const { wrapper, cmsContent } = setupMocks({
|
||||
dataFromStoreApi: ["2023", "2022", "2021"],
|
||||
dataFromStoreApi: [2023, 2022, 2021],
|
||||
});
|
||||
|
||||
//Act
|
||||
|
|
@ -46,7 +46,7 @@ describe("year-question.vue", () => {
|
|||
|
||||
|
||||
function setupMocks({
|
||||
modelValueProp = "1900",
|
||||
modelValueProp = 1900,
|
||||
cmsQuestionText = "CMS text goes here",
|
||||
dataFromStoreApi = [],
|
||||
}) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
};
|
||||
},
|
||||
props: {
|
||||
modelValue: String,
|
||||
modelValue: Number,
|
||||
cmsWidgetName: String,
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { issPageValues } from "@/router/router-constants/issPage-values";
|
||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||
import { useMainStore } from "../store";
|
||||
import { useMainStore } from "@/store";
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
|
|
@ -405,9 +405,7 @@ export default {
|
|||
// mimic part-questions page data for consistency
|
||||
for (let partOrQuestion of partsOrQuestions) {
|
||||
if (this.hasCapabilityQuestions([partOrQuestion])) {
|
||||
let capabilityQuestionsForGlassLocation = (
|
||||
await self.mainStore.getCapabilityQuestions(useMainStore().vehicle.carId, partOrQuestion.parts[0].partNumber)
|
||||
).data;
|
||||
let capabilityQuestionsForGlassLocation = (await useMainStore().getCapabilityQuestions(useMainStore().vehicle.carId, partOrQuestion.parts[0].partNumber)).data;
|
||||
|
||||
capabilityQuestionsForGlassLocation.forEach((question) => {
|
||||
question.answers = question.answers.map((answer) => {
|
||||
|
|
@ -434,7 +432,7 @@ export default {
|
|||
const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions);
|
||||
|
||||
// save to store lineItems.glassParts
|
||||
self.mainStore.updateGlassParts(collectedGlassParts);
|
||||
useMainStore().updateGlassParts(collectedGlassParts);
|
||||
|
||||
self.$router.navigate(self.navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,self.$route);
|
||||
}
|
||||
|
|
|
|||
2343
src/mixins/vehicle-questions-mixin.spec.js
Normal file
2343
src/mixins/vehicle-questions-mixin.spec.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -57,12 +57,6 @@ const getDefaultState = () => {
|
|||
isVerified: false,
|
||||
},
|
||||
},
|
||||
serviceLocation: {
|
||||
address: null,
|
||||
city: null,
|
||||
state: null,
|
||||
zipCode: null,
|
||||
},
|
||||
referralNumber: null,
|
||||
referralDate: null,
|
||||
accountNumber: 0,
|
||||
|
|
@ -96,6 +90,7 @@ export const useMainStore = defineStore({
|
|||
getters: {
|
||||
vehicle: (state) => state.order.vehicle,
|
||||
damage: (state) => state.order.damage,
|
||||
lineItems: (state) => state.order.lineItems,
|
||||
eventBusItem: (state) => ( eventCategory, eventSubCategory) => {
|
||||
|
||||
const matchedEvent = state.applicationUser.eventBus.find(
|
||||
|
|
@ -359,12 +354,38 @@ export const useMainStore = defineStore({
|
|||
this.applicationUser.pageData[issPageValues.CAPABILITY_QUESTIONS] = null;
|
||||
},
|
||||
|
||||
resetSupportingItemsState() {
|
||||
this.order.lineItems.supportingItems = null;
|
||||
},
|
||||
|
||||
resetDamageState() {
|
||||
this.order.damage.isRepair = null;
|
||||
this.order.damage.numberOfChips = null;
|
||||
this.order.damage.glassToReplace = null;
|
||||
},
|
||||
|
||||
resetMoldingAndCapabilityQuestionAnswersIfNeeded(matchedParts) {
|
||||
const partsOrQuestionsDataToCompareWith =
|
||||
this.pageData(issPageValues.MOLDING_QUESTIONS)?.partsOrQuestions ??
|
||||
this.pageData(issPageValues.CAPABILITY_QUESTIONS)?.partsOrQuestions ??
|
||||
[];
|
||||
|
||||
const previouslySelectedPartNumbers = getAllPartNumbers(partsOrQuestionsDataToCompareWith);
|
||||
const currentlySelectedPartNumbers = getAllPartNumbers(matchedParts);
|
||||
|
||||
const haveSelectedVehiclePartsChanged =
|
||||
previouslySelectedPartNumbers !== currentlySelectedPartNumbers;
|
||||
|
||||
if (haveSelectedVehiclePartsChanged) {
|
||||
this.updateGlassParts(null);
|
||||
this.resetSupportingItemsState();
|
||||
this.updateMoldingQuestionAnswers(null);
|
||||
this.updateCapabilityQuestionAnswers(null);
|
||||
this.updatePageData({ page: issPageValues.MOLDING_QUESTIONS, data: null});
|
||||
this.updatePageData({ page: issPageValues.CAPABILITY_QUESTIONS, data: null});
|
||||
}
|
||||
},
|
||||
|
||||
updateVehicleYear(year) {
|
||||
if(this.order.vehicle.year != year)
|
||||
{
|
||||
|
|
@ -731,3 +752,14 @@ function convertGlassPieceNamingFromApi(glassArray) {
|
|||
return glassArray;
|
||||
}
|
||||
|
||||
function getAllPartNumbers(partsOrQuestions) {
|
||||
return partsOrQuestions[0]?.parts
|
||||
? [...partsOrQuestions]
|
||||
.map((glass) => glass.parts)
|
||||
.flat()
|
||||
.map((part) => part.partNumber)
|
||||
.filter((partNumber) => !partNumber.toUpperCase().includes("FEE"))
|
||||
.sort()
|
||||
.join(",")
|
||||
: [];
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue