Merge branch 'develop' into feature/CSR-941

This commit is contained in:
Matt Sykes 2022-12-06 12:52:47 -05:00
commit d949f873e7
15 changed files with 117 additions and 70 deletions

View file

@ -27,7 +27,7 @@
ref="funnelFooter"
cmsWidgetName="FunnelFooterWidget"
:isForwardActionDisabled="!isMetaValid"
@back-clicked="handleBackButtonClicked"
@back-clicked="handleBackButtonAction"
@ForwardClicked="handleForwardButtonAction" />
</div>
</div>

View file

@ -100,7 +100,7 @@ export default {
return false;
}
// Example returnedAnswers:
// returnedAnswer examples:
// "1|nextQuestion|3|No"
// "5|answer|DW02104|Yes"
@ -108,15 +108,12 @@ export default {
const questionNum = parseInt(returnedAnswerArray[0]);
const questionType = returnedAnswerArray[1];
const questionAnswer = returnedAnswerArray[2];
const questionAnswerText = returnedAnswerArray[3];
const answeredQuestions = [];
this.questions.forEach((q) => {
// find this question and mark it as "answered" by populating answerSelected
if (q.questionSequence === questionNum) {
q.answerSelected = returnedAnswer;
q.answerNumber = questionNum;
q.selectedAnswerText = questionAnswerText;
}
// remove all answers AFTER this question...
// (needed in case user is changing previously answered questions)
@ -126,6 +123,7 @@ export default {
if (q.answerSelected) {
answeredQuestions.push({
questionText: q.questionText,
selectedAnswer: q.answerSelected,
selectedAnswerText: q.answerSelected.split("|")[3],
questionNum: q.questionSequence,
});

View file

@ -53,7 +53,7 @@ describe("textboxQuestion.vue", () => {
expect(paragraph.attributes("class")).toContain("rounded-pill");
});
it.only("Should return form-control class", async () => {
it("Should return form-control class", async () => {
// Act
const wrapper = shallowMount(textboxQuestion, {
global: {

View file

@ -84,12 +84,14 @@ const baseStoreGettersDamage = () => {
{
questionText:
"Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
{
questionText:
"Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?",
selectedAnswer: "2|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 2,
},
@ -166,10 +168,10 @@ describe("capabilityQuestions.vue", () => {
});
describe("watch on selectedAnswers should be set up...", () => {
test("Should trigger handleAnswerUpdates if watched data changes", async () => {
test("Should trigger handleCompletedQuestionChainAnswers if watched data changes", async () => {
// Arrange
const { wrapper } = setupMocks({});
const spy = jest.spyOn(wrapper.vm, "handleAnswerUpdates");
const spy = jest.spyOn(wrapper.vm, "handleCompletedQuestionChainAnswers");
// Act
wrapper.setData({
@ -180,12 +182,14 @@ describe("capabilityQuestions.vue", () => {
{
questionText:
"Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
{
questionText:
"Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?",
selectedAnswer: "2|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 2,
},

View file

@ -112,7 +112,7 @@ export default {
"selectedAnswers." + glass.answerKey,
(newValue) => {
if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates(newValue, glass.answerKey);
this.handleCompletedQuestionChainAnswers(newValue, glass.answerKey);
}
},
{ deep: true }

View file

@ -104,6 +104,7 @@ import { vinLookupMethodSelections } from "@/constants/vin-lookup-method-selecti
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
import experimentMixin from "@/mixins/experiment-mixin";
import { experimentSettings } from "@/constants/experiments";
import vinPagesMixin from "@/mixins/vin-pages-mixin";
// Define Validation Rules
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
@ -120,6 +121,7 @@ defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
export default {
name: "estimate",
mixins: [vinPagesMixin],
data() {
return {
selectedVinLookupMethod: null,
@ -203,10 +205,15 @@ export default {
}
this.displayNonServiceableZipAlert = false;
return this.$router.navigateWithSaving(
this.navigationScenarios.CLICKED_FORWARD_WITH_NO_QUESTIONS,
this.$route
);
if (this.isRepair) {
return this.$router.navigateWithSaving(
this.navigationScenarios.CLICKED_FORWARD_WITH_NO_QUESTIONS,
this.$route
);
} else {
// if we're skipping the vin-lookup but it's not a repair, we still need to get the parts
await this.navigateForwardWithSingleCarMatch();
}
}
if (this.selectedVinLookupMethod === vinLookupMethodSelections.MANUALVIN) {

View file

@ -88,12 +88,14 @@ const baseStoreGettersDamage = () => {
{
questionText:
"Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
{
questionText:
"Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?",
selectedAnswer: "2|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 2,
},
@ -170,10 +172,10 @@ describe("moldingQuestions.vue", () => {
});
describe("watch on selectedAnswers should be set up...", () => {
test("Should trigger handleAnswerUpdates if watched data changes", async () => {
test("Should trigger handleCompletedQuestionChainAnswers if watched data changes", async () => {
// Arrange
const { wrapper } = setupMocks({});
const spy = jest.spyOn(wrapper.vm, "handleAnswerUpdates");
const spy = jest.spyOn(wrapper.vm, "handleCompletedQuestionChainAnswers");
// Act
wrapper.setData({
@ -184,12 +186,14 @@ describe("moldingQuestions.vue", () => {
{
questionText:
"Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
{
questionText:
"Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?",
selectedAnswer: "2|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 2,
},

View file

@ -111,7 +111,7 @@ export default {
"selectedAnswers." + glass.answerKey,
(newValue) => {
if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates(newValue, glass.answerKey);
this.handleCompletedQuestionChainAnswers(newValue, glass.answerKey);
}
},
{ deep: true }

View file

@ -78,12 +78,14 @@ const baseStoreGettersDamage = () => {
{
questionText:
"Is your vehicle equipped with the Panoramic Sunroof which can be identified by having a glass panel over the rear seats?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
{
questionText:
"Is your vehicle equipped with a heated windshield that melts snow and ice from underneath the windshield wiper blades?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 2,
},
@ -152,14 +154,14 @@ describe("partQuestions.vue...", () => {
});
describe("watch on selectedAnswers should be set up...", () => {
test("Should trigger handleAnswerUpdates if watched data changes", async () => {
test("Should trigger handleCompletedQuestionChainAnswers if watched data changes", async () => {
// Arrange
store.getters = {
pageData: baseStoreGettersPageData,
damage: baseStoreGettersDamage,
};
const { wrapper } = setupMocks({});
const spy = jest.spyOn(wrapper.vm, "handleAnswerUpdates");
const spy = jest.spyOn(wrapper.vm, "handleCompletedQuestionChainAnswers");
// Act
wrapper.setData({
@ -169,11 +171,13 @@ describe("partQuestions.vue...", () => {
answeredQuestions: [
{
questionText: "One?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
{
questionText: "Two?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 2,
},

View file

@ -109,7 +109,7 @@ export default {
"selectedAnswers." + glass.answerKey,
(newValue) => {
if (newValue && Object.keys(newValue).length > 0) {
this.handleAnswerUpdates(newValue, glass.answerKey);
this.handleCompletedQuestionChainAnswers(newValue, glass.answerKey);
}
},
{ deep: true }

View file

@ -1,8 +1,11 @@
<template>
<baseInputButton v-bind="$props" @buttonClicked="handleAnswerChange" v-model="selectedValue">
<div class="package-label" for="testradio">
<div
class="package-label"
:class="[this.buttonLabelSubCopy ? 'has-subheader' : '']"
for="testradio">
<div class="package-specs">
<p :class="[this.buttonLabelSubCopy ? 'mb-2' : 'm-0']">
<p class="m-0">
<span v-html="this.buttonLabel"></span>
<span class="pricing-info" v-html="this.buttonAuxillaryCopy"></span>
</p>
@ -113,6 +116,10 @@ export default {
overflow: hidden;
min-height: 58px;
&.has-subheader {
min-height: 78px;
}
&:before {
content: "";
position: relative;
@ -193,12 +200,16 @@ export default {
font-weight: 500;
display: flex;
justify-content: space-between;
span.pricing-info {
color: $green;
span {
&.pricing-info {
color: $green;
font-size: 0.875rem;
}
}
&.sub-label {
color: $green;
text-transform: uppercase;
font-size: 0.75rem;
}
}
ul {
@ -206,6 +217,7 @@ export default {
padding: 0;
li {
margin-bottom: 0.5rem;
font-size: 0.875rem;
}
}
}

View file

@ -150,7 +150,7 @@ export default {
return glass;
},
handleAnswerUpdates(answer, glassKey, vm) {
handleCompletedQuestionChainAnswers(answer, glassKey, vm) {
// 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
// (does not get run for each invididual question's answer, only when
@ -176,51 +176,39 @@ export default {
self.selectedAnswers = {};
// loop through every answered question on the currently answered glass part
answer.answeredQuestions?.forEach((answeredQuestion) => {
answer.answeredQuestions?.forEach((answeredQuestion, answeredQuestionIndex) => {
/* answeredQuestion example format:
{
"questionText": "Is your Grand Cherokee the Laredo model?",
"selectedAnswer": "1|nextQuestion|3|Yes",
"selectedAnswerText": "Yes",
"questionNum": 1,
}
*/
const answeredQuestionText = answeredQuestion.questionText.toUpperCase();
const answeredQuestionAnswer = answeredQuestion.selectedAnswerText.toUpperCase();
const answeredQuestionAnswer = answeredQuestion.selectedAnswer.toUpperCase();
const answeredQuestionAnswerText =
answeredQuestion.selectedAnswerText.toUpperCase();
const answeredQuestionNum = answeredQuestion.questionNum;
// HANDLE DUPLICATE QUESTIONS
// loop through all glass data
self.questionsData.forEach((glass, glassIndex) => {
/* glass 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
}
*/
if (glassIndex === answer.index) {
glass.questions.forEach((question, questionIndex) => {
// clear out any previously set answers on first pass with first answered question
if (answeredQuestionIndex === 0) question.answerSelected = null;
// limit duplicate search to glass pieces that follow after the currently being answered glass piece
if (answeredQuestionNum - 1 === questionIndex) {
// on the right question
question.answerSelected = answeredQuestionAnswer;
}
});
}
// limit duplicate search to glass pieces that are in or follow after the currently being answered glass piece
if (glassIndex > answer.index) {
let indexToSuppressTo;
// reset this glass piece, in case user is changing their previous answers
@ -229,8 +217,8 @@ export default {
// loop through this glass piece's questions, looking for a questionText match
glass.questions.forEach((question, questionIndex) => {
// clear out any previously set answers
question.answerSelected = null;
// clear out any previously set answers on first pass with first answer
if (answeredQuestionIndex === 0) question.answerSelected = null;
// clear or set suppressThisQuestion property for each question
if (indexToSuppressTo && questionIndex + 1 < indexToSuppressTo) {
@ -245,7 +233,9 @@ export default {
// handle matching answer in duplicated question
question.answers.forEach((ans) => {
if (ans.answerText.toUpperCase() === answeredQuestionAnswer) {
if (
ans.answerText.toUpperCase() === answeredQuestionAnswerText
) {
matchedAnswer = ans;
ans.selected = true;
} else {
@ -339,7 +329,7 @@ export default {
glass.isSuppressedPart = true;
}
}
});
}); // DONE looping through glass.questions
// Update key to force re-render of glass piece with duplicate question in case user changes previous related answer in the chain
self.questionsData[glassIndex].key =
@ -469,6 +459,7 @@ export default {
},
// Can't use `this` because navigateForward is also called from quote
navigateBack(vm) {
console.log("VQM = = navigateBack() run ");
const self = vm ?? this;
const partsOrQuestions = (
self.$store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS) ??

View file

@ -410,7 +410,7 @@ describe("vehicle-questions-mixin", () => {
});
});
describe("handleAnswerUpdates", () => {
describe("handleCompletedQuestionChainAnswers", () => {
describe("selectedAnswers", () => {
test("should be cleared to be empty", () => {
// Arrange
@ -419,6 +419,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test duplicate question 1?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
@ -438,7 +439,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
wrapper.vm.handleAnswerUpdates(answer, "", wrapper.vm);
wrapper.vm.handleCompletedQuestionChainAnswers(answer, "", wrapper.vm);
// Assert
expect(wrapper.vm.selectedAnswers).toMatchObject({});
@ -453,6 +454,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test duplicate question 1?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
@ -477,7 +479,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
wrapper.vm.handleAnswerUpdates(answer, "", wrapper.vm);
wrapper.vm.handleCompletedQuestionChainAnswers(answer, "", wrapper.vm);
// Assert
expect(wrapper.vm.questionsData[1].answerData).toEqual(null);
@ -489,6 +491,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test duplicate question 1?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
@ -514,7 +517,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
wrapper.vm.handleAnswerUpdates(answer, "", wrapper.vm);
wrapper.vm.handleCompletedQuestionChainAnswers(answer, "", wrapper.vm);
// Assert
expect(wrapper.vm.questionsData[1].isSuppressedPart).toEqual(null);
@ -526,6 +529,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test duplicate question 1?",
selectedAnswer: "1|nextQuestion|3|Yes",
selectedAnswerText: "Yes",
questionNum: 1,
},
@ -587,7 +591,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
wrapper.vm.handleAnswerUpdates(answer, "", wrapper.vm);
wrapper.vm.handleCompletedQuestionChainAnswers(answer, "", wrapper.vm);
// Assert
expect(wrapper.vm.questionsData[1].questions[0].answerSelected).toEqual(null);
@ -603,6 +607,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test duplicate question 1?",
selectedAnswer: "1|nextQuestion|3|No",
selectedAnswerText: "No",
questionNum: 1,
},
@ -661,7 +666,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
wrapper.vm.handleCompletedQuestionChainAnswers(answerNo, "", wrapper.vm);
const glassWithDuplicate = wrapper.vm.questionsData[1];
// Assert
@ -675,6 +680,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test duplicate question 1?",
selectedAnswer: "1|nextQuestion|3|No",
selectedAnswerText: "No",
questionNum: 1,
},
@ -731,7 +737,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
wrapper.vm.handleCompletedQuestionChainAnswers(answerNo, "", wrapper.vm);
const duplicateQuestion = wrapper.vm.questionsData[1].questions[0];
// Assert
@ -746,6 +752,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test duplicate question 1?",
selectedAnswer: "1|nextQuestion|3|No",
selectedAnswerText: "No",
questionNum: 1,
},
@ -835,7 +842,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
wrapper.vm.handleCompletedQuestionChainAnswers(answerNo, "", wrapper.vm);
const nextQuestionAfterDuplicate = wrapper.vm.questionsData[1].questions[2];
// Assert
@ -849,6 +856,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test question 3?",
selectedAnswer: "1|nextQuestion|3|No",
selectedAnswerText: "No",
questionNum: 1,
},
@ -969,7 +977,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
wrapper.vm.handleCompletedQuestionChainAnswers(answerNo, "", wrapper.vm);
const duplicatedQuestion = wrapper.vm.questionsData[1].questions[2];
const duplicatedQuestionAnswer = duplicatedQuestion.answers.filter((a) => {
return a.selected;
@ -997,6 +1005,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test duplicate question 1?",
selectedAnswer: "1|nextQuestion|3|No",
selectedAnswerText: "No",
questionNum: 1,
},
@ -1085,7 +1094,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
wrapper.vm.handleCompletedQuestionChainAnswers(answerNo, "", wrapper.vm);
// Assert
expect(
@ -1109,6 +1118,7 @@ describe("vehicle-questions-mixin", () => {
answeredQuestions: [
{
questionText: "Test question 3?",
selectedAnswer: "1|nextQuestion|3|No",
selectedAnswerText: "No",
questionNum: 1,
},
@ -1197,7 +1207,7 @@ describe("vehicle-questions-mixin", () => {
];
// Act
await wrapper.vm.handleAnswerUpdates(answerNo, "", wrapper.vm);
await wrapper.vm.handleCompletedQuestionChainAnswers(answerNo, "", wrapper.vm);
const questionsToTest = wrapper.vm.questionsData[1].questions;
const answerLeadingToDuplicate = questionsToTest[0].answers[1];

View file

@ -241,6 +241,22 @@ const routingTable = function (store) {
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_QUESTIONS,
destinationFmgPageValue: fmgPageValues.QUOTE,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_PART_QUESTIONS,
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_MULTIPLE_PARTS_TO_CHOOSE,
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_MOLDING_QUESTIONS,
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS,
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAPABILITY_QUESTIONS,
destinationFmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
},
],
},
{

View file

@ -1435,8 +1435,9 @@ export const actions = {
if (
context.state.order.vehicle.make.toLowerCase() === "ford" &&
context.state.order.vehicle.year >= 2018
)
) {
return true;
}
if (
context.state.order.vehicle.make.toLowerCase() === "bmw" &&