Merge branch 'develop' into feature/CSR-439
This commit is contained in:
commit
159aadb2b4
45 changed files with 2381 additions and 2313 deletions
|
|
@ -11,19 +11,9 @@ module.exports = {
|
|||
"!src/constants/*.js",
|
||||
"!src/router/**/*.js",
|
||||
"!src/helpers/unit-test-helper.js",
|
||||
"!src/layouts/component-test/component-test.vue",
|
||||
"!src/layouts/form-test/form-test.vue",
|
||||
"!src/layouts/vin-lookup/vin-lookup.vue",
|
||||
"!src/layouts/vehicle-damage/windshield-damage-type-question/windshield-damage-type-question.vue",
|
||||
"!src/layouts/vehicle-damage/windshield-options/windshield-options.vue",
|
||||
"!src/layouts/part-questions/**/*.vue",
|
||||
"!src/layouts/reveal/**/*.vue",
|
||||
"!src/layouts/estimate/**/*.vue",
|
||||
// TODO REMOVE THESE AFTER WRITING UNIT TESTS
|
||||
"!src/layouts/address-vehicles/address-vehicles.vue",
|
||||
"!src/layouts/address-vehicles/address-vehicles-question/address-vehicles-question.vue",
|
||||
"!src/ux-components/alert\alert.vue",
|
||||
"!src/helpers/validation-rules.js",
|
||||
"!src/layouts/reveal/**/*.vue"
|
||||
// END
|
||||
], // ! means exclude from coverage.
|
||||
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
||||
|
|
|
|||
|
|
@ -112,7 +112,8 @@ describe("buttonQuestion.vue", () => {
|
|||
const wrapper = shallowMount(buttonQuestion, setupMocks({}));
|
||||
await wrapper.setProps({
|
||||
answers: ["2022", "2021", "2020"],
|
||||
isMultiSelect: false
|
||||
isMultiSelect: false,
|
||||
modelValue: []
|
||||
});
|
||||
const val = { checkValue: true, value: "2021", }
|
||||
wrapper.vm.handleCheckedChanged(val);
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@
|
|||
data-test="button"
|
||||
:validationRules="validationRules"
|
||||
:class="[suppressError ? 'alertError' : '']"
|
||||
:clearOnUnmount="clearOnUnmount"
|
||||
/>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
@ -84,6 +85,10 @@ export default {
|
|||
validationRules: String,
|
||||
suppressError: Boolean,
|
||||
useTextForValue: Boolean,
|
||||
clearOnUnmount: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getFieldSetClasses() {
|
||||
|
|
@ -133,17 +138,14 @@ export default {
|
|||
return answer.Name ? answer.Name : answer;
|
||||
},
|
||||
handleCheckedChanged(val) {
|
||||
|
||||
if(this.isMultiSelect && this.selectedValues) {
|
||||
// Add or remove item to array of data to emit
|
||||
const newSelectedValues = this.selectedValues;
|
||||
|
||||
if(this.selectingInitiatesLoad) {
|
||||
this.selectedValues = [val.value];
|
||||
} else {
|
||||
if(Array.isArray(this.selectedValues)) {
|
||||
const newSelectedValues = this.selectedValues;
|
||||
val.checkValue ? newSelectedValues.push(val.value) : newSelectedValues.splice(newSelectedValues.indexOf(val.value), 1);
|
||||
this.selectedValues = newSelectedValues;
|
||||
}
|
||||
} else {
|
||||
this.selectedValues = [val.value];
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
@ -179,4 +181,22 @@ export default {
|
|||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.vehicle-parts {
|
||||
.question-text {
|
||||
span {
|
||||
font-size: .875rem;
|
||||
text-align: left;
|
||||
margin: 0 0 .5rem 0;
|
||||
}
|
||||
}
|
||||
.question-text {
|
||||
margin: 0;
|
||||
}
|
||||
fieldset {
|
||||
.ui-radio {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -29,20 +29,22 @@ export default {
|
|||
name: "FunnelSubHeader",
|
||||
props: {
|
||||
hasBackButton: Boolean,
|
||||
backButtonAccessibleText: String,
|
||||
cmsWidgetName: String,
|
||||
},
|
||||
components: {
|
||||
buttonBack,
|
||||
},
|
||||
computed: {
|
||||
text(){
|
||||
text() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderText');
|
||||
},
|
||||
subText(){
|
||||
subText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'HeaderSubText');
|
||||
},
|
||||
headerColor(){
|
||||
backButtonAccessibleText() {
|
||||
return this.getCmsContent(this.cmsWidgetName, 'BackButtonAccessibleText');
|
||||
},
|
||||
headerColor() {
|
||||
return this.subText ? 'dark-header' : 'light-header';
|
||||
}
|
||||
},
|
||||
|
|
|
|||
241
src/common-components/question-chain/question-chain.spec.js
Normal file
241
src/common-components/question-chain/question-chain.spec.js
Normal file
|
|
@ -0,0 +1,241 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import questionChain from "@/common-components/question-chain/question-chain.vue";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import store from "@/store";
|
||||
jest.mock("@/store",()=>{return{};},{virtual:true});
|
||||
|
||||
describe("Question Chain component", () => {
|
||||
|
||||
it("Should not emit a modelValue change when setting selectedValue if isNewModelValueComplete is false", () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({ modelValueProp: [] });
|
||||
wrapper.vm.currentQuestion = 6;
|
||||
const answerReturned = [wrapper.vm.questions[wrapper.vm.currentQuestion].answers[0].Name.toString()];
|
||||
const localThis = {
|
||||
$emit: jest.fn(),
|
||||
getNewModelValue: jest.fn(() => { return false })
|
||||
}
|
||||
|
||||
//Act
|
||||
questionChain.computed.selectedValue.set.call(localThis, answerReturned);
|
||||
|
||||
//Assert
|
||||
expect(localThis.$emit).not.toBeCalled();
|
||||
});
|
||||
|
||||
it("Should emit a modelValue change when setting selectedValue if isNewModelValueComplete is true", () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({ modelValueProp: [] });
|
||||
wrapper.vm.currentQuestion = 6;
|
||||
const answerReturned = [wrapper.vm.questions[wrapper.vm.currentQuestion].answers[0].Name.toString()];
|
||||
const localThis = {
|
||||
$emit: jest.fn(),
|
||||
getNewModelValue: jest.fn(() => { return true })
|
||||
}
|
||||
|
||||
//Act
|
||||
questionChain.computed.selectedValue.set.call(localThis, answerReturned);
|
||||
|
||||
//Assert
|
||||
expect(localThis.$emit).toBeCalledWith("update:modelValue", true);
|
||||
});
|
||||
|
||||
it("should return false if no returned answer is given", () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({ modelValueProp: [] });
|
||||
wrapper.vm.currentQuestion = 1;
|
||||
const answerReturned = null;
|
||||
|
||||
//Act
|
||||
const result = wrapper.vm.getNewModelValue(answerReturned);
|
||||
|
||||
//Assert
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return false if the user's answer on the current question leads to another question", () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({ modelValueProp: [] });
|
||||
wrapper.vm.currentQuestion = 1;
|
||||
const answerReturned = [wrapper.vm.questions[wrapper.vm.currentQuestion].answers[0].Name.toString()];
|
||||
|
||||
//Act
|
||||
const result = wrapper.vm.getNewModelValue(answerReturned);
|
||||
|
||||
//Assert
|
||||
expect(result).toEqual(false);
|
||||
});
|
||||
|
||||
it("should return an object with the final answer if the user's answer on the current question is a part number", () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({ modelValueProp: [] });
|
||||
wrapper.vm.currentQuestion = 6;
|
||||
const answerReturned = [wrapper.vm.questions[wrapper.vm.currentQuestion].answers[0].Name.toString()];
|
||||
|
||||
//Act
|
||||
const result = wrapper.vm.getNewModelValue(answerReturned);
|
||||
|
||||
//Assert
|
||||
expect(result).toEqual(
|
||||
{
|
||||
answerResult: 'DW02102',
|
||||
answeredQuestions: [
|
||||
{
|
||||
questionText: 'Is your vehicle equipped with heated seats?',
|
||||
selectedAnswerText: 'Yes'
|
||||
}
|
||||
]
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function setupMocks({
|
||||
modelValueProp = "",
|
||||
questionDataProp = {
|
||||
"glassName": "Single",
|
||||
"glassLocation": "Windshield",
|
||||
"parts": null,
|
||||
"partQuestions": [
|
||||
{
|
||||
"questionSequence": 1,
|
||||
"questionText": "Is your Cherokee the Overland edition which can be identified by having a wood and leather wrapped steering wheel?",
|
||||
"answers": [
|
||||
{
|
||||
"answerText": "Yes",
|
||||
"nextQuestionSequence": 2,
|
||||
"answerResult": ""
|
||||
},
|
||||
{
|
||||
"answerText": "No",
|
||||
"nextQuestionSequence": 3,
|
||||
"answerResult": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"questionSequence": 2,
|
||||
"questionText": "Is your vehicle equipped with rain sensing wipers that adjust their speed automatically when it rains?",
|
||||
"answers": [
|
||||
{
|
||||
"answerText": "Yes",
|
||||
"nextQuestionSequence": null,
|
||||
"answerResult": "DW02270"
|
||||
},
|
||||
{
|
||||
"answerText": "No",
|
||||
"nextQuestionSequence": null,
|
||||
"answerResult": "DW02264"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"questionSequence": 3,
|
||||
"questionText": "Is your vehicle equipped with rain sensing wipers that adjust their speed automatically when it rains?",
|
||||
"answers": [
|
||||
{
|
||||
"answerText": "Yes",
|
||||
"nextQuestionSequence": null,
|
||||
"answerResult": "DW02268"
|
||||
},
|
||||
{
|
||||
"answerText": "No",
|
||||
"nextQuestionSequence": 4,
|
||||
"answerResult": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"questionSequence": 4,
|
||||
"questionText": "Is your vehicle equipped with automatic climate control which will change the fan speed automatically in order to maintain a set temperature?",
|
||||
"answers": [
|
||||
{
|
||||
"answerText": "Yes",
|
||||
"nextQuestionSequence": 5,
|
||||
"answerResult": ""
|
||||
},
|
||||
{
|
||||
"answerText": "No",
|
||||
"nextQuestionSequence": 6,
|
||||
"answerResult": ""
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"questionSequence": 5,
|
||||
"questionText": "Is your vehicle equipped with heated seats?",
|
||||
"answers": [
|
||||
{
|
||||
"answerText": "Yes",
|
||||
"nextQuestionSequence": null,
|
||||
"answerResult": "DW02104"
|
||||
},
|
||||
{
|
||||
"answerText": "No",
|
||||
"nextQuestionSequence": null,
|
||||
"answerResult": "DW02103"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"questionSequence": 6,
|
||||
"questionText": "Is your vehicle equipped with heated seats?",
|
||||
"answers": [
|
||||
{
|
||||
"answerText": "Yes",
|
||||
"nextQuestionSequence": null,
|
||||
"answerResult": "DW02102"
|
||||
},
|
||||
{
|
||||
"answerText": "No",
|
||||
"nextQuestionSequence": null,
|
||||
"answerResult": "DW02101"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
},
|
||||
methodsToMock = [],
|
||||
}) {
|
||||
|
||||
//Mock store
|
||||
store.dispatch = jest.fn(() => dataFromStoreApi);
|
||||
store.getters = { vehicle: {year: 2019, make: 'honda', model: 'civc', style: '2 Door', category: 'CAR'} };
|
||||
const mountOptions = getMountOptions({
|
||||
store: {
|
||||
dispatch: store.dispatch,
|
||||
getters: store.getters,
|
||||
},
|
||||
});
|
||||
|
||||
//Mock props
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn()
|
||||
}
|
||||
}
|
||||
mountOptions.propsData = {
|
||||
modelValue: modelValueProp,
|
||||
questionData: questionDataProp,
|
||||
};
|
||||
mountOptions.mixins = [mockMixin];
|
||||
|
||||
//Mock methods
|
||||
methodsToMock.forEach((methodName) => {
|
||||
questionChain.methods[methodName] = jest.fn();
|
||||
});
|
||||
|
||||
const wrapper = shallowMount(questionChain, mountOptions);
|
||||
|
||||
//Mock CMS content
|
||||
const cmsContent = {
|
||||
};
|
||||
return { wrapper, cmsContent };
|
||||
}
|
||||
120
src/common-components/question-chain/question-chain.vue
Normal file
120
src/common-components/question-chain/question-chain.vue
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<div v-for="(q, i) in questions" :key="i">
|
||||
<transition appear name="fade" mode="out-in">
|
||||
<buttonQuestion
|
||||
v-if="q.questionSequence === currentQuestion"
|
||||
class="radioQuestion"
|
||||
:questionText="q.questionText"
|
||||
:answers="q.answers"
|
||||
:groupName="`${questionData.glassName}-${questionData.glassLocation}-${i}`"
|
||||
textPosition="text-start"
|
||||
v-model="selectedValue"
|
||||
:isRequired=true
|
||||
:validationRules="validationRules"
|
||||
:clearOnUnmount=false
|
||||
/>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
|
||||
export default {
|
||||
name: "questionChain",
|
||||
data() {
|
||||
return {
|
||||
currentQuestion: 1,
|
||||
answeredQuestions: [],
|
||||
};
|
||||
},
|
||||
props: {
|
||||
questionData: Object,
|
||||
validationRules: String,
|
||||
modelValue: Array,
|
||||
},
|
||||
computed: {
|
||||
|
||||
questions() {
|
||||
console.log("answeredQuestions: ", this.answeredQuestions)
|
||||
const questions = this.questionData.partQuestions.map((q, i) => {
|
||||
return {
|
||||
questionText: q.questionText,
|
||||
questionSequence: q.questionSequence,
|
||||
answers: q.answers.map((a) => {
|
||||
return {
|
||||
Text: a.answerText,
|
||||
// Name will either be nextQuestionSequence or answerResult
|
||||
Name: a.nextQuestionSequence ? a.nextQuestionSequence : "answer-" + a.answerResult,
|
||||
nextQuestionSequence: a.nextQuestionSequence,
|
||||
answerResult: a.answerResult,
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
// add an empty item to be array[0] since we start with 1
|
||||
questions.unshift({ "DeliberatelyBlankObject": "This object has been added as a placeholder only for question #0"});
|
||||
return questions;
|
||||
},
|
||||
selectedValue: {
|
||||
get: function() {
|
||||
return this.modelValue;
|
||||
},
|
||||
set: function(returnedAnswer) {
|
||||
const isNewModelValueComplete = this.getNewModelValue(returnedAnswer);
|
||||
|
||||
if (isNewModelValueComplete) {
|
||||
this.$emit("update:modelValue", isNewModelValueComplete);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNewModelValue(returnedAnswer) {
|
||||
if (!returnedAnswer || !Array.isArray(returnedAnswer)) { return false }
|
||||
const lastAnswer = returnedAnswer[returnedAnswer.length - 1];
|
||||
const currentQuestion = this.questions[this.currentQuestion];
|
||||
|
||||
if (lastAnswer.indexOf("answer-") === 0) {
|
||||
// if it is an answerResult
|
||||
const finalAnswer = lastAnswer.slice(7);
|
||||
|
||||
const currentQuestionSelectedAnswer = currentQuestion.answers.find(
|
||||
({ answerResult }) => answerResult === finalAnswer
|
||||
);
|
||||
|
||||
// add current item to list of answered questions
|
||||
this.answeredQuestions.push(
|
||||
{
|
||||
questionText: currentQuestion.questionText,
|
||||
selectedAnswerText: currentQuestionSelectedAnswer.Text,
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
answerResult: finalAnswer,
|
||||
answeredQuestions: this.answeredQuestions,
|
||||
};
|
||||
} else {
|
||||
const currentQuestionSelectedAnswer = currentQuestion.answers.find(
|
||||
({ nextQuestionSequence }) => nextQuestionSequence === parseInt(lastAnswer)
|
||||
);
|
||||
|
||||
// add current item to list of answered questions
|
||||
this.answeredQuestions.push(
|
||||
{
|
||||
questionText: currentQuestion.questionText,
|
||||
selectedAnswerText: currentQuestionSelectedAnswer.Text,
|
||||
}
|
||||
);
|
||||
|
||||
this.currentQuestion = parseInt(lastAnswer); // update count to display next question
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
buttonQuestion,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -1,35 +0,0 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import textInput from "./text-input";
|
||||
|
||||
describe("text-input.vue", () => {
|
||||
it("Should render a text input", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(textInput, {
|
||||
propsData: {
|
||||
name: "test",
|
||||
label: "unit test label",
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.exists()).toBe(true);
|
||||
});
|
||||
|
||||
it("Should return aria-required state", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(textInput, {
|
||||
propsData: {
|
||||
name: "test",
|
||||
label: "unit test label",
|
||||
isRequired: true,
|
||||
},
|
||||
});
|
||||
|
||||
// Assert
|
||||
const input = wrapper.find("input");
|
||||
|
||||
expect(input.attributes()["aria-required"]).toEqual("true");
|
||||
});
|
||||
});
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
<!-- Simple implementation of an input field -->
|
||||
<template>
|
||||
<div class="d-flex w-50 mb-2" :class="{ 'has-error': !!errorMessage }">
|
||||
<input
|
||||
type="text"
|
||||
:name="name"
|
||||
:value="inputValue"
|
||||
:id="name"
|
||||
:aria-required="isRequired"
|
||||
@input="handleChange"
|
||||
@blur="handleBlur"
|
||||
:data-focus-target="name"
|
||||
/>
|
||||
<label
|
||||
:for="name"
|
||||
:aria-labelledby="name"
|
||||
class="d-flex justify-content-center py-3 px-4"
|
||||
>
|
||||
<span class="m-0">{{ label }}</span>
|
||||
</label>
|
||||
</div>
|
||||
<div v-show="errorMessage" class="row px-3 form-test-error">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { useField } from "vee-validate";
|
||||
|
||||
export default {
|
||||
name: "textInput",
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
default: "text",
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
isRequired: Boolean,
|
||||
},
|
||||
setup(props) {
|
||||
const {
|
||||
value: inputValue,
|
||||
errorMessage,
|
||||
handleBlur,
|
||||
handleChange,
|
||||
meta,
|
||||
} = useField(props.name, undefined, {
|
||||
initialValue: props.value,
|
||||
});
|
||||
|
||||
return {
|
||||
handleChange,
|
||||
handleBlur,
|
||||
errorMessage,
|
||||
inputValue,
|
||||
meta,
|
||||
};
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -167,7 +167,7 @@ describe("textboxQuestion.vue", () => {
|
|||
|
||||
});
|
||||
|
||||
it("Should call this.handleChange with new value when this.semiAggressiveValidation = true, the value is changed, and the new value is valid", async () => {
|
||||
it("Should call this.handleChange with new value when the value is changed and the new value is valid", async () => {
|
||||
// Arrange
|
||||
const wrapper = shallowMount(textboxQuestion, {
|
||||
global: {
|
||||
|
|
@ -178,7 +178,6 @@ describe("textboxQuestion.vue", () => {
|
|||
propsData: {
|
||||
options: {},
|
||||
modelValue: "foo",
|
||||
semiAggressiveValidation: true,
|
||||
},
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
@change="handleChange"
|
||||
@blur="handleChange"
|
||||
:maxlength="maxLength ? maxLength : '999'"
|
||||
@focus="$emit('focus', $event.target.value)"
|
||||
/>
|
||||
<div v-show="errorMessage" class="row my-2 form-test-error">
|
||||
<span class="d-inline-flex mt-0" role="alert">{{ errorMessage }}</span>
|
||||
|
|
@ -54,7 +55,6 @@ export default {
|
|||
default: "",
|
||||
},
|
||||
validationRules: String,
|
||||
semiAggressiveValidation: Boolean,
|
||||
cmsWidgetName: String,
|
||||
maxLength: String,
|
||||
},
|
||||
|
|
@ -129,12 +129,10 @@ export default {
|
|||
},
|
||||
watch: {
|
||||
async value(newValue) {
|
||||
if (this.semiAggressiveValidation) {
|
||||
const result = await validate(newValue, this.validationRules); // do a test validation check, without triggering full validation
|
||||
if (result.valid) {
|
||||
this.handleChange(newValue); // trigger full validation on this field only
|
||||
}
|
||||
}
|
||||
const result = await validate(newValue, this.validationRules); // do a test validation check, without triggering full validation
|
||||
if (result.valid) {
|
||||
this.handleChange(newValue); // trigger full validation on this field only
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ const storeMutations = {
|
|||
UPDATE_IS_REPAIR: "updateIsRepair",
|
||||
UPDATE_NUMBER_OF_CHIPS: "updateNumberOfChips",
|
||||
UPDATE_GLASS_TO_REPLACE: "updateGlassToReplace",
|
||||
UPDATE_PARTS: "updateParts",
|
||||
UPDATE_GLASS_PARTS: "updateGlassParts",
|
||||
UPDATE_REGISTRATION_LICENSE_PLATE : "updateRegistrationLicensePlate",
|
||||
UPDATE_REGISTRATION_ADDRESS: "updateRegistrationAddress",
|
||||
UPDATE_REGISTRATION_CITY: "updateRegistrationCity",
|
||||
|
|
@ -40,7 +40,7 @@ const storeMutations = {
|
|||
RESET_VEHICLE_STATE: "resetVehicleState",
|
||||
RESET_DAMAGE_STATE: "resetDamageState",
|
||||
RESET_REGISTRATION_STATE: "resetRegistrationState",
|
||||
RESET_PARTS_STATE: "resetPartsState",
|
||||
RESET_GLASS_PARTS_STATE: "resetGlassPartsState",
|
||||
RESET_STATE: "resetState",
|
||||
|
||||
// OTHER MUTATIONS
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ const tintMap = {
|
|||
{ name: "brown tint, blue shade", src: "Glass-BlueShade-BrownTint.svg" },
|
||||
{ name: "gray tint, blue shade", src: "Glass-BlueShade-GrayTint.svg" },
|
||||
{ name: "green tint, blue shade", src: "Glass-BlueShade-GreenTint.svg" },
|
||||
{ name: "blue shade", src: "Glass-BlueShade-NoTint.svg" },
|
||||
{ name: "clear, blue shade", src: "Glass-BlueShade-NoTint.svg" },
|
||||
|
||||
// Brown Shade
|
||||
{ name: "brown tint, brown shade", src: "Glass-BrownShade-BrownTint.svg" },
|
||||
|
|
@ -26,13 +26,13 @@ const tintMap = {
|
|||
{ name: "green tint, green shade", src: "Glass-GreenShade-GreenTint.svg" },
|
||||
|
||||
// Tints Only
|
||||
{ name: "blue tint", src: "Glass-NoShade-BlueTint.svg" },
|
||||
{ name: "brown tint", src: "Glass-NoShade-BrownTint.svg" },
|
||||
{ name: "blue tint privacy", src: "Glass-NoShade-BlueTint.svg" },
|
||||
{ name: "bronze tint", src: "Glass-NoShade-BrownTint.svg" },
|
||||
{ name: "dark brown tint", src: "Glass-NoShade-DarkBrownTint.svg" },
|
||||
{ name: "dark gray tint", src: "Glass-NoShade-DarkGrayTint.svg" },
|
||||
{ name: "privacy, black frame", src: "Glass-NoShade-Privacy.svg" },
|
||||
{ name: "gray tint", src: "Glass-NoShade-GrayTint.svg" },
|
||||
{ name: "green tint", src: "Glass-NoShade-GreenTint.svg" },
|
||||
{ name: "gray tint privacy", src: "Glass-NoShade-Privacy.svg" },
|
||||
{ name: "gray tint privacy", src: "Glass-NoShade-GrayTint.svg" },
|
||||
|
||||
// No shade or tint
|
||||
{ name: "clear", src: "Glass-NoShade-NoTint.svg" }
|
||||
|
|
@ -41,13 +41,13 @@ const tintMap = {
|
|||
windshield: [
|
||||
// Blue Shade
|
||||
{ name: "blue tint, blue shade", src: "Windshield-BlueShade-BlueTint.svg" },
|
||||
{ name: "brown tint, blue shade", src: "Windshield-BlueShade-BrownTint.svg" },
|
||||
{ name: "bronze tint, blue shade", src: "Windshield-BlueShade-BrownTint.svg" },
|
||||
{ name: "gray tint, blue shade", src: "Windshield-BlueShade-GrayTint.svg" },
|
||||
{ name: "green tint, blue shade", src: "Windshield-BlueShade-GreenTint.svg" },
|
||||
{ name: "blue shade", src: "Windshield-BlueShade-NoTint.svg" },
|
||||
{ name: "clear, blue shade", src: "Windshield-BlueShade-NoTint.svg" },
|
||||
|
||||
// Brown Shade
|
||||
{ name: "brown tint, brown shade", src: "Windshield-BrownShade-BrownTint.svg" },
|
||||
{ name: "bronze tint, bronze shade", src: "Windshield-BrownShade-BrownTint.svg" },
|
||||
|
||||
// Gray Shade
|
||||
{ name: "blue tint, gray shade", src: "Windshield-GrayShade-BlueTint.svg" },
|
||||
|
|
@ -62,12 +62,12 @@ const tintMap = {
|
|||
|
||||
// Tints Only
|
||||
{ name: "blue tint", src: "Windshield-NoShade-BlueTint.svg" },
|
||||
{ name: "brown tint", src: "Windshield-NoShade-BrownTint.svg" },
|
||||
{ name: "bronze tint", src: "Windshield-NoShade-BrownTint.svg" },
|
||||
{ name: "dark brown tint", src: "Windshield-NoShade-DarkBrownTint.svg" },
|
||||
{ name: "dark gray tint", src: "Windshield-NoShade-DarkGrayTint.svg" },
|
||||
{ name: "privacy, black frame", src: "Windshield-NoShade-Privacy.svg" },
|
||||
{ name: "gray tint", src: "Windshield-NoShade-GrayTint.svg" },
|
||||
{ name: "green tint", src: "Windshield-NoShade-GreenTint.svg" },
|
||||
{ name: "gray tint privacy", src: "Windshield-NoShade-Privacy.svg" },
|
||||
{ name: "gray tint privacy", src: "Windshield-NoShade-GrayTint.svg" },
|
||||
|
||||
// No shade or tint
|
||||
{ name: "clear", src: "Windshield-NoShade-NoTint.svg" },
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
import addressLookup from "@/layouts/address-lookup/address-lookup.vue";
|
||||
|
||||
// Supporting Files
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
|
|
@ -32,7 +31,7 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: false
|
||||
});
|
||||
|
||||
|
|
@ -48,9 +47,9 @@ describe("address-lookup.vue", () => {
|
|||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
expect(wrapper.findComponent({ ref: "alertNonServiceableZip" }).isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
test("if the address matches a different vehicle display the Matched Different VehicleAlert", async () => {
|
||||
// Arrange
|
||||
const mockRegistrationAddress = {
|
||||
|
|
@ -60,7 +59,7 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true
|
||||
});
|
||||
|
||||
|
|
@ -76,8 +75,8 @@ describe("address-lookup.vue", () => {
|
|||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.findComponent({ ref: "alertMatchedDifferentVehicle" }).isVisible()).toBe(true);
|
||||
});
|
||||
expect(wrapper.findComponent({ ref: "alertMatchedDifferentVehicle" }).isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
test("if the looking up VIN by address is not allowed in the state selected display the Vin Lookup By HomeAddress Not Allowed Alert", async () => {
|
||||
// Arrange
|
||||
|
|
@ -88,41 +87,27 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
isZipServiceable: true
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true,
|
||||
lookupVinbyAddressResponse: {
|
||||
isStatePermissible: false,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "CARID");
|
||||
|
||||
baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
baseMixin.methods.dispatchStoreAction.mockImplementation((actionName, value) => {
|
||||
let data = {};
|
||||
if (actionName == storeActions.VALIDATE_ZIP) {
|
||||
data = {
|
||||
isServiceable: true
|
||||
}
|
||||
}
|
||||
else if (actionName == storeActions.LOOKUP_VIN_BY_ADDRESS) {
|
||||
data = {
|
||||
isStatePermissible: false,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve({ data });
|
||||
})
|
||||
|
||||
await wrapper.setData({
|
||||
customerQuestions: {
|
||||
addressQuestions: mockRegistrationAddress
|
||||
|
|
@ -133,7 +118,7 @@ describe("address-lookup.vue", () => {
|
|||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.findComponent({ ref: "alertVinLookupsByHomeAddressNotAllowed" }).isVisible()).toBe(true);
|
||||
expect(wrapper.findComponent({ ref: "alertVinLookupsByHomeAddressNotAllowed" }).isVisible()).toBe(true);
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -146,31 +131,16 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
isZipServiceable: true
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true,
|
||||
lookupVinbyAddressResponse: {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [] // Return no vehicles
|
||||
}
|
||||
});
|
||||
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "CARID");
|
||||
|
||||
baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
baseMixin.methods.dispatchStoreAction.mockImplementation((actionName, value) => {
|
||||
let data = {};
|
||||
if (actionName == storeActions.VALIDATE_ZIP) {
|
||||
data = {
|
||||
isServiceable: true
|
||||
}
|
||||
}
|
||||
else if (actionName == storeActions.LOOKUP_VIN_BY_ADDRESS) {
|
||||
data = {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [] // Return no vehicles
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve({ data });
|
||||
|
||||
})
|
||||
|
||||
await wrapper.setData({
|
||||
customerQuestions: {
|
||||
addressQuestions: mockRegistrationAddress
|
||||
|
|
@ -183,21 +153,21 @@ describe("address-lookup.vue", () => {
|
|||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.findComponent({ ref: "alertVinNotFound" }).isVisible()).toBe(true);
|
||||
expect(wrapper.findComponent({ ref: "alertVinNotFound" }).isVisible()).toBe(true);
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("navigation", () => {
|
||||
|
||||
|
||||
test("if the back button is clicked, navigate back", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.backButtonAction();
|
||||
await wrapper.vm.backButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
|
||||
|
|
@ -213,41 +183,27 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
isZipServiceable: true
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true,
|
||||
lookupVinbyAddressResponse: {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "CARID");
|
||||
|
||||
baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
baseMixin.methods.dispatchStoreAction.mockImplementation((actionName, value) => {
|
||||
let data = {};
|
||||
if (actionName == storeActions.VALIDATE_ZIP) {
|
||||
data = {
|
||||
isServiceable: true
|
||||
}
|
||||
}
|
||||
else if (actionName == storeActions.LOOKUP_VIN_BY_ADDRESS) {
|
||||
data = {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve({ data });
|
||||
})
|
||||
|
||||
await wrapper.setData({
|
||||
customerQuestions: {
|
||||
addressQuestions: mockRegistrationAddress
|
||||
|
|
@ -261,7 +217,7 @@ describe("address-lookup.vue", () => {
|
|||
|
||||
// Assert
|
||||
expect(wrapper.vm.navigateForward).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
test("if the car entered matches one of multiple vehicles found, update vehicle info and navigate to the heritage funnel", async () => {
|
||||
// Arrange
|
||||
|
|
@ -272,42 +228,27 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
isZipServiceable: true
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true,
|
||||
lookupVinbyAddressResponse: {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "CARID");
|
||||
|
||||
baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
baseMixin.methods.dispatchStoreAction.mockImplementation((actionName, value) => {
|
||||
let data = {};
|
||||
if (actionName == storeActions.VALIDATE_ZIP) {
|
||||
data = {
|
||||
isServiceable: true
|
||||
}
|
||||
}
|
||||
else if (actionName == storeActions.LOOKUP_VIN_BY_ADDRESS) {
|
||||
data = {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve({ data });
|
||||
|
||||
})
|
||||
|
||||
await wrapper.setData({
|
||||
customerQuestions: {
|
||||
addressQuestions: mockRegistrationAddress
|
||||
|
|
@ -322,8 +263,7 @@ describe("address-lookup.vue", () => {
|
|||
// Assert
|
||||
expect(wrapper.vm.updateVehicleInfo).toHaveBeenCalled();
|
||||
expect(navigateAfterSaveToHeritageFunnel).toHaveBeenCalled();
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
test("if the car entered does not match any of the multiple vehicles found, navigate to address-vehicles page", async () => {
|
||||
// Arrange
|
||||
|
|
@ -334,54 +274,39 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
isZipServiceable: true
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true,
|
||||
lookupVinbyAddressResponse: {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "CARID_A");
|
||||
|
||||
baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
baseMixin.methods.dispatchStoreAction.mockImplementation((actionName, value) => {
|
||||
let data = {};
|
||||
if (actionName == storeActions.VALIDATE_ZIP) {
|
||||
data = {
|
||||
isServiceable: true
|
||||
}
|
||||
}
|
||||
else if (actionName == storeActions.LOOKUP_VIN_BY_ADDRESS) {
|
||||
data = {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve({ data });
|
||||
|
||||
})
|
||||
|
||||
const carsFound = [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
|
||||
await wrapper.setData({
|
||||
customerQuestions: {
|
||||
|
|
@ -396,8 +321,7 @@ describe("address-lookup.vue", () => {
|
|||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalledWith(navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES, undefined, {}, {}, carsFound);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
test("if the car entered matches one of the vehicles found but the zip is NOT serviceable, do not navigate forward", async () => {
|
||||
// Arrange
|
||||
|
|
@ -408,42 +332,27 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
isZipServiceable: true
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: false,
|
||||
lookupVinbyAddressResponse: {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "CARID");
|
||||
|
||||
baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
baseMixin.methods.dispatchStoreAction.mockImplementation((actionName, value) => {
|
||||
let data = {};
|
||||
if (actionName == storeActions.VALIDATE_ZIP) {
|
||||
data = {
|
||||
isServiceable: false
|
||||
}
|
||||
}
|
||||
else if (actionName == storeActions.LOOKUP_VIN_BY_ADDRESS) {
|
||||
data = {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve({ data });
|
||||
|
||||
})
|
||||
|
||||
await wrapper.setData({
|
||||
customerQuestions: {
|
||||
addressQuestions: mockRegistrationAddress
|
||||
|
|
@ -458,7 +367,7 @@ describe("address-lookup.vue", () => {
|
|||
// Assert
|
||||
expect(wrapper.vm.navigateForward).toHaveBeenCalledTimes(0);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
test("if a different vehicle is found than the one entered and the selected glass is not available for that vehicle, navigate back to vehicle-damage page", async () => {
|
||||
// Arrange
|
||||
|
|
@ -469,31 +378,21 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
isZipServiceable: true
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true,
|
||||
lookupVinbyAddressResponse: {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "CARID");
|
||||
|
||||
baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
baseMixin.methods.dispatchStoreAction.mockImplementation((actionName, value) => {
|
||||
let data = {};
|
||||
if (actionName == storeActions.LOOKUP_VIN_BY_ADDRESS) {
|
||||
data = {
|
||||
isStatePermissible: true,
|
||||
vinVehicles: [{
|
||||
vin: "TEST_VIN",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.resolve({ data });
|
||||
|
||||
})
|
||||
|
||||
await wrapper.setData({
|
||||
customerQuestions: {
|
||||
addressQuestions: mockRegistrationAddress
|
||||
|
|
@ -520,18 +419,79 @@ describe("address-lookup.vue", () => {
|
|||
await wrapper.vm.navigateForward(carEntered, carsFound);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalledWith(navigationScenarios.CONTINUING_WITH_DIFFERENT_GLASS, undefined, {}, {"displayVehicleChangeAlert": true}, {});
|
||||
expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, undefined, {}, { "displayVehicleChangeAlert": true }, {});
|
||||
|
||||
});
|
||||
|
||||
test("single car was found and matches entered vehicle => navigateForwardWithSingleCarMatch", async () => {
|
||||
// Arrange
|
||||
const carEntered = {
|
||||
carId: "CARID2"
|
||||
};
|
||||
|
||||
const carsFound = [
|
||||
{
|
||||
vin: "TEST_VIN_2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const { wrapper } = setupMocks({}, {});
|
||||
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||
|
||||
// Act
|
||||
wrapper.vm.navigateForward(carEntered, carsFound);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("multiple cars were found and one matches entered vehicle => navigateForwardWithSingleCarMatch", async () => {
|
||||
// Arrange
|
||||
const carEntered = {
|
||||
carId: "CARID2"
|
||||
};
|
||||
|
||||
const carsFound = [
|
||||
{
|
||||
vin: "TEST_VIN_1",
|
||||
vehicle: {
|
||||
carId: "CARID1"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN_2",
|
||||
vehicle: {
|
||||
carId: "CARID2"
|
||||
}
|
||||
},
|
||||
{
|
||||
vin: "TEST_VIN_3",
|
||||
vehicle: {
|
||||
carId: "CARID3"
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
const { wrapper } = setupMocks({}, {});
|
||||
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||
|
||||
// Act
|
||||
wrapper.vm.navigateForward(carEntered, carsFound);
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("reseting dependent state", () => {
|
||||
describe("resetting dependent state", () => {
|
||||
test("when reseting dependent state, license plate is set to null and parts state and dependencies are reset", async () => {
|
||||
// Arrange
|
||||
const commitSpy = jest.spyOn(store, "commit");
|
||||
const dispatchSpy = jest.spyOn(store, "dispatch");
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true
|
||||
});
|
||||
|
||||
|
|
@ -544,7 +504,7 @@ describe("address-lookup.vue", () => {
|
|||
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe("registration and service zips", () => {
|
||||
describe("if registration zip is serviceable", () => {
|
||||
test("if registration address is provided => update service address on successful continue", async () => {
|
||||
|
|
@ -556,7 +516,7 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup,{
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: true
|
||||
});
|
||||
|
||||
|
|
@ -572,7 +532,7 @@ describe("address-lookup.vue", () => {
|
|||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalledWith(storeActions.UPDATE_SERVICE_LOCATION_WITH_VEHICLE_REGISTRATION);
|
||||
expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalledWith(storeActions.UPDATE_SERVICE_LOCATION_WITH_VEHICLE_REGISTRATION);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -586,7 +546,7 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: false
|
||||
});
|
||||
|
||||
|
|
@ -618,9 +578,9 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
isZipServiceable: false
|
||||
}
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: false
|
||||
}
|
||||
);
|
||||
|
||||
expect(wrapper.vm.showServiceZipField).toBeFalsy();
|
||||
|
|
@ -650,9 +610,9 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {
|
||||
isZipServiceable: false
|
||||
}
|
||||
const { wrapper } = setupMocks({
|
||||
isZipServiceable: false
|
||||
}
|
||||
);
|
||||
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "CARID");
|
||||
|
|
@ -667,7 +627,7 @@ describe("address-lookup.vue", () => {
|
|||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(baseMixin.methods.dispatchStoreAction).not.toHaveBeenCalledWith(storeActions.UPDATE_SERVICE_LOCATION_WITH_VEHICLE_REGISTRATION);
|
||||
expect(wrapper.vm.dispatchStoreAction).not.toHaveBeenCalledWith(storeActions.UPDATE_SERVICE_LOCATION_WITH_VEHICLE_REGISTRATION);
|
||||
});
|
||||
|
||||
test("if registration address, service zip are provided, and user clicks continue => both zips are saved and are different", async () => {
|
||||
|
|
@ -679,12 +639,13 @@ describe("address-lookup.vue", () => {
|
|||
zipCode: "43215"
|
||||
}
|
||||
|
||||
const { wrapper } = setupMocks(addressLookup, {});
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "CARID");
|
||||
|
||||
baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
baseMixin.methods.dispatchStoreAction.mockImplementation((actionName, value) => {
|
||||
wrapper.vm.dispatchStoreAction = jest.fn();
|
||||
wrapper.vm.dispatchStoreAction.mockImplementation((actionName, value) => {
|
||||
let data = {};
|
||||
if (actionName == storeActions.VALIDATE_ZIP) {
|
||||
if (value == "43215") {
|
||||
|
|
@ -718,15 +679,16 @@ describe("address-lookup.vue", () => {
|
|||
addressQuestions: mockRegistrationAddress
|
||||
}
|
||||
})
|
||||
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
await wrapper.setData({
|
||||
serviceZipCode: "12345"
|
||||
})
|
||||
|
||||
// Act
|
||||
// // Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
// // Assert
|
||||
expect(store.getters.order.serviceLocation.zipCode).not.toEqual(store.getters.vehicle.registration.zipCode);
|
||||
expect(store.getters.vehicle.registration.zipCode).toEqual("43215");
|
||||
expect(store.getters.order.serviceLocation.zipCode).toEqual("12345");
|
||||
|
|
@ -735,10 +697,9 @@ describe("address-lookup.vue", () => {
|
|||
});
|
||||
});
|
||||
|
||||
function setupMocks(mountOptions, { isZipServiceable = true, lookupVinbyAddressResponse }) {
|
||||
function setupMocks({ isZipServiceable = true, lookupVinbyAddressResponse, partsOrQuestions = [] }) {
|
||||
store.commit(storeMutations.RESET_STATE);
|
||||
const wrapper = shallowMount(addressLookup, getMountOptions({
|
||||
...mountOptions,
|
||||
actionList: [
|
||||
{
|
||||
actionName: storeActions.VALIDATE_ZIP,
|
||||
|
|
@ -757,7 +718,13 @@ function setupMocks(mountOptions, { isZipServiceable = true, lookupVinbyAddressR
|
|||
}
|
||||
}]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
actionName: storeActions.GET_PARTS_OR_QUESTIONS,
|
||||
data: {
|
||||
partsOrQuestions: partsOrQuestions
|
||||
}
|
||||
},
|
||||
],
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
|
|
@ -766,12 +733,11 @@ function setupMocks(mountOptions, { isZipServiceable = true, lookupVinbyAddressR
|
|||
}));
|
||||
|
||||
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");
|
||||
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
||||
wrapper.vm.setCmsContent = jest.fn();
|
||||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
|
||||
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
|
||||
|
||||
return { wrapper };
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,15 +82,15 @@ import { settleAllPromises } from "@/helpers/layout-helper";
|
|||
import store from "@/store";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { navigateAfterSaveToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
import { getDamageString, isGlassAvailableForCarId } from "@/helpers/damage-helper";
|
||||
import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||
|
||||
defineRule("service-zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
||||
defineRule("service-zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
|
||||
|
||||
export default {
|
||||
name: "address-lookup",
|
||||
mixins: [vinPagesMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||
|
|
@ -291,26 +291,23 @@ export default {
|
|||
this.$router.navigate(
|
||||
// then navigate back to "vehicle-damage", and display vehicle changed alert
|
||||
// on that page
|
||||
this.navigationScenarios.CONTINUING_WITH_DIFFERENT_GLASS,
|
||||
this.navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS,
|
||||
this.$route, {}, {
|
||||
displayVehicleChangeAlert: true
|
||||
}, {}
|
||||
);
|
||||
} else {
|
||||
// otherwise
|
||||
this.$refs.loadingModal.showModal();
|
||||
navigateAfterSaveToHeritageFunnel(this.$route);
|
||||
this.navigateForwardWithSingleCarMatch();
|
||||
}
|
||||
} else if (carsFound.length > 1) {
|
||||
// if multiple cars were found
|
||||
let matchingCars = carsFound.filter(car => car.vehicle.carId === carEntered.carId);
|
||||
if (matchingCars.length === 1) {
|
||||
this.$refs.loadingModal.showModal();
|
||||
|
||||
// and one and only of them matches the car id entered
|
||||
const matchingCar = matchingCars[0];
|
||||
this.updateVehicleInfo(matchingCar.vin, matchingCar.vehicle);
|
||||
navigateAfterSaveToHeritageFunnel(this.$route);
|
||||
this.navigateForwardWithSingleCarMatch();
|
||||
} else {
|
||||
// if there are no matches or there are multiple matches, navigate to "address-vehicles" page
|
||||
this.$router.navigate(this.navigationScenarios.CONTINUING_WITH_MULTIPLE_VEHICLES, this.$route, {}, {}, carsFound);
|
||||
|
|
@ -319,18 +316,18 @@ export default {
|
|||
|
||||
},
|
||||
validateZip(zip) {
|
||||
return baseMixin.methods.dispatchStoreAction(
|
||||
return this.dispatchStoreAction(
|
||||
storeActions.VALIDATE_ZIP,
|
||||
{ zip });
|
||||
},
|
||||
lookupVin(lastName, streetAddress, zip, state) {
|
||||
return baseMixin.methods.dispatchStoreAction(
|
||||
return this.dispatchStoreAction(
|
||||
storeActions.LOOKUP_VIN_BY_ADDRESS,
|
||||
{
|
||||
licenseLastName: lastName,
|
||||
licenseStreetAddress: streetAddress,
|
||||
licenseZip: zip,
|
||||
licenseState: state,
|
||||
licenseZip: zip,
|
||||
licenseState: state
|
||||
}, false
|
||||
);
|
||||
},
|
||||
|
|
@ -361,9 +358,7 @@ export default {
|
|||
const serviceLocation = store.getters.order.serviceLocation;
|
||||
|
||||
if (!serviceLocation.address && serviceLocation.zipCode && serviceLocation.zipCode == store.getters.vehicle.registration.zipCode) {
|
||||
baseMixin.methods.dispatchStoreAction(
|
||||
storeActions.UPDATE_SERVICE_LOCATION_WITH_VEHICLE_REGISTRATION
|
||||
);
|
||||
this.dispatchStoreAction(storeActions.UPDATE_SERVICE_LOCATION_WITH_VEHICLE_REGISTRATION);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
inputId="cbf28188fdf2436688fd735915f7ee56"
|
||||
disableAutoFill
|
||||
validationRules="city-required"
|
||||
semiAggressiveValidation
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -52,7 +51,6 @@
|
|||
mask="#####"
|
||||
disableAutoFill
|
||||
validationRules="zip-code-required|zip-code-format"
|
||||
semiAggressiveValidation
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@
|
|||
inputId="00450a91b8964a768ce3992e6feb890f"
|
||||
disableAutoFill
|
||||
validationRules="email-address-required|email-address-format"
|
||||
semiAggressiveValidation
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import addressVehiclesQuestion from "@/layouts/address-vehicles/address-vehicles-question/address-vehicles-question";
|
||||
|
||||
|
||||
describe("addressVehiclesQuestion.vue", () => {
|
||||
|
||||
it("Should return content for differentVehicleAlertHeader", () => {
|
||||
|
|
@ -40,7 +39,7 @@ describe("addressVehiclesQuestion.vue", () => {
|
|||
|
||||
// Act
|
||||
const localThis = { $emit: jest.fn() }
|
||||
addressVehiclesQuestion.computed.selectedVehicleVin.set.call(localThis, 'newValue');
|
||||
addressVehiclesQuestion.computed.selectedVehicleVinAsArray.set.call(localThis, ['newValue']);
|
||||
|
||||
// Assert
|
||||
expect(localThis.$emit).toBeCalledWith("update:modelValue", "newValue");
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
groupName="ChooseAddressVehicle"
|
||||
:questionText="questionText"
|
||||
:answers="vehicles"
|
||||
v-model="selectedVehicleVin"
|
||||
v-model="selectedVehicleVinAsArray"
|
||||
isRequired=true
|
||||
:validation-rules="validationRules"
|
||||
/>
|
||||
|
|
@ -62,16 +62,18 @@ export default {
|
|||
questionText() {
|
||||
return this.getCmsContent("VehicleConfirmationQuestion", "QuestionText");
|
||||
},
|
||||
selectedVehicleVin: {
|
||||
selectedVehicleVinAsArray: {
|
||||
get: function() {
|
||||
return this.modelValue;
|
||||
const modelValueAsArray = this.modelValue ? [this.modelValue] : [];
|
||||
return modelValueAsArray;
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
const newValueAsScalar = newValue && newValue.length > 0 ? newValue[newValue.length-1] : null;
|
||||
this.$emit("update:modelValue", newValueAsScalar);
|
||||
}
|
||||
},
|
||||
selectedVehicle() {
|
||||
return this.vehicles.find( ({ vin }) => vin === this.selectedVehicleVin[0] );
|
||||
selectedVehicle() { // this computed is only needed for the computed differentVehicleAlertBody text above
|
||||
return this.vehicles.find( ({ vin }) => vin === this.selectedVehicleVinAsArray[this.selectedVehicleVinAsArray.length-1] );
|
||||
},
|
||||
},
|
||||
components: {
|
||||
|
|
|
|||
|
|
@ -5,9 +5,10 @@ import addressVehicles from "@/layouts/address-vehicles/address-vehicles";
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import store from "@/store";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import * as navigateToHeritage from "@/helpers/heritage-integration/navigation-helper";
|
||||
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock("@/helpers/damage-helper", () => ({
|
||||
isGlassAvailableForCarId: () => {
|
||||
|
|
@ -17,11 +18,12 @@ jest.mock("@/helpers/damage-helper", () => ({
|
|||
|
||||
|
||||
describe("addressVehicles.vue", () => {
|
||||
|
||||
test("Should return true for valid page requisites if carId / zipCode / emailAddress / pageData exists", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.$router.navigate = jest.fn();
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, "NOT NULL");
|
||||
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_ZIP_CODE, "12345");
|
||||
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, "test@test.com");
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
|
@ -35,10 +37,9 @@ describe("addressVehicles.vue", () => {
|
|||
test("Should return false for valid page requisites if carId is missing", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.$router.navigate = jest.fn();
|
||||
|
||||
// Act
|
||||
wrapper.vm.$store.getters.order.vehicle.carId = null;
|
||||
store.commit(storeMutations.UPDATE_CAR_ID, null);
|
||||
const result = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
//Assert
|
||||
|
|
@ -55,8 +56,8 @@ describe("addressVehicles.vue", () => {
|
|||
wrapper.vm.$router.navigate = jest.fn();
|
||||
|
||||
// Act
|
||||
wrapper.setData({
|
||||
selectedVehicleVin: ['5NMS3CADXLH233004'],
|
||||
await wrapper.setData({
|
||||
selectedVehicleVin: '5NMS3CADXLH233004',
|
||||
});
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
|
|
@ -85,8 +86,8 @@ describe("addressVehicles.vue", () => {
|
|||
wrapper.vm.navigateForward = jest.fn().mockImplementation(()=> {});
|
||||
|
||||
// Act
|
||||
wrapper.setData({
|
||||
selectedVehicleVin: ['5NMS3CADXLH233004'],
|
||||
await wrapper.setData({
|
||||
selectedVehicleVin: '5NMS3CADXLH233004',
|
||||
});
|
||||
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
|
@ -115,8 +116,8 @@ describe("addressVehicles.vue", () => {
|
|||
wrapper.vm.updateCustomerInfo = jest.fn().mockImplementation(()=> {});
|
||||
|
||||
// Act
|
||||
wrapper.setData({
|
||||
selectedVehicleVin: ['5NMS3CADXLH233004'],
|
||||
await wrapper.setData({
|
||||
selectedVehicleVin: '5NMS3CADXLH233004',
|
||||
});
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
wrapper.vm.$nextTick();
|
||||
|
|
@ -142,15 +143,15 @@ describe("addressVehicles.vue", () => {
|
|||
wrapper.vm.$router.navigateAfterSave = jest.fn();
|
||||
|
||||
// Act
|
||||
wrapper.setData({
|
||||
selectedVehicleVin: ['5NMS3CADXLH233004'],
|
||||
await wrapper.setData({
|
||||
selectedVehicleVin: '5NMS3CADXLH233004',
|
||||
isSelectedGlassAvailableForVehicle: false,
|
||||
isCarIdDifferent: true,
|
||||
});
|
||||
await wrapper.vm.updateCustomerInfo(wrapper.vm.selectedVehicle.vin, wrapper.vm.selectedVehicle.vehicle);
|
||||
|
||||
//Assert
|
||||
expect(store.dispatch).toBeCalledWith("resetDamageAndDependencies");
|
||||
expect(wrapper.vm.dispatchStoreAction).toBeCalledWith("resetDamageAndDependencies");
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
|
@ -164,7 +165,7 @@ describe("addressVehicles.vue", () => {
|
|||
await wrapper.vm.lookupVin('1234567890');
|
||||
|
||||
//Assert
|
||||
expect(store.dispatch).toBeCalledWith("lookupVehicleByVin", {"vin": "1234567890"});
|
||||
expect(wrapper.vm.dispatchStoreAction).toBeCalledWith("lookupVehicleByVin", {"vin": "1234567890"});
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
|
@ -172,11 +173,10 @@ describe("addressVehicles.vue", () => {
|
|||
test("If selectedVehicleVin changes, then should update isCarIdDifferent", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
|
||||
// Act
|
||||
wrapper.setData({
|
||||
selectedVehicleVin: ['5NMS3CADXLH233004'],
|
||||
await wrapper.setData({
|
||||
selectedVehicleVin: '5NMS3CADXLH233004',
|
||||
isCarIdDifferent: false,
|
||||
});
|
||||
await wrapper.vm.resetDependentState();
|
||||
|
|
@ -193,8 +193,8 @@ describe("addressVehicles.vue", () => {
|
|||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
|
||||
// Act
|
||||
wrapper.setData({
|
||||
selectedVehicleVin: ['5NMS3CADXLH233004'],
|
||||
await wrapper.setData({
|
||||
selectedVehicleVin: '5NMS3CADXLH233004',
|
||||
isCarIdDifferent: false,
|
||||
});
|
||||
await wrapper.vm.resetDependentState();
|
||||
|
|
@ -212,8 +212,8 @@ describe("addressVehicles.vue", () => {
|
|||
wrapper.vm.$router.navigateAfterSave = jest.fn();
|
||||
|
||||
// Act
|
||||
wrapper.setData({
|
||||
selectedVehicleVin: ['5NMS3CADXLH233004'],
|
||||
await wrapper.setData({
|
||||
selectedVehicleVin: '5NMS3CADXLH233004',
|
||||
isSelectedGlassAvailableForVehicle: false,
|
||||
isCarIdDifferent: true,
|
||||
});
|
||||
|
|
@ -225,7 +225,7 @@ describe("addressVehicles.vue", () => {
|
|||
wrapper.unmount();
|
||||
});
|
||||
|
||||
test("Should navigate to navigateAfterSaveToHeritageFunnel if carId is not different on navigateForward", async () => {
|
||||
test("carId is not different on navigateForward (car was found) => Should handle navigating forward with car match", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
|
|
@ -233,79 +233,49 @@ describe("addressVehicles.vue", () => {
|
|||
navigateToHeritage.navigateAfterSaveToHeritageFunnel = jest.fn();
|
||||
|
||||
// Act
|
||||
wrapper.setData({
|
||||
selectedVehicleVin: ['5NMS3CADXLH233004'],
|
||||
isSelectedGlassAvailableForVehicle: true,
|
||||
await wrapper.setData({
|
||||
isCarIdDifferent: false,
|
||||
});
|
||||
await wrapper.vm.navigateForward();
|
||||
|
||||
//Assert
|
||||
expect(navigateToHeritage.navigateAfterSaveToHeritageFunnel).toBeCalledTimes(1);
|
||||
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toBeCalledTimes(1);
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
function setupMocks({
|
||||
// modelValueProp = "1900",
|
||||
cmsQuestionText = "CMS text goes here",
|
||||
// dataFromStoreApi = [],
|
||||
}) {
|
||||
function setupMocks({}) {
|
||||
//Mock store
|
||||
store.dispatch = jest.fn(() => {});
|
||||
store.getters = {
|
||||
pageData: jest.fn((pageName) => {
|
||||
// console.log('pageName: ', pageName) // address-vehicles
|
||||
return [
|
||||
{
|
||||
vehicle: {
|
||||
"carId": "CR00069309",
|
||||
"category": "SUV",
|
||||
"year": 2020,
|
||||
"make": "Hyundai",
|
||||
"model": "Santa Fe",
|
||||
"style": "4 door utility",
|
||||
"imageUrl": "https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13769/13769_cc0320_032_WW8.jpg",
|
||||
"imageVifNumber": "13769",
|
||||
"imageVifColor": "white"
|
||||
},
|
||||
vin: "5NMS3CADXLH233004"
|
||||
},
|
||||
];
|
||||
}),
|
||||
order: {
|
||||
store.commit(storeMutations.RESET_STATE);
|
||||
store.commit(storeMutations.UPDATE_PAGE_DATA, {
|
||||
page: "address-vehicles",
|
||||
data: [{
|
||||
vehicle: {
|
||||
carId: "123",
|
||||
"carId": "CR00069309",
|
||||
"category": "SUV",
|
||||
"year": 2020,
|
||||
"make": "Hyundai",
|
||||
"model": "Santa Fe",
|
||||
"style": "4 door utility",
|
||||
"imageUrl": "https://dbhdyzvm8lm25.cloudfront.net/color_0320_032/MY2020/13769/13769_cc0320_032_WW8.jpg",
|
||||
"imageVifNumber": "13769",
|
||||
"imageVifColor": "white"
|
||||
},
|
||||
serviceLocation: {
|
||||
zipCode: "12345"
|
||||
},
|
||||
customer: {
|
||||
emailAddress: "qw@er.ty"
|
||||
}
|
||||
},
|
||||
damage: {
|
||||
glassToReplace: "Windshield"
|
||||
},
|
||||
vehicle: {
|
||||
carId: "456",
|
||||
}
|
||||
};
|
||||
|
||||
// baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
|
||||
vin: "5NMS3CADXLH233004"
|
||||
}],
|
||||
})
|
||||
|
||||
const mountOptions = getMountOptions({
|
||||
store: {
|
||||
dispatch: store.dispatch,
|
||||
getters: store.getters,
|
||||
},
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
actionList: [
|
||||
{
|
||||
actionName: storeActions.LOOKUP_VEHICLE_BY_VIN,
|
||||
data: {}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
//Mock props
|
||||
|
|
@ -320,16 +290,6 @@ function setupMocks({
|
|||
}
|
||||
return null;
|
||||
}),
|
||||
// dispatchStoreAction: jest.fn(() => {
|
||||
// console.log("23424243")
|
||||
// }),
|
||||
// isGlassAvailableForCarId: () => {
|
||||
// console.log("%%%%%%%%%%%%%%%")
|
||||
// return Promise.resolve(true)
|
||||
// }
|
||||
|
||||
// lookupVin: jest.fn(() => Promise.resolve(lookupVinResponse)),
|
||||
|
||||
},
|
||||
computed: {
|
||||
dynamicStrings() {
|
||||
|
|
@ -337,18 +297,13 @@ function setupMocks({
|
|||
}
|
||||
}
|
||||
}
|
||||
// mountOptions.propsData = {
|
||||
// modelValue: modelValueProp,
|
||||
// };
|
||||
|
||||
mountOptions.mixins = [mockMixin];
|
||||
|
||||
const wrapper = shallowMount(addressVehicles, mountOptions);
|
||||
|
||||
//Mock CMS content
|
||||
const cmsContent = {
|
||||
QuestionText: cmsQuestionText,
|
||||
};
|
||||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,25 +59,25 @@ import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
|||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import store from "@/store";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { navigateAfterSaveToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
import { isGlassAvailableForCarId } from "@/helpers/damage-helper";
|
||||
import { doesCopyContainRouterLink,
|
||||
splitCopyOnCMSPlaceHolder,
|
||||
getRouterLinkRouteFromCopy,
|
||||
getRouterLinkDisplayTextFromCopy, } from "@/helpers/cms-content-helper"
|
||||
getRouterLinkDisplayTextFromCopy, } from "@/helpers/cms-content-helper";
|
||||
import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("vehicle-required", required(errorMessages.VEHICLE_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "address-vehicles",
|
||||
mixins: [vinPagesMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||
|
|
@ -102,7 +102,7 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
selectedVehicleVin: null,
|
||||
selectedVehicleVin: "",
|
||||
isCarIdDifferent: false,
|
||||
isSelectedGlassAvailableForVehicle: true,
|
||||
};
|
||||
|
|
@ -145,7 +145,7 @@ export default {
|
|||
return store.getters.pageData(fmgPageValues.ADDRESS_VEHICLES);
|
||||
},
|
||||
selectedVehicle() {
|
||||
return this.VehiclesForQuestions.find( ({ vin }) => vin === this.selectedVehicleVin[0] );
|
||||
return this.VehiclesForQuestions.find( ({ vin }) => vin === this.selectedVehicleVin );
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
|
@ -188,13 +188,12 @@ export default {
|
|||
);
|
||||
return;
|
||||
} else {
|
||||
this.$refs.loadingModal.showModal();
|
||||
navigateAfterSaveToHeritageFunnel(this.$route);
|
||||
this.navigateForwardWithSingleCarMatch();
|
||||
return;
|
||||
}
|
||||
},
|
||||
lookupVin(vin) {
|
||||
return baseMixin.methods.dispatchStoreAction(
|
||||
return this.dispatchStoreAction(
|
||||
storeActions.LOOKUP_VEHICLE_BY_VIN,
|
||||
{ vin }
|
||||
);
|
||||
|
|
@ -205,7 +204,7 @@ export default {
|
|||
//this.$store.dispatch(storeActions.SAVE_VIN, vin,vehicle),
|
||||
updateCustomerInfo(vin, vehicle) {
|
||||
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
this.dispatchStoreAction(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
}
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_VIN, vin);
|
||||
store.commit(storeMutations.UPDATE_YEAR, vehicle.year);
|
||||
|
|
@ -221,10 +220,13 @@ export default {
|
|||
},
|
||||
|
||||
watch: {
|
||||
selectedVehicleVin() {
|
||||
// does this vehicle match the previously selected carId?
|
||||
this.isCarIdDifferent = this.selectedVehicle.vehicle.carId !== store.getters.vehicle.carId;
|
||||
this.$refs.funnelFooter.updateButtonText(`Continue with ${this.selectedVehicle.vehicle.year} ${this.selectedVehicle.vehicle.make} ${this.selectedVehicle.vehicle.model}`);
|
||||
selectedVehicleVin: {
|
||||
handler() {
|
||||
// does this vehicle match the previously selected carId?
|
||||
this.isCarIdDifferent = this.selectedVehicle.vehicle.carId !== store.getters.vehicle.carId;
|
||||
this.$refs.funnelFooter.updateButtonText(`Continue with ${this.selectedVehicle.vehicle.year} ${this.selectedVehicle.vehicle.make} ${this.selectedVehicle.vehicle.model}`);
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
},
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,244 +0,0 @@
|
|||
<template>
|
||||
<div class="container-fluid shadow rounded-3 p-2 position-relative make-tall">
|
||||
<funnelHeader ref="funnelHeader" />
|
||||
<vehicleBanner ref="vehicleBanner" :displayGenericVehicleImage=false />
|
||||
<funnelSubHeader ref="funnelSubHeader" />
|
||||
<Form
|
||||
@submit="onSubmit"
|
||||
@invalid-submit="onInvalidSubmit"
|
||||
ref="theForm"
|
||||
v-slot="{ values, meta, errors }"
|
||||
>
|
||||
<damageLocationQuestion
|
||||
ref="damageLocation"
|
||||
v-model="selectedDamageLocations"
|
||||
groupName="DamageLocationQuestion"
|
||||
/>
|
||||
<windshieldOptions
|
||||
ref="windshieldOptions"
|
||||
v-model="selectedWindshieldOptions"
|
||||
:hasRepairReplaceConflict="hasRepairReplaceConflict"
|
||||
:selectedDamageLocations="selectedDamageLocations"
|
||||
/>
|
||||
<alert
|
||||
class="my-3"
|
||||
v-show="hasRepairReplaceConflict"
|
||||
alertClass="alert-danger"
|
||||
alertHeadline="You'll need to schedule separate appointments"
|
||||
alertCopy="Vehicle service requiring both glass repair and replacement must be scheduled separately, as they're performed by different technicians. Continue scheduling your first service now, and then come back to schedule the second service."
|
||||
:isDismissible="false"
|
||||
/>
|
||||
<sideDoorOptions
|
||||
ref="sideDoorOptions"
|
||||
groupName="SideDoorSideQuestion"
|
||||
v-model="sideDoorOptionsData"
|
||||
v-show="!hasRepairReplaceConflict"
|
||||
:selectedDamageLocations="selectedDamageLocations"
|
||||
/>
|
||||
<replaceOptionsQuestion
|
||||
ref="backGlassOptions"
|
||||
:isAvailable="isRearWindowDamageLocation && !hasRepairReplaceConflict"
|
||||
v-model="selectedRearReplaceOptions"
|
||||
groupName="BackGlassReplaceOptionsQuestion"
|
||||
validationRules="replace-options-required"
|
||||
/>
|
||||
<funnel-footer
|
||||
ref="funnelFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@back-clicked="backButtonAction"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
/>
|
||||
|
||||
<!-- KEEP TEMPORARILY FOR TROUBLESHOOTING VALIDATION -->
|
||||
<div class="g-2 mt-6 mx-4 w-25 position-fixed fixed-top">
|
||||
<p>Current Form, values:</p>
|
||||
<pre>{{ values }}</pre>
|
||||
<p>Errors:</p>
|
||||
<pre>{{ errors }}</pre>
|
||||
<p>Is Form Valid? (Meta.valid):</p>
|
||||
<pre>{{ meta.valid }}</pre>
|
||||
</div>
|
||||
|
||||
</Form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// Components
|
||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||
import replaceOptionsQuestion from "@/layouts/vehicle-damage/replace-options-question/replace-options-question";
|
||||
import damageLocationQuestion from "@/layouts/vehicle-damage/damage-location-question/damage-location-question";
|
||||
import sideDoorOptions from "@/layouts/vehicle-damage/side-door-options/side-door-options";
|
||||
import windshieldOptions from "@/layouts/vehicle-damage/windshield-options/windshield-options";
|
||||
import alert from "@/ux-components/alert/alert";
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import store from "@/store";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { damageLocationsCms } from "@/constants/damage-locations-cms.js";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("replace-options-required", required(errorMessages.REPLACE_OPTIONS_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "form-test",
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage('vehicle-damage');
|
||||
const damageOptionsPromise =
|
||||
baseMixin.methods.dispatchStoreAction(
|
||||
storeActions.GET_DAMAGE_OPTIONS,
|
||||
{ carId: store.getters.vehicle.carId }
|
||||
);
|
||||
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: "cmsContent",
|
||||
promise: cmsContentPromise,
|
||||
},
|
||||
{
|
||||
resultKey: "damageOptions",
|
||||
promise: damageOptionsPromise,
|
||||
},
|
||||
];
|
||||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
next((vm) => {
|
||||
vm.$refs.funnelHeader.initializeComponent(
|
||||
resultMap.cmsContent.FunnelHeaderWidget
|
||||
);
|
||||
vm.$refs.vehicleBanner.initializeComponent(
|
||||
resultMap.cmsContent.VehicleBannerWidget
|
||||
);
|
||||
vm.$refs.funnelSubHeader.initializeComponent(
|
||||
resultMap.cmsContent.FunnelSubHeaderWidget
|
||||
);
|
||||
vm.$refs.damageLocation.initializeComponent(
|
||||
resultMap.cmsContent.DamageLocationQuestion, resultMap.damageOptions
|
||||
);
|
||||
vm.$refs.sideDoorOptions.initializeComponent(
|
||||
resultMap.cmsContent.SideDoorSideQuestion, resultMap.cmsContent.DriverSideReplaceOptionsQuestion, resultMap.cmsContent.PassengerSideReplaceOptionsQuestion, resultMap.damageOptions.driverSideOptions.availableReplacementOptions, resultMap.damageOptions.passengerSideOptions.availableReplacementOptions
|
||||
);
|
||||
vm.$refs.windshieldOptions.initializeComponent(
|
||||
resultMap.cmsContent.WindshieldDamageTypeQuestion, resultMap.cmsContent.WindshieldChipCountQuestion,
|
||||
resultMap.cmsContent.WindshieldReplaceOptionsQuestion, resultMap.damageOptions.windshieldOptions.availableReplacementOptions
|
||||
);
|
||||
vm.$refs.backGlassOptions.initializeComponent(
|
||||
resultMap.cmsContent.RearReplaceOptionsQuestion, resultMap.damageOptions.backGlassOptions.availableReplacementOptions
|
||||
);
|
||||
vm.$refs.funnelFooter.initializeComponent(
|
||||
resultMap.cmsContent.FunnelFooterWidget
|
||||
);
|
||||
});
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
selectedDamageLocations: [],
|
||||
sideDoorOptionsData: {
|
||||
selectedDoorSides: [],
|
||||
selectedDriverSideReplaceOptions: [],
|
||||
selectedPassengerSideReplaceOptions: []
|
||||
},
|
||||
selectedWindshieldOptions: [],
|
||||
selectedRearReplaceOptions: [],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
return store.getters.vehicle.carId !== null;
|
||||
},
|
||||
resetDependentState() {
|
||||
store.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
},
|
||||
onSubmit(values) {
|
||||
window.alert('Success! Submitted values: ' + JSON.stringify(values, null ,2))
|
||||
console.log('submitted: ', JSON.stringify(values, null ,2));
|
||||
},
|
||||
onInvalidSubmit({ values, errors, results }) {
|
||||
// identify the first error field and put focus on it
|
||||
console.log("values: ", values);
|
||||
console.log("errors: ", errors);
|
||||
console.log("results: ", results);
|
||||
// get error names array
|
||||
const errorNames = errors ? Object.keys(errors) : [];
|
||||
const firstErrorEl = errorNames[0];
|
||||
if (firstErrorEl) {
|
||||
console.log('firstErrorEl: ', firstErrorEl);
|
||||
const qsString = "[data-focus-target='" + firstErrorEl + "']";
|
||||
document.querySelector(qsString).focus();
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
isWindshieldDamageLocation() {
|
||||
return this.selectedDamageLocations.some(selectedDamages =>
|
||||
{
|
||||
return selectedDamages.toUpperCase() === damageLocationsCms.WINDSHIELD;
|
||||
});
|
||||
},
|
||||
isSideDoorDamageLocation() {
|
||||
return this.selectedDamageLocations.some(selectedDamages =>
|
||||
{
|
||||
return selectedDamages.toUpperCase() === damageLocationsCms.SIDEDOOR;
|
||||
});
|
||||
},
|
||||
isRearWindowDamageLocation() {
|
||||
return this.selectedDamageLocations.some(selectedDamages =>
|
||||
{
|
||||
return selectedDamages.toUpperCase() === damageLocationsCms.REARWINDOW;
|
||||
});
|
||||
},
|
||||
isWindshieldRepair() {
|
||||
if (!this.isWindshieldDamageLocation) return false;
|
||||
|
||||
return this.selectedWindshieldOptions.selectedWindshieldDamageType && this.selectedWindshieldOptions.selectedWindshieldDamageType.some(selectedDamageType =>
|
||||
{
|
||||
return selectedDamageType.toUpperCase() === "REPAIR";
|
||||
});
|
||||
},
|
||||
isDriverSideReplace() {
|
||||
if (!this.isSideDoorDamageLocation) return false;
|
||||
|
||||
return this.sideDoorOptionsData.selectedDoorSides.some(selectedDriverSide =>
|
||||
{
|
||||
return selectedDriverSide.toUpperCase() === damageLocationsCms.DRIVERSIDE;
|
||||
});
|
||||
},
|
||||
isPassengerSideReplace() {
|
||||
if (!this.isSideDoorDamageLocation) return false;
|
||||
|
||||
return this.sideDoorOptionsData.selectedDoorSides.some(selectedPassengerSide =>
|
||||
{
|
||||
return selectedPassengerSide.toUpperCase() === damageLocationsCms.PASSENGERSIDE;
|
||||
});
|
||||
},
|
||||
hasRepairReplaceConflict() {
|
||||
return this.isWindshieldDamageLocation && this.selectedDamageLocations.length > 1 && this.isWindshieldRepair;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
funnelHeader,
|
||||
funnelFooter,
|
||||
vehicleBanner,
|
||||
funnelSubHeader,
|
||||
sideDoorOptions,
|
||||
damageLocationQuestion,
|
||||
windshieldOptions,
|
||||
replaceOptionsQuestion,
|
||||
Form,
|
||||
alert,
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
@ -4,11 +4,11 @@ import licensePlateLookup from "@/layouts/license-plate-lookup/license-plate-loo
|
|||
// Supporting Files
|
||||
import { settleAllPromises } from "@/helpers/layout-helper.js";
|
||||
import * as navigateToHeritage from "@/helpers/heritage-integration/navigation-helper";
|
||||
import baseMixin from "@/mixins/base-mixin";
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import { nextTick } from "vue";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import store from "@/store";
|
||||
|
||||
|
|
@ -223,19 +223,22 @@ describe("license-plate-lookup.vue", () => {
|
|||
});
|
||||
|
||||
describe("navigateForward", () => {
|
||||
test("NavigateAfterSave should be called if isCarIdDifferent is true and isSelectedGlassAvailableForVehicle is false when navigateForward is called", async () => {
|
||||
test("navigateAfterSave should be called if isCarIdDifferent is true and isSelectedGlassAvailableForVehicle is false when navigateForward is called", async () => {
|
||||
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
wrapper.vm.isCarIdDifferent = true;
|
||||
wrapper.vm.isSelectedGlassAvailableForVehicle = false;
|
||||
await wrapper.setData({
|
||||
isCarIdDifferent: true,
|
||||
isSelectedGlassAvailableForVehicle: false
|
||||
})
|
||||
|
||||
wrapper.vm.$router.navigateAfterSave = jest.fn();
|
||||
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {
|
||||
return '';
|
||||
});
|
||||
store.dispatch = jest.fn();
|
||||
wrapper.vm.dispatchStoreAction = jest.fn();
|
||||
|
||||
await wrapper.vm.navigateForward();
|
||||
|
||||
|
|
@ -249,7 +252,9 @@ describe("license-plate-lookup.vue", () => {
|
|||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
wrapper.vm.isCarIdDifferent = false;
|
||||
await wrapper.setData({
|
||||
isCarIdDifferent: false
|
||||
})
|
||||
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {
|
||||
return '';
|
||||
});
|
||||
|
|
@ -259,6 +264,38 @@ describe("license-plate-lookup.vue", () => {
|
|||
//Assert
|
||||
expect(navigateToHeritage.navigateAfterSaveToHeritageFunnel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("carId matches returned vehicle => navigateForwardWithSingleCarMatch", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
await wrapper.setData({
|
||||
isCarIdDifferent: false
|
||||
})
|
||||
|
||||
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||
|
||||
// Act
|
||||
await wrapper.vm.navigateForward();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
test("selected glass is available for returned vehicle => navigateForwardWithSingleCarMatch", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
await wrapper.setData({
|
||||
isSelectedGlassAvailableForVehicle: true
|
||||
})
|
||||
|
||||
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||
|
||||
// Act
|
||||
await wrapper.vm.navigateForward();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -269,7 +306,9 @@ describe("license-plate-lookup.vue", () => {
|
|||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
wrapper.vm.licensePlate = "NEWPLATE";
|
||||
wrapper.setData({
|
||||
licensePlate: "NEWPLATE"
|
||||
})
|
||||
wrapper.vm.getCmsContent = jest.fn();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
|
|
@ -283,7 +322,9 @@ describe("license-plate-lookup.vue", () => {
|
|||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
wrapper.vm.registrationZip = "55555";
|
||||
await wrapper.setData({
|
||||
registrationZip: "55555"
|
||||
})
|
||||
wrapper.vm.getCmsContent = jest.fn();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
|
|
@ -297,7 +338,9 @@ describe("license-plate-lookup.vue", () => {
|
|||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
wrapper.vm.serviceZip = "55555";
|
||||
await wrapper.setData({
|
||||
serviceZip: "55555"
|
||||
})
|
||||
wrapper.vm.getCmsContent = jest.fn();
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
|
|
@ -469,22 +512,23 @@ describe("license-plate-lookup.vue", () => {
|
|||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
wrapper.vm.isCarIdDifferent = true;
|
||||
wrapper.vm.isSelectedGlassAvailableForVehicle = false;
|
||||
await wrapper.setData({
|
||||
isCarIdDifferent: true,
|
||||
isSelectedGlassAvailableForVehicle: false
|
||||
})
|
||||
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => {
|
||||
return '';
|
||||
});
|
||||
store.commit = jest.fn();
|
||||
store.dispatch = jest.fn();
|
||||
|
||||
const vehicleInfo = { year: "2020", make: "honda", model: "civic", style: "2 door", carId: "TestId", category: "testCat", imageUrl: "image.jpg", imageVifNumber: "123", imageColor: "blue" }
|
||||
await wrapper.vm.updateCustomerInfo('vin', vehicleInfo, 'registrationState');
|
||||
|
||||
//Assert
|
||||
expect(store.dispatch).toHaveBeenCalled();
|
||||
expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalled();
|
||||
})
|
||||
|
||||
test("dispatch non blocking store action called on validate zip", async () => {
|
||||
test("dispatchStoreAction called on validate zip", async () => {
|
||||
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
|
@ -494,10 +538,10 @@ describe("license-plate-lookup.vue", () => {
|
|||
|
||||
|
||||
//Assert
|
||||
expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalled();
|
||||
expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("dispatch non blocking store action called on lookup vin", async () => {
|
||||
test("dispatchStoreAction called on lookup vin", async () => {
|
||||
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
|
@ -507,22 +551,18 @@ describe("license-plate-lookup.vue", () => {
|
|||
|
||||
|
||||
//Assert
|
||||
expect(baseMixin.methods.dispatchStoreAction).toHaveBeenCalled();
|
||||
expect(wrapper.vm.dispatchStoreAction).toHaveBeenCalled();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
function setupMocks({
|
||||
pageHeaderWidgetHeaderText = {},
|
||||
mountOptionsMockData = {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
},
|
||||
mountOptionsMockData = {},
|
||||
partsOrQuestions = []
|
||||
}) {
|
||||
store.commit(storeMutations.RESET_STATE);
|
||||
//Mock api responses
|
||||
baseMixin.methods.dispatchStoreAction = jest.fn();
|
||||
const apiResponses = {
|
||||
cmsContent: {
|
||||
FunnelSubHeaderWidget: pageHeaderWidgetHeaderText,
|
||||
|
|
@ -537,6 +577,21 @@ function setupMocks({
|
|||
},
|
||||
};
|
||||
|
||||
mountOptionsMockData = {
|
||||
...mountOptionsMockData,
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
},
|
||||
actionList: [
|
||||
{
|
||||
actionName: storeActions.GET_PARTS_OR_QUESTIONS,
|
||||
data: {
|
||||
partsOrQuestions: partsOrQuestions
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
const apiPromise = Promise.resolve(apiResponses);
|
||||
|
||||
settleAllPromises.mockImplementation(() => apiPromise);
|
||||
|
|
@ -547,7 +602,7 @@ function setupMocks({
|
|||
|
||||
const wrapper = shallowMount(licensePlateLookup, mountOptions);
|
||||
|
||||
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
||||
wrapper.vm.setCmsContent = jest.fn();
|
||||
wrapper.vm.getCmsContent = jest.fn().mockImplementation(() => "");
|
||||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@
|
|||
v-model="email"
|
||||
inputId="email"
|
||||
validationRules="email-address-required|email-address-format"
|
||||
semiAggressiveValidation
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -54,7 +53,6 @@
|
|||
v-model="serviceZip"
|
||||
inputId="serviceZip"
|
||||
validationRules="zip-required|zip-format"
|
||||
semiAggressiveValidation
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -94,14 +92,13 @@ import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
|||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import store from "@/store";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { navigateAfterSaveToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
import { getDamageString, isGlassAvailableForCarId, } from "@/helpers/damage-helper";
|
||||
import { required, regex, } from "@/helpers/validation-rules";
|
||||
import { Form, defineRule, } from "vee-validate";
|
||||
import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("license-plate-required", required(errorMessages.LICENSE_PLATE_REQUIRED));
|
||||
|
|
@ -112,6 +109,7 @@ defineRule("email-address-format", regex(/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9_\-.]+
|
|||
|
||||
export default {
|
||||
name: "license-plate-lookup",
|
||||
mixins: [vinPagesMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||
|
|
@ -297,22 +295,21 @@ export default {
|
|||
);
|
||||
return;
|
||||
} else {
|
||||
this.$refs.loadingModal.showModal();
|
||||
navigateAfterSaveToHeritageFunnel(this.$route);
|
||||
this.navigateForwardWithSingleCarMatch();
|
||||
return;
|
||||
}
|
||||
},
|
||||
validateZip(zip) {
|
||||
return baseMixin.methods.dispatchStoreAction(storeActions.VALIDATE_ZIP, {
|
||||
return this.dispatchStoreAction(storeActions.VALIDATE_ZIP, {
|
||||
zip,
|
||||
});
|
||||
},
|
||||
lookupVin(plate, state) {
|
||||
return baseMixin.methods.dispatchStoreAction(storeActions.LOOKUP_VIN_BY_PLATE,{ licensePlate: plate, licenseState: state }, false);
|
||||
return this.dispatchStoreAction(storeActions.LOOKUP_VIN_BY_PLATE,{ licensePlate: plate, licenseState: state }, false);
|
||||
},
|
||||
updateCustomerInfo(vin, vehicleInfo, registrationState, serviceState) {
|
||||
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
|
||||
store.dispatch(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
this.dispatchStoreAction(storeActions.RESET_DAMAGE_STATE_AND_DEPENDENCIES);
|
||||
}
|
||||
store.commit(storeMutations.UPDATE_VEHICLE_VIN, vin);
|
||||
store.commit(storeMutations.UPDATE_YEAR, vehicleInfo.year);
|
||||
|
|
|
|||
|
|
@ -5,9 +5,7 @@
|
|||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||
<div class="fade-on-route-transition sub-container make-tall">
|
||||
<h1>Part Questions Page Placeholder</h1>
|
||||
<funnel-footer
|
||||
cmsWidgetName="FunnelFooterWidget" @back-clicked="backButtonAction"
|
||||
/>
|
||||
<funnel-footer cmsWidgetName="FunnelFooterWidget" @back-clicked="backButtonAction" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -65,14 +63,11 @@ export default {
|
|||
);
|
||||
},
|
||||
arePagePrerequisitesValid() {
|
||||
if (Object.keys(store.getters.pageData(fmgPageValues.PART_QUESTIONS)).length !== 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return Object.keys(store.getters.pageData(fmgPageValues.PART_QUESTIONS)).length !== 0;
|
||||
},
|
||||
resetDependentState() {
|
||||
// Set
|
||||
store.commit(storeMutations.UPDATE_PARTS, null);
|
||||
store.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
|
||||
// Invokes
|
||||
store.dispatch(storeActions.RESET_PARTS_AND_DEPS);
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export default {
|
|||
},
|
||||
resetDependentState() {
|
||||
// Set
|
||||
store.commit(storeMutations.UPDATE_PARTS, null);
|
||||
store.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
|
||||
// Invokes
|
||||
store.dispatch(storeActions.RESET_PARTS_AND_DEPS);
|
||||
|
|
|
|||
|
|
@ -145,8 +145,8 @@ describe("vehicle-damage.vue", () => {
|
|||
|
||||
wrapper.vm.selectedRearReplaceOptions = ["Stationary"];
|
||||
|
||||
const expectedGlassToReplace = [{ location: "Windshield", name: "Single" }, { location: "Driver", name: "Back" },
|
||||
{ location: "Passenger", name: "Quarter" }, { location: "Rear", name: "Stationary" }];
|
||||
const expectedGlassToReplace = [{ glassLocation: "Windshield", glassName: "Single" }, { glassLocation: "Driver", glassName: "Back" },
|
||||
{ glassLocation: "Passenger", glassName: "Quarter" }, { glassLocation: "Rear", glassName: "Stationary" }];
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
|
|
@ -237,7 +237,7 @@ describe("vehicle-damage.vue", () => {
|
|||
selectedWindshieldDamageType: ["Replace"]
|
||||
};
|
||||
|
||||
const expectedGlassToReplace = [{ location: "Windshield", name: "Single" },];
|
||||
const expectedGlassToReplace = [{ glassLocation: "Windshield", glassName: "Single" },];
|
||||
|
||||
//Act
|
||||
vehicleDamage.beforeRouteEnter.call(
|
||||
|
|
|
|||
|
|
@ -314,25 +314,25 @@ export default {
|
|||
const selectedGlassToReplace = [];
|
||||
if (this.isWindshieldDamageLocation && !this.isWindshieldRepair){
|
||||
this.selectedWindshieldOptions.selectedWindshieldReplaceOptions.forEach(wsItem => {
|
||||
selectedGlassToReplace.push({ location: damageLocationsSelected.WINDSHIELD, name: wsItem});
|
||||
selectedGlassToReplace.push({ glassLocation: damageLocationsSelected.WINDSHIELD, glassName: wsItem});
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isDriverSideReplace){
|
||||
this.sideDoorOptionsData.selectedDriverSideReplaceOptions.forEach(driverItem => {
|
||||
selectedGlassToReplace.push({ location: damageLocationsSelected.DRIVER, name: driverItem});
|
||||
selectedGlassToReplace.push({ glassLocation: damageLocationsSelected.DRIVER, glassName: driverItem});
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isPassengerSideReplace){
|
||||
this.sideDoorOptionsData.selectedPassengerSideReplaceOptions.forEach(passengerItem => {
|
||||
selectedGlassToReplace.push({ location: damageLocationsSelected.PASSENGER, name: passengerItem});
|
||||
selectedGlassToReplace.push({ glassLocation: damageLocationsSelected.PASSENGER, glassName: passengerItem});
|
||||
})
|
||||
}
|
||||
|
||||
if (this.isRearWindowDamageLocation) {
|
||||
this.selectedRearReplaceOptions.forEach(rearItem => {
|
||||
selectedGlassToReplace.push({ location: damageLocationsSelected.REAR, name: rearItem});
|
||||
selectedGlassToReplace.push({ glassLocation: damageLocationsSelected.REAR, glassName: rearItem});
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
<funnelSubHeader
|
||||
cmsWidgetName="FunnelSubHeaderWidget"
|
||||
:hasBackButton="true"
|
||||
backButtonAccessibleText="Change Vehicle Year"
|
||||
@click-event="backButtonAction"
|
||||
/>
|
||||
<div class="fade-on-route-transition">
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
<funnelSubHeader
|
||||
cmsWidgetName="FunnelSubHeaderWidget"
|
||||
:hasBackButton="true"
|
||||
backButtonAccessibleText="Change Vehicle Make"
|
||||
@click-event="backButtonAction"
|
||||
/>
|
||||
<div class="fade-on-route-transition">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<p class="mb-0">{{ colorQuestionText }}</p>
|
||||
<p class="mb-0 color-question-text">{{ colorQuestionText }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -29,7 +29,11 @@
|
|||
:buttonID="`${glassLocation}-${glassName}-${name}`"
|
||||
:groupName="`${glassLocation}-${glassName}`"
|
||||
@isCheckedChanged="ResetTintAndPartSelections()"
|
||||
:validationRules="validationRules"
|
||||
/>
|
||||
<div class="row form-test-error mt-1">
|
||||
<error-message :name="`${glassLocation}-${glassName}`" v-if="!suppressError"></error-message>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="fade" mode="out-in">
|
||||
|
|
@ -53,6 +57,7 @@
|
|||
:loaderEnabled="false"
|
||||
isRequired
|
||||
:groupName="`${glassLocation}-${glassName}-${name}`"
|
||||
:validationRules="validationRules"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -68,6 +73,7 @@ import buttonQuestion from "@/common-components/button-question/button-question"
|
|||
// Supporting files
|
||||
import { getTintImage } from "@/constants/tint-mapper";
|
||||
import { getCustomTransformValue } from "@/constants/dynamictext-mapper";
|
||||
import { ErrorMessage } from 'vee-validate';
|
||||
|
||||
export default {
|
||||
name: "glass-part-question",
|
||||
|
|
@ -84,6 +90,7 @@ export default {
|
|||
glassLocation: String,
|
||||
colorAnswers: Array,
|
||||
modelValue: Object,
|
||||
validationRules: String,
|
||||
},
|
||||
mounted() {
|
||||
this.LoadPreselectedValues();
|
||||
|
|
@ -91,6 +98,7 @@ export default {
|
|||
components: {
|
||||
listCard,
|
||||
buttonQuestion,
|
||||
ErrorMessage,
|
||||
},
|
||||
computed: {
|
||||
colorQuestionText() {
|
||||
|
|
@ -168,23 +176,25 @@ export default {
|
|||
// Check if only a single part is present for the tint and set the v-model if it is.
|
||||
AutoSelectIfSinglePart() {
|
||||
// Check if the selected tint only has a single feature
|
||||
Object.keys(this.PartDataFromApi.partsOrQuestions).forEach((key) => {
|
||||
const currentGlassSelection =
|
||||
this.PartDataFromApi.partsOrQuestions[key];
|
||||
Object.keys(this.PartDataFromApi.partsOrQuestions ?? {}).forEach(
|
||||
(key) => {
|
||||
const currentGlassSelection =
|
||||
this.PartDataFromApi.partsOrQuestions[key];
|
||||
|
||||
if (
|
||||
currentGlassSelection.glassName === this.glassName &&
|
||||
currentGlassSelection.glassLocation === this.glassLocation
|
||||
) {
|
||||
if (currentGlassSelection.parts.length === 1) {
|
||||
this.selectedPart = {
|
||||
[currentGlassSelection.glassLocation]: [
|
||||
currentGlassSelection.parts[0].partNumber,
|
||||
],
|
||||
};
|
||||
if (
|
||||
currentGlassSelection.glassName === this.glassName &&
|
||||
currentGlassSelection.glassLocation === this.glassLocation
|
||||
) {
|
||||
if (currentGlassSelection.parts.length === 1) {
|
||||
this.selectedPart = {
|
||||
[currentGlassSelection.glassLocation]: [
|
||||
currentGlassSelection.parts[0].partNumber,
|
||||
],
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
},
|
||||
|
||||
// Loads the preselected values from the store.
|
||||
|
|
@ -227,4 +237,9 @@ export default {
|
|||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
|
||||
.color-question-text {
|
||||
color: $black;
|
||||
font-weight: $font-weight-bold;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
<template>
|
||||
<div class="page-container-grouped-styles">
|
||||
<Form
|
||||
@submit="onSubmit"
|
||||
@invalid-submit="onInvalidSubmit"
|
||||
ref="theForm"
|
||||
v-slot="{ meta }"
|
||||
>
|
||||
<div class="page-container-grouped-styles vehicle-parts">
|
||||
<funnelHeader ref="funnelHeader" cmsWidgetName="FunnelHeaderWidget" />
|
||||
<vehicleBanner ref="vehicleBanner" cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage="false" />
|
||||
<funnelSubHeader ref="funnelSubHeader" cmsWidgetName="FunnelSubHeaderWidget" />
|
||||
|
|
@ -8,10 +14,10 @@
|
|||
<div class="row">
|
||||
<div class="col">
|
||||
<alert
|
||||
class="rounded border-0 shadow-sm"
|
||||
alertClass="alert-warning"
|
||||
cmsWidgetName="AlertWidget"
|
||||
:isDismissible="false"
|
||||
class="rounded border-0 shadow-sm"
|
||||
alertClass="alert-warning"
|
||||
cmsWidgetName="AlertWidget"
|
||||
:isDismissible="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -24,16 +30,24 @@
|
|||
</div>
|
||||
|
||||
<glassPartQuestion
|
||||
:ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`"
|
||||
v-model="glassParts[item.glassLocation + '-' + item.glassName]"
|
||||
:glassLocation="item.glassLocation"
|
||||
:glassName="item.glassName"
|
||||
:colorAnswers="item.colorAnswers"
|
||||
:ref="`${RefPrefix}-${item.glassLocation}-${item.glassName}`"
|
||||
v-model="glassParts[item.glassLocation + '-' + item.glassName]"
|
||||
:glassLocation="item.glassLocation"
|
||||
:glassName="item.glassName"
|
||||
:colorAnswers="item.colorAnswers"
|
||||
validationRules="replace-options-required"
|
||||
/>
|
||||
</div>
|
||||
<funnelFooter cmsWidgetName="FunnelFooterWidget" ref="funnelFooter" @back-clicked="backButtonAction" @ForwardClicked="forwardButtonAction" />
|
||||
<funnelFooter
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
ref="funnelFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@back-clicked="backButtonAction"
|
||||
@ForwardClicked="forwardButtonAction"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -50,132 +64,141 @@ import { settleAllPromises } from "@/helpers/layout-helper";
|
|||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import store from "@/store";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("replace-options-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "vehicle-parts",
|
||||
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);
|
||||
name: "vehicle-parts",
|
||||
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);
|
||||
|
||||
// Glass Part Question dynamic component
|
||||
Object.keys(vm.$refs)
|
||||
.filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined)
|
||||
.forEach((c) =>
|
||||
vm.$refs[c][0].initializeComponent({
|
||||
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
|
||||
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget,
|
||||
})
|
||||
);
|
||||
// Glass Part Question dynamic component
|
||||
Object.keys(vm.$refs)
|
||||
.filter((r) => r.includes(vm.RefPrefix) && vm.$refs[r][0] !== undefined)
|
||||
.forEach((c) =>
|
||||
vm.$refs[c][0].initializeComponent({
|
||||
ColorQuestionWidget: resultMap.cmsContent.ColorQuestionWidget,
|
||||
FeatureQuestionWidget: resultMap.cmsContent.FeatureQuestionWidget,
|
||||
})
|
||||
);
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
glassParts: {},
|
||||
alertWidgetData: Object,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
Form,
|
||||
glassPartQuestion,
|
||||
funnelHeader,
|
||||
vehicleBanner,
|
||||
funnelSubHeader,
|
||||
funnelFooter,
|
||||
alert,
|
||||
},
|
||||
computed: {
|
||||
PartsForQuestions() {
|
||||
const partsData = this.PartsFromApi;
|
||||
|
||||
// Map API result data, to vehicle-parts data structure
|
||||
const mappedData = partsData.partsOrQuestions.map((g) => {
|
||||
return {
|
||||
glassName: g.glassName,
|
||||
glassLocation: g.glassLocation,
|
||||
colorAnswers: g.parts.reduce((arr, p) => {
|
||||
arr.push({
|
||||
ColorAnswerText: p.color,
|
||||
FeatureAnswers: [
|
||||
{
|
||||
FeatureAnswerText:
|
||||
p.description === "" ? p.color : p.description,
|
||||
PartNumber: p.partNumber,
|
||||
},
|
||||
],
|
||||
});
|
||||
return arr;
|
||||
}, []),
|
||||
};
|
||||
});
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
glassParts: {},
|
||||
alertWidgetData: Object,
|
||||
};
|
||||
},
|
||||
components: {
|
||||
glassPartQuestion,
|
||||
funnelHeader,
|
||||
vehicleBanner,
|
||||
funnelSubHeader,
|
||||
funnelFooter,
|
||||
alert,
|
||||
},
|
||||
computed: {
|
||||
PartsForQuestions() {
|
||||
const partsData = this.PartsFromApi;
|
||||
|
||||
// Map API result data, to vehicle-parts data structure
|
||||
const mappedData = partsData.partsOrQuestions.map((g) => {
|
||||
return {
|
||||
glassName: g.glassName,
|
||||
glassLocation: g.glassLocation,
|
||||
colorAnswers: g.parts.reduce((arr, p) => {
|
||||
arr.push({
|
||||
ColorAnswerText: p.color,
|
||||
FeatureAnswers: [
|
||||
{
|
||||
FeatureAnswerText:
|
||||
p.description === "" ? p.color : p.description,
|
||||
PartNumber: p.partNumber,
|
||||
},
|
||||
],
|
||||
});
|
||||
return arr;
|
||||
}, []),
|
||||
};
|
||||
});
|
||||
|
||||
return mappedData;
|
||||
},
|
||||
|
||||
PartsFromApi() {
|
||||
return store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
|
||||
},
|
||||
|
||||
RefPrefix() {
|
||||
return "partQuestion";
|
||||
},
|
||||
return mappedData;
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
// Check if isRepair is populated and if the pageData we need is here (Parts data)
|
||||
if (
|
||||
(store.getters.damage.isRepair != null) &&
|
||||
Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS))
|
||||
.length !== 0
|
||||
) {
|
||||
return true;
|
||||
|
||||
PartsFromApi() {
|
||||
return store.getters.pageData(fmgPageValues.VEHICLE_PARTS);
|
||||
},
|
||||
|
||||
RefPrefix() {
|
||||
return "partQuestion";
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
// Check if isRepair is populated and if the pageData we need is here (Parts data)
|
||||
if (
|
||||
(store.getters.damage.isRepair != null) &&
|
||||
Object.keys(store.getters.pageData(fmgPageValues.VEHICLE_PARTS))
|
||||
.length !== 0
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
backButtonAction() {
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
forwardButtonAction() {
|
||||
const selectedGlassPartNumbers = [];
|
||||
const matchedParts = [];
|
||||
|
||||
// Compile all selected parts from the page.
|
||||
for (let [key, value] of Object.entries(this.glassParts)) {
|
||||
for (let [glassKey, glassValue] of Object.entries(value)) {
|
||||
selectedGlassPartNumbers.push(glassValue[0]);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
backButtonAction() {
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_BACK, this.$route);
|
||||
},
|
||||
forwardButtonAction() {
|
||||
const selectedGlassPartNumbers = [];
|
||||
const matchedParts = [];
|
||||
// Match them to the parts from the API.
|
||||
for (let [key, value] of Object.entries(
|
||||
this.PartsFromApi.partsOrQuestions
|
||||
)) {
|
||||
for (let [partKey, partValue] of Object.entries(value.parts)) {
|
||||
const currentPart =
|
||||
this.PartsFromApi.partsOrQuestions[key].parts[partKey];
|
||||
const isMatched = selectedGlassPartNumbers.some(
|
||||
(p) => p === currentPart.partNumber
|
||||
);
|
||||
|
||||
// Compile all selected parts from the page.
|
||||
for (let [key, value] of Object.entries(this.glassParts)) {
|
||||
for (let [glassKey, glassValue] of Object.entries(value)) {
|
||||
selectedGlassPartNumbers.push(glassValue[0]);
|
||||
if (isMatched) {
|
||||
matchedParts.push(currentPart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Match them to the parts from the API.
|
||||
for (let [key, value] of Object.entries(
|
||||
this.PartsFromApi.partsOrQuestions
|
||||
)) {
|
||||
for (let [partKey, partValue] of Object.entries(value.parts)) {
|
||||
const currentPart =
|
||||
this.PartsFromApi.partsOrQuestions[key].parts[partKey];
|
||||
const isMatched = selectedGlassPartNumbers.some(
|
||||
(p) => p === currentPart.partNumber
|
||||
);
|
||||
|
||||
if (isMatched) {
|
||||
matchedParts.push(currentPart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no parts could be matched, throw an error.
|
||||
if (matchedParts.length === 0) {
|
||||
throw new Error("Could not match any parts to the selected parts");
|
||||
}
|
||||
// If no parts could be matched, throw an error.
|
||||
if (matchedParts.length === 0) {
|
||||
throw new Error("Could not match any parts to the selected parts");
|
||||
}
|
||||
|
||||
// Save parts to the store.
|
||||
store.commit(storeMutations.UPDATE_GLASS_PARTS, matchedParts);
|
||||
// Save parts to the store.
|
||||
store.commit(storeMutations.UPDATE_PARTS, matchedParts);
|
||||
|
||||
|
|
@ -210,10 +233,11 @@ export default {
|
|||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.LoadInitialPartsData();
|
||||
});
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.LoadInitialPartsData();
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
<funnelSubHeader
|
||||
cmsWidgetName="FunnelSubHeaderWidget"
|
||||
:hasBackButton="true"
|
||||
backButtonAccessibleText="Change Vehicle Model"
|
||||
@click-event="backButtonAction"
|
||||
/>
|
||||
<div class="fade-on-route-transition">
|
||||
|
|
|
|||
|
|
@ -1,37 +1,38 @@
|
|||
import { shallowMount } from "@vue/test-utils";
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import vinLookup from "./vin-lookup.vue";
|
||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
import { navigationScenarios } from "@/router/router-constants/navigation-scenarios.js";
|
||||
|
||||
import store from "@/store";
|
||||
|
||||
jest.mock("@/store", () => ({
|
||||
commit: jest.fn(),
|
||||
dispatch: jest.fn(),
|
||||
getters: {
|
||||
vehicle: {
|
||||
year: 2019,
|
||||
carId: 'initial carId'
|
||||
},
|
||||
order: {
|
||||
serviceLocation: {
|
||||
zipCode: "45253"
|
||||
commit: jest.fn(),
|
||||
dispatch: jest.fn(),
|
||||
getters: {
|
||||
vehicle: {
|
||||
year: 2019,
|
||||
carId: 'initial carId'
|
||||
},
|
||||
customer: {
|
||||
emailAddress: "builddigitaltest@safelite.com"
|
||||
order: {
|
||||
serviceLocation: {
|
||||
zipCode: "45253"
|
||||
},
|
||||
customer: {
|
||||
emailAddress: "builddigitaltest@safelite.com"
|
||||
}
|
||||
},
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
isVerified: true
|
||||
}
|
||||
},
|
||||
damage: {
|
||||
glassToReplace: "windshield"
|
||||
}
|
||||
},
|
||||
payment: {
|
||||
insuranceCoverage: {
|
||||
isVerified: true
|
||||
}
|
||||
},
|
||||
damage: {
|
||||
glassToReplace: "windshield"
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
import { getDamageString, getIsWindshieldOnly,isGlassAvailableForCarId } from "@/helpers/damage-helper";
|
||||
import { getDamageString, getIsWindshieldOnly, isGlassAvailableForCarId } from "@/helpers/damage-helper";
|
||||
|
||||
jest.mock("@/helpers/damage-helper", () => ({
|
||||
isGlassAvailableForCarId: jest.fn(() => {
|
||||
|
|
@ -45,41 +46,21 @@ jest.mock("@/helpers/damage-helper", () => ({
|
|||
describe("vin-lookup.vue", () => {
|
||||
it("Should update the funnel-footer forward button when VIN is changed", (done) => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({ });
|
||||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
const { wrapper } = setupMocks({});
|
||||
//Act
|
||||
wrapper.setData({vin: "newValue"});
|
||||
wrapper.setData({ vin: "newValue" });
|
||||
//Assert
|
||||
wrapper.vm.$nextTick(() => {
|
||||
expect(wrapper.vm.$refs.funnelFooter.updateButtonText).toBeCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
it("Should call navigateForward() if the store carId matches the vin response carId and forward button is clicked", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({ });
|
||||
const zipValidationApiResponse = {
|
||||
data: {
|
||||
isServiceable: true
|
||||
}
|
||||
};
|
||||
const vehicleLookupApiResponse = {
|
||||
data: {
|
||||
carId: 'initial carId'
|
||||
}
|
||||
};
|
||||
|
||||
const zipPromise = Promise.resolve(zipValidationApiResponse);
|
||||
const vinPromise = Promise.resolve(vehicleLookupApiResponse);
|
||||
|
||||
wrapper.vm.validateZip = jest.fn().mockImplementation(() => zipPromise);
|
||||
wrapper.vm.lookupVehicle = jest.fn().mockImplementation(() => vinPromise);
|
||||
|
||||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
|
@ -87,26 +68,231 @@ describe("vin-lookup.vue", () => {
|
|||
//Assert
|
||||
expect(wrapper.vm.navigateForward).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Should do a VIN lookup if the user has clicked on the VIN field and entered a new VIN or changed a previously matched VIN.", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.vinTouched = true;
|
||||
wrapper.vm.vin = "foo";
|
||||
wrapper.vm.initialVin = "!foo";
|
||||
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
const vehicleLookupApiResponse = {
|
||||
data: {
|
||||
carId: 'new carId' // does not match the store value
|
||||
}
|
||||
};
|
||||
const vinPromise = Promise.resolve(vehicleLookupApiResponse);
|
||||
|
||||
wrapper.vm.lookupVehicle = jest.fn().mockImplementation(() => vinPromise);
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.lookupVehicle).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Should not call navigateForward() if the store carId does not match the vin response carId and forward button is clicked", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
// New lookup
|
||||
wrapper.vm.vinTouched = true;
|
||||
wrapper.vm.vin = "";
|
||||
wrapper.vm.initialVin = "foo";
|
||||
|
||||
const vehicleLookupApiResponse = {
|
||||
data: {
|
||||
carId: 'new carId' // does not match the store value
|
||||
}
|
||||
};
|
||||
const vinPromise = Promise.resolve(vehicleLookupApiResponse);
|
||||
|
||||
wrapper.vm.lookupVehicle = jest.fn().mockImplementation(() => vinPromise);
|
||||
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.navigateForward).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Should call navigateForward() if the store carId does not match the vin response carId but does match previously enterted carId and forward button is clicked", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
const vehicleLookupApiResponse = {
|
||||
data: {
|
||||
carId: 'new carId' // does not match the store value
|
||||
}
|
||||
};
|
||||
const vinPromise = Promise.resolve(vehicleLookupApiResponse);
|
||||
|
||||
wrapper.vm.lookupVehicle = jest.fn().mockImplementation(() => vinPromise);
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
|
||||
wrapper.vm.previouslyEnteredCarId = 'new carId';
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.navigateForward).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Should not call navigateForward() if zip service returns a non-serviceable flag", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
const zipValidationApiResponse = {
|
||||
data: {
|
||||
isServiceable: false
|
||||
}
|
||||
};
|
||||
|
||||
const zipPromise = Promise.resolve(zipValidationApiResponse);
|
||||
|
||||
wrapper.vm.validateZip = jest.fn().mockImplementation(() => zipPromise);
|
||||
wrapper.vm.setupUiForNonServiceableZip = jest.fn();
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
|
||||
wrapper.vm.previouslyEnteredCarId = 'new carId';
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.navigateForward).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("Should not call navigateForward() when forward button is clicked but lookupVehicle errors out.", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.vinTouched = true;
|
||||
wrapper.vm.vin = "foo";
|
||||
wrapper.vm.initialVin = "!foo";
|
||||
|
||||
const vehicleLookupApiResponse = {
|
||||
status: {
|
||||
carId: 'new carId' // does not match the store value
|
||||
}
|
||||
};
|
||||
const vinPromise = Promise.reject(vehicleLookupApiResponse);
|
||||
|
||||
const response = {
|
||||
status: 404
|
||||
};
|
||||
wrapper.vm.lookupVehicle = jest.fn().mockImplementation((response) => vinPromise);
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
|
||||
wrapper.vm.previouslyEnteredCarId = 'new carId';
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.navigateForward).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("navigateForward", () => {
|
||||
test("carId is different from returned vehicle and selected glass isn't available => continue with different glass", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
customMountOptions: {
|
||||
router: {
|
||||
navigateAfterSave: jest.fn()
|
||||
}
|
||||
}
|
||||
});
|
||||
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||
wrapper.setData({
|
||||
isCarIdDifferent: true,
|
||||
isSelectedGlassAvailableForVehicle: false
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.navigateForward();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigateAfterSave).toBeCalledTimes(1);
|
||||
expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalledWith(navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, wrapper.vm.$route, expect.anything(), expect.anything(), expect.anything());
|
||||
})
|
||||
|
||||
test("carId matches => navigateForwardWithSingleCarMatch", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||
wrapper.setData({
|
||||
isCarIdDifferent: false,
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.navigateForward();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toBeCalledTimes(1);
|
||||
})
|
||||
|
||||
test("selected glass is available for returned vehicle => navigateForwardWithSingleCarMatch", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
wrapper.vm.navigateForwardWithSingleCarMatch = jest.fn();
|
||||
wrapper.setData({
|
||||
isSelectedGlassAvailableForVehicle: true,
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.navigateForward();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toBeCalledTimes(1);
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
function setupMocks({ customMountOptions }) {
|
||||
const mountOptions = getMountOptions({
|
||||
...customMountOptions
|
||||
});
|
||||
|
||||
|
||||
const mountOptions = getMountOptions({});
|
||||
|
||||
const finalMountOptions = Object.assign(mountOptions, customMountOptions);
|
||||
// Modify/augment default mount options
|
||||
finalMountOptions.global.mocks["$store"] = store;
|
||||
finalMountOptions.global.mixins = [mockMixin];
|
||||
finalMountOptions['attachTo'] = document.body; // append wrapper to document.body to test DOM methods
|
||||
|
||||
const wrapper = shallowMount(vinLookup, finalMountOptions);
|
||||
return { wrapper };
|
||||
}
|
||||
mountOptions.global.mocks["$store"] = store;
|
||||
mountOptions.global.mixins = [mockMixin];
|
||||
mountOptions['attachTo'] = document.body; // append wrapper to document.body to test DOM methods
|
||||
|
||||
const mockMixin = {
|
||||
const wrapper = shallowMount(vinLookup, mountOptions);
|
||||
mockOutPromises(wrapper);
|
||||
mockOutStubFunctions(wrapper);
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
function mockOutPromises(wrapper) {
|
||||
const zipValidationApiResponse = {
|
||||
data: {
|
||||
isServiceable: true
|
||||
}
|
||||
};
|
||||
const vehicleLookupApiResponse = {
|
||||
data: {
|
||||
carId: 'initial carId'
|
||||
}
|
||||
};
|
||||
|
||||
const zipPromise = Promise.resolve(zipValidationApiResponse);
|
||||
const vinPromise = Promise.resolve(vehicleLookupApiResponse);
|
||||
|
||||
wrapper.vm.validateZip = jest.fn().mockImplementation(() => zipPromise);
|
||||
wrapper.vm.lookupVehicle = jest.fn().mockImplementation(() => vinPromise);
|
||||
}
|
||||
|
||||
function mockOutStubFunctions(wrapper) {
|
||||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
|
||||
}
|
||||
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn(() => "placeholder CMS content"),
|
||||
getCmsContent: jest.fn(() => "placeholder CMS content"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
<div class="page-container-grouped-styles">
|
||||
<loadingModal ref="loadingModal"/>
|
||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||
<vehicleBanner
|
||||
<vehicleBanner
|
||||
cmsWidgetName="VehicleBannerWidget"
|
||||
:displayGenericVehicleImage="false"
|
||||
/>
|
||||
|
|
@ -25,6 +25,9 @@
|
|||
validationRules="vin-required|vin-format"
|
||||
:isDisabled="isVinFieldReadOnly"
|
||||
maxLength="17"
|
||||
:mask="vinMask"
|
||||
@focus="setVinTouched"
|
||||
@maska="rawVinValue = $event.target.dataset.maskRawValue"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -55,7 +58,6 @@
|
|||
isRequired
|
||||
disableAutoFill
|
||||
validationRules="email-address-required|email-address-format"
|
||||
semiAggressiveValidation
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -132,7 +134,6 @@ import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
|||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import store from "@/store";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
|
|
@ -141,6 +142,8 @@ import { required, regex } from "@/helpers/validation-rules";
|
|||
import { Form, defineRule } from "vee-validate";
|
||||
import { navigateAfterSaveToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
import { getFunnelCookie } from "@/helpers/heritage-integration/cookie-helper";
|
||||
import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||
import { StatusCodes } from 'http-status-codes';
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
||||
|
|
@ -152,6 +155,7 @@ defineRule("vin-format", regex(/^[a-hA-Hj-nJ-NpPr-zR-Z0-9]{17}$/, errorMessages.
|
|||
|
||||
export default {
|
||||
name: "vin-lookup",
|
||||
mixins: [vinPagesMixin],
|
||||
async beforeRouteEnter(to, from, next) {
|
||||
// Call APIs
|
||||
const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);
|
||||
|
|
@ -183,10 +187,16 @@ export default {
|
|||
previouslyEnteredCarId: '',
|
||||
invalidZip: '',
|
||||
vinPopulatedOnPageLoad: this.getVinFromStore()?.length > 0,
|
||||
initialVin: this.getVinFromStore(),
|
||||
vinTouched: false,
|
||||
rawVinValue: "",
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
this.attachCustomEvents();
|
||||
if (this.vinPopulatedOnPageLoad) {
|
||||
this.setupVinMask();
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
vin() {
|
||||
|
|
@ -194,13 +204,17 @@ export default {
|
|||
this.$refs.funnelFooter.updateButtonText(
|
||||
this.getCmsContent("FunnelFooterWidget", "ForwardButtonText")
|
||||
);
|
||||
}
|
||||
},
|
||||
zip() {
|
||||
this.noServiceZip = false;
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
perfectMatchNewVinAlert() {
|
||||
const isVinPerfectMatch = this.vinPopulatedOnPageLoad && this.vin === this.getVinFromStore();
|
||||
this.updateIsCarIdDifferent(isVinPerfectMatch);
|
||||
return isVinPerfectMatch;
|
||||
const vinToCheck = this.vinTouched ? this.rawVinValue : this.initialVin;
|
||||
const isVinPerfectMatch = this.vinPopulatedOnPageLoad && vinToCheck === this.getVinFromStore();
|
||||
this.updateIsCarIdDifferent(isVinPerfectMatch);
|
||||
return isVinPerfectMatch;
|
||||
},
|
||||
MatchedDifferentVehicleAlertHeader(){
|
||||
const text = this.getCmsContent("MatchedDifferentVehicle",
|
||||
|
|
@ -238,10 +252,22 @@ export default {
|
|||
getIsWindshieldOnly())
|
||||
},
|
||||
isVinFieldReadOnly(){
|
||||
return this.$store.getters.payment.insuranceCoverage.isVerified || getFunnelCookie().HasDelayedClaimRegistration;
|
||||
return this.$store.getters.payment.insuranceCoverage.isVerified || getFunnelCookie().HasDelayedClaimRegistration;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setVinTouched() {
|
||||
if (!this.vinTouched) {
|
||||
this.vinMask = "XXXXXXXXXXXXXXXXX";
|
||||
this.vin = this.initialVin;
|
||||
}
|
||||
|
||||
this.vinTouched = true;
|
||||
},
|
||||
setupVinMask() {
|
||||
const lastSixChars = this.initialVin.substring(11, this.initialVin.length);
|
||||
this.vinMask = `!X!X!X!X!X!X!X!X!X!X!X${lastSixChars}`;
|
||||
},
|
||||
arePagePrerequisitesValid() {
|
||||
return store.getters.vehicle.carId !== null;
|
||||
},
|
||||
|
|
@ -285,22 +311,51 @@ export default {
|
|||
}
|
||||
},
|
||||
async forwardButtonAction() {
|
||||
let zipValidationResponse;
|
||||
let vehicleLookupResponse;
|
||||
|
||||
const zipValidation = this.validateZip(this.zip);
|
||||
const vehicleLookup = this.lookupVehicle(this.vin);
|
||||
const zipValidationResponse = await zipValidation;
|
||||
const vehicleLookupResponse = await vehicleLookup.catch(() => {
|
||||
this.vinNotFound = true;
|
||||
this.$refs.funnelFooter.removeLoader();
|
||||
this.noServiceZip = false;
|
||||
return;
|
||||
});
|
||||
if (!zipValidationResponse.data.isServiceable) {
|
||||
this.customAlertData.zip = this.zip;
|
||||
this.$refs.funnelFooter.removeLoader();
|
||||
this.noServiceZip = true;
|
||||
this.invalidZip = this.zip;
|
||||
|
||||
if (this.vinTouched && this.vin != this.initialVin) {
|
||||
// If the user has clicked on the VIN field, either they are doing a new VIN lookup or changing the VIN previously matched.
|
||||
// Therefore we need to do a Vehicle Lookup
|
||||
const vinToLookup = this.vinTouched ? this.vin : this.initialVin;
|
||||
const vehicleLookup = this.lookupVehicle(vinToLookup);
|
||||
|
||||
zipValidationResponse = await zipValidation;
|
||||
vehicleLookupResponse = await vehicleLookup.catch(() => {
|
||||
this.vinNotFound = true;
|
||||
this.$refs.funnelFooter.removeLoader();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Check if Service Zip entered is servicable, if not display an alert
|
||||
if (!zipValidationResponse.data.isServiceable) {
|
||||
this.setupUiForNonServiceableZip(this.zip);
|
||||
}
|
||||
|
||||
if (!vehicleLookupResponse || !zipValidationResponse.data.isServiceable) {
|
||||
this.$refs.funnelFooter.removeLoader();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const zipValidationResponse = await zipValidation;
|
||||
|
||||
// Check if Service Zip entered is serviceable, if not display an alert
|
||||
if (!zipValidationResponse.data.isServiceable) {
|
||||
this.setupUiForNonServiceableZip(this.zip);
|
||||
this.$refs.funnelFooter.removeLoader();
|
||||
return;
|
||||
} else {
|
||||
this.navigateForward();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vehicleLookupResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isCarIdDifferent = vehicleLookupResponse.data.carId !== store.getters.vehicle.carId;
|
||||
|
||||
if (this.isCarIdDifferent && (vehicleLookupResponse.data.carId !== this.previouslyEnteredCarId)) {
|
||||
|
|
@ -314,26 +369,27 @@ export default {
|
|||
this.isCarIdDifferent = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateStore(vehicleLookupResponse.data, zipValidationResponse.data);
|
||||
this.navigateForward();
|
||||
|
||||
},
|
||||
navigateForward(){
|
||||
if(this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle){
|
||||
this.$router.navigate(this.navigationScenarios.CLICKED_FORWARD, this.$route, {}, { displayVehicleChangeAlert: true }, {});
|
||||
if (this.isCarIdDifferent && !this.isSelectedGlassAvailableForVehicle) {
|
||||
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS, this.$route, {}, { displayVehicleChangeAlert: true }, {});
|
||||
return;
|
||||
} else {
|
||||
this.$refs.loadingModal.showModal();
|
||||
navigateAfterSaveToHeritageFunnel(this.$route);
|
||||
this.navigateForwardWithSingleCarMatch();
|
||||
return;
|
||||
}
|
||||
},
|
||||
validateZip(zip) {
|
||||
return baseMixin.methods.dispatchStoreAction(storeActions.VALIDATE_ZIP, {
|
||||
return this.dispatchStoreAction(storeActions.VALIDATE_ZIP, {
|
||||
zip,
|
||||
});
|
||||
},
|
||||
lookupVehicle(vin) {
|
||||
return baseMixin.methods.dispatchStoreAction(
|
||||
return this.dispatchStoreAction(
|
||||
storeActions.LOOKUP_VEHICLE_BY_VIN,
|
||||
{ vin }
|
||||
);
|
||||
|
|
@ -356,6 +412,11 @@ export default {
|
|||
store.commit(storeMutations.UPDATE_SERVICE_LOCATION_STATE, zipInfo.state);
|
||||
store.commit(storeMutations.UPDATE_CUSTOMER_EMAIL_ADDRESS, this.email);
|
||||
},
|
||||
setupUiForNonServiceableZip(zip) {
|
||||
this.customAlertData.zip = zip;
|
||||
this.invalidZip = zip;
|
||||
this.noServiceZip = true;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
Form,
|
||||
|
|
|
|||
28
src/mixins/vin-pages-mixin.js
Normal file
28
src/mixins/vin-pages-mixin.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import store from "@/store";
|
||||
import { storeActions } from "@/constants/store-actions.js";
|
||||
import { storeMutations } from "@/constants/store-mutations.js";
|
||||
import { navigateAfterSaveToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
async navigateForwardWithSingleCarMatch() {
|
||||
const result = await this.dispatchStoreAction(storeActions.GET_PARTS_OR_QUESTIONS);
|
||||
|
||||
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) {
|
||||
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS, this.$route, {}, {}, result.data);
|
||||
}
|
||||
else if (hasGlassLocationWithMultipleParts) {
|
||||
this.$router.navigateAfterSave(this.navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS, this.$route, {}, {}, result.data);
|
||||
}
|
||||
else {
|
||||
store.commit(storeMutations.UPDATE_GLASS_PARTS, result.data);
|
||||
this.$refs.loadingModal.showModal();
|
||||
navigateAfterSaveToHeritageFunnel(this.$route);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
921
src/mixins/vin-pages-mixin.spec.js
Normal file
921
src/mixins/vin-pages-mixin.spec.js
Normal file
|
|
@ -0,0 +1,921 @@
|
|||
import vinPagesMixin from "@/mixins/vin-pages-mixin";
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import { setupMocksForJsFiles, getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||
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 { navigateAfterSaveToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
|
||||
jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
|
||||
navigateAfterSaveToHeritageFunnel: jest.fn()
|
||||
}));
|
||||
|
||||
describe("vin-pages-mixin", () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
})
|
||||
|
||||
describe("navigateForwardWithSingleCarMatch", () => {
|
||||
describe("should go to parts-questions", () => {
|
||||
test("single glass location 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const { wrapper } = setupMocks({
|
||||
partsOrQuestions: partsOrQuestions
|
||||
});
|
||||
|
||||
// Act
|
||||
await wrapper.vm.navigateForwardWithSingleCarMatch();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigateAfterSave).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.vm.$router.navigateAfterSave).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.navigateAfterSave).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.vm.$router.navigateAfterSave).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.navigateAfterSave).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.vm.$router.navigateAfterSave).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.navigateAfterSave).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.vm.$router.navigateAfterSave).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.navigateAfterSave).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.vm.$router.navigateAfterSave).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.navigateAfterSave).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.vm.$router.navigateAfterSave).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.navigateAfterSave).toHaveBeenCalledTimes(1);
|
||||
expect(wrapper.vm.$router.navigateAfterSave).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(navigateAfterSaveToHeritageFunnel).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
|
||||
});
|
||||
|
||||
// wrapper.vm.navigateAfterSaveToHeritageFunnel = jest.fn();
|
||||
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(navigateAfterSaveToHeritageFunnel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setupMocks({ partsOrQuestions = [] }) {
|
||||
const baseMixin = setupMocksForJsFiles({
|
||||
actionList: [
|
||||
{
|
||||
actionName: storeActions.GET_PARTS_OR_QUESTIONS,
|
||||
data: {
|
||||
partsOrQuestions: partsOrQuestions
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const mocks = getMountOptions({
|
||||
router: {
|
||||
navigateAfterSave: jest.fn()
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
const mockVinComponent = {
|
||||
components: { loadingModal },
|
||||
template: '<loadingModal ref="loadingModal" />',
|
||||
// render() {
|
||||
// return '<div ref="loadingModal"></div>'
|
||||
// },
|
||||
mixins: [vinPagesMixin, baseMixin.baseMixin]
|
||||
};
|
||||
|
||||
// const mockVinComponent = Vue.component("mockcomponent", {
|
||||
// template: '<loadingModal ref="loadingModal" />',
|
||||
// mixins: [vinPagesMixin, baseMixin.baseMixin]
|
||||
// })
|
||||
|
||||
const wrapper = shallowMount(mockVinComponent, mocks);
|
||||
|
||||
wrapper.vm.$refs.loadingModal.showModal = jest.fn();
|
||||
|
||||
return { wrapper };
|
||||
}
|
||||
|
|
@ -18,22 +18,7 @@ import eventBus from "@/helpers/event-bus/event-bus";
|
|||
import store from "@/store";
|
||||
import analyticsMixin from "@/mixins/analytics-mixin";
|
||||
|
||||
// Components
|
||||
import ComponentTest from "@/layouts/component-test/component-test.vue";
|
||||
import FormTest from "@/layouts/form-test/form-test.vue";
|
||||
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/component-test", // This is a temporary route for testing.
|
||||
name: "ComponentTest",
|
||||
component: ComponentTest,
|
||||
},
|
||||
{
|
||||
path: "/form-test", // This is a temporary route for testing.
|
||||
name: "FormTest",
|
||||
component: FormTest,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
name: "root",
|
||||
|
|
@ -46,6 +31,7 @@ const routes = [
|
|||
|
||||
// If the saved session has timed out, clear the session, execute 404 logic.
|
||||
if (getFunnelCookie() !== null && !isSavedSessionStillActive()) {
|
||||
await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
|
||||
await GoToFunnelStartOn404(next);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,11 +8,11 @@ const navigationScenarios = {
|
|||
CLICKED_FORWARD: "CLICKED_FORWARD",
|
||||
CLICKED_FORWARD_WITH_VIN: "CLICKED_FORWARD_WITH_VIN",
|
||||
SELECTED_PARTS: "SELECTED_PARTS",
|
||||
CONTINUING_WITH_PARTS_QUESTION: "CONTINUING_WITH_PARTS_QUESTION",
|
||||
CONTINUING_WITH_MULTIPLE_PARTS: "CONTINUING_WITH_MULTIPLE_PARTS",
|
||||
SELECTED_VIN_WITH_PART_QUESTIONS: "SELECTED_VIN_WITH_PART_QUESTIONS",
|
||||
SELECTED_VIN_WITH_MULTIPLE_PARTS: "SELECTED_VIN_WITH_MULTIPLE_PARTS",
|
||||
CONTINUING_WITH_SINGLE_PART: "CONTINUING_WITH_SINGLE_PART",
|
||||
CONTINUING_WITH_MULTIPLE_VEHICLES: "CONTINUING_WITH_MULTIPLE_VEHICLES",
|
||||
CONTINUING_WITH_DIFFERENT_GLASS: "CONTINUING_WITH_DIFFERENT_GLASS",
|
||||
SELECTED_VIN_HAS_MISMATCHED_GLASS: "SELECTED_VIN_HAS_MISMATCHED_GLASS",
|
||||
CLICKED_FORWARD_WITHOUT_VIN: "CLICKED_FORWARD_WITHOUT_VIN",
|
||||
SELECTED_MANUAL_VIN: "SELECTED_MANUAL_VIN",
|
||||
SELECTED_LICENSE_PLATE: "SELECTED_LICENSE_PLATE",
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ const routingTable = [
|
|||
maps: [
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_PARTS,
|
||||
|
|
@ -105,12 +105,20 @@ const routingTable = [
|
|||
destinationFmgPageValue: fmgPageValues.ESTIMATE,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||
scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_VIN,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||
}
|
||||
],
|
||||
},
|
||||
|
|
@ -125,6 +133,14 @@ const routingTable = [
|
|||
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -139,9 +155,17 @@ const routingTable = [
|
|||
destinationFmgPageValue: fmgPageValues.ADDRESS_VEHICLES,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CONTINUING_WITH_DIFFERENT_GLASS,
|
||||
scenario: navigationScenarios.SELECTED_VIN_HAS_MISMATCHED_GLASS,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -154,7 +178,15 @@ const routingTable = [
|
|||
{
|
||||
scenario: navigationScenarios.CLICKED_FORWARD,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_DAMAGE,
|
||||
},
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_VIN_WITH_PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.SELECTED_VIN_WITH_MULTIPLE_PARTS,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||
}
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -178,6 +210,15 @@ const routingTable = [
|
|||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
fmgPageValue: fmgPageValues.PART_QUESTIONS,
|
||||
maps: [
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK,
|
||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP
|
||||
}
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
export { routingTable };
|
||||
|
|
|
|||
|
|
@ -115,7 +115,7 @@ export const mutations = {
|
|||
updateGlassToReplace(state, glassToReplace) {
|
||||
state.order.damage.glassToReplace = glassToReplace;
|
||||
},
|
||||
updateParts(state, partsData) {
|
||||
updateGlassParts(state, partsData) {
|
||||
state.order.lineItems.glassParts = partsData;
|
||||
},
|
||||
updatePageData(state, pageData) {
|
||||
|
|
@ -216,9 +216,8 @@ export const mutations = {
|
|||
state.order.vehicle.registration.firstName = null;
|
||||
state.order.vehicle.registration.lastName = null;
|
||||
},
|
||||
resetPartsState(state) {
|
||||
resetGlassPartsState(state) {
|
||||
state.order.lineItems.glassParts = null;
|
||||
state.order.lineItems.otherParts = null;
|
||||
},
|
||||
resetState(state) {
|
||||
Object.assign(state, getDefaultState());
|
||||
|
|
@ -404,14 +403,14 @@ export const actions = {
|
|||
},
|
||||
resetDamageAndDependencies(context) {
|
||||
context.commit(storeMutations.RESET_DAMAGE_STATE);
|
||||
context.commit(storeMutations.RESET_PARTS_STATE);
|
||||
context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||
},
|
||||
resetRegistrationAndDependencies(context) {
|
||||
context.commit(storeMutations.RESET_REGISTRATION_STATE);
|
||||
context.commit(storeMutations.RESET_PARTS_STATE)
|
||||
context.commit(storeMutations.RESET_GLASS_PARTS_STATE)
|
||||
},
|
||||
resetPartsAndDependencies(context) {
|
||||
context.commit(storeMutations.RESET_PARTS_STATE);
|
||||
context.commit(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||
},
|
||||
resetState(context) {
|
||||
context.commit(storeMutations.RESET_STATE);
|
||||
|
|
@ -543,7 +542,16 @@ export const actions = {
|
|||
|
||||
|
||||
// Parts API Actions
|
||||
getPartsOrQuestions(context, { carId, glassArray, zipCode, vin = '' }) {
|
||||
getPartsOrQuestions(context) {
|
||||
const vehicle = context.getters.vehicle;
|
||||
const damage = context.getters.damage;
|
||||
const order = context.state.order;
|
||||
|
||||
const carId = vehicle.carId;
|
||||
const glassArray = damage.glassToReplace;
|
||||
const zipCode = order.serviceLocation.zipCode;
|
||||
const vin = vehicle.vin;
|
||||
|
||||
return globalMethods.callHttpClient({
|
||||
method: endpoints.GetPartsOrQuestions.method,
|
||||
endpoint: endpoints.GetPartsOrQuestions.url,
|
||||
|
|
|
|||
|
|
@ -183,7 +183,7 @@ describe("Mutations", () => {
|
|||
const storeState = state;
|
||||
|
||||
// Act
|
||||
mutations.updateParts(storeState, { 'Windshield-Single': 'PARTNUM101'});
|
||||
mutations.updateGlassParts(storeState, { 'Windshield-Single': 'PARTNUM101'});
|
||||
|
||||
// Assert
|
||||
expect(storeState.order.lineItems.glassParts).toEqual({ 'Windshield-Single': 'PARTNUM101'});
|
||||
|
|
@ -446,7 +446,7 @@ describe("Actions", () => {
|
|||
await actions.resetDamageAndDependencies(context)
|
||||
|
||||
expect(commit).toBeCalledWith(storeMutations.RESET_DAMAGE_STATE);
|
||||
expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_STATE);
|
||||
expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -462,7 +462,7 @@ describe("Actions", () => {
|
|||
await actions.resetRegistrationAndDependencies(context)
|
||||
|
||||
expect(commit).toBeCalledWith(storeMutations.RESET_REGISTRATION_STATE);
|
||||
expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_STATE);
|
||||
expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -477,7 +477,7 @@ describe("Actions", () => {
|
|||
// Act
|
||||
await actions.resetPartsAndDependencies(context)
|
||||
|
||||
expect(commit).toBeCalledWith(storeMutations.RESET_PARTS_STATE);
|
||||
expect(commit).toBeCalledWith(storeMutations.RESET_GLASS_PARTS_STATE);
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -747,7 +747,7 @@ describe("Getters", () => {
|
|||
const storeState = state;
|
||||
|
||||
// Act
|
||||
mutations.updateParts(storeState, {"Rear-Stationary": 'PART101'});
|
||||
mutations.updateGlassParts(storeState, {"Rear-Stationary": 'PART101'});
|
||||
|
||||
// Assert
|
||||
expect(getters.lineItems(storeState).glassParts).toEqual({"Rear-Stationary": 'PART101'});
|
||||
|
|
|
|||
|
|
@ -78,6 +78,10 @@ export default {
|
|||
validationRules: String,
|
||||
selectedValues: [Array, String],
|
||||
hasError: Boolean,
|
||||
clearOnUnmount: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -92,9 +96,11 @@ export default {
|
|||
: this.selectedValues[0];
|
||||
}
|
||||
},
|
||||
unmounted() { // needed to clear this button's selectedValues if it is removed
|
||||
this.checkValue = false;
|
||||
this.handleCheckChange();
|
||||
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: {
|
||||
displayLoader() {
|
||||
|
|
|
|||
|
|
@ -78,6 +78,10 @@ export default {
|
|||
validationRules: String,
|
||||
selectedValues: [Array, String],
|
||||
hasError: Boolean,
|
||||
clearOnUnmount: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -92,9 +96,11 @@ export default {
|
|||
: this.selectedValues[0];
|
||||
}
|
||||
},
|
||||
unmounted() { // needed to clear this button's selectedValues if it is removed
|
||||
this.checkValue = false;
|
||||
this.handleCheckChange();
|
||||
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: {
|
||||
displayLoader() {
|
||||
|
|
|
|||
|
|
@ -86,6 +86,10 @@ export default {
|
|||
selectedValues: [Array, String],
|
||||
modelValue: Object,
|
||||
hasError: Boolean,
|
||||
clearOnUnmount: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
|
@ -99,9 +103,11 @@ export default {
|
|||
: this.selectedValues[0];
|
||||
}
|
||||
},
|
||||
unmounted() { // needed to clear this button's selectedValues if it is removed
|
||||
this.checkValue = false;
|
||||
this.handleCheckChange();
|
||||
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: {
|
||||
getLabelClasses() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue