Merge branch 'develop' into feature/CSR-514
This commit is contained in:
commit
c55d068edd
17 changed files with 659 additions and 311 deletions
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
<script>
|
||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
import { useValidateForm } from "vee-validate";
|
||||
|
||||
export default {
|
||||
name: "questionChain",
|
||||
|
|
@ -32,38 +33,46 @@ export default {
|
|||
validationRules: String,
|
||||
modelValue: Array,
|
||||
partIndex: Number,
|
||||
key: String,
|
||||
},
|
||||
created() {
|
||||
async created() {
|
||||
// validate form upon create to prevent out of sync / persistent valid states
|
||||
await useValidateForm(); // do a test validation check, without triggering full validation
|
||||
|
||||
this.questionData.partQuestions.map((q, i) => {
|
||||
let answerPair = [];
|
||||
const eachQuestion = {
|
||||
questionText: q.questionText,
|
||||
questionSequence: q.questionSequence,
|
||||
answers: q.answers.map((a) => {
|
||||
answerPair.push(a.nextQuestionSequence ? a.nextQuestionSequence : a.answerResult);
|
||||
return {
|
||||
Text: a.answerText,
|
||||
// Name will either be nextQuestionSequence or answerResult
|
||||
// Name will be used by list-button as the input value.
|
||||
// It must be a single string or number, so concatenating together a string with
|
||||
// 4 pieces of data separated by pipe characters:
|
||||
// question number|type of answer|answer value|answer text
|
||||
Name: a.nextQuestionSequence ?
|
||||
q.questionSequence + "|nextQuestion|" + a.nextQuestionSequence + "|" + a.answerText :
|
||||
q.questionSequence + "|answer|" + a.answerResult + "|" + a.answerText,
|
||||
nextQuestionSequence: a.nextQuestionSequence,
|
||||
}
|
||||
}),
|
||||
answerSelected: "",
|
||||
const question = {
|
||||
questionText: q.questionText,
|
||||
questionSequence: q.questionSequence,
|
||||
answers: q.answers.map((a) => {
|
||||
answerPair.push(a.nextQuestionSequence ? a.nextQuestionSequence : a.answerResult);
|
||||
return {
|
||||
Text: a.answerText + " (" + a.answerResult + a.nextQuestionSequence + ")",
|
||||
// Name will either be nextQuestionSequence or answerResult
|
||||
// Name will be used by list-button as the input value.
|
||||
// It must be a single string or number, so concatenating together a string with
|
||||
// 4 pieces of data separated by pipe characters:
|
||||
// question number|type of answer|answer value|answer text
|
||||
Name: a.nextQuestionSequence ?
|
||||
q.questionSequence + "|nextQuestion|" + a.nextQuestionSequence + "|" + a.answerText :
|
||||
q.questionSequence + "|answer|" + a.answerResult + "|" + a.answerText,
|
||||
nextQuestionSequence: a.nextQuestionSequence,
|
||||
}
|
||||
}),
|
||||
answerSelected: "",
|
||||
};
|
||||
eachQuestion.answerPair = answerPair;
|
||||
this.questions.push(eachQuestion);
|
||||
question.answerPair = answerPair;
|
||||
if (!q.suppressQuestion) {
|
||||
this.questions.push(question);
|
||||
}
|
||||
});
|
||||
// set this.currentQuestionNum to first valid question
|
||||
this.currentQuestionNum = this.questions[1].questionSequence;
|
||||
},
|
||||
computed: {
|
||||
selectedValue: {
|
||||
get: function() {
|
||||
return "";
|
||||
return this.modelValue[0];
|
||||
},
|
||||
set: function(returnedAnswer) {
|
||||
const isQuestionChainComplete = this.handleReturnedAnswer(returnedAnswer);
|
||||
|
|
@ -78,7 +87,7 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
handleReturnedAnswer(returnedAnswer) { // returns either a final answer or Boolean false
|
||||
handleReturnedAnswer(returnedAnswer) { // this method will return either a final answer or Boolean false
|
||||
if (!returnedAnswer) { return false }
|
||||
|
||||
// Example returnedAnswers:
|
||||
|
|
@ -86,25 +95,25 @@ export default {
|
|||
// "5|answer|DW02104|Yes"
|
||||
|
||||
const returnedAnswerArray = returnedAnswer.split("|");
|
||||
const questionNum = returnedAnswerArray[0];
|
||||
const questionNum = parseInt(returnedAnswerArray[0]);
|
||||
const questionType = returnedAnswerArray[1];
|
||||
const questionAnswer = returnedAnswerArray[2];
|
||||
const questionAnswerText = returnedAnswerArray[3];
|
||||
|
||||
// remove all previous answers after the index of this one in questions
|
||||
this.questions.map((q) => {
|
||||
if ((q.questionSequence > questionNum) || (q.answerPair?.includes(questionAnswer))) {
|
||||
q.answerSelected = "";
|
||||
this.questions.forEach((q) => {
|
||||
// mark this question as "answered"
|
||||
if (q.questionSequence === questionNum) {
|
||||
q.answerSelected = questionAnswerText;
|
||||
q.answerNumber = questionNum;
|
||||
}
|
||||
// remove all previous answers after the index of this one in questions
|
||||
if ((q.questionSequence > questionNum)) {
|
||||
delete q.answerSelected;
|
||||
}
|
||||
return q;
|
||||
});
|
||||
|
||||
// set this question as "answered"
|
||||
this.questions[questionNum].answerSelected = questionAnswerText;
|
||||
this.questions[questionNum].answerNumber = questionNum;
|
||||
|
||||
// update to next question index
|
||||
this.currentQuestionNum = questionType === "nextQuestion" ? parseInt(questionAnswer) : parseInt(questionNum); // update count to display next question
|
||||
this.currentQuestionNum = questionType === "nextQuestion" ? parseInt(questionAnswer) : questionNum; // update count to display next question
|
||||
|
||||
// return false if there's a nextQuestion... or return an object with "final" answers
|
||||
if (questionType === "nextQuestion") {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ const storeMutations = {
|
|||
UPDATE_REFERRAL_DATE: "updateReferralDate",
|
||||
UPDATE_REFERRAL_CORRELATION_ID: "updateReferralCorrelationId",
|
||||
UPDATE_PARENT_ACCT_NUMBER: "updateParentAcctNumber",
|
||||
UPDATE_EON: "updateEON",
|
||||
UPDATE_SAVED_SESSION_ID: "updateSavedSessionId",
|
||||
UPDATE_CRM_CUSTOMER_ID: "updateCrmCustomerId",
|
||||
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ export async function getPageToRouteExistingOrderTo(toRoute = {}, existingHerita
|
|||
if (toRoute.query[queryStrings.START_TYPE] === 'fmg') {
|
||||
// If they have an existing order, return 'heritage' for the page name.
|
||||
if (existingHeritageOrder) {
|
||||
return 'heritage';
|
||||
return fmgPageValues.HERITAGE;
|
||||
}
|
||||
|
||||
return await getLatestPageForRedirection();
|
||||
|
|
@ -61,11 +61,16 @@ async function getLatestPageForRedirection() {
|
|||
// If this is a non-CTA navigation, determine where to send the user based on page prerequisites.
|
||||
// This also works if a user has a 'fmg' start_type query string but no current order.
|
||||
// That shouldn't happen, but it's possible.
|
||||
const vehicleMakeComponent = (await lazyLoadComponent('vehicle-make')()).default;
|
||||
const vehicleModelComponent = (await lazyLoadComponent('vehicle-model')()).default;
|
||||
const vehicleStyleComponent = (await lazyLoadComponent('vehicle-style')()).default;
|
||||
const vehicleDamageComponent = (await lazyLoadComponent('vehicle-damage')()).default;
|
||||
|
||||
const vehicleMakeComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_MAKE);
|
||||
const vehicleModelComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_MODEL);
|
||||
const vehicleStyleComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_STYLE);
|
||||
const vehicleDamageComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_DAMAGE);
|
||||
const estimateComponent = await getLazyLoadedComponent(fmgPageValues.ESTIMATE);
|
||||
const vinLookupComponent = await getLazyLoadedComponent(fmgPageValues.VIN_LOOKUP);
|
||||
const partQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.PART_QUESTIONS);
|
||||
const vehiclePartsComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_PARTS);
|
||||
const moldingQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.MOLDING_QUESTIONS);
|
||||
const capabilityQuestionsComponent = await getLazyLoadedComponent(fmgPageValues.CAPABILITY_QUESTIONS);
|
||||
|
||||
if (!vehicleMakeComponent.methods.arePagePrerequisitesValid()) {
|
||||
return fmgPageValues.VEHICLE_YEAR;
|
||||
|
|
@ -75,15 +80,28 @@ async function getLatestPageForRedirection() {
|
|||
return fmgPageValues.VEHICLE_MODEL;
|
||||
} else if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) {
|
||||
return fmgPageValues.VEHICLE_STYLE;
|
||||
} else if (store.getters.damage.isRepair == null || !store.getters.vehicle.carId) {
|
||||
} else if (!estimateComponent.methods.arePagePrerequisitesValid()) {
|
||||
return fmgPageValues.VEHICLE_DAMAGE;
|
||||
} else {
|
||||
if (store.getters.vehicle.vin) {
|
||||
if (capabilityQuestionsComponent.methods.arePagePrerequisitesValid()) {
|
||||
return fmgPageValues.CAPABILITY_QUESTIONS;
|
||||
}
|
||||
else if (moldingQuestionsComponent.methods.arePagePrerequisitesValid()) {
|
||||
return fmgPageValues.MOLDING_QUESTIONS;
|
||||
}
|
||||
else if (vehiclePartsComponent.methods.arePagePrerequisitesValid()) {
|
||||
return fmgPageValues.VEHICLE_PARTS;
|
||||
}
|
||||
else if (partQuestionsComponent.methods.arePagePrerequisitesValid()) {
|
||||
return fmgPageValues.PART_QUESTIONS;
|
||||
}
|
||||
else if (vinLookupComponent.methods.arePagePrerequisitesValid()) {
|
||||
return fmgPageValues.VIN_LOOKUP;
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
return fmgPageValues.ESTIMATE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -126,4 +144,8 @@ function isVinRelatedPage(toRoute) {
|
|||
fmgPageValue === fmgPageValues.ADDRESS_LOOKUP ||
|
||||
fmgPageValue === fmgPageValues.ADDRESS_VEHICLES ||
|
||||
fmgPageValue === fmgPageValues.ESTIMATE;
|
||||
}
|
||||
|
||||
async function getLazyLoadedComponent(pageName) {
|
||||
return (await lazyLoadComponent(pageName)()).default;
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import { storeActions } from "@/constants/store-actions";
|
|||
import { setupMocksForJsFiles, getMockOrderInfo } from "@/helpers/unit-test-helper.js";
|
||||
import { externalUrls } from "@/router/router-constants/externalUrl-values";
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||
|
||||
import store from "@/store";
|
||||
import router from "@/router";
|
||||
|
|
@ -15,278 +16,269 @@ jest.mock("@/router/dynamic-routing/component-loader.js", () => ({
|
|||
}));
|
||||
|
||||
describe("getPageToRouteExistingOrderTo", () => {
|
||||
|
||||
test("getPageToRouteExistingOrderTo, should return vehicle-year", async () => {
|
||||
test("should return vehicle-year", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
lazyLoadComponent
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: false
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe('vehicle-year');
|
||||
expect(result).toBe(fmgPageValues.VEHICLE_YEAR);
|
||||
});
|
||||
|
||||
test("getPageToRouteExistingOrderTo, should return vehicle-model", async () => {
|
||||
test("should return vehicle-make", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
lazyLoadComponent
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: false
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe('vehicle-model');
|
||||
expect(result).toBe(fmgPageValues.VEHICLE_MAKE);
|
||||
});
|
||||
|
||||
test("getPageToRouteExistingOrderTo, should return vehicle-damage", async () => {
|
||||
test("should return vehicle-model", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
lazyLoadComponent
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
store.getters.damage.isRepair = undefined;
|
||||
store.getters.vehicle.carId = 'C00000';
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||
[fmgPageValues.VEHICLE_STYLE]: false,
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe('vehicle-damage');
|
||||
expect(result).toBe(fmgPageValues.VEHICLE_MODEL);
|
||||
});
|
||||
|
||||
test("getPageToRouteExistingOrderTo, should return vin-lookup", async () => {
|
||||
test("should return vehicle-style", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
lazyLoadComponent
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
store.getters.damage.isRepair = true;
|
||||
store.getters.vehicle.carId = 'C00000';
|
||||
store.getters.vehicle.vin = "1FADP3F26DL212886"
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||
[fmgPageValues.VEHICLE_DAMAGE]: false,
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe('vin-lookup');
|
||||
expect(result).toBe(fmgPageValues.VEHICLE_STYLE);
|
||||
});
|
||||
|
||||
test("getPageToRouteExistingOrderTo, should return estimate", async () => {
|
||||
test("should return vehicle-damage", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
lazyLoadComponent
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
.mockReturnValueOnce(() => {
|
||||
return {
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||
[fmgPageValues.VEHICLE_DAMAGE]: true,
|
||||
[fmgPageValues.ESTIMATE]: false
|
||||
})
|
||||
|
||||
store.getters.damage.isRepair = true;
|
||||
store.getters.vehicle.carId = 'C00000';
|
||||
store.getters.vehicle.vin = null;
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe('estimate');
|
||||
expect(result).toBe(fmgPageValues.VEHICLE_DAMAGE);
|
||||
});
|
||||
|
||||
test("getPageToRouteExistingOrderTo, existing order, should return heritage", async () => {
|
||||
test("user has YMMS and no vehicle questions > should return vin-lookup", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||
[fmgPageValues.VEHICLE_DAMAGE]: true,
|
||||
[fmgPageValues.ESTIMATE]: true,
|
||||
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
|
||||
[fmgPageValues.MOLDING_QUESTIONS]: false,
|
||||
[fmgPageValues.VEHICLE_PARTS]: false,
|
||||
[fmgPageValues.PART_QUESTIONS]: false,
|
||||
[fmgPageValues.VIN_LOOKUP]: true,
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe(fmgPageValues.VIN_LOOKUP);
|
||||
});
|
||||
|
||||
test("user has YMMS but no questions or carId > should return estimate", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||
[fmgPageValues.VEHICLE_DAMAGE]: true,
|
||||
[fmgPageValues.ESTIMATE]: true,
|
||||
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
|
||||
[fmgPageValues.MOLDING_QUESTIONS]: false,
|
||||
[fmgPageValues.VEHICLE_PARTS]: false,
|
||||
[fmgPageValues.PART_QUESTIONS]: false,
|
||||
[fmgPageValues.VIN_LOOKUP]: false,
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe(fmgPageValues.ESTIMATE);
|
||||
});
|
||||
|
||||
test("user has capability questions and molding questions > should return capability questions", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||
[fmgPageValues.VEHICLE_DAMAGE]: true,
|
||||
[fmgPageValues.ESTIMATE]: true,
|
||||
[fmgPageValues.CAPABILITY_QUESTIONS]: true,
|
||||
[fmgPageValues.MOLDING_QUESTIONS]: true,
|
||||
[fmgPageValues.VEHICLE_PARTS]: false,
|
||||
[fmgPageValues.PART_QUESTIONS]: false,
|
||||
[fmgPageValues.VIN_LOOKUP]: false,
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe(fmgPageValues.CAPABILITY_QUESTIONS);
|
||||
});
|
||||
|
||||
test("user has molding questions and part questions > should return molding questions", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||
[fmgPageValues.VEHICLE_DAMAGE]: true,
|
||||
[fmgPageValues.ESTIMATE]: true,
|
||||
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
|
||||
[fmgPageValues.MOLDING_QUESTIONS]: true,
|
||||
[fmgPageValues.VEHICLE_PARTS]: false,
|
||||
[fmgPageValues.PART_QUESTIONS]: true,
|
||||
[fmgPageValues.VIN_LOOKUP]: false,
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe(fmgPageValues.MOLDING_QUESTIONS);
|
||||
});
|
||||
|
||||
test("user has vehicle parts questions > should return vehicle-parts", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||
[fmgPageValues.VEHICLE_DAMAGE]: true,
|
||||
[fmgPageValues.ESTIMATE]: true,
|
||||
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
|
||||
[fmgPageValues.MOLDING_QUESTIONS]: false,
|
||||
[fmgPageValues.VEHICLE_PARTS]: true,
|
||||
[fmgPageValues.PART_QUESTIONS]: true,
|
||||
[fmgPageValues.VIN_LOOKUP]: false,
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe(fmgPageValues.VEHICLE_PARTS);
|
||||
});
|
||||
|
||||
test("user has part questions > should return part-questions", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {}
|
||||
};
|
||||
|
||||
// Mock out the lazy load calls for all components.
|
||||
mockLazyLoadComponentReturnValues({
|
||||
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||
[fmgPageValues.VEHICLE_DAMAGE]: true,
|
||||
[fmgPageValues.ESTIMATE]: true,
|
||||
[fmgPageValues.CAPABILITY_QUESTIONS]: false,
|
||||
[fmgPageValues.MOLDING_QUESTIONS]: false,
|
||||
[fmgPageValues.VEHICLE_PARTS]: false,
|
||||
[fmgPageValues.PART_QUESTIONS]: true,
|
||||
[fmgPageValues.VIN_LOOKUP]: false,
|
||||
})
|
||||
|
||||
// Act
|
||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||
|
||||
//Assert
|
||||
expect(result).toBe(fmgPageValues.PART_QUESTIONS);
|
||||
});
|
||||
|
||||
test("existing order > should return heritage", async () => {
|
||||
// Arrange
|
||||
const toRoute = {
|
||||
query: {
|
||||
|
|
@ -298,7 +290,7 @@ describe("getPageToRouteExistingOrderTo", () => {
|
|||
const result = await getPageToRouteExistingOrderTo(toRoute, true);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe("heritage");
|
||||
expect(result).toBe(fmgPageValues.HERITAGE);
|
||||
})
|
||||
});
|
||||
|
||||
|
|
@ -401,4 +393,22 @@ describe("navigateToHeritageFunnel", () => {
|
|||
expect(router.navigateToExternalUrl).toHaveBeenCalled();
|
||||
saveOrderFunction.mockRestore();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* `arePagePrerequisitesValidObject` is an object where the keys are fmgPageValue names and the values are booleans that indicate
|
||||
* whether arePagePrerequisitesValid is true or false
|
||||
*/
|
||||
function mockLazyLoadComponentReturnValues(arePagePrerequisitesValidObject = {}) {
|
||||
lazyLoadComponent.mockImplementation((pageName) => {
|
||||
return async () => {
|
||||
return Promise.resolve({
|
||||
default: {
|
||||
methods: {
|
||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(arePagePrerequisitesValidObject[pageName])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,16 +31,12 @@ export function isSavedSessionStillActive() {
|
|||
const savedSessionTimeStamp = new Date(getFunnelCookie().SavedSessionTimeoutDate);
|
||||
const isSavedSessionTimedOut = (new Date(new Date().toUTCString()) > savedSessionTimeStamp);
|
||||
|
||||
if (isSavedSessionTimedOut) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return !isSavedSessionTimedOut;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Function to get the date for the saved session timeout.
|
||||
Function to calculate the date for the saved session timeout.
|
||||
*/
|
||||
|
||||
export function getDateForSavedSessionTimeout() {
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
return true; // TODO - DO TRUE TEST OF PAGEDATA
|
||||
return false; // TODO - DO TRUE TEST OF PAGEDATA
|
||||
// return Object.keys(store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS)).length > 0;
|
||||
},
|
||||
showThisPartQuestionChain(part, i) {
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
if(store.getters.damage.isRepair!=null){
|
||||
if(store.getters.damage.isRepair != null){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
return true; // TODO - DO TRUE TEST OF PAGEDATA
|
||||
return false; // TODO - DO TRUE TEST OF PAGEDATA
|
||||
// return Object.keys(store.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)).length > 0;
|
||||
},
|
||||
showThisPartQuestionChain(part, i) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@
|
|||
<div v-for="(part, i) in partsQuestionsData" :key="i">
|
||||
<questionChain
|
||||
ref="questionChain"
|
||||
v-model="selectedModel"
|
||||
:key="part.key"
|
||||
v-model="selectedAnswer"
|
||||
:questionData="part"
|
||||
:partIndex="i"
|
||||
v-if="showThisPartQuestionChain(part, i)"
|
||||
|
|
@ -91,17 +92,14 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
selectedModel: [],
|
||||
partsQuestionsData: this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS)?.partsOrQuestions.filter((p) => {
|
||||
if (Array.isArray(p.partQuestions) && p.partQuestions.length > 0) {
|
||||
return {
|
||||
glassName: p.glassName,
|
||||
glassLocation: p.glassLocation,
|
||||
partQuestions: p.partQuestions,
|
||||
};
|
||||
}
|
||||
partsQuestionsFromApi: this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS)?.partsOrQuestions.filter((p) => {
|
||||
return Array.isArray(p.partQuestions) && p.partQuestions.length > 0;
|
||||
}),
|
||||
selectedAnswer: [],
|
||||
currentPartNum: 0,
|
||||
newAnswersArray: [],
|
||||
partsQuestionsData: [],
|
||||
foundDuplicateQuestions: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
|
@ -113,10 +111,16 @@ export default {
|
|||
},
|
||||
},
|
||||
mixins: [vehicleQuestionsMixin],
|
||||
mounted() {
|
||||
this.partsQuestionsData = this.partsQuestionsFromApi.map((part, i) => {
|
||||
part.key = part.glassLocation + part.glassName;
|
||||
return part;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
showThisPartQuestionChain(part, i) {
|
||||
if (part.partQuestions?.length < 1) { return false; } // return false if only one partQuestion
|
||||
if (this.currentPartNum === i || part.answerData?.answerResult.length > 0) { return true; }
|
||||
if (part.partQuestions?.length < 1 || part.suppressPart) { return false; } // return false if no partQuestions or if suppressed
|
||||
if (this.currentPartNum === i || part.answerData?.answerResult?.length > 0) { return true; }
|
||||
return false;
|
||||
},
|
||||
backButtonAction() {
|
||||
|
|
@ -136,6 +140,11 @@ export default {
|
|||
};
|
||||
});
|
||||
|
||||
// clear out answerData for future page loads; must occur prior to store save
|
||||
this.partsQuestionsData.forEach((part) => {
|
||||
part.answerData = {};
|
||||
});
|
||||
|
||||
// save to vuex store as order.damage.partQuestionAnswers (array)
|
||||
await this.dispatchStoreAction(this.storeActions.SAVE_PART_QUESTION_ANSWERS, partQuestionAnswersArray, false);
|
||||
|
||||
|
|
@ -146,7 +155,6 @@ export default {
|
|||
});
|
||||
|
||||
const glassNameAndPartsForStore = partsLookup.data.glassNameAndParts;
|
||||
|
||||
const hasGlassLocationWithMultipleParts = this.hasGlassLocationWithMultipleParts(glassNameAndPartsForStore);
|
||||
const hasChildPartQuestions = this.hasChildPartQuestions(glassNameAndPartsForStore);
|
||||
const hasCapabilityQuestions = this.hasCapabilityQuestions(glassNameAndPartsForStore);
|
||||
|
|
@ -175,16 +183,220 @@ export default {
|
|||
}
|
||||
},
|
||||
arePagePrerequisitesValid() {
|
||||
return Object.keys(store.getters.pageData(fmgPageValues.PART_QUESTIONS)).length > 0;
|
||||
const partQuestionsFromPageData = store.getters.pageData(fmgPageValues.PART_QUESTIONS);
|
||||
return partQuestionsFromPageData && Object.keys(partQuestionsFromPageData).length > 0;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
selectedModel(model) {
|
||||
this.partsQuestionsData[model.partIndex].answerData = {
|
||||
answerResult: model.answerResult,
|
||||
answeredQuestions: model.answeredQuestions,
|
||||
selectedAnswer(answer) {
|
||||
// when selectedAnswer updates, user has completed this part's question chain and has a final answer
|
||||
// (does not get run for each invididual question's answer, only when
|
||||
// all relevent questions for the current part have been answered)
|
||||
const glassPart = this.partsQuestionsData[answer.partIndex];
|
||||
const completeAnsweredQuestions = [...answer.answeredQuestions];
|
||||
const answeredQuestionIndexes = [];
|
||||
|
||||
// examine all the answers returned that were part of the user's journey through question-chain
|
||||
|
||||
// loop through every answered question on currently answered glass part
|
||||
answer.answeredQuestions.forEach((aq) => {
|
||||
|
||||
// gather all the question numbers of the answered questions
|
||||
answeredQuestionIndexes.push(aq.questionNum);
|
||||
|
||||
const answeredQuestionText = aq.questionText.toUpperCase();
|
||||
const answeredQuestionAnswer = aq.selectedAnswerText.toUpperCase();
|
||||
|
||||
// HANDLE DUPLICATE QUESTIONS
|
||||
// loop through all glass parts data (but only examining parts after currently being answered part)
|
||||
this.partsQuestionsData.forEach((glassPart, i) => {
|
||||
|
||||
// restrict duplicate logic to only parts that follow the currently being answered part
|
||||
if (i > answer.partIndex) {
|
||||
|
||||
// loop through this glass part's part questions, looking for a questionText match
|
||||
glassPart.partQuestions.forEach((pq, pqIndex) => {
|
||||
// does pq.questionText match answeredQuestionText? (do we have a duplicate question?)
|
||||
|
||||
if (pq.questionText.toUpperCase() === answeredQuestionText) {
|
||||
// which one of this partQuestions' answers matches our answer?
|
||||
|
||||
let matchedAnswer;
|
||||
pq.answers.forEach((ans, ansIndex) => {
|
||||
delete pq.answers[ansIndex].selected;
|
||||
if (ans.answerText.toUpperCase() === answeredQuestionAnswer) {
|
||||
matchedAnswer = ans;
|
||||
pq.answers[ansIndex].selected = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (matchedAnswer) {
|
||||
const thisAnsweredPartQuestion = glassPart.partQuestions[pqIndex];
|
||||
|
||||
// remove answerData from this glass part
|
||||
delete glassPart.answerData;
|
||||
delete glassPart.suppressPart;
|
||||
|
||||
// Update the key to re-render this part's question-chain component
|
||||
this.partsQuestionsData[i].key = this.partsQuestionsData[i].glassLocation + this.partsQuestionsData[i].glassName + Date.now().toString();
|
||||
|
||||
// handle suppressing downstream in this question chain
|
||||
const rejectedAnswer = thisAnsweredPartQuestion.answers.filter((ans) => {
|
||||
return !ans.selected;
|
||||
});
|
||||
|
||||
if (rejectedAnswer[0].nextQuestionSequence) {
|
||||
glassPart.partQuestions[rejectedAnswer[0].nextQuestionSequence - 1].suppressQuestion = true;
|
||||
}
|
||||
if (matchedAnswer.nextQuestionSequence) {
|
||||
// ensure that accepted answer is NOT suppressed
|
||||
delete glassPart.partQuestions[matchedAnswer.nextQuestionSequence - 1].suppressQuestion;
|
||||
}
|
||||
|
||||
// handle suppressing upstream in this question chain
|
||||
glassPart.partQuestions.forEach((q) => {
|
||||
q.answers.forEach((thisAns) => {
|
||||
// restore any of the answers that formerly led to the duplicated question
|
||||
if (thisAns.originalNextQuestionSequence === pq.questionSequence) {
|
||||
// restore original nextQuestionSequence
|
||||
thisAns.nextQuestionSequence = thisAns.originalNextQuestionSequence;
|
||||
delete thisAns.originalNextQuestionSequence;
|
||||
// restore original answerResult
|
||||
if (thisAns.originalAnswerResult) {
|
||||
thisAns.answerResult = thisAns.originalAnswerResult;
|
||||
delete thisAns.originalAnswerResult;
|
||||
}
|
||||
}
|
||||
// search for any of the answers that lead to the duplicated question
|
||||
if (thisAns.nextQuestionSequence === pq.questionSequence) {
|
||||
// update either the nextQuestionSequence or the answerResult
|
||||
if (matchedAnswer.nextQuestionSequence) {
|
||||
thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence;
|
||||
thisAns.nextQuestionSequence = matchedAnswer.nextQuestionSequence;
|
||||
} else {
|
||||
thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence;
|
||||
thisAns.nextQuestionSequence = null;
|
||||
thisAns.originalAnswerResult = thisAns.originalAnswerResult || thisAns.answerResult;
|
||||
thisAns.answerResult = matchedAnswer.answerResult;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// suppress current question
|
||||
thisAnsweredPartQuestion.suppressQuestion = true;
|
||||
|
||||
const thisGlassPart = "glassPart" + i;
|
||||
if (this.foundDuplicateQuestions[thisGlassPart]) {
|
||||
if (!this.foundDuplicateQuestions[thisGlassPart].includes(thisAnsweredPartQuestion.questionSequence)) {
|
||||
this.foundDuplicateQuestions[thisGlassPart].push(thisAnsweredPartQuestion.questionSequence);
|
||||
}
|
||||
} else {
|
||||
this.foundDuplicateQuestions[thisGlassPart] = [thisAnsweredPartQuestion.questionSequence];
|
||||
}
|
||||
|
||||
// are there any questions left that are not suppressed?
|
||||
const remainingQuestions = glassPart.partQuestions.filter((q) => {
|
||||
return !q.suppressQuestion;
|
||||
});
|
||||
|
||||
if (remainingQuestions.length < 1) {
|
||||
// this is the final answer for this glass part
|
||||
|
||||
// mark this part as completely answered by adding answerData
|
||||
const answeredQuestionObj = {
|
||||
questionText: pq.questionText,
|
||||
selectedAnswerText: matchedAnswer.answerText,
|
||||
questionNum: pq.questionSequence,
|
||||
};
|
||||
// set the answerData as 'already answered'
|
||||
glassPart.answerData = {
|
||||
answerResult: matchedAnswer.nextQuestionSequence ? matchedAnswer.nextQuestionSequence : matchedAnswer.answerResult,
|
||||
answeredQuestions: [answeredQuestionObj],
|
||||
};
|
||||
|
||||
// suppress this glassPart because it has an answer
|
||||
glassPart.suppressPart = true;
|
||||
}
|
||||
|
||||
} // END of if (matchedAnswer)
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// look through all (this part's) part questions for any duplicates that were suppressed;
|
||||
// add them to the list of answered questions if found
|
||||
|
||||
// EX answeredQuestionIndexes: [1,5,11,13]
|
||||
|
||||
// EX this.foundDuplicateQuestions = {
|
||||
// "glassPart1": [1],
|
||||
// "glassPart2": [7, 10]
|
||||
// };
|
||||
|
||||
const thisPartsDupes = this.foundDuplicateQuestions["glassPart" + answer.partIndex];
|
||||
|
||||
thisPartsDupes?.forEach((dupe) => {
|
||||
// dupe is a single integer
|
||||
const dupeQuestion = glassPart.partQuestions[dupe - 1];
|
||||
const dupeQuestionAnswer = dupeQuestion.answers.find((q) => q.selected === true);
|
||||
|
||||
glassPart.partQuestions.forEach((q) => {
|
||||
let includeThisDupeInAnsweredQuestions = false;
|
||||
|
||||
// did one of the answers of this question point to the duplicated question?
|
||||
q.answers.forEach((a) => {
|
||||
if ((dupe === a.originalNextQuestionSequence) &&
|
||||
(answeredQuestionIndexes.includes(q.questionSequence)) &&
|
||||
(a.answerText.toUpperCase() === dupeQuestionAnswer.answerText.toUpperCase())) {
|
||||
includeThisDupeInAnsweredQuestions = true;
|
||||
}
|
||||
});
|
||||
|
||||
// is this q.questionSequnce listed as the duplicated question's nextQuestionSequence?
|
||||
if ((q.questionSequence === dupeQuestionAnswer.nextQuestionSequence) && (answeredQuestionIndexes.includes(q.questionSequence))) {
|
||||
includeThisDupeInAnsweredQuestions = true;
|
||||
}
|
||||
|
||||
if (includeThisDupeInAnsweredQuestions) {
|
||||
completeAnsweredQuestions.push({
|
||||
questionNum: dupeQuestion.questionSequence,
|
||||
questionText: dupeQuestion.questionText,
|
||||
selectedAnswerText: dupeQuestionAnswer.answerText,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// make sure there are no duplicated dupes...
|
||||
const foundInCompleteAnsweredQuestions = new Set();
|
||||
let filteredCompleteAnsweredQuestions = completeAnsweredQuestions.filter(el => {
|
||||
const duplicate = foundInCompleteAnsweredQuestions.has(el.questionText);
|
||||
foundInCompleteAnsweredQuestions.add(el.questionText);
|
||||
return !duplicate;
|
||||
});
|
||||
filteredCompleteAnsweredQuestions = filteredCompleteAnsweredQuestions.sort((a,b) => a.questionNum - b.questionNum);
|
||||
|
||||
// set final answer data for the current answered glass part
|
||||
glassPart.answerData = {
|
||||
answerResult: answer.answerResult,
|
||||
answeredQuestions: filteredCompleteAnsweredQuestions,
|
||||
}
|
||||
|
||||
// this part has been fully answered, so advance to next part's question chain
|
||||
for (let i = answer.partIndex + 1; i < this.partsQuestionsData.length; i++) {
|
||||
// if this part has not yet been fully answered, then make it the current part
|
||||
if (!this.partsQuestionsData[i].answerData?.answerResult) {
|
||||
this.currentPartNum = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.currentPartNum = model.partIndex + 1;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
if(store.getters.vehicle.carId){
|
||||
if (store.getters.vehicle.carId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ export default {
|
|||
);
|
||||
},
|
||||
arePagePrerequisitesValid() {
|
||||
if (store.getters.vehicle.year){
|
||||
if (store.getters.vehicle.year){
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { nextTick } from "vue";
|
|||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import store from "@/store";
|
||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||
|
|
@ -145,7 +146,7 @@ describe("vehicle-parts.vue", () => {
|
|||
test("Initial data, should populate this.glassParts", async () => {
|
||||
|
||||
//Arrange
|
||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
||||
store.getters.pageData.mockReturnValue(basePartResponse);
|
||||
store.getters.lineItems = { glassParts: [{ partNumber: 'DB12209YPYNOEM' }] }
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
|
|
@ -178,12 +179,8 @@ describe("vehicle-parts.vue", () => {
|
|||
expect(wrapper.vm.glassParts).toEqual({ "Rear-Stationary": { "Rear": ['DB12209YPYNOEM'] } });
|
||||
});
|
||||
|
||||
test("BackButtonAction triggers a router.navigate change", async () => {
|
||||
|
||||
test("User had part questions > BackButtonAction triggers a router.navigate change with correct scenario", async () => {
|
||||
//Arrange
|
||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
||||
store.getters.lineItems = { glassParts: null }
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
|
|
@ -196,7 +193,25 @@ describe("vehicle-parts.vue", () => {
|
|||
}
|
||||
},
|
||||
store: {
|
||||
getters: store.getters
|
||||
getters: {
|
||||
pageData: () => {
|
||||
return {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassName: "Stationary",
|
||||
glassLocation: "Rear",
|
||||
parts: null,
|
||||
partQuestions: [{
|
||||
testProperty: "some value"
|
||||
}]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: null
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
|
@ -212,7 +227,46 @@ describe("vehicle-parts.vue", () => {
|
|||
wrapper.vm.backButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_PART_QUESTION_ANSWERS, wrapper.vm.$route);
|
||||
|
||||
});
|
||||
|
||||
test("User did not have part questions > BackButtonAction triggers a router.navigate change with correct scenario", async () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
mountOptionsMockData: {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
navigate: jest.fn()
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
fmgPage: 'vehicle-parts',
|
||||
}
|
||||
},
|
||||
store: {
|
||||
getters: {
|
||||
pageData: () => basePartResponse,
|
||||
lineItems: {
|
||||
glassParts: null
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
//Act
|
||||
vehicleParts.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-parts" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITHOUT_PART_QUESTION_ANSWERS, wrapper.vm.$route);
|
||||
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ import store from "@/store";
|
|||
import { storeMutations } from "@/constants/store-mutations.js";
|
||||
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
import { assertParenthesizedExpression } from "@babel/types";
|
||||
|
||||
export default {
|
||||
name: "vehicle-parts",
|
||||
|
|
@ -126,7 +127,7 @@ export default {
|
|||
return {
|
||||
glassName: g.glassName,
|
||||
glassLocation: g.glassLocation,
|
||||
colorAnswers: g.parts.reduce((arr, p) => {
|
||||
colorAnswers: g.parts?.reduce((arr, p) => {
|
||||
arr.push({
|
||||
ColorAnswerText: p.color,
|
||||
FeatureAnswers: [
|
||||
|
|
@ -155,7 +156,7 @@ export default {
|
|||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
// Check if isRepair is populated and if the pageData we need is here (Parts data)
|
||||
return store.getters.damage.isRepair != null &&
|
||||
return store.getters.damage.isRepair != null && store.getters.pageData(fmgPageValues.VEHICLE_PARTS) &&
|
||||
Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS)).length !== 0;
|
||||
},
|
||||
backButtonAction() {
|
||||
|
|
@ -175,7 +176,6 @@ export default {
|
|||
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(
|
||||
|
|
@ -184,9 +184,9 @@ export default {
|
|||
|
||||
if (isMatched) {
|
||||
matchedParts.push({
|
||||
"glassLocation": value.glassLocation,
|
||||
"glassName": value.glassName,
|
||||
"parts": [currentPart]
|
||||
glassLocation: value.glassLocation,
|
||||
glassName: value.glassName,
|
||||
parts: [currentPart]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,12 +8,12 @@ export default {
|
|||
return partsOrQuestions?.some(pq => pq.parts?.length > 1);
|
||||
},
|
||||
hasChildPartQuestions(partsOrQuestions) {
|
||||
return partsOrQuestions.some(pq => {
|
||||
return partsOrQuestions?.some(pq => {
|
||||
return pq.parts?.some(part => part.childPartQuestions?.length > 0);
|
||||
});
|
||||
},
|
||||
hasCapabilityQuestions(partsOrQuestions) {
|
||||
return partsOrQuestions.some(pq => {
|
||||
return partsOrQuestions?.some(pq => {
|
||||
return pq.parts?.some(part => part.requiresCapabilityQuestions === true);
|
||||
});
|
||||
},
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ const fmgPageValues = {
|
|||
REVEAL: "reveal",
|
||||
ESTIMATE: "estimate",
|
||||
ADDRESS_VEHICLES: "address-vehicles",
|
||||
QUOTE: "quote"
|
||||
QUOTE: "quote",
|
||||
HERITAGE: "heritage"
|
||||
};
|
||||
|
||||
export { fmgPageValues };
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@ const getDefaultState = () => {
|
|||
referralDate: null,
|
||||
referralCorrelationId: null,
|
||||
accountNumber: 0,
|
||||
eon: null
|
||||
},
|
||||
applicationUser: {
|
||||
eventBus: [],
|
||||
|
|
@ -123,7 +124,7 @@ export const mutations = {
|
|||
state.order.damage.glassToReplace = glassToReplace;
|
||||
},
|
||||
updatePartQuestionAnswers(state, answersArray) {
|
||||
state.order.damage.partQuestionAnswers = answersArray;
|
||||
state.order.damage.partQuestionAnswers = answersArray;
|
||||
},
|
||||
updateGlassParts(state, partsData) {
|
||||
state.order.lineItems.glassParts = partsData;
|
||||
|
|
@ -143,6 +144,9 @@ export const mutations = {
|
|||
updateParentAcctNumber(state, parentAcctNumber) {
|
||||
state.order.accountNumber = parentAcctNumber;
|
||||
},
|
||||
updateEON(state, eon) {
|
||||
state.order.eon = eon;
|
||||
},
|
||||
updateIsInsurance(state, isInsurance) {
|
||||
state.order.payment.isInsurance = isInsurance;
|
||||
},
|
||||
|
|
@ -271,6 +275,9 @@ export const mutations = {
|
|||
state.order.lineItems.glassParts = null;
|
||||
state.order.damage.partQuestionAnswers = null;
|
||||
state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null;
|
||||
state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null;
|
||||
state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null;
|
||||
state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null;
|
||||
},
|
||||
resetState(state) {
|
||||
Object.assign(state, getDefaultState());
|
||||
|
|
@ -283,6 +290,14 @@ export const mutations = {
|
|||
state.order.referralNumber = orderInformation.referralNumber;
|
||||
state.order.referralDate = orderInformation.referralDate;
|
||||
state.order.referralCorrelationId = orderInformation.referralCorrelationId;
|
||||
state.order.eon = orderInformation.eon;
|
||||
|
||||
if (state.order.vehicle.vin !== orderInformation.vehicle?.vin) {
|
||||
state.applicationUser.pageData[fmgPageValues.PART_QUESTIONS] = null;
|
||||
state.applicationUser.pageData[fmgPageValues.VEHICLE_PARTS] = null;
|
||||
state.applicationUser.pageData[fmgPageValues.MOLDING_QUESTIONS] = null;
|
||||
state.applicationUser.pageData[fmgPageValues.CAPABILITY_QUESTIONS] = null;
|
||||
}
|
||||
|
||||
state.order.vehicle = Object.assign(state.order.vehicle, {
|
||||
year: orderInformation.vehicle?.year,
|
||||
|
|
@ -542,10 +557,11 @@ export const actions = {
|
|||
},
|
||||
|
||||
// Misc Actions
|
||||
updateStoreWithSaveOrderResponse(context, { referralNumber, referralDate, referralCorrelationId, accountNumber, savedSessionId, crmCustomerId }) {
|
||||
updateStoreWithSaveOrderResponse(context, { referralNumber, referralDate, referralCorrelationId, eon, accountNumber, savedSessionId, crmCustomerId }) {
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
||||
context.commit(storeMutations.UPDATE_EON, eon);
|
||||
context.commit(storeMutations.UPDATE_PARENT_ACCT_NUMBER, accountNumber);
|
||||
context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId);
|
||||
context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
|
||||
|
|
@ -612,10 +628,11 @@ export const actions = {
|
|||
},
|
||||
|
||||
// Misc Actions
|
||||
setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId }) {
|
||||
setReferralInformation(context, { referralNumber, referralDate, referralCorrelationId, eon }) {
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
||||
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
||||
context.commit(storeMutations.UPDATE_EON, eon);
|
||||
},
|
||||
|
||||
GetExperimentsByUser(context, { userId }) {
|
||||
|
|
@ -708,6 +725,7 @@ export const actions = {
|
|||
const damage = context.getters.damage;
|
||||
const order = context.state.order;
|
||||
const applicationUser = context.getters.applicationUser;
|
||||
const lineItems = context.state.order.lineItems;
|
||||
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.SaveOrder.method,
|
||||
|
|
@ -738,6 +756,9 @@ export const actions = {
|
|||
customer: {
|
||||
emailAddress: order.customer.emailAddress,
|
||||
},
|
||||
lineItems: {
|
||||
glassParts: lineItems.glassParts
|
||||
},
|
||||
serviceLocation: {
|
||||
streetAddress: order.serviceLocation.address,
|
||||
city: order.serviceLocation.city,
|
||||
|
|
@ -751,7 +772,6 @@ export const actions = {
|
|||
lastPage: applicationUser.lastPageVisited,
|
||||
crmCustomerId: applicationUser.crmCustomerId,
|
||||
savedSessionId: applicationUser.savedSessionId,
|
||||
|
||||
},
|
||||
});
|
||||
},
|
||||
|
|
@ -766,8 +786,8 @@ export const actions = {
|
|||
accountNumber: accountNumber?.toString()
|
||||
},
|
||||
}).then((response) => {
|
||||
// clear the state if the existing referral number does not equal what is returned from loadOrder
|
||||
if (context.state.order.referralNumber != response.data.referralNumber) {
|
||||
// clear the state if the existing EON does not equal what is returned from loadOrder
|
||||
if (context.state.order.eon && context.state.order.eon != response.data.eon) {
|
||||
context.commit(storeMutations.RESET_STATE);
|
||||
}
|
||||
context.commit(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, response.data);
|
||||
|
|
@ -794,6 +814,7 @@ export const actions = {
|
|||
|
||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
|
||||
//Save new values
|
||||
context.commit(storeMutations.UPDATE_YEAR, year);
|
||||
|
|
@ -814,6 +835,7 @@ export const actions = {
|
|||
|
||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
|
||||
//Save new values
|
||||
context.commit(storeMutations.UPDATE_MAKE, make);
|
||||
|
|
@ -833,6 +855,7 @@ export const actions = {
|
|||
|
||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
|
||||
//Save new values
|
||||
context.commit(storeMutations.UPDATE_MODEL, model);
|
||||
|
|
@ -850,6 +873,7 @@ export const actions = {
|
|||
|
||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
|
||||
//Save new values
|
||||
context.commit(storeMutations.UPDATE_STYLE, style);
|
||||
|
|
@ -880,6 +904,7 @@ export const actions = {
|
|||
//Reset dependent state when changing
|
||||
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
|
||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
|
||||
if (!isSelectedGlassAvailableForVehicle) {
|
||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
|
|
@ -898,6 +923,7 @@ export const actions = {
|
|||
|
||||
if (!isSelectedGlassAvailableForVehicle) {
|
||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
}
|
||||
|
||||
//Save new values
|
||||
|
|
@ -913,6 +939,7 @@ export const actions = {
|
|||
|
||||
if (!isSelectedGlassAvailableForVehicle) {
|
||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
}
|
||||
|
||||
//Save new values
|
||||
|
|
@ -938,6 +965,7 @@ export const actions = {
|
|||
|
||||
if (!isSelectedGlassAvailableForVehicle) {
|
||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
}
|
||||
|
||||
//Save new values
|
||||
|
|
|
|||
|
|
@ -575,12 +575,13 @@ describe("Actions", () => {
|
|||
lastPageVisited: "test-page",
|
||||
crmCustomerId: "xxx-xxx-xxx",
|
||||
savedSessionId: "xxx-xxx-xxx"
|
||||
}
|
||||
},
|
||||
};
|
||||
context.state = {
|
||||
order: {
|
||||
serviceLocation: {},
|
||||
customer: {}
|
||||
customer: {},
|
||||
lineItems: {}
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -793,13 +794,27 @@ describe("Actions", () => {
|
|||
|
||||
|
||||
// Act
|
||||
const payload = { isCarIdDifferent: true, isSelectedGlassAvailableForVehicle: false, vehicleInfo: { carId: 'C010101', vin: "XXXXX" }, registrationInfo: { zipCode: "80020" }, serviceLocationInfo: { state: "CO" }, customerEmail: "test@safleite.com" };
|
||||
const payload = {
|
||||
isCarIdDifferent: true,
|
||||
isSelectedGlassAvailableForVehicle: false,
|
||||
vehicleInfo: {
|
||||
carId: 'C010101', vin: "XXXXX"
|
||||
},
|
||||
registrationInfo: {
|
||||
zipCode: "80020"
|
||||
},
|
||||
serviceLocationInfo: {
|
||||
state: "CO"
|
||||
},
|
||||
customerEmail: "test@safleite.com"
|
||||
};
|
||||
|
||||
actions.saveVinLookup(context, payload);
|
||||
|
||||
// Assert
|
||||
expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||
expect(dispatch).toHaveBeenNthCalledWith(2, storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
expect(dispatch).toHaveBeenNthCalledWith(2, storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
expect(dispatch).toHaveBeenNthCalledWith(3, storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
|
||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_VEHICLE, payload.vehicleInfo);
|
||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_REGISTRATION, payload.registrationInfo);
|
||||
|
|
|
|||
Loading…
Reference in a new issue