Merge pull request #679 from Safelite/rlsmerge/develop-to-quote-page-mvp
Rlsmerge/develop to quote page mvp
This commit is contained in:
commit
3a819af3af
76 changed files with 4592 additions and 2097 deletions
|
|
@ -13,6 +13,8 @@ module.exports = {
|
||||||
"!src/router/**/*.js",
|
"!src/router/**/*.js",
|
||||||
"!src/helpers/unit-test-helper.js",
|
"!src/helpers/unit-test-helper.js",
|
||||||
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
|
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
|
||||||
|
"!src/layouts/molding-questions/**/*.vue",
|
||||||
|
"!src/layouts/capability-questions/**/*.vue",
|
||||||
"!src/layouts/part-questions/**/*.vue",
|
"!src/layouts/part-questions/**/*.vue",
|
||||||
"!src/layouts/reveal/**/*.vue",
|
"!src/layouts/reveal/**/*.vue",
|
||||||
"!src/ux-components/text-link/**/*.vue",
|
"!src/ux-components/text-link/**/*.vue",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
<div class="w-100 d-flex justify-content-center">
|
<div class="w-100 d-flex justify-content-center">
|
||||||
<fieldset class="w-100" :aria-required=isRequired :class="getFieldSetClasses" :role="isMultiSelect ? 'group' : 'radiogroup'" :aria-labelledby="formattedGroupName">
|
<fieldset class="w-100" :aria-required=isRequired :class="getFieldSetClasses" :role="isMultiSelect ? 'group' : 'radiogroup'" :aria-labelledby="formattedGroupName">
|
||||||
<legend class="sr-only" :data-focus-target="formattedGroupName" :id="formattedGroupName" tabindex="-1">
|
<legend class="sr-only" :data-focus-target="formattedGroupName" :id="formattedGroupName" tabindex="-1">
|
||||||
|
{{ questionText }}
|
||||||
{{(isMultiSelect && answers && answers.length > 1) ? 'Select one or more options below.' : 'Select an option below.' }}
|
{{(isMultiSelect && answers && answers.length > 1) ? 'Select one or more options below.' : 'Select an option below.' }}
|
||||||
</legend>
|
</legend>
|
||||||
<div :class="getComponentLoopWrapperClasses" role="application">
|
<div :class="getComponentLoopWrapperClasses" role="application">
|
||||||
|
|
@ -36,7 +37,7 @@
|
||||||
data-test="button"
|
data-test="button"
|
||||||
:validationRules="validationRules"
|
:validationRules="validationRules"
|
||||||
:class="[suppressError ? 'alertError' : '' , isCashOrInsurance ? 'radio-fancy' : '']"
|
:class="[suppressError ? 'alertError' : '' , isCashOrInsurance ? 'radio-fancy' : '']"
|
||||||
:clearOnUnmount="clearOnUnmount"
|
:valueToLogType="valueToLogType"
|
||||||
/>
|
/>
|
||||||
<!-- For nested questions -->
|
<!-- For nested questions -->
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
|
|
@ -93,10 +94,7 @@ export default {
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
suppressError: Boolean,
|
suppressError: Boolean,
|
||||||
useTextForValue: Boolean,
|
useTextForValue: Boolean,
|
||||||
clearOnUnmount: {
|
valueToLogType: String,
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
formattedGroupName() {
|
formattedGroupName() {
|
||||||
|
|
@ -179,7 +177,6 @@ export default {
|
||||||
this.selectedValues = val.value;
|
this.selectedValues = val.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.$emit("isCheckedChanged", val);
|
this.$emit("isCheckedChanged", val);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,12 @@
|
||||||
<menuModal/>
|
<menuModal/>
|
||||||
</div>
|
</div>
|
||||||
<alert
|
<alert
|
||||||
class="position-absolute rounded-0 w-100 border-0 shadow-sm"
|
|
||||||
v-if="displayGlobalAlert"
|
v-if="displayGlobalAlert"
|
||||||
:alertClass="globalAlertMessage.type"
|
class="position-absolute rounded-0 w-100 border-0 shadow-sm"
|
||||||
|
cmsWidgetName="GlobalAlert"
|
||||||
:manualHeadline="globalAlertMessage.messageHeadline"
|
:manualHeadline="globalAlertMessage.messageHeadline"
|
||||||
:manualCopy="globalAlertMessage.messageCopy"
|
:manualCopy="globalAlertMessage.messageCopy"
|
||||||
|
:alertClass="globalAlertMessage.type"
|
||||||
v-bind:isDismissible="globalAlertMessage.isDismissible"
|
v-bind:isDismissible="globalAlertMessage.isDismissible"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="menu-modal-container">
|
<div class="menu-modal-container">
|
||||||
<button class="menu-button" type="button" :class="[isActive ? 'active' : '']" data-bs-toggle="modal" data-bs-target="#footerModal">
|
<button class="menu-button" type="button" :class="[isActive ? 'active' : '']" data-bs-toggle="modal" data-bs-target="#footerModal" aria-label="Hamburger Menu (modal window)">
|
||||||
<div class="bar1"></div>
|
<div class="bar1"></div>
|
||||||
<div class="bar2"></div>
|
<div class="bar2"></div>
|
||||||
<div class="bar3"></div>
|
<div class="bar3"></div>
|
||||||
|
|
@ -8,6 +8,13 @@
|
||||||
</div>
|
</div>
|
||||||
<!-- Modal -->
|
<!-- Modal -->
|
||||||
<div class="modal fade" id="footerModal" tabindex="-1" aria-labelledby="footerModalLabel" aria-hidden="true" v-on="{ 'show.bs.modal' : show, 'hide.bs.modal' : hide }" :style="`height: calc(100% - ${currentFooterAndHeaderHeight}px);`">
|
<div class="modal fade" id="footerModal" tabindex="-1" aria-labelledby="footerModalLabel" aria-hidden="true" v-on="{ 'show.bs.modal' : show, 'hide.bs.modal' : hide }" :style="`height: calc(100% - ${currentFooterAndHeaderHeight}px);`">
|
||||||
|
<div class="menu-modal-container">
|
||||||
|
<button class="menu-button" type="button" :class="[isActive ? 'active' : '']" data-bs-toggle="modal" data-bs-target="#footerModal" aria-label="Hamburger Menu (modal window)">
|
||||||
|
<div class="bar1"></div>
|
||||||
|
<div class="bar2"></div>
|
||||||
|
<div class="bar3"></div>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
<div class="modal-dialog modal-fullscreen">
|
<div class="modal-dialog modal-fullscreen">
|
||||||
<div class="modal-content">
|
<div class="modal-content">
|
||||||
<div class="modal-header visually-hidden">
|
<div class="modal-header visually-hidden">
|
||||||
|
|
@ -100,6 +107,8 @@ export default {
|
||||||
height: calc(100% - 72px);
|
height: calc(100% - 72px);
|
||||||
top: 72px;
|
top: 72px;
|
||||||
border-top: 1px solid $gray-300;
|
border-top: 1px solid $gray-300;
|
||||||
|
overflow-x: visible;
|
||||||
|
overflow-y: visible;
|
||||||
.modal-body {
|
.modal-body {
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
|
@ -114,6 +123,45 @@ export default {
|
||||||
border-top: none;
|
border-top: none;
|
||||||
padding: 2rem;
|
padding: 2rem;
|
||||||
}
|
}
|
||||||
|
.menu-modal-container {
|
||||||
|
position: absolute;
|
||||||
|
padding: 1.47rem 1rem 1.47rem 1.47rem;
|
||||||
|
right: 0;
|
||||||
|
top: -4.5rem;
|
||||||
|
button {
|
||||||
|
border: none;
|
||||||
|
&.menu-button {
|
||||||
|
width: 1.5rem;
|
||||||
|
height: 1.5rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: $white;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0;//Required to prevent 'squish' on iPhone
|
||||||
|
z-index: 1056;
|
||||||
|
.bar1,
|
||||||
|
.bar2,
|
||||||
|
.bar3 {
|
||||||
|
width: 14px;
|
||||||
|
height: 2px;
|
||||||
|
background-color: $blue;
|
||||||
|
margin: 1px 0;
|
||||||
|
transition: 0.25s;
|
||||||
|
}
|
||||||
|
&.active .bar1 {
|
||||||
|
transform: rotate(-45deg) translate(-3px, 3px);
|
||||||
|
}
|
||||||
|
&.active .bar2 {opacity: 0;}
|
||||||
|
&.active .bar3 {
|
||||||
|
transform: rotate(45deg) translate(-3px, -3px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
//.modal-backdrop styles are in common-styles.scss
|
//.modal-backdrop styles are in common-styles.scss
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@
|
||||||
:class="(q.questionSequence === currentQuestionNum) && 'current-question'"
|
:class="(q.questionSequence === currentQuestionNum) && 'current-question'"
|
||||||
:questionText="q.questionText"
|
:questionText="q.questionText"
|
||||||
:answers="q.answers"
|
:answers="q.answers"
|
||||||
:groupName="`${questionData.glassName}-${questionData.glassLocation}-${i}`"
|
:groupName="`${keyString}-${q.questionSequence}`"
|
||||||
v-model="selectedValue"
|
v-model="q.answerSelected"
|
||||||
|
@isCheckedChanged="handleChainCompleted"
|
||||||
isRequired
|
isRequired
|
||||||
:validationRules="validationRules"
|
:validationRules="validationRules"
|
||||||
:clearOnUnmount=false
|
|
||||||
/>
|
/>
|
||||||
</transition>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -19,13 +19,14 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||||
|
import { useValidateForm } from "vee-validate";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "questionChain",
|
name: "questionChain",
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
currentQuestionNum: 1,
|
currentQuestionNum: 0,
|
||||||
questions: [{ "BlankObject": "NOT USED... placeholder for question #0 to simplify indexing"}],
|
questions: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -33,53 +34,59 @@ export default {
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
modelValue: Array,
|
modelValue: Array,
|
||||||
partIndex: Number,
|
partIndex: Number,
|
||||||
|
keyString: String,
|
||||||
},
|
},
|
||||||
created() {
|
async created() {
|
||||||
this.questionData.partQuestions.map((q, i) => {
|
// validate form upon create to prevent out of sync / persistent valid states
|
||||||
let answerPair = [];
|
await useValidateForm(); // do a test validation check, without triggering full validation
|
||||||
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: "",
|
|
||||||
};
|
|
||||||
eachQuestion.answerPair = answerPair;
|
|
||||||
this.questions.push(eachQuestion);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
selectedValue: {
|
|
||||||
get: function() {
|
|
||||||
return "";
|
|
||||||
},
|
|
||||||
set: function(returnedAnswer) {
|
|
||||||
const isQuestionChainComplete = this.handleReturnedAnswer(returnedAnswer);
|
|
||||||
|
|
||||||
if (isQuestionChainComplete) {
|
this.questionData.map((q, i) => {
|
||||||
this.$emit("update:modelValue", isQuestionChainComplete);
|
let answerPair = [];
|
||||||
}
|
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,
|
||||||
|
// 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: q.answerSelected || "",
|
||||||
|
};
|
||||||
|
question.answerPair = answerPair;
|
||||||
|
if (!q.isDuplicateQuestion) {
|
||||||
|
this.questions.push(question);
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
currentQuestion() {
|
|
||||||
return this.questions[this.currentQuestionNum];
|
if (!this.modelValue?.length > 0) {
|
||||||
},
|
// set this.currentQuestionNum to first valid question
|
||||||
|
this.currentQuestionNum = this.questions[0].questionSequence;
|
||||||
|
// scroll the next question into view
|
||||||
|
this.$nextTick(() => {
|
||||||
|
document.querySelector('.current-question').scrollIntoView({behavior: "smooth"});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
handleReturnedAnswer(returnedAnswer) { // returns either a final answer or Boolean false
|
handleChainCompleted(returnedAnswer) {
|
||||||
|
const isQuestionChainComplete = this.handleReturnedAnswer(returnedAnswer.value);
|
||||||
|
|
||||||
|
if (isQuestionChainComplete) {
|
||||||
|
this.$emit("update:modelValue", isQuestionChainComplete);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handleReturnedAnswer(returnedAnswer) { // this method will return either a final answer or Boolean false
|
||||||
if (!returnedAnswer) { return false }
|
if (!returnedAnswer) { return false }
|
||||||
|
|
||||||
// Example returnedAnswers:
|
// Example returnedAnswers:
|
||||||
|
|
@ -87,28 +94,31 @@ export default {
|
||||||
// "5|answer|DW02104|Yes"
|
// "5|answer|DW02104|Yes"
|
||||||
|
|
||||||
const returnedAnswerArray = returnedAnswer.split("|");
|
const returnedAnswerArray = returnedAnswer.split("|");
|
||||||
const questionNum = returnedAnswerArray[0];
|
const questionNum = parseInt(returnedAnswerArray[0]);
|
||||||
const questionType = returnedAnswerArray[1];
|
const questionType = returnedAnswerArray[1];
|
||||||
const questionAnswer = returnedAnswerArray[2];
|
const questionAnswer = returnedAnswerArray[2];
|
||||||
const questionAnswerText = returnedAnswerArray[3];
|
const questionAnswerText = returnedAnswerArray[3];
|
||||||
|
|
||||||
// remove all previous answers after the index of this one in questions
|
this.questions.forEach((q) => {
|
||||||
this.questions.map((q) => {
|
// mark this question as "answered"
|
||||||
if ((q.questionSequence > questionNum) || (q.answerPair?.includes(questionAnswer))) {
|
if (q.questionSequence === questionNum) {
|
||||||
q.answerSelected = "";
|
q.answerSelected = returnedAnswer;
|
||||||
|
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
|
|
||||||
|
|
||||||
// return false if there's a nextQuestion... or return an object with "final" answers
|
// return false if there's a nextQuestion... or return an object with "final" answers
|
||||||
if (questionType === "nextQuestion") {
|
if (questionType === "nextQuestion") {
|
||||||
|
// update to next question index
|
||||||
|
this.currentQuestionNum = parseInt(questionAnswer); // update count to display next question
|
||||||
|
// scroll the next question into view
|
||||||
|
this.$nextTick(() => {
|
||||||
|
document.querySelector('.current-question').scrollIntoView({behavior: "smooth"});
|
||||||
|
})
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
const answeredQuestions = [];
|
const answeredQuestions = [];
|
||||||
|
|
@ -116,30 +126,23 @@ export default {
|
||||||
if (q.answerSelected) {
|
if (q.answerSelected) {
|
||||||
answeredQuestions.push({
|
answeredQuestions.push({
|
||||||
questionText: q.questionText,
|
questionText: q.questionText,
|
||||||
selectedAnswerText: q.answerSelected,
|
selectedAnswerText: q.answerSelected.split("|")[3],
|
||||||
questionNum: q.answerNumber,
|
questionNum: q.questionSequence,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// reset current question index
|
||||||
|
this.currentQuestionNum = 0; // reset count
|
||||||
|
|
||||||
return {
|
return {
|
||||||
answerResult: questionAnswer,
|
answerResult: questionAnswer,
|
||||||
answeredQuestions: answeredQuestions,
|
answeredQuestions: answeredQuestions,
|
||||||
partIndex: this.partIndex,
|
partIndex: this.partIndex,
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
|
||||||
currentQuestion: {
|
|
||||||
handler() {
|
|
||||||
// scrolls page to next active question
|
|
||||||
this.$nextTick(() => {
|
|
||||||
document.querySelector('.current-question').scrollIntoView({behavior: "smooth"});
|
|
||||||
})
|
|
||||||
},
|
|
||||||
deep: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
components: {
|
components: {
|
||||||
buttonQuestion,
|
buttonQuestion,
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@
|
||||||
:aria-disabled="isDisabled"
|
:aria-disabled="isDisabled"
|
||||||
:disabled="isDisabled"
|
:disabled="isDisabled"
|
||||||
:aria-required="isRequired"
|
:aria-required="isRequired"
|
||||||
|
:aria-label="questionText"
|
||||||
:class="[hasIcon ? 'has-icon' : '', iconRight ? 'icon-right' : '']"
|
:class="[hasIcon ? 'has-icon' : '', iconRight ? 'icon-right' : '']"
|
||||||
:validationRules="validationRules"
|
:validationRules="validationRules"
|
||||||
@change="handleChange"
|
@change="handleChange"
|
||||||
|
|
|
||||||
|
|
@ -29,5 +29,8 @@ const GaLabels = {
|
||||||
ADDRESS_LOOKUP: 'Address_Look_up',
|
ADDRESS_LOOKUP: 'Address_Look_up',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const ValueToLogTypes = {
|
||||||
|
LAST_5: "last_5",
|
||||||
|
};
|
||||||
|
|
||||||
export { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents};
|
export { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes };
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,9 @@ const applicationConfig = {
|
||||||
ANALYTICS_SESSION_TIMEOUT_MINUTES: 30,
|
ANALYTICS_SESSION_TIMEOUT_MINUTES: 30,
|
||||||
SAVED_SESSION_TIMEOUT_DAYS: 45,
|
SAVED_SESSION_TIMEOUT_DAYS: 45,
|
||||||
COOKIE_PATH: "/",
|
COOKIE_PATH: "/",
|
||||||
CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT // "Localhost", "Dev", "QA", and "Prod"
|
CURRENT_ENVIRONMENT: process.env.VUE_APP_CURRENT_ENVIRONMENT, // "Localhost", "Dev", "QA", and "Prod"
|
||||||
|
APPLICATION_NAME: "FixMyGlass",
|
||||||
|
SITE_ENTRY_TRIGGER_VALUE: "FixMyGlass"
|
||||||
};
|
};
|
||||||
|
|
||||||
export { applicationConfig };
|
export { applicationConfig };
|
||||||
|
|
@ -59,6 +59,14 @@ const endpoints = {
|
||||||
url: "/parts/api/v1/parts/parts",
|
url: "/parts/api/v1/parts/parts",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
},
|
},
|
||||||
|
GetCapabilityQuestions: {
|
||||||
|
url: "/parts/api/v1/parts/capability-questions",
|
||||||
|
method: "GET"
|
||||||
|
},
|
||||||
|
ApplyCapabilityAnswerToPart: {
|
||||||
|
url: "/parts/api/v1/parts/apply-capability-answer-to-part",
|
||||||
|
method: "POST"
|
||||||
|
},
|
||||||
SaveOrder: {
|
SaveOrder: {
|
||||||
url: "/order/api/v1/order/save",
|
url: "/order/api/v1/order/save",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
|
@ -72,7 +80,7 @@ const endpoints = {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
},
|
},
|
||||||
LogExperimentExposureIfAssigned:{
|
LogExperimentExposureIfAssigned:{
|
||||||
url: "/analytics/api/v1/analytics/log-experiment-exposure",
|
url: "/experiments/api/v1/experiments/log-exposure",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
},
|
},
|
||||||
LogPageView:{
|
LogPageView:{
|
||||||
|
|
@ -90,6 +98,10 @@ const endpoints = {
|
||||||
GetExperimentsByUser: {
|
GetExperimentsByUser: {
|
||||||
url: "/analytics/api/v1/analytics/get-experiments",
|
url: "/analytics/api/v1/analytics/get-experiments",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
},
|
||||||
|
RunExperimentsForTrigger: {
|
||||||
|
url: "/experiments/api/v1/experiments/run",
|
||||||
|
method: "POST"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,5 +6,10 @@ const experimentSettings = {
|
||||||
GOOGLE_CUSTOM_DIMENSION_INDEX: 'Google Custom Dimension Index'
|
GOOGLE_CUSTOM_DIMENSION_INDEX: 'Google Custom Dimension Index'
|
||||||
}
|
}
|
||||||
|
|
||||||
export { experimentUniverses, experimentSettings};
|
const experimentTriggers = {
|
||||||
|
SITE_ENTRY: "SiteEntry",
|
||||||
|
PAGE_ENTRY: "PageEntry"
|
||||||
|
};
|
||||||
|
|
||||||
|
export { experimentUniverses, experimentSettings, experimentTriggers };
|
||||||
|
|
||||||
3
src/constants/header-keys.js
Normal file
3
src/constants/header-keys.js
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export const headerKeys = {
|
||||||
|
EXPERIMENT: "X-Experiment-Data"
|
||||||
|
}
|
||||||
|
|
@ -21,6 +21,8 @@ const storeActions = {
|
||||||
|
|
||||||
GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions",
|
GET_PARTS_OR_QUESTIONS: "getPartsOrQuestions",
|
||||||
GET_PARTS: "getParts",
|
GET_PARTS: "getParts",
|
||||||
|
GET_CAPABILITY_QUESTIONS: "getCapabilityQuestions",
|
||||||
|
GET_PART_FROM_CAPABILITY_QUESTION_ANSWER: "getPartFromCapabilityQuestionAnswer",
|
||||||
SAVE_ORDER: "saveOrder",
|
SAVE_ORDER: "saveOrder",
|
||||||
LOAD_ORDER: "loadOrder",
|
LOAD_ORDER: "loadOrder",
|
||||||
UPDATE_STORE_WITH_SAVE_ORDER_RESPONSE: "updateStoreWithSaveOrderResponse",
|
UPDATE_STORE_WITH_SAVE_ORDER_RESPONSE: "updateStoreWithSaveOrderResponse",
|
||||||
|
|
@ -30,7 +32,9 @@ const storeActions = {
|
||||||
LOG_CUSTOM_EVENT: "logCustomEvent",
|
LOG_CUSTOM_EVENT: "logCustomEvent",
|
||||||
INITIALIZE_SESSION: "initializeSession",
|
INITIALIZE_SESSION: "initializeSession",
|
||||||
GET_EXPERIMENTS_BY_USER: "GetExperimentsByUser",
|
GET_EXPERIMENTS_BY_USER: "GetExperimentsByUser",
|
||||||
|
RUN_EXPERIMENTS_FOR_TRIGGER: "runExperimentsForTrigger",
|
||||||
CLEAR_VIN: "clearVin",
|
CLEAR_VIN: "clearVin",
|
||||||
|
RESET_SAVE_ORDER_PROMISE: "resetSaveOrderPromise",
|
||||||
|
|
||||||
// DEPENDENCY MUTATIONS
|
// DEPENDENCY MUTATIONS
|
||||||
RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies",
|
RESET_VEHICLE_STATE_AND_DEPENDENCIES: "resetVehicleAndDependencies",
|
||||||
|
|
@ -53,6 +57,7 @@ const storeActions = {
|
||||||
SAVE_REGISTRATION_ADDRESS_LOOKUP: "saveRegistrationAddressLookup",
|
SAVE_REGISTRATION_ADDRESS_LOOKUP: "saveRegistrationAddressLookup",
|
||||||
SAVE_GLASS_PARTS: "saveGlassParts",
|
SAVE_GLASS_PARTS: "saveGlassParts",
|
||||||
SAVE_PART_QUESTION_ANSWERS: "savePartQuestionAnswers",
|
SAVE_PART_QUESTION_ANSWERS: "savePartQuestionAnswers",
|
||||||
|
SAVE_CAPABILITY_QUESTION_ANSWERS: "saveCapabilityQuestionAnswers"
|
||||||
};
|
};
|
||||||
|
|
||||||
export { storeActions };
|
export { storeActions };
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,9 @@ const storeMutations = {
|
||||||
UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips",
|
UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips",
|
||||||
UPDATE_GLASS_TO_REPLACE: "updateGlassToReplace",
|
UPDATE_GLASS_TO_REPLACE: "updateGlassToReplace",
|
||||||
UPDATE_PART_QUESTION_ANSWERS: "updatePartQuestionAnswers",
|
UPDATE_PART_QUESTION_ANSWERS: "updatePartQuestionAnswers",
|
||||||
|
UPDATE_CAPABILITY_QUESTION_ANSWERS: "updateCapabilityQuestionAnswers",
|
||||||
UPDATE_GLASS_PARTS: "updateGlassParts",
|
UPDATE_GLASS_PARTS: "updateGlassParts",
|
||||||
|
UPDATE_OTHER_PARTS: "updateOtherParts",
|
||||||
|
|
||||||
UPDATE_REGISTRATION_LICENSE_PLATE : "updateRegistrationLicensePlate",
|
UPDATE_REGISTRATION_LICENSE_PLATE : "updateRegistrationLicensePlate",
|
||||||
UPDATE_REGISTRATION_ADDRESS: "updateRegistrationAddress",
|
UPDATE_REGISTRATION_ADDRESS: "updateRegistrationAddress",
|
||||||
|
|
@ -39,7 +41,8 @@ const storeMutations = {
|
||||||
UPDATE_REFERRAL_DATE: "updateReferralDate",
|
UPDATE_REFERRAL_DATE: "updateReferralDate",
|
||||||
UPDATE_REFERRAL_CORRELATION_ID: "updateReferralCorrelationId",
|
UPDATE_REFERRAL_CORRELATION_ID: "updateReferralCorrelationId",
|
||||||
UPDATE_PARENT_ACCT_NUMBER: "updateParentAcctNumber",
|
UPDATE_PARENT_ACCT_NUMBER: "updateParentAcctNumber",
|
||||||
UPDATE_SAVE_QUOTE_ID: "updateSaveQuoteId",
|
UPDATE_EON: "updateEON",
|
||||||
|
UPDATE_SAVED_SESSION_ID: "updateSavedSessionId",
|
||||||
UPDATE_CRM_CUSTOMER_ID: "updateCrmCustomerId",
|
UPDATE_CRM_CUSTOMER_ID: "updateCrmCustomerId",
|
||||||
|
|
||||||
// EVENT BUS MUTATIONS
|
// EVENT BUS MUTATIONS
|
||||||
|
|
@ -52,12 +55,17 @@ const storeMutations = {
|
||||||
RESET_REGISTRATION_STATE: "resetRegistrationState",
|
RESET_REGISTRATION_STATE: "resetRegistrationState",
|
||||||
RESET_GLASS_PARTS_STATE: "resetGlassPartsState",
|
RESET_GLASS_PARTS_STATE: "resetGlassPartsState",
|
||||||
RESET_STATE: "resetState",
|
RESET_STATE: "resetState",
|
||||||
|
RESET_SAVE_ORDER_PROMISE: "resetSaveOrderPromise",
|
||||||
|
|
||||||
// OTHER MUTATIONS
|
// OTHER MUTATIONS
|
||||||
UPDATE_PAGE_DATA: "updatePageData",
|
UPDATE_PAGE_DATA: "updatePageData",
|
||||||
UPDATE_STATE_WITH_ORDER_INFORMATION: "updateStateWithOrderInformation",
|
UPDATE_STATE_WITH_ORDER_INFORMATION: "updateStateWithOrderInformation",
|
||||||
UPDATE_SAVE_ORDER_PROMISE: "updateSaveOrderPromise",
|
UPDATE_SAVE_ORDER_PROMISE: "updateSaveOrderPromise",
|
||||||
UPDATE_LAST_PAGE_VISITED: "updateLastPageVisited",
|
UPDATE_LAST_PAGE_VISITED: "updateLastPageVisited",
|
||||||
|
|
||||||
|
// EXPERIMENT MUTATIONS
|
||||||
|
UPDATE_EXPERIMENTS: "updateExperiments",
|
||||||
|
UPDATE_TRIGGERED_SITE_ENTRY: "updateTriggeredSiteEntry",
|
||||||
};
|
};
|
||||||
|
|
||||||
export { storeMutations };
|
export { storeMutations };
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,21 @@
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import analyticsMixIn from "@/mixins/analytics-mixin.js";
|
import analyticsMixIn from "@/mixins/analytics-mixin.js";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
import { applicationConfig } from "@/constants/application-config.js";
|
import { applicationConfig } from "@/constants/application-config.js";
|
||||||
import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
|
import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
|
||||||
|
import { headerKeys } from "@/constants/header-keys";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
callHttpClient({ method, endpoint, payload, logApiCall = true }) {
|
callHttpClient({ method, endpoint, payload, logApiCall = true }) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO;
|
const cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO;
|
||||||
const payloadAndAnalyticsData = Object.assign({}, payload, { AppName: "FixMyGlass" });
|
const payloadAndAnalyticsData = Object.assign({}, payload, { AppName: "FixMyGlass" });
|
||||||
|
const headers = {
|
||||||
|
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings)
|
||||||
|
}
|
||||||
|
|
||||||
axios({ method: method, url: cfDistroUrl + endpoint, data: payloadAndAnalyticsData, crossDomain: true, responseType: {} })
|
axios({ method: method, url: cfDistroUrl + endpoint, data: payloadAndAnalyticsData, crossDomain: true, responseType: {}, headers: headers })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
|
|
||||||
if (logApiCall) {
|
if (logApiCall) {
|
||||||
|
|
|
||||||
|
|
@ -7,21 +7,20 @@ import { applicationConfig } from "@/constants/application-config";
|
||||||
*/
|
*/
|
||||||
export function updateOrCreateFunnelCookie() {
|
export function updateOrCreateFunnelCookie() {
|
||||||
const wasClaimRegistrationDelayed = getFunnelCookie()?.HasDelayedClaimRegistration;
|
const wasClaimRegistrationDelayed = getFunnelCookie()?.HasDelayedClaimRegistration;
|
||||||
|
const shouldSuppressConceptFunnel = getFunnelCookie()?.SuppressConceptFunnel;
|
||||||
// Create the cookie
|
|
||||||
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}={}; path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()};`;
|
|
||||||
|
|
||||||
// Set up cookie with all the props.
|
// Set up cookie with all the props.
|
||||||
setFunnelCookieProperties({
|
setFunnelCookieProperties({
|
||||||
LastTouched: new Date().toUTCString(),
|
LastTouched: new Date().toUTCString(),
|
||||||
SavedQuoteTimeoutDate: store.getters.applicationUser.savedSessionTimeout,
|
SavedSessionTimeoutDate: store.getters.applicationUser.savedSessionTimeout,
|
||||||
DidHeritageFunnelUpdateLast: false,
|
DidHeritageFunnelUpdateLast: false,
|
||||||
ShouldResetState: false,
|
ShouldResetState: false,
|
||||||
ReferralNumber: store.getters.order.referralNumber,
|
ReferralNumber: store.getters.order.referralNumber,
|
||||||
ReferralDate: store.getters.order.referralDate,
|
ReferralDate: store.getters.order.referralDate,
|
||||||
ReferralCorrelationId: store.getters.order.referralCorrelationId,
|
ReferralCorrelationId: store.getters.order.referralCorrelationId,
|
||||||
ReferralParentAccountNumber: store.getters.order.accountNumber,
|
ReferralParentAccountNumber: store.getters.order.accountNumber,
|
||||||
HasDelayedClaimRegistration: wasClaimRegistrationDelayed
|
HasDelayedClaimRegistration: wasClaimRegistrationDelayed,
|
||||||
|
SuppressConceptFunnel: shouldSuppressConceptFunnel
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,14 +45,14 @@ export function getFunnelCookie() {
|
||||||
Removes cookie from browser.
|
Removes cookie from browser.
|
||||||
*/
|
*/
|
||||||
export function deleteFunnelCookie() {
|
export function deleteFunnelCookie() {
|
||||||
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=; Max-Age=0; path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()}`;
|
createOrUpdateCookie(cookieNames.FUNNEL_SESSION_INFO, undefined, { maxAge: 0 });
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets cookie domain value. Localhost will be empty "".
|
Gets cookie domain value. Localhost will be empty "".
|
||||||
*/
|
*/
|
||||||
export function getCookieDomainValue() {
|
export function getCookieDomainValue() {
|
||||||
return location.hostname.includes("localhost") ? "" : `domain=${getDomainWithoutSubdomain()};`;
|
return isLocalhost() ? "" : `domain=${getDomainWithoutSubdomain()};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -104,10 +103,17 @@ export function getSessionIdValue(){
|
||||||
return '00000000-0000-0000-0000-000000000000';
|
return '00000000-0000-0000-0000-000000000000';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setCookieProperties(properties) {
|
/*
|
||||||
|
Updates session ID cookie with new expiration date
|
||||||
|
*/
|
||||||
|
export function updateSessionIdCookie() {
|
||||||
|
createOrUpdateCookie(cookieNames.SESSION_ID, getSessionIdValue(), { maxAge: 60 * 30 });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setCookieProperties(properties, { useDefaultFunnelCookieAttributes = true, maxAge, isSecure }) {
|
||||||
if (typeof properties == "object") {
|
if (typeof properties == "object") {
|
||||||
Object.keys(properties).forEach(key => {
|
Object.keys(properties).forEach(key => {
|
||||||
document.cookie = `${key}=${properties[key]}`;
|
createOrUpdateCookie(key, properties[key], { useDefaultFunnelCookieAttributes, maxAge, isSecure });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,20 +137,39 @@ function setFunnelCookieProperties(properties) {
|
||||||
Object.keys(properties).forEach(key => {
|
Object.keys(properties).forEach(key => {
|
||||||
cookie[key] = properties[key];
|
cookie[key] = properties[key];
|
||||||
});
|
});
|
||||||
|
|
||||||
const cookieValueJson = JSON.stringify(cookie);
|
|
||||||
|
|
||||||
document.cookie = `${cookieNames.FUNNEL_SESSION_INFO}=${cookieValueJson}; path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()}`;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const cookieValueJson = JSON.stringify(cookie ?? {});
|
||||||
|
createOrUpdateCookie(cookieNames.FUNNEL_SESSION_INFO, cookieValueJson, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Used to create a cookie.
|
||||||
|
`useDefaultFunnelCookieAttributes` will set the path and domain to our defaults
|
||||||
|
*/
|
||||||
|
function createOrUpdateCookie(key, value = "", { useDefaultFunnelCookieAttributes = true, maxAge, isSecure = true }) {
|
||||||
|
let cookieToAdd = `${key}=${value}; `;
|
||||||
|
|
||||||
|
if (useDefaultFunnelCookieAttributes) {
|
||||||
|
cookieToAdd += `path=${applicationConfig.COOKIE_PATH}; ${getCookieDomainValue()} `;
|
||||||
|
}
|
||||||
|
if (isSecure && !isLocalhost()) {
|
||||||
|
cookieToAdd += `secure; `;
|
||||||
|
}
|
||||||
|
if (!isNaN(maxAge)) {
|
||||||
|
cookieToAdd += `max-age=${maxAge};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.cookie = cookieToAdd;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Gets current domain without the subdomain for cookie.
|
Gets current domain without the subdomain for cookie.
|
||||||
*/
|
*/
|
||||||
function getDomainWithoutSubdomain() {
|
function getDomainWithoutSubdomain() {
|
||||||
let url = location.hostname;
|
let url = location.hostname;
|
||||||
if (url.includes("localhost")) {
|
if (isLocalhost()) {
|
||||||
return "localhost";
|
return "localhost";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,3 +193,7 @@ function getCookieValueByName(name) {
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isLocalhost() {
|
||||||
|
return location.hostname.includes("localhost");
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,8 @@ describe("cookies", () => {
|
||||||
ReferralDate: testReferralDate,
|
ReferralDate: testReferralDate,
|
||||||
ReferralCorrelationId: testReferralCorrelationId,
|
ReferralCorrelationId: testReferralCorrelationId,
|
||||||
ShouldResetState: testShouldResetState,
|
ShouldResetState: testShouldResetState,
|
||||||
DidHeritageFunnelUpdateLast: testDidHeritageFunnelUpdateLast
|
DidHeritageFunnelUpdateLast: testDidHeritageFunnelUpdateLast,
|
||||||
|
SuppressConceptFunnel: true
|
||||||
}
|
}
|
||||||
|
|
||||||
setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) });
|
setupCookies({ funnelCookieValue: JSON.stringify(testCookieValue) });
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export async function getPageToRouteExistingOrderTo(toRoute = {}, existingHerita
|
||||||
if (toRoute.query[queryStrings.START_TYPE] === 'fmg') {
|
if (toRoute.query[queryStrings.START_TYPE] === 'fmg') {
|
||||||
// If they have an existing order, return 'heritage' for the page name.
|
// If they have an existing order, return 'heritage' for the page name.
|
||||||
if (existingHeritageOrder) {
|
if (existingHeritageOrder) {
|
||||||
return 'heritage';
|
return fmgPageValues.HERITAGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await getLatestPageForRedirection();
|
return await getLatestPageForRedirection();
|
||||||
|
|
@ -39,9 +39,11 @@ export async function getPageToRouteExistingOrderTo(toRoute = {}, existingHerita
|
||||||
Used to navigate to the heritage funnel with the correct query string and url.
|
Used to navigate to the heritage funnel with the correct query string and url.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export async function navigateToHeritageFunnel() {
|
export async function navigateToHeritageFunnel(shouldSaveOrder = true) {
|
||||||
// Create the order (or save existing order) when navigating to Heritage Funnel.
|
// Create the order (or save existing order) when navigating to Heritage Funnel.
|
||||||
await saveOrder();
|
if (shouldSaveOrder) {
|
||||||
|
await saveOrder();
|
||||||
|
}
|
||||||
|
|
||||||
router.navigateToExternalUrl(
|
router.navigateToExternalUrl(
|
||||||
externalUrls.HERITAGE_FUNNEL,
|
externalUrls.HERITAGE_FUNNEL,
|
||||||
|
|
@ -59,11 +61,16 @@ async function getLatestPageForRedirection() {
|
||||||
// If this is a non-CTA navigation, determine where to send the user based on page prerequisites.
|
// 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.
|
// 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.
|
// That shouldn't happen, but it's possible.
|
||||||
const vehicleMakeComponent = (await lazyLoadComponent('vehicle-make')()).default;
|
const vehicleMakeComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_MAKE);
|
||||||
const vehicleModelComponent = (await lazyLoadComponent('vehicle-model')()).default;
|
const vehicleModelComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_MODEL);
|
||||||
const vehicleStyleComponent = (await lazyLoadComponent('vehicle-style')()).default;
|
const vehicleStyleComponent = await getLazyLoadedComponent(fmgPageValues.VEHICLE_STYLE);
|
||||||
const vehicleDamageComponent = (await lazyLoadComponent('vehicle-damage')()).default;
|
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()) {
|
if (!vehicleMakeComponent.methods.arePagePrerequisitesValid()) {
|
||||||
return fmgPageValues.VEHICLE_YEAR;
|
return fmgPageValues.VEHICLE_YEAR;
|
||||||
|
|
@ -73,12 +80,25 @@ async function getLatestPageForRedirection() {
|
||||||
return fmgPageValues.VEHICLE_MODEL;
|
return fmgPageValues.VEHICLE_MODEL;
|
||||||
} else if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) {
|
} else if (!vehicleDamageComponent.methods.arePagePrerequisitesValid()) {
|
||||||
return fmgPageValues.VEHICLE_STYLE;
|
return fmgPageValues.VEHICLE_STYLE;
|
||||||
} else if (store.getters.damage.isRepair == null || !store.getters.vehicle.carId) {
|
} else if (!estimateComponent.methods.arePagePrerequisitesValid()) {
|
||||||
return fmgPageValues.VEHICLE_DAMAGE;
|
return fmgPageValues.VEHICLE_DAMAGE;
|
||||||
} else {
|
} 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;
|
return fmgPageValues.VIN_LOOKUP;
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
return fmgPageValues.ESTIMATE;
|
return fmgPageValues.ESTIMATE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -125,3 +145,7 @@ function isVinRelatedPage(toRoute) {
|
||||||
fmgPageValue === fmgPageValues.ADDRESS_VEHICLES ||
|
fmgPageValue === fmgPageValues.ADDRESS_VEHICLES ||
|
||||||
fmgPageValue === fmgPageValues.ESTIMATE;
|
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 { setupMocksForJsFiles, getMockOrderInfo } from "@/helpers/unit-test-helper.js";
|
||||||
import { externalUrls } from "@/router/router-constants/externalUrl-values";
|
import { externalUrls } from "@/router/router-constants/externalUrl-values";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
|
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
|
|
@ -15,278 +16,269 @@ jest.mock("@/router/dynamic-routing/component-loader.js", () => ({
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("getPageToRouteExistingOrderTo", () => {
|
describe("getPageToRouteExistingOrderTo", () => {
|
||||||
|
test("should return vehicle-year", async () => {
|
||||||
test("getPageToRouteExistingOrderTo, should return vehicle-year", async () => {
|
|
||||||
// Arrange
|
// Arrange
|
||||||
const toRoute = {
|
const toRoute = {
|
||||||
query: {}
|
query: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mock out the lazy load calls for all components.
|
// Mock out the lazy load calls for all components.
|
||||||
lazyLoadComponent
|
mockLazyLoadComponentReturnValues({
|
||||||
.mockReturnValueOnce(() => {
|
[fmgPageValues.VEHICLE_MAKE]: false
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
//Assert
|
//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
|
// Arrange
|
||||||
const toRoute = {
|
const toRoute = {
|
||||||
query: {}
|
query: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mock out the lazy load calls for all components.
|
// Mock out the lazy load calls for all components.
|
||||||
lazyLoadComponent
|
mockLazyLoadComponentReturnValues({
|
||||||
.mockReturnValueOnce(() => {
|
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||||
return {
|
[fmgPageValues.VEHICLE_MODEL]: false
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
//Assert
|
//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
|
// Arrange
|
||||||
const toRoute = {
|
const toRoute = {
|
||||||
query: {}
|
query: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mock out the lazy load calls for all components.
|
// Mock out the lazy load calls for all components.
|
||||||
lazyLoadComponent
|
mockLazyLoadComponentReturnValues({
|
||||||
.mockReturnValueOnce(() => {
|
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||||
return {
|
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||||
default: {
|
[fmgPageValues.VEHICLE_STYLE]: false,
|
||||||
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';
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
//Assert
|
//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
|
// Arrange
|
||||||
const toRoute = {
|
const toRoute = {
|
||||||
query: {}
|
query: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mock out the lazy load calls for all components.
|
// Mock out the lazy load calls for all components.
|
||||||
lazyLoadComponent
|
mockLazyLoadComponentReturnValues({
|
||||||
.mockReturnValueOnce(() => {
|
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||||
return {
|
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||||
default: {
|
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||||
methods: {
|
[fmgPageValues.VEHICLE_DAMAGE]: false,
|
||||||
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"
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
//Assert
|
//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
|
// Arrange
|
||||||
const toRoute = {
|
const toRoute = {
|
||||||
query: {}
|
query: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Mock out the lazy load calls for all components.
|
// Mock out the lazy load calls for all components.
|
||||||
lazyLoadComponent
|
mockLazyLoadComponentReturnValues({
|
||||||
.mockReturnValueOnce(() => {
|
[fmgPageValues.VEHICLE_MAKE]: true,
|
||||||
return {
|
[fmgPageValues.VEHICLE_MODEL]: true,
|
||||||
default: {
|
[fmgPageValues.VEHICLE_STYLE]: true,
|
||||||
methods: {
|
[fmgPageValues.VEHICLE_DAMAGE]: true,
|
||||||
arePagePrerequisitesValid: jest.fn().mockReturnValueOnce(true)
|
[fmgPageValues.ESTIMATE]: false
|
||||||
}
|
})
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.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 = null;
|
|
||||||
// Act
|
// Act
|
||||||
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
const result = await getPageToRouteExistingOrderTo(toRoute, false);
|
||||||
|
|
||||||
//Assert
|
//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
|
// Arrange
|
||||||
const toRoute = {
|
const toRoute = {
|
||||||
query: {
|
query: {
|
||||||
|
|
@ -298,7 +290,7 @@ describe("getPageToRouteExistingOrderTo", () => {
|
||||||
const result = await getPageToRouteExistingOrderTo(toRoute, true);
|
const result = await getPageToRouteExistingOrderTo(toRoute, true);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(result).toBe("heritage");
|
expect(result).toBe(fmgPageValues.HERITAGE);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -309,10 +301,10 @@ describe("navigateToHeritageFunnel", () => {
|
||||||
const mockCorrelationId = "55";
|
const mockCorrelationId = "55";
|
||||||
const mockReferralDate = "2022";
|
const mockReferralDate = "2022";
|
||||||
const mockAccountNumber = "167132";
|
const mockAccountNumber = "167132";
|
||||||
const mockSaveQuoteId = "xxx-xxx-xxx";
|
const mockSavedSessionId = "xxx-xxx-xxx";
|
||||||
const mockCrmCustomerId = "xxx-xxx-xxx"
|
const mockCrmCustomerId = "xxx-xxx-xxx"
|
||||||
|
|
||||||
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, mockAccountNumber, mockSaveQuoteId, mockCrmCustomerId);
|
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, mockAccountNumber, mockSavedSessionId, mockCrmCustomerId);
|
||||||
|
|
||||||
const mockData = {
|
const mockData = {
|
||||||
actionList: [{
|
actionList: [{
|
||||||
|
|
@ -331,7 +323,7 @@ describe("navigateToHeritageFunnel", () => {
|
||||||
// Assert
|
// Assert
|
||||||
expect(saveOrderFunction).toHaveBeenCalled();
|
expect(saveOrderFunction).toHaveBeenCalled();
|
||||||
|
|
||||||
// Should alway save before we navigate to heritage
|
// Should save before we navigate to heritage by default
|
||||||
const saveOrderFunctionCallOrder = saveOrderFunction.mock.invocationCallOrder[0];
|
const saveOrderFunctionCallOrder = saveOrderFunction.mock.invocationCallOrder[0];
|
||||||
const routerNavigateFunctionCallOrder = router.navigateToExternalUrl.mock.invocationCallOrder[0];
|
const routerNavigateFunctionCallOrder = router.navigateToExternalUrl.mock.invocationCallOrder[0];
|
||||||
expect(saveOrderFunctionCallOrder).toBeLessThan(routerNavigateFunctionCallOrder);
|
expect(saveOrderFunctionCallOrder).toBeLessThan(routerNavigateFunctionCallOrder);
|
||||||
|
|
@ -344,10 +336,10 @@ describe("navigateToHeritageFunnel", () => {
|
||||||
const mockCorrelationId = "55";
|
const mockCorrelationId = "55";
|
||||||
const mockReferralDate = "2022";
|
const mockReferralDate = "2022";
|
||||||
const mockAccountNumber = "167132";
|
const mockAccountNumber = "167132";
|
||||||
const mockSaveQuoteId = "xxx-xxx-xxx";
|
const mockSavedSessionId = "xxx-xxx-xxx";
|
||||||
const mockCrmCustomerId = "xxx-xxx-xxx"
|
const mockCrmCustomerId = "xxx-xxx-xxx"
|
||||||
|
|
||||||
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, mockAccountNumber, mockSaveQuoteId, mockCrmCustomerId);
|
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, mockAccountNumber, mockSavedSessionId, mockCrmCustomerId);
|
||||||
|
|
||||||
const mockData = {
|
const mockData = {
|
||||||
actionList: [{
|
actionList: [{
|
||||||
|
|
@ -373,4 +365,50 @@ describe("navigateToHeritageFunnel", () => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("should not save order, but should still navigate", async () => {
|
||||||
|
// Arrange
|
||||||
|
const mockReferralNumber = "2";
|
||||||
|
const mockCorrelationId = "55";
|
||||||
|
const mockReferralDate = "2022";
|
||||||
|
|
||||||
|
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate);
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
actionList: [{
|
||||||
|
actionName: storeActions.SAVE_ORDER,
|
||||||
|
data: mockOrderInfo,
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
setupMocksForJsFiles(mockData);
|
||||||
|
const saveOrderFunction = jest.spyOn(orderHelper, "saveOrder");
|
||||||
|
router.navigateToExternalUrl = jest.fn();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await navigateToHeritageFunnel(false);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(saveOrderFunction).not.toHaveBeenCalled();
|
||||||
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ export async function saveOrder() {
|
||||||
and returns the response.
|
and returns the response.
|
||||||
*/
|
*/
|
||||||
async function loadOrder(referralNumber, referralDate, referralCorrelationId, accountNumber) {
|
async function loadOrder(referralNumber, referralDate, referralCorrelationId, accountNumber) {
|
||||||
|
// await the saveOrderPromise in the store to make sure we're loading up to date information
|
||||||
|
await store.getters.applicationUser.saveOrderPromise;
|
||||||
const response = await baseMixin.methods.dispatchStoreAction(storeActions.LOAD_ORDER,
|
const response = await baseMixin.methods.dispatchStoreAction(storeActions.LOAD_ORDER,
|
||||||
{
|
{
|
||||||
referralNumber: referralNumber.toString(),
|
referralNumber: referralNumber.toString(),
|
||||||
|
|
@ -82,7 +84,7 @@ async function saveOrderHelper() {
|
||||||
referralCorrelationId: savedOrderInfo.data.referralCorrelationId,
|
referralCorrelationId: savedOrderInfo.data.referralCorrelationId,
|
||||||
referralDate: savedOrderInfo.data.referralDate,
|
referralDate: savedOrderInfo.data.referralDate,
|
||||||
accountNumber: savedOrderInfo.data.accountNumber.toString(),
|
accountNumber: savedOrderInfo.data.accountNumber.toString(),
|
||||||
saveQuoteId: savedOrderInfo.data.saveQuoteId,
|
savedSessionId: savedOrderInfo.data.savedSessionId,
|
||||||
crmCustomerId: savedOrderInfo.data.crmCustomerId.toString(),
|
crmCustomerId: savedOrderInfo.data.crmCustomerId.toString(),
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -112,10 +112,10 @@ describe("saveOrder", () => {
|
||||||
const mockCorrelationId = "55";
|
const mockCorrelationId = "55";
|
||||||
const mockReferralDate = "2022";
|
const mockReferralDate = "2022";
|
||||||
const mockAccountNumber = "167132";
|
const mockAccountNumber = "167132";
|
||||||
const mockSaveQuoteId = "xxx-xxx-xxx";
|
const mockSavedSessionId = "xxx-xxx-xxx";
|
||||||
const mockCrmCustomerId = "xxx-xxx-xxx"
|
const mockCrmCustomerId = "xxx-xxx-xxx"
|
||||||
|
|
||||||
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, mockAccountNumber, mockSaveQuoteId, mockCrmCustomerId);
|
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, mockAccountNumber, mockSavedSessionId, mockCrmCustomerId);
|
||||||
|
|
||||||
const mockData = {
|
const mockData = {
|
||||||
actionList: [
|
actionList: [
|
||||||
|
|
@ -141,7 +141,7 @@ describe("saveOrder", () => {
|
||||||
referralDate: mockReferralDate,
|
referralDate: mockReferralDate,
|
||||||
referralCorrelationId: mockCorrelationId,
|
referralCorrelationId: mockCorrelationId,
|
||||||
accountNumber: mockAccountNumber,
|
accountNumber: mockAccountNumber,
|
||||||
saveQuoteId: mockSaveQuoteId,
|
savedSessionId: mockSavedSessionId,
|
||||||
crmCustomerId: mockCrmCustomerId,
|
crmCustomerId: mockCrmCustomerId,
|
||||||
}, false);
|
}, false);
|
||||||
});
|
});
|
||||||
|
|
@ -152,10 +152,10 @@ describe("saveOrder", () => {
|
||||||
const mockReferralDate = "2022-03-15T10:56:24.597";
|
const mockReferralDate = "2022-03-15T10:56:24.597";
|
||||||
const mockReferralCorrelationId = "404d2b04-f86e-45c3-b373-127b6217b060";
|
const mockReferralCorrelationId = "404d2b04-f86e-45c3-b373-127b6217b060";
|
||||||
const mockAccountNumber = "167132";
|
const mockAccountNumber = "167132";
|
||||||
const mockSaveQuoteId = "xxx-xxx-xxx";
|
const mockSavedSessionId = "xxx-xxx-xxx";
|
||||||
const mockCrmCustomerId = "xxx-xxx-xxx"
|
const mockCrmCustomerId = "xxx-xxx-xxx"
|
||||||
|
|
||||||
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockReferralCorrelationId, mockReferralDate, mockAccountNumber, mockSaveQuoteId, mockCrmCustomerId);
|
const mockOrderInfo = getMockOrderInfo(mockReferralNumber, mockReferralCorrelationId, mockReferralDate, mockAccountNumber, mockSavedSessionId, mockCrmCustomerId);
|
||||||
|
|
||||||
const mockData = {
|
const mockData = {
|
||||||
actionList: [{
|
actionList: [{
|
||||||
|
|
|
||||||
|
|
@ -28,19 +28,15 @@ export function isAnalyticsSessionStillActive() {
|
||||||
*/
|
*/
|
||||||
export function isSavedSessionStillActive() {
|
export function isSavedSessionStillActive() {
|
||||||
if (getFunnelCookie() !== null) {
|
if (getFunnelCookie() !== null) {
|
||||||
const savedSessionTimeStamp = new Date(getFunnelCookie().SavedQuoteTimeoutDate);
|
const savedSessionTimeStamp = new Date(getFunnelCookie().SavedSessionTimeoutDate);
|
||||||
const isSavedSessionTimedOut = (new Date(new Date().toUTCString()) > savedSessionTimeStamp);
|
const isSavedSessionTimedOut = (new Date(new Date().toUTCString()) > savedSessionTimeStamp);
|
||||||
|
|
||||||
if (isSavedSessionTimedOut) {
|
return !isSavedSessionTimedOut;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Function to get the date for the saved session timeout.
|
Function to calculate the date for the saved session timeout.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function getDateForSavedSessionTimeout() {
|
export function getDateForSavedSessionTimeout() {
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ describe("isSavedSessionStillActive", () => {
|
||||||
mockDate.setDate(mockDate.getDate() + 1);
|
mockDate.setDate(mockDate.getDate() + 1);
|
||||||
|
|
||||||
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie")
|
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie")
|
||||||
.mockReturnValue({ SavedQuoteTimeoutDate: mockDate });
|
.mockReturnValue({ SavedSessionTimeoutDate: mockDate });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = isSavedSessionStillActive();
|
const result = isSavedSessionStillActive();
|
||||||
|
|
@ -56,7 +56,7 @@ describe("isSavedSessionStillActive", () => {
|
||||||
mockDate.setDate(mockDate.getDate() - 1)
|
mockDate.setDate(mockDate.getDate() - 1)
|
||||||
|
|
||||||
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie")
|
cookieHelper.getFunnelCookie = jest.spyOn(cookieHelper, "getFunnelCookie")
|
||||||
.mockReturnValue({ SavedQuoteTimeoutDate: mockDate });
|
.mockReturnValue({ SavedSessionTimeoutDate: mockDate });
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
const result = isSavedSessionStillActive();
|
const result = isSavedSessionStillActive();
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { cookieNames } from "@/constants/cookie-names";
|
import { cookieNames } from "@/constants/cookie-names";
|
||||||
import { Form } from "vee-validate";
|
import { Form } from "vee-validate";
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
import { getCookieDomainValue } from "@/helpers/heritage-integration/cookie-helper";
|
import { getCookieDomainValue, setCookieProperties } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents } from "@/constants/analytics";
|
import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes } from "@/constants/analytics";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
import { routerParams } from "@/router/router-constants/router-params";
|
import { routerParams } from "@/router/router-constants/router-params";
|
||||||
|
|
||||||
|
|
@ -48,6 +48,7 @@ export function getMountOptions(mockData) {
|
||||||
mocks.GaActions = GaActions;
|
mocks.GaActions = GaActions;
|
||||||
mocks.GaLabels = GaLabels;
|
mocks.GaLabels = GaLabels;
|
||||||
mocks.GaEvents = GaEvents;
|
mocks.GaEvents = GaEvents;
|
||||||
|
mocks.ValueToLogTypes = ValueToLogTypes;
|
||||||
mocks.queryStrings = queryStrings;
|
mocks.queryStrings = queryStrings;
|
||||||
mocks.routerParams = routerParams;
|
mocks.routerParams = routerParams;
|
||||||
|
|
||||||
|
|
@ -90,13 +91,13 @@ export function removeAllTestCookies() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, accountNumber = "0", saveQuoteId, crmCustomerId) {
|
export function getMockOrderInfo(mockReferralNumber, mockCorrelationId, mockReferralDate, accountNumber = "0", savedSessionId, crmCustomerId) {
|
||||||
return {
|
return {
|
||||||
referralNumber: mockReferralNumber,
|
referralNumber: mockReferralNumber,
|
||||||
referralCorrelationId: mockCorrelationId,
|
referralCorrelationId: mockCorrelationId,
|
||||||
referralDate: mockReferralDate,
|
referralDate: mockReferralDate,
|
||||||
accountNumber: accountNumber,
|
accountNumber: accountNumber,
|
||||||
saveQuoteId: saveQuoteId,
|
savedSessionId: savedSessionId,
|
||||||
crmCustomerId: crmCustomerId,
|
crmCustomerId: crmCustomerId,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -105,7 +106,7 @@ export function setupCookies({ funnelCookieValue = "", includeHeritageCookie = t
|
||||||
Object.keys(cookies).forEach(key => {
|
Object.keys(cookies).forEach(key => {
|
||||||
const cookieValue = key == cookieNames.FUNNEL_SESSION_INFO ? funnelCookieValue : cookies[key];
|
const cookieValue = key == cookieNames.FUNNEL_SESSION_INFO ? funnelCookieValue : cookies[key];
|
||||||
if (includeHeritageCookie || key != cookieNames.FUNNEL_SESSION_INFO)
|
if (includeHeritageCookie || key != cookieNames.FUNNEL_SESSION_INFO)
|
||||||
document.cookie = `${key}=${cookieValue}; path=/; ${getCookieDomainValue()}`;
|
setCookieProperties({ [key]: cookieValue }, { isSecure: false });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -351,10 +351,9 @@ describe("address-lookup.vue", () => {
|
||||||
addressQuestions: mockRegistrationAddress
|
addressQuestions: mockRegistrationAddress
|
||||||
},
|
},
|
||||||
isCarIdDifferent: true,
|
isCarIdDifferent: true,
|
||||||
isGlassAvailableForCarId: false,
|
isSelectedGlassAvailableForVehicle: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
let carsFound = [{
|
let carsFound = [{
|
||||||
vin: "TEST_VIN2",
|
vin: "TEST_VIN2",
|
||||||
vehicle: {
|
vehicle: {
|
||||||
|
|
@ -499,38 +498,6 @@ describe("address-lookup.vue", () => {
|
||||||
expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe(true);
|
expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
// test.only("if registration address is provided user clicks continue => show service zip field on continue click", async () => {
|
|
||||||
// // Arrange
|
|
||||||
// const mockRegistrationAddress = {
|
|
||||||
// streetAddress: "1234 Main St",
|
|
||||||
// city: "Columbus",
|
|
||||||
// state: "OH",
|
|
||||||
// zipCode: "43215"
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const { wrapper } = setupMocks({
|
|
||||||
// isZipServiceable: false
|
|
||||||
// }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// expect(wrapper.vm.showServiceZipField).toBeFalsy();
|
|
||||||
// expect(wrapper.findComponent({ ref: "serviceZip" }).exists()).toBe(false);
|
|
||||||
// store.commit(storeMutations.UPDATE_CAR_ID, "CARID");
|
|
||||||
|
|
||||||
// await wrapper.setData({
|
|
||||||
// customerQuestions: {
|
|
||||||
// addressQuestions: mockRegistrationAddress
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
|
|
||||||
// // Act
|
|
||||||
// await wrapper.vm.forwardButtonAction();
|
|
||||||
|
|
||||||
// // Assert
|
|
||||||
// expect(wrapper.vm.showServiceZipField).toBe(true);
|
|
||||||
// expect(wrapper.findComponent({ ref: "serviceZip" }).isVisible()).toBe(true);
|
|
||||||
// });
|
|
||||||
|
|
||||||
test("if registration address, service zip are provided, and user clicks continue => don't update service address", async () => {
|
test("if registration address, service zip are provided, and user clicks continue => don't update service address", async () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
const mockRegistrationAddress = {
|
const mockRegistrationAddress = {
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ export default {
|
||||||
displayVinLookupByHomeAddressNotAllowedAlert: false,
|
displayVinLookupByHomeAddressNotAllowedAlert: false,
|
||||||
previouslyEnteredCarId: "",
|
previouslyEnteredCarId: "",
|
||||||
isCarIdDifferent: false,
|
isCarIdDifferent: false,
|
||||||
isSelectedGlassAvailableForVehicle: false,
|
isSelectedGlassAvailableForVehicle: true,
|
||||||
customAlertData: {},
|
customAlertData: {},
|
||||||
displayInvalidZipAlert: false,
|
displayInvalidZipAlert: false,
|
||||||
showServiceZipField: this.getServiceZipFromStore(),
|
showServiceZipField: this.getServiceZipFromStore(),
|
||||||
|
|
@ -230,9 +230,6 @@ export default {
|
||||||
const carFound = carsFound[0].vehicle;
|
const carFound = carsFound[0].vehicle;
|
||||||
|
|
||||||
this.isCarIdDifferent = carFound.carId !== this.$store.getters.vehicle.carId;
|
this.isCarIdDifferent = carFound.carId !== this.$store.getters.vehicle.carId;
|
||||||
console.log(this.isCarIdDifferent);
|
|
||||||
console.log(carFound.carId);
|
|
||||||
console.log(this.$store.getters.vehicle.carId);
|
|
||||||
if (this.isCarIdDifferent && carFound.carId !== this.previouslyEnteredCarId) {
|
if (this.isCarIdDifferent && carFound.carId !== this.previouslyEnteredCarId) {
|
||||||
// Display Alert
|
// Display Alert
|
||||||
this.previouslyEnteredCarId = carFound.carId;
|
this.previouslyEnteredCarId = carFound.carId;
|
||||||
|
|
@ -261,7 +258,6 @@ console.log(this.$store.getters.vehicle.carId);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// No VINS found.
|
// No VINS found.
|
||||||
console.log("no vins");
|
|
||||||
this.displayVinNotFoundAlert = true;
|
this.displayVinNotFoundAlert = true;
|
||||||
return this.$refs.funnelFooter.removeLoader();
|
return this.$refs.funnelFooter.removeLoader();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,13 +56,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
<alert ref="alertVerificationWarning" v-if="displayVerificationWarning"
|
<alert ref="alertVerificationWarning"
|
||||||
|
v-if="displayVerificationWarning"
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
cmsWidgetName="AlertVerificationWarningWidget"
|
cmsWidgetName="AlertVerificationWarningWidget"
|
||||||
alertClass="alert-warning"
|
alertClass="alert-warning"
|
||||||
v-bind:isDismissible="false"
|
v-bind:isDismissible="false"
|
||||||
/>
|
/>
|
||||||
<alert ref="alertNoMatchWarning" v-if="displayNoMatchWarning"
|
<alert ref="alertNoMatchWarning"
|
||||||
|
v-if="displayNoMatchWarning"
|
||||||
class="mb-4"
|
class="mb-4"
|
||||||
cmsWidgetName="AlertNoMatchWarningWidget"
|
cmsWidgetName="AlertNoMatchWarningWidget"
|
||||||
alertClass="alert-warning"
|
alertClass="alert-warning"
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import addressVehiclesQuestion from "@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question";
|
import addressVehiclesQuestion from "@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question";
|
||||||
|
import { ValueToLogTypes } from "@/constants/analytics";
|
||||||
|
|
||||||
|
|
||||||
describe("addressVehiclesQuestion.vue", () => {
|
describe("addressVehiclesQuestion.vue", () => {
|
||||||
|
|
||||||
|
|
@ -54,9 +56,15 @@ const mockMixin = {
|
||||||
return 'FoundWindshieldTestReturn';
|
return 'FoundWindshieldTestReturn';
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
vehicles: jest.fn(() => {
|
vehicles: jest.fn(() => {
|
||||||
return [{ vehicle: "test" }];
|
return [{ vehicle: "test" }];
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
|
computed: {
|
||||||
|
ValueToLogTypes() {
|
||||||
|
return ValueToLogTypes;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,15 @@
|
||||||
v-model="selectedVehicleVinAsArray"
|
v-model="selectedVehicleVinAsArray"
|
||||||
isRequired
|
isRequired
|
||||||
:validation-rules="validationRules"
|
:validation-rules="validationRules"
|
||||||
|
:valueToLogType="ValueToLogTypes.LAST_5"
|
||||||
/>
|
/>
|
||||||
<alert
|
<alert ref="differentVehicleAlert"
|
||||||
ref="differentVehicleAlert"
|
|
||||||
v-if="isCarIdDifferent"
|
v-if="isCarIdDifferent"
|
||||||
class="my-3"
|
class="my-3"
|
||||||
alertClass="alert-warning"
|
cmsWidgetName="FoundWindshield"
|
||||||
:manualHeadline="differentVehicleAlertHeader"
|
:manualHeadline="differentVehicleAlertHeader"
|
||||||
:manualCopy="differentVehicleAlertBody"
|
:manualCopy="differentVehicleAlertBody"
|
||||||
|
alertClass="alert-warning"
|
||||||
v-bind:isDismissible="false"
|
v-bind:isDismissible="false"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -125,8 +125,8 @@ export default {
|
||||||
// Map API result data, to address-vehicles data structure
|
// Map API result data, to address-vehicles data structure
|
||||||
const mappedData = this.VehiclesFromApi.map((v) => {
|
const mappedData = this.VehiclesFromApi.map((v) => {
|
||||||
const maskSymbol = "X";
|
const maskSymbol = "X";
|
||||||
const vinStart = maskSymbol.repeat(v.vin.length-4);
|
const vinStart = maskSymbol.repeat(v.vin.length - 6);
|
||||||
const vinEnd = v.vin.substring(v.vin.length-4);
|
const vinEnd = v.vin.substring(v.vin.length - 6);
|
||||||
return {
|
return {
|
||||||
vin: v.vin,
|
vin: v.vin,
|
||||||
vehicle: v.vehicle,
|
vehicle: v.vehicle,
|
||||||
|
|
|
||||||
159
src/layouts/capability-questions/capability-questions.vue
Normal file
159
src/layouts/capability-questions/capability-questions.vue
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
<template>
|
||||||
|
<Form
|
||||||
|
@submit="onSubmit"
|
||||||
|
@invalid-submit="onInvalidSubmit"
|
||||||
|
ref="theForm"
|
||||||
|
v-slot="{ meta }"
|
||||||
|
>
|
||||||
|
<div class="page-container-grouped-styles capability-questions">
|
||||||
|
<loadingModal ref="loadingModal"/>
|
||||||
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
|
||||||
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
|
<div class="fade-on-route-transition sub-container make-tall">
|
||||||
|
<alert
|
||||||
|
ref="alertFewMoreQuestions"
|
||||||
|
class="my-5"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
:manualHeadline="AlertFewMoreQuestionsHeader"
|
||||||
|
:manualCopy="AlertFewMoreQuestionsCopy"
|
||||||
|
v-bind:isDismissible="false"
|
||||||
|
/>
|
||||||
|
<questionChain
|
||||||
|
ref="questionChain"
|
||||||
|
v-model="selectedAnswer"
|
||||||
|
:questionData="capabilityQuestionsData"
|
||||||
|
:partIndex="i"
|
||||||
|
validationRules="questions-required"
|
||||||
|
/>
|
||||||
|
<funnel-footer
|
||||||
|
ref="funnelFooter"
|
||||||
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
:isForwardActionDisabled="!meta.valid"
|
||||||
|
@back-clicked="backButtonAction"
|
||||||
|
@ForwardClicked="forwardButtonAction"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Components
|
||||||
|
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||||
|
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||||
|
import alert from "@/ux-components/alert/alert";
|
||||||
|
import questionChain from "@/common-components/question-chain/question-chain";
|
||||||
|
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||||
|
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||||
|
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
||||||
|
|
||||||
|
// Supporting Files
|
||||||
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
|
import store from "@/store";
|
||||||
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
|
import { Form, defineRule } from "vee-validate";
|
||||||
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
|
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||||
|
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
|
||||||
|
|
||||||
|
// DEFINE VALIDATION RULES
|
||||||
|
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "capability-questions",
|
||||||
|
mixins: [vehicleQuestionsMixin],
|
||||||
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
// Call APIs
|
||||||
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedAnswer: [],
|
||||||
|
// TODO This shouldn't be an object with property `partQuestions`
|
||||||
|
capabilityQuestionsData: {
|
||||||
|
partQuestions: store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS).capabilityQuestions
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
AlertFewMoreQuestionsHeader() {
|
||||||
|
return this.getCmsContent("AdditionalPartsQuestionsAlert", "HeadlineText");
|
||||||
|
},
|
||||||
|
AlertFewMoreQuestionsCopy() {
|
||||||
|
return this.getCmsContent("AdditionalPartsQuestionsAlert", "BodyText");
|
||||||
|
},
|
||||||
|
windshieldPart() {
|
||||||
|
return this.pageData.partsOrQuestions.find(x => x.glassLocation === damageLocationsSelected.WINDSHIELD);
|
||||||
|
},
|
||||||
|
windshieldPartInfo() {
|
||||||
|
return this.windshieldPart.parts[0];
|
||||||
|
},
|
||||||
|
pageData() {
|
||||||
|
return this.$store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid() {
|
||||||
|
const capabilityQuestionsPageData = store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);
|
||||||
|
return capabilityQuestionsPageData && Object.keys(capabilityQuestionsPageData).length > 0;
|
||||||
|
},
|
||||||
|
async forwardButtonAction() {
|
||||||
|
const selectedAnswerResult1 = this.selectedAnswer.answerResult;
|
||||||
|
const selectedAnswerResult2 = this.pageData.capabilityQuestions[0].answers.find(x => x.answerResult1 === selectedAnswerResult1).answerResult2;
|
||||||
|
|
||||||
|
this.dispatchStoreAction(storeActions.SAVE_CAPABILITY_QUESTION_ANSWERS, {
|
||||||
|
glassName: this.windshieldPart.glassName,
|
||||||
|
glassLocation: this.windshieldPart.glassLocation,
|
||||||
|
result1: selectedAnswerResult1,
|
||||||
|
result2: selectedAnswerResult2
|
||||||
|
});
|
||||||
|
|
||||||
|
const partFromCapabilityQuestionAnswer = (await this.dispatchStoreAction(storeActions.GET_PART_FROM_CAPABILITY_QUESTION_ANSWER)).data;
|
||||||
|
let partsOrQuestions = this.pageData.partsOrQuestions;
|
||||||
|
partsOrQuestions.find(partOrQuestion => partOrQuestion.glassLocation === damageLocationsSelected.WINDSHIELD).parts = partFromCapabilityQuestionAnswer;
|
||||||
|
|
||||||
|
this.navigateForward(partsOrQuestions);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
funnelHeader,
|
||||||
|
vehicleBanner,
|
||||||
|
alert,
|
||||||
|
questionChain,
|
||||||
|
funnelSubHeader,
|
||||||
|
funnelFooter,
|
||||||
|
loadingModal,
|
||||||
|
Form,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.capability-questions {
|
||||||
|
.question-text {
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
span {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -11,11 +11,10 @@
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
<div class="fade-on-route-transition sub-container make-tall">
|
<div class="fade-on-route-transition sub-container make-tall">
|
||||||
<alert
|
<alert
|
||||||
class="vinLookupMethodHeading"
|
class="vinLookupMethodHeading"
|
||||||
v-model="customAlertData"
|
cmsWidgetName="AlertVinLookupQuestion"
|
||||||
alertClass=""
|
alertClass=""
|
||||||
cmsWidgetName="AlertVinLookupQuestion"
|
/>
|
||||||
/>
|
|
||||||
<buttonQuestion
|
<buttonQuestion
|
||||||
cmsWidgetName="VinLookupMethod"
|
cmsWidgetName="VinLookupMethod"
|
||||||
:answers="answersFromCms"
|
:answers="answersFromCms"
|
||||||
|
|
@ -81,7 +80,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
if(store.getters.damage.isRepair!=null){
|
if(store.getters.damage.isRepair != null){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -235,7 +235,7 @@ describe("license-plate-lookup.vue", () => {
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("navigateToHeritageFunnel should be called if isCarIdDifferent is false or isSelectedGlassAvailableForVehicle is true when navigateForward is called", async () => {
|
test("navigateForwardWithSingleCarMatch should be called if isCarIdDifferent is false or isSelectedGlassAvailableForVehicle is true when navigateForward is called", async () => {
|
||||||
|
|
||||||
// Arrange
|
// Arrange
|
||||||
const { wrapper } = setupMocks({});
|
const { wrapper } = setupMocks({});
|
||||||
|
|
@ -247,11 +247,11 @@ describe("license-plate-lookup.vue", () => {
|
||||||
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {
|
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {
|
||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
navigateToHeritage.navigateToHeritageFunnel = jest.fn();
|
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||||
await wrapper.vm.navigateForward();
|
await wrapper.vm.navigateForward();
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
expect(navigateToHeritage.navigateToHeritageFunnel).toHaveBeenCalled();
|
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("carId matches returned vehicle => navigateForwardWithSingleCarMatch", async () => {
|
test("carId matches returned vehicle => navigateForwardWithSingleCarMatch", async () => {
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ export default {
|
||||||
displayVinNotFoundAlert: false,
|
displayVinNotFoundAlert: false,
|
||||||
displayMatchedDifferentVehicleAlert: false,
|
displayMatchedDifferentVehicleAlert: false,
|
||||||
previouslyEnteredCarId: "",
|
previouslyEnteredCarId: "",
|
||||||
isSelectedGlassAvailableForVehicle: false,
|
isSelectedGlassAvailableForVehicle: true,
|
||||||
isCarIdDifferent: false,
|
isCarIdDifferent: false,
|
||||||
customAlertData: {},
|
customAlertData: {},
|
||||||
displayInvalidZipAlert: false,
|
displayInvalidZipAlert: false,
|
||||||
|
|
|
||||||
202
src/layouts/molding-questions/molding-questions.vue
Normal file
202
src/layouts/molding-questions/molding-questions.vue
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
<template>
|
||||||
|
<Form
|
||||||
|
@submit="onSubmit"
|
||||||
|
@invalid-submit="onInvalidSubmit"
|
||||||
|
ref="theForm"
|
||||||
|
v-slot="{ meta }"
|
||||||
|
>
|
||||||
|
<div class="page-container-grouped-styles molding-questions">
|
||||||
|
<loadingModal ref="loadingModal"/>
|
||||||
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
|
||||||
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
|
<div class="fade-on-route-transition sub-container make-tall">
|
||||||
|
<h1>MOLDING QUESTIONS TEMPORARY PLACEHOLDER</h1>
|
||||||
|
<alert
|
||||||
|
ref="alertFewMoreQuestions"
|
||||||
|
class="my-5"
|
||||||
|
alertClass="alert-warning"
|
||||||
|
:manualHeadline="AlertFewMoreQuestionsHeader"
|
||||||
|
:manualCopy="AlertFewMoreQuestionsCopy"
|
||||||
|
v-bind:isDismissible="false"
|
||||||
|
/>
|
||||||
|
<div v-for="(part, i) in moldingQuestionsData" :key="i">
|
||||||
|
<questionChain
|
||||||
|
ref="questionChain"
|
||||||
|
v-model="selectedModel"
|
||||||
|
:questionData="part"
|
||||||
|
:partIndex="i"
|
||||||
|
v-if="showThisPartQuestionChain(part, i)"
|
||||||
|
validationRules="questions-required"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<funnel-footer
|
||||||
|
ref="funnelFooter"
|
||||||
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
|
:isForwardActionDisabled="!meta.valid"
|
||||||
|
@back-clicked="backButtonAction"
|
||||||
|
@ForwardClicked="forwardButtonAction"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// Components
|
||||||
|
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||||
|
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||||
|
import alert from "@/ux-components/alert/alert";
|
||||||
|
import questionChain from "@/common-components/question-chain/question-chain";
|
||||||
|
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||||
|
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||||
|
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
||||||
|
|
||||||
|
// Supporting Files
|
||||||
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
|
import store from "@/store";
|
||||||
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
|
import { Form, defineRule } from "vee-validate";
|
||||||
|
import { required } from "@/helpers/validation-rules";
|
||||||
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
|
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||||
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
|
||||||
|
// DEFINE VALIDATION RULES
|
||||||
|
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "molding-questions",
|
||||||
|
mixins: [vehicleQuestionsMixin],
|
||||||
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
// Call APIs
|
||||||
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedModel: [],
|
||||||
|
partsQuestionsData: this.$store.getters.pageData(fmgPageValues.MOLDING_QUESTIONS).partsOrQuestions.filter((p) => {
|
||||||
|
if (Array.isArray(p.partQuestions) && p.partQuestions.length > 0) {
|
||||||
|
return {
|
||||||
|
glassName: p.glassName,
|
||||||
|
glassLocation: p.glassLocation,
|
||||||
|
partQuestions: p.partQuestions,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
currentPartNum: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
AlertFewMoreQuestionsHeader() {
|
||||||
|
return this.getCmsContent("AlertPartsQuestions", "HeadlineText");
|
||||||
|
},
|
||||||
|
AlertFewMoreQuestionsCopy() {
|
||||||
|
return this.getCmsContent("AlertPartsQuestions", "BodyText");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
arePagePrerequisitesValid() {
|
||||||
|
return false; // TODO - DO TRUE TEST OF PAGEDATA
|
||||||
|
// return Object.keys(store.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)).length > 0;
|
||||||
|
},
|
||||||
|
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; }
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
backButtonAction() {
|
||||||
|
// route to move backwards
|
||||||
|
this.$router.navigate(
|
||||||
|
this.navigationScenarios.CLICKED_BACK,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
},
|
||||||
|
async forwardButtonAction() {
|
||||||
|
const partQuestionAnswersArray = this.partsQuestionsData.map((item) => {
|
||||||
|
return {
|
||||||
|
glassLocation: item.glassLocation,
|
||||||
|
glassName: item.glassName,
|
||||||
|
result: item.answerData.answerResult,
|
||||||
|
answeredQuestions: item.answerData.answeredQuestions,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// save to vuex store as order.damage.partQuestionAnswers (array)
|
||||||
|
await this.dispatchStoreAction(this.storeActions.SAVE_PART_QUESTION_ANSWERS, partQuestionAnswersArray, false);
|
||||||
|
|
||||||
|
// call API parts method
|
||||||
|
const partsLookup = await this.dispatchStoreAction(storeActions.GET_PARTS)
|
||||||
|
.catch(() => {
|
||||||
|
return this.$refs.funnelFooter.removeLoader();
|
||||||
|
});
|
||||||
|
|
||||||
|
const glassNameAndPartsForStore = partsLookup.data.glassNameAndPartsForStore;
|
||||||
|
|
||||||
|
const hasCapabilityQuestions = this.hasCapabilityQuestions(glassNameAndPartsForStore);
|
||||||
|
|
||||||
|
if (hasCapabilityQuestions) {
|
||||||
|
// if has capability questions
|
||||||
|
// go to capability-questions page
|
||||||
|
this.$router.navigate(this.navigationScenarios.HAS_CAPABILITY_QUESTIONS,this.$route,{},{},{partsOrQuestions: glassNameAndPartsForStore});
|
||||||
|
} else {
|
||||||
|
// if single parts only
|
||||||
|
const collectedGlassParts = this.reducedGlassPartsArray(glassNameAndPartsForStore);
|
||||||
|
// save to store lineItems.glassParts
|
||||||
|
this.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts);
|
||||||
|
// go to heritage quote page
|
||||||
|
this.$refs.loadingModal.showModal();
|
||||||
|
navigateToHeritageFunnel();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
selectedModel(model) {
|
||||||
|
this.moldingQuestionsData[model.partIndex].answerData = {
|
||||||
|
answerResult: model.answerResult,
|
||||||
|
answeredQuestions: model.answeredQuestions,
|
||||||
|
}
|
||||||
|
this.currentPartNum = model.partIndex + 1;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
funnelHeader,
|
||||||
|
vehicleBanner,
|
||||||
|
alert,
|
||||||
|
questionChain,
|
||||||
|
funnelSubHeader,
|
||||||
|
funnelFooter,
|
||||||
|
loadingModal,
|
||||||
|
Form,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.molding-questions {
|
||||||
|
.question-text {
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
span {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -6,12 +6,14 @@
|
||||||
v-slot="{ meta }"
|
v-slot="{ meta }"
|
||||||
>
|
>
|
||||||
<div class="page-container-grouped-styles part-questions">
|
<div class="page-container-grouped-styles part-questions">
|
||||||
|
<loadingModal ref="loadingModal"/>
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
<div class="fade-on-route-transition sub-container make-tall">
|
<div class="fade-on-route-transition sub-container make-tall">
|
||||||
<alert
|
<alert
|
||||||
ref="alertFewMoreQuestions"
|
ref="alertFewMoreQuestions"
|
||||||
|
cmsWidgetName="AlertFewMoreQuestionsWidget"
|
||||||
class="my-5"
|
class="my-5"
|
||||||
alertClass="alert-warning"
|
alertClass="alert-warning"
|
||||||
:manualHeadline="AlertFewMoreQuestionsHeader"
|
:manualHeadline="AlertFewMoreQuestionsHeader"
|
||||||
|
|
@ -21,8 +23,10 @@
|
||||||
<div v-for="(part, i) in partsQuestionsData" :key="i">
|
<div v-for="(part, i) in partsQuestionsData" :key="i">
|
||||||
<questionChain
|
<questionChain
|
||||||
ref="questionChain"
|
ref="questionChain"
|
||||||
v-model="selectedModel"
|
:key="part.key"
|
||||||
:questionData="part"
|
:keyString="part.key"
|
||||||
|
v-model="selectedAnswers[part.glassLocation + '-' + part.glassName]"
|
||||||
|
:questionData="part.partQuestions"
|
||||||
:partIndex="i"
|
:partIndex="i"
|
||||||
v-if="showThisPartQuestionChain(part, i)"
|
v-if="showThisPartQuestionChain(part, i)"
|
||||||
validationRules="questions-required"
|
validationRules="questions-required"
|
||||||
|
|
@ -31,7 +35,7 @@
|
||||||
<funnel-footer
|
<funnel-footer
|
||||||
ref="funnelFooter"
|
ref="funnelFooter"
|
||||||
cmsWidgetName="FunnelFooterWidget"
|
cmsWidgetName="FunnelFooterWidget"
|
||||||
:isForwardActionDisabled="waitingForLoad || !meta.valid"
|
:isForwardActionDisabled="!meta.valid"
|
||||||
@back-clicked="backButtonAction"
|
@back-clicked="backButtonAction"
|
||||||
@ForwardClicked="forwardButtonAction"
|
@ForwardClicked="forwardButtonAction"
|
||||||
/>
|
/>
|
||||||
|
|
@ -48,6 +52,7 @@ import alert from "@/ux-components/alert/alert";
|
||||||
import questionChain from "@/common-components/question-chain/question-chain";
|
import questionChain from "@/common-components/question-chain/question-chain";
|
||||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||||
|
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
||||||
|
|
||||||
// Supporting Files
|
// Supporting Files
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
|
|
@ -59,6 +64,8 @@ import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { Form, defineRule } from "vee-validate";
|
import { Form, defineRule } from "vee-validate";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
|
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||||
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
|
||||||
|
|
@ -86,12 +93,14 @@ export default {
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedModel: [],
|
partsQuestionsFromApi: this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS)?.partsOrQuestions.filter((p) => {
|
||||||
partsQuestionsData: [],
|
return Array.isArray(p.partQuestions) && p.partQuestions.length > 0;
|
||||||
waitingForLoad: true,
|
}),
|
||||||
|
selectedAnswers: {},
|
||||||
currentPartNum: 0,
|
currentPartNum: 0,
|
||||||
glassNameAndParts: [],
|
newAnswersArray: [],
|
||||||
hasMultipleParts: false,
|
partsQuestionsData: [],
|
||||||
|
foundDuplicateQuestions: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -102,35 +111,16 @@ export default {
|
||||||
return this.getCmsContent("AlertPartsQuestions", "BodyText");
|
return this.getCmsContent("AlertPartsQuestions", "BodyText");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
async mounted() {
|
mixins: [vehicleQuestionsMixin],
|
||||||
const questionData = await this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS);
|
mounted() {
|
||||||
|
this.LoadInitialPartsData();
|
||||||
// Map API result data, to part-questions data structure
|
|
||||||
this.partsQuestionsData = questionData.partsOrQuestions.filter((p) => {
|
|
||||||
if (Array.isArray(p.partQuestions) && p.partQuestions.length > 0) {
|
|
||||||
return {
|
|
||||||
glassName: p.glassName,
|
|
||||||
glassLocation: p.glassLocation,
|
|
||||||
partQuestions: p.partQuestions,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
this.waitingForLoad = false;
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
showThisPartQuestionChain(part, i) {
|
showThisPartQuestionChain(part, i) {
|
||||||
if (part.partQuestions?.length < 1) { return false; } // return false if only one partQuestion
|
if (part.partQuestions?.length < 1 || part.isSuppressedPart) { return false; } // return false if no partQuestions or if suppressed
|
||||||
if (this.currentPartNum === i || part.answerData?.answerResult.length > 0) { return true; }
|
if (this.currentPartNum === i || part.answerData?.answerResult?.length > 0) { return true; }
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
backButtonAction() {
|
|
||||||
// route to move backwards
|
|
||||||
this.$router.navigate(
|
|
||||||
this.navigationScenarios.CLICKED_BACK,
|
|
||||||
this.$route
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async forwardButtonAction() {
|
async forwardButtonAction() {
|
||||||
const partQuestionAnswersArray = this.partsQuestionsData.map((item) => {
|
const partQuestionAnswersArray = this.partsQuestionsData.map((item) => {
|
||||||
return {
|
return {
|
||||||
|
|
@ -138,65 +128,367 @@ export default {
|
||||||
glassName: item.glassName,
|
glassName: item.glassName,
|
||||||
result: item.answerData.answerResult,
|
result: item.answerData.answerResult,
|
||||||
answeredQuestions: item.answerData.answeredQuestions,
|
answeredQuestions: item.answerData.answeredQuestions,
|
||||||
|
isSuppressedPart: item.isSuppressedPart,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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)
|
// save to vuex store as order.damage.partQuestionAnswers (array)
|
||||||
await this.dispatchStoreAction(this.storeActions.SAVE_PART_QUESTION_ANSWERS, partQuestionAnswersArray, false);
|
await this.dispatchStoreAction(this.storeActions.SAVE_PART_QUESTION_ANSWERS, partQuestionAnswersArray, false);
|
||||||
|
|
||||||
// call API parts method
|
// call API parts method
|
||||||
const partsLookup = await this.dispatchStoreAction(storeActions.GET_PARTS)
|
const partsLookup = await this.dispatchStoreAction(storeActions.GET_PARTS)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
this.$refs.funnelFooter.removeLoader();
|
return this.$refs.funnelFooter.removeLoader();
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!partsLookup) { return }
|
const glassNameAndPartsForStore = partsLookup.data.glassNameAndParts;
|
||||||
|
this.navigateForward(glassNameAndPartsForStore);
|
||||||
this.glassNameAndParts = partsLookup.data.glassNameAndParts;
|
|
||||||
|
|
||||||
// test response data for multiple parts
|
|
||||||
this.hasMultipleParts = this.glassNameAndParts.some((glass) => glass.parts?.length > 1);
|
|
||||||
|
|
||||||
// loop through all glass items
|
|
||||||
const collectedGlassParts = [];
|
|
||||||
this.glassNameAndParts.forEach((glass) => {
|
|
||||||
if (Array.isArray(glass.parts) && glass.parts.length === 1) {
|
|
||||||
const singlePart = glass.parts[0];
|
|
||||||
collectedGlassParts.push({
|
|
||||||
"partNumber": singlePart.partNumber,
|
|
||||||
"description": singlePart.description,
|
|
||||||
"color": singlePart.color,
|
|
||||||
"requiresRecalibration": singlePart.requiresRecalibration,
|
|
||||||
"childParts": singlePart.childParts,
|
|
||||||
"price": singlePart.price,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
});
|
|
||||||
store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts);
|
|
||||||
this.navigateForward();
|
|
||||||
},
|
|
||||||
async navigateForward() {
|
|
||||||
if (this.hasMultipleParts) {
|
|
||||||
// if multiple parts on any glass
|
|
||||||
// go to vehicle-parts page and pass the partsData
|
|
||||||
this.$router.navigate(this.navigationScenarios.ANSWERED_QUESTIONS_WITH_MULTIPLE_PARTS,this.$route,{},{},{partsOrQuestions: this.glassNameAndParts});
|
|
||||||
} else {
|
|
||||||
// if single parts only
|
|
||||||
// go to quote page
|
|
||||||
this.$router.navigate(this.navigationScenarios.ANSWERED_QUESTIONS_WITH_SINGLE_PART,this.$route);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
arePagePrerequisitesValid() {
|
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;
|
||||||
},
|
},
|
||||||
},
|
handleAnswerUpdates(key, answer) { // only runs when all questions in a question-chain have been answered
|
||||||
watch: {
|
// when selectedAnswers updates, user has completed this part's question chain and has a final answer
|
||||||
selectedModel(model) {
|
// (does not get run for each invididual question's answer, only when
|
||||||
this.partsQuestionsData[model.partIndex].answerData = {
|
// all relevent questions for the current part have been answered)
|
||||||
answerResult: model.answerResult,
|
const glassPartWithAnswer = this.partsQuestionsData[answer.partIndex];
|
||||||
answeredQuestions: model.answeredQuestions,
|
const completeAnsweredQuestions = answer.answeredQuestions ? [...answer.answeredQuestions] : [];
|
||||||
|
const answeredQuestionIndexes = [];
|
||||||
|
|
||||||
|
// since user has answered a question differently than anything that was preloaded,
|
||||||
|
// then clear out the preloaded answers
|
||||||
|
this.selectedAnswers = {};
|
||||||
|
|
||||||
|
// 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) => {
|
||||||
|
// clear out any previously set answers
|
||||||
|
//delete pq.answerSelected;
|
||||||
|
pq.answerSelected = null;
|
||||||
|
|
||||||
|
// does pq.questionText match answeredQuestionText? (aka 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;
|
||||||
|
pq.answers[ansIndex].selected = null;
|
||||||
|
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;
|
||||||
|
glassPart.answerData = null;
|
||||||
|
// delete glassPart.isSuppressedPart;
|
||||||
|
glassPart.isSuppressedPart = null;
|
||||||
|
|
||||||
|
// 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].suppressDuplicateQuestion = true;
|
||||||
|
}
|
||||||
|
if (matchedAnswer.nextQuestionSequence) {
|
||||||
|
// ensure that accepted answer is NOT suppressed
|
||||||
|
glassPart.partQuestions[matchedAnswer.nextQuestionSequence - 1].suppressDuplicateQuestion = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
this.originalNextQuestionSequence = null;
|
||||||
|
// restore original answerResult
|
||||||
|
if (thisAns.originalAnswerResult) {
|
||||||
|
thisAns.answerResult = thisAns.originalAnswerResult;
|
||||||
|
thisAns.originalAnswerResult = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 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.suppressDuplicateQuestion = 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.suppressDuplicateQuestion;
|
||||||
|
});
|
||||||
|
|
||||||
|
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,
|
||||||
|
isDuplicateQuestion: pq.suppressDuplicateQuestion,
|
||||||
|
};
|
||||||
|
// 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.isSuppressedPart = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // END of if (matchedAnswer)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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 = glassPartWithAnswer.partQuestions[dupe - 1];
|
||||||
|
const dupeQuestionAnswer = dupeQuestion.answers.find((q) => q.selected === true);
|
||||||
|
|
||||||
|
glassPartWithAnswer.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.questionSequence 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,
|
||||||
|
isDuplicateQuestion: dupeQuestion.suppressDuplicateQuestion,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// make sure there are no duplicated dupes in the list...
|
||||||
|
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
|
||||||
|
glassPartWithAnswer.answerData = {
|
||||||
|
answerResult: answer.answerResult,
|
||||||
|
answeredQuestions: filteredCompleteAnsweredQuestions,
|
||||||
}
|
}
|
||||||
this.currentPartNum = model.partIndex + 1;
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
LoadInitialPartsData() {
|
||||||
|
|
||||||
|
// are there alreadyAnsweredQuestions?
|
||||||
|
const alreadyAnsweredQuestions = store.getters.damage.partQuestionAnswers;
|
||||||
|
|
||||||
|
this.partsQuestionsData = this.partsQuestionsFromApi.map((part, i) => {
|
||||||
|
part.key = part.glassLocation + "-" + part.glassName;
|
||||||
|
this.selectedAnswers[part.key] = [];
|
||||||
|
|
||||||
|
alreadyAnsweredQuestions?.forEach((savedPart) => {
|
||||||
|
if (!savedPart.glassLocation || !savedPart.glassName || !savedPart.answeredQuestions || !savedPart.result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (part.glassLocation === savedPart.glassLocation && part.glassName === savedPart.glassName) {
|
||||||
|
let answerString = "";
|
||||||
|
|
||||||
|
// matches; loop through answeredQuestions for matches
|
||||||
|
savedPart.answeredQuestions.forEach((aq) => {
|
||||||
|
if (!aq.questionNum || !aq.selectedAnswerText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// determine which answer was previously chosen
|
||||||
|
const theAns = part.partQuestions[aq.questionNum-1].answers.find((a) => {
|
||||||
|
return a.answerText.toUpperCase() === aq.selectedAnswerText.toUpperCase();
|
||||||
|
});
|
||||||
|
if (theAns.nextQuestionSequence) {
|
||||||
|
answerString = `${aq.questionNum}|nextQuestion|${theAns.nextQuestionSequence}|${theAns.answerText}`
|
||||||
|
} else {
|
||||||
|
answerString = `${aq.questionNum}|answer|${theAns.answerResult}|${theAns.answerText}`
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark this partQuestion as answered (question-chain will read this)
|
||||||
|
part.partQuestions[aq.questionNum-1].answerSelected = answerString;
|
||||||
|
// mark this partQuestion as duplicate if it is (question-chain will read this)
|
||||||
|
if (aq.isDuplicateQuestion) {
|
||||||
|
part.partQuestions[aq.questionNum-1].isDuplicateQuestion = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// advance the currentPartNum
|
||||||
|
this.currentPartNum = i;
|
||||||
|
|
||||||
|
// add answerData to current part
|
||||||
|
part.answerData = {
|
||||||
|
answerResult: savedPart.result,
|
||||||
|
answeredQuestions: savedPart.answeredQuestions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set up watch for each set of part questions, which gets updated when all questions for a part have been answered
|
||||||
|
this.$watch("selectedAnswers." + part.key, (newValue) => {
|
||||||
|
if (newValue) {
|
||||||
|
this.handleAnswerUpdates(part.key, newValue);
|
||||||
|
}
|
||||||
|
}, {deep: true})
|
||||||
|
|
||||||
|
return part;
|
||||||
|
});
|
||||||
|
|
||||||
|
// this block is purely for handling duplicate questions
|
||||||
|
alreadyAnsweredQuestions?.forEach((savedPart, partIndex) => {
|
||||||
|
savedPart.answeredQuestions.forEach((aq) => {
|
||||||
|
if (aq.isDuplicateQuestion) {
|
||||||
|
// find this one in partsQuestionData
|
||||||
|
const dupedPartsQuestion = this.partsQuestionsData[partIndex].partQuestions[aq.questionNum - 1];
|
||||||
|
dupedPartsQuestion.suppressDuplicateQuestion = true;
|
||||||
|
|
||||||
|
const dupedPartsQuestionAnswer = dupedPartsQuestion.answers.filter((ans) => {
|
||||||
|
return ans.answerText.toUpperCase() === aq.selectedAnswerText.toUpperCase();
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
let isDupedPartsQuestionFirst;
|
||||||
|
if (aq.questionNum === 1) { isDupedPartsQuestionFirst = true }
|
||||||
|
|
||||||
|
this.partsQuestionsData.forEach((glassPart, i) => {
|
||||||
|
|
||||||
|
// loop through this glass part's part questions, looking for a questionText match
|
||||||
|
glassPart.partQuestions.forEach((pq, pqIndex) => {
|
||||||
|
// if this dupedQ is the first in the array then
|
||||||
|
// suppress all questions for this part up until the answer's nextQuestionSequence
|
||||||
|
if (isDupedPartsQuestionFirst &&
|
||||||
|
dupedPartsQuestionAnswer.nextQuestionSequence &&
|
||||||
|
pq.questionSequence < dupedPartsQuestionAnswer.nextQuestionSequence) {
|
||||||
|
pq.suppressDuplicateQuestion = true;
|
||||||
|
}
|
||||||
|
pq.answers.forEach((thisAns) => {
|
||||||
|
if (thisAns.nextQuestionSequence === aq.questionNum) {
|
||||||
|
// update either the nextQuestionSequence or the answerResult
|
||||||
|
if (dupedPartsQuestionAnswer.nextQuestionSequence) {
|
||||||
|
thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence;
|
||||||
|
thisAns.nextQuestionSequence = dupedPartsQuestionAnswer.nextQuestionSequence;
|
||||||
|
// update this change to answerSelected
|
||||||
|
pq.answerSelected = `${pq.questionSequence}|nextQuestion|${thisAns.nextQuestionSequence}|${thisAns.answerText}`;
|
||||||
|
} else {
|
||||||
|
thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence;
|
||||||
|
thisAns.nextQuestionSequence = null;
|
||||||
|
thisAns.originalAnswerResult = thisAns.originalAnswerResult || thisAns.answerResult;
|
||||||
|
thisAns.answerResult = dupedPartsQuestionAnswer.answerResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -206,18 +498,19 @@ export default {
|
||||||
questionChain,
|
questionChain,
|
||||||
funnelSubHeader,
|
funnelSubHeader,
|
||||||
funnelFooter,
|
funnelFooter,
|
||||||
|
loadingModal,
|
||||||
Form,
|
Form,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.part-questions {
|
.part-questions {
|
||||||
.question-text {
|
.question-text {
|
||||||
margin-bottom: .5rem;
|
margin-bottom: .5rem;
|
||||||
span {
|
span {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<div v-if="isAvailable && this.answersToDisplay.length > 0" class="replace-options-question" :class="this.answersToDisplay.length < 2 ? 'd-none' : ''" aria-live="polite">
|
<div v-if="shouldDisplayReplaceOptionsQuestion" class="replace-options-question" :class="this.answersToDisplay.length < 2 ? 'd-none' : ''" aria-live="polite">
|
||||||
<buttonQuestion
|
<buttonQuestion
|
||||||
isWide
|
isWide
|
||||||
:questionText="questionText"
|
:questionText="questionText"
|
||||||
|
|
@ -80,11 +80,19 @@ export default ({
|
||||||
return ans;
|
return ans;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
shouldDisplayReplaceOptionsQuestion() {
|
||||||
|
return this.isAvailable && this.answersToDisplay.length > 0
|
||||||
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
isAvailable(val) {
|
isAvailable(val) {
|
||||||
// CHECK TO UPDATE SELECTED VALUES WHEN ISAVAILABLE IS TRUE
|
// CHECK TO UPDATE SELECTED VALUES WHEN ISAVAILABLE IS TRUE
|
||||||
val && this.updateSelectedValues();
|
val && this.updateSelectedValues();
|
||||||
|
},
|
||||||
|
shouldDisplayReplaceOptionsQuestion(shouldDisplayReplaceOptionsQuestion) {
|
||||||
|
if (!shouldDisplayReplaceOptionsQuestion) {
|
||||||
|
this.selectedValues = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ import store from "@/store";
|
||||||
import { defineRule } from "vee-validate";
|
import { defineRule } from "vee-validate";
|
||||||
import { required } from "@/helpers/validation-rules";
|
import { required } from "@/helpers/validation-rules";
|
||||||
import { errorMessages } from "@/constants/error-messages";
|
import { errorMessages } from "@/constants/error-messages";
|
||||||
|
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
|
||||||
|
|
||||||
// DEFINE VALIDATION RULES
|
// DEFINE VALIDATION RULES
|
||||||
defineRule("damage-side-required", required(errorMessages.DAMAGE_SIDE_REQUIRED));
|
defineRule("damage-side-required", required(errorMessages.DAMAGE_SIDE_REQUIRED));
|
||||||
|
|
@ -112,7 +113,7 @@ export default ({
|
||||||
this.selectedValues = this.getSideDoorReplacementOptions(this.selectedValues.selectedDoorSides, this.selectedValues.selectedDriverSideReplaceOptions, newValue);
|
this.selectedValues = this.getSideDoorReplacementOptions(this.selectedValues.selectedDoorSides, this.selectedValues.selectedDriverSideReplaceOptions, newValue);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
answersToDisplay(){
|
answersToDisplay(){
|
||||||
const filteredAnswers = Array.isArray(this.answersFromCms)
|
const filteredAnswers = Array.isArray(this.answersFromCms)
|
||||||
? this.answersFromCms.filter(ans =>
|
? this.answersFromCms.filter(ans =>
|
||||||
{
|
{
|
||||||
|
|
@ -128,10 +129,10 @@ export default ({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isDriverSideReplaceOptionsQuestionAvailable(){
|
isDriverSideReplaceOptionsQuestionAvailable(){
|
||||||
return (Array.isArray(this.selectedDoorSidesValues) && this.selectedDoorSidesValues.includes("DriverSide") && (Array.isArray(this.selectedDamageLocations) && this.selectedDamageLocations.includes("SideDoor")));
|
return (Array.isArray(this.selectedDoorSidesValues) && this.selectedDoorSidesValues.includes(damageLocationsSelected.DRIVERSIDE) && (Array.isArray(this.selectedDamageLocations) && this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR)));
|
||||||
},
|
},
|
||||||
isPassengerSideReplaceOptionsQuestionAvailable(){
|
isPassengerSideReplaceOptionsQuestionAvailable(){
|
||||||
return (Array.isArray(this.selectedDoorSidesValues) && this.selectedDoorSidesValues.includes("PassengerSide") && (Array.isArray(this.selectedDamageLocations) && this.selectedDamageLocations.includes("SideDoor")));
|
return (Array.isArray(this.selectedDoorSidesValues) && this.selectedDoorSidesValues.includes(damageLocationsSelected.PASSENGERSIDE) && (Array.isArray(this.selectedDamageLocations) && this.selectedDamageLocations.includes(damageLocationsSelected.SIDEDOOR)));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,7 @@ describe("vehicle-damage.vue", () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("alert", () => {
|
describe("alert", () => {
|
||||||
test("when displayVehicleChangeAlert router params is true, the alert: 'vehicleChangeAlert' should be visible", () => {
|
test("when displayVehicleChangeAlert router params is true, the alert: 'vehicleChangeAlert' should be rendered", () => {
|
||||||
// Arrange & Act
|
// Arrange & Act
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
mountOptionsMockData: {
|
mountOptionsMockData: {
|
||||||
|
|
@ -269,10 +269,10 @@ describe("vehicle-damage.vue", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.findComponent({ ref: 'vehicleChangeAlert' }).isVisible()).toBe(true);
|
expect(wrapper.findComponent({ ref: 'vehicleChangeAlert' }).exists()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("when displayVehicleChangeAlert router params is false, the alert: 'vehicleChangeAlert' should not be visible", () => {
|
test("when displayVehicleChangeAlert router params is false, the alert: 'vehicleChangeAlert' should not be rendered", () => {
|
||||||
// Arrange & Act
|
// Arrange & Act
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
mountOptionsMockData: {
|
mountOptionsMockData: {
|
||||||
|
|
@ -284,10 +284,10 @@ describe("vehicle-damage.vue", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.findComponent({ ref: 'vehicleChangeAlert' }).isVisible()).toBe(false);
|
expect(wrapper.findComponent({ ref: 'vehicleChangeAlert' }).exists()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("when displayVehicleChangeAlert router params is undefined, the alert: 'vehicleChangeAlert' should not be visible", () => {
|
test("when displayVehicleChangeAlert router params is undefined, the alert: 'vehicleChangeAlert' should not be rendered", () => {
|
||||||
// Arrange & Act
|
// Arrange & Act
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
mountOptionsMockData: {
|
mountOptionsMockData: {
|
||||||
|
|
@ -299,7 +299,7 @@ describe("vehicle-damage.vue", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.findComponent({ ref: 'vehicleChangeAlert' }).isVisible()).toBe(false);
|
expect(wrapper.findComponent({ ref: 'vehicleChangeAlert' }).exists()).toBe(false);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,11 +10,10 @@
|
||||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
|
<vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
|
||||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||||
<div class="fade-on-route-transition sub-container make-tall">
|
<div class="fade-on-route-transition sub-container make-tall">
|
||||||
<alert
|
<alert ref="vehicleChangeAlert"
|
||||||
ref="vehicleChangeAlert"
|
v-if="shouldDisplayVehicleChangeAlert"
|
||||||
class="mt-5 mb-0"
|
class="mt-5 mb-0"
|
||||||
cmsWidgetName="VehicleChangeAlert"
|
cmsWidgetName="VehicleChangeAlert"
|
||||||
v-show="shouldDisplayVehicleChangeAlert"
|
|
||||||
alertClass="alert-warning"
|
alertClass="alert-warning"
|
||||||
:isDismissible="false"
|
:isDismissible="false"
|
||||||
/>
|
/>
|
||||||
|
|
@ -32,9 +31,9 @@
|
||||||
:selectedDamageLocations="selectedDamageLocations"
|
:selectedDamageLocations="selectedDamageLocations"
|
||||||
/>
|
/>
|
||||||
<alert
|
<alert
|
||||||
|
v-if="hasRepairReplaceConflict"
|
||||||
class="my-5"
|
class="my-5"
|
||||||
cmsWidgetName="HasReplacementConflict"
|
cmsWidgetName="HasReplacementConflict"
|
||||||
v-show="hasRepairReplaceConflict"
|
|
||||||
alertClass="alert-danger"
|
alertClass="alert-danger"
|
||||||
:isDismissible="false"
|
:isDismissible="false"
|
||||||
/>
|
/>
|
||||||
|
|
@ -156,7 +155,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
if(store.getters.vehicle.carId){
|
if (store.getters.vehicle.carId) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,6 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||||
import store from "@/store";
|
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
name: "windshieldDamageTypeQuestion",
|
name: "windshieldDamageTypeQuestion",
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,19 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="windshield-options">
|
<div class="windshield-options">
|
||||||
<windshieldDamageTypeQuestion cmsWidgetName="WindshieldDamageTypeQuestion"
|
<windshieldDamageTypeQuestion cmsWidgetName="WindshieldDamageTypeQuestion"
|
||||||
:isAvailable=isWindshieldDamageLocation
|
:isAvailable="isWindshieldDamageLocation"
|
||||||
:suppressError="hasRepairReplaceConflict || showNoReplacementAvailableError"
|
:suppressError="hasRepairReplaceConflict || showNoReplacementAvailableError"
|
||||||
groupName="WindshieldDamageTypeQuestion"
|
groupName="WindshieldDamageTypeQuestion"
|
||||||
v-model="selectedWindshieldDamageTypeValue"
|
v-model="selectedWindshieldDamageTypeValue"
|
||||||
:validationRules="windshieldDamageTypeQuestionValidationRules"
|
:validationRules="windshieldDamageTypeQuestionValidationRules"
|
||||||
/>
|
/>
|
||||||
<alert
|
<alert
|
||||||
|
v-if="showNoReplacementAvailableError"
|
||||||
class="my-3"
|
class="my-3"
|
||||||
cmsWidgetName="NoReplacementAvailableError"
|
cmsWidgetName="NoReplacementAvailableError"
|
||||||
v-if="showNoReplacementAvailableError"
|
|
||||||
alertClass="alert-danger"
|
alertClass="alert-danger"
|
||||||
:isDismissible="false"
|
:isDismissible="false"
|
||||||
/>
|
/>
|
||||||
<windshieldChipCountQuestion cmsWidgetName="WindshieldChipCountQuestion"
|
<windshieldChipCountQuestion cmsWidgetName="WindshieldChipCountQuestion"
|
||||||
:isAvailable="isRepairOptionSelected && !hasRepairReplaceConflict"
|
:isAvailable="isRepairOptionSelected && !hasRepairReplaceConflict"
|
||||||
groupName="WindshieldChipCountQuestion"
|
groupName="WindshieldChipCountQuestion"
|
||||||
|
|
@ -30,9 +30,9 @@
|
||||||
isRequired
|
isRequired
|
||||||
/>
|
/>
|
||||||
<alert
|
<alert
|
||||||
|
v-if="hasSplitSingleConflict"
|
||||||
class="mt-5"
|
class="mt-5"
|
||||||
cmsWidgetName="SplitSingleConflict"
|
cmsWidgetName="SplitSingleConflict"
|
||||||
v-if="hasSplitSingleConflict"
|
|
||||||
alertClass="alert-danger"
|
alertClass="alert-danger"
|
||||||
:isDismissible="false"
|
:isDismissible="false"
|
||||||
/>
|
/>
|
||||||
|
|
@ -55,7 +55,7 @@ defineRule("windshield-damage-type-required", required(errorMessages.WINDSHIELD_
|
||||||
defineRule("windshield-chip-count-required", required(errorMessages.WINDSHIELD_CHIP_COUNT_REQUIRED));
|
defineRule("windshield-chip-count-required", required(errorMessages.WINDSHIELD_CHIP_COUNT_REQUIRED));
|
||||||
defineRule("windshield-replace-options-required", required(errorMessages.WINSHIELD_REPLACE_OPTIONS_REQUIRED));
|
defineRule("windshield-replace-options-required", required(errorMessages.WINSHIELD_REPLACE_OPTIONS_REQUIRED));
|
||||||
|
|
||||||
defineRule("check-for-repair-and-replace", (selectedWindshieldDamageType, [selectedDamageLocations]) => {
|
defineRule("check-for-repair-and-replace", (selectedWindshieldDamageType, selectedDamageLocations) => {
|
||||||
return selectedWindshieldDamageType.toString() != damageLocationsSelected.REPAIR ||
|
return selectedWindshieldDamageType.toString() != damageLocationsSelected.REPAIR ||
|
||||||
!selectedDamageLocations.includes(damageLocationsSelected.WINDSHIELD) ||
|
!selectedDamageLocations.includes(damageLocationsSelected.WINDSHIELD) ||
|
||||||
selectedDamageLocations.length === 1;
|
selectedDamageLocations.length === 1;
|
||||||
|
|
@ -137,15 +137,16 @@ export default ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isWindshieldDamageLocation() {
|
isWindshieldDamageLocation() {
|
||||||
return this.selectedDamageLocations.some(selectedDamages =>
|
return this.selectedDamageLocations.some(selectedDamageLocation => selectedDamageLocation === damageLocationsSelected.WINDSHIELD);
|
||||||
{
|
|
||||||
return Boolean(selectedDamages.toUpperCase() === "WINDSHIELD");
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
isRepairOptionSelected(){
|
isRepairOptionSelected(){
|
||||||
|
if (!this.selectedWindshieldDamageTypeValue) return false;
|
||||||
|
|
||||||
return this.selectedWindshieldDamageTypeValue === damageLocationsSelected.REPAIR && this.isWindshieldDamageLocation;
|
return this.selectedWindshieldDamageTypeValue === damageLocationsSelected.REPAIR && this.isWindshieldDamageLocation;
|
||||||
},
|
},
|
||||||
isReplaceOptionSelected(){
|
isReplaceOptionSelected(){
|
||||||
|
if (!this.selectedWindshieldDamageTypeValue) return false;
|
||||||
|
|
||||||
return this.selectedWindshieldDamageTypeValue === damageLocationsSelected.REPLACE && this.isWindshieldDamageLocation;
|
return this.selectedWindshieldDamageTypeValue === damageLocationsSelected.REPLACE && this.isWindshieldDamageLocation;
|
||||||
},
|
},
|
||||||
isWindshieldReplaceAvailable() {
|
isWindshieldReplaceAvailable() {
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,7 @@ export default {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
if (store.getters.vehicle.year){
|
if (store.getters.vehicle.year){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,10 @@ import { shallowMount } from "@vue/test-utils";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import { nextTick } from "vue";
|
import { nextTick } from "vue";
|
||||||
import baseMixin from "@/mixins/base-mixin.js";
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import store from "@/store";
|
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.
|
// Mock our module for promises.
|
||||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||||
|
|
@ -21,6 +24,10 @@ jest.mock("@/helpers/cms-content-helper", () => ({
|
||||||
fetchCmsContentForPage: jest.fn(),
|
fetchCmsContentForPage: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
|
||||||
|
navigateToHeritageFunnel: jest.fn()
|
||||||
|
}));
|
||||||
|
|
||||||
// Mock store
|
// Mock store
|
||||||
jest.mock("@/store", () => { return {}; }, { virtual: true });
|
jest.mock("@/store", () => { return {}; }, { virtual: true });
|
||||||
|
|
||||||
|
|
@ -140,7 +147,7 @@ describe("vehicle-parts.vue", () => {
|
||||||
test("Initial data, should populate this.glassParts", async () => {
|
test("Initial data, should populate this.glassParts", async () => {
|
||||||
|
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
store.getters.pageData.mockReturnValue(basePartResponse);
|
||||||
store.getters.lineItems = { glassParts: [{ partNumber: 'DB12209YPYNOEM' }] }
|
store.getters.lineItems = { glassParts: [{ partNumber: 'DB12209YPYNOEM' }] }
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
|
|
@ -173,12 +180,8 @@ describe("vehicle-parts.vue", () => {
|
||||||
expect(wrapper.vm.glassParts).toEqual({ "Rear-Stationary": { "Rear": ['DB12209YPYNOEM'] } });
|
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
|
//Arrange
|
||||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
|
||||||
store.getters.lineItems = { glassParts: null }
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
mountOptionsMockData: {
|
mountOptionsMockData: {
|
||||||
router: {
|
router: {
|
||||||
|
|
@ -191,7 +194,25 @@ describe("vehicle-parts.vue", () => {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
store: {
|
store: {
|
||||||
getters: store.getters
|
getters: {
|
||||||
|
pageData: () => {
|
||||||
|
return {
|
||||||
|
partsOrQuestions: [
|
||||||
|
{
|
||||||
|
glassName: "Stationary",
|
||||||
|
glassLocation: "Rear",
|
||||||
|
parts: null,
|
||||||
|
partQuestions: [{
|
||||||
|
testProperty: "some value"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lineItems: {
|
||||||
|
glassParts: null
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -207,17 +228,12 @@ describe("vehicle-parts.vue", () => {
|
||||||
wrapper.vm.backButtonAction();
|
wrapper.vm.backButtonAction();
|
||||||
|
|
||||||
//Assert
|
//Assert
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS, wrapper.vm.$route);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("ForwardButtonAction triggers a router.navigate change and saves selected parts to store", async () => {
|
test("User did not have part questions > BackButtonAction triggers a router.navigate change with correct scenario", async () => {
|
||||||
|
|
||||||
//Arrange
|
//Arrange
|
||||||
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
|
||||||
store.getters.lineItems = { glassParts: {} }
|
|
||||||
store.commit = jest.fn();
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
const { wrapper } = setupMocks({
|
||||||
mountOptionsMockData: {
|
mountOptionsMockData: {
|
||||||
router: {
|
router: {
|
||||||
|
|
@ -229,6 +245,85 @@ describe("vehicle-parts.vue", () => {
|
||||||
fmgPage: 'vehicle-parts',
|
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_TO_GO_TO_VIN_LOOKUP, wrapper.vm.$route);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
test("ForwardButtonAction triggers a router.navigate change if there are child part questions", async () => {
|
||||||
|
|
||||||
|
//Arrange
|
||||||
|
store.getters.pageData.mockReturnValueOnce({
|
||||||
|
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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
store.getters.lineItems = { glassParts: {} }
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
mountOptionsMockData: {
|
||||||
|
router: {
|
||||||
|
navigate: jest.fn()
|
||||||
|
},
|
||||||
|
route: {
|
||||||
|
query: {
|
||||||
|
fmgPage: 'vehicle-parts',
|
||||||
|
}
|
||||||
|
},
|
||||||
store: {
|
store: {
|
||||||
getters: store.getters,
|
getters: store.getters,
|
||||||
commit: store.commit
|
commit: store.commit
|
||||||
|
|
@ -236,6 +331,70 @@ describe("vehicle-parts.vue", () => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
wrapper.setData({ glassParts: { "Rear-Stationary": { partNumber: 'FW03861GTYN' } } });
|
||||||
|
|
||||||
|
//Act
|
||||||
|
vehicleParts.beforeRouteEnter.call(
|
||||||
|
wrapper.vm,
|
||||||
|
{ query: { fmgPage: "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
|
||||||
|
store.getters.pageData.mockReturnValueOnce({
|
||||||
|
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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
store.getters.lineItems = { glassParts: {} }
|
||||||
|
store.getters.vehicle = { carId: "TEST_CAR_ID" }
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
mountOptionsMockData: {
|
||||||
|
router: {
|
||||||
|
navigate: jest.fn()
|
||||||
|
},
|
||||||
|
route: {
|
||||||
|
query: {
|
||||||
|
fmgPage: 'vehicle-parts',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
store: {
|
||||||
|
getters: store.getters,
|
||||||
|
commit: store.commit
|
||||||
|
},
|
||||||
|
actionList: [
|
||||||
|
{
|
||||||
|
actionName: storeActions.GET_CAPABILITY_QUESTIONS,
|
||||||
|
data: []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
wrapper.setData({ glassParts: { "Rear-Stationary": { partNumber: 'DB12209GTYN' } } });
|
wrapper.setData({ glassParts: { "Rear-Stationary": { partNumber: 'DB12209GTYN' } } });
|
||||||
|
|
||||||
//Act
|
//Act
|
||||||
|
|
@ -251,6 +410,51 @@ describe("vehicle-parts.vue", () => {
|
||||||
//Assert
|
//Assert
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("ForwardButtonAction saves selected parts to store if no molding or capability questions", async () => {
|
||||||
|
|
||||||
|
//Arrange
|
||||||
|
store.getters.pageData.mockReturnValueOnce(basePartResponse);
|
||||||
|
store.getters.lineItems = { glassParts: {} }
|
||||||
|
store.getters.vehicle = { carId: "TEST_CAR_ID" }
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks({
|
||||||
|
mountOptionsMockData: {
|
||||||
|
router: {
|
||||||
|
navigate: jest.fn()
|
||||||
|
},
|
||||||
|
route: {
|
||||||
|
query: {
|
||||||
|
fmgPage: 'vehicle-parts',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
store: {
|
||||||
|
getters: store.getters,
|
||||||
|
commit: store.commit
|
||||||
|
},
|
||||||
|
navigateToHeritageFunnel: jest.fn()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
wrapper.vm.$store.commit = jest.fn();
|
||||||
|
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
|
||||||
|
|
||||||
|
wrapper.setData({ glassParts: { "Rear-Stationary": { partNumber: 'DB12209GTYN' } } });
|
||||||
|
|
||||||
|
//Act
|
||||||
|
vehicleParts.beforeRouteEnter.call(
|
||||||
|
wrapper.vm,
|
||||||
|
{ query: { fmgPage: "vehicle-parts" } },
|
||||||
|
undefined,
|
||||||
|
(c) => c(wrapper.vm)
|
||||||
|
);
|
||||||
|
|
||||||
|
await wrapper.vm.forwardButtonAction();
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(wrapper.vm.$store.commit).toHaveBeenCalled();
|
||||||
|
expect(navigateToHeritageFunnel).toHaveBeenCalled();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {} }) {
|
function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {} }) {
|
||||||
|
|
@ -288,6 +492,7 @@ function setupMocks({ pageHeaderWidgetHeaderText = {}, mountOptionsMockData = {}
|
||||||
|
|
||||||
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
||||||
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
|
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
|
||||||
|
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
|
||||||
|
|
||||||
return { wrapper, apiPromise };
|
return { wrapper, apiPromise };
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
|
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm">
|
||||||
<div class="page-container-grouped-styles vehicle-parts">
|
<div class="page-container-grouped-styles vehicle-parts">
|
||||||
|
<loadingModal ref="loadingModal"/>
|
||||||
<funnelHeader ref="funnelHeader" cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader ref="funnelHeader" cmsWidgetName="FunnelHeaderWidget" />
|
||||||
<vehicleBanner
|
<vehicleBanner
|
||||||
ref="vehicleBanner"
|
ref="vehicleBanner"
|
||||||
|
|
@ -49,217 +50,182 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
// Components
|
// Components
|
||||||
import glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass-part-question";
|
import glassPartQuestion from "@/layouts/vehicle-parts/glass-part-question/glass-part-question";
|
||||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||||
import alert from "@/ux-components/alert/alert";
|
import alert from "@/ux-components/alert/alert";
|
||||||
// Supporting Files
|
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
||||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
// Supporting Files
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import store from "@/store";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { Form } from "vee-validate";
|
import { Form } from "vee-validate";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
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 {
|
export default {
|
||||||
name: "vehicle-parts",
|
name: "vehicle-parts",
|
||||||
|
mixins: [vehicleQuestionsMixin],
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
// Call APIs
|
// Call APIs
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
// Settle promises and get results
|
// Settle promises and get results
|
||||||
const promiseResultMap = [
|
const promiseResultMap = [
|
||||||
{
|
{
|
||||||
resultKey: "cmsContent",
|
resultKey: "cmsContent",
|
||||||
promise: cmsContentPromise,
|
promise: cmsContentPromise,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
// Call the "next" function to complete the transition to this page.
|
// Call the "next" function to complete the transition to this page.
|
||||||
next((vm) => {
|
next((vm) => {
|
||||||
vm.setCmsContent(resultMap.cmsContent);
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
|
|
||||||
// Glass Part Question dynamic component
|
// Glass Part Question dynamic component
|
||||||
Object.keys(vm.$refs)
|
Object.keys(vm.$refs)
|
||||||
.filter(
|
.filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined)
|
||||||
(r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined
|
.forEach((c) =>
|
||||||
)
|
|
||||||
.forEach((c) =>
|
|
||||||
vm.$refs[c][0].initializeComponent({
|
vm.$refs[c][0].initializeComponent({
|
||||||
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
|
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
|
||||||
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget,
|
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
glassParts: {},
|
glassParts: {},
|
||||||
alertWidgetData: Object,
|
alertWidgetData: Object,
|
||||||
alreadyPopulatedPartsData: {},
|
alreadyPopulatedPartsData: {},
|
||||||
};
|
};
|
||||||
},
|
|
||||||
components: {
|
|
||||||
Form,
|
|
||||||
glassPartQuestion,
|
|
||||||
funnelHeader,
|
|
||||||
vehicleBanner,
|
|
||||||
funnelSubHeader,
|
|
||||||
funnelFooter,
|
|
||||||
alert,
|
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isForwardActionDisabled() {
|
isForwardActionDisabled() {
|
||||||
return (
|
return (
|
||||||
Object.keys(this.matchedParts).length !==
|
this.selectedGlassPartNumbers.length !== this.PartsFromApi.partsOrQuestions.length
|
||||||
this.PartsFromApi.partsOrQuestions.length
|
);
|
||||||
);
|
},
|
||||||
},
|
selectedGlassPartNumbers () {
|
||||||
matchedParts() {
|
// Compile all selected parts from the page.
|
||||||
const matchedParts = [];
|
const numberArray = [];
|
||||||
|
for (let glassPart of Object.values(this.glassParts)) {
|
||||||
|
if (glassPart?.partNumber) { numberArray.push(glassPart.partNumber) }
|
||||||
|
}
|
||||||
|
return numberArray;
|
||||||
|
},
|
||||||
|
PartsOrQuestions() {
|
||||||
|
const partsData = this.PartsFromApi;
|
||||||
|
|
||||||
// Match them to the parts from the API.
|
// Map API result data, to vehicle-parts data structure
|
||||||
this.PartsFromApi.partsOrQuestions.forEach((part) => {
|
const mappedData = partsData.partsOrQuestions.map((g) => {
|
||||||
const selectedPartForGlassLocationAndName =
|
return {
|
||||||
this.glassParts[`${part.glassLocation}-${part.glassName}`];
|
glassName: g.glassName,
|
||||||
const selectedPartData = part.parts.filter(
|
glassLocation: g.glassLocation,
|
||||||
(part) =>
|
colorAnswers: g.parts?.reduce((arr, p) => {
|
||||||
selectedPartForGlassLocationAndName &&
|
arr.push({
|
||||||
part.partNumber == selectedPartForGlassLocationAndName?.partNumber
|
ColorAnswerText: p.color,
|
||||||
)[0];
|
FeatureAnswers: [
|
||||||
|
{
|
||||||
|
FeatureAnswerText: p.description === "" ? p.color : p.description,
|
||||||
|
PartNumber: p.partNumber,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
|
}, []),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
if (selectedPartData) matchedParts.push(selectedPartData);
|
return mappedData;
|
||||||
});
|
},
|
||||||
|
|
||||||
return matchedParts;
|
PartsFromApi() {
|
||||||
},
|
return this.$store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
|
||||||
PartsOrQuestions() {
|
},
|
||||||
const partsData = this.PartsFromApi;
|
|
||||||
|
|
||||||
// Map API result data, to vehicle-parts data structure
|
RefPrefix() {
|
||||||
const mappedData = partsData.partsOrQuestions.map((g) => {
|
return "partQuestion";
|
||||||
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 store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
|
|
||||||
},
|
|
||||||
|
|
||||||
RefPrefix() {
|
|
||||||
return "partQuestion";
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
arePagePrerequisitesValid() {
|
arePagePrerequisitesValid() {
|
||||||
// Check if isRepair is populated and if the pageData we need is here (Parts data)
|
// Check if isRepair is populated and if the pageData we need is here (Parts data)
|
||||||
if (
|
return store.getters.damage.isRepair != null && store.getters.pageData(fmgPageValues.VEHICLE_PARTS) &&
|
||||||
store.getters.damage.isRepair != null &&
|
Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS)).length !== 0;
|
||||||
Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS))
|
},
|
||||||
.length !== 0
|
async forwardButtonAction() {
|
||||||
) {
|
const matchedParts = [];
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
// Match them to the parts from the API.
|
||||||
},
|
for (let [key, value] of Object.entries(
|
||||||
backButtonAction() {
|
this.PartsFromApi.partsOrQuestions
|
||||||
this.$router.navigate(
|
)) {
|
||||||
this.navigationScenarios.CLICKED_BACK,
|
for (let [partKey, partValue] of Object.entries(value.parts)) {
|
||||||
this.$route
|
const currentPart = this.PartsFromApi.partsOrQuestions[key].parts[partKey];
|
||||||
);
|
|
||||||
},
|
|
||||||
async forwardButtonAction() {
|
|
||||||
const selectedGlassPartNumbers = [];
|
|
||||||
const matchedParts = [];
|
|
||||||
|
|
||||||
// Compile all selected parts from the page.
|
const isMatched = this.selectedGlassPartNumbers.some(
|
||||||
for (let [key, value] of Object.entries(this.glassParts)) {
|
(p) => p === currentPart.partNumber
|
||||||
for (let [glassKey, glassValue] of Object.entries(value)) {
|
);
|
||||||
selectedGlassPartNumbers.push(glassValue[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Match them to the parts from the API.
|
if (isMatched) {
|
||||||
for (let [key, value] of Object.entries(
|
matchedParts.push({
|
||||||
this.PartsFromApi.partsOrQuestions
|
glassLocation: value.glassLocation,
|
||||||
)) {
|
glassName: value.glassName,
|
||||||
for (let [partKey, partValue] of Object.entries(value.parts)) {
|
parts: [currentPart]
|
||||||
const currentPart =
|
});
|
||||||
this.PartsFromApi.partsOrQuestions[key].parts[partKey];
|
}
|
||||||
const isMatched = selectedGlassPartNumbers.some(
|
}
|
||||||
(p) => p === currentPart.partNumber
|
}
|
||||||
);
|
// If no parts could be matched, throw an error (isForwardActionDisabled is based off of matchedParts)
|
||||||
|
if (this.isForwardActionDisabled) {
|
||||||
if (isMatched) {
|
this.$refs.funnelFooter.removeLoader();
|
||||||
matchedParts.push(currentPart);
|
throw new Error("Could not match any parts to the selected parts");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If no parts could be matched, throw an error.
|
// Navigate to the next page
|
||||||
if (this.isForwardActionDisabled) {
|
this.navigateForward(matchedParts);
|
||||||
this.$refs.funnelFooter.removeLoader();
|
},
|
||||||
throw new Error("Could not match any parts to the selected parts");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save parts to the store.
|
LoadInitialPartsData() {
|
||||||
await this.dispatchStoreAction(
|
const partsData = this.PartsFromApi;
|
||||||
storeActions.SAVE_GLASS_PARTS,
|
const alreadyPopulatedPartsData =
|
||||||
matchedParts
|
this.$store.getters.lineItems.glassParts === null
|
||||||
);
|
? {}
|
||||||
|
: this.$store.getters.lineItems.glassParts;
|
||||||
|
|
||||||
// Navigate to the next page.
|
partsData.partsOrQuestions.map((g) => {
|
||||||
this.$router.navigate(
|
// If the part is already populated, use the value from the store and populate the v-model.
|
||||||
this.navigationScenarios.SELECTED_PARTS,
|
Object.keys(alreadyPopulatedPartsData).forEach((key) => {
|
||||||
this.$route
|
const partNumber = alreadyPopulatedPartsData[key].partNumber;
|
||||||
);
|
g.parts.forEach((p) => {
|
||||||
},
|
if (p.partNumber === partNumber) {
|
||||||
|
this.glassParts[g.glassLocation + "-" + g.glassName] = {
|
||||||
LoadInitialPartsData() {
|
[g.glassLocation]: [partNumber],
|
||||||
const partsData = this.PartsFromApi;
|
};
|
||||||
const alreadyPopulatedPartsData =
|
}
|
||||||
this.$store.getters.lineItems.glassParts === null
|
});
|
||||||
? {}
|
});
|
||||||
: this.$store.getters.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(alreadyPopulatedPartsData).forEach((key) => {
|
|
||||||
const partNumber = alreadyPopulatedPartsData[key].partNumber;
|
|
||||||
g.parts.forEach((p) => {
|
|
||||||
if (p.partNumber === partNumber) {
|
|
||||||
this.glassParts[g.glassLocation + "-" + g.glassName] = {
|
|
||||||
[g.glassLocation]: [partNumber],
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
},
|
||||||
});
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.LoadInitialPartsData();
|
this.LoadInitialPartsData();
|
||||||
},
|
},
|
||||||
};
|
components: {
|
||||||
|
Form,
|
||||||
|
glassPartQuestion,
|
||||||
|
funnelHeader,
|
||||||
|
vehicleBanner,
|
||||||
|
funnelSubHeader,
|
||||||
|
funnelFooter,
|
||||||
|
alert,
|
||||||
|
loadingModal,
|
||||||
|
},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,15 @@ jest.mock("@/store", () => ({
|
||||||
vehicle: {
|
vehicle: {
|
||||||
model: "TL",
|
model: "TL",
|
||||||
},
|
},
|
||||||
|
applicationUser:{
|
||||||
|
pageData: {
|
||||||
|
"part-questions": null,
|
||||||
|
"vehicle-make": {},
|
||||||
|
"vehicle-model": {},
|
||||||
|
"vehicle-style": {},
|
||||||
|
"vehicle-damage": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,14 +3,21 @@
|
||||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||||
<div class="select-car">
|
<div class="select-car">
|
||||||
<div class="select-car-form rounded text-center">
|
<div class="select-car-form rounded text-center">
|
||||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" displayGenericVehicleImage />
|
<vehicleBanner
|
||||||
|
cmsWidgetName="VehicleBannerWidget"
|
||||||
|
displayGenericVehicleImage
|
||||||
|
/>
|
||||||
<funnelSubHeader
|
<funnelSubHeader
|
||||||
cmsWidgetName="FunnelSubHeaderWidget"
|
cmsWidgetName="FunnelSubHeaderWidget"
|
||||||
:hasBackButton="true"
|
:hasBackButton="true"
|
||||||
@click-event="backButtonAction"
|
@click-event="backButtonAction"
|
||||||
/>
|
/>
|
||||||
<div class="fade-on-route-transition">
|
<div class="fade-on-route-transition">
|
||||||
<styleQuestion v-model="selectedStyle" ref="styleQuestion" cmsWidgetName="VehicleStyleQuestion" />
|
<styleQuestion
|
||||||
|
v-model="selectedStyle"
|
||||||
|
ref="styleQuestion"
|
||||||
|
cmsWidgetName="VehicleStyleQuestion"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -29,14 +36,19 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
|
import router from "@/router";
|
||||||
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "vehicle-style",
|
name: "vehicle-style",
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedStyle: null,
|
selectedStyle: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {},
|
computed: {},
|
||||||
|
|
||||||
async beforeRouteEnter(to, from, next) {
|
async beforeRouteEnter(to, from, next) {
|
||||||
|
|
@ -58,14 +70,31 @@ export default {
|
||||||
];
|
];
|
||||||
|
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
|
const visitedVehicleDamage = JSON.stringify(store.getters.applicationUser.pageData).indexOf(fmgPageValues.VEHICLE_DAMAGE) < 0 ? false : true;
|
||||||
|
|
||||||
// Call the "next" function to complete the transition to this page.
|
// Call the "next" function to complete the transition to this page.
|
||||||
next((vm) => {
|
|
||||||
vm.setCmsContent(resultMap.cmsContent);
|
// If we have exactly one style then navigate directly to vehicle-damage
|
||||||
vm.$refs.styleQuestion.initializeComponent(
|
if(resultMap.styleQuestionInitialData.length === 1 && !visitedVehicleDamage) {
|
||||||
resultMap.styleQuestionInitialData
|
store.commit(storeMutations.UPDATE_STYLE, resultMap.styleQuestionInitialData[0]);
|
||||||
);
|
|
||||||
});
|
await store.dispatch(storeActions.SET_VEHICLE,
|
||||||
|
{
|
||||||
|
year: store.getters.vehicle.year,
|
||||||
|
make: store.getters.vehicle.make,
|
||||||
|
model: store.getters.vehicle.model,
|
||||||
|
style: store.getters.vehicle.style,
|
||||||
|
});
|
||||||
|
|
||||||
|
//emulate selecting the vehicle style
|
||||||
|
router.overrideNavigation(navigationScenarios.SELECTED_STYLE, to, next);
|
||||||
|
|
||||||
|
} else{
|
||||||
|
next( (vm) => {
|
||||||
|
vm.setCmsContent(resultMap.cmsContent);
|
||||||
|
vm.$refs.styleQuestion.initializeComponent(resultMap.styleQuestionInitialData)
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,11 @@ import store from "@/store";
|
||||||
jest.mock("@/store", () => ({
|
jest.mock("@/store", () => ({
|
||||||
commit: jest.fn(),
|
commit: jest.fn(),
|
||||||
dispatch: jest.fn(),
|
dispatch: jest.fn(),
|
||||||
|
getters: {
|
||||||
|
applicationUser: {
|
||||||
|
experiments: []
|
||||||
|
}
|
||||||
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Mock our module for promises.
|
// Mock our module for promises.
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { experimentUniverses } from "@/constants/experiments";
|
import { experimentUniverses } from "@/constants/experiments";
|
||||||
import { getDeviceIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper";
|
import { getDeviceIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
|
|
||||||
|
|
@ -48,14 +49,19 @@ export default {
|
||||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||||
const yearQuestionInitialDataPromise = yearQuestion.methods.loadInitialData();
|
const yearQuestionInitialDataPromise = yearQuestion.methods.loadInitialData();
|
||||||
|
|
||||||
// Log experiment exposure
|
const experimentForLogging = store.getters.applicationUser.experiments.find(e => e.universeName === experimentUniverses.CONCEPT_FUNNEL);
|
||||||
const logExperimentExposurePromise = baseMixin.methods.dispatchStoreAction(storeActions.LOG_EXPERIMENT_EXPOSURE,
|
|
||||||
{
|
// If the concept funnel experiment is found, as it should be when coming from safelite.com, then log the experiment exposure.
|
||||||
userId: getDeviceIdValue(),
|
if (experimentForLogging !== undefined) {
|
||||||
sessionKey: getSessionKeyValue(),
|
// Log experiment exposure
|
||||||
pageName: to.query.fmgPage,
|
baseMixin.methods.dispatchStoreAction(storeActions.LOG_EXPERIMENT_EXPOSURE,
|
||||||
universeName: experimentUniverses.CONCEPT_FUNNEL
|
{
|
||||||
}, false);
|
userId: getDeviceIdValue(),
|
||||||
|
sessionKey: getSessionKeyValue(),
|
||||||
|
pageName: to.query.fmgPage,
|
||||||
|
experiment: experimentForLogging
|
||||||
|
}, false);
|
||||||
|
}
|
||||||
|
|
||||||
// Settle promises and get results
|
// Settle promises and get results
|
||||||
const promiseResultMap = [
|
const promiseResultMap = [
|
||||||
|
|
@ -67,10 +73,6 @@ export default {
|
||||||
resultKey: "yearQuestionInitialData",
|
resultKey: "yearQuestionInitialData",
|
||||||
promise: yearQuestionInitialDataPromise,
|
promise: yearQuestionInitialDataPromise,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
resultKey: "logExperimentExposure",
|
|
||||||
promise: logExperimentExposurePromise,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
let resultMap = await settleAllPromises(promiseResultMap);
|
let resultMap = await settleAllPromises(promiseResultMap);
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,9 @@ import vinInformation from "@/layouts/vin-lookup/vin-information/vin-information
|
||||||
describe("vinInformation.vue", () => {
|
describe("vinInformation.vue", () => {
|
||||||
it("Should return input class active if isActive is true", async () => {
|
it("Should return input class active if isActive is true", async () => {
|
||||||
// Act
|
// Act
|
||||||
const wrapper = shallowMount(vinInformation);
|
const wrapper = shallowMount(vinInformation, {
|
||||||
|
mixins: [mockMixin]
|
||||||
|
});
|
||||||
|
|
||||||
await wrapper.setData({
|
await wrapper.setData({
|
||||||
isActive: true,
|
isActive: true,
|
||||||
|
|
@ -16,3 +18,9 @@ describe("vinInformation.vue", () => {
|
||||||
expect(wrapper.vm.isActive).toEqual(false);
|
expect(wrapper.vm.isActive).toEqual(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const mockMixin = {
|
||||||
|
methods: {
|
||||||
|
getCmsContent: jest.fn()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="vin-information">
|
<div class="vin-information">
|
||||||
<div class="vin-toggle d-inline-flex" :class="[isActive ? 'active' : '']" @click="toggleClass()">
|
<div class="vin-toggle d-inline-flex mt-2" :class="[isActive ? 'active' : '']" @click="toggleClass()">
|
||||||
<textLink :text="HeadlineText" linkType="text" href="#!" />
|
<textLink
|
||||||
|
linkType="text"
|
||||||
|
href="#!"
|
||||||
|
:text="WhereCanIFindMyVINHeadline"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="vin-info">
|
<div class="vin-info">
|
||||||
<div class="mt-2" v-html="BodyText"></div>
|
<div class="mt-2" v-html="WhereCanIFindMyVINBodyCopy"></div>
|
||||||
<img :src="OptionalImage" alt="" />
|
<img :src="WhereCanIFindMyVINImage" alt="" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -13,6 +17,8 @@
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import textLink from "@/ux-components/text-link/text-link";
|
import textLink from "@/ux-components/text-link/text-link";
|
||||||
|
//Supporting files
|
||||||
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
export default {
|
export default {
|
||||||
name: "vinInformation",
|
name: "vinInformation",
|
||||||
components: {
|
components: {
|
||||||
|
|
@ -21,15 +27,23 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isActive: false,
|
isActive: false,
|
||||||
HeadlineText: "Where can I find my VIN?",
|
|
||||||
BodyText: "<p class='mb-2'>You can find your VIN in a few places:</p> <ol> <li>Driver side windshield</li> <li>Driver side doorjamb</li> <li>Vehicle registration card</li> <li>Auto insurance card or app</li> </ol>",
|
|
||||||
OptionalImage: "https://digitalconsumercms-dev.safelite.com/images/default-source/default-album/vin_location.svg?sfvrsn=63a9a2cf_3",
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleClass: function(event){
|
toggleClass: function(event){
|
||||||
this.isActive = !this.isActive;
|
this.isActive = !this.isActive;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
WhereCanIFindMyVINHeadline(){
|
||||||
|
return this.getCmsContent("WhereCanIFindMyVINToggle", "HeaderText");
|
||||||
|
},
|
||||||
|
WhereCanIFindMyVINBodyCopy(){
|
||||||
|
return this.getCmsContent("WhereCanIFindMyVINToggle", "BodyText");
|
||||||
|
},
|
||||||
|
WhereCanIFindMyVINImage(){
|
||||||
|
return this.getCmsContent("WhereCanIFindMyVINToggle", "Image");
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,6 +79,7 @@ export default {
|
||||||
max-height: 500px;
|
max-height: 500px;
|
||||||
transition: all 250ms ease-in;
|
transition: all 250ms ease-in;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.vin-info {
|
.vin-info {
|
||||||
|
|
@ -72,8 +87,12 @@ export default {
|
||||||
transition: all 250ms ease-out;
|
transition: all 250ms ease-out;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
p {
|
||||||
|
margin-bottom: .5rem;
|
||||||
|
}
|
||||||
img {
|
img {
|
||||||
max-width: 420px;
|
max-width: 100%;
|
||||||
width: 117%;
|
width: 117%;
|
||||||
height: auto;
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -178,7 +178,7 @@ export default {
|
||||||
customAlertData: {},
|
customAlertData: {},
|
||||||
previouslyEnteredCarId: '',
|
previouslyEnteredCarId: '',
|
||||||
vinPopulatedOnPageLoad: this.getVinFromStore()?.length > 0,
|
vinPopulatedOnPageLoad: this.getVinFromStore()?.length > 0,
|
||||||
isSelectedGlassAvailableForVehicle: false,
|
isSelectedGlassAvailableForVehicle: true,
|
||||||
displayInvalidZipAlert: false,
|
displayInvalidZipAlert: false,
|
||||||
displayNonServiceableZipAlert: false,
|
displayNonServiceableZipAlert: false,
|
||||||
displayVinNotFoundAlert: false,
|
displayVinNotFoundAlert: false,
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,9 @@ import { storeActions } from "@/constants/store-actions";
|
||||||
import { setCookieProperties, getDeviceIdValue, getSessionIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper";
|
import { setCookieProperties, getDeviceIdValue, getSessionIdValue, getSessionKeyValue } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
import { experimentSettings } from "@/constants/experiments";
|
import { experimentSettings } from "@/constants/experiments";
|
||||||
import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents } from "@/constants/analytics";
|
import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes } from "@/constants/analytics";
|
||||||
import { cookieNames } from "@/constants/cookie-names";
|
import { cookieNames } from "@/constants/cookie-names";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
import baseMixin from "@/mixins/base-mixin";
|
import baseMixin from "@/mixins/base-mixin";
|
||||||
|
|
||||||
|
|
@ -19,6 +20,7 @@ export default {
|
||||||
action: '',
|
action: '',
|
||||||
event: pageEvent,
|
event: pageEvent,
|
||||||
shouldUseSessionId: false,
|
shouldUseSessionId: false,
|
||||||
|
experimentsForUser: store.getters.applicationUser.experiments,
|
||||||
};
|
};
|
||||||
|
|
||||||
baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false);
|
baseMixin.methods.dispatchStoreAction(storeActions.LOG_PAGE_VIEW, payload, false);
|
||||||
|
|
@ -37,18 +39,20 @@ export default {
|
||||||
label: label,
|
label: label,
|
||||||
value: value,
|
value: value,
|
||||||
shouldUseSessionId: false,
|
shouldUseSessionId: false,
|
||||||
|
experimentsForUser: store.getters.applicationUser.experiments
|
||||||
};
|
};
|
||||||
|
|
||||||
baseMixin.methods.dispatchStoreAction(storeActions.LOG_CUSTOM_EVENT, payload, false);
|
baseMixin.methods.dispatchStoreAction(storeActions.LOG_CUSTOM_EVENT, payload, false);
|
||||||
},
|
},
|
||||||
|
|
||||||
pushEventToGA(category, action, label, pushToLogApp = false) {
|
pushEventToGA(category, action, label, pushToLogApp = false, valueToLogType = null) {
|
||||||
const currentPageName = getPageNameByQueryString();
|
const currentPageName = getPageNameByQueryString();
|
||||||
|
const labelToLog = getValueToLog(label, valueToLogType);
|
||||||
const eventToBePushed = {
|
const eventToBePushed = {
|
||||||
'event': GaEvents.GENERIC_EVENT,
|
'event': GaEvents.GENERIC_EVENT,
|
||||||
'category': category,
|
'category': category,
|
||||||
'action': action,
|
'action': action,
|
||||||
'label': label,
|
'label': labelToLog,
|
||||||
'value': undefined,
|
'value': undefined,
|
||||||
'path': `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}`
|
'path': `/fmg/?${queryStrings.FMG_PAGE}=${currentPageName}`
|
||||||
}
|
}
|
||||||
|
|
@ -56,7 +60,7 @@ export default {
|
||||||
pushToDataLayerIfDefined(eventToBePushed);
|
pushToDataLayerIfDefined(eventToBePushed);
|
||||||
|
|
||||||
if (pushToLogApp) {
|
if (pushToLogApp) {
|
||||||
this.logCustomEvent(category, action, label, undefined);
|
this.logCustomEvent(category, action, labelToLog, undefined);
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
@ -74,7 +78,8 @@ export default {
|
||||||
this.logPageView(analyticsPageEvents.ENTRY);
|
this.logPageView(analyticsPageEvents.ENTRY);
|
||||||
},
|
},
|
||||||
|
|
||||||
pushExperimentsToDataLayer(experiments) {
|
pushExperimentsToDataLayer() {
|
||||||
|
const experiments = store.getters.applicationUser.experiments;
|
||||||
experiments?.forEach(exp => {
|
experiments?.forEach(exp => {
|
||||||
|
|
||||||
// Set Google Dimension Index based on experiment settings.
|
// Set Google Dimension Index based on experiment settings.
|
||||||
|
|
@ -121,10 +126,14 @@ export default {
|
||||||
|
|
||||||
if (response.data) {
|
if (response.data) {
|
||||||
if (response.data.sessionKey && skey === 0) {
|
if (response.data.sessionKey && skey === 0) {
|
||||||
setCookieProperties({ [cookieNames.SESSION_KEY]: response.data.sessionKey});
|
setCookieProperties({ [cookieNames.SESSION_KEY]: response.data.sessionKey}, {
|
||||||
|
useDefaultFunnelCookieAttributes: false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (response.data.sessionId && sid === '00000000-0000-0000-0000-000000000000') {
|
if (response.data.sessionId && sid === '00000000-0000-0000-0000-000000000000') {
|
||||||
setCookieProperties({ [cookieNames.SESSION_ID]: response.data.sessionId});
|
setCookieProperties({ [cookieNames.SESSION_ID]: response.data.sessionId}, {
|
||||||
|
maxAge: 60 * 30 // 30 minutes
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -145,6 +154,9 @@ export default {
|
||||||
},
|
},
|
||||||
GaLabels() {
|
GaLabels() {
|
||||||
return GaLabels;
|
return GaLabels;
|
||||||
|
},
|
||||||
|
ValueToLogTypes() {
|
||||||
|
return ValueToLogTypes;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -164,3 +176,10 @@ function getPageNameByQueryString() {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getValueToLog(value, valueToLogType) {
|
||||||
|
if (valueToLogType != null && valueToLogType === ValueToLogTypes.LAST_5 ) {
|
||||||
|
return value.slice(-5);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import analyticsMixin from "@/mixins/analytics-mixin";
|
import analyticsMixin from "@/mixins/analytics-mixin";
|
||||||
import { setupMocksForJsFiles, setupCookies } from "@/helpers/unit-test-helper.js";
|
import { setupMocksForJsFiles, setupCookies } from "@/helpers/unit-test-helper.js";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents } from "@/constants/analytics";
|
import { analyticsPageEvents, GaCategories, GaActions, GaLabels, GaEvents, ValueToLogTypes } from "@/constants/analytics";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
describe("analyticsMixin.js", () => {
|
describe("analyticsMixin.js", () => {
|
||||||
test("logPageView: calls dispatch with type and payload", () => {
|
test("logPageView: calls dispatch with type and payload", () => {
|
||||||
|
|
@ -39,21 +40,71 @@ describe("analyticsMixin.js", () => {
|
||||||
expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled();
|
expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("pushEventToGA, should call logEvent too", () => {
|
test("pushEventToGA, should call dataLayer push and logCustomEvent too", () => {
|
||||||
// Arrange
|
// Arrange
|
||||||
|
window.dataLayer = [];
|
||||||
const mockData = {
|
const mockData = {
|
||||||
actionList: [{
|
actionList: [{
|
||||||
actionName: storeActions.LOG_CUSTOM_EVENT
|
actionName: storeActions.LOG_CUSTOM_EVENT
|
||||||
}],
|
}],
|
||||||
}
|
}
|
||||||
const mocks = setupMocksForJsFiles(mockData);
|
const mocks = setupMocksForJsFiles(mockData);
|
||||||
|
var mockDataLayer = [];
|
||||||
|
mockDataLayer.push({
|
||||||
|
event: 'event',
|
||||||
|
category: 'category',
|
||||||
|
action: 'action',
|
||||||
|
label: 'label',
|
||||||
|
value: undefined,
|
||||||
|
path: '/fmg/?fmgPage='
|
||||||
|
});
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true);
|
analyticsMixin.methods.pushEventToGA('category', 'action', 'label', true);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
|
expect(mockDataLayer).toEqual(expect.arrayContaining(window.dataLayer));
|
||||||
expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled();
|
expect(mocks.baseMixin.methods.dispatchStoreAction).toBeCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 only logs last 5 of label", () => {
|
||||||
|
// Arrange
|
||||||
|
window.dataLayer = [];
|
||||||
|
var expectedDataLayer = [];
|
||||||
|
expectedDataLayer.push({
|
||||||
|
event: 'event',
|
||||||
|
category: 'category',
|
||||||
|
action: 'action',
|
||||||
|
label: '33333',
|
||||||
|
value: undefined,
|
||||||
|
path: '/fmg/?fmgPage='
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
analyticsMixin.methods.pushEventToGA('category', 'action', '1111122222333333', false, ValueToLogTypes.LAST_5);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("pushEventToGA, should call dataLayer push and ValueToLogTypes.LAST_5 logs only the last 3 characters for a 3 character string", () => {
|
||||||
|
// Arrange
|
||||||
|
window.dataLayer = [];
|
||||||
|
var expectedDataLayer = [];
|
||||||
|
expectedDataLayer.push({
|
||||||
|
event: 'event',
|
||||||
|
category: 'category',
|
||||||
|
action: 'action',
|
||||||
|
label: '111',
|
||||||
|
value: undefined,
|
||||||
|
path: '/fmg/?fmgPage='
|
||||||
|
});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
analyticsMixin.methods.pushEventToGA('category', 'action', '111', false, ValueToLogTypes.LAST_5);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(expectedDataLayer).toEqual(expect.arrayContaining(window.dataLayer));
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Experiments, should push to dataLayer with default Google Custom Dimension Index", () => {
|
test("Experiments, should push to dataLayer with default Google Custom Dimension Index", () => {
|
||||||
|
|
@ -69,6 +120,21 @@ describe("analyticsMixin.js", () => {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Mock store
|
||||||
|
jest.mock("@/store", () => { return {}; }, { virtual: true });
|
||||||
|
|
||||||
|
store.getters = {
|
||||||
|
applicationUser: {
|
||||||
|
experiments: [
|
||||||
|
{
|
||||||
|
settings: {},
|
||||||
|
variationName: 'test',
|
||||||
|
universeName: 'testUniverse'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData);
|
analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData);
|
||||||
|
|
@ -97,6 +163,20 @@ describe("analyticsMixin.js", () => {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
// Mock store
|
||||||
|
jest.mock("@/store", () => { return {}; }, { virtual: true });
|
||||||
|
|
||||||
|
store.getters = {
|
||||||
|
applicationUser: {
|
||||||
|
experiments: [
|
||||||
|
{
|
||||||
|
settings: { "Google Custom Dimension Index": "5" },
|
||||||
|
variationName: 'test',
|
||||||
|
universeName: 'testUniverse'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData);
|
analyticsMixin.methods.pushExperimentsToDataLayer(mockExperimentData);
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,10 @@ export default {
|
||||||
},
|
},
|
||||||
dynamicStrings(){
|
dynamicStrings(){
|
||||||
return dynamicStrings;
|
return dynamicStrings;
|
||||||
}
|
},
|
||||||
|
cssClassNameForCmsWidget(){
|
||||||
|
return "widget-name-" + this.cmsWidgetName;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
15
src/mixins/experiment-mixin.js
Normal file
15
src/mixins/experiment-mixin.js
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
hasSettingEqualTo(settingName, settingValue) {
|
||||||
|
return store.getters.experimentSettings[settingName] === settingValue;
|
||||||
|
},
|
||||||
|
hasSetting(settingName) {
|
||||||
|
return store.getters.experimentSettings.hasOwnProperty(settingName);
|
||||||
|
},
|
||||||
|
getSettingValue(settingName) {
|
||||||
|
return this.hasSetting(settingName) ? store.getters.experimentSettings[settingName] : null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
148
src/mixins/experiment-mixin.spec.js
Normal file
148
src/mixins/experiment-mixin.spec.js
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
import experimentMixin from "@/mixins/experiment-mixin";
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
describe("experiment-mixin", () => {
|
||||||
|
describe("hasSettingEqualTo", () => {
|
||||||
|
test("setting exists and value matches => return true", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.hasSettingEqualTo("Setting2", "Value2");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("setting exists and value does not match => return false", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.hasSettingEqualTo("Setting2", "Value3");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("setting does not exist => return false", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.hasSettingEqualTo("SettingBoogly", "Woogly");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("there are no settings => return false", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({ experimentSettings: {} });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.hasSettingEqualTo("Setting2", "Value3");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("hasSetting", () => {
|
||||||
|
test("has setting => return true", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.hasSetting("Setting4");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not have setting => return false", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.hasSetting("BooglyWoogly");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("experimentSettings is empty => return false", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({ experimentSettings: {} });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.hasSetting("Setting4");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getSettingValue", () => {
|
||||||
|
test("has setting => return correct value", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.getSettingValue("Setting3");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toEqual("Value3")
|
||||||
|
});
|
||||||
|
|
||||||
|
test("does not have setting => return null", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({});
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.getSettingValue("Hello");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("experimentSettings is empty => return null", () => {
|
||||||
|
// Arrange
|
||||||
|
const { wrapper } = setupMocks({ experimentSettings: {} });
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const result = wrapper.vm.getSettingValue("Hello");
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function setupMocks({ experimentSettings }) {
|
||||||
|
const mocks = getMountOptions({});
|
||||||
|
|
||||||
|
const testExperimentSettings = {
|
||||||
|
"Setting1": "Value1",
|
||||||
|
"Setting2": "Value2",
|
||||||
|
"Setting3": "Value3",
|
||||||
|
"Setting4": "Value1",
|
||||||
|
"Setting5": "Value2",
|
||||||
|
"Setting6": "Value3"
|
||||||
|
};
|
||||||
|
|
||||||
|
store.getters = {
|
||||||
|
experimentSettings: experimentSettings ?? testExperimentSettings
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockComponent = {
|
||||||
|
template: "<div></div>",
|
||||||
|
mixins: [experimentMixin]
|
||||||
|
};
|
||||||
|
|
||||||
|
const wrapper = shallowMount(mockComponent, mocks);
|
||||||
|
|
||||||
|
return { wrapper };
|
||||||
|
}
|
||||||
148
src/mixins/vehicle-questions-mixin.js
Normal file
148
src/mixins/vehicle-questions-mixin.js
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
|
import { storeMutations } from "@/constants/store-mutations.js";
|
||||||
|
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
|
||||||
|
import { navigationScenarios } from "../router/router-constants/navigation-scenarios";
|
||||||
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
import baseMixin from "@/mixins/base-mixin.js";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
hasPartQuestions(partsOrQuestions) {
|
||||||
|
return partsOrQuestions?.some(pq => pq.partQuestions?.length > 0);
|
||||||
|
},
|
||||||
|
hasGlassLocationWithMultipleParts(partsOrQuestions) {
|
||||||
|
return partsOrQuestions?.some(pq => pq.parts?.length > 1);
|
||||||
|
},
|
||||||
|
hasChildPartQuestions(partsOrQuestions) {
|
||||||
|
return partsOrQuestions?.some(pq => {
|
||||||
|
return pq.parts?.some(part => part.childPartQuestions?.length > 0);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hasCapabilityQuestions(partsOrQuestions) {
|
||||||
|
return partsOrQuestions?.some(pq => {
|
||||||
|
return pq.parts?.some(part => part.requiresCapabilityQuestions === true);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// method to only include keys listed for lineItems.glassParts in
|
||||||
|
// https://safelite.atlassian.net/wiki/spaces/DC/pages/17137665/Catalog+Front-End+State#glassParts
|
||||||
|
reducedGlassPartsArray(glassParts) {
|
||||||
|
const reducedGlassParts = [];
|
||||||
|
glassParts.forEach((glass) => {
|
||||||
|
if (Array.isArray(glass.parts) && glass.parts.length === 1) {
|
||||||
|
const singlePart = glass.parts[0];
|
||||||
|
reducedGlassParts.push({
|
||||||
|
partNumber: singlePart.partNumber,
|
||||||
|
description: singlePart.description,
|
||||||
|
color: singlePart.color,
|
||||||
|
requiresRecalibration: singlePart.requiresRecalibration,
|
||||||
|
requiresCapabilityQuestions: singlePart.requiresCapabilityQuestions,
|
||||||
|
recalibrationType: singlePart.recalibrationType,
|
||||||
|
childParts: singlePart.childParts,
|
||||||
|
price: singlePart.price,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return reducedGlassParts;
|
||||||
|
},
|
||||||
|
comparePageIndices(currentPage, fmgPage) {
|
||||||
|
const orderedVehicleQuestionPages = [
|
||||||
|
fmgPageValues.PART_QUESTIONS,
|
||||||
|
fmgPageValues.VEHICLE_PARTS,
|
||||||
|
fmgPageValues.MOLDING_QUESTIONS,
|
||||||
|
fmgPageValues.CAPABILITY_QUESTIONS,
|
||||||
|
fmgPageValues.QUOTE
|
||||||
|
]
|
||||||
|
|
||||||
|
return orderedVehicleQuestionPages.indexOf(currentPage) - orderedVehicleQuestionPages.indexOf(fmgPage);
|
||||||
|
},
|
||||||
|
currentPageComesBeforePage(currentPage = this.$route.query.fmgPage, fmgPage) {
|
||||||
|
return this.comparePageIndices(currentPage, fmgPage) < 0;
|
||||||
|
},
|
||||||
|
currentPageComesAfterPage(currentPage = this.$route.query.fmgPage, fmgPage) {
|
||||||
|
return this.comparePageIndices(currentPage, fmgPage) > 0;
|
||||||
|
},
|
||||||
|
// Can't use `this` because navigateForward is also called from vin-pages-mixin
|
||||||
|
async navigateForward(partsOrQuestions, vm) {
|
||||||
|
const self = vm ?? this;
|
||||||
|
const currentPage = self.$route.query.fmgPage;
|
||||||
|
|
||||||
|
const hasPartQuestions = this.hasPartQuestions(partsOrQuestions)
|
||||||
|
const hasGlassLocationWithMultipleParts = this.hasGlassLocationWithMultipleParts(partsOrQuestions);
|
||||||
|
const hasChildPartQuestions = this.hasChildPartQuestions(partsOrQuestions);
|
||||||
|
const hasCapabilityQuestions = this.hasCapabilityQuestions(partsOrQuestions);
|
||||||
|
|
||||||
|
if (hasPartQuestions && this.currentPageComesBeforePage(currentPage, fmgPageValues.PART_QUESTIONS)) {
|
||||||
|
self.$router.navigate(self.navigationScenarios.HAS_PART_QUESTIONS, self.$route, {}, {}, {partsOrQuestions: partsOrQuestions});
|
||||||
|
}
|
||||||
|
else if (hasGlassLocationWithMultipleParts && this.currentPageComesBeforePage(currentPage, fmgPageValues.VEHICLE_PARTS)) {
|
||||||
|
// if multiple parts on any glass
|
||||||
|
// go to vehicle-parts page and pass the partsData
|
||||||
|
self.$router.navigate(self.navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE,self.$route,{},{},{partsOrQuestions: partsOrQuestions});
|
||||||
|
} else if (hasChildPartQuestions && this.currentPageComesBeforePage(currentPage, fmgPageValues.MOLDING_QUESTIONS)) {
|
||||||
|
// if any childpart questions
|
||||||
|
// go to molding-questions page and pass the partsData
|
||||||
|
self.$router.navigate(self.navigationScenarios.HAS_MOLDING_QUESTIONS,self.$route,{},{},{partsOrQuestions: partsOrQuestions});
|
||||||
|
} else if (hasCapabilityQuestions && this.currentPageComesBeforePage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS)) {
|
||||||
|
// if has capability questions
|
||||||
|
// go to capability-questions page and pass the partsData
|
||||||
|
const windshieldPart = partsOrQuestions.filter(x => x.glassLocation === damageLocationsSelected.WINDSHIELD)[0].parts[0];
|
||||||
|
let capabilityQuestions = (await baseMixin.methods.dispatchStoreAction(storeActions.GET_CAPABILITY_QUESTIONS, {
|
||||||
|
carId: store.getters.vehicle.carId,
|
||||||
|
partNumber: windshieldPart.partNumber
|
||||||
|
})).data;
|
||||||
|
|
||||||
|
capabilityQuestions.forEach(question => {
|
||||||
|
question.answers = question.answers.map(answer => {
|
||||||
|
return {
|
||||||
|
...answer,
|
||||||
|
answerResult: answer.answerResult1
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
self.$router.navigate(self.navigationScenarios.HAS_CAPABILITY_QUESTIONS, self.$route, {}, {}, { partsOrQuestions, capabilityQuestions });
|
||||||
|
} else {
|
||||||
|
// if single parts only
|
||||||
|
const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions);
|
||||||
|
// save to store lineItems.glassParts
|
||||||
|
self.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts);
|
||||||
|
|
||||||
|
self.$refs.loadingModal.showModal();
|
||||||
|
navigateToHeritageFunnel();
|
||||||
|
|
||||||
|
// For quote pages MVP release
|
||||||
|
// self.$router.navigate(self.navigationScenarios.ANSWERED_ALL_QUESTIONS, self.$route);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
backButtonAction() {
|
||||||
|
const partsOrQuestions = this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS)?.partsOrQuestions;
|
||||||
|
const hasPartQuestions = this.hasPartQuestions(partsOrQuestions);
|
||||||
|
const hasGlassLocationWithMultipleParts = this.hasGlassLocationWithMultipleParts(partsOrQuestions);
|
||||||
|
const hasChildPartQuestions = this.hasChildPartQuestions(partsOrQuestions);
|
||||||
|
const hasCapabilityQuestions = this.hasCapabilityQuestions(partsOrQuestions);
|
||||||
|
let backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP;
|
||||||
|
|
||||||
|
const currentPage = this.$route.query.fmgPage;
|
||||||
|
if (hasCapabilityQuestions && this.currentPageComesAfterPage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS)) {
|
||||||
|
backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_CAPABILITY_QUESTIONS;
|
||||||
|
}
|
||||||
|
else if (hasChildPartQuestions && this.currentPageComesAfterPage(currentPage, fmgPageValues.MOLDING_QUESTIONS)) {
|
||||||
|
backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_MOLDING_QUESTIONS;
|
||||||
|
}
|
||||||
|
else if (hasGlassLocationWithMultipleParts && this.currentPageComesAfterPage(currentPage, fmgPageValues.VEHICLE_PARTS)) {
|
||||||
|
backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS;
|
||||||
|
}
|
||||||
|
else if (hasPartQuestions && this.currentPageComesAfterPage(currentPage, fmgPageValues.PART_QUESTIONS)) {
|
||||||
|
backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$router.navigate(
|
||||||
|
backNavigationScenario,
|
||||||
|
this.$route
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1146
src/mixins/vehicle-questions-mixin.spec.js
Normal file
1146
src/mixins/vehicle-questions-mixin.spec.js
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -1,28 +1,13 @@
|
||||||
import store from "@/store";
|
|
||||||
import { storeActions } from "@/constants/store-actions.js";
|
import { storeActions } from "@/constants/store-actions.js";
|
||||||
import { storeMutations } from "@/constants/store-mutations.js";
|
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
methods: {
|
methods: {
|
||||||
async navigateForwardWithSingleCarMatch() {
|
async navigateForwardWithSingleCarMatch() {
|
||||||
const result = await this.dispatchStoreAction(storeActions.GET_PARTS_OR_QUESTIONS);
|
const result = await this.dispatchStoreAction(storeActions.GET_PARTS_OR_QUESTIONS);
|
||||||
|
|
||||||
const partsOrQuestions = result.data.partsOrQuestions;
|
const partsOrQuestions = result.data.partsOrQuestions;
|
||||||
const hasPartsQuestions = partsOrQuestions.some(pq => pq.partQuestions?.length > 0);
|
|
||||||
const hasGlassLocationWithMultipleParts = partsOrQuestions.some(pq => pq.parts?.length > 1);
|
|
||||||
|
|
||||||
if (hasPartsQuestions) {
|
vehicleQuestionsMixin.methods.navigateForward(partsOrQuestions, this);
|
||||||
this.$router.navigate(this.navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS, this.$route, {}, {}, result.data);
|
},
|
||||||
}
|
|
||||||
else if (hasGlassLocationWithMultipleParts) {
|
|
||||||
this.$router.navigate(this.navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS, this.$route, {}, {}, result.data);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
store.commit(storeMutations.UPDATE_GLASS_PARTS, result.data);
|
|
||||||
this.$refs.loadingModal.showModal();
|
|
||||||
navigateToHeritageFunnel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,14 +2,11 @@ import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import { setupMocksForJsFiles, getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { setupMocksForJsFiles, getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
|
||||||
import store from "@/store";
|
|
||||||
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
||||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
|
||||||
|
|
||||||
jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
|
jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
|
||||||
navigateToHeritageFunnel: jest.fn()
|
navigateForward: jest.fn()
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe("vin-pages-mixin", () => {
|
describe("vin-pages-mixin", () => {
|
||||||
|
|
@ -18,864 +15,17 @@ describe("vin-pages-mixin", () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("navigateForwardWithSingleCarMatch", () => {
|
describe("navigateForwardWithSingleCarMatch", () => {
|
||||||
describe("should go to parts-questions", () => {
|
test("should navigateForward", async () => {
|
||||||
test("single glass location has part question => go to parts-questions", async () => {
|
// Arrange
|
||||||
// Arrange
|
const { wrapper } = setupMocks({});
|
||||||
const partsOrQuestions = [
|
vehicleQuestionsMixin.methods.navigateForward = jest.fn();
|
||||||
{
|
|
||||||
"glassName": "Single",
|
|
||||||
"glassLocation": "Windshield",
|
|
||||||
"parts": null,
|
|
||||||
"partQuestions": [
|
|
||||||
{
|
|
||||||
"questionSequence": 1,
|
|
||||||
"questionText": "Is there a line running across the bottom, driver's side then up the center of the Windshield?",
|
|
||||||
"answers": [
|
|
||||||
{
|
|
||||||
"answerText": "Yes",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01144"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"answerText": "No",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01143"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
// Act
|
||||||
partsOrQuestions: partsOrQuestions
|
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
// Assert
|
||||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
expect(vehicleQuestionsMixin.methods.navigateForward).toHaveBeenCalled();
|
||||||
|
})
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions });
|
|
||||||
});
|
|
||||||
|
|
||||||
test("multiple glass locations have part questions => go to parts-questions", async () => {
|
|
||||||
// Arrange
|
|
||||||
const partsOrQuestions = [
|
|
||||||
{
|
|
||||||
"glassName": "Single",
|
|
||||||
"glassLocation": "Windshield",
|
|
||||||
"parts": null,
|
|
||||||
"partQuestions": [
|
|
||||||
{
|
|
||||||
"questionSequence": 1,
|
|
||||||
"questionText": "Is there a line running across the bottom, driver's side then up the center of the Windshield?",
|
|
||||||
"answers": [
|
|
||||||
{
|
|
||||||
"answerText": "Yes",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01144"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"answerText": "No",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01143"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Front",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DD08158GTYN",
|
|
||||||
"description": "driver side, front",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Quarter",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DQ08162GTYN",
|
|
||||||
"description": "driver side, 1 hole",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "SideDoor",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": null,
|
|
||||||
"partQuestions": [
|
|
||||||
{
|
|
||||||
"questionSequence": 2,
|
|
||||||
"questionText": "Is this a super awesome question?",
|
|
||||||
"answers": [
|
|
||||||
{
|
|
||||||
"answerText": "Yes",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01144000"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"answerText": "Super yes",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01143001"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Stationary",
|
|
||||||
"glassLocation": "Rear",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DB08165GTNN",
|
|
||||||
"description": "heated glass, stationary",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
partsOrQuestions: partsOrQuestions
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions });
|
|
||||||
});
|
|
||||||
|
|
||||||
test("multiple glass locations selected, one has part question => go to parts-questions", async () => {
|
|
||||||
// Arrange
|
|
||||||
const partsOrQuestions = [
|
|
||||||
{
|
|
||||||
"glassName": "Single",
|
|
||||||
"glassLocation": "Windshield",
|
|
||||||
"parts": null,
|
|
||||||
"partQuestions": [
|
|
||||||
{
|
|
||||||
"questionSequence": 1,
|
|
||||||
"questionText": "Is there a line running across the bottom, driver's side then up the center of the Windshield?",
|
|
||||||
"answers": [
|
|
||||||
{
|
|
||||||
"answerText": "Yes",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01144"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"answerText": "No",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01143"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Front",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DD08158GTYN",
|
|
||||||
"description": "driver side, front",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Quarter",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DQ08162GTYN",
|
|
||||||
"description": "driver side, 1 hole",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "SideDoor",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DD08160GTYN",
|
|
||||||
"description": "driver side, body side, 1 hole",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Stationary",
|
|
||||||
"glassLocation": "Rear",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DB08165GTNN",
|
|
||||||
"description": "heated glass, stationary",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
partsOrQuestions: partsOrQuestions
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions });
|
|
||||||
});
|
|
||||||
|
|
||||||
test("a selected glass location has part questions and multiple parts => go to parts-questions", async () => {
|
|
||||||
// Arrange
|
|
||||||
const partsOrQuestions = [
|
|
||||||
{
|
|
||||||
"glassName": "Single",
|
|
||||||
"glassLocation": "Windshield",
|
|
||||||
"parts": null,
|
|
||||||
"partQuestions": [
|
|
||||||
{
|
|
||||||
"questionSequence": 1,
|
|
||||||
"questionText": "Is there a line running across the bottom, driver's side then up the center of the Windshield?",
|
|
||||||
"answers": [
|
|
||||||
{
|
|
||||||
"answerText": "Yes",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01144"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"answerText": "No",
|
|
||||||
"nextQuestionSequence": null,
|
|
||||||
"answerResult": "DW01143"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Front",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DD08158GTYN",
|
|
||||||
"description": "driver side, front",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Quarter",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DQ08162GTYN",
|
|
||||||
"description": "driver side, 1 hole",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DQ08162YPYN",
|
|
||||||
"description": "driver side, 1 hole",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "SideDoor",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DD08160GTYN",
|
|
||||||
"description": "driver side, body side, 1 hole",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DD08160YPYN",
|
|
||||||
"description": "driver side, body side, 1 hole",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Stationary",
|
|
||||||
"glassLocation": "Rear",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DB08165GTNN",
|
|
||||||
"description": "heated glass, stationary",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DB08165YPNN",
|
|
||||||
"description": "heated glass, stationary",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DB08166GTNN",
|
|
||||||
"description": "stationary",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DB08167GTNN",
|
|
||||||
"description": "heated glass, movable, 8 hole",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DB08167YPNN",
|
|
||||||
"description": "heated glass, movable, 8 hole",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
partsOrQuestions: partsOrQuestions
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS, wrapper.vm.$route, {}, {}, { partsOrQuestions });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("should go to vehicle-parts", () => {
|
|
||||||
test("single glass location has multiple parts => go to vehicle-parts", async () => {
|
|
||||||
// Arrange
|
|
||||||
const partsOrQuestions = [
|
|
||||||
{
|
|
||||||
"glassName": "Stationary",
|
|
||||||
"glassLocation": "Rear",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FB25724GTYN",
|
|
||||||
"description": "heated glass, solar, antenna",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "FB25759GTYN",
|
|
||||||
"description": "heated glass, solar, antenna, w/diversity antenna",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
partsOrQuestions: partsOrQuestions
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS, wrapper.vm.$route, {}, {}, { partsOrQuestions });
|
|
||||||
});
|
|
||||||
|
|
||||||
test("multiple glass locations selected, one of them has multiple parts => go to vehicle parts", async () => {
|
|
||||||
// Arrange
|
|
||||||
const partsOrQuestions = [
|
|
||||||
{
|
|
||||||
"glassName": "Single",
|
|
||||||
"glassLocation": "Windshield",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FW03647GTNN",
|
|
||||||
"description": "solar, 3rd visor band",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": [
|
|
||||||
{
|
|
||||||
"partNumber": "MWF03647",
|
|
||||||
"partType": "MOULDING",
|
|
||||||
"description": "Upper "
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Back",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FD25747GTYN",
|
|
||||||
"description": "solar, driver side, rear, ex models and above",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Front",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FD25719GTYN",
|
|
||||||
"description": "solar, driver side, front, ex models and above",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Vent",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FV25749GTNN",
|
|
||||||
"description": "solar, driver side, rear",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Stationary",
|
|
||||||
"glassLocation": "Rear",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FB25724GTYN",
|
|
||||||
"description": "heated glass, solar, antenna",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "FB25759GTYN",
|
|
||||||
"description": "heated glass, solar, antenna, w/diversity antenna",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
partsOrQuestions: partsOrQuestions
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS, wrapper.vm.$route, {}, {}, { partsOrQuestions });
|
|
||||||
});
|
|
||||||
|
|
||||||
test("multiple glass locations selected, multiple have multiple parts => go to vehicle-parts", async () => {
|
|
||||||
// Arrange
|
|
||||||
const partsOrQuestions = [
|
|
||||||
{
|
|
||||||
"glassName": "Single",
|
|
||||||
"glassLocation": "Windshield",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DW02101GTYN",
|
|
||||||
"description": "solar",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Back",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DD12202GTYN",
|
|
||||||
"description": "solar, driver side, rear",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DD12202YPYN",
|
|
||||||
"description": "solar, driver side, rear",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Front",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DD12198GTYN",
|
|
||||||
"description": "solar, driver side, front",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DD12200GTYN",
|
|
||||||
"description": "solar, driver side, front, laminated, soundproofing",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Quarter",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DQ12204GTYNOEM",
|
|
||||||
"description": "solar, driver side, encap",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DQ12204YPYNOEM",
|
|
||||||
"description": "solar, driver side, encap",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DQ12205GTYNOEM",
|
|
||||||
"description": "solar, antenna, driver side, encap",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DQ12205YPYNOEM",
|
|
||||||
"description": "solar, antenna, driver side, encap",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DQ12207GTYN",
|
|
||||||
"description": "solar, driver side, encap, chrome molding",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DQ12207YPYNOEM",
|
|
||||||
"description": "solar, driver side, encap, chrome molding",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DQ12208GTYNOEM",
|
|
||||||
"description": "solar, antenna, driver side, encap, chrome molding",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DQ12208YPYNOEM",
|
|
||||||
"description": "solar, antenna, driver side, encap, chrome molding",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Stationary",
|
|
||||||
"glassLocation": "Rear",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "DB12209GTYN",
|
|
||||||
"description": "heated glass, solar, 1 hole",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"partNumber": "DB12209YPYN",
|
|
||||||
"description": "heated glass, solar, 1 hole",
|
|
||||||
"color": "Gray Tint Privacy",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
partsOrQuestions: partsOrQuestions
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
|
|
||||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS, wrapper.vm.$route, {}, {}, { partsOrQuestions });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("should go to heritage funnel", () => {
|
|
||||||
test("single glass location selected, has no part questions and has one part => go to heritage funnel", async () => {
|
|
||||||
// Arrange
|
|
||||||
const partsOrQuestions = [
|
|
||||||
{
|
|
||||||
"glassName": "Single",
|
|
||||||
"glassLocation": "Windshield",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FW04186GTYN",
|
|
||||||
"description": "solar, soundproofing, lane keep assist",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": true,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": [
|
|
||||||
{
|
|
||||||
"partNumber": "GGG 3563 KIT",
|
|
||||||
"partType": "MOULDING",
|
|
||||||
"description": "Kit, Top & Sides "
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
partsOrQuestions: partsOrQuestions
|
|
||||||
});
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(navigateToHeritageFunnel).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("multiple glass locations selected, each has one part and no part questions => go to heritage funnel", async () => {
|
|
||||||
// Arrange
|
|
||||||
const partsOrQuestions = [
|
|
||||||
{
|
|
||||||
"glassName": "Single",
|
|
||||||
"glassLocation": "Windshield",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FW04186GTYN",
|
|
||||||
"description": "solar, soundproofing, lane keep assist",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": true,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": [
|
|
||||||
{
|
|
||||||
"partNumber": "GGG 3563 KIT",
|
|
||||||
"partType": "MOULDING",
|
|
||||||
"description": "Kit, Top & Sides "
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Back",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FD25457GTYN",
|
|
||||||
"description": "solar, driver side, rear",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Front",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FD27090GTYN",
|
|
||||||
"description": "solar, driver side, front",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Vent",
|
|
||||||
"glassLocation": "Driver",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FV25459GTNN",
|
|
||||||
"description": "solar, driver side, rear",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"glassName": "Stationary",
|
|
||||||
"glassLocation": "Rear",
|
|
||||||
"parts": [
|
|
||||||
{
|
|
||||||
"partNumber": "FB25460GTYN",
|
|
||||||
"description": "heated glass, solar",
|
|
||||||
"color": "Green Tint",
|
|
||||||
"requiresRecalibration": false,
|
|
||||||
"requiresCapabilityQuestions": false,
|
|
||||||
"childParts": null
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"partQuestions": null
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const { wrapper } = setupMocks({
|
|
||||||
partsOrQuestions: partsOrQuestions
|
|
||||||
});
|
|
||||||
|
|
||||||
store.commit = jest.fn();
|
|
||||||
|
|
||||||
// Act
|
|
||||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
|
||||||
|
|
||||||
// Assert
|
|
||||||
expect(store.commit).toHaveBeenCalledTimes(1);
|
|
||||||
expect(store.commit).toHaveBeenCalledWith(storeMutations.UPDATE_GLASS_PARTS, { partsOrQuestions })
|
|
||||||
expect(wrapper.vm.$refs.loadingModal.showModal).toHaveBeenCalledTimes(1);
|
|
||||||
expect(navigateToHeritageFunnel).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -891,27 +41,14 @@ function setupMocks({ partsOrQuestions = [] }) {
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
const mocks = getMountOptions({
|
const mocks = getMountOptions({});
|
||||||
router: {
|
|
||||||
navigate: jest.fn()
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
const mockVinComponent = {
|
const mockVinComponent = {
|
||||||
components: { loadingModal },
|
components: { loadingModal },
|
||||||
template: '<loadingModal ref="loadingModal" />',
|
template: '<loadingModal ref="loadingModal" />',
|
||||||
// render() {
|
|
||||||
// return '<div ref="loadingModal"></div>'
|
|
||||||
// },
|
|
||||||
mixins: [vinPagesMixin, baseMixin.baseMixin]
|
mixins: [vinPagesMixin, baseMixin.baseMixin]
|
||||||
};
|
};
|
||||||
|
|
||||||
// const mockVinComponent = Vue.component("mockcomponent", {
|
|
||||||
// template: '<loadingModal ref="loadingModal" />',
|
|
||||||
// mixins: [vinPagesMixin, baseMixin.baseMixin]
|
|
||||||
// })
|
|
||||||
|
|
||||||
const wrapper = shallowMount(mockVinComponent, mocks);
|
const wrapper = shallowMount(mockVinComponent, mocks);
|
||||||
|
|
||||||
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
|
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import { getDeviceIdValue } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
|
|
||||||
// Heritage integration
|
// Heritage integration
|
||||||
import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper";
|
import { isSavedSessionStillActive } from "@/helpers/heritage-integration/session-helper";
|
||||||
import { updateOrCreateFunnelCookie, getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
import { updateOrCreateFunnelCookie, getFunnelCookie, updateSessionIdCookie } from "@/helpers/heritage-integration/cookie-helper";
|
||||||
import { loadOrderIfPresent, saveOrder } from "@/helpers/heritage-integration/order-helper";
|
import { loadOrderIfPresent, saveOrder } from "@/helpers/heritage-integration/order-helper";
|
||||||
import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
import { getPageToRouteExistingOrderTo, navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||||
|
|
||||||
|
|
@ -18,6 +18,8 @@ import baseMixin from "@/mixins/base-mixin";
|
||||||
import eventBus from "@/helpers/event-bus/event-bus";
|
import eventBus from "@/helpers/event-bus/event-bus";
|
||||||
import store from "@/store";
|
import store from "@/store";
|
||||||
import analyticsMixin from "@/mixins/analytics-mixin";
|
import analyticsMixin from "@/mixins/analytics-mixin";
|
||||||
|
import { experimentTriggers } from "../constants/experiments";
|
||||||
|
import { applicationConfig } from "../constants/application-config";
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import quote from "@/layouts/quote/quote.vue";
|
import quote from "@/layouts/quote/quote.vue";
|
||||||
|
|
@ -37,6 +39,14 @@ const routes = [
|
||||||
if (analyticsMixin.methods.noSession()) {
|
if (analyticsMixin.methods.noSession()) {
|
||||||
await analyticsMixin.methods.initSession();
|
await analyticsMixin.methods.initSession();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
updateSessionIdCookie();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getFunnelCookie()?.SuppressConceptFunnel) {
|
||||||
|
await navigateToHeritageFunnel(false);
|
||||||
|
return next(false);
|
||||||
|
}
|
||||||
|
|
||||||
// If the saved session has timed out, clear the session, execute 404 logic.
|
// If the saved session has timed out, clear the session, execute 404 logic.
|
||||||
if (getFunnelCookie() !== null && !isSavedSessionStillActive()) {
|
if (getFunnelCookie() !== null && !isSavedSessionStillActive()) {
|
||||||
|
|
@ -46,6 +56,9 @@ const routes = [
|
||||||
|
|
||||||
// On entering the funnel "fresh", read cookie information, decide what to do next.
|
// On entering the funnel "fresh", read cookie information, decide what to do next.
|
||||||
if (from.redirectedFrom === undefined) {
|
if (from.redirectedFrom === undefined) {
|
||||||
|
// clear the saveOrderPromise - if it exists in the vuex store but a new instance was created
|
||||||
|
// the saveOrderPromise will no longer point to a valid promise
|
||||||
|
baseMixin.methods.dispatchStoreAction(storeActions.RESET_SAVE_ORDER_PROMISE);
|
||||||
const loadOrderResponse = await loadOrderIfPresent();
|
const loadOrderResponse = await loadOrderIfPresent();
|
||||||
const pageToRedirectTo = await getPageToRouteExistingOrderTo(to, loadOrderResponse);
|
const pageToRedirectTo = await getPageToRouteExistingOrderTo(to, loadOrderResponse);
|
||||||
|
|
||||||
|
|
@ -56,11 +69,12 @@ const routes = [
|
||||||
return next(false);
|
return next(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Assign our fmgPage so it will load normally like the other pages.
|
// Assign our fmgPage so it will load normally like the other pages.
|
||||||
to.query.fmgPage = pageToRedirectTo;
|
to.query.fmgPage = pageToRedirectTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await runExperiments(to.query.fmgPage);
|
||||||
|
|
||||||
// Process funnel cookie.
|
// Process funnel cookie.
|
||||||
updateOrCreateFunnelCookie();
|
updateOrCreateFunnelCookie();
|
||||||
|
|
||||||
|
|
@ -125,29 +139,33 @@ const router = createRouter({
|
||||||
router.afterEach((to, from) => {
|
router.afterEach((to, from) => {
|
||||||
// Update lastPageVisited in the store
|
// Update lastPageVisited in the store
|
||||||
store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name);
|
store.commit(storeMutations.UPDATE_LAST_PAGE_VISITED, to.name);
|
||||||
// Push page view to GA
|
|
||||||
analyticsMixin.methods.pushPageViewToGA(to.query[queryStrings.FMG_PAGE]);
|
|
||||||
|
|
||||||
baseMixin.methods.dispatchStoreAction(storeActions.GET_EXPERIMENTS_BY_USER, { userId: getDeviceIdValue() })
|
// Push page view to GA
|
||||||
.then( (response) => {
|
analyticsMixin.methods.pushPageViewToGA();
|
||||||
analyticsMixin.methods.pushExperimentsToDataLayer(response.data);
|
|
||||||
});
|
// Push experiments to Data Layer
|
||||||
|
analyticsMixin.methods.pushExperimentsToDataLayer();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) => {
|
router.navigate = (scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData) => {
|
||||||
navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData);
|
navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
router.navigateToExternalUrl = (url, optionalQuery = {}) => {
|
router.navigateToExternalUrl = (url, optionalQuery = {}) => {
|
||||||
navigateToUrl(url, optionalQuery);
|
navigateToUrl(url, optionalQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Use this navigation when you need to call next() explicitly. beforeRouteEnter is a good example.
|
||||||
|
router.overrideNavigation = (scenario, currentRoute, next, optionalQuery = {}, optionalParams = {}, optionalPageData) => {
|
||||||
|
router.navigate(scenario, currentRoute, optionalQuery, optionalParams, optionalPageData);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
// PRIVATE FUNCTIONS
|
// PRIVATE FUNCTIONS
|
||||||
|
|
||||||
// Navigate to the next route, depending on the scenario.
|
// Navigate to the next route, depending on the scenario.
|
||||||
async function navigate(scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData = {}) {
|
async function navigate(scenario, currentRoute, optionalQuery = {}, optionalParams = {}, optionalPageData) {
|
||||||
if (!scenario) {
|
if (!scenario) {
|
||||||
console.error("No scenario provided. Please review the routing table.");
|
console.error("No scenario provided. Please review the routing table.");
|
||||||
return;
|
return;
|
||||||
|
|
@ -160,8 +178,9 @@ async function navigate(scenario, currentRoute, optionalQuery = {}, optionalPara
|
||||||
if (destinationFmgPageValue !== undefined) {
|
if (destinationFmgPageValue !== undefined) {
|
||||||
// We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one.
|
// We're always pushing the same path, just changing query strings. Make sure our optional query strings get combined with our fmgPage one.
|
||||||
|
|
||||||
// Append page data to the store for the NEXT page, if any. It will be an empty object if none is provided.
|
// Update page data to the store for next page if provided. Otherwise, keep existing page data or set to empty object
|
||||||
baseMixin.methods.savePageDataToStore(destinationFmgPageValue, optionalPageData);
|
const existingPageDataForPage = store.getters.pageData(destinationFmgPageValue);
|
||||||
|
baseMixin.methods.savePageDataToStore(destinationFmgPageValue, optionalPageData ? optionalPageData : existingPageDataForPage ?? {});
|
||||||
|
|
||||||
// if cookie and referralNumber/Date exists OR an emailAddress has been saved
|
// if cookie and referralNumber/Date exists OR an emailAddress has been saved
|
||||||
if ((getFunnelCookie()?.ReferralNumber && getFunnelCookie()?.ReferralDate) || store.getters.order.customer?.emailAddress) {
|
if ((getFunnelCookie()?.ReferralNumber && getFunnelCookie()?.ReferralDate) || store.getters.order.customer?.emailAddress) {
|
||||||
|
|
@ -183,15 +202,16 @@ async function navigate(scenario, currentRoute, optionalQuery = {}, optionalPara
|
||||||
// Get navigation map depending on the scenario and the current 'page' you're on.
|
// Get navigation map depending on the scenario and the current 'page' you're on.
|
||||||
function getNavigationMap(scenario, currentRoute) {
|
function getNavigationMap(scenario, currentRoute) {
|
||||||
const fmgPageValue = currentRoute.query.fmgPage;
|
const fmgPageValue = currentRoute.query.fmgPage;
|
||||||
const matchedQueryValue = routingTable
|
const matchedQueryValue = routingTable(store)
|
||||||
.filter(
|
.filter(
|
||||||
(item) =>
|
(item) =>
|
||||||
item.fmgPageValue === fmgPageValue &&
|
item.fmgPageValue === fmgPageValue &&
|
||||||
item.maps.filter((map) => map.scenario === scenario).length > 0
|
item.maps.filter((map) => map.scenario === scenario).length > 0
|
||||||
)
|
)
|
||||||
.map((m) => m.maps.filter((map) => map.scenario === scenario));
|
.map((m) => m.maps.filter((map) => map.scenario === scenario))[0]
|
||||||
|
.filter(x => x.filter === true || x.filter === undefined);
|
||||||
|
|
||||||
return matchedQueryValue[0][0];
|
return matchedQueryValue[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------- Private Functions ----------------------------------------------------------
|
//---------------------------------------------------------- Private Functions ----------------------------------------------------------
|
||||||
|
|
@ -257,4 +277,21 @@ function arePagePrerequisitesValid(component) {
|
||||||
return component.default.methods.arePagePrerequisitesValid();
|
return component.default.methods.arePagePrerequisitesValid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Run SiteEntry and PageEntry triggers for experiments
|
||||||
|
async function runExperiments(nextPage) {
|
||||||
|
if (!store.getters.applicationUser.triggeredSiteEntry) {
|
||||||
|
await baseMixin.methods.dispatchStoreAction(storeActions.RUN_EXPERIMENTS_FOR_TRIGGER, {
|
||||||
|
userId: getDeviceIdValue(),
|
||||||
|
triggerEvent: experimentTriggers.SITE_ENTRY,
|
||||||
|
triggerValue: applicationConfig.SITE_ENTRY_TRIGGER_VALUE
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await baseMixin.methods.dispatchStoreAction(storeActions.RUN_EXPERIMENTS_FOR_TRIGGER, {
|
||||||
|
userId: getDeviceIdValue(),
|
||||||
|
triggerEvent: experimentTriggers.PAGE_ENTRY,
|
||||||
|
triggerValue: nextPage
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,14 @@ const fmgPageValues = {
|
||||||
VIN_LOOKUP: "vin-lookup",
|
VIN_LOOKUP: "vin-lookup",
|
||||||
VEHICLE_PARTS: "vehicle-parts",
|
VEHICLE_PARTS: "vehicle-parts",
|
||||||
PART_QUESTIONS: "part-questions",
|
PART_QUESTIONS: "part-questions",
|
||||||
|
MOLDING_QUESTIONS: "molding-questions",
|
||||||
|
CAPABILITY_QUESTIONS: "capability-questions",
|
||||||
LICENSE_PLATE_LOOKUP: "license-plate-lookup",
|
LICENSE_PLATE_LOOKUP: "license-plate-lookup",
|
||||||
REVEAL: "reveal",
|
REVEAL: "reveal",
|
||||||
ESTIMATE: "estimate",
|
ESTIMATE: "estimate",
|
||||||
ADDRESS_VEHICLES: "address-vehicles",
|
ADDRESS_VEHICLES: "address-vehicles",
|
||||||
QUOTE: "quote"
|
QUOTE: "quote",
|
||||||
|
HERITAGE: "heritage"
|
||||||
};
|
};
|
||||||
|
|
||||||
export { fmgPageValues };
|
export { fmgPageValues };
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@ const navigationScenarios = {
|
||||||
CLICKED_FORWARD: "CLICKED_FORWARD",
|
CLICKED_FORWARD: "CLICKED_FORWARD",
|
||||||
CLICKED_FORWARD_WITH_VIN: "CLICKED_FORWARD_WITH_VIN",
|
CLICKED_FORWARD_WITH_VIN: "CLICKED_FORWARD_WITH_VIN",
|
||||||
SELECTED_PARTS: "SELECTED_PARTS",
|
SELECTED_PARTS: "SELECTED_PARTS",
|
||||||
SELECTED_VIN_WITH_PART_QUESTIONS: "SELECTED_VIN_WITH_PART_QUESTIONS",
|
SELECTED_VIN_WITH_PART_QUESTIONS: "HAS_PART_QUESTIONS",
|
||||||
SELECTED_VIN_WITH_MULTIPLE_PARTS: "SELECTED_VIN_WITH_MULTIPLE_PARTS",
|
SELECTED_VIN_WITH_MULTIPLE_PARTS: "HAS_MULTIPLE_PARTS_TO_CHOOSE",
|
||||||
|
SELECTED_VIN_WITH_MOLDING_QUESTIONS: "HAS_MOLDING_QUESTIONS",
|
||||||
|
SELECTED_VIN_WITH_CAPABILITY_QUESTIONS: "HAS_CAPABILITY_QUESTIONS",
|
||||||
CONTINUING_WITH_SINGLE_PART: "CONTINUING_WITH_SINGLE_PART",
|
CONTINUING_WITH_SINGLE_PART: "CONTINUING_WITH_SINGLE_PART",
|
||||||
CONTINUING_WITH_MULTIPLE_VEHICLES: "CONTINUING_WITH_MULTIPLE_VEHICLES",
|
CONTINUING_WITH_MULTIPLE_VEHICLES: "CONTINUING_WITH_MULTIPLE_VEHICLES",
|
||||||
SELECTED_VIN_HAS_MISMATCHED_GLASS: "SELECTED_VIN_HAS_MISMATCHED_GLASS",
|
SELECTED_VIN_HAS_MISMATCHED_GLASS: "SELECTED_VIN_HAS_MISMATCHED_GLASS",
|
||||||
|
|
@ -19,7 +21,18 @@ const navigationScenarios = {
|
||||||
SELECTED_HOME_ADDRESS: "SELECTED_HOME_ADDRESS",
|
SELECTED_HOME_ADDRESS: "SELECTED_HOME_ADDRESS",
|
||||||
ANSWERED_QUESTIONS_WITH_SINGLE_PART: "ANSWERED_QUESTIONS_WITH_SINGLE_PART",
|
ANSWERED_QUESTIONS_WITH_SINGLE_PART: "ANSWERED_QUESTIONS_WITH_SINGLE_PART",
|
||||||
ANSWERED_QUESTIONS_WITH_MULTIPLE_PARTS: "ANSWERED_QUESTIONS_WITH_MULTIPLE_PARTS",
|
ANSWERED_QUESTIONS_WITH_MULTIPLE_PARTS: "ANSWERED_QUESTIONS_WITH_MULTIPLE_PARTS",
|
||||||
SELECTED_PROVIDE_VIN_DIFFERENT_WAY: "SELECTED_PROVIDE_VIN_DIFFERENT_WAY"
|
SELECTED_PROVIDE_VIN_DIFFERENT_WAY: "SELECTED_PROVIDE_VIN_DIFFERENT_WAY",
|
||||||
|
CLICKED_BACK_WITH_PART_QUESTION_ANSWERS: "CLICKED_BACK_WITH_PART_QUESTION_ANSWERS",
|
||||||
|
CLICKED_BACK_WITHOUT_PART_QUESTION_ANSWERS: "CLICKED_BACK_WITHOUT_PART_QUESTION_ANSWERS",
|
||||||
|
ANSWERED_ALL_QUESTIONS: "ANSWERED_ALL_QUESTIONS",
|
||||||
|
HAS_PART_QUESTIONS: "HAS_PART_QUESTIONS",
|
||||||
|
HAS_MULTIPLE_PARTS_TO_CHOOSE: "HAS_MULTIPLE_PARTS_TO_CHOOSE",
|
||||||
|
HAS_MOLDING_QUESTIONS: "HAS_MOLDING_QUESTIONS",
|
||||||
|
HAS_CAPABILITY_QUESTIONS: "HAS_CAPABILITY_QUESTIONS",
|
||||||
|
CLICKED_BACK_TO_GO_TO_VIN_LOOKUP: "CLICKED_BACK_TO_GO_TO_VIN_LOOKUP",
|
||||||
|
CLICKED_BACK_TO_GO_TO_PART_QUESTIONS: "CLICKED_BACK_TO_GO_TO_PART_QUESTIONS",
|
||||||
|
CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS: "CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS",
|
||||||
|
CLICKED_BACK_TO_GO_TO_MOLDING_QUESTIONS: "CLICKED_BACK_TO_GO_TO_MOLDING_QUESTIONS"
|
||||||
};
|
};
|
||||||
|
|
||||||
export { navigationScenarios };
|
export { navigationScenarios };
|
||||||
|
|
|
||||||
|
|
@ -1,233 +1,345 @@
|
||||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios";
|
||||||
|
|
||||||
const routingTable = [
|
// Get store from router/index.js instead of importing it here to get updated values
|
||||||
{
|
const routingTable = function(store) {
|
||||||
fmgPageValue: fmgPageValues.VEHICLE_YEAR,
|
return [
|
||||||
maps: [
|
{
|
||||||
{
|
fmgPageValue: fmgPageValues.VEHICLE_YEAR,
|
||||||
scenario: navigationScenarios.SELECTED_YEAR,
|
maps: [
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_MAKE,
|
{
|
||||||
},
|
scenario: navigationScenarios.SELECTED_YEAR,
|
||||||
],
|
destinationFmgPageValue: fmgPageValues.VEHICLE_MAKE,
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
fmgPageValue: fmgPageValues.VEHICLE_MAKE,
|
},
|
||||||
maps: [
|
{
|
||||||
{
|
fmgPageValue: fmgPageValues.VEHICLE_MAKE,
|
||||||
scenario: navigationScenarios.SELECTED_MAKE,
|
maps: [
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_MODEL,
|
{
|
||||||
},
|
scenario: navigationScenarios.SELECTED_MAKE,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.VEHICLE_MODEL,
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_YEAR,
|
{
|
||||||
},
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
],
|
destinationFmgPageValue: fmgPageValues.VEHICLE_YEAR,
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
fmgPageValue: fmgPageValues.VEHICLE_MODEL,
|
},
|
||||||
maps: [
|
{
|
||||||
{
|
fmgPageValue: fmgPageValues.VEHICLE_MODEL,
|
||||||
scenario: navigationScenarios.SELECTED_MODEL,
|
maps: [
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
{
|
||||||
},
|
scenario: navigationScenarios.SELECTED_MODEL,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_MAKE,
|
{
|
||||||
},
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
],
|
destinationFmgPageValue: fmgPageValues.VEHICLE_MAKE,
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
fmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
},
|
||||||
maps: [
|
{
|
||||||
{
|
fmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
||||||
scenario: navigationScenarios.SELECTED_STYLE,
|
maps: [
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
{
|
||||||
},
|
scenario: navigationScenarios.SELECTED_STYLE,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_MODEL,
|
{
|
||||||
},
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
],
|
destinationFmgPageValue: fmgPageValues.VEHICLE_MODEL,
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
fmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
},
|
||||||
maps: [
|
{
|
||||||
{
|
fmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
maps: [
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
{
|
||||||
},
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.VEHICLE_STYLE,
|
||||||
scenario: navigationScenarios.CLICKED_FORWARD_WITH_VIN,
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
{
|
||||||
},
|
scenario: navigationScenarios.CLICKED_FORWARD_WITH_VIN,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
||||||
scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.ESTIMATE,
|
{
|
||||||
},
|
scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
|
||||||
],
|
destinationFmgPageValue: fmgPageValues.ESTIMATE,
|
||||||
},
|
},
|
||||||
{
|
],
|
||||||
fmgPageValue: fmgPageValues.VEHICLE_PARTS,
|
},
|
||||||
maps: [
|
{
|
||||||
{
|
fmgPageValue: fmgPageValues.REVEAL,
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
maps: [
|
||||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
{
|
||||||
},
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
scenario: navigationScenarios.SELECTED_PARTS,
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.QUOTE,
|
{
|
||||||
},
|
scenario: navigationScenarios.SELECTED_PARTS,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
||||||
scenario: navigationScenarios.CLICKED_FORWARD,
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.REVEAL,
|
],
|
||||||
},
|
},
|
||||||
],
|
{
|
||||||
},
|
fmgPageValue: fmgPageValues.VIN_LOOKUP,
|
||||||
{
|
maps: [
|
||||||
fmgPageValue: fmgPageValues.REVEAL,
|
{
|
||||||
maps: [
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.ESTIMATE,
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
{
|
||||||
},
|
scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
scenario: navigationScenarios.SELECTED_PARTS,
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
{
|
||||||
},
|
scenario: navigationScenarios.CLICKED_BACK_WITH_VIN,
|
||||||
],
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fmgPageValue: fmgPageValues.VIN_LOOKUP,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
||||||
maps: [
|
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||||
{
|
},
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
{
|
||||||
destinationFmgPageValue: fmgPageValues.ESTIMATE,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
||||||
},
|
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||||
{
|
},
|
||||||
scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS,
|
{
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_MOLDING_QUESTIONS,
|
||||||
},
|
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS
|
||||||
{
|
},
|
||||||
scenario: navigationScenarios.CLICKED_BACK_WITH_VIN,
|
{
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_CAPABILITY_QUESTIONS,
|
||||||
},
|
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS
|
||||||
{
|
}
|
||||||
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
],
|
||||||
//destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
},
|
||||||
destinationFmgPageValue: fmgPageValues.QUOTE,
|
{
|
||||||
},
|
fmgPageValue: fmgPageValues.LICENSE_PLATE_LOOKUP,
|
||||||
{
|
maps: [
|
||||||
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
{
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
},
|
destinationFmgPageValue: fmgPageValues.ESTIMATE,
|
||||||
],
|
},
|
||||||
},
|
{
|
||||||
{
|
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||||
fmgPageValue: fmgPageValues.LICENSE_PLATE_LOOKUP,
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
maps: [
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
||||||
destinationFmgPageValue: fmgPageValues.ESTIMATE,
|
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.CLICKED_FORWARD,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_MOLDING_QUESTIONS,
|
||||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_CAPABILITY_QUESTIONS,
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fmgPageValue: fmgPageValues.ADDRESS_LOOKUP,
|
fmgPageValue: fmgPageValues.ADDRESS_LOOKUP,
|
||||||
maps: [
|
maps: [
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
destinationFmgPageValue: fmgPageValues.ESTIMATE,
|
destinationFmgPageValue: fmgPageValues.ESTIMATE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES,
|
scenario: navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES,
|
||||||
destinationFmgPageValue: fmgPageValues.ADDRESS_VEHICLES,
|
destinationFmgPageValue: fmgPageValues.ADDRESS_VEHICLES,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS,
|
scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS,
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
||||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||||
}
|
},
|
||||||
],
|
{
|
||||||
},
|
scenario: navigationScenarios.SELECTED_VIN_WITH_MOLDING_QUESTIONS,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS
|
||||||
fmgPageValue: fmgPageValues.ADDRESS_VEHICLES,
|
},
|
||||||
maps: [
|
{
|
||||||
{
|
scenario: navigationScenarios.SELECTED_VIN_WITH_CAPABILITY_QUESTIONS,
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS
|
||||||
destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP,
|
}
|
||||||
},
|
],
|
||||||
{
|
},
|
||||||
scenario: navigationScenarios.CLICKED_FORWARD,
|
{
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
fmgPageValue: fmgPageValues.ADDRESS_VEHICLES,
|
||||||
},
|
maps: [
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
}
|
},
|
||||||
],
|
{
|
||||||
},
|
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
||||||
{
|
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||||
fmgPageValue: fmgPageValues.ESTIMATE,
|
},
|
||||||
maps: [
|
{
|
||||||
{
|
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
},
|
||||||
},
|
{
|
||||||
{
|
scenario: navigationScenarios.SELECTED_VIN_WITH_MOLDING_QUESTIONS,
|
||||||
scenario: navigationScenarios.SELECTED_MANUAL_VIN,
|
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS
|
||||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
},
|
||||||
},
|
{
|
||||||
{
|
scenario: navigationScenarios.SELECTED_VIN_WITH_CAPABILITY_QUESTIONS,
|
||||||
scenario: navigationScenarios.SELECTED_LICENSE_PLATE,
|
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS
|
||||||
destinationFmgPageValue: fmgPageValues.LICENSE_PLATE_LOOKUP,
|
},
|
||||||
},
|
{
|
||||||
{
|
scenario: navigationScenarios.SELECTED_PROVIDE_VIN_DIFFERENT_WAY,
|
||||||
scenario: navigationScenarios.SELECTED_HOME_ADDRESS,
|
destinationFmgPageValue: fmgPageValues.ESTIMATE
|
||||||
destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP,
|
}
|
||||||
},
|
],
|
||||||
],
|
},
|
||||||
},
|
{
|
||||||
{
|
fmgPageValue: fmgPageValues.ESTIMATE,
|
||||||
fmgPageValue: fmgPageValues.PART_QUESTIONS,
|
maps: [
|
||||||
maps: [
|
{
|
||||||
{
|
scenario: navigationScenarios.CLICKED_BACK,
|
||||||
scenario: navigationScenarios.CLICKED_BACK,
|
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP
|
},
|
||||||
},
|
{
|
||||||
{
|
scenario: navigationScenarios.SELECTED_MANUAL_VIN,
|
||||||
scenario: navigationScenarios.ANSWERED_QUESTIONS_WITH_MULTIPLE_PARTS,
|
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
||||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
},
|
||||||
},
|
{
|
||||||
{
|
scenario: navigationScenarios.SELECTED_LICENSE_PLATE,
|
||||||
scenario: navigationScenarios.ANSWERED_QUESTIONS_WITH_SINGLE_PART,
|
destinationFmgPageValue: fmgPageValues.LICENSE_PLATE_LOOKUP,
|
||||||
destinationFmgPageValue: fmgPageValues.QUOTE,
|
},
|
||||||
},
|
{
|
||||||
]
|
scenario: navigationScenarios.SELECTED_HOME_ADDRESS,
|
||||||
},
|
destinationFmgPageValue: fmgPageValues.ADDRESS_LOOKUP,
|
||||||
];
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fmgPageValue: fmgPageValues.PART_QUESTIONS,
|
||||||
|
maps: [
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP,
|
||||||
|
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.HAS_MULTIPLE_PARTS_TO_CHOOSE,
|
||||||
|
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.HAS_MOLDING_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.ANSWERED_ALL_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.QUOTE,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fmgPageValue: fmgPageValues.VEHICLE_PARTS,
|
||||||
|
maps: [
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.SELECTED_PARTS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.QUOTE,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||||
|
destinationFmgPageValue: fmgPageValues.REVEAL,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP,
|
||||||
|
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.HAS_MOLDING_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.ANSWERED_ALL_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.QUOTE,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fmgPageValue: fmgPageValues.MOLDING_QUESTIONS,
|
||||||
|
maps: [
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP,
|
||||||
|
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.HAS_CAPABILITY_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.ANSWERED_ALL_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.QUOTE,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
fmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||||
|
maps: [
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP,
|
||||||
|
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_MOLDING_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS
|
||||||
|
},
|
||||||
|
{
|
||||||
|
scenario: navigationScenarios.ANSWERED_ALL_QUESTIONS,
|
||||||
|
destinationFmgPageValue: fmgPageValues.QUOTE
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
export { routingTable };
|
export { routingTable };
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,11 @@ import { storeMutations } from "@/constants/store-mutations";
|
||||||
import { getDateForSavedSessionTimeout } from "@/helpers/heritage-integration/session-helper";
|
import { getDateForSavedSessionTimeout } from "@/helpers/heritage-integration/session-helper";
|
||||||
import createPersistedState from "vuex-persistedstate";
|
import createPersistedState from "vuex-persistedstate";
|
||||||
import globalMethods from "@/global-methods";
|
import globalMethods from "@/global-methods";
|
||||||
import { storeActions } from "../constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { applicationConfig } from "@/constants/application-config";
|
||||||
|
import { experimentTriggers } from "@/constants/experiments";
|
||||||
|
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
|
||||||
|
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||||
|
|
||||||
// Export State
|
// Export State
|
||||||
const getDefaultState = () => {
|
const getDefaultState = () => {
|
||||||
|
|
@ -45,10 +49,10 @@ const getDefaultState = () => {
|
||||||
numberOfChips: null,
|
numberOfChips: null,
|
||||||
glassToReplace: null,
|
glassToReplace: null,
|
||||||
partQuestionAnswers: null,
|
partQuestionAnswers: null,
|
||||||
|
capabilityQuestionAnswers: null
|
||||||
},
|
},
|
||||||
lineItems: {
|
lineItems: {
|
||||||
glassParts: null,
|
glassParts: null
|
||||||
otherParts: null
|
|
||||||
},
|
},
|
||||||
payment: {
|
payment: {
|
||||||
isInsurance: null,
|
isInsurance: null,
|
||||||
|
|
@ -60,15 +64,18 @@ const getDefaultState = () => {
|
||||||
referralDate: null,
|
referralDate: null,
|
||||||
referralCorrelationId: null,
|
referralCorrelationId: null,
|
||||||
accountNumber: 0,
|
accountNumber: 0,
|
||||||
|
eon: null
|
||||||
},
|
},
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
eventBus: [],
|
eventBus: [],
|
||||||
pageData: {},
|
pageData: {},
|
||||||
savedSessionTimeout: getDateForSavedSessionTimeout(),
|
savedSessionTimeout: getDateForSavedSessionTimeout(),
|
||||||
saveOrderPromise: null,
|
saveOrderPromise: null,
|
||||||
saveQuoteId: null,
|
savedSessionId: null,
|
||||||
crmCustomerId: null,
|
crmCustomerId: null,
|
||||||
lastPageVisited: null,
|
lastPageVisited: null,
|
||||||
|
experiments: [],
|
||||||
|
triggeredSiteEntry: false
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -118,11 +125,17 @@ export const mutations = {
|
||||||
state.order.damage.glassToReplace = glassToReplace;
|
state.order.damage.glassToReplace = glassToReplace;
|
||||||
},
|
},
|
||||||
updatePartQuestionAnswers(state, answersArray) {
|
updatePartQuestionAnswers(state, answersArray) {
|
||||||
state.order.damage.partQuestionAnswers = answersArray;
|
state.order.damage.partQuestionAnswers = answersArray;
|
||||||
|
},
|
||||||
|
updateCapabilityQuestionAnswers(state, answersArray) {
|
||||||
|
state.order.damage.capabilityQuestionAnswers = answersArray;
|
||||||
},
|
},
|
||||||
updateGlassParts(state, partsData) {
|
updateGlassParts(state, partsData) {
|
||||||
state.order.lineItems.glassParts = partsData;
|
state.order.lineItems.glassParts = partsData;
|
||||||
},
|
},
|
||||||
|
updateOtherParts(state, partsData) {
|
||||||
|
state.order.lineItems.otherParts = partsData;
|
||||||
|
},
|
||||||
updatePageData(state, pageData) {
|
updatePageData(state, pageData) {
|
||||||
state.applicationUser.pageData[pageData.page] = pageData.data;
|
state.applicationUser.pageData[pageData.page] = pageData.data;
|
||||||
},
|
},
|
||||||
|
|
@ -138,6 +151,9 @@ export const mutations = {
|
||||||
updateParentAcctNumber(state, parentAcctNumber) {
|
updateParentAcctNumber(state, parentAcctNumber) {
|
||||||
state.order.accountNumber = parentAcctNumber;
|
state.order.accountNumber = parentAcctNumber;
|
||||||
},
|
},
|
||||||
|
updateEON(state, eon) {
|
||||||
|
state.order.eon = eon;
|
||||||
|
},
|
||||||
updateIsInsurance(state, isInsurance) {
|
updateIsInsurance(state, isInsurance) {
|
||||||
state.order.payment.isInsurance = isInsurance;
|
state.order.payment.isInsurance = isInsurance;
|
||||||
},
|
},
|
||||||
|
|
@ -208,8 +224,8 @@ export const mutations = {
|
||||||
updateSaveOrderPromise(state, saveOrderPromise){
|
updateSaveOrderPromise(state, saveOrderPromise){
|
||||||
state.applicationUser.saveOrderPromise = saveOrderPromise;
|
state.applicationUser.saveOrderPromise = saveOrderPromise;
|
||||||
},
|
},
|
||||||
updateSaveQuoteId(state, saveQuoteId) {
|
updateSavedSessionId(state, savedSessionId) {
|
||||||
state.applicationUser.saveQuoteId = saveQuoteId;
|
state.applicationUser.savedSessionId = savedSessionId;
|
||||||
},
|
},
|
||||||
updateCrmCustomerId(state, crmCustomerId) {
|
updateCrmCustomerId(state, crmCustomerId) {
|
||||||
state.applicationUser.crmCustomerId = crmCustomerId;
|
state.applicationUser.crmCustomerId = crmCustomerId;
|
||||||
|
|
@ -264,16 +280,32 @@ export const mutations = {
|
||||||
},
|
},
|
||||||
resetGlassPartsState(state) {
|
resetGlassPartsState(state) {
|
||||||
state.order.lineItems.glassParts = null;
|
state.order.lineItems.glassParts = null;
|
||||||
|
state.order.damage.partQuestionAnswers = null;
|
||||||
|
state.order.damage.capabilityQuestionAnswers = 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) {
|
resetState(state) {
|
||||||
Object.assign(state, getDefaultState());
|
Object.assign(state, getDefaultState());
|
||||||
},
|
},
|
||||||
|
resetSaveOrderPromise(state) {
|
||||||
|
state.applicationUser.saveOrderPromise = null;
|
||||||
|
},
|
||||||
// Misc Mutations
|
// Misc Mutations
|
||||||
updateStateWithOrderInformation(state, orderInformation) {
|
updateStateWithOrderInformation(state, orderInformation) {
|
||||||
state.order.referralNumber = orderInformation.referralNumber;
|
state.order.referralNumber = orderInformation.referralNumber;
|
||||||
state.order.referralDate = orderInformation.referralDate;
|
state.order.referralDate = orderInformation.referralDate;
|
||||||
state.order.referralCorrelationId = orderInformation.referralCorrelationId;
|
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, {
|
state.order.vehicle = Object.assign(state.order.vehicle, {
|
||||||
year: orderInformation.vehicle?.year,
|
year: orderInformation.vehicle?.year,
|
||||||
|
|
@ -312,7 +344,14 @@ export const mutations = {
|
||||||
state.order.payment.insuranceCoverage.isVerified = orderInformation?.insuranceInfo.coverageVerified;
|
state.order.payment.insuranceCoverage.isVerified = orderInformation?.insuranceInfo.coverageVerified;
|
||||||
|
|
||||||
state.order.customer.emailAddress = orderInformation.customer.emailAddress;
|
state.order.customer.emailAddress = orderInformation.customer.emailAddress;
|
||||||
|
state.applicationUser.experiments = orderInformation.experiments;
|
||||||
},
|
},
|
||||||
|
updateExperiments(state, experiments) {
|
||||||
|
state.applicationUser.experiments = experiments;
|
||||||
|
},
|
||||||
|
updateTriggeredSiteEntry(state, wasSiteEntryTriggered) {
|
||||||
|
state.applicationUser.triggeredSiteEntry = wasSiteEntryTriggered;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export Getters
|
// Export Getters
|
||||||
|
|
@ -333,6 +372,35 @@ export const getters = {
|
||||||
applicationUser: (state) => state.applicationUser,
|
applicationUser: (state) => state.applicationUser,
|
||||||
order: (state) => state.order,
|
order: (state) => state.order,
|
||||||
payment: (state) => state.order.payment,
|
payment: (state) => state.order.payment,
|
||||||
|
experimentOrder: (state) => {
|
||||||
|
return {
|
||||||
|
vehicleYear: state.order.vehicle.year,
|
||||||
|
vehicleMake: state.order.vehicle.make,
|
||||||
|
vehicleModel: state.order.vehicle.model,
|
||||||
|
vehicleStyle: state.order.vehicle.style,
|
||||||
|
isRepair: state.order.damage.isRepair,
|
||||||
|
numberOfChips: state.order.damage.numberOfChips,
|
||||||
|
carId: state.order.vehicle.carId,
|
||||||
|
serviceCity: state.order.serviceLocation.city,
|
||||||
|
serviceState: state.order.serviceLocation.state,
|
||||||
|
serviceZipCode: state.order.serviceLocation.zipCode,
|
||||||
|
parentAccountNumber: state.order.accountNumber,
|
||||||
|
isCoverageVerified: state.order.payment.insuranceCoverage.isVerified,
|
||||||
|
orderPartNumbers: [...getAllValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "partNumber"), ...getAllValuesOfPropertyInArrayOfObjects(state.order.lineItems.otherParts, "partNumber")],
|
||||||
|
orderPartTypes: [...getAllValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "recalibrationType"), ...getAllValuesOfPropertyInArrayOfObjects(state.order.lineItems.otherParts, "recalibrationType")],
|
||||||
|
hasRecalibrationPart: getAllValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "requiresRecalibration")?.length > 0,
|
||||||
|
selectedMultiGlass: state.order.damage.glassToReplace?.length > 1,
|
||||||
|
selectedWindshieldGlass: getAllValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.WINDSHIELD),
|
||||||
|
selectedBackGlass: getAllValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.REAR),
|
||||||
|
selectedDriverSideGlass: getAllValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.DRIVER),
|
||||||
|
selectedPassengerSideGlass: getAllValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.PASSENGER)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
experimentSettings: (state) => state.applicationUser.experiments.map(x => x.settings).reduce((r, c) => Object.assign(r, c), {})
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAllValuesOfPropertyInArrayOfObjects(array, propertyName) {
|
||||||
|
return (array ?? []).map(x => x[propertyName]).filter(x => x);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export Actions
|
// Export Actions
|
||||||
|
|
@ -455,6 +523,9 @@ export const actions = {
|
||||||
resetState(context) {
|
resetState(context) {
|
||||||
context.commit(storeMutations.RESET_STATE);
|
context.commit(storeMutations.RESET_STATE);
|
||||||
},
|
},
|
||||||
|
resetSaveOrderPromise(context) {
|
||||||
|
context.commit(storeMutations.RESET_SAVE_ORDER_PROMISE);
|
||||||
|
},
|
||||||
|
|
||||||
// Content API Actions
|
// Content API Actions
|
||||||
getRouteInfo(context, { pageName }) {
|
getRouteInfo(context, { pageName }) {
|
||||||
|
|
@ -481,39 +552,51 @@ export const actions = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Analytics Actions
|
// Analytics Actions
|
||||||
logExperimentExposure(context, { userId, sessionKey, pageName, universeName }) {
|
logExperimentExposure(context, { userId, sessionKey, pageName, experiment }) {
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
method: endpoints.LogExperimentExposureIfAssigned.method,
|
method: endpoints.LogExperimentExposureIfAssigned.method,
|
||||||
endpoint: endpoints.LogExperimentExposureIfAssigned.url,
|
endpoint: endpoints.LogExperimentExposureIfAssigned.url,
|
||||||
payload: {
|
payload: {
|
||||||
userId: userId,
|
experimentForLogging: {
|
||||||
sessionKey: sessionKey,
|
userId: userId,
|
||||||
pageName: pageName,
|
experimentUniverseId: experiment.universeId,
|
||||||
universeName: universeName
|
experimentUniverseName: experiment.universeName,
|
||||||
|
experimentTestId: experiment.testId,
|
||||||
|
experimentTestName: experiment.testName,
|
||||||
|
experimentVariationId: experiment.variationId,
|
||||||
|
experimentVariationName: experiment.variationName,
|
||||||
|
enabled: experiment.isActive,
|
||||||
|
isExposed: experiment.isExposed,
|
||||||
|
userPartitionNumber: experiment.userPartitionNumber,
|
||||||
|
assignmentId: experiment.assignmentId,
|
||||||
|
sessionKey: sessionKey,
|
||||||
|
pageName: pageName,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Misc Actions
|
// Misc Actions
|
||||||
updateStoreWithSaveOrderResponse(context, { referralNumber, referralDate, referralCorrelationId, accountNumber, saveQuoteId, crmCustomerId }) {
|
updateStoreWithSaveOrderResponse(context, { referralNumber, referralDate, referralCorrelationId, eon, accountNumber, savedSessionId, crmCustomerId }) {
|
||||||
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
|
context.commit(storeMutations.UPDATE_REFERRAL_NUMBER, referralNumber);
|
||||||
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
||||||
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
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_PARENT_ACCT_NUMBER, accountNumber);
|
||||||
context.commit(storeMutations.UPDATE_SAVE_QUOTE_ID, saveQuoteId);
|
context.commit(storeMutations.UPDATE_SAVED_SESSION_ID, savedSessionId);
|
||||||
context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
|
context.commit(storeMutations.UPDATE_CRM_CUSTOMER_ID, crmCustomerId);
|
||||||
},
|
},
|
||||||
|
logPageView(context, { userId, sessionKey, pageName, sessionId, action, event, shouldUseSessionId, experimentsForUser }) {
|
||||||
logPageView(context, { userId, sessionKey, pageName, sessionId, action, event, shouldUseSessionId }) {
|
|
||||||
var payload = {
|
var payload = {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
sessionKey: sessionKey,
|
sessionKey: sessionKey,
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
pageName: pageName,
|
pageName: pageName,
|
||||||
applicationName: 'SafeliteDotCom',
|
applicationName: applicationConfig.APPLICATION_NAME,
|
||||||
action: action,
|
action: action,
|
||||||
event: event,
|
event: event,
|
||||||
shouldUseSessionId: shouldUseSessionId
|
shouldUseSessionId: shouldUseSessionId,
|
||||||
|
experimentsForUser: experimentsForUser
|
||||||
};
|
};
|
||||||
|
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
|
|
@ -523,18 +606,19 @@ export const actions = {
|
||||||
logApiCall: false
|
logApiCall: false
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
logCustomEvent(context, { userId, sessionKey, pageName, sessionId, category, action, label, value, shouldUseSessionId }) {
|
logCustomEvent(context, { userId, sessionKey, pageName, sessionId, category, action, label, value, shouldUseSessionId, experimentsForUser }) {
|
||||||
var payload = {
|
var payload = {
|
||||||
userId: userId,
|
userId: userId,
|
||||||
sessionKey: sessionKey,
|
sessionKey: sessionKey,
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
pageName: pageName,
|
pageName: pageName,
|
||||||
applicationName: 'SafeliteDotCom',
|
applicationName: applicationConfig.APPLICATION_NAME,
|
||||||
category: category,
|
category: category,
|
||||||
action: action,
|
action: action,
|
||||||
label: label,
|
label: label,
|
||||||
value: value,
|
value: value,
|
||||||
shouldUseSessionId: shouldUseSessionId
|
shouldUseSessionId: shouldUseSessionId,
|
||||||
|
experimentsForUser: experimentsForUser
|
||||||
};
|
};
|
||||||
|
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
|
|
@ -546,7 +630,7 @@ export const actions = {
|
||||||
},
|
},
|
||||||
initializeSession(context, { userId, sessionId, userAgent, referrer }) {
|
initializeSession(context, { userId, sessionId, userAgent, referrer }) {
|
||||||
var payload = {
|
var payload = {
|
||||||
applicationName: 'SafeliteDotCom',
|
applicationName: applicationConfig.APPLICATION_NAME,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
deviceId: userId,
|
deviceId: userId,
|
||||||
sessionId: sessionId,
|
sessionId: sessionId,
|
||||||
|
|
@ -565,10 +649,11 @@ export const actions = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// Misc 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_NUMBER, referralNumber);
|
||||||
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
context.commit(storeMutations.UPDATE_REFERRAL_DATE, referralDate);
|
||||||
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
context.commit(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, referralCorrelationId);
|
||||||
|
context.commit(storeMutations.UPDATE_EON, eon);
|
||||||
},
|
},
|
||||||
|
|
||||||
GetExperimentsByUser(context, { userId }) {
|
GetExperimentsByUser(context, { userId }) {
|
||||||
|
|
@ -579,6 +664,28 @@ export const actions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async runExperimentsForTrigger(context, { userId, triggerEvent, triggerValue }) {
|
||||||
|
if (triggerEvent == experimentTriggers.SITE_ENTRY) {
|
||||||
|
context.commit(storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload = {
|
||||||
|
applicationName: applicationConfig.APPLICATION_NAME,
|
||||||
|
userId: userId,
|
||||||
|
triggerEvent: triggerEvent,
|
||||||
|
triggerValue: triggerValue,
|
||||||
|
experimentOrder: context.getters.experimentOrder
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await globalMethods.callHttpClient({
|
||||||
|
method: endpoints.RunExperimentsForTrigger.method,
|
||||||
|
endpoint: endpoints.RunExperimentsForTrigger.url,
|
||||||
|
payload: payload,
|
||||||
|
});
|
||||||
|
|
||||||
|
context.commit(storeMutations.UPDATE_EXPERIMENTS, response.data.experiments);
|
||||||
|
},
|
||||||
|
|
||||||
getEvoxImage(context, { relativeUrl }) {
|
getEvoxImage(context, { relativeUrl }) {
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
method: endpoints.GetPageData.method,
|
method: endpoints.GetPageData.method,
|
||||||
|
|
@ -635,12 +742,36 @@ export const actions = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getCapabilityQuestions(context, { carId, partNumber }) {
|
||||||
|
return globalMethods.callHttpClient({
|
||||||
|
method: endpoints.GetCapabilityQuestions.method,
|
||||||
|
endpoint: `${endpoints.GetCapabilityQuestions.url}/${carId}/${partNumber}`,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
getPartFromCapabilityQuestionAnswer(context, selectedAnswerResult1) {
|
||||||
|
const pageData = context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);
|
||||||
|
|
||||||
|
const part = pageData.partsOrQuestions.find(x => x.glassLocation === damageLocationsSelected.WINDSHIELD).parts[0];
|
||||||
|
const capabilityQuestionAnswers = context.getters.damage.capabilityQuestionAnswers;
|
||||||
|
|
||||||
|
return globalMethods.callHttpClient({
|
||||||
|
method: endpoints.ApplyCapabilityAnswerToPart.method,
|
||||||
|
endpoint: endpoints.ApplyCapabilityAnswerToPart.url,
|
||||||
|
payload: {
|
||||||
|
part,
|
||||||
|
capabilityAnswerResults: capabilityQuestionAnswers
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
// Order API Actions
|
// Order API Actions
|
||||||
saveOrder(context) {
|
saveOrder(context) {
|
||||||
const vehicle = context.getters.vehicle;
|
const vehicle = context.getters.vehicle;
|
||||||
const damage = context.getters.damage;
|
const damage = context.getters.damage;
|
||||||
const order = context.state.order;
|
const order = context.state.order;
|
||||||
const applicationUser = context.getters.applicationUser;
|
const applicationUser = context.getters.applicationUser;
|
||||||
|
const lineItems = context.state.order.lineItems;
|
||||||
|
|
||||||
return globalMethods.callHttpClient({
|
return globalMethods.callHttpClient({
|
||||||
method: endpoints.SaveOrder.method,
|
method: endpoints.SaveOrder.method,
|
||||||
|
|
@ -671,6 +802,9 @@ export const actions = {
|
||||||
customer: {
|
customer: {
|
||||||
emailAddress: order.customer.emailAddress,
|
emailAddress: order.customer.emailAddress,
|
||||||
},
|
},
|
||||||
|
lineItems: {
|
||||||
|
glassParts: lineItems.glassParts
|
||||||
|
},
|
||||||
serviceLocation: {
|
serviceLocation: {
|
||||||
streetAddress: order.serviceLocation.address,
|
streetAddress: order.serviceLocation.address,
|
||||||
city: order.serviceLocation.city,
|
city: order.serviceLocation.city,
|
||||||
|
|
@ -683,8 +817,8 @@ export const actions = {
|
||||||
existingPromoCode: null,
|
existingPromoCode: null,
|
||||||
lastPage: applicationUser.lastPageVisited,
|
lastPage: applicationUser.lastPageVisited,
|
||||||
crmCustomerId: applicationUser.crmCustomerId,
|
crmCustomerId: applicationUser.crmCustomerId,
|
||||||
saveQuoteId: applicationUser.saveQuoteId,
|
savedSessionId: applicationUser.savedSessionId,
|
||||||
|
experiments: applicationUser.experiments,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -699,7 +833,10 @@ export const actions = {
|
||||||
accountNumber: accountNumber?.toString()
|
accountNumber: accountNumber?.toString()
|
||||||
},
|
},
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
context.commit(storeMutations.RESET_STATE);
|
// 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);
|
context.commit(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, response.data);
|
||||||
return response;
|
return response;
|
||||||
});
|
});
|
||||||
|
|
@ -724,6 +861,7 @@ export const actions = {
|
||||||
|
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||||
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
context.commit(storeMutations.UPDATE_YEAR, year);
|
context.commit(storeMutations.UPDATE_YEAR, year);
|
||||||
|
|
@ -744,6 +882,7 @@ export const actions = {
|
||||||
|
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||||
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
context.commit(storeMutations.UPDATE_MAKE, make);
|
context.commit(storeMutations.UPDATE_MAKE, make);
|
||||||
|
|
@ -763,6 +902,7 @@ export const actions = {
|
||||||
|
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||||
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
context.commit(storeMutations.UPDATE_MODEL, model);
|
context.commit(storeMutations.UPDATE_MODEL, model);
|
||||||
|
|
@ -780,6 +920,7 @@ export const actions = {
|
||||||
|
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||||
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
context.commit(storeMutations.UPDATE_STYLE, style);
|
context.commit(storeMutations.UPDATE_STYLE, style);
|
||||||
|
|
@ -810,6 +951,7 @@ export const actions = {
|
||||||
//Reset dependent state when changing
|
//Reset dependent state when changing
|
||||||
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
|
if (vehicleInfo.vin !== context.state.order.vehicle.vin) {
|
||||||
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
||||||
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
|
|
||||||
if (!isSelectedGlassAvailableForVehicle) {
|
if (!isSelectedGlassAvailableForVehicle) {
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
|
|
@ -828,6 +970,7 @@ export const actions = {
|
||||||
|
|
||||||
if (!isSelectedGlassAvailableForVehicle) {
|
if (!isSelectedGlassAvailableForVehicle) {
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
|
|
@ -843,6 +986,7 @@ export const actions = {
|
||||||
|
|
||||||
if (!isSelectedGlassAvailableForVehicle) {
|
if (!isSelectedGlassAvailableForVehicle) {
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
|
|
@ -851,10 +995,22 @@ export const actions = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
savePartQuestionAnswers(context, partQuestionAnswersArray) {
|
savePartQuestionAnswers(context, partQuestionAnswersArray) {
|
||||||
|
// if part question answers have changed, reset subsequent question answers
|
||||||
|
const previousResultsArray = context.getters.damage.partQuestionAnswers;
|
||||||
|
const havePartQuestionAnswersChanged = previousResultsArray.length !== partQuestionAnswersArray.length ||
|
||||||
|
!previousResultsArray.every((x, i) => x.result === partQuestionAnswersArray[i].result);
|
||||||
|
|
||||||
|
if (havePartQuestionAnswersChanged) {
|
||||||
|
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||||
|
}
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
context.commit(storeMutations.UPDATE_PART_QUESTION_ANSWERS, partQuestionAnswersArray);
|
context.commit(storeMutations.UPDATE_PART_QUESTION_ANSWERS, partQuestionAnswersArray);
|
||||||
},
|
},
|
||||||
|
saveCapabilityQuestionAnswers(context, capabilityQuestionAnswers) {
|
||||||
|
//Save new values
|
||||||
|
context.commit(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, capabilityQuestionAnswers);
|
||||||
|
},
|
||||||
// Misc order actions
|
// Misc order actions
|
||||||
saveServiceLocation(context, serviceLocationInfo) {
|
saveServiceLocation(context, serviceLocationInfo) {
|
||||||
context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo);
|
context.commit(storeMutations.UPDATE_SERVICE_LOCATION, serviceLocationInfo);
|
||||||
|
|
@ -868,6 +1024,7 @@ export const actions = {
|
||||||
|
|
||||||
if (!isSelectedGlassAvailableForVehicle) {
|
if (!isSelectedGlassAvailableForVehicle) {
|
||||||
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
context.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||||
|
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Save new values
|
//Save new values
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@ import globalMethods from "@/global-methods";
|
||||||
import { mutations, state, actions, getters } from "@/store";
|
import { mutations, state, actions, getters } from "@/store";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { experimentTriggers } from "@/constants/experiments";
|
||||||
|
|
||||||
// Mock global method
|
// Mock global method
|
||||||
globalMethods.callHttpClient = jest.fn();
|
globalMethods.callHttpClient = jest.fn();
|
||||||
|
|
||||||
|
|
@ -249,6 +251,36 @@ describe("Mutations", () => {
|
||||||
expect(storeState.order.payment.insuranceCoverage.isVerified).toEqual(true);
|
expect(storeState.order.payment.insuranceCoverage.isVerified).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("updateExperiments, should set experiments", () => {
|
||||||
|
// Arrange
|
||||||
|
const storeState = state;
|
||||||
|
const mockExperimentsList = [
|
||||||
|
{
|
||||||
|
universeName: "XYZ",
|
||||||
|
settings: {
|
||||||
|
ExperimentSetting: "ExperimentValue"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// Act
|
||||||
|
mutations.updateExperiments(storeState, mockExperimentsList);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(storeState.applicationUser.experiments).toEqual(mockExperimentsList);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updateTriggeredSiteEntry, should set triggeredSiteEntry", () => {
|
||||||
|
// Arrange
|
||||||
|
const storeState = state;
|
||||||
|
|
||||||
|
// Act
|
||||||
|
mutations.updateTriggeredSiteEntry(storeState, true);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(storeState.applicationUser.triggeredSiteEntry).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Actions", () => {
|
describe("Actions", () => {
|
||||||
|
|
@ -574,13 +606,14 @@ describe("Actions", () => {
|
||||||
applicationUser: {
|
applicationUser: {
|
||||||
lastPageVisited: "test-page",
|
lastPageVisited: "test-page",
|
||||||
crmCustomerId: "xxx-xxx-xxx",
|
crmCustomerId: "xxx-xxx-xxx",
|
||||||
saveQuoteId: "xxx-xxx-xxx"
|
savedSessionId: "xxx-xxx-xxx"
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
context.state = {
|
context.state = {
|
||||||
order: {
|
order: {
|
||||||
serviceLocation: {},
|
serviceLocation: {},
|
||||||
customer: {}
|
customer: {},
|
||||||
|
lineItems: {}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -630,7 +663,7 @@ describe("Actions", () => {
|
||||||
referralDate: new Date().toUTCString(),
|
referralDate: new Date().toUTCString(),
|
||||||
referralCorrelationId: "xxx-xxx-xxx",
|
referralCorrelationId: "xxx-xxx-xxx",
|
||||||
accountNumber: "167132",
|
accountNumber: "167132",
|
||||||
saveQuoteId: "xxx-xxx-xxx",
|
savedSessionId: "xxx-xxx-xxx",
|
||||||
crmCustomerId: "xxx-xxx-xxx",
|
crmCustomerId: "xxx-xxx-xxx",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -639,7 +672,7 @@ describe("Actions", () => {
|
||||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_DATE, new Date().toUTCString());
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_DATE, new Date().toUTCString());
|
||||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, "xxx-xxx-xxx");
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_REFERRAL_CORRELATION_ID, "xxx-xxx-xxx");
|
||||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_PARENT_ACCT_NUMBER, "167132");
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_PARENT_ACCT_NUMBER, "167132");
|
||||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_SAVE_QUOTE_ID, "xxx-xxx-xxx");
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_SAVED_SESSION_ID, "xxx-xxx-xxx");
|
||||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_CRM_CUSTOMER_ID, "xxx-xxx-xxx");
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_CRM_CUSTOMER_ID, "xxx-xxx-xxx");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -793,13 +826,27 @@ describe("Actions", () => {
|
||||||
|
|
||||||
|
|
||||||
// Act
|
// 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);
|
actions.saveVinLookup(context, payload);
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(dispatch).toHaveBeenNthCalledWith(1, storeActions.RESET_REGISTRATION_STATE_AND_DEPENDENCIES);
|
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_VEHICLE, payload.vehicleInfo);
|
||||||
expect(commit).toBeCalledWith(storeMutations.UPDATE_REGISTRATION, payload.registrationInfo);
|
expect(commit).toBeCalledWith(storeMutations.UPDATE_REGISTRATION, payload.registrationInfo);
|
||||||
|
|
@ -1041,6 +1088,72 @@ describe("Actions", () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("runExperimentsForTrigger", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mutations.resetState(state);
|
||||||
|
globalMethods.callHttpClient = jest.fn().mockReturnValue({
|
||||||
|
data: {
|
||||||
|
experiments: [
|
||||||
|
{
|
||||||
|
mockProperty: "mockValue"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
test("triggerEvent is SiteEntry => set triggeredSiteEntry to true in store", async () => {
|
||||||
|
// Arrange
|
||||||
|
const context = state;
|
||||||
|
context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value));
|
||||||
|
context.getters = {
|
||||||
|
...getters,
|
||||||
|
applicationUser: getters.applicationUser(context)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await actions.runExperimentsForTrigger(context, {
|
||||||
|
triggerEvent: experimentTriggers.SITE_ENTRY,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(context.commit).toHaveBeenNthCalledWith(1, storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, true);
|
||||||
|
expect(context.getters.applicationUser.triggeredSiteEntry).toBe(true);
|
||||||
|
expect(globalMethods.callHttpClient).toHaveBeenCalled();
|
||||||
|
expect(context.commit).toHaveBeenNthCalledWith(2, storeMutations.UPDATE_EXPERIMENTS, [
|
||||||
|
{
|
||||||
|
mockProperty: "mockValue"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("triggerEvent is not SiteEntry => triggeredSiteEntry is false in store", async () => {
|
||||||
|
// Arrange
|
||||||
|
const context = state;
|
||||||
|
context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value));
|
||||||
|
context.getters = {
|
||||||
|
...getters,
|
||||||
|
applicationUser: getters.applicationUser(context)
|
||||||
|
};
|
||||||
|
expect(context.commit).toHaveBeenCalledTimes(0);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
await actions.runExperimentsForTrigger(context, {
|
||||||
|
triggerEvent: "NotSiteEntry",
|
||||||
|
});
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
expect(context.commit).not.toHaveBeenCalledWith(storeMutations.UPDATE_TRIGGERED_SITE_ENTRY, expect.any);
|
||||||
|
expect(context.getters.applicationUser.triggeredSiteEntry).toBe(false);
|
||||||
|
|
||||||
|
expect(globalMethods.callHttpClient).toHaveBeenCalledTimes(1);
|
||||||
|
expect(context.commit).toHaveBeenNthCalledWith(1, storeMutations.UPDATE_EXPERIMENTS, [
|
||||||
|
{
|
||||||
|
mockProperty: "mockValue"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1138,4 +1251,321 @@ describe("Getters", () => {
|
||||||
expect(getters.payment(storeState).insuranceCoverage.isVerified).toEqual(true);
|
expect(getters.payment(storeState).insuranceCoverage.isVerified).toEqual(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("experimentOrder", () => {
|
||||||
|
test("glassToReplace, glassParts, and otherParts are null > return correct experimentOrder values", () => {
|
||||||
|
// Arrange
|
||||||
|
const storeState = state;
|
||||||
|
const mockStateValues = {
|
||||||
|
vehicleYear: 1000,
|
||||||
|
vehicleMake: "CarMake",
|
||||||
|
vehicleModel: "CarModel",
|
||||||
|
vehicleStyle: "SuperCoolStyle",
|
||||||
|
isRepair: true,
|
||||||
|
numberOfChips: 9999999,
|
||||||
|
carId: "Gibberish",
|
||||||
|
serviceCity: "Columbus",
|
||||||
|
serviceState: "OH-IO",
|
||||||
|
serviceZipCode: 43215,
|
||||||
|
parentAccountNumber: "999999",
|
||||||
|
isCoverageVerified: true,
|
||||||
|
glassParts: null,
|
||||||
|
otherParts: null,
|
||||||
|
glassToReplace: null
|
||||||
|
}
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mutations.updateYear(storeState, mockStateValues.vehicleYear);
|
||||||
|
mutations.updateMake(storeState, mockStateValues.vehicleMake);
|
||||||
|
mutations.updateModel(storeState, mockStateValues.vehicleModel);
|
||||||
|
mutations.updateStyle(storeState, mockStateValues.vehicleStyle);
|
||||||
|
mutations.updateIsRepair(storeState, mockStateValues.isRepair);
|
||||||
|
mutations.updateNumberOfChips(storeState, mockStateValues.numberOfChips);
|
||||||
|
mutations.updateCarId(storeState, mockStateValues.carId);
|
||||||
|
mutations.updateServiceLocation(storeState, {
|
||||||
|
city: mockStateValues.serviceCity,
|
||||||
|
state: mockStateValues.serviceState,
|
||||||
|
zipCode: mockStateValues.serviceZipCode
|
||||||
|
});
|
||||||
|
mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber);
|
||||||
|
mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified);
|
||||||
|
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
||||||
|
mutations.updateOtherParts(storeState, mockStateValues.otherParts);
|
||||||
|
mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(getters.experimentOrder(storeState)).toEqual({
|
||||||
|
vehicleYear: mockStateValues.vehicleYear,
|
||||||
|
vehicleMake: mockStateValues.vehicleMake,
|
||||||
|
vehicleModel: mockStateValues.vehicleModel,
|
||||||
|
vehicleStyle: mockStateValues.vehicleStyle,
|
||||||
|
isRepair: mockStateValues.isRepair,
|
||||||
|
numberOfChips: mockStateValues.numberOfChips,
|
||||||
|
carId: mockStateValues.carId,
|
||||||
|
serviceCity: mockStateValues.serviceCity,
|
||||||
|
serviceState: mockStateValues.serviceState,
|
||||||
|
serviceZipCode: mockStateValues.serviceZipCode,
|
||||||
|
parentAccountNumber: mockStateValues.parentAccountNumber,
|
||||||
|
isCoverageVerified: mockStateValues.isCoverageVerified,
|
||||||
|
orderPartNumbers: [],
|
||||||
|
orderPartTypes: [],
|
||||||
|
hasRecalibrationPart: false,
|
||||||
|
selectedMultiGlass: false,
|
||||||
|
selectedWindshieldGlass: false,
|
||||||
|
selectedBackGlass: false,
|
||||||
|
selectedDriverSideGlass: false,
|
||||||
|
selectedPassengerSideGlass: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("glassToReplace, glassParts, and otherParts are empty > return correct experimentOrder values", () => {
|
||||||
|
// Arrange
|
||||||
|
const storeState = state;
|
||||||
|
const mockStateValues = {
|
||||||
|
vehicleYear: 1000,
|
||||||
|
vehicleMake: "CarMake",
|
||||||
|
vehicleModel: "CarModel",
|
||||||
|
vehicleStyle: "SuperCoolStyle",
|
||||||
|
isRepair: true,
|
||||||
|
numberOfChips: 9999999,
|
||||||
|
carId: "Gibberish",
|
||||||
|
serviceCity: "Columbus",
|
||||||
|
serviceState: "OH-IO",
|
||||||
|
serviceZipCode: 43215,
|
||||||
|
parentAccountNumber: "999999",
|
||||||
|
isCoverageVerified: true,
|
||||||
|
glassParts: [],
|
||||||
|
otherParts: [],
|
||||||
|
glassToReplace: []
|
||||||
|
}
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mutations.updateYear(storeState, mockStateValues.vehicleYear);
|
||||||
|
mutations.updateMake(storeState, mockStateValues.vehicleMake);
|
||||||
|
mutations.updateModel(storeState, mockStateValues.vehicleModel);
|
||||||
|
mutations.updateStyle(storeState, mockStateValues.vehicleStyle);
|
||||||
|
mutations.updateIsRepair(storeState, mockStateValues.isRepair);
|
||||||
|
mutations.updateNumberOfChips(storeState, mockStateValues.numberOfChips);
|
||||||
|
mutations.updateCarId(storeState, mockStateValues.carId);
|
||||||
|
mutations.updateServiceLocation(storeState, {
|
||||||
|
city: mockStateValues.serviceCity,
|
||||||
|
state: mockStateValues.serviceState,
|
||||||
|
zipCode: mockStateValues.serviceZipCode
|
||||||
|
});
|
||||||
|
mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber);
|
||||||
|
mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified);
|
||||||
|
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
||||||
|
mutations.updateOtherParts(storeState, mockStateValues.otherParts);
|
||||||
|
mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(getters.experimentOrder(storeState)).toEqual({
|
||||||
|
vehicleYear: mockStateValues.vehicleYear,
|
||||||
|
vehicleMake: mockStateValues.vehicleMake,
|
||||||
|
vehicleModel: mockStateValues.vehicleModel,
|
||||||
|
vehicleStyle: mockStateValues.vehicleStyle,
|
||||||
|
isRepair: mockStateValues.isRepair,
|
||||||
|
numberOfChips: mockStateValues.numberOfChips,
|
||||||
|
carId: mockStateValues.carId,
|
||||||
|
serviceCity: mockStateValues.serviceCity,
|
||||||
|
serviceState: mockStateValues.serviceState,
|
||||||
|
serviceZipCode: mockStateValues.serviceZipCode,
|
||||||
|
parentAccountNumber: mockStateValues.parentAccountNumber,
|
||||||
|
isCoverageVerified: mockStateValues.isCoverageVerified,
|
||||||
|
orderPartNumbers: [],
|
||||||
|
orderPartTypes: [],
|
||||||
|
hasRecalibrationPart: false,
|
||||||
|
selectedMultiGlass: false,
|
||||||
|
selectedWindshieldGlass: false,
|
||||||
|
selectedBackGlass: false,
|
||||||
|
selectedDriverSideGlass: false,
|
||||||
|
selectedPassengerSideGlass: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Single windshield requiring recalibration is selected > return correct experimentOrder values", () => {
|
||||||
|
// Arrange
|
||||||
|
const storeState = state;
|
||||||
|
const mockStateValues = {
|
||||||
|
vehicleYear: 1000,
|
||||||
|
vehicleMake: "CarMake",
|
||||||
|
vehicleModel: "CarModel",
|
||||||
|
vehicleStyle: "SuperCoolStyle",
|
||||||
|
isRepair: false,
|
||||||
|
numberOfChips: 0,
|
||||||
|
carId: "Gibberish",
|
||||||
|
serviceCity: "Columbus",
|
||||||
|
serviceState: "OH-IO",
|
||||||
|
serviceZipCode: 43215,
|
||||||
|
parentAccountNumber: "999999",
|
||||||
|
isCoverageVerified: false,
|
||||||
|
glassParts: [
|
||||||
|
{
|
||||||
|
partNumber: "WINDSHIELDPARTNUMBER",
|
||||||
|
description: "This is a windshield",
|
||||||
|
recalibrationType: "ADAS, maybe",
|
||||||
|
requiresRecalibration: true,
|
||||||
|
requiresCapabilityQuestions: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
otherParts: [
|
||||||
|
|
||||||
|
],
|
||||||
|
glassToReplace: [
|
||||||
|
{
|
||||||
|
glassLocation: "Windshield",
|
||||||
|
glassName: "Single"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mutations.updateYear(storeState, mockStateValues.vehicleYear);
|
||||||
|
mutations.updateMake(storeState, mockStateValues.vehicleMake);
|
||||||
|
mutations.updateModel(storeState, mockStateValues.vehicleModel);
|
||||||
|
mutations.updateStyle(storeState, mockStateValues.vehicleStyle);
|
||||||
|
mutations.updateIsRepair(storeState, mockStateValues.isRepair);
|
||||||
|
mutations.updateNumberOfChips(storeState, mockStateValues.numberOfChips);
|
||||||
|
mutations.updateCarId(storeState, mockStateValues.carId);
|
||||||
|
mutations.updateServiceLocation(storeState, {
|
||||||
|
city: mockStateValues.serviceCity,
|
||||||
|
state: mockStateValues.serviceState,
|
||||||
|
zipCode: mockStateValues.serviceZipCode
|
||||||
|
});
|
||||||
|
mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber);
|
||||||
|
mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified);
|
||||||
|
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
||||||
|
mutations.updateOtherParts(storeState, mockStateValues.otherParts);
|
||||||
|
mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(getters.experimentOrder(storeState)).toEqual({
|
||||||
|
vehicleYear: mockStateValues.vehicleYear,
|
||||||
|
vehicleMake: mockStateValues.vehicleMake,
|
||||||
|
vehicleModel: mockStateValues.vehicleModel,
|
||||||
|
vehicleStyle: mockStateValues.vehicleStyle,
|
||||||
|
isRepair: mockStateValues.isRepair,
|
||||||
|
numberOfChips: mockStateValues.numberOfChips,
|
||||||
|
carId: mockStateValues.carId,
|
||||||
|
serviceCity: mockStateValues.serviceCity,
|
||||||
|
serviceState: mockStateValues.serviceState,
|
||||||
|
serviceZipCode: mockStateValues.serviceZipCode,
|
||||||
|
parentAccountNumber: mockStateValues.parentAccountNumber,
|
||||||
|
isCoverageVerified: mockStateValues.isCoverageVerified,
|
||||||
|
orderPartNumbers: ["WINDSHIELDPARTNUMBER"],
|
||||||
|
orderPartTypes: ["ADAS, maybe"],
|
||||||
|
hasRecalibrationPart: true,
|
||||||
|
selectedMultiGlass: false,
|
||||||
|
selectedWindshieldGlass: true,
|
||||||
|
selectedBackGlass: false,
|
||||||
|
selectedDriverSideGlass: false,
|
||||||
|
selectedPassengerSideGlass: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Select multiglass > return correct experimentOrder values", () => {
|
||||||
|
// Arrange
|
||||||
|
const storeState = state;
|
||||||
|
const mockStateValues = {
|
||||||
|
vehicleYear: 1000,
|
||||||
|
vehicleMake: "CarMake",
|
||||||
|
vehicleModel: "CarModel",
|
||||||
|
vehicleStyle: "SuperCoolStyle",
|
||||||
|
isRepair: false,
|
||||||
|
numberOfChips: 0,
|
||||||
|
carId: "Gibberish",
|
||||||
|
serviceCity: "Columbus",
|
||||||
|
serviceState: "OH-IO",
|
||||||
|
serviceZipCode: 43215,
|
||||||
|
parentAccountNumber: "999999",
|
||||||
|
isCoverageVerified: false,
|
||||||
|
glassParts: [
|
||||||
|
{
|
||||||
|
partNumber: "BACKGLASS_PN",
|
||||||
|
description: "This is a back glass",
|
||||||
|
recalibrationType: null,
|
||||||
|
requiresRecalibration: false,
|
||||||
|
requiresCapabilityQuestions: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
partNumber: "DRIVERGLASS_PN",
|
||||||
|
description: "This is a driver side glass",
|
||||||
|
recalibrationType: null,
|
||||||
|
requiresRecalibration: false,
|
||||||
|
requiresCapabilityQuestions: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
partNumber: "PASSENGERGLASS_PN",
|
||||||
|
description: "This is a passenger side glass",
|
||||||
|
recalibrationType: null,
|
||||||
|
requiresRecalibration: false,
|
||||||
|
requiresCapabilityQuestions: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
otherParts: [
|
||||||
|
|
||||||
|
],
|
||||||
|
glassToReplace: [
|
||||||
|
{
|
||||||
|
glassLocation: "Rear",
|
||||||
|
glassName: "Stationary"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Driver",
|
||||||
|
glassName: "Front"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Passenger",
|
||||||
|
glassName: "Front"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
glassLocation: "Passenger",
|
||||||
|
glassName: "Quarter"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
//Act
|
||||||
|
mutations.updateYear(storeState, mockStateValues.vehicleYear);
|
||||||
|
mutations.updateMake(storeState, mockStateValues.vehicleMake);
|
||||||
|
mutations.updateModel(storeState, mockStateValues.vehicleModel);
|
||||||
|
mutations.updateStyle(storeState, mockStateValues.vehicleStyle);
|
||||||
|
mutations.updateIsRepair(storeState, mockStateValues.isRepair);
|
||||||
|
mutations.updateNumberOfChips(storeState, mockStateValues.numberOfChips);
|
||||||
|
mutations.updateCarId(storeState, mockStateValues.carId);
|
||||||
|
mutations.updateServiceLocation(storeState, {
|
||||||
|
city: mockStateValues.serviceCity,
|
||||||
|
state: mockStateValues.serviceState,
|
||||||
|
zipCode: mockStateValues.serviceZipCode
|
||||||
|
});
|
||||||
|
mutations.updateParentAcctNumber(storeState, mockStateValues.parentAccountNumber);
|
||||||
|
mutations.updateInsuranceVerifiedStatus(storeState, mockStateValues.isCoverageVerified);
|
||||||
|
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
||||||
|
mutations.updateOtherParts(storeState, mockStateValues.otherParts);
|
||||||
|
mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace);
|
||||||
|
|
||||||
|
//Assert
|
||||||
|
expect(getters.experimentOrder(storeState)).toEqual({
|
||||||
|
vehicleYear: mockStateValues.vehicleYear,
|
||||||
|
vehicleMake: mockStateValues.vehicleMake,
|
||||||
|
vehicleModel: mockStateValues.vehicleModel,
|
||||||
|
vehicleStyle: mockStateValues.vehicleStyle,
|
||||||
|
isRepair: mockStateValues.isRepair,
|
||||||
|
numberOfChips: mockStateValues.numberOfChips,
|
||||||
|
carId: mockStateValues.carId,
|
||||||
|
serviceCity: mockStateValues.serviceCity,
|
||||||
|
serviceState: mockStateValues.serviceState,
|
||||||
|
serviceZipCode: mockStateValues.serviceZipCode,
|
||||||
|
parentAccountNumber: mockStateValues.parentAccountNumber,
|
||||||
|
isCoverageVerified: mockStateValues.isCoverageVerified,
|
||||||
|
orderPartNumbers: ["BACKGLASS_PN", "DRIVERGLASS_PN", "PASSENGERGLASS_PN"],
|
||||||
|
orderPartTypes: [],
|
||||||
|
hasRecalibrationPart: false,
|
||||||
|
selectedMultiGlass: true,
|
||||||
|
selectedWindshieldGlass: false,
|
||||||
|
selectedBackGlass: true,
|
||||||
|
selectedDriverSideGlass: true,
|
||||||
|
selectedPassengerSideGlass: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
});
|
});
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
//Blue gradient background mixin
|
//Blue gradient background mixin
|
||||||
@mixin blue-gradient {
|
@mixin blue-gradient {
|
||||||
background: linear-gradient(270deg, $blue-500 0%, $blue-700 100%);
|
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
<div
|
<div
|
||||||
class="alert fade show text-center mb-0 py-2 px-4"
|
class="alert fade show text-center mb-0 py-2 px-4"
|
||||||
role="alert"
|
role="alert"
|
||||||
:class="[isDismissible ? 'alert-dismissible' : '', this.alertClass]"
|
:class="[isDismissible ? 'alert-dismissible' : '', this.alertClass, this.cssClassNameForCmsWidget]"
|
||||||
>
|
>
|
||||||
<p class="m-0 fw-bold alert-heading">{{ alertHeadline }}</p>
|
<p class="m-0 fw-bold alert-heading">{{ alertHeadline }}</p>
|
||||||
<template v-for="paragraph in splitAlertCopyForParagraphTag" :key="paragraph">
|
<template v-for="paragraph in splitAlertCopyForParagraphTag" :key="paragraph">
|
||||||
|
|
@ -54,7 +54,15 @@ export default {
|
||||||
alert-info (blue)
|
alert-info (blue)
|
||||||
*/
|
*/
|
||||||
alertClass: String,
|
alertClass: String,
|
||||||
cmsWidgetName: String,
|
cmsWidgetName: {
|
||||||
|
type: String,
|
||||||
|
default(rawProps) {
|
||||||
|
if (!rawProps.cmsWidgetName) {
|
||||||
|
console.log('Error: Missing a CMS Widget Name (required field)');
|
||||||
|
}
|
||||||
|
return 'widgetUndefined';
|
||||||
|
},
|
||||||
|
},
|
||||||
manualHeadline: String,
|
manualHeadline: String,
|
||||||
manualCopy: String,
|
manualCopy: String,
|
||||||
shouldScrollToOnMount: {
|
shouldScrollToOnMount: {
|
||||||
|
|
@ -64,10 +72,10 @@ export default {
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
alertHeadline(){
|
alertHeadline(){
|
||||||
return this.cmsWidgetName ? this.getCmsContent(this.cmsWidgetName, 'HeadlineText') : this.manualHeadline;
|
return this.manualHeadline ? this.manualHeadline : this.getCmsContent(this.cmsWidgetName, 'HeadlineText');
|
||||||
},
|
},
|
||||||
alertCopy(){
|
alertCopy(){
|
||||||
return this.cmsWidgetName ? this.getCmsContent(this.cmsWidgetName, 'BodyText') : this.manualCopy;
|
return this.manualCopy ? this.manualCopy : this.getCmsContent(this.cmsWidgetName, 'BodyText');
|
||||||
},
|
},
|
||||||
splitAlertCopyForParagraphTag(){
|
splitAlertCopyForParagraphTag(){
|
||||||
return splitCMSCopyOnParagraphTag(this.alertCopy);
|
return splitCMSCopyOnParagraphTag(this.alertCopy);
|
||||||
|
|
|
||||||
|
|
@ -54,14 +54,14 @@ export default {
|
||||||
.btn {
|
.btn {
|
||||||
&.btn-primary {
|
&.btn-primary {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: linear-gradient(270deg, $blue-500 0%, $blue-700 100%);
|
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: $border-radius-lg;
|
border-radius: $border-radius-lg;
|
||||||
color: $white;
|
color: $white;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
background: linear-gradient(270deg, $blue-500 0%, $blue-700 100%);
|
background: linear-gradient(270deg, $blue 0%, $blue-800 100%);
|
||||||
}
|
}
|
||||||
&:focus {
|
&:focus {
|
||||||
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
|
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
|
||||||
|
|
|
||||||
|
|
@ -1,54 +1,24 @@
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div class="list-group list-button-horizontal d-flex flex-column w-100"
|
||||||
class="list-group list-button-horizontal d-flex flex-column w-100"
|
:class="[(errors.length > 0 || hasError) ? 'has-error' : '', isCashOrInsurance ? 'radio-fancy' : '']"
|
||||||
:class="[(errors.length > 0 || hasError) ? 'has-error' : '' , isCashOrInsurance ? 'radio-fancy' : '']"
|
@keyup.space="triggerButton()" @keyup.up="handleKeyupArrow()" @keyup.down="handleKeyupArrow()"
|
||||||
@keyup.space="triggerButton()"
|
@keyup.left="handleKeyupArrow()" @keyup.right="handleKeyupArrow()">
|
||||||
@keyup.up="handleKeyupArrow()"
|
<input :type="isMultiSelect ? 'checkbox' : 'radio'" :id="buttonID" :name="groupName" :value="value"
|
||||||
@keyup.down="handleKeyupArrow()"
|
:aria-required="isRequired" v-model="checkValue" @change="handleInputChange()" />
|
||||||
@keyup.left="handleKeyupArrow()"
|
<label tabindex="-1" :for="buttonID" :aria-labelledby="buttonID"
|
||||||
@keyup.right="handleKeyupArrow()"
|
class="d-flex flex-column justify-content-center p-3" @mouseup="triggerButton()">
|
||||||
>
|
<span class="m-0" :class="textPosition">
|
||||||
<input
|
{{ buttonLabel }}
|
||||||
:type="isMultiSelect ? 'checkbox' : 'radio'"
|
</span>
|
||||||
:id="buttonID"
|
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="textPosition">
|
||||||
:name="groupName"
|
{{ buttonLabelSubCopy }}
|
||||||
:value="value"
|
</span>
|
||||||
:aria-required="isRequired"
|
<span v-if="screenReaderOnlyText" class="sr-only">
|
||||||
v-model="checkValue"
|
{{ screenReaderOnlyText }}
|
||||||
@change="handleInputChange()"
|
</span>
|
||||||
/>
|
<loader v-if="isLoaderDisplayed && selectingInitiatesLoad" :class="[loaderColor, loaderPosition]" />
|
||||||
<label
|
</label>
|
||||||
tabindex="-1"
|
</div>
|
||||||
:for="buttonID"
|
|
||||||
:aria-labelledby="buttonID"
|
|
||||||
class="d-flex flex-column justify-content-center p-3"
|
|
||||||
@mouseup="triggerButton()"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
class="m-0"
|
|
||||||
:class="textPosition"
|
|
||||||
>
|
|
||||||
{{buttonLabel}}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="buttonLabelSubCopy"
|
|
||||||
class="m-0 small"
|
|
||||||
:class="textPosition"
|
|
||||||
>
|
|
||||||
{{ buttonLabelSubCopy }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
v-if="screenReaderOnlyText"
|
|
||||||
class="sr-only"
|
|
||||||
>
|
|
||||||
{{ screenReaderOnlyText }}
|
|
||||||
</span>
|
|
||||||
<loader
|
|
||||||
v-if="isLoaderDisplayed && selectingInitiatesLoad"
|
|
||||||
:class="[loaderColor, loaderPosition]"
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
@ -79,10 +49,7 @@ export default {
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
selectedValues: [Array, String],
|
selectedValues: [Array, String],
|
||||||
hasError: Boolean,
|
hasError: Boolean,
|
||||||
clearOnUnmount: {
|
valueToLogType: String,
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -93,14 +60,8 @@ export default {
|
||||||
created() {
|
created() {
|
||||||
if (Array.isArray(this.selectedValues)) {
|
if (Array.isArray(this.selectedValues)) {
|
||||||
this.checkValue = this.isMultiSelect
|
this.checkValue = this.isMultiSelect
|
||||||
? this.selectedValues.includes(this.value)
|
? this.selectedValues.includes(this.value)
|
||||||
: this.selectedValues[0];
|
: this.selectedValues[0];
|
||||||
}
|
|
||||||
},
|
|
||||||
unmounted() { // needed to clear this button's selectedValues if it is removed to keep validation in sync
|
|
||||||
if (this.clearOnUnmount) {
|
|
||||||
this.checkValue = false;
|
|
||||||
this.handleCheckChange();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
@ -108,7 +69,7 @@ export default {
|
||||||
this.isLoaderDisplayed = true;
|
this.isLoaderDisplayed = true;
|
||||||
},
|
},
|
||||||
handleInputChange() {
|
handleInputChange() {
|
||||||
if(!this.selectingInitiatesLoad) {
|
if (!this.selectingInitiatesLoad) {
|
||||||
this.handleCheckChange();
|
this.handleCheckChange();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -117,17 +78,17 @@ export default {
|
||||||
return; // Prevent arrow keys from doing anything if element is a checkbox
|
return; // Prevent arrow keys from doing anything if element is a checkbox
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!this.selectingInitiatesLoad) {
|
if (!this.selectingInitiatesLoad) {
|
||||||
this.handleCheckChange();
|
this.handleCheckChange();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
triggerButton() {
|
triggerButton() {
|
||||||
if(this.selectingInitiatesLoad) {
|
if (this.selectingInitiatesLoad) {
|
||||||
this.displayLoader();
|
this.displayLoader();
|
||||||
this.handleCheckChange();
|
this.handleCheckChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true);
|
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true, this.valueToLogType);
|
||||||
},
|
},
|
||||||
handleCheckChange() {
|
handleCheckChange() {
|
||||||
const emitEvent = {
|
const emitEvent = {
|
||||||
|
|
@ -175,6 +136,7 @@ export default {
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.list-button-horizontal {
|
.list-button-horizontal {
|
||||||
|
|
||||||
input[type="radio"],
|
input[type="radio"],
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
@ -182,27 +144,32 @@ export default {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
|
|
||||||
&:focus-visible + label {
|
&:focus-visible+label {
|
||||||
box-shadow: 0 0 0 2.5px $blue;
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
&:focus + label {
|
|
||||||
|
&:focus+label {
|
||||||
box-shadow: 0 0 0 2.5px $blue;
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
&:checked + label {
|
|
||||||
|
&:checked+label {
|
||||||
background: $blue-100;
|
background: $blue-100;
|
||||||
box-shadow: 0 0 0 1px $blue;
|
box-shadow: 0 0 0 1px $blue;
|
||||||
outline: none;
|
outline: none;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
&:checked:focus + label {
|
|
||||||
|
&:checked:focus+label {
|
||||||
box-shadow: 0 0 0 2.5px $blue;
|
box-shadow: 0 0 0 2.5px $blue;
|
||||||
}
|
}
|
||||||
&:checked + label p:first-child {
|
|
||||||
|
&:checked+label p:first-child {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
outline: none;
|
outline: none;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
@ -212,6 +179,7 @@ export default {
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
color: $gray-600;
|
color: $gray-600;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
@include media-breakpoint-up(sm) {
|
@include media-breakpoint-up(sm) {
|
||||||
box-shadow: 0 0 0 4px $blue-300;
|
box-shadow: 0 0 0 4px $blue-300;
|
||||||
|
|
@ -219,9 +187,11 @@ export default {
|
||||||
z-index: 4 !important;
|
z-index: 4 !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+ p {
|
|
||||||
|
+p {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
font-size: .875rem;
|
font-size: .875rem;
|
||||||
}
|
}
|
||||||
|
|
@ -233,16 +203,19 @@ export default {
|
||||||
border: 1px solid $blue-700;
|
border: 1px solid $blue-700;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
color: $blue;
|
color: $blue;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label:hover {
|
label:hover {
|
||||||
background-color: $blue-700;
|
background-color: $blue-700;
|
||||||
color: $white;
|
color: $white;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="radio"],
|
input[type="radio"],
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"] {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
@ -250,14 +223,16 @@ export default {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
width: 0;
|
width: 0;
|
||||||
|
|
||||||
&:focus-visible + label {
|
&:focus-visible+label {
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
&:focus + label {
|
|
||||||
|
&:focus+label {
|
||||||
z-index: 3;
|
z-index: 3;
|
||||||
}
|
}
|
||||||
&:checked + label {
|
|
||||||
|
&:checked+label {
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
color: $white;
|
color: $white;
|
||||||
|
|
@ -265,10 +240,12 @@ export default {
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
&:checked:focus + label {
|
|
||||||
|
&:checked:focus+label {
|
||||||
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
|
box-shadow: 0 0 0 3px, 0 0 0 5.5px $blue-700;
|
||||||
}
|
}
|
||||||
&:checked + label p:first-child {
|
|
||||||
|
&:checked+label p:first-child {
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -298,11 +275,12 @@ export default {
|
||||||
&:first-of-type {
|
&:first-of-type {
|
||||||
.list-button-horizontal.radio-fancy {
|
.list-button-horizontal.radio-fancy {
|
||||||
input[type="radio"] {
|
input[type="radio"] {
|
||||||
&:checked + label {
|
&:checked+label {
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
border-top-right-radius: 0;
|
border-top-right-radius: 0;
|
||||||
}
|
}
|
||||||
&:checked:focus + label {
|
|
||||||
|
&:checked:focus+label {
|
||||||
border-bottom-right-radius: 0.5rem;
|
border-bottom-right-radius: 0.5rem;
|
||||||
border-top-right-radius: 0.5rem;
|
border-top-right-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
@ -313,11 +291,12 @@ export default {
|
||||||
&:last-of-type {
|
&:last-of-type {
|
||||||
.list-button-horizontal.radio-fancy {
|
.list-button-horizontal.radio-fancy {
|
||||||
input[type="radio"] {
|
input[type="radio"] {
|
||||||
&:checked + label {
|
&:checked+label {
|
||||||
border-bottom-left-radius: 0;
|
border-bottom-left-radius: 0;
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
}
|
}
|
||||||
&:checked:focus + label {
|
|
||||||
|
&:checked:focus+label {
|
||||||
border-bottom-left-radius: 0.5rem;
|
border-bottom-left-radius: 0.5rem;
|
||||||
border-top-left-radius: 0.5rem;
|
border-top-left-radius: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ describe("list-button.vue", () => {
|
||||||
wrapper.vm.handleCheckChange();
|
wrapper.vm.handleCheckChange();
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: Boolean, buttonId: 'list-card-id'}]);
|
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: false, buttonId: 'list-card-id'}]);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -204,11 +204,11 @@ describe("list-button.vue", () => {
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
isWide: false,
|
isWide: false,
|
||||||
modelValue: ["List Card Checkbox"],
|
modelValue: ["List Card Checkbox"],
|
||||||
selectedValues: ["Car-Front"]
|
selectedValues: "Car-Front"
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.componentVM.checkValue).toEqual("Car-Front");
|
expect(wrapper.componentVM.checkValue).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleInputChange is triggered", async () => {
|
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleInputChange is triggered", async () => {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
class="list-group list-button rounded-3 d-flex flex-column w-100 mb-2"
|
class="list-group list-button rounded-3 d-flex flex-column w-100 mb-2"
|
||||||
:class="[(errors.length > 0 || hasError) ? 'has-error' : '']"
|
:class="[(errors.length > 0 || hasError) ? 'has-error' : '']"
|
||||||
@keyup.space="triggerButton"
|
@keyup.space="triggerButton"
|
||||||
|
@keyup.enter="triggerButton"
|
||||||
@keyup.up="handleKeyupArrow"
|
@keyup.up="handleKeyupArrow"
|
||||||
@keyup.down="handleKeyupArrow"
|
@keyup.down="handleKeyupArrow"
|
||||||
@keyup.left="handleKeyupArrow"
|
@keyup.left="handleKeyupArrow"
|
||||||
|
|
@ -14,12 +15,14 @@
|
||||||
:value="value"
|
:value="value"
|
||||||
:aria-required="isRequired"
|
:aria-required="isRequired"
|
||||||
v-model="checkValue"
|
v-model="checkValue"
|
||||||
|
:checked="checkValue"
|
||||||
@change="handleInputChange"
|
@change="handleInputChange"
|
||||||
|
:aria-label="value"
|
||||||
>
|
>
|
||||||
<label
|
<label
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
:for="buttonID"
|
:for="buttonID"
|
||||||
:aria-labelledby="buttonID"
|
:aria-label="buttonLabel"
|
||||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||||
@mouseup="triggerButton"
|
@mouseup="triggerButton"
|
||||||
>
|
>
|
||||||
|
|
@ -52,6 +55,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { useField } from "vee-validate";
|
import { useField } from "vee-validate";
|
||||||
|
import { toRef } from "vue";
|
||||||
import loader from "@/ux-components/loader/loader";
|
import loader from "@/ux-components/loader/loader";
|
||||||
import { queryStrings } from "@/constants/query-strings";
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
|
|
||||||
|
|
@ -73,35 +77,38 @@ export default {
|
||||||
// Field initial value
|
// Field initial value
|
||||||
type: [String, Number],
|
type: [String, Number],
|
||||||
default: "",
|
default: "",
|
||||||
},
|
},
|
||||||
|
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
selectedValues: [Array, String],
|
selectedValues: [Array, String],
|
||||||
hasError: Boolean,
|
hasError: Boolean,
|
||||||
clearOnUnmount: {
|
valueToLogType: String,
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
isLoaderDisplayed: false,
|
isLoaderDisplayed: false,
|
||||||
checkValue: Boolean,
|
checkValue: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
created() {
|
mounted() {
|
||||||
if (Array.isArray(this.selectedValues)) {
|
if (Array.isArray(this.validateValue)) {
|
||||||
this.checkValue = this.isMultiSelect
|
this.checkValue = this.isValueSelectedByArray(this.selectedValues);
|
||||||
? this.selectedValues.includes(this.value)
|
const isSelectedByValidator = this.isValueSelectedByArray(this.validateValue);
|
||||||
: this.selectedValues[0];
|
|
||||||
|
if (this.checkValue != isSelectedByValidator) {
|
||||||
|
this.handleChange(this.value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
else {
|
||||||
unmounted() { // needed to clear this button's selectedValues if it is removed to keep validation in sync
|
this.checkValue = this.selectedValues == this.value;
|
||||||
if (this.clearOnUnmount) {
|
|
||||||
this.checkValue = false;
|
|
||||||
this.handleCheckChange();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
isValueSelectedByArray(arr) {
|
||||||
|
return this.isMultiSelect
|
||||||
|
? arr.includes(this.value)
|
||||||
|
: arr[0];
|
||||||
|
},
|
||||||
displayLoader() {
|
displayLoader() {
|
||||||
this.isLoaderDisplayed = true;
|
this.isLoaderDisplayed = true;
|
||||||
},
|
},
|
||||||
|
|
@ -120,7 +127,7 @@ export default {
|
||||||
this.displayLoader();
|
this.displayLoader();
|
||||||
this.handleCheckChange();
|
this.handleCheckChange();
|
||||||
}
|
}
|
||||||
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true);
|
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true, this.valueToLogType);
|
||||||
},
|
},
|
||||||
handleCheckChange() {
|
handleCheckChange() {
|
||||||
const emitEvent = {
|
const emitEvent = {
|
||||||
|
|
@ -154,11 +161,14 @@ export default {
|
||||||
const {
|
const {
|
||||||
handleChange,
|
handleChange,
|
||||||
errors,
|
errors,
|
||||||
} = useField(props.groupName, props.validationRules, fieldOptions);
|
value
|
||||||
|
} = useField(toRef(props, "groupName"), toRef(props, "validationRules"), fieldOptions);
|
||||||
|
|
||||||
|
const validateValue = value;
|
||||||
return {
|
return {
|
||||||
handleChange,
|
handleChange,
|
||||||
errors,
|
errors,
|
||||||
|
validateValue,
|
||||||
fieldOptions, // only need to expose this for unit test purposes
|
fieldOptions, // only need to expose this for unit test purposes
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ describe("list-card.vue", () => {
|
||||||
groupID: "checkbox-demo-1",
|
groupID: "checkbox-demo-1",
|
||||||
groupName: "Checkbox 1",
|
groupName: "Checkbox 1",
|
||||||
buttonImage: "windshield-damage.svg",
|
buttonImage: "windshield-damage.svg",
|
||||||
modelValue: ["List Card Checkbox"],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -33,7 +32,6 @@ describe("list-card.vue", () => {
|
||||||
groupID: "radio-demo-1",
|
groupID: "radio-demo-1",
|
||||||
groupName: "radio 1",
|
groupName: "radio 1",
|
||||||
buttonImage: "windshield-damage.svg",
|
buttonImage: "windshield-damage.svg",
|
||||||
modelValue: ["List Card Checkbox"],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -53,7 +51,6 @@ describe("list-card.vue", () => {
|
||||||
groupName: "radio 1",
|
groupName: "radio 1",
|
||||||
buttonImage: "windshield-damage.svg",
|
buttonImage: "windshield-damage.svg",
|
||||||
buttonLabelSubCopy: "Test",
|
buttonLabelSubCopy: "Test",
|
||||||
modelValue: ["List Card Checkbox"],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -73,7 +70,6 @@ describe("list-card.vue", () => {
|
||||||
groupName: "radio 1",
|
groupName: "radio 1",
|
||||||
buttonImage: "windshield-damage.svg",
|
buttonImage: "windshield-damage.svg",
|
||||||
buttonLabelSubCopy: "Test",
|
buttonLabelSubCopy: "Test",
|
||||||
modelValue: ["List Card Checkbox"],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -93,7 +89,6 @@ describe("list-card.vue", () => {
|
||||||
groupName: "radio 1",
|
groupName: "radio 1",
|
||||||
buttonImage: "windshield-damage.svg",
|
buttonImage: "windshield-damage.svg",
|
||||||
buttonLabelSubCopy: "Test",
|
buttonLabelSubCopy: "Test",
|
||||||
modelValue: ["List Card Checkbox"],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -113,7 +108,6 @@ describe("list-card.vue", () => {
|
||||||
groupName: "radio 1",
|
groupName: "radio 1",
|
||||||
buttonImage: "windshield-damage.svg",
|
buttonImage: "windshield-damage.svg",
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
modelValue: ["List Card Checkbox"],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -135,7 +129,6 @@ describe("list-card.vue", () => {
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
isWide: true,
|
isWide: true,
|
||||||
buttonLabelSubCopy: "",
|
buttonLabelSubCopy: "",
|
||||||
modelValue: ["List Card Checkbox"],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -157,7 +150,6 @@ describe("list-card.vue", () => {
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
isWide: true,
|
isWide: true,
|
||||||
buttonLabelSubCopy: "Button Subcopy",
|
buttonLabelSubCopy: "Button Subcopy",
|
||||||
modelValue: ["List Card Checkbox"],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -178,7 +170,6 @@ describe("list-card.vue", () => {
|
||||||
buttonImage: "windshield-damage.svg",
|
buttonImage: "windshield-damage.svg",
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
isWide: false,
|
isWide: false,
|
||||||
modelValue: ["List Card Checkbox"],
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -199,13 +190,13 @@ describe("list-card.vue", () => {
|
||||||
buttonImage: "windshield-damage.svg",
|
buttonImage: "windshield-damage.svg",
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
isWide: false,
|
isWide: false,
|
||||||
modelValue: ["List Card Checkbox"],
|
buttonID: 'list-card-id',
|
||||||
buttonID: 'list-card-id'
|
selectedValues: "List Card Checkbox"
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
wrapper.vm.handleCheckChange();
|
wrapper.vm.handleCheckChange();
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: "List Card Checkbox", buttonId: 'list-card-id'}]);
|
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{value: "List Card Checkbox", checkValue: true, buttonId: 'list-card-id'}]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
||||||
|
|
@ -220,12 +211,11 @@ describe("list-card.vue", () => {
|
||||||
buttonImage: "windshield-damage.svg",
|
buttonImage: "windshield-damage.svg",
|
||||||
isRequired: true,
|
isRequired: true,
|
||||||
isWide: false,
|
isWide: false,
|
||||||
modelValue: ["List Card Checkbox"],
|
selectedValues: "Car-Front"
|
||||||
selectedValues: ["Car-Front"]
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.componentVM.checkValue).toEqual("Car-Front");
|
expect(wrapper.componentVM.checkValue).toEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should set an initial value for validation if selectedValues include the value", async () => {
|
it("Should set an initial value for validation if selectedValues include the value", async () => {
|
||||||
|
|
@ -234,7 +224,6 @@ describe("list-card.vue", () => {
|
||||||
propsData: {
|
propsData: {
|
||||||
value: "Windshield",
|
value: "Windshield",
|
||||||
groupName: "radio 1",
|
groupName: "radio 1",
|
||||||
modelValue: ["Windshield"],
|
|
||||||
selectedValues: ["Windshield"],
|
selectedValues: ["Windshield"],
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,11 @@
|
||||||
isWide ? 'horizontal' : '',
|
isWide ? 'horizontal' : '',
|
||||||
(errors.length > 0 || hasError) ? 'has-error' : '',
|
(errors.length > 0 || hasError) ? 'has-error' : '',
|
||||||
]"
|
]"
|
||||||
@keyup.space="triggerButton()"
|
@keyup.space="triggerButton"
|
||||||
@keyup.up="handleKeyupArrow()"
|
@keyup.up="handleKeyupArrow"
|
||||||
@keyup.down="handleKeyupArrow()"
|
@keyup.down="handleKeyupArrow"
|
||||||
@keyup.left="handleKeyupArrow()"
|
@keyup.left="handleKeyupArrow"
|
||||||
@keyup.right="handleKeyupArrow()"
|
@keyup.right="handleKeyupArrow"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
:type="isMultiSelect ? 'checkbox' : 'radio'"
|
:type="isMultiSelect ? 'checkbox' : 'radio'"
|
||||||
|
|
@ -26,7 +26,7 @@
|
||||||
<label
|
<label
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
:for="buttonID"
|
:for="buttonID"
|
||||||
:aria-labelledby="buttonID"
|
:aria-label="buttonLabel"
|
||||||
class="d-flex w-100 align-items-center px-2 h-100"
|
class="d-flex w-100 align-items-center px-2 h-100"
|
||||||
:class="getLabelClasses"
|
:class="getLabelClasses"
|
||||||
@mouseup="triggerButton"
|
@mouseup="triggerButton"
|
||||||
|
|
@ -87,37 +87,25 @@ export default {
|
||||||
colLength: String,
|
colLength: String,
|
||||||
validationRules: String,
|
validationRules: String,
|
||||||
selectedValues: [Array, String],
|
selectedValues: [Array, String],
|
||||||
modelValue: Object,
|
|
||||||
hasError: Boolean,
|
hasError: Boolean,
|
||||||
clearOnUnmount: {
|
valueToLogType: String,
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
checkValue: null,
|
checkValue: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
created() {
|
mounted() {
|
||||||
if (Array.isArray(this.selectedValues)) {
|
if (Array.isArray(this.validateValue)) {
|
||||||
this.checkValue = this.isMultiSelect
|
this.checkValue = this.isValueSelectedByArray(this.selectedValues);
|
||||||
? this.selectedValues.includes(this.value)
|
const isSelectedByValidator = this.isValueSelectedByArray(this.validateValue);
|
||||||
: this.selectedValues[0];
|
|
||||||
}
|
if (this.checkValue != isSelectedByValidator) {
|
||||||
else if (Array.isArray(this.modelValue)) {
|
this.handleChange(this.value);
|
||||||
this.checkValue = this.isMultiSelect
|
}
|
||||||
? this.modelValue.includes(this.value)
|
|
||||||
: this.modelValue[0];
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this.checkValue = this.selectedValues == this.value || this.modelValue == this.value;
|
this.checkValue = this.selectedValues == this.value;
|
||||||
}
|
|
||||||
},
|
|
||||||
unmounted() { // needed to clear this button's selectedValues if it is removed to keep validation in sync
|
|
||||||
if (this.clearOnUnmount) {
|
|
||||||
this.checkValue = false;
|
|
||||||
this.handleCheckChange();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
|
@ -134,10 +122,15 @@ export default {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
isValueSelectedByArray(arr) {
|
||||||
|
return this.isMultiSelect
|
||||||
|
? arr.includes(this.value)
|
||||||
|
: arr[0];
|
||||||
|
},
|
||||||
handleInputChange() {
|
handleInputChange() {
|
||||||
if(!this.selectingInitiatesLoad) {
|
if(!this.selectingInitiatesLoad) {
|
||||||
this.handleCheckChange();
|
this.handleCheckChange();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleKeyupArrow() {
|
handleKeyupArrow() {
|
||||||
if (this.isMultiSelect) {
|
if (this.isMultiSelect) {
|
||||||
|
|
@ -154,7 +147,7 @@ export default {
|
||||||
this.handleCheckChange();
|
this.handleCheckChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true);
|
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true, this.valueToLogType);
|
||||||
},
|
},
|
||||||
handleCheckChange() {
|
handleCheckChange() {
|
||||||
const emitEvent = {
|
const emitEvent = {
|
||||||
|
|
@ -165,20 +158,13 @@ export default {
|
||||||
|
|
||||||
this.handleChange(this.value);
|
this.handleChange(this.value);
|
||||||
this.$emit("isCheckedChanged", emitEvent);
|
this.$emit("isCheckedChanged", emitEvent);
|
||||||
this.$emit("update:modelValue", emitEvent);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
// Changing this will impact pre-selection data loads on vehicle-parts.
|
// Changing this will impact pre-selection data loads on vehicle-parts.
|
||||||
// If changed, please regression test that vehicle-parts data still loads correctly with previous selections.
|
// If changed, please regression test that vehicle-parts data still loads correctly with previous selections.
|
||||||
modelValue(newVal) {
|
|
||||||
if (newVal !== undefined) {
|
|
||||||
this.checkValue = newVal.value;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selectedValues(newVal) {
|
selectedValues(newVal) {
|
||||||
if (typeof newVal === "string") {
|
if (typeof newVal === "string") {
|
||||||
this.handleChange(newVal);
|
|
||||||
this.checkValue = newVal == this.value;
|
this.checkValue = newVal == this.value;
|
||||||
}
|
}
|
||||||
else if (newVal !== undefined) {
|
else if (newVal !== undefined) {
|
||||||
|
|
@ -204,11 +190,16 @@ export default {
|
||||||
const {
|
const {
|
||||||
handleChange,
|
handleChange,
|
||||||
errors,
|
errors,
|
||||||
|
value
|
||||||
} = useField(toRef(props, "groupName"), toRef(props, "validationRules"), fieldOptions);
|
} = useField(toRef(props, "groupName"), toRef(props, "validationRules"), fieldOptions);
|
||||||
|
|
||||||
|
// First land on the blank, unselected page, no handleChange
|
||||||
|
// Land on page with initial values, handleChange
|
||||||
|
const validateValue = value;
|
||||||
return {
|
return {
|
||||||
handleChange,
|
handleChange,
|
||||||
errors,
|
errors,
|
||||||
|
validateValue,
|
||||||
fieldOptions, // only need to expose this for unit test purposes
|
fieldOptions, // only need to expose this for unit test purposes
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { shallowMount } from "@vue/test-utils";
|
import { shallowMount } from "@vue/test-utils";
|
||||||
import radio from "./radio";
|
import radio from "./radio";
|
||||||
import { nextTick } from "vue";
|
import { nextTick } from "vue";
|
||||||
|
import { GaActions } from "@/constants/analytics";
|
||||||
|
|
||||||
describe("radio.vue", () => {
|
describe("radio.vue", () => {
|
||||||
it("Should return group name", async () => {
|
it("Should return group name", async () => {
|
||||||
|
|
@ -64,6 +65,13 @@ describe("radio.vue", () => {
|
||||||
it("Should emit button value on click", async () => {
|
it("Should emit button value on click", async () => {
|
||||||
// Act
|
// Act
|
||||||
const wrapper = shallowMount(radio, {
|
const wrapper = shallowMount(radio, {
|
||||||
|
global: {
|
||||||
|
mocks: {
|
||||||
|
'$route': { query: { fmgPage: 'page-name' } },
|
||||||
|
GaActions: GaActions,
|
||||||
|
pushEventToGA: jest.fn(),
|
||||||
|
}
|
||||||
|
},
|
||||||
propsData: {
|
propsData: {
|
||||||
buttonLabel: "Windshield",
|
buttonLabel: "Windshield",
|
||||||
value: "List Card Checkbox",
|
value: "List Card Checkbox",
|
||||||
|
|
@ -77,12 +85,20 @@ describe("radio.vue", () => {
|
||||||
});
|
});
|
||||||
wrapper.vm.handleCheckChange();
|
wrapper.vm.handleCheckChange();
|
||||||
// Assert
|
// Assert
|
||||||
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{"buttonID": "List Card Checkbox", value: "List Card Checkbox", checkValue: false}]);
|
expect(wrapper.emitted()["isCheckedChanged"][0]).toEqual([{"buttonID": "List Card Checkbox", value: "List Card Checkbox", checkValue: false}]);;
|
||||||
|
expect(wrapper.vm.pushEventToGA).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
it("Should set checkValue data if selectedButtonIDs has value(s)", async () => {
|
||||||
// Act
|
// Act
|
||||||
const wrapper = shallowMount(radio, {
|
const wrapper = shallowMount(radio, {
|
||||||
|
global: {
|
||||||
|
mocks: {
|
||||||
|
'$route': { query: { fmgPage: 'page-name' } },
|
||||||
|
GaActions: GaActions,
|
||||||
|
pushEventToGA: jest.fn(),
|
||||||
|
}
|
||||||
|
},
|
||||||
propsData: {
|
propsData: {
|
||||||
buttonLabel: "Windshield",
|
buttonLabel: "Windshield",
|
||||||
buttonID: "List Card Checkbox",
|
buttonID: "List Card Checkbox",
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { useField } from "vee-validate";
|
import { useField } from "vee-validate";
|
||||||
|
import { queryStrings } from "@/constants/query-strings";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "radio",
|
name: "radio",
|
||||||
props: {
|
props: {
|
||||||
|
|
@ -39,7 +41,8 @@ export default {
|
||||||
screenReaderOnlyText: String,
|
screenReaderOnlyText: String,
|
||||||
selectedValues: String,
|
selectedValues: String,
|
||||||
hasError: Boolean,
|
hasError: Boolean,
|
||||||
validationRules: String
|
validationRules: String,
|
||||||
|
valueToLogType: String,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|
@ -65,6 +68,8 @@ export default {
|
||||||
|
|
||||||
this.$emit("isCheckedChanged", emitEvent);
|
this.$emit("isCheckedChanged", emitEvent);
|
||||||
this.$emit("update:modelValue", emitEvent);
|
this.$emit("update:modelValue", emitEvent);
|
||||||
|
|
||||||
|
this.pushEventToGA(this.$route.query[queryStrings.FMG_PAGE], this.GaActions.CLICKED, this.value.toString(), true, this.valueToLogType);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue