CSR-803: bulk of refactoring

This commit is contained in:
Adam Caouette 2022-11-05 16:13:50 -04:00
parent 4324f0ee5f
commit a1c20e41a2
8 changed files with 1165 additions and 452 deletions

View file

@ -31,8 +31,9 @@ export default {
props: { props: {
questionData: Object, questionData: Object,
validationRules: String, validationRules: String,
modelValue: Array, modelValue: Object,
glassIndex: Number, glassIndex: Number,
answerKey: String,
}, },
async created() { async created() {
// do a test validation check upon create to prevent out of sync / incorrect valid states // do a test validation check upon create to prevent out of sync / incorrect valid states
@ -65,7 +66,7 @@ export default {
}), }),
answerSelected: q.answerSelected || "", answerSelected: q.answerSelected || "",
}; };
if (!q.suppressQuestion) { if (!q.suppressThisQuestion) {
this.questions.push(question); this.questions.push(question);
} }
}); });
@ -81,7 +82,6 @@ export default {
}, },
methods: { methods: {
handleAnswer(question, returnedAnswer) { handleAnswer(question, returnedAnswer) {
question.answerSelected = returnedAnswer;
/* /*
returnedAnswer example format: returnedAnswer example format:
{ {
@ -90,6 +90,8 @@ export default {
"buttonId": "Driver-Front-1-1|answer|DD11132|Yes" "buttonId": "Driver-Front-1-1|answer|DD11132|Yes"
} }
*/ */
question.answerSelected = returnedAnswer;
const isQuestionChainComplete = this.getQuestionChainAnswerIfComplete(returnedAnswer); const isQuestionChainComplete = this.getQuestionChainAnswerIfComplete(returnedAnswer);
if (isQuestionChainComplete) { if (isQuestionChainComplete) {

View file

@ -0,0 +1,106 @@
<template>
<div :class="`page-container-grouped-styles questions-page`">
<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"
cmsWidgetName="alertWidget"
class="my-5"
alertClass="alert-warning"
:manualHeadline="alertFewMoreQuestionsHeader"
:manualCopy="alertFewMoreQuestionsCopy"
v-bind:isDismissible="false" />
<div v-for="(glass, i) in questionsData" :key="glass.key">
<questionChain
ref="questionChain"
v-model="qPselectedAnswers2[glass.answerKey]"
:questionData="glass.questions"
:glassIndex="i"
v-if="showThisQuestionChain(glass, i)"
:answerKey="glass.answerKey"
:validationRules="validationRules" />
</div>
<funnel-footer
ref="funnelFooter"
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!isMetaValid"
@back-clicked="backButtonAction"
@ForwardClicked="handleForwardButtonAction" />
</div>
</div>
</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";
export default {
name: "questions-page",
props: {
isMetaValid: Boolean,
alertFewMoreQuestionsHeader: String,
alertFewMoreQuestionsCopy: String,
questionsData: Array,
validationRules: String,
modelValue: Array,
currentGlassPieceIndex: Number,
},
computed: {
qPselectedAnswers2: {
get() {
return this.modelValue;
},
set(newValue) {
this.$emit("update:modelValue", newValue);
},
},
},
methods: {
showThisQuestionChain(glass, i) {
if (!glass.questions || glass.questions?.length < 1 || glass.isSuppressedPart) {
return false;
} // return false if no questions or if suppressed
return this.currentGlassPieceIndex === i || glass.answerData?.answerResult?.length > 0;
},
handleForwardButtonAction() {
this.$emit("forwardButtonAction");
},
handleBackButtonAction() {
this.$emit("backButtonAction");
},
showLoadingModal() {
this.$refs.loadingModal.showModal();
},
},
components: {
funnelHeader,
vehicleBanner,
alert,
questionChain,
funnelSubHeader,
funnelFooter,
loadingModal,
},
};
</script>
<style lang="scss">
.questions-page {
.question-text {
margin-bottom: 0.5rem;
span {
text-align: left;
}
}
}
</style>

View file

@ -7,7 +7,6 @@ import { GaCategories, GaActions, GaLabels } from "@/constants/analytics";
import { headerKeys } from "@/constants/header-keys"; import { headerKeys } from "@/constants/header-keys";
export default { export default {
<<<<<<< Updated upstream
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;
@ -15,21 +14,6 @@ export default {
const headers = { const headers = {
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings), [headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings),
}; };
=======
callHttpClient({ method, endpoint, payload, logApiCall = true }) {
return new Promise((resolve, reject) => {
let cfDistroUrl = applicationConfig.CONSUMER_CF_DISTRO;
if (endpoint.includes("/order/")) {
cfDistroUrl = "https://localhost:5001";
}
if (endpoint.includes("/parts/")) {
cfDistroUrl = "https://localhost:5003";
}
const payloadAndAnalyticsData = Object.assign({}, payload, { AppName: "FixMyGlass" });
const headers = {
[headerKeys.EXPERIMENT]: JSON.stringify(store.getters.experimentSettings)
}
>>>>>>> Stashed changes
axios({ axios({
method: method, method: method,

View file

@ -1,47 +1,28 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form
<div class="page-container-grouped-styles capability-questions"> @submit="onSubmit"
<loadingModal ref="loadingModal" /> @invalid-submit="onInvalidSubmit"
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> ref="theForm"
<vehicleBanner v-slot="{ meta }"
cmsWidgetName="VehicleBannerWidget" >
:displayGenericVehicleImage="false" /> <questions-page
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" /> ref="questionsPage"
<div class="fade-on-route-transition sub-container make-tall"> :isMetaValid="meta.valid"
<alert :alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
ref="alertFewMoreQuestions" :alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
class="my-5" :questionsData="questionsData"
alertClass="alert-warning" :validationRules="questions-required"
:manualHeadline="AlertFewMoreQuestionsHeader" v-model="selectedAnswers2"
:manualCopy="AlertFewMoreQuestionsCopy" @forwardButtonAction="forwardButtonAction"
v-bind:isDismissible="false" /> @backButtonAction="backButtonAction"
<div v-for="(glass, i) in questionsData" :key="glass.key"> :currentGlassPieceIndex="currentGlassPieceIndex"
<questionChain ref="questionChain" />
v-model="selectedAnswers[glass.glassLocation + '-' + glass.glassName]"
:questionData="glass.questions" :glassIndex="i"
v-if="showThisQuestionChain(glass, i)"
validationRules="questions-required" />
</div>
<funnel-footer
ref="funnelFooter"
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
</div>
</Form> </Form>
</template> </template>
<script> <script>
// Components // Components
import funnelHeader from "@/common-components/funnel-header/funnel-header"; import questionsPage from "@/common-components/questions-page/questions-page";
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 // Supporting Files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -80,9 +61,9 @@ export default {
}, },
data() { data() {
return { return {
questionsData: store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS).partsOrQuestions, questionsData: [],
selectedAnswers: {}, selectedAnswers2: {},
currentQuestionChainIndex: 0, currentGlassPieceIndex: 0,
}; };
}, },
computed: { computed: {
@ -100,14 +81,25 @@ export default {
// are there alreadyAnsweredQuestions? // are there alreadyAnsweredQuestions?
const alreadyAnsweredQuestions = store.getters.damage.capabilityQuestionAnswers; const alreadyAnsweredQuestions = store.getters.damage.capabilityQuestionAnswers;
this.questionsData = this.pageData.partsOrQuestions.filter(x => x.capabilityQuestions).map((glass, i) => { this.questionsData = this.pageData.partsOrQuestions.filter((x) => x.capabilityQuestions).map((glass, i) => {
// NOTE: questions for property "questions" can differ between layouts // NOTE: questions for property "questions" can differ between layouts
glass.questions = glass.capabilityQuestions; glass.questions = glass.capabilityQuestions;
glass.answerKey = glass.glassLocation + "-" + glass.glassName;
// reset selectedAnswers for this glass // reset selectedAnswers for this glass
this.selectedAnswers[glass.key] = []; this.selectedAnswers2[glass.answerKey] = [];
// pass in: glass, i, alreadyAnsweredQuestions
return this.setupInitialData(glass, i, alreadyAnsweredQuestions); const updatedGlass = this.setupInitialData(glass, i, alreadyAnsweredQuestions);
// Set up watch for each set of glass questions
this.$watch("selectedAnswers2." + glass.answerKey, (newValue) => {
if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates(newValue, glass.answerKey);
}
}, { deep: true });
return updatedGlass;
}); });
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
@ -118,13 +110,14 @@ export default {
capabilityQuestionsPageData && Object.keys(capabilityQuestionsPageData).length > 0 capabilityQuestionsPageData && Object.keys(capabilityQuestionsPageData).length > 0
); );
}, },
showThisQuestionChain(glass, i) {
if (!glass.questions || glass.questions?.length < 1 || glass.isSuppressedPart) { return false; } // return false if no capabilityQuestions or if suppressed
return this.currentQuestionChainIndex === i || glass.answerData?.answerResult?.length > 0;
},
async forwardButtonAction() { async forwardButtonAction() {
const questionAnswersArray = this.questionsData.map((glass) => { const questionAnswersArray = this.questionsData.map((glass) => {
const selectedAnswerResult2 = this.getCorrespondingAnswerResult2(glass.answerData.answerResult); // get answerResult2 of returned answer
let selectedAnswerResult2;
glass.questions.forEach(q => {
const idx = q.answers.findIndex(a => a.answerResult === glass.answerData.answerResult);
if (idx !== -1) { selectedAnswerResult2 = q.answers[idx].answerResult2 }
});
return { return {
glassLocation: glass.glassLocation, glassLocation: glass.glassLocation,
@ -143,52 +136,34 @@ export default {
}); });
// save to vuex store as order.damage.capabilityQuestionAnswers (array) // save to vuex store as order.damage.capabilityQuestionAnswers (array)
await this.dispatchStoreAction(this.storeActions.SAVE_CAPABILITY_QUESTION_ANSWERS, questionAnswersArray, false); await this.dispatchStoreAction(
this.storeActions.SAVE_CAPABILITY_QUESTION_ANSWERS,
questionAnswersArray,
false
);
// get parts from the capabilityQuestionAnswers // get parts from the capabilityQuestionAnswers
let partsOrQuestions = this.pageData.partsOrQuestions; const partsOrQuestions = this.pageData.partsOrQuestions;
for (let answer of questionAnswersArray) { for (let answer of questionAnswersArray) {
const correspondingPart = partsOrQuestions.find(partOrQuestion => partOrQuestion.glassLocation === answer.glassLocation); const partFromCapabilityQuestionAnswer = (
const partFromCapabilityQuestionAnswer = (await this.dispatchStoreAction(this.storeActions.GET_PART_FROM_CAPABILITY_QUESTION_ANSWER, answer.glassLocation, false)).data; await this.dispatchStoreAction(
this.storeActions.GET_PART_FROM_CAPABILITY_QUESTION_ANSWER,
answer.glassLocation,
false
)
).data;
partsOrQuestions.find( partsOrQuestions.find(
(partOrQuestion) => partOrQuestion.location === answer.location (partOrQuestion) => partOrQuestion.glassLocation === answer.glassLocation
).parts = partFromCapabilityQuestionAnswer; ).parts = partFromCapabilityQuestionAnswer;
} }
this.navigateForward(partsOrQuestions); this.navigateForward(partsOrQuestions);
}, },
// TODO I'm sure there's a better/simplier way to do this that I'm missing but my brain's fried
// Find the part that has some answer that has an answerResult matching the selected answerResult, then get the corresponding answerResult2
getCorrespondingAnswerResult2(answerResult1) {
return this.questionsData
.find(part => part.capabilityQuestions.some(question => question.answers.some(answer => answer.answerResult == answerResult1))) // found part
.capabilityQuestions.find(question => question.answers.some(answer => answer.answerResult == answerResult1)) // found question
.answers.find(answer => answer.answerResult == answerResult1)
.answerResult2;
},
}, },
components: { components: {
funnelHeader,
vehicleBanner,
alert,
questionChain,
funnelSubHeader,
funnelFooter,
loadingModal,
Form, Form,
questionsPage,
}, },
}; };
</script> </script>
<style lang="scss">
.capability-questions {
.question-text {
margin-bottom: 0.5rem;
span {
text-align: left;
}
}
}
</style>

View file

@ -1,51 +1,28 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form
<div class="page-container-grouped-styles molding-questions"> @submit="onSubmit"
<loadingModal ref="loadingModal" /> @invalid-submit="onInvalidSubmit"
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> ref="theForm"
<vehicleBanner v-slot="{ meta }"
cmsWidgetName="VehicleBannerWidget" >
:displayGenericVehicleImage="false" /> <questions-page
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" /> ref="questionsPage"
<div class="fade-on-route-transition sub-container make-tall"> :isMetaValid="meta.valid"
<alert :alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
ref="alertFewMoreQuestions" :alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
cmsWidgetName="AlertFewMoreQuestionsWidget" :questionsData="questionsData"
class="my-5" :validationRules="questions-required"
alertClass="alert-warning" v-model="selectedAnswers2"
:manualHeadline="AlertFewMoreQuestionsHeader" @forwardButtonAction="forwardButtonAction"
:manualCopy="AlertFewMoreQuestionsCopy" @backButtonAction="backButtonAction"
v-bind:isDismissible="false" :currentGlassPieceIndex="currentGlassPieceIndex"
/> />
<div v-for="(glass, i) in questionsData" :key="glass.key">
<questionChain
ref="questionChain"
v-model="selectedAnswers[glass.glassLocation + '-' + glass.glassName]"
:questionData="glass.questions"
:glassIndex="i"
v-if="showThisQuestionChain(glass, i)"
validationRules="questions-required" />
</div>
<funnel-footer
ref="funnelFooter"
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
</div>
</Form> </Form>
</template> </template>
<script> <script>
// Components // Components
import funnelHeader from "@/common-components/funnel-header/funnel-header"; import questionsPage from "@/common-components/questions-page/questions-page";
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 // Supporting Files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -84,9 +61,9 @@ export default {
}, },
data() { data() {
return { return {
questionsData: store.getters.pageData(fmgPageValues.MOLDING_QUESTIONS).partsOrQuestions, questionsData: [],
selectedAnswers: {}, selectedAnswers2: {},
currentGlassIndex: 0, currentGlassPieceIndex: 0,
}; };
}, },
computed: { computed: {
@ -107,11 +84,21 @@ export default {
this.questionsData = this.pageData.partsOrQuestions.filter(x => x.parts[0].childPartQuestions.length).map((glass, i) => { this.questionsData = this.pageData.partsOrQuestions.filter(x => x.parts[0].childPartQuestions.length).map((glass, i) => {
// NOTE: questions for property "questions" can differ between layouts // NOTE: questions for property "questions" can differ between layouts
glass.questions = glass.parts[0].childPartQuestions; glass.questions = glass.parts[0].childPartQuestions;
glass.answerKey = glass.glassLocation + "-" + glass.glassName;
// reset selectedAnswers for this glass // reset selectedAnswers for this glass
this.selectedAnswers[glass.key] = []; this.selectedAnswers2[glass.answerKey] = [];
// pass in: glass, i, alreadyAnsweredQuestions
return this.setupInitialData(glass, i, alreadyAnsweredQuestions); const updatedGlass = this.setupInitialData(glass, i, alreadyAnsweredQuestions);
this.$watch("selectedAnswers2." + glass.answerKey, (newValue) => {
if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates(newValue, glass.answerKey);
}
}, { deep: true });
return updatedGlass;
}); });
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
@ -122,12 +109,6 @@ export default {
moldingQuestionsFromPageData && Object.keys(moldingQuestionsFromPageData).length > 0 moldingQuestionsFromPageData && Object.keys(moldingQuestionsFromPageData).length > 0
); );
}, },
showThisQuestionChain(glass, i) {
if (!glass.questions || glass.questions?.length < 1 || glass.isSuppressedPart) {
return false;
} // return false if no questions or if suppressed
return this.currentGlassIndex === i || glass.answerData?.answerResult?.length > 0;
},
async forwardButtonAction() { async forwardButtonAction() {
const questionAnswersArray = this.questionsData.map((glass) => { const questionAnswersArray = this.questionsData.map((glass) => {
return { return {
@ -170,25 +151,8 @@ export default {
}, },
}, },
components: { components: {
funnelHeader,
vehicleBanner,
alert,
questionChain,
funnelSubHeader,
funnelFooter,
loadingModal,
Form, Form,
questionsPage,
}, },
}; };
</script> </script>
<style lang="scss">
.molding-questions {
.question-text {
margin-bottom: 0.5rem;
span {
text-align: left;
}
}
}
</style>

View file

@ -1,51 +1,28 @@
<template> <template>
<Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }"> <Form
<div class="page-container-grouped-styles part-questions"> @submit="onSubmit"
<loadingModal ref="loadingModal" /> @invalid-submit="onInvalidSubmit"
<funnelHeader cmsWidgetName="FunnelHeaderWidget" /> ref="theForm"
<vehicleBanner v-slot="{ meta }"
cmsWidgetName="VehicleBannerWidget" >
:displayGenericVehicleImage="false" /> <questions-page
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" /> ref="questionsPage"
<div class="fade-on-route-transition sub-container make-tall"> :isMetaValid="meta.valid"
<alert :alertFewMoreQuestionsHeader="AlertFewMoreQuestionsHeader"
ref="alertFewMoreQuestions" :alertFewMoreQuestionsCopy="AlertFewMoreQuestionsCopy"
cmsWidgetName="AlertFewMoreQuestionsWidget" :questionsData="questionsData"
class="my-5" :validationRules="questions-required"
alertClass="alert-warning" v-model="selectedAnswers"
:manualHeadline="AlertFewMoreQuestionsHeader" @forwardButtonAction="forwardButtonAction"
:manualCopy="AlertFewMoreQuestionsCopy" @backButtonAction="backButtonAction"
v-bind:isDismissible="false" :currentGlassPieceIndex="currentGlassPieceIndex"
/> />
<div v-for="(glass, i) in questionsData" :key="glass.key">
<questionChain
ref="questionChain"
v-model="selectedAnswers[glass.glassLocation + '-' + glass.glassName]"
:questionData="glass.questions"
:glassIndex="i"
v-if="showThisQuestionChain(glass, i)"
validationRules="questions-required" />
</div>
<funnel-footer
ref="funnelFooter"
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!meta.valid"
@back-clicked="backButtonAction"
@ForwardClicked="forwardButtonAction" />
</div>
</div>
</Form> </Form>
</template> </template>
<script> <script>
// Components // Components
import funnelHeader from "@/common-components/funnel-header/funnel-header"; import questionsPage from "@/common-components/questions-page/questions-page";
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 // Supporting Files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper"; import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
@ -62,6 +39,7 @@ defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));
export default { export default {
name: "part-questions", name: "part-questions",
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);
@ -85,7 +63,7 @@ export default {
return { return {
questionsData: [], questionsData: [],
selectedAnswers: {}, selectedAnswers: {},
currentGlassIndex: 0, currentGlassPieceIndex: 0,
}; };
}, },
computed: { computed: {
@ -99,7 +77,6 @@ export default {
return this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS); return this.$store.getters.pageData(fmgPageValues.PART_QUESTIONS);
}, },
}, },
mixins: [vehicleQuestionsMixin],
mounted() { mounted() {
// are there alreadyAnsweredQuestions? // are there alreadyAnsweredQuestions?
const alreadyAnsweredQuestions = store.getters.damage.partQuestionAnswers; const alreadyAnsweredQuestions = store.getters.damage.partQuestionAnswers;
@ -107,23 +84,28 @@ export default {
this.questionsData = this.pageData.partsOrQuestions.filter(x => x.partQuestions).map((glass, i) => { this.questionsData = this.pageData.partsOrQuestions.filter(x => x.partQuestions).map((glass, i) => {
// NOTE: questions for property "questions" can differ between layouts // NOTE: questions for property "questions" can differ between layouts
glass.questions = glass.partQuestions; glass.questions = glass.partQuestions;
glass.answerKey = glass.glassLocation + "-" + glass.glassName;
// reset selectedAnswers for this glass // reset selectedAnswers for this glass
this.selectedAnswers[glass.key] = []; this.selectedAnswers[glass.answerKey] = [];
// pass in: glass, i, alreadyAnsweredQuestions
return this.setupInitialData(glass, i, alreadyAnsweredQuestions); const updatedGlass = this.setupInitialData(glass, i, alreadyAnsweredQuestions);
// Set up watch for each set of glass questions
this.$watch("selectedAnswers." + glass.answerKey, (newValue) => {
if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates(newValue, glass.answerKey);
}
}, {deep: true});
return updatedGlass;
}); });
}, },
methods: { methods: {
arePagePrerequisitesValid() { arePagePrerequisitesValid() {
const partQuestionsFromPageData = store.getters.pageData(fmgPageValues.PART_QUESTIONS); const partQuestionsFromPageData = store.getters.pageData(fmgPageValues.PART_QUESTIONS);
return partQuestionsFromPageData && Object.keys(partQuestionsFromPageData).length > 0; return partQuestionsFromPageData && Object.keys(partQuestionsFromPageData).length > 0;
}, },
showThisQuestionChain(glass, i) {
if (!glass.questions || glass.questions?.length < 1 || glass.isSuppressedPart) {
return false;
} // return false if no questions or if suppressed
return this.currentGlassIndex === i || glass.answerData?.answerResult?.length > 0;
},
async forwardButtonAction() { async forwardButtonAction() {
const questionAnswersArray = this.questionsData.map((glass) => { const questionAnswersArray = this.questionsData.map((glass) => {
return { return {
@ -159,25 +141,9 @@ export default {
}, },
}, },
components: { components: {
funnelHeader,
vehicleBanner,
alert,
questionChain,
funnelSubHeader,
funnelFooter,
loadingModal,
Form, Form,
questionsPage,
}, },
}; };
</script> </script>
<style lang="scss">
.part-questions {
.question-text {
margin-bottom: 0.5rem;
span {
text-align: left;
}
}
}
</style>

View file

@ -1,6 +1,5 @@
import { fmgPageValues } from "@/router/router-constants/fmgPage-values"; import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { storeMutations } from "@/constants/store-mutations.js"; import { storeMutations } from "@/constants/store-mutations.js";
import { damageLocationsSelected } from "@/constants/damage-locations-selected";
import { navigationScenarios } from "../router/router-constants/navigation-scenarios"; import { navigationScenarios } from "../router/router-constants/navigation-scenarios";
import { storeActions } from "@/constants/store-actions"; import { storeActions } from "@/constants/store-actions";
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper"; import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
@ -68,8 +67,8 @@ export default {
return this.comparePageIndices(currentPage, fmgPage) > 0; return this.comparePageIndices(currentPage, fmgPage) > 0;
}, },
setupInitialData(glass, i, alreadyAnsweredQuestions, vm) { setupInitialData(glass, i, alreadyAnsweredQuestions, vm) {
glass.key = glass.glassLocation + "-" + glass.glassName;
const self = vm ?? this; const self = vm ?? this;
// clear answerData if no questions are already answered // clear answerData if no questions are already answered
if (!alreadyAnsweredQuestions) { if (!alreadyAnsweredQuestions) {
glass.answerData = null; glass.answerData = null;
@ -82,9 +81,7 @@ export default {
!answeredGlass.glassName || !answeredGlass.glassName ||
!answeredGlass.answeredQuestions || !answeredGlass.answeredQuestions ||
(!answeredGlass.result && !answeredGlass.partNum) (!answeredGlass.result && !answeredGlass.partNum)
) { ) { return }
return;
}
// test if glass parts match // test if glass parts match
if ( if (
@ -94,33 +91,33 @@ export default {
let answerString = ""; let answerString = "";
// loop through answeredQuestions for matches // loop through answeredQuestions for matches
answeredGlass.answeredQuestions.forEach((aq) => { answeredGlass.answeredQuestions.forEach((answeredQuestion) => {
if (!aq.questionNum || !aq.selectedAnswerText) { if (!answeredQuestion.questionNum || !answeredQuestion.selectedAnswerText) {
return; return;
} }
// determine which answer was previously chosen // determine which answer was previously chosen
const chosenAns = glass.questions[aq.questionNum - 1].answers.find((a) => { const chosenAns = glass.questions[answeredQuestion.questionNum - 1].answers.find((a) => {
return ( return (
a.answerText.toUpperCase() === aq.selectedAnswerText.toUpperCase() a.answerText.toUpperCase() === answeredQuestion.selectedAnswerText.toUpperCase()
); );
}); });
// set the answerString to use for answerSelected // set the answerString to use for answerSelected
if (chosenAns.nextQuestionSequence) { if (chosenAns.nextQuestionSequence) {
answerString = `${aq.questionNum}|nextQuestion|${chosenAns.nextQuestionSequence}|${chosenAns.answerText}`; answerString = `${answeredQuestion.questionNum}|nextQuestion|${chosenAns.nextQuestionSequence}|${chosenAns.answerText}`;
} else { } else {
answerString = `${aq.questionNum}|answer|${chosenAns.answerResult}|${chosenAns.answerText}`; answerString = `${answeredQuestion.questionNum}|answer|${chosenAns.answerResult}|${chosenAns.answerText}`;
} }
// mark this question as answered (question-chain will read this) // mark this question as answered (question-chain will read this)
glass.questions[aq.questionNum - 1].answerSelected = answerString; glass.questions[answeredQuestion.questionNum - 1].answerSelected = answerString;
// mark this question as suppressed if needed (question-chain uses this) // mark this question as suppressed if needed (question-chain uses this)
if (aq.suppressQuestion) { if (answeredQuestion.suppressThisQuestion) {
glass.questions[aq.questionNum - 1].suppressQuestion = true; glass.questions[answeredQuestion.questionNum - 1].suppressThisQuestion = true;
} }
}); });
// advance the currentGlassIndex // advance the currentGlassPieceIndex
self.currentGlassIndex = i; self.currentGlassPieceIndex = i;
const answerResult = answeredGlass.partNum const answerResult = answeredGlass.partNum
? answeredGlass.partNum ? answeredGlass.partNum
@ -135,21 +132,9 @@ export default {
} }
}); });
// Set up watch for each set of glass questions
// (updated when all questions for a glass have been answered in question-chain)
self.$watch(
"selectedAnswers." + glass.glassLocation + "-" + glass.glassName,
(newValue) => {
if (newValue) {
self.handleAnswerUpdates(newValue);
}
},
{ deep: true }
);
return glass; return glass;
}, },
handleAnswerUpdates(answer, vm) { handleAnswerUpdates(answer, glassKey, vm) {
// only runs when all questions in a question-chain have been answered // only runs when all questions in a question-chain have been answered
// when selectedAnswers updates, user has completed this part's question chain and has a final answer // when selectedAnswers updates, user has completed this part's question chain and has a final answer
// (does not get run for each invididual question's answer, only when // (does not get run for each invididual question's answer, only when
@ -171,236 +156,183 @@ export default {
const self = vm ?? this; const self = vm ?? this;
// collect a list of the answered questions' numbers, needed later below // clear out any preloaded answers
const answeredQuestionIndexes = [];
const foundDuplicateQuestions = [];
// if user has answered a question differently than anything that was preloaded,
// we need to clear out any preloaded answers
self.selectedAnswers = {}; self.selectedAnswers = {};
// loop through every answered question on the currently answered glass part // loop through every answered question on the currently answered glass part
answer.answeredQuestions?.forEach((aq) => { answer.answeredQuestions?.forEach((answeredQuestion) => {
// keep track of this question number /* answeredQuestion example format:
answeredQuestionIndexes.push(aq.questionNum); {
"questionText": "Is your Grand Cherokee the Laredo model?",
"selectedAnswerText": "Yes",
"questionNum": 1,
}
*/
const answeredQuestionText = aq.questionText.toUpperCase(); const answeredQuestionText = answeredQuestion.questionText.toUpperCase();
const answeredQuestionAnswer = aq.selectedAnswerText.toUpperCase(); const answeredQuestionAnswer = answeredQuestion.selectedAnswerText.toUpperCase();
// HANDLE DUPLICATE QUESTIONS // HANDLE DUPLICATE QUESTIONS
// loop through all glass parts data // loop through all glass pieces data
self.questionsData.forEach((glass, gpIndex) => { self.questionsData.forEach((glassPiece, pieceIndex) => {
/* glassPiece example format:
{
"glassName": "Single",
"glassLocation": "Windshield",
"parts": null,
"questions": [
{
"questionSequence": 1,
"questionText": "Is your vehicle equipped with a black dotted pattern behind the rear view mirror, known as a third visor frit?",
"answers": [
{
"answerResult": "DW01537",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "DW01537b",
"answerText": "No",
"nextQuestionSequence": null
}
]
}
],
"answerKey": "Windshield-Single",
"answerData": null
}
*/
// only look for duplicates forward... to parts that follow after the currently being answered part // limit duplicate search to glass pieces that follow after the currently being answered glass piece
if (gpIndex > answer.glassIndex) { if (pieceIndex > answer.glassIndex) {
let suppressUntil; let indexToSuppressTo;
// reset this glass part, in case user is changing their previous answers // reset this glass piece, in case user is changing their previous answers
glass.answerData = null; glassPiece.answerData = null;
glass.isSuppressedPart = null; glassPiece.isSuppressedPart = null;
// loop through this glass part's questions, looking for a questionText match // loop through this glass piece's questions, looking for a questionText match
glass.questions.forEach((pq, pqIndex) => { glassPiece.questions.forEach((question, questionIndex) => {
// clear out any previously set answers // clear out any previously set answers
pq.answerSelected = null; question.answerSelected = null;
// clear or set suppressQuestion property for each question // clear or set suppressThisQuestion property for each question
if (suppressUntil) { if (indexToSuppressTo && questionIndex + 1 < indexToSuppressTo) {
// if suppressUntil has been set, then suppress this question if before it question.suppressThisQuestion = true;
if (pqIndex + 1 < suppressUntil) {
pq.suppressQuestion = true;
} else {
pq.suppressQuestion = null;
}
} else { } else {
pq.suppressQuestion = null; question.suppressThisQuestion = null;
} }
// if these match then we have a duplicate question // handle duplicate questions
if (pq.questionText.toUpperCase() === answeredQuestionText) { if (question.questionText.toUpperCase() === answeredQuestionText) {
const thisAnsweredQuestion = glass.questions[pqIndex];
let matchedAnswer; let matchedAnswer;
let rejectedAnswers = []; // set as an array, in case we ever have questions with more than 2 answers...
// which one of this questions' answers matches our answer? // handle matching answer in duplicated question
pq.answers.forEach((ans) => { question.answers.forEach((ans) => {
if (ans.answerText.toUpperCase() === answeredQuestionAnswer) { if (ans.answerText.toUpperCase() === answeredQuestionAnswer) {
matchedAnswer = ans; matchedAnswer = ans;
ans.selected = true; ans.selected = true;
} else { } else {
rejectedAnswers.push(ans);
ans.selected = null; ans.selected = null;
} }
}); });
// Update the key to re-render this part's question-chain component // handle duplicate's nextQuestion logic on other related questions
self.questionsData[gpIndex].key = self.questionsData[gpIndex].glassLocation + self.questionsData[gpIndex].glassName + Date.now().toString();
// handle suppressing downstream in this question chain
if (matchedAnswer.nextQuestionSequence) { if (matchedAnswer.nextQuestionSequence) {
// ensure that the question that the accepted answer has set to be next is NOT suppressed // clear any suppression on the nextQuestion
glass.questions[matchedAnswer.nextQuestionSequence - 1].suppressQuestion = null; glassPiece.questions[matchedAnswer.nextQuestionSequence - 1].suppressThisQuestion = null;
// if the duplicate is the 1ST question, then set suppressUntil var to lowest nextQuestion number // if the duplicate is 1ST question in array, set indexToSuppressTo
if (pqIndex === 0) { if (questionIndex === 0) {
if (!suppressUntil) { suppressUntil = matchedAnswer.nextQuestionSequence } if (!indexToSuppressTo || matchedAnswer.nextQuestionSequence < indexToSuppressTo) { indexToSuppressTo = matchedAnswer.nextQuestionSequence}
if (matchedAnswer.nextQuestionSequence < suppressUntil) { suppressUntil = matchedAnswer.nextQuestionSequence}
} }
} }
// handle suppressing upstream in this question chain // update answers in this glass piece's questions with duplication logic modifications
glass.questions.forEach((q) => { glassPiece.questions.forEach((q) => {
q.answers.forEach((thisAns) => { q.answers.forEach((a) => {
// restore any of the answers that formerly led to the duplicated question // revert any previously set nextQuestion logic modifications
if (thisAns.originalNextQuestionSequence === pq.questionSequence) { if (a.originalNextQuestionSequence === question.questionSequence) {
// restore original nextQuestionSequence // restore original nextQuestionSequence
thisAns.nextQuestionSequence = thisAns.originalNextQuestionSequence; a.nextQuestionSequence = a.originalNextQuestionSequence;
self.originalNextQuestionSequence = null; self.originalNextQuestionSequence = null;
// restore original answerResult // restore original answerResult
if (thisAns.originalAnswerResult) { if (a.originalAnswerResult) {
thisAns.answerResult = thisAns.originalAnswerResult; a.answerResult = a.originalAnswerResult;
thisAns.originalAnswerResult = null; a.originalAnswerResult = null;
} }
} }
// search for any answers that lead to the duplicated question // modify logic on any related questions
if (thisAns.nextQuestionSequence === pq.questionSequence) { if (a.nextQuestionSequence === question.questionSequence) {
// update either the nextQuestionSequence or the answerResult // update either nextQuestionSequence or answerResult
// update questions that lead to duplicated question
if (matchedAnswer.nextQuestionSequence) { if (matchedAnswer.nextQuestionSequence) {
thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence; a.originalNextQuestionSequence = a.nextQuestionSequence;
thisAns.nextQuestionSequence = matchedAnswer.nextQuestionSequence; a.nextQuestionSequence = matchedAnswer.nextQuestionSequence;
} else { } else {
thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence; a.originalNextQuestionSequence = a.nextQuestionSequence;
thisAns.nextQuestionSequence = null; a.nextQuestionSequence = null;
thisAns.originalAnswerResult = thisAns.originalAnswerResult || thisAns.answerResult; a.originalAnswerResult = a.originalAnswerResult || a.answerResult;
thisAns.answerResult = matchedAnswer.answerResult; a.answerResult = matchedAnswer.answerResult;
} }
} }
}); });
}); });
// suppress current question // suppress current question
thisAnsweredQuestion.suppressQuestion = true; question.suppressThisQuestion = true;
const thisGlassPart = "glass" + gpIndex;
if (foundDuplicateQuestions[thisGlassPart]) {
if (!foundDuplicateQuestions[thisGlassPart].includes(thisAnsweredQuestion.questionSequence)) {
foundDuplicateQuestions[thisGlassPart].push(thisAnsweredQuestion.questionSequence);
}
} else {
foundDuplicateQuestions[thisGlassPart] = [thisAnsweredQuestion.questionSequence];
}
// are there any questions left that are not suppressed? // are there any questions left that are not suppressed?
const remainingQuestions = glass.questions.filter((q) => { const remainingQuestions = glassPiece.questions.filter((q) => {
return !q.suppressQuestion; return !q.suppressThisQuestion;
}); });
if (remainingQuestions.length < 1) { if (remainingQuestions.length < 1) {
// this is the final answer for this glass part // this is the final answer for this glass piece
// mark this part as completely answered by adding answerData // mark this glass piece as completely answered by adding answerData
const answeredQuestionObj = { const answeredQuestionObj = {
questionText: pq.questionText, questionText: question.questionText,
selectedAnswerText: matchedAnswer.answerText, selectedAnswerText: matchedAnswer.answerText,
questionNum: pq.questionSequence, questionNum: question.questionSequence,
suppressQuestion: pq.suppressQuestion, suppressThisQuestion: question.suppressThisQuestion,
}; };
// set the answerData as 'already answered' // set the answerData (used as indicator that it has been already answered)
glass.answerData = { glassPiece.answerData = {
answerResult: matchedAnswer.nextQuestionSequence ? matchedAnswer.nextQuestionSequence : matchedAnswer.answerResult, answerResult: matchedAnswer.nextQuestionSequence ? matchedAnswer.nextQuestionSequence : matchedAnswer.answerResult,
answeredQuestions: [answeredQuestionObj], answeredQuestions: [answeredQuestionObj],
}; };
// suppress this glass because it has an answer // suppress this glass piece because it has an answer
glass.isSuppressedPart = true; glassPiece.isSuppressedPart = true;
} }
} }
}); });
// Update the key to re-render this part's question-chain component // Update key to force re-render of glass piece with duplicate question in case user changes previous related answer in the chain
self.questionsData[gpIndex].key = self.questionsData[gpIndex].glassLocation + self.questionsData[gpIndex].glassName + Date.now().toString(); self.questionsData[pieceIndex].key = self.questionsData[pieceIndex].key + Date.now().toString();
} }
}); });
}); });
// DETERMINE ANSWERED QUESTIONS LIST FOR THIS GLASS PART
// 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 foundDuplicateQuestions = {
// "glassPart1": [1],
// "glassPart2": [7, 10]
// };
const thisPartsDupes = foundDuplicateQuestions["glass" + answer.glassIndex];
const completeAnsweredQuestions = answer.answeredQuestions ? [...answer.answeredQuestions] : [];
const glassPartAnswered = self.questionsData[answer.glassIndex];
thisPartsDupes?.forEach((dupe) => {
// dupe is a single integer
const dupeQuestion = glassPartAnswered.questions[dupe - 1];
const dupeQuestionAnswer = dupeQuestion.answers.find((q) => q.selected === true);
glassPartAnswered.questions.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,
suppressQuestion: dupeQuestion.suppressQuestion,
});
}
});
});
// 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 // set final answer data for the current answered glass part
glassPartAnswered.answerData = { self.questionsData[answer.glassIndex].answerData = {
answerResult: answer.answerResult, answerResult: answer.answerResult,
answeredQuestions: filteredCompleteAnsweredQuestions, answeredQuestions: answer.answeredQuestions,
} }
// this part has been fully answered, so advance to next part's question chain // this part has been fully answered, so advance to next part's question chain
for (let i = answer.glassIndex + 1; i < self.questionsData.length; i++) { for (let i = answer.glassIndex + 1; i < this.questionsData.length; i++) {
// if this part has not yet been fully answered, then make it the current part // if this part has not yet been fully answered, then make it the current part
if (!self.questionsData[i].answerData?.answerResult) { if (!this.questionsData[i].answerData?.answerResult) {
self.currentGlassIndex = i; this.currentGlassPieceIndex = i;
break; break;
} }
} }
@ -495,12 +427,19 @@ export default {
{ partsOrQuestions } { partsOrQuestions }
); );
} else { } else {
// if single parts only // if single parts only
const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions); const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions);
// save to store lineItems.glassParts // save to store lineItems.glassParts
self.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts); self.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts);
self.$refs.loadingModal.showModal(); if (self.$refs.questionsPage) {
self.$refs.questionsPage.showLoadingModal();
} else if (self.$refs.loadingModal) {
self.$refs.loadingModal.showModal();
}
navigateToHeritageFunnel(); navigateToHeritageFunnel();
} }
}, },

View file

@ -337,24 +337,6 @@ describe("vehicle-questions-mixin", () => {
}); });
describe("setupInitialData", () => { describe("setupInitialData", () => {
describe("should always", () => {
test("return glass with new key attribute", async () => {
// Arrange
const { wrapper } = setupMocks({});
const glass = {
glassName: "Single",
glassLocation: "Windshield",
};
const i = 0;
// Act
const returnedGlass = await wrapper.vm.setupInitialData(glass, i);
// Assert
expect(returnedGlass).toMatchObject({ key: "Windshield-Single" });
});
});
describe("if no alreadyAnsweredQuestions", () => { describe("if no alreadyAnsweredQuestions", () => {
test("should return glass with answerData of null", async () => { test("should return glass with answerData of null", async () => {
// Arrange // Arrange
@ -382,8 +364,7 @@ describe("vehicle-questions-mixin", () => {
questions: [ questions: [
{ {
questionSequence: 1, questionSequence: 1,
questionText: questionText: "Does the rubber seal around your windshield have a chrome strip running through it?",
"Does the rubber seal around your windshield have a chrome strip running through it?",
answers: [ answers: [
{ {
answerResult: "WKT D1106 C", answerResult: "WKT D1106 C",
@ -407,8 +388,7 @@ describe("vehicle-questions-mixin", () => {
partNum: "WKT D1106 C", partNum: "WKT D1106 C",
answeredQuestions: [ answeredQuestions: [
{ {
questionText: questionText: "Does the rubber seal around your windshield have a chrome strip running through it?",
"Does the rubber seal around your windshield have a chrome strip running through it?",
selectedAnswerText: "Yes", selectedAnswerText: "Yes",
questionNum: 1, questionNum: 1,
}, },
@ -430,6 +410,803 @@ describe("vehicle-questions-mixin", () => {
}); });
}); });
describe("handleAnswerUpdates", () => {
describe("selectedAnswers", () => {
test("should be cleared to be empty", () => {
// Arrange
const answer = {
"answerResult": "DD11132",
"answeredQuestions": [
{
"questionText": "Test duplicate question 1?",
"selectedAnswerText": "Yes",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.selectedAnswers = { "test": "mockSelectedAnswers" };
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"questions": [],
"answerData": "testAnswerData1"
}
];
// Act
wrapper.vm.handleAnswerUpdates(answer, '', wrapper.vm);
// Assert
expect(wrapper.vm.selectedAnswers).toMatchObject({});
});
});
describe("questions in glass parts that are after the answered glass", () => {
test("should have answerData cleared", () => {
// Arrange
const answer = {
"answerResult": "DD11132",
"answeredQuestions": [
{
"questionText": "Test duplicate question 1?",
"selectedAnswerText": "Yes",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"questions": [],
"answerData": "testAnswerData1"
},
{
"glassLocation": "Driver",
"glassName": "Front",
"questions": [],
"answerData": "testAnswerData2"
}
];
// Act
wrapper.vm.handleAnswerUpdates(answer, '', wrapper.vm);
// Assert
expect(wrapper.vm.questionsData[1].answerData).toEqual(null);
});
test("should have isSuppressedPart cleared", () => {
// Arrange
const answer = {
"answerResult": "DD11132",
"answeredQuestions": [
{
"questionText": "Test duplicate question 1?",
"selectedAnswerText": "Yes",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"questions": [],
"answerData": "testAnswerData1"
},
{
"glassLocation": "Driver",
"glassName": "Front",
"questions": [],
"answerData": "testAnswerData2",
isSuppressedPart: true
}
];
// Act
wrapper.vm.handleAnswerUpdates(answer, '', wrapper.vm);
// Assert
expect(wrapper.vm.questionsData[1].isSuppressedPart).toEqual(null);
});
test("should set answerSelected for each glass part question to null ", () => {
// Arrange
const answer = {
"answerResult": "DD11132",
"answeredQuestions": [
{
"questionText": "Test duplicate question 1?",
"selectedAnswerText": "Yes",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"questions": [],
"answerData": "testAnswerData1"
},
{
"glassLocation": "Driver",
"glassName": "Front",
"questions": [
{
"questionSequence": 1,
"questionText": "Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?",
"answers": [
{
"answerResult": "",
"answerText": "Yes",
"nextQuestionSequence": 2
},
{
"answerResult": "",
"answerText": "No",
"nextQuestionSequence": 3
}
],
"answerSelected": "456"
},
{
"questionSequence": 2,
"questionText": "Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?",
"answers": [
{
"answerResult": "FW04848",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "FW04846",
"answerText": "No",
"nextQuestionSequence": null
}
],
"answerSelected": "789"
}
],
"answerData": "testAnswerData2"
}
];
// Act
wrapper.vm.handleAnswerUpdates(answer, '', wrapper.vm);
// Assert
expect(wrapper.vm.questionsData[1].questions[0].answerSelected).toEqual(null);
expect(wrapper.vm.questionsData[1].questions[1].answerSelected).toEqual(null);
});
});
describe("if there is a duplicate question", () => {
test("then the glass piece's key should be updated", () => {
// Arrange
const answerNo = {
"answerResult": "456",
"answeredQuestions": [
{
"questionText": "Test duplicate question 1?",
"selectedAnswerText": "No",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"key": "testkey1",
"questions": [
{
"questionSequence": 1,
"questionText": "Test duplicate question 1?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "456",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
]
},
{
"glassLocation": "Driver",
"glassName": "Front",
"key": "testkey2",
"questions": [
{
"questionSequence": 1,
"questionText": "Test duplicate question 1?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "234",
"answerText": "No",
"nextQuestionSequence": null
}
]
}
]
}
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, '', wrapper.vm);
const glassPieceWithDuplicate = wrapper.vm.questionsData[1];
// Assert
expect(glassPieceWithDuplicate.key).not.toBe("testkey2");
});
test("then that question should be supressed", () => {
// Arrange
const answerNo = {
"answerResult": "456",
"answeredQuestions": [
{
"questionText": "Test duplicate question 1?",
"selectedAnswerText": "No",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"questions": [
{
"questionSequence": 1,
"questionText": "Test duplicate question 1?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "456",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
]
},
{
"glassLocation": "Driver",
"glassName": "Front",
"questions": [
{
"questionSequence": 1,
"questionText": "Test duplicate question 1?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "234",
"answerText": "No",
"nextQuestionSequence": null
}
]
}
]
}
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, '', wrapper.vm);
const duplicateQuestion = wrapper.vm.questionsData[1].questions[0];
// Assert
expect(duplicateQuestion.suppressThisQuestion).toBeTruthy();
});
describe("and the duplicate has a nextQuestionSequence value", () => {
test("then the question set as nextQuestionSequence should not be suppressed", () => {
// Arrange
const answerNo = {
"answerResult": "456",
"answeredQuestions": [
{
"questionText": "Test duplicate question 1?",
"selectedAnswerText": "No",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"questions": [
{
"questionSequence": 1,
"questionText": "Test duplicate question 1?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "456",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
]
},
{
"glassLocation": "Driver",
"glassName": "Front",
"questions": [
{
"questionSequence": 1,
"questionText": "ZZZTest duplicate question 1?",
"answers": [
{
"answerResult": "",
"answerText": "Yes",
"nextQuestionSequence": 2
},
{
"answerResult": "",
"answerText": "No",
"nextQuestionSequence": 3
}
]
},
{
"questionSequence": 2,
"questionText": "Test question 2?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "234",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
{
"questionSequence": 3,
"questionText": "Test question 3?",
"answers": [
{
"answerResult": "345",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "456",
"answerText": "No",
"nextQuestionSequence": null
}
],
"suppressThisQuestion": true
}
]
}
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, '', wrapper.vm);
const nextQuestionAfterDuplicate = wrapper.vm.questionsData[1].questions[2];
// Assert
expect(nextQuestionAfterDuplicate.suppressThisQuestion).not.toBeTruthy();
});
test("then the nextQuestionSequence should be updated and the original value saved", () => {
// Arrange
const answerNo = {
"answerResult": "456",
"answeredQuestions": [
{
"questionText": "Test question 3?",
"selectedAnswerText": "No",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"questions": [
{
"questionSequence": 1,
"questionText": "Test question 3?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "456",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
]
},
{
"glassLocation": "Driver",
"glassName": "Front",
"questions": [
{
"questionSequence": 1,
"questionText": "Test question 1?",
"answers": [
{
"answerResult": "",
"answerText": "Yes",
"nextQuestionSequence": 2
},
{
"answerResult": "",
"answerText": "No",
"nextQuestionSequence": 3
}
]
},
{
"questionSequence": 2,
"questionText": "Test question 2?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "234",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
{
"questionSequence": 3,
"questionText": "Test question 3?",
"answers": [
{
"answerResult": "",
"answerText": "Yes",
"nextQuestionSequence": 4
},
{
"answerResult": "",
"answerText": "No",
"nextQuestionSequence": 5
}
]
},
{
"questionSequence": 4,
"questionText": "Test question 4?",
"answers": [
{
"answerResult": "567",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "678",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
{
"questionSequence": 5,
"questionText": "Test question 5?",
"answers": [
{
"answerResult": "789",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "890",
"answerText": "No",
"nextQuestionSequence": null
}
]
}
]
}
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, '', wrapper.vm);
const duplicatedQuestion = wrapper.vm.questionsData[1].questions[2];
const duplicatedQuestionAnswer = duplicatedQuestion.answers.filter(a => {
return a.selected;
})
const answerToTest = wrapper.vm.questionsData[1].questions[0].answers.filter(a => {
return a.originalNextQuestionSequence;
});
// Assert
expect(answerToTest[0].originalNextQuestionSequence).toEqual(duplicatedQuestion.questionSequence);
expect(answerToTest[0].nextQuestionSequence).toEqual(duplicatedQuestionAnswer[0].nextQuestionSequence);
});
describe("and the duplicate was the first question for that part", () => {
test("then any questions up to the nextQuestionSequence value should be marked as suppressed", () => {
// Arrange
const answerNo = {
"answerResult": "456",
"answeredQuestions": [
{
"questionText": "Test duplicate question 1?",
"selectedAnswerText": "No",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"questions": [
{
"questionSequence": 1,
"questionText": "Test duplicate question 1?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "456",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
]
},
{
"glassLocation": "Driver",
"glassName": "Front",
"questions": [
{
"questionSequence": 1,
"questionText": "Test duplicate question 1?",
"answers": [
{
"answerResult": "",
"answerText": "Yes",
"nextQuestionSequence": 2
},
{
"answerResult": "",
"answerText": "No",
"nextQuestionSequence": 3
}
]
},
{
"questionSequence": 2,
"questionText": "Test question 2?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "234",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
{
"questionSequence": 3,
"questionText": "Test question 3?",
"answers": [
{
"answerResult": "345",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "456",
"answerText": "No",
"nextQuestionSequence": null
}
]
}
]
}
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, '', wrapper.vm);
// Assert
expect(wrapper.vm.questionsData[1].questions[0].suppressThisQuestion).toBeTruthy();
expect(wrapper.vm.questionsData[1].questions[1].suppressThisQuestion).toBeTruthy();
expect(wrapper.vm.questionsData[1].questions[2].suppressThisQuestion).toBeFalsy();
});
});
});
describe("and the duplicate has an answerResult", () => {
test("then any questions in the same glass piece that lead to the duplicate should be modified to just provide the duplicate's answerResult", async () => {
// Arrange
const answerNo = {
"answerResult": "456",
"answeredQuestions": [
{
"questionText": "Test question 3?",
"selectedAnswerText": "No",
"questionNum": 1,
}
],
"glassIndex": 0
};
const { wrapper } = setupMocks({});
wrapper.vm.questionsData = [
{
"glassLocation": "Windshield",
"glassName": "Single",
"questions": [
{
"questionSequence": 1,
"questionText": "Test question 3?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "456",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
]
},
{
"glassLocation": "Driver",
"glassName": "Front",
"questions": [
{
"questionSequence": 1,
"questionText": "Test question 1?",
"answers": [
{
"answerResult": "",
"answerText": "Yes",
"nextQuestionSequence": 2
},
{
"answerResult": "",
"answerText": "No",
"nextQuestionSequence": 3
}
]
},
{
"questionSequence": 2,
"questionText": "Test question 2?",
"answers": [
{
"answerResult": "123",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "234",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
{
"questionSequence": 3,
"questionText": "Test question 3?",
"answers": [
{
"answerResult": "345",
"answerText": "Yes",
"nextQuestionSequence": null
},
{
"answerResult": "456",
"answerText": "No",
"nextQuestionSequence": null
}
]
},
]
}
];
// Act
await wrapper.vm.handleAnswerUpdates(answerNo, '', wrapper.vm);
const questionsToTest = wrapper.vm.questionsData[1].questions;
const answerLeadingToDuplicate = questionsToTest[0].answers[1];
const duplicateQuestion = questionsToTest[2];
const answerInDuplicateQuestion = duplicateQuestion.answers.filter(a => {
return a.selected;
})
// Assert
expect(answerLeadingToDuplicate.answerResult).toEqual(answerInDuplicateQuestion[0].answerResult);
expect(answerLeadingToDuplicate.originalAnswerResult).toBeFalsy();
expect(answerLeadingToDuplicate.nextQuestionSequence).toEqual(null);
expect(answerLeadingToDuplicate.originalNextQuestionSequence).toEqual(duplicateQuestion.questionSequence);
});
});
});
});
describe("navigateForward", () => { describe("navigateForward", () => {
describe("should go to parts-questions", () => { describe("should go to parts-questions", () => {
test("single glass location has part question => go to parts-questions", async () => { test("single glass location has part question => go to parts-questions", async () => {