Merge branch 'develop' into feature/CSR-803-refactoring-2
This commit is contained in:
commit
720d79ea9d
32 changed files with 1570 additions and 279 deletions
|
|
@ -88,7 +88,6 @@ stages:
|
|||
__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__: $(__VUE_APP_GOOGLE_TAG_MANAGER_NOSCRIPT_FRAME_SRC__)
|
||||
cfDistributionId: $(cfDistributionId)
|
||||
|
||||
|
||||
# QA Build/Deploy
|
||||
- stage: Qa
|
||||
condition: eq(variables['Build.SourceBranch'], variables['qa-branch'] )
|
||||
|
|
|
|||
|
|
@ -28,8 +28,7 @@ module.exports = {
|
|||
testMatch: ["**/*.spec.(js|jsx|ts|tsx)|**/__tests__/*.(js|jsx|ts|tsx)"],
|
||||
coverageThreshold: {
|
||||
global: {
|
||||
// TODO after release/2022.09.15, raise this back up!!
|
||||
statements: 80,
|
||||
statements: 85,
|
||||
// Got the go ahead from Mark to temporarily lower this. Taking out initialize component made the year,make,model and style coverage drop a bit. Once unit tests for license plate lookup, vin lookup and address lookup are in the coverage should go back up to 90
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -175,11 +175,16 @@ export default {
|
|||
if(this.selectingInitiatesLoad) {
|
||||
this.selectedValues = val.value;
|
||||
} else {
|
||||
if(Array.isArray(this.selectedValues)) {
|
||||
if(this.isMultiSelect) {
|
||||
const newSelectedValues = this.selectedValues;
|
||||
val.checkValue ? newSelectedValues.push(val.value) : newSelectedValues.splice(newSelectedValues.indexOf(val.value), 1);
|
||||
this.selectedValues = newSelectedValues;
|
||||
}
|
||||
else if (Array.isArray(this.selectedValues)) {
|
||||
this.selectedValues[0] = val.value;
|
||||
const temp = this.selectedValues;
|
||||
this.selectedValues = temp;
|
||||
}
|
||||
else {
|
||||
this.selectedValues = val.value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@
|
|||
<div class="modal fade modal-component" :id="this.cmsWidgetName" tabindex="-1" aria-labelledby="ModalComponentLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-body px-4 py-5">
|
||||
<div class="modal-body ps-4 pe-4 pt-5 pb-4">
|
||||
<img :src="this.ModalImage" class="w-100 mb-4" alt="" />
|
||||
<h5 class="mb-4" v-html="this.ModalHeadline"></h5>
|
||||
<p class="fw-bold mb-2" v-html="this.ModalSubheadertext"></p>
|
||||
<p class="fw-bold mb-2 subheader-text" v-html="this.ModalSubheadertext"></p>
|
||||
<p class="mb-0" v-html="this.ModalBodyText"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<div class="modal-footer px-4 pt-4 pb-5">
|
||||
<button class="btn btn-secondary w-100" data-bs-dismiss="modal" v-html="this.ModalCloseButtonText"></button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -45,6 +45,11 @@ export default {
|
|||
|
||||
<style lang="scss">
|
||||
.modal {
|
||||
h5,
|
||||
strong,
|
||||
.subheader-text {
|
||||
color: $black;
|
||||
}
|
||||
&.modal-component {
|
||||
.modal-dialog {
|
||||
.modal-content {
|
||||
|
|
@ -60,6 +65,9 @@ export default {
|
|||
}
|
||||
.modal-footer {
|
||||
border-top: none;
|
||||
button {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1,60 @@
|
|||
test.todo("some test to be written in the future");
|
||||
import { shallowMount } from "@vue/test-utils";
|
||||
import TextBlock from "./text-block";
|
||||
|
||||
|
||||
describe("modal.vue", () => {
|
||||
it("Should display 'Text' when 'Text' is defined in the CMS", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(TextBlock, {
|
||||
mixins: [mockMixin]
|
||||
});
|
||||
expect(wrapper.html()).toEqual(expect.stringContaining(mockCmsContent['Text']));
|
||||
});
|
||||
|
||||
it("Should contain the typeStyle class as defined by the prop", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(TextBlock, {
|
||||
mixins: [mockMixin],
|
||||
propsData: mockProps,
|
||||
});
|
||||
expect(wrapper.html()).toEqual(expect.stringContaining(mockProps['typeStyle']));
|
||||
});
|
||||
it("Should contain the justifyText class as defined by the prop", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(TextBlock, {
|
||||
mixins: [mockMixin],
|
||||
propsData: mockProps,
|
||||
});
|
||||
expect(wrapper.html()).toEqual(expect.stringContaining(mockProps['justifyText']));
|
||||
});
|
||||
it("Should contain the fontWeight class as defined by the prop", async () => {
|
||||
// Act
|
||||
const wrapper = shallowMount(TextBlock, {
|
||||
mixins: [mockMixin],
|
||||
propsData: mockProps,
|
||||
});
|
||||
expect(wrapper.html()).toEqual(expect.stringContaining(mockProps['fontWeight']));
|
||||
});
|
||||
});
|
||||
|
||||
///////////////
|
||||
// Constants //
|
||||
///////////////
|
||||
|
||||
const mockMixin = {
|
||||
methods: {
|
||||
getCmsContent: jest.fn((widgetName, cmsFieldName) => {
|
||||
return mockCmsContent[cmsFieldName];
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const mockProps = {
|
||||
fontWeight: 'mockFontWeight',
|
||||
typeStyle: 'mockTypeStyle',
|
||||
justifyText: 'mockJustifyText'
|
||||
}
|
||||
|
||||
const mockCmsContent = {
|
||||
'Text': "Sample text here.",
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="text-block d-flex w-100" :class="[justifyText, typeStyle, fontWeight]" v-html="this.TextBlockCopy"></div>
|
||||
<div class="text-block d-flex w-100 mt-2" :class="[justifyText, typeStyle, fontWeight]" v-html="this.TextBlockCopy"></div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -34,5 +34,5 @@ export default {
|
|||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -95,7 +95,7 @@ async function getLatestPageForRedirection() {
|
|||
else if (partQuestionsComponent.methods.arePagePrerequisitesValid()) {
|
||||
return fmgPageValues.PART_QUESTIONS;
|
||||
}
|
||||
else if (vinLookupComponent.methods.arePagePrerequisitesValid()) {
|
||||
else if (vinLookupComponent.methods.arePagePrerequisitesValid() && !store.getters.damage.isRepair) {
|
||||
return fmgPageValues.VIN_LOOKUP;
|
||||
}
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" ref="vehicleBanner" :displayGenericVehicleImage=false />
|
||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" ref="funnelSubHeader" />
|
||||
<div class="fade-on-route-transition sub-container make-tall">
|
||||
<customerQuestions ref="customerQuestions" v-model="customerQuestions" />
|
||||
<customerQuestions ref="customerQuestions" v-model="customerQuestions" />
|
||||
<alert ref="alertVinNotFound"
|
||||
v-if="displayVinNotFoundAlert"
|
||||
class="mb-4"
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-5">
|
||||
<div class="row mt-4">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
cmsWidgetName="EmailAddressQuestionWidget"
|
||||
|
|
@ -35,7 +35,15 @@
|
|||
validationRules="email-address-required|email-address-format"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<textBlock
|
||||
cmsWidgetName="QuoteEmailTextBlockWidget"
|
||||
typeStyle="caption"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
@ -45,6 +53,7 @@ import { defineRule } from "vee-validate";
|
|||
import { required } from "@/helpers/validation-rules";
|
||||
import { regex } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import textBlock from '@/common-components/text-block/text-block';
|
||||
|
||||
// DEFINE VALIDATION RULES
|
||||
defineRule("first-name-required", required(errorMessages.FIRST_NAME_REQUIRED));
|
||||
|
|
@ -87,6 +96,7 @@ export default ({
|
|||
components: {
|
||||
addressQuestions,
|
||||
textboxQuestion,
|
||||
textBlock
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
|
@ -36,48 +36,6 @@ describe("estimate.vue", () => {
|
|||
//Assesrt
|
||||
expect(wrapper.emitted()["update:modelValue"][0]).toEqual([{ modelValue: ["Provide my license plate # Most accurate VIN match"] }]);
|
||||
});
|
||||
test("isRepair is set to true, arePagePrerequisitesValid should return true", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
store.commit( storeMutations.UPDATE_IS_REPAIR, true );
|
||||
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
//Assert
|
||||
expect(arePagePrerequisitesValid).toBe(true);
|
||||
});
|
||||
test("isRepair is set to true, arePagePrerequisitesValid should return true", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
store.commit( storeMutations.UPDATE_IS_REPAIR, true );
|
||||
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
//Assert
|
||||
expect(arePagePrerequisitesValid).toBe(true);
|
||||
});
|
||||
test.todo("isRepair is false and there are no lineItems => should return false")
|
||||
test.todo("isRepair is false and lineItems is null => should return false")
|
||||
test.todo("isRepair is false are there are lineItems => should return true")
|
||||
test("isRepair is set to null, arePagePrerequisitesValid should return false", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
store.commit( storeMutations.UPDATE_IS_REPAIR, null );
|
||||
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
//Assert
|
||||
expect(arePagePrerequisitesValid).toBe(false);
|
||||
});
|
||||
|
||||
test("After selecting provide my home address on ForwardButtonAction triggers a router.navigateWithSaving", async () => {
|
||||
|
||||
|
|
@ -132,26 +90,125 @@ describe("estimate.vue", () => {
|
|||
|
||||
test("Provide my license plate on ForwardButtonAction triggers a router.navigateWithSaving", async () => {
|
||||
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
await wrapper.setData({
|
||||
selectedVinLookupMethod: vinLookupMethodSelections.LICENSEPLATE
|
||||
})
|
||||
|
||||
//Act
|
||||
wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
await wrapper.setData({
|
||||
selectedVinLookupMethod: vinLookupMethodSelections.LICENSEPLATE
|
||||
})
|
||||
|
||||
//Act
|
||||
wrapper.vm.forwardButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
|
||||
|
||||
});
|
||||
|
||||
describe("arePagePrerequisitesValid", () => {
|
||||
beforeEach(() => {
|
||||
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
|
||||
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, null);
|
||||
})
|
||||
|
||||
test("isRepair is false and there are no glassToReplace => should return false", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
|
||||
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, []);
|
||||
|
||||
// Act
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(arePagePrerequisitesValid).toBe(false);
|
||||
})
|
||||
|
||||
test("isRepair is false and glassToReplace is null => should return false", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
|
||||
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, null);
|
||||
|
||||
// Act
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(arePagePrerequisitesValid).toBe(false);
|
||||
})
|
||||
|
||||
test("isRepair is false are there is one glassToReplace => should return true", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
|
||||
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [{glassLocation: "TEST", glassName: "NAME"}]);
|
||||
|
||||
// Act
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(arePagePrerequisitesValid).toBe(true);
|
||||
})
|
||||
|
||||
test("isRepair is false are there are multiple glassToReplace => should return true", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
store.commit(storeMutations.UPDATE_IS_REPAIR, false);
|
||||
store.commit(storeMutations.UPDATE_GLASS_TO_REPLACE, [{glassLocation: "TEST1", glassName: "NAME1"}, {glassLocation: "TEST2", glassName: "NAME2"}, {glassLocation: "TEST3", glassName: "NAME3"}]);
|
||||
|
||||
// Act
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(arePagePrerequisitesValid).toBe(true);
|
||||
})
|
||||
|
||||
test("isRepair is set to null => arePagePrerequisitesValid should return false", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
store.commit(storeMutations.UPDATE_IS_REPAIR, null);
|
||||
|
||||
// Act
|
||||
console.log(store.getters.damage)
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(arePagePrerequisitesValid).toBe(false);
|
||||
});
|
||||
|
||||
test("isRepair is set to true => arePagePrerequisitesValid should return true", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
store.commit(storeMutations.UPDATE_IS_REPAIR, true);
|
||||
|
||||
// Act
|
||||
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
|
||||
|
||||
// Assert
|
||||
expect(arePagePrerequisitesValid).toBe(true);
|
||||
});
|
||||
})
|
||||
|
||||
test("Changing zip should reset alert", async () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
//Act
|
||||
wrapper.vm.displayNonServiceableZipAlert = "true";
|
||||
wrapper.vm.serviceZipCode = "43015";
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.displayNonServiceableZipAlert).toEqual(false);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function setupMocks({
|
||||
groupName = "estimate",
|
||||
cmsQuestionText = "Let's get your VIN. Or we can look it up for you!",
|
||||
cmsAnswers = [{ Name: "Provide my VIN manually Most specific to your vehicle" }, { Name: "Provide my license plate # Most accurate VIN match" }, { Name: "Provide my home address Most convenient VIN match" }],
|
||||
funnelFooterWidget = { ForwardButtonText: "test txt" },
|
||||
mountOptionsMockData = {
|
||||
router: {
|
||||
navigate: jest.fn(),
|
||||
|
|
@ -165,12 +222,14 @@ function setupMocks({
|
|||
const cmsContent = {
|
||||
groupName: groupName,
|
||||
QuestionText: cmsQuestionText,
|
||||
Answers: cmsAnswers
|
||||
Answers: cmsAnswers,
|
||||
FunnelFooterWidget: funnelFooterWidget
|
||||
};
|
||||
|
||||
const apiPromise = Promise.resolve(cmsContent);
|
||||
const apiPromise = Promise.resolve({ cmsContent });
|
||||
settleAllPromises.mockImplementation(() => apiPromise);
|
||||
fetchCmsContentForPage.mockImplementation(() => Promise.resolve());
|
||||
|
||||
|
||||
const mountOptions = getMountOptions({ ...mountOptionsMockData, mixins: [baseMixin] });
|
||||
mountOptions['attachTo'] = document.body;
|
||||
|
|
|
|||
|
|
@ -6,26 +6,86 @@
|
|||
v-slot="{ meta }"
|
||||
>
|
||||
<div class="page-container-grouped-styles">
|
||||
<loadingModal ref="loadingModal"/>
|
||||
<funnelHeader cmsWidgetName="FunnelHeaderWidget" />
|
||||
<vehicleBanner cmsWidgetName="VehicleBannerWidget" />
|
||||
<funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
|
||||
<div class="fade-on-route-transition sub-container make-tall">
|
||||
<alert
|
||||
class="vinLookupMethodHeading"
|
||||
cmsWidgetName="AlertVinLookupQuestion"
|
||||
alertClass=""
|
||||
/>
|
||||
<buttonQuestion
|
||||
cmsWidgetName="VinLookupMethod"
|
||||
:answers="answersFromCms"
|
||||
groupName="vinLookupMethodOption"
|
||||
buttonType="listButton"
|
||||
v-model="selectedVinLookupMethod"
|
||||
isRequired
|
||||
validationRules="option-required"
|
||||
/>
|
||||
<div v-if="!isRepair">
|
||||
<alert
|
||||
class="vinLookupMethodHeading"
|
||||
cmsWidgetName="AlertVinLookupQuestion"
|
||||
alertClass=""
|
||||
/>
|
||||
<buttonQuestion
|
||||
cmsWidgetName="VinLookupMethod"
|
||||
:answers="answersFromCms"
|
||||
groupName="vinLookupMethodOption"
|
||||
buttonType="listButton"
|
||||
v-model="selectedVinLookupMethod"
|
||||
isRequired
|
||||
validationRules="option-required"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="isRepair">
|
||||
<alert
|
||||
class="my-4"
|
||||
cmsWidgetName="AlertQuoteReady"
|
||||
alertClass="alert-info"
|
||||
v-bind:isDismissible="false"
|
||||
/>
|
||||
<div class="row my-2">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
cmsWidgetName="ServiceZipQuestionWidget"
|
||||
v-model="serviceZipCode"
|
||||
inputId="serviceZipCode"
|
||||
mask="#####"
|
||||
isRequired
|
||||
disableAutoFill
|
||||
validationRules="zip-required|zip-format"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
cmsWidgetName="EmailAddressQuestionWidget"
|
||||
v-model="emailAddress"
|
||||
inputId="emailAddress"
|
||||
isRequired
|
||||
disableAutoFill
|
||||
validationRules="email-address-required|email-address-format"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
<textBlock
|
||||
cmsWidgetName="QuoteEmailTextBlockWidget"
|
||||
typeStyle="caption"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<alert ref="alertInvalidZip"
|
||||
v-if="displayInvalidZipAlert"
|
||||
class="my-4"
|
||||
cmsWidgetName="AlertInvalidZipWidget"
|
||||
alertClass="alert-danger"
|
||||
v-bind:isDismissible="false"
|
||||
/>
|
||||
<alert
|
||||
class="my-4"
|
||||
:manualHeadline="AlertNonServiceableZipHeader"
|
||||
:manualCopy="AlertNonServiceableZipBody"
|
||||
v-model="customAlertData"
|
||||
v-if="displayNonServiceableZipAlert"
|
||||
alertClass="alert-danger"
|
||||
/>
|
||||
</div>
|
||||
<funnel-footer
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
ref="funnelFooter"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
@isDisabled="!meta.valid"
|
||||
@back-clicked="backButtonAction"
|
||||
|
|
@ -37,6 +97,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
// Components
|
||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
|
||||
|
|
@ -44,22 +105,37 @@ import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
|
|||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||
import buttonQuestion from "@/common-components/button-question/button-question";
|
||||
import alert from "@/ux-components/alert/alert";
|
||||
import textboxQuestion from "@/common-components/textbox-question/textbox-question";
|
||||
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
||||
import textBlock from "@/common-components/text-block/text-block";
|
||||
|
||||
//Supporting Files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
import { required } from "@/helpers/validation-rules";
|
||||
import { required, regex } from "@/helpers/validation-rules";
|
||||
import { errorMessages } from "@/constants/error-messages";
|
||||
import { Form, defineRule } from "vee-validate";
|
||||
import store from "@/store";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { vinLookupMethodSelections } from "@/constants/vin-lookup-method-selections.js";
|
||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
|
||||
// Define Validation Rules
|
||||
defineRule("zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
|
||||
defineRule("zip-format", regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT));
|
||||
defineRule("email-address-required", required(errorMessages.EMAIL_ADDRESS_REQUIRED));
|
||||
defineRule("email-address-format", regex(/^([a-zA-Z0-9_\-.+]+)@([a-zA-Z0-9_\-.]+)\.([a-zA-Z]{2,})$/, errorMessages.EMAIL_ADDRESS_FORMAT));
|
||||
defineRule("option-required", required(errorMessages.OPTION_REQUIRED));
|
||||
|
||||
export default {
|
||||
name: "estimate",
|
||||
data() {
|
||||
return {
|
||||
selectedVinLookupMethod: "",
|
||||
serviceZipCode: this.getZipFromStore(),
|
||||
emailAddress: this.getEmailFromStore(),
|
||||
displayInvalidZipAlert: false,
|
||||
displayNonServiceableZipAlert: false,
|
||||
};
|
||||
},
|
||||
|
||||
|
|
@ -75,10 +151,28 @@ export default {
|
|||
];
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
next((vm) => {
|
||||
if (resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText.includes("|"))
|
||||
{
|
||||
const forwardTextOption = resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText.split("|");
|
||||
if (store.getters.damage.isRepair){
|
||||
resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText = forwardTextOption[1]
|
||||
}
|
||||
else
|
||||
{
|
||||
resultMap.cmsContent.FunnelFooterWidget.ForwardButtonText = forwardTextOption[0]
|
||||
}
|
||||
}
|
||||
|
||||
vm.setCmsContent(resultMap.cmsContent);
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
getZipFromStore(){
|
||||
return store.getters.order.serviceLocation.zipCode;
|
||||
},
|
||||
getEmailFromStore(){
|
||||
return store.getters.order.customer.emailAddress;
|
||||
},
|
||||
arePagePrerequisitesValid() {
|
||||
return store.getters.damage.isRepair || store.getters.damage.glassToReplace?.length > 0;
|
||||
},
|
||||
|
|
@ -90,6 +184,31 @@ export default {
|
|||
);
|
||||
},
|
||||
async forwardButtonAction() {
|
||||
if (this.isRepair){
|
||||
const zipCodeData = await this.getZipCodeData(this.serviceZipCode);
|
||||
//todo: validation
|
||||
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailAddress, false);
|
||||
await this.dispatchStoreAction(storeActions.SAVE_SERVICE_LOCATION, {
|
||||
zipCode: this.serviceZipCode,
|
||||
state: zipCodeData.state,
|
||||
}, false);
|
||||
|
||||
if (!zipCodeData.isValid) {
|
||||
this.displayInvalidZipAlert = true;
|
||||
return this.$refs.funnelFooter.removeLoader();
|
||||
}
|
||||
this.displayInvalidZipAlert = false;
|
||||
|
||||
if (!zipCodeData.isServiceable) {
|
||||
this.displayNonServiceableZipAlert = true;
|
||||
return this.$refs.funnelFooter.removeLoader();
|
||||
}
|
||||
this.displayNonServiceableZipAlert = false;
|
||||
|
||||
this.$refs.loadingModal.showModal();
|
||||
navigateToHeritageFunnel();
|
||||
}
|
||||
|
||||
if (this.selectedVinLookupMethod === vinLookupMethodSelections.MANUALVIN) {
|
||||
await this.dispatchStoreAction(storeActions.CLEAR_VIN);
|
||||
return this.$router.navigateWithSaving(
|
||||
|
|
@ -112,13 +231,27 @@ export default {
|
|||
},
|
||||
},
|
||||
computed: {
|
||||
AlertNonServiceableZipHeader(){
|
||||
return this.getCmsContent("AlertNonServiceableZipWidget", "HeadlineText").replaceAll("{custom:serviceZip}", this.serviceZipCode);
|
||||
},
|
||||
AlertNonServiceableZipBody(){
|
||||
return this.getCmsContent("AlertNonServiceableZipWidget", "BodyText");
|
||||
},
|
||||
questionText() {
|
||||
return this.getCmsContent("VinLookupMethod", "QuestionText");
|
||||
},
|
||||
answersFromCms() {
|
||||
return this.getCmsContent("VinLookupMethod", "Answers");
|
||||
},
|
||||
isRepair() {
|
||||
return store.getters.damage.isRepair;
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
serviceZipCode() {
|
||||
this.displayNonServiceableZipAlert = false;
|
||||
},
|
||||
},
|
||||
components: {
|
||||
funnelHeader,
|
||||
vehicleBanner,
|
||||
|
|
@ -127,6 +260,9 @@ export default {
|
|||
buttonQuestion,
|
||||
Form,
|
||||
alert,
|
||||
textboxQuestion,
|
||||
loadingModal,
|
||||
textBlock
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-2">
|
||||
<div class="row mt-2">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
cmsWidgetName="EmailAddressQuestionWidget"
|
||||
|
|
@ -46,6 +46,14 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
<textBlock
|
||||
cmsWidgetName="QuoteEmailTextBlockWidget"
|
||||
typeStyle="caption"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<alert
|
||||
v-if="displayInvalidZipAlert"
|
||||
class="my-3"
|
||||
|
|
@ -109,6 +117,7 @@ import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-he
|
|||
import alert from "@/ux-components/alert/alert";
|
||||
import textboxQuestion from "@/common-components/textbox-question/textbox-question";
|
||||
import loadingModal from "@/common-components/loading-modal/loading-modal.vue";
|
||||
import textBlock from '@/common-components/text-block/text-block';
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
|
|
@ -360,7 +369,8 @@ export default {
|
|||
funnelSubHeader,
|
||||
textboxQuestion,
|
||||
alert,
|
||||
loadingModal,
|
||||
loadingModal,
|
||||
textBlock,
|
||||
Form
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,6 +30,76 @@
|
|||
typeStyle="caption"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">h1</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="h1"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">h2</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="h2"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">h3</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="h3"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">h4</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="h4"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">h5</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="h5"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">h6</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="h6"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">Body</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="body"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">Label</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="label"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">Small</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="small"
|
||||
class="mt-4"
|
||||
/>
|
||||
<h3 style="m-0">Caption</h3>
|
||||
<textBlock
|
||||
cmsWidgetName="quoteDisclaimer"
|
||||
justifyText="left"
|
||||
typeStyle="caption"
|
||||
class="mt-4"
|
||||
/>
|
||||
<funnel-footer
|
||||
cmsWidgetName="FunnelFooterWidget"
|
||||
:isForwardActionDisabled="!meta.valid"
|
||||
|
|
@ -75,10 +145,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
arePagePrerequisitesValid() {
|
||||
if (store.getters.order.damage.isRepair || store.getters.order.lineItems.glassParts.length > 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return store.getters.order.damage.isRepair || (store.getters.order.lineItems?.glassParts != null && store.getters.order.lineItems.glassParts.length > 0);
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,8 @@ export default ({
|
|||
return this.modelValue;
|
||||
},
|
||||
set: function(newValue) {
|
||||
this.$emit("update:modelValue", newValue);
|
||||
const numberValue = Number(newValue);
|
||||
this.$emit("update:modelValue", numberValue);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ describe("vehicle-parts.vue", () => {
|
|||
wrapper.vm.backButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS, wrapper.vm.$route);
|
||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, wrapper.vm.$route);
|
||||
|
||||
});
|
||||
|
||||
|
|
@ -277,7 +277,7 @@ describe("vehicle-parts.vue", () => {
|
|||
wrapper.vm.backButtonAction();
|
||||
|
||||
//Assert
|
||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP, wrapper.vm.$route);
|
||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, wrapper.vm.$route);
|
||||
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
|||
import { storeActions } from "@/constants/store-actions";
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import router from "@/router"
|
||||
import store from "@/store"
|
||||
import { storeMutations } from "@/constants/store-mutations";
|
||||
|
||||
// Mock our module for promises.
|
||||
jest.mock("@/helpers/layout-helper.js", () => ({
|
||||
|
|
@ -21,25 +24,16 @@ jest.mock("@/helpers/cms-content-helper", () => ({
|
|||
fetchCmsContentForPage: jest.fn(),
|
||||
}));
|
||||
|
||||
// Mock Store
|
||||
jest.mock("@/store", () => ({
|
||||
getters: {
|
||||
vehicle: {
|
||||
model: "TL",
|
||||
},
|
||||
applicationUser:{
|
||||
pageData: {
|
||||
"part-questions": null,
|
||||
"vehicle-make": {},
|
||||
"vehicle-model": {},
|
||||
"vehicle-style": {},
|
||||
"vehicle-damage": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
// Mock fetchCmsContentForPage
|
||||
jest.mock("@/router", () => ({
|
||||
overrideNavigation: jest.fn(),
|
||||
}));
|
||||
|
||||
describe("vehicle-style.vue", () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
})
|
||||
|
||||
test("Style question component is initized with api data", async (done) => {
|
||||
//Arrange
|
||||
const styleQuestionInitialData = ["2 Door", "4 Door"];
|
||||
|
|
@ -63,9 +57,7 @@ describe("vehicle-style.vue", () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("vehicle-style.vue", () => {
|
||||
test("BackButtonAction triggers a router.navigateWithoutSaving change", async (done) => {
|
||||
//Arrange
|
||||
const { wrapper, apiPromise } = setupMocks({
|
||||
|
|
@ -95,20 +87,12 @@ describe("vehicle-style.vue", () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("vehicle-style.vue", () => {
|
||||
test("selectVehicle triggers a dispatchStoreAction commit", async (done) => {
|
||||
test("setVehicle triggers a dispatchStoreAction commit", async (done) => {
|
||||
//Arrange
|
||||
const { wrapper, apiPromise } = setupMocks({
|
||||
pageHeaderWidgetHeaderText: "Select a style to get started",
|
||||
mountOptionsMockData: {
|
||||
store: {
|
||||
commit: jest.fn(),
|
||||
getters: {
|
||||
vehicle: {},
|
||||
},
|
||||
},
|
||||
actionList: [
|
||||
{
|
||||
actionName: storeActions.SET_VEHICLE,
|
||||
|
|
@ -134,9 +118,7 @@ describe("vehicle-style.vue", () => {
|
|||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("vehicle-style.vue", () => {
|
||||
test("Model set, arePagePrerequisitesValid should be true ", async () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
|
@ -155,7 +137,61 @@ describe("vehicle-style.vue", () => {
|
|||
//Assert
|
||||
expect(arePagePrerequisitesValid).toBe(true);
|
||||
});
|
||||
|
||||
test("there is only one vehicle style => autoselect and move to vehicle damage", async () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
styleQuestionInitialData: ["2 door sedan"],
|
||||
});
|
||||
|
||||
// Act
|
||||
await vehicleStyle.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-style" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(store.commit).toHaveBeenCalledWith(storeMutations.UPDATE_STYLE, "2 door sedan");
|
||||
expect(router.overrideNavigation).toHaveBeenCalled();
|
||||
})
|
||||
|
||||
test("there is only one vehicle style and vehicle-damage was visited => don't autoselect or move to vehicle damage", async () => {
|
||||
//Arrange
|
||||
const { wrapper } = setupMocks({
|
||||
styleQuestionInitialData: ["2 door sedan"],
|
||||
mountOptionsMockData: {
|
||||
store: {
|
||||
getters: {
|
||||
applicationUser: {
|
||||
pageData: {
|
||||
"part-questions": null,
|
||||
"vehicle-make": {},
|
||||
"vehicle-model": {},
|
||||
"vehicle-style": {},
|
||||
"vehicle-damage": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Act
|
||||
await vehicleStyle.beforeRouteEnter.call(
|
||||
wrapper.vm,
|
||||
{ query: { fmgPage: "vehicle-style" } },
|
||||
undefined,
|
||||
(c) => c(wrapper.vm)
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(store.commit).not.toHaveBeenCalledWith(storeMutations.UPDATE_STYLE, "2 door sedan");
|
||||
expect(router.overrideNavigation).not.toHaveBeenCalled();
|
||||
})
|
||||
});
|
||||
|
||||
function setupMocks({
|
||||
vehicleStyleQuestionCmsContent = {},
|
||||
styleQuestionInitialData = {},
|
||||
|
|
@ -190,7 +226,26 @@ function setupMocks({
|
|||
initializeComponent: jest.fn(),
|
||||
};
|
||||
|
||||
const mountOptions = getMountOptions(mountOptionsMockData);
|
||||
store.commit = jest.fn();
|
||||
store.dispatch = jest.fn();
|
||||
store.getters = mountOptionsMockData.store?.getters ?? {
|
||||
vehicle: {
|
||||
model: "TL",
|
||||
},
|
||||
applicationUser: {
|
||||
pageData: {
|
||||
"part-questions": null,
|
||||
"vehicle-make": {},
|
||||
"vehicle-model": {},
|
||||
"vehicle-style": {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mountOptions = getMountOptions({
|
||||
...mountOptionsMockData,
|
||||
store
|
||||
});
|
||||
const wrapper = shallowMount(vehicleStyle, mountOptions);
|
||||
wrapper.vm.setCmsContent = baseMixin.methods.setCmsContent;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
<script>
|
||||
// Components
|
||||
import baseMixin from "@/mixins/base-mixin.js";
|
||||
import styleQuestion from "@/layouts/vehicle-style/style-question/style-question";
|
||||
import funnelHeader from "@/common-components/funnel-header/funnel-header";
|
||||
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
|
||||
|
|
@ -71,14 +72,12 @@ export default {
|
|||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
const visitedVehicleDamage = JSON.stringify(store.getters.applicationUser.pageData).indexOf(fmgPageValues.VEHICLE_DAMAGE) < 0 ? false : true;
|
||||
|
||||
// Call the "next" function to complete the transition to this page.
|
||||
|
||||
|
||||
// If we have exactly one style then navigate directly to vehicle-damage
|
||||
if(resultMap.styleQuestionInitialData.length === 1 && !visitedVehicleDamage) {
|
||||
store.commit(storeMutations.UPDATE_STYLE, resultMap.styleQuestionInitialData[0]);
|
||||
|
||||
await store.dispatch(storeActions.SET_VEHICLE,
|
||||
await baseMixin.methods.dispatchStoreAction(storeActions.SET_VEHICLE,
|
||||
{
|
||||
year: store.getters.vehicle.year,
|
||||
make: store.getters.vehicle.make,
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ describe("vin-lookup.vue", () => {
|
|||
it("Should call navigateForward() if the store carId matches the vin response carId and forward button is clicked", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
mockOutPromises();
|
||||
mockOutPromises({ carId: "C00000" });
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
|
||||
// Act
|
||||
|
|
@ -78,7 +78,7 @@ describe("vin-lookup.vue", () => {
|
|||
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({});
|
||||
mockOutPromises('C11111');
|
||||
mockOutPromises({ carId: 'C11111' });
|
||||
|
||||
wrapper.vm.vinTouched = true;
|
||||
wrapper.vm.vin = "";
|
||||
|
|
@ -95,7 +95,7 @@ describe("vin-lookup.vue", () => {
|
|||
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({});
|
||||
mockOutPromises('C11111');
|
||||
mockOutPromises({ carId: 'C11111' });
|
||||
|
||||
wrapper.vm.lookupVehicle = jest.fn().mockImplementation(() => vinPromise);
|
||||
wrapper.vm.navigateForward = jest.fn();
|
||||
|
|
@ -203,6 +203,22 @@ describe("vin-lookup.vue", () => {
|
|||
expect(wrapper.vm.navigateForwardWithSingleCarMatch).toBeCalledTimes(1);
|
||||
})
|
||||
})
|
||||
|
||||
describe("alerts", () => {
|
||||
test("Zip is invalid => show AlertInvalidZipWidget", async () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
mockOutPromises({ isZipValid: false });
|
||||
await wrapper.setData({serviceZipCode: "11111"})
|
||||
|
||||
// Act
|
||||
await wrapper.vm.forwardButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.displayInvalidZipAlert).toEqual(true);
|
||||
expect(wrapper.findComponent({ref: "alertInvalidZip"}).exists()).toBe(true);
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
|
@ -217,19 +233,17 @@ function setupMocks({ customMountOptions }) {
|
|||
mountOptions['attachTo'] = document.body; // append wrapper to document.body to test DOM methods
|
||||
|
||||
const wrapper = shallowMount(vinLookup, mountOptions);
|
||||
mockOutPromises(wrapper);
|
||||
mockOutStubFunctions(wrapper);
|
||||
return { wrapper };
|
||||
}
|
||||
|
||||
function mockOutPromises(carId = 'C00000') {
|
||||
function mockOutPromises({carId, isZipValid = true, isZipServiceable = true}) {
|
||||
const apiResponses = {
|
||||
serviceZipValidationResponse: {
|
||||
isValid: true,
|
||||
isServiceable: true
|
||||
},
|
||||
vehicleLookupResponse: {
|
||||
carId: carId
|
||||
},
|
||||
zipCodeData: {
|
||||
isValid: true, isServiceable: true, state: "OH"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -239,6 +253,7 @@ function mockOutPromises(carId = 'C00000') {
|
|||
function mockOutStubFunctions(wrapper) {
|
||||
wrapper.vm.$refs.funnelFooter.updateButtonText = jest.fn();
|
||||
wrapper.vm.$refs.funnelFooter.removeLoader = jest.fn();
|
||||
wrapper.vm.getZipCodeData = jest.fn().mockReturnValue({ isValid: true, isServiceable: true, state: "OH" });
|
||||
}
|
||||
|
||||
const mockMixin = {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row my-2">
|
||||
<div class="row mt-2">
|
||||
<div class="col">
|
||||
<textboxQuestion
|
||||
cmsWidgetName="EmailAddressQuestionWidget"
|
||||
|
|
@ -56,6 +56,14 @@
|
|||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-2">
|
||||
<div class="col">
|
||||
<textBlock
|
||||
cmsWidgetName="QuoteEmailTextBlockWidget"
|
||||
typeStyle="caption"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<alert ref="alertInvalidZip"
|
||||
v-if="displayInvalidZipAlert"
|
||||
class="my-4"
|
||||
|
|
@ -124,6 +132,7 @@ import alert from "@/ux-components/alert/alert";
|
|||
import textboxQuestion from "@/common-components/textbox-question/textbox-question";
|
||||
import vinInformation from "@/layouts/vin-lookup/vin-information/vin-information";
|
||||
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
||||
import textBlock from '@/common-components/text-block/text-block';
|
||||
|
||||
// Supporting files
|
||||
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
|
||||
|
|
@ -221,25 +230,24 @@ export default {
|
|||
async forwardButtonAction() {
|
||||
// If this is a new VIN Lookup, do both a Vehicle Lookup and a Zip Validation
|
||||
if (!this.vinPopulatedOnPageLoad) {
|
||||
const serviceZipValidationResponse = this.dispatchStoreAction(storeActions.VALIDATE_ZIP, { zip: this.serviceZipCode });
|
||||
const vehicleLookupResponse = this.dispatchStoreAction(storeActions.LOOKUP_VEHICLE_BY_VIN, { vin: this.vin });
|
||||
|
||||
// Settle promises and get results
|
||||
const promiseResultMap = [
|
||||
{
|
||||
resultKey: "serviceZipValidationResponse",
|
||||
promise: serviceZipValidationResponse,
|
||||
},
|
||||
{
|
||||
resultKey: "vehicleLookupResponse",
|
||||
promise: vehicleLookupResponse,
|
||||
},
|
||||
{
|
||||
resultKey: "zipCodeData",
|
||||
promise: this.getZipCodeData(this.serviceZipCode)
|
||||
}
|
||||
];
|
||||
|
||||
const resultMap = await settleAllPromises(promiseResultMap);
|
||||
|
||||
// If a Service Zip is entered and it is an invalid zip code (ex. 11111) then show an alert
|
||||
const isZipValid = resultMap.serviceZipValidationResponse.isValid;
|
||||
const isZipValid = resultMap.zipCodeData.isValid;
|
||||
if (this.serviceZipCode && !isZipValid) {
|
||||
this.displayInvalidZipAlert = true;
|
||||
return this.$refs.funnelFooter.removeLoader();
|
||||
|
|
@ -247,14 +255,14 @@ export default {
|
|||
this.displayInvalidZipAlert = false;
|
||||
|
||||
// If either lookup fails, remove the loader and stop processing the page.
|
||||
if (!resultMap.vehicleLookupResponse || !resultMap.serviceZipValidationResponse.isServiceable) {
|
||||
if (!resultMap.vehicleLookupResponse || !resultMap.zipCodeData.isServiceable) {
|
||||
// If the vehicle result is undefined, the vin entered was invalid.
|
||||
if(!resultMap.vehicleLookupResponse) {
|
||||
this.displayVinNotFoundAlert = true;
|
||||
}
|
||||
|
||||
// Check if Service Zip entered is serviceable, if not display an alert
|
||||
if (!resultMap.serviceZipValidationResponse.isServiceable) {
|
||||
if (!resultMap.zipCodeData.isServiceable) {
|
||||
this.displayNonServiceableZipAlert = true;
|
||||
}
|
||||
|
||||
|
|
@ -288,7 +296,7 @@ export default {
|
|||
await this.dispatchStoreAction(storeActions.SAVE_EMAIL, this.emailAddress, false);
|
||||
await this.dispatchStoreAction(storeActions.SAVE_SERVICE_LOCATION, {
|
||||
zipCode: this.serviceZipCode,
|
||||
state: resultMap.serviceZipValidationResponse.state,
|
||||
state: resultMap.zipCodeData.state,
|
||||
}, false);
|
||||
|
||||
return await this.navigateForward();
|
||||
|
|
@ -296,10 +304,10 @@ export default {
|
|||
}
|
||||
|
||||
// If a VIN has already been found. Validate the Service Zip (in case of changes)
|
||||
const zipValidationResponse = await this.dispatchStoreAction(storeActions.VALIDATE_ZIP, {zip: this.serviceZipCode});
|
||||
const zipCodeData = await this.getZipCodeData(this.serviceZipCode);
|
||||
|
||||
// Check if Service Zip entered is serviceable
|
||||
if (zipValidationResponse.data.isServiceable) {
|
||||
if (zipCodeData.isServiceable) {
|
||||
// If the Service Zip entered is serviceable then save the Zip Info and Email Address and navigate forward
|
||||
if (this.$store.getters.order.serviceLocation.zipCode != this.serviceZipCode) {
|
||||
const vehicleRegistrationInfo = this.$store.getters.vehicle.registration;
|
||||
|
|
@ -314,7 +322,7 @@ export default {
|
|||
else {
|
||||
await this.dispatchStoreAction(storeActions.SAVE_SERVICE_LOCATION, {
|
||||
zipCode: this.serviceZipCode,
|
||||
state: zipValidationResponse.data.state
|
||||
state: zipCodeData.state
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
|
@ -399,6 +407,7 @@ export default {
|
|||
alert,
|
||||
vinInformation,
|
||||
loadingModal,
|
||||
textBlock,
|
||||
Form,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { vehicleCategories } from "@/constants/vehicle-categories.js";
|
|||
import { routerParams } from "@/router/router-constants/router-params";
|
||||
import { queryStrings } from "@/constants/query-strings";
|
||||
import { dynamicStrings } from "@/constants/dynamic-strings";
|
||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||
|
||||
export default {
|
||||
data() {
|
||||
|
|
@ -47,6 +48,15 @@ export default {
|
|||
const footerInfoBox = document.querySelector(".footer#infoBox");
|
||||
return footerInfoBox ? footerInfoBox.offsetHeight : 0;
|
||||
},
|
||||
async getZipCodeData(zipCode) {
|
||||
const serviceZipValidationResponse = await this.dispatchStoreAction(storeActions.VALIDATE_ZIP, { zip: zipCode });
|
||||
|
||||
return {
|
||||
isValid: serviceZipValidationResponse.data.isValid,
|
||||
isServiceable: serviceZipValidationResponse.data.isServiceable,
|
||||
state: serviceZipValidationResponse.data.state
|
||||
};
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
storeActions() {
|
||||
|
|
|
|||
|
|
@ -95,6 +95,25 @@ describe("baseMixin.js", () => {
|
|||
// Assert
|
||||
expect(global.document.querySelector).toBeCalledWith("[data-focus-target='fieldOne']");
|
||||
});
|
||||
|
||||
test("getZipCodeData calls dispatch", () => {
|
||||
const mixIn = getMixInInstance({});
|
||||
mixIn.methods.dispatchStoreAction = jest.fn();
|
||||
mixIn.methods.dispatchStoreAction.mockReturnValue({data: { isValid:true, isServiceable:true, state:"OH" }});
|
||||
const type = "";
|
||||
const payload = { zip: 43015 };
|
||||
|
||||
const mockData = {
|
||||
actionList: [{
|
||||
actionName: storeActions.VALIDATE_ZIP
|
||||
}],
|
||||
}
|
||||
|
||||
mixIn.methods.getZipCodeData(type, payload);
|
||||
|
||||
expect(mixIn.methods.dispatchStoreAction).toBeCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
function getMixInInstance({ isDispatchSuccess = true }) {
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ export default {
|
|||
// if single parts only
|
||||
const collectedGlassParts = this.reducedGlassPartsArray(partsOrQuestions);
|
||||
// save to store lineItems.glassParts
|
||||
// TODO KO
|
||||
self.$store.commit(storeMutations.UPDATE_GLASS_PARTS, collectedGlassParts);
|
||||
|
||||
self.$refs.loadingModal.showModal();
|
||||
|
|
@ -127,20 +128,20 @@ export default {
|
|||
const hasGlassLocationWithMultipleParts = this.hasGlassLocationWithMultipleParts(partsOrQuestions);
|
||||
const hasChildPartQuestions = this.hasChildPartQuestions(partsOrQuestions);
|
||||
const hasCapabilityQuestions = this.hasCapabilityQuestions(partsOrQuestions);
|
||||
let backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP;
|
||||
let backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS;
|
||||
|
||||
const currentPage = this.$route.query.fmgPage;
|
||||
if (hasCapabilityQuestions && this.currentPageComesAfterPage(currentPage, fmgPageValues.CAPABILITY_QUESTIONS)) {
|
||||
backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_CAPABILITY_QUESTIONS;
|
||||
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_CAPABILITY_QUESTIONS;
|
||||
}
|
||||
else if (hasChildPartQuestions && this.currentPageComesAfterPage(currentPage, fmgPageValues.MOLDING_QUESTIONS)) {
|
||||
backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_MOLDING_QUESTIONS;
|
||||
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS;
|
||||
}
|
||||
else if (hasGlassLocationWithMultipleParts && this.currentPageComesAfterPage(currentPage, fmgPageValues.VEHICLE_PARTS)) {
|
||||
backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS;
|
||||
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE;
|
||||
}
|
||||
else if (hasPartQuestions && this.currentPageComesAfterPage(currentPage, fmgPageValues.PART_QUESTIONS)) {
|
||||
backNavigationScenario = navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS;
|
||||
backNavigationScenario = navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS;
|
||||
}
|
||||
|
||||
this.$router.navigateWithoutSaving(
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { storeActions } from "@/constants/store-actions";
|
|||
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';
|
||||
import { navigateToHeritageFunnel } from "@/helpers/heritage-integration/navigation-helper";
|
||||
import { navigationScenarios } from "../router/router-constants/navigation-scenarios";
|
||||
import { getters } from "@/store"
|
||||
|
||||
jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
|
||||
navigateToHeritageFunnel: jest.fn()
|
||||
|
|
@ -154,6 +155,46 @@ describe("vehicle-questions-mixin", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("currentPageComesBeforePage", () => {
|
||||
const testCases = [[fmgPageValues.PART_QUESTIONS, fmgPageValues.VEHICLE_PARTS, true],
|
||||
[fmgPageValues.VEHICLE_PARTS, fmgPageValues.VEHICLE_PARTS, false],
|
||||
[fmgPageValues.QUOTE, fmgPageValues.VEHICLE_PARTS, false],
|
||||
[fmgPageValues.QUOTE, fmgPageValues.QUOTE, false],
|
||||
[fmgPageValues.VEHICLE_PARTS, fmgPageValues.PART_QUESTIONS, false],
|
||||
[fmgPageValues.PART_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, true],
|
||||
[fmgPageValues.MOLDING_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, true],];
|
||||
test.each(testCases)("%s comes before %s is %s", (currentPage, nextPage, expectedResult) => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.currentPageComesBeforePage(currentPage, nextPage);
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(expectedResult);
|
||||
})
|
||||
});
|
||||
|
||||
describe("currentPageComesAfterPage", () => {
|
||||
const testCases = [[fmgPageValues.PART_QUESTIONS, fmgPageValues.VEHICLE_PARTS, false],
|
||||
[fmgPageValues.VEHICLE_PARTS, fmgPageValues.VEHICLE_PARTS, false],
|
||||
[fmgPageValues.QUOTE, fmgPageValues.VEHICLE_PARTS, true],
|
||||
[fmgPageValues.QUOTE, fmgPageValues.QUOTE, false],
|
||||
[fmgPageValues.VEHICLE_PARTS, fmgPageValues.PART_QUESTIONS, true],
|
||||
[fmgPageValues.PART_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, false],
|
||||
[fmgPageValues.MOLDING_QUESTIONS, fmgPageValues.CAPABILITY_QUESTIONS, false],];
|
||||
test.each(testCases)("%s comes after %s is %s", (currentPage, nextPage, expectedResult) => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({});
|
||||
|
||||
// Act
|
||||
const result = wrapper.vm.currentPageComesAfterPage(currentPage, nextPage);
|
||||
|
||||
// Assert
|
||||
expect(result).toEqual(expectedResult);
|
||||
})
|
||||
});
|
||||
|
||||
describe("navigateForward", () => {
|
||||
describe("should go to parts-questions", () => {
|
||||
test("single glass location has part question => go to parts-questions", async () => {
|
||||
|
|
@ -890,23 +931,23 @@ describe("vehicle-questions-mixin", () => {
|
|||
"childParts": null,
|
||||
"childPartQuestions": [
|
||||
{
|
||||
"questionSequence": 1,
|
||||
"questionText": "Does the rubber seal around your windshield have a chrome strip running through it?",
|
||||
"answers": [
|
||||
{
|
||||
"answerResult": "WKT D1106 C",
|
||||
"answerText": "Yes",
|
||||
"nextQuestionSequence": null
|
||||
},
|
||||
{
|
||||
"answerResult": "WKT D1106 B",
|
||||
"answerText": "No",
|
||||
"nextQuestionSequence": null
|
||||
}
|
||||
]
|
||||
"questionSequence": 1,
|
||||
"questionText": "Does the rubber seal around your windshield have a chrome strip running through it?",
|
||||
"answers": [
|
||||
{
|
||||
"answerResult": "WKT D1106 C",
|
||||
"answerText": "Yes",
|
||||
"nextQuestionSequence": null
|
||||
},
|
||||
{
|
||||
"answerResult": "WKT D1106 B",
|
||||
"answerText": "No",
|
||||
"nextQuestionSequence": null
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
}
|
||||
],
|
||||
"partQuestions": null
|
||||
|
|
@ -1096,7 +1137,7 @@ describe("vehicle-questions-mixin", () => {
|
|||
|
||||
// Act
|
||||
await wrapper.vm.navigateForward(partsOrQuestions);
|
||||
|
||||
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$store.commit).toHaveBeenCalledTimes(1);
|
||||
|
|
@ -1105,7 +1146,63 @@ describe("vehicle-questions-mixin", () => {
|
|||
expect(navigateToHeritageFunnel).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
describe("backButtonAction", () => {
|
||||
test("current page is quote and there are no questions => go to vin-lookup", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({ fmgPage: fmgPageValues.QUOTE });
|
||||
|
||||
// Act
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS, {"query": {"fmgPage": fmgPageValues.QUOTE}})
|
||||
})
|
||||
|
||||
test("current page is quote and there are part questions and molding questions => go to molding questions", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({ fmgPage: fmgPageValues.QUOTE });
|
||||
wrapper.vm.hasPartQuestions = jest.fn().mockReturnValue(true);
|
||||
wrapper.vm.hasChildPartQuestions = jest.fn().mockReturnValue(true);
|
||||
|
||||
// Act
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS, {"query": {"fmgPage": fmgPageValues.QUOTE}})
|
||||
})
|
||||
|
||||
test("current page is molding questions and there are part questions, multiple parts to choose, and capability questions => go to vehicle-parts", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({ fmgPage: fmgPageValues.MOLDING_QUESTIONS });
|
||||
wrapper.vm.hasPartQuestions = jest.fn().mockReturnValue(true);
|
||||
wrapper.vm.hasGlassLocationWithMultipleParts = jest.fn().mockReturnValue(true);
|
||||
wrapper.vm.hasChildPartQuestions = jest.fn().mockReturnValue(true);
|
||||
wrapper.vm.hasCapabilityQuestions = jest.fn().mockReturnValue(true);
|
||||
|
||||
// Act
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE, {"query": {"fmgPage": fmgPageValues.MOLDING_QUESTIONS}})
|
||||
})
|
||||
|
||||
test("current page is molding questions and there are part questions and capability questions => go to part-questions", () => {
|
||||
// Arrange
|
||||
const { wrapper } = setupMocks({ fmgPage: fmgPageValues.MOLDING_QUESTIONS });
|
||||
wrapper.vm.hasPartQuestions = jest.fn().mockReturnValue(true);
|
||||
wrapper.vm.hasGlassLocationWithMultipleParts = jest.fn().mockReturnValue(false);
|
||||
wrapper.vm.hasChildPartQuestions = jest.fn().mockReturnValue(true);
|
||||
wrapper.vm.hasCapabilityQuestions = jest.fn().mockReturnValue(true);
|
||||
|
||||
// Act
|
||||
wrapper.vm.backButtonAction();
|
||||
|
||||
// Assert
|
||||
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalledWith(navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS, {"query": {"fmgPage": fmgPageValues.MOLDING_QUESTIONS}})
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
function setupMocks({ fmgPage = fmgPageValues.VIN_LOOKUP }) {
|
||||
|
|
@ -1118,11 +1215,12 @@ function setupMocks({ fmgPage = fmgPageValues.VIN_LOOKUP }) {
|
|||
|
||||
const mocks = getMountOptions({
|
||||
router: {
|
||||
navigate: jest.fn(), navigateWithSaving: jest.fn(), navigateWithSaving: jest.fn()
|
||||
navigate: jest.fn(), navigateWithSaving: jest.fn(), navigateWithoutSaving: jest.fn()
|
||||
},
|
||||
store: {
|
||||
commit: jest.fn(),
|
||||
dispatch: jest.fn()
|
||||
dispatch: jest.fn(),
|
||||
getters: getters
|
||||
},
|
||||
route: {
|
||||
query: {
|
||||
|
|
|
|||
|
|
@ -25,10 +25,10 @@ const navigationScenarios = {
|
|||
HAS_MOLDING_QUESTIONS: "HAS_MOLDING_QUESTIONS",
|
||||
HAS_CAPABILITY_QUESTIONS: "HAS_CAPABILITY_QUESTIONS",
|
||||
HAS_NO_MORE_QUESTIONS: "HAS_NO_MORE_QUESTIONS",
|
||||
CLICKED_BACK_TO_GO_TO_VIN_LOOKUP: "CLICKED_BACK_TO_GO_TO_VIN_LOOKUP",
|
||||
CLICKED_BACK_TO_GO_TO_PART_QUESTIONS: "CLICKED_BACK_TO_GO_TO_PART_QUESTIONS",
|
||||
CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS: "CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS",
|
||||
CLICKED_BACK_TO_GO_TO_MOLDING_QUESTIONS: "CLICKED_BACK_TO_GO_TO_MOLDING_QUESTIONS"
|
||||
CLICKED_BACK_WITH_NO_QUESTIONS: "CLICKED_BACK_WITH_NO_QUESTIONS",
|
||||
CLICKED_BACK_WITH_PART_QUESTIONS: "CLICKED_BACK_WITH_PART_QUESTIONS",
|
||||
CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE: "CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE",
|
||||
CLICKED_BACK_WITH_MOLDING_QUESTIONS: "CLICKED_BACK_WITH_MOLDING_QUESTIONS"
|
||||
};
|
||||
|
||||
export { navigationScenarios };
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ const routingTable = function(store) {
|
|||
fmgPageValue: fmgPageValues.PART_QUESTIONS,
|
||||
maps: [
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP
|
||||
},
|
||||
{
|
||||
|
|
@ -256,11 +256,11 @@ const routingTable = function(store) {
|
|||
destinationFmgPageValue: fmgPageValues.REVEAL,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS,
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP,
|
||||
},
|
||||
{
|
||||
|
|
@ -281,15 +281,15 @@ const routingTable = function(store) {
|
|||
fmgPageValue: fmgPageValues.MOLDING_QUESTIONS,
|
||||
maps: [
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||
},
|
||||
{
|
||||
|
|
@ -306,19 +306,19 @@ const routingTable = function(store) {
|
|||
fmgPageValue: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||
maps: [
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VIN_LOOKUP,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_NO_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.VIN_LOOKUP
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_PART_QUESTIONS,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_PART_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.PART_QUESTIONS
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_VEHICLE_PARTS,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_MULTIPLE_PARTS_TO_CHOOSE,
|
||||
destinationFmgPageValue: fmgPageValues.VEHICLE_PARTS
|
||||
},
|
||||
{
|
||||
scenario: navigationScenarios.CLICKED_BACK_TO_GO_TO_MOLDING_QUESTIONS,
|
||||
scenario: navigationScenarios.CLICKED_BACK_WITH_MOLDING_QUESTIONS,
|
||||
destinationFmgPageValue: fmgPageValues.MOLDING_QUESTIONS
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -399,7 +399,7 @@ export const getters = {
|
|||
funnelSelectedPassengerSideGlass: getAllValuesOfPropertyInArrayOfObjects(state.order.damage.glassToReplace, "glassLocation").includes(damageLocationsSelected.PASSENGER),
|
||||
|
||||
funnelOrderPartNumbers: [...getAllValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "partNumber"), ...getAllValuesOfPropertyInArrayOfObjects(state.order.lineItems.otherParts, "partNumber")],
|
||||
|
||||
|
||||
funnelOrderPartTypes: [...getAllValuesOfPropertyInArrayOfObjects(state.order.lineItems.glassParts, "recalibrationType"), ...getAllValuesOfPropertyInArrayOfObjects(state.order.lineItems.otherParts, "recalibrationType")],
|
||||
}
|
||||
},
|
||||
|
|
@ -717,7 +717,7 @@ export const actions = {
|
|||
endpoint: endpoints.GetPartsOrQuestions.url,
|
||||
payload: {
|
||||
carId: carId,
|
||||
glass: glassArray,
|
||||
glass: glassArray ?? [],
|
||||
zip: zipCode,
|
||||
vin: vin
|
||||
},
|
||||
|
|
@ -942,8 +942,14 @@ export const actions = {
|
|||
.slice()
|
||||
.sort()
|
||||
.every((obj, index) => obj.glassLocation === selectedGlassPassedInSorted[index].glassLocation && obj.glassName === selectedGlassPassedInSorted[index].glassName);
|
||||
const isWindshieldRepairTheSame = isWindshieldRepair === context.state.order.damage.isRepair;
|
||||
const isChipCountTheSame = Array.isArray(selectedWindshieldChipCount) //TODO: fix the underlying components so this is never an array
|
||||
? selectedWindshieldChipCount[0] === context.state.order.damage.numberOfChips
|
||||
: selectedWindshieldChipCount === context.state.order.damage.numberOfChips;
|
||||
|
||||
if (!isGlassToReplaceTheSame) {
|
||||
const isDamageChanging = !isGlassToReplaceTheSame || !isWindshieldRepairTheSame || (isWindshieldRepair && !isChipCountTheSame);
|
||||
|
||||
if (isDamageChanging) {
|
||||
//Reset dependent state when changing
|
||||
context.dispatch(storeActions.RESET_PARTS_STATE_AND_DEPENDENCIES);
|
||||
|
||||
|
|
@ -1004,9 +1010,10 @@ export const actions = {
|
|||
},
|
||||
savePartQuestionAnswers(context, partQuestionAnswersArray) {
|
||||
// if part question answers have changed, reset subsequent question answers
|
||||
const previousResultsArray = context.getters.damage.partQuestionAnswers;
|
||||
const havePartQuestionAnswersChanged = previousResultsArray?.length !== partQuestionAnswersArray.length ||
|
||||
!previousResultsArray.every((x, i) => x.result === partQuestionAnswersArray[i].result);
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(context.getters.damage.partQuestionAnswers, "result");
|
||||
const sortedPartQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(partQuestionAnswersArray, "result")
|
||||
const havePartQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedPartQuestionAnswersArray.length ||
|
||||
!sortedPreviousResultsArray?.every((x, i) => x.result === sortedPartQuestionAnswersArray[i].result);
|
||||
|
||||
if (havePartQuestionAnswersChanged) {
|
||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
|
|
@ -1021,9 +1028,8 @@ export const actions = {
|
|||
context.commit(storeMutations.UPDATE_PART_QUESTION_ANSWERS, partQuestionAnswersArray);
|
||||
},
|
||||
resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, matchedParts) {
|
||||
const previousResultsArray = [{ parts: context.getters.lineItems.glassParts }];
|
||||
const partsOrQuestionsDataToCompareWith = context.getters.pageData(fmgPageValues.MOLDING_QUESTIONS)?.partsOrQuestions ?? context.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS)?.partsOrQuestions ?? [];
|
||||
|
||||
|
||||
function getAllPartNumbers(partsOrQuestions) {
|
||||
return partsOrQuestions[0]?.parts
|
||||
? [...partsOrQuestions].map(glass => glass.parts).flat().map(part => part.partNumber).filter(partNumber => !partNumber.toUpperCase().includes("FEE")).sort().join(",")
|
||||
|
|
@ -1044,9 +1050,10 @@ export const actions = {
|
|||
}
|
||||
},
|
||||
saveMoldingQuestionAnswers(context, moldingQuestionAnswers) {
|
||||
const previousResultsArray = context.getters.damage.moldingQuestionAnswers;
|
||||
const haveMoldingQuestionAnswersChanged = previousResultsArray?.length !== moldingQuestionAnswers.length ||
|
||||
!previousResultsArray.every((x, i) => x.partNum === moldingQuestionAnswers[i].partNum);
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(context.getters.damage.moldingQuestionAnswers, "partNum");
|
||||
const sortedMoldingQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(moldingQuestionAnswers, "partNum");
|
||||
const haveMoldingQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedMoldingQuestionAnswersArray.length ||
|
||||
!sortedPreviousResultsArray?.every((x, i) => x.partNum === sortedMoldingQuestionAnswersArray[i].partNum);
|
||||
|
||||
if (haveMoldingQuestionAnswersChanged) {
|
||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
|
|
@ -1058,9 +1065,10 @@ export const actions = {
|
|||
context.commit(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, moldingQuestionAnswers);
|
||||
},
|
||||
saveCapabilityQuestionAnswers(context, capabilityQuestionAnswers) {
|
||||
const previousResultsArray = context.getters.damage.capabilityQuestionAnswers;
|
||||
const haveCapabilityQuestionAnswersChanged = previousResultsArray?.length !== capabilityQuestionAnswers.length ||
|
||||
!previousResultsArray.every((x, i) => x.result === capabilityQuestionAnswers[i].result);
|
||||
const sortedPreviousResultsArray = sortArrayOfObjectsByPropertyValue(context.getters.damage.capabilityQuestionAnswers, "result");
|
||||
const sortedCapabilityQuestionAnswersArray = sortArrayOfObjectsByPropertyValue(capabilityQuestionAnswers, "result");
|
||||
const haveCapabilityQuestionAnswersChanged = sortedPreviousResultsArray?.length !== sortedCapabilityQuestionAnswersArray.length ||
|
||||
!sortedPreviousResultsArray?.every((x, i) => x.result === sortedCapabilityQuestionAnswersArray[i].result);
|
||||
|
||||
if (haveCapabilityQuestionAnswersChanged) {
|
||||
context.commit(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
|
|
@ -1126,4 +1134,17 @@ function getHasRecalibrationPart(state) {
|
|||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function sortArrayOfObjectsByPropertyValue(arrayOfObjects, propertyName) {
|
||||
if (!arrayOfObjects) return null;
|
||||
|
||||
return arrayOfObjects.sort((a, b) => {
|
||||
if (a[propertyName] < b[propertyName])
|
||||
return -1;
|
||||
else if (a[propertyName] > b[propertyName])
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
})
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ import { mutations, state, actions, getters } from "@/store";
|
|||
import { storeMutations } from "@/constants/store-mutations";
|
||||
import { storeActions } from "@/constants/store-actions";
|
||||
import { experimentTriggers } from "@/constants/experiments";
|
||||
import { fmgPageValues } from "@/router/router-constants/fmgPage-values"
|
||||
|
||||
// Mock global method
|
||||
globalMethods.callHttpClient = jest.fn();
|
||||
|
|
@ -258,9 +259,9 @@ describe("Mutations", () => {
|
|||
{
|
||||
universeName: "XYZ",
|
||||
settings: {
|
||||
ExperimentSetting: "ExperimentValue"
|
||||
ExperimentSetting: "ExperimentValue"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// Act
|
||||
|
|
@ -649,6 +650,51 @@ describe("Actions", () => {
|
|||
expect(commit).toBeCalledWith(storeMutations.UPDATE_STATE_WITH_ORDER_INFORMATION, { "referralNumber": 123 });
|
||||
});
|
||||
|
||||
it("loadOrder: state doesn't have EON => do not reset state", async () => {
|
||||
// Arrange
|
||||
const context = state;
|
||||
|
||||
globalMethods.callHttpClient.mockImplementation(() => {
|
||||
return Promise.resolve({ data: { eon: "123" } });
|
||||
});
|
||||
|
||||
context.commit = jest.fn();
|
||||
context.state = {
|
||||
order: {
|
||||
}
|
||||
}
|
||||
|
||||
// Act
|
||||
const response = await actions.loadOrder(context, { referralNumber: "123", referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx" });
|
||||
|
||||
// Assert
|
||||
expect(response.data.eon).toEqual("123");
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.RESET_STATE);
|
||||
});
|
||||
|
||||
it("loadOrder eon doesn't match eon in state => reset state", async () => {
|
||||
// Arrange
|
||||
const context = state;
|
||||
|
||||
globalMethods.callHttpClient.mockImplementation(() => {
|
||||
return Promise.resolve({ data: { eon: "123" } });
|
||||
});
|
||||
|
||||
context.commit = jest.fn();
|
||||
context.state = {
|
||||
order: {
|
||||
eon: "456"
|
||||
}
|
||||
}
|
||||
|
||||
// Act
|
||||
const response = await actions.loadOrder(context, { referralNumber: "123", referralDate: new Date().toUTCString(), referralCorrelationId: "xxx-xxx-xxx" });
|
||||
|
||||
// Assert
|
||||
expect(response.data.eon).toEqual("123");
|
||||
expect(context.commit).toBeCalledWith(storeMutations.RESET_STATE);
|
||||
});
|
||||
|
||||
it("updateStoreWithSaveOrderResponse, should call commit six times", () => {
|
||||
// Arrange
|
||||
const context = state;
|
||||
|
|
@ -657,11 +703,11 @@ describe("Actions", () => {
|
|||
context.commit = commit;
|
||||
|
||||
// Act
|
||||
actions.updateStoreWithSaveOrderResponse(context,
|
||||
actions.updateStoreWithSaveOrderResponse(context,
|
||||
{
|
||||
referralNumber: "123",
|
||||
referralDate: new Date().toUTCString(),
|
||||
referralCorrelationId: "xxx-xxx-xxx",
|
||||
referralNumber: "123",
|
||||
referralDate: new Date().toUTCString(),
|
||||
referralCorrelationId: "xxx-xxx-xxx",
|
||||
accountNumber: "167132",
|
||||
savedSessionId: "xxx-xxx-xxx",
|
||||
crmCustomerId: "xxx-xxx-xxx",
|
||||
|
|
@ -826,19 +872,19 @@ describe("Actions", () => {
|
|||
|
||||
|
||||
// Act
|
||||
const payload = {
|
||||
isCarIdDifferent: true,
|
||||
isSelectedGlassAvailableForVehicle: false,
|
||||
vehicleInfo: {
|
||||
carId: 'C010101', vin: "XXXXX"
|
||||
},
|
||||
registrationInfo: {
|
||||
zipCode: "80020"
|
||||
},
|
||||
serviceLocationInfo: {
|
||||
state: "CO"
|
||||
},
|
||||
customerEmail: "test@safleite.com"
|
||||
const payload = {
|
||||
isCarIdDifferent: true,
|
||||
isSelectedGlassAvailableForVehicle: false,
|
||||
vehicleInfo: {
|
||||
carId: 'C010101', vin: "XXXXX"
|
||||
},
|
||||
registrationInfo: {
|
||||
zipCode: "80020"
|
||||
},
|
||||
serviceLocationInfo: {
|
||||
state: "CO"
|
||||
},
|
||||
customerEmail: "test@safleite.com"
|
||||
};
|
||||
|
||||
actions.saveVinLookup(context, payload);
|
||||
|
|
@ -1065,7 +1111,7 @@ describe("Actions", () => {
|
|||
context.state = {
|
||||
order: {
|
||||
damage: {
|
||||
glassToReplace: [{glassName: 'Single', glassLocation: 'Windshield'}]
|
||||
glassToReplace: [{ glassName: 'Single', glassLocation: 'Windshield' }]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1077,7 +1123,7 @@ describe("Actions", () => {
|
|||
context.dispatch = dispatch;
|
||||
|
||||
// Act
|
||||
const payload = { isWindshieldRepair: false, selectedGlassToReplace: [{glassName: 'Rear', glassLocation: 'quarter'}], selectedWindshieldChipCount: 0};
|
||||
const payload = { isWindshieldRepair: false, selectedGlassToReplace: [{ glassName: 'Rear', glassLocation: 'quarter' }], selectedWindshieldChipCount: 0 };
|
||||
actions.saveVehicleDamage(context, payload);
|
||||
|
||||
// Assert
|
||||
|
|
@ -1156,50 +1202,677 @@ describe("Actions", () => {
|
|||
})
|
||||
|
||||
describe("savePartQuestionAnswers", () => {
|
||||
test.todo("saves partQuestionAnswers")
|
||||
let context;
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mutations.resetState(state);
|
||||
context = state;
|
||||
context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value));
|
||||
context.getters = {
|
||||
...getters,
|
||||
damage: getters.damage(context)
|
||||
};
|
||||
})
|
||||
|
||||
test.todo("there are no previous answers => resets necessary fields")
|
||||
function testPartQuestionAnswerDependenciesHaveBeenReset(context, shouldPartQuestionAnswersBeReset) {
|
||||
if (shouldPartQuestionAnswersBeReset) {
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.VEHICLE_PARTS, data: null });
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null });
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null });
|
||||
}
|
||||
else {
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.VEHICLE_PARTS, data: null });
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null });
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null });
|
||||
}
|
||||
|
||||
// mix up the order to make sure sort is working
|
||||
test.todo("previous answers match current answers => does not reset fields")
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PART_QUESTION_ANSWERS, expect.anything());
|
||||
}
|
||||
|
||||
test.todo("previous answers does not match current answers => resets necessary fields")
|
||||
test("there are no previous answers => resets necessary fields", async () => {
|
||||
// Arrange
|
||||
const previousPartQuestionAnswers = [];
|
||||
const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }, { result: "I'M A PART2!" }];
|
||||
|
||||
actions.savePartQuestionAnswers(context, previousPartQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.savePartQuestionAnswers(context, currentPartQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testPartQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("previous answers does not match current answers => resets necessary fields", () => {
|
||||
// Arrange
|
||||
const previousPartQuestionAnswers = [{ result: "I'M A PART2!" }, { result: "I'M A PART4!" }, { result: "I'M A PART3!" }];
|
||||
const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }, { result: "I'M A PART2!" }];
|
||||
|
||||
actions.savePartQuestionAnswers(context, previousPartQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.savePartQuestionAnswers(context, currentPartQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testPartQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("previous answers match current answers => does not reset fields", () => {
|
||||
// Arrange
|
||||
const previousPartQuestionAnswers = [{ result: "I'M A PART2!" }, { result: "I'M A PART!" }, { result: "I'M A PART3!" }];
|
||||
const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }, { result: "I'M A PART2!" }];
|
||||
|
||||
actions.savePartQuestionAnswers(context, previousPartQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.savePartQuestionAnswers(context, currentPartQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testPartQuestionAnswerDependenciesHaveBeenReset(context, false);
|
||||
})
|
||||
test("previous answers have more questions/answers than current => resets fields", () => {
|
||||
// Arrange
|
||||
const previousPartQuestionAnswers = [{ result: "I'M A PART2!" }, { result: "I'M A PART!" }, { result: "I'M A PART3!" }];
|
||||
const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }];
|
||||
|
||||
actions.savePartQuestionAnswers(context, previousPartQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.savePartQuestionAnswers(context, currentPartQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testPartQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("current answers have more questions/answers than previous => resets fields", () => {
|
||||
// Arrange
|
||||
const previousPartQuestionAnswers = [{ result: "I'M A PART2!" }, { result: "I'M A PART3!" }];
|
||||
const currentPartQuestionAnswers = [{ result: "I'M A PART!" }, { result: "I'M A PART3!" }, { result: "I'M A PART2!" }];
|
||||
|
||||
actions.savePartQuestionAnswers(context, previousPartQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.savePartQuestionAnswers(context, currentPartQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testPartQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
})
|
||||
|
||||
describe("resetMoldingAndCapabilityQuestionAnswersIfNeeded", () => {
|
||||
test.todo("there are no saved parts => resets necessary fields")
|
||||
let context;
|
||||
beforeEach(() => {
|
||||
mutations.resetState(state);
|
||||
context = state;
|
||||
context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value));
|
||||
context.getters = {
|
||||
...getters,
|
||||
pageData: getters.pageData(context)
|
||||
};
|
||||
})
|
||||
|
||||
// make sure you check the different variations where molding-questions and capability-questions do or don't have pageData
|
||||
// mix up the order to make sure sort is working
|
||||
test.todo("previously saved parts match selected parts => does not reset fields")
|
||||
function testVehiclePartDependenciesHaveBeenReset(context, shouldAnswersBeReset) {
|
||||
if (shouldAnswersBeReset) {
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null });
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null });
|
||||
}
|
||||
else {
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_MOLDING_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.MOLDING_QUESTIONS, data: null });
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null });
|
||||
}
|
||||
}
|
||||
|
||||
test.todo("previously saved parts do not match selected parts => resets necessary fields")
|
||||
test("there are no saved parts from molding or capability question pages => resets necessary fields", () => {
|
||||
// Arrange
|
||||
const previouslySelectedParts = {};
|
||||
|
||||
const currentlySelectedParts = [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }]
|
||||
}
|
||||
];
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.MOLDING_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
// Act
|
||||
actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts);
|
||||
|
||||
// Assert
|
||||
testVehiclePartDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
describe("previously saved parts from molding-questions match selected parts => does not reset fields", () => {
|
||||
test("single glass location", () => {
|
||||
// Arrange
|
||||
const previouslySelectedParts = {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART1" }, { partNumber: "PART2" }, { partNumber: "PART3" }]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const currentlySelectedParts = [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }]
|
||||
}
|
||||
];
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.MOLDING_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
// Act
|
||||
actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts);
|
||||
|
||||
// Assert
|
||||
testVehiclePartDependenciesHaveBeenReset(context, false);
|
||||
})
|
||||
|
||||
test("multiple glass locations", () => {
|
||||
// Arrange
|
||||
const previouslySelectedParts = {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART1" }, { partNumber: "PART2" }, { partNumber: "PART3" }]
|
||||
},
|
||||
{
|
||||
glassLocation: "Driver",
|
||||
glassName: "Front",
|
||||
parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const currentlySelectedParts = [
|
||||
{
|
||||
glassLocation: "Driver",
|
||||
glassName: "Front",
|
||||
parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }]
|
||||
},
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }]
|
||||
}
|
||||
];
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.MOLDING_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
// Act
|
||||
actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts);
|
||||
|
||||
// Assert
|
||||
testVehiclePartDependenciesHaveBeenReset(context, false);
|
||||
})
|
||||
})
|
||||
|
||||
describe("previously saved parts from capability-questions match selected parts and there are none from molding-questions => does not reset fields", () => {
|
||||
test("Single glass location", () => {
|
||||
// Arrange
|
||||
const previouslySelectedParts = {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART1" }, { partNumber: "PART2" }, { partNumber: "PART3" }]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const currentlySelectedParts = [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }]
|
||||
}
|
||||
];
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
// Act
|
||||
actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts);
|
||||
|
||||
// Assert
|
||||
testVehiclePartDependenciesHaveBeenReset(context, false);
|
||||
})
|
||||
|
||||
test("multiple glass locations", () => {
|
||||
// Arrange
|
||||
const previouslySelectedParts = {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART1" }, { partNumber: "PART2" }, { partNumber: "PART3" }]
|
||||
},
|
||||
{
|
||||
glassLocation: "Driver",
|
||||
glassName: "Front",
|
||||
parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const currentlySelectedParts = [
|
||||
{
|
||||
glassLocation: "Driver",
|
||||
glassName: "Front",
|
||||
parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }]
|
||||
},
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }]
|
||||
}
|
||||
];
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
// Act
|
||||
actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts);
|
||||
|
||||
// Assert
|
||||
testVehiclePartDependenciesHaveBeenReset(context, false);
|
||||
})
|
||||
})
|
||||
|
||||
describe("previously saved parts from molding-questions do not match selected parts => resets necessary fields", () => {
|
||||
test("single glass location", () => {
|
||||
// Arrange
|
||||
const previouslySelectedParts = {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART1" }, { partNumber: "PART6" }, { partNumber: "PART3" }]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const currentlySelectedParts = [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }]
|
||||
}
|
||||
];
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.MOLDING_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
// Act
|
||||
actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts);
|
||||
|
||||
// Assert
|
||||
testVehiclePartDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("multiple glass locations", () => {
|
||||
// Arrange
|
||||
const previouslySelectedParts = {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART1" }, { partNumber: "PART6" }, { partNumber: "PART3" }]
|
||||
},
|
||||
{
|
||||
glassLocation: "Driver",
|
||||
glassName: "Front",
|
||||
parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const currentlySelectedParts = [
|
||||
{
|
||||
glassLocation: "Driver",
|
||||
glassName: "Front",
|
||||
parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }]
|
||||
},
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }]
|
||||
}
|
||||
];
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.MOLDING_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
// Act
|
||||
actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts);
|
||||
|
||||
// Assert
|
||||
testVehiclePartDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
})
|
||||
|
||||
describe("previously saved parts from capability-questions do not match selected parts => resets necessary fields", () => {
|
||||
test("single glass location", () => {
|
||||
// Arrange
|
||||
const previouslySelectedParts = {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART1" }, { partNumber: "PART6" }, { partNumber: "PART3" }]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const currentlySelectedParts = [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }]
|
||||
}
|
||||
];
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.MOLDING_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
// Act
|
||||
actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts);
|
||||
|
||||
// Assert
|
||||
testVehiclePartDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("multiple glass locations", () => {
|
||||
// Arrange
|
||||
const previouslySelectedParts = {
|
||||
partsOrQuestions: [
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART1" }, { partNumber: "PART6" }, { partNumber: "PART3" }]
|
||||
},
|
||||
{
|
||||
glassLocation: "Driver",
|
||||
glassName: "Front",
|
||||
parts: [{ partNumber: "PART5" }, { partNumber: "PART4" }]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const currentlySelectedParts = [
|
||||
{
|
||||
glassLocation: "Driver",
|
||||
glassName: "Front",
|
||||
parts: [{ partNumber: "PART4" }, { partNumber: "PART5" }]
|
||||
},
|
||||
{
|
||||
glassLocation: "Windshield",
|
||||
glassName: "Single",
|
||||
parts: [{ partNumber: "PART3" }, { partNumber: "PART1" }, { partNumber: "PART2" }]
|
||||
}
|
||||
];
|
||||
|
||||
mutations.updatePageData(context, {
|
||||
page: fmgPageValues.CAPABILITY_QUESTIONS,
|
||||
data: previouslySelectedParts
|
||||
});
|
||||
|
||||
// Act
|
||||
actions.resetMoldingAndCapabilityQuestionAnswersIfNeeded(context, currentlySelectedParts);
|
||||
|
||||
// Assert
|
||||
testVehiclePartDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("saveMoldingQuestionAnswers", () => {
|
||||
test.todo("saves moldingQuestionAnswers")
|
||||
let context;
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mutations.resetState(state);
|
||||
context = state;
|
||||
context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value));
|
||||
context.getters = {
|
||||
...getters,
|
||||
damage: getters.damage(context)
|
||||
};
|
||||
})
|
||||
|
||||
test.todo("there are no previous answers => resets necessary fields")
|
||||
function testMoldingQuestionAnswerDependenciesHaveBeenReset(context, shouldAnswersBeReset) {
|
||||
if (shouldAnswersBeReset) {
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null });
|
||||
}
|
||||
else {
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_CAPABILITY_QUESTION_ANSWERS, null);
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_PAGE_DATA, { page: fmgPageValues.CAPABILITY_QUESTIONS, data: null });
|
||||
}
|
||||
}
|
||||
|
||||
// mix up the order to make sure sort is working
|
||||
test.todo("previous answers match current answers => does not reset fields")
|
||||
test("there are no previous answers => resets necessary fields", () => {
|
||||
// Arrange
|
||||
const previousMoldingQuestionAnswers = [];
|
||||
const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART2!" }];
|
||||
|
||||
test.todo("previous answers does not match current answers => resets necessary fields")
|
||||
actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("previous answers match current answers => does not reset fields", () => {
|
||||
// Arrange
|
||||
const previousMoldingQuestionAnswers = [{ partNum: "I'M A PART2!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART!" }];;
|
||||
const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART2!" }];
|
||||
|
||||
actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testMoldingQuestionAnswerDependenciesHaveBeenReset(context, false);
|
||||
})
|
||||
|
||||
test("previous answers do not match current answers => resets necessary fields", () => {
|
||||
// Arrange
|
||||
const previousMoldingQuestionAnswers = [{ partNum: "I'M A PART2!" }, { partNum: "I'M A PART4!" }, { partNum: "I'M A PART!" }];;
|
||||
const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART2!" }];
|
||||
|
||||
actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("previous answers have more questions/answers than current => resets fields", () => {
|
||||
// Arrange
|
||||
const previousMoldingQuestionAnswers = [{ partNum: "I'M A PART2!" }, { partNum: "I'M A PART4!" }, { partNum: "I'M A PART!" }];;
|
||||
const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }];
|
||||
|
||||
actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("current answers have more questions/answers than previous => resets fields", () => {
|
||||
// Arrange
|
||||
const previousMoldingQuestionAnswers = [{ partNum: "I'M A PART2!" }, { partNum: "I'M A PART!" }];;
|
||||
const currentMoldingQuestionAnswers = [{ partNum: "I'M A PART!" }, { partNum: "I'M A PART3!" }, { partNum: "I'M A PART2!" }];
|
||||
|
||||
actions.saveMoldingQuestionAnswers(context, previousMoldingQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveMoldingQuestionAnswers(context, currentMoldingQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testMoldingQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
})
|
||||
|
||||
describe("saveCapabilityQuestionAnswers", () => {
|
||||
test.todo("saves capabilityQuestionAnswers")
|
||||
let context;
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
mutations.resetState(state);
|
||||
context = state;
|
||||
context.commit = jest.fn().mockImplementation((storeMutation, value) => mutations[storeMutation](context, value));
|
||||
context.getters = {
|
||||
...getters,
|
||||
damage: getters.damage(context)
|
||||
};
|
||||
})
|
||||
|
||||
test.todo("there are no previous answers => resets necessary fields")
|
||||
function testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, shouldAnswersBeReset) {
|
||||
if (shouldAnswersBeReset) {
|
||||
expect(context.commit).toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
}
|
||||
else {
|
||||
expect(context.commit).not.toBeCalledWith(storeMutations.UPDATE_GLASS_PARTS, null);
|
||||
}
|
||||
}
|
||||
|
||||
// mix up the order to make sure sort is working
|
||||
test.todo("previous answers match current answers => does not reset fields")
|
||||
test("there are no previous answers => resets necessary fields", () => {
|
||||
// Arrange
|
||||
const previousCapabilityQuestionAnswers = [];
|
||||
const currentCapabilityQuestionAnswers = [{ result: "DYNAMIC" }, { result: "STATIC" }, { result: "UNKNOWN" }];
|
||||
|
||||
test.todo("previous answers does not match current answers => resets necessary fields")
|
||||
actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("previous answers match current answers => does not reset fields", () => {
|
||||
// Arrange
|
||||
const previousCapabilityQuestionAnswers = [{ result: "STATIC" }, { result: "DYNAMIC" }, { result: "UNKNOWN" }];
|
||||
const currentCapabilityQuestionAnswers = [{ result: "DYNAMIC" }, { result: "STATIC" }, { result: "UNKNOWN" }];
|
||||
|
||||
actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, false);
|
||||
})
|
||||
|
||||
test("previous answers do not match current answers => resets necessary fields", () => {
|
||||
// Arrange
|
||||
const previousCapabilityQuestionAnswers = [{ result: "DYNAMIC" }, { result: "DYNAMIC" }, { result: "STATIC" }];
|
||||
const currentCapabilityQuestionAnswers = [{ result: "STATIC" }, { result: "STATIC" }, { result: "DYNAMIC" }];
|
||||
|
||||
actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("previous answers have more questions/answers than current => resets fields", () => {
|
||||
// Arrange
|
||||
const previousCapabilityQuestionAnswers = [{ result: "STATIC" }, { result: "DYNAMIC" }, { result: "UNKNOWN" }];
|
||||
const currentCapabilityQuestionAnswers = [{ result: "DYNAMIC" }];
|
||||
|
||||
actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
|
||||
test("current answers have more questions/answers than previous => resets fields", () => {
|
||||
// Arrange
|
||||
const previousCapabilityQuestionAnswers = [{ result: "STATIC" }, { result: "UNKNOWN" }];
|
||||
const currentCapabilityQuestionAnswers = [{ result: "DYNAMIC" }, { result: "STATIC" }, { result: "UNKNOWN" }];
|
||||
actions.saveCapabilityQuestionAnswers(context, previousCapabilityQuestionAnswers);
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Act
|
||||
actions.saveCapabilityQuestionAnswers(context, currentCapabilityQuestionAnswers);
|
||||
|
||||
// Assert
|
||||
testCapabilityQuestionAnswerDependenciesHaveBeenReset(context, true);
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
describe("Getters", () => {
|
||||
it("Vehicle getter, should return vehicle data", () => {
|
||||
// Arrange
|
||||
|
|
@ -1315,7 +1988,7 @@ describe("Getters", () => {
|
|||
funnelOtherParts: null,
|
||||
funnelGlassToReplace: null
|
||||
}
|
||||
|
||||
|
||||
//Act
|
||||
mutations.updateYear(storeState, mockStateValues.funnelVehicleYear);
|
||||
mutations.updateMake(storeState, mockStateValues.funnelVehicleMake);
|
||||
|
|
@ -1334,7 +2007,7 @@ describe("Getters", () => {
|
|||
mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts);
|
||||
mutations.updateOtherParts(storeState, mockStateValues.funnelOtherParts);
|
||||
mutations.updateGlassToReplace(storeState, mockStateValues.funnelGlassToReplace);
|
||||
|
||||
|
||||
//Assert
|
||||
expect(getters.experimentOrder(storeState)).toEqual({
|
||||
funnelVehicleYear: mockStateValues.funnelVehicleYear,
|
||||
|
|
@ -1380,7 +2053,7 @@ describe("Getters", () => {
|
|||
funnelOtherParts: [],
|
||||
funnelGlassToReplace: []
|
||||
}
|
||||
|
||||
|
||||
//Act
|
||||
mutations.updateYear(storeState, mockStateValues.funnelVehicleYear);
|
||||
mutations.updateMake(storeState, mockStateValues.funnelVehicleMake);
|
||||
|
|
@ -1399,7 +2072,7 @@ describe("Getters", () => {
|
|||
mutations.updateGlassParts(storeState, mockStateValues.funnelGlassParts);
|
||||
mutations.updateOtherParts(storeState, mockStateValues.funnelOtherParts);
|
||||
mutations.updateGlassToReplace(storeState, mockStateValues.funnelGlassToReplace);
|
||||
|
||||
|
||||
//Assert
|
||||
expect(getters.experimentOrder(storeState)).toEqual({
|
||||
funnelVehicleYear: mockStateValues.funnelVehicleYear,
|
||||
|
|
@ -1451,7 +2124,7 @@ describe("Getters", () => {
|
|||
}
|
||||
],
|
||||
otherParts: [
|
||||
|
||||
|
||||
],
|
||||
glassToReplace: [
|
||||
{
|
||||
|
|
@ -1460,7 +2133,7 @@ describe("Getters", () => {
|
|||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
//Act
|
||||
mutations.updateYear(storeState, mockStateValues.vehicleYear);
|
||||
mutations.updateMake(storeState, mockStateValues.vehicleMake);
|
||||
|
|
@ -1479,7 +2152,7 @@ describe("Getters", () => {
|
|||
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
||||
mutations.updateOtherParts(storeState, mockStateValues.otherParts);
|
||||
mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace);
|
||||
|
||||
|
||||
//Assert
|
||||
expect(getters.experimentOrder(storeState)).toEqual({
|
||||
funnelVehicleYear: mockStateValues.vehicleYear,
|
||||
|
|
@ -1545,7 +2218,7 @@ describe("Getters", () => {
|
|||
}
|
||||
],
|
||||
otherParts: [
|
||||
|
||||
|
||||
],
|
||||
glassToReplace: [
|
||||
{
|
||||
|
|
@ -1566,7 +2239,7 @@ describe("Getters", () => {
|
|||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
//Act
|
||||
mutations.updateYear(storeState, mockStateValues.vehicleYear);
|
||||
mutations.updateMake(storeState, mockStateValues.vehicleMake);
|
||||
|
|
@ -1585,7 +2258,7 @@ describe("Getters", () => {
|
|||
mutations.updateGlassParts(storeState, mockStateValues.glassParts);
|
||||
mutations.updateOtherParts(storeState, mockStateValues.otherParts);
|
||||
mutations.updateGlassToReplace(storeState, mockStateValues.glassToReplace);
|
||||
|
||||
|
||||
//Assert
|
||||
expect(getters.experimentOrder(storeState)).toEqual({
|
||||
funnelVehicleYear: mockStateValues.vehicleYear,
|
||||
|
|
|
|||
|
|
@ -138,11 +138,13 @@ html {
|
|||
pointer-events: all;
|
||||
font-weight: $font-weight-normal;
|
||||
}
|
||||
&.btn.btn-primary:hover,
|
||||
&.btn.btn-primary:focus,
|
||||
&.btn.btn-primary:focus-visible {
|
||||
&.btn.btn-primary:hover {
|
||||
background: $gray-200;
|
||||
box-shadow: none;
|
||||
}
|
||||
&.btn.btn-primary:focus,
|
||||
&.btn.btn-primary:focus-visible {
|
||||
box-shadow: 0 0 0 3px $white, 0 0 0 5.5px $blue-700;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -211,11 +211,12 @@ describe("list-button-horizontal.vue", () => {
|
|||
isWide: false,
|
||||
modelValue: ["List Card Checkbox"],
|
||||
isMultiSelect: false,
|
||||
value: "Car-Front",
|
||||
selectedValues: ["Car-Front"]
|
||||
},
|
||||
});
|
||||
// Assert
|
||||
expect(wrapper.componentVM.checkValue).toEqual(["Car-Front"]);
|
||||
expect(wrapper.vm.checkValue).toEqual(true);
|
||||
});
|
||||
|
||||
it("Should run handleCheckChange if selectingInitiatesLoad is false and handleInputChange is triggered", async () => {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,34 @@
|
|||
<template>
|
||||
<div class="list-group list-button-horizontal d-flex flex-column w-100"
|
||||
:class="[(errors.length > 0 || hasError) ? 'has-error' : '', isCashOrInsurance ? 'radio-fancy' : '']"
|
||||
@keyup.space="triggerButton()" @keyup.up="handleKeyupArrow()" @keyup.down="handleKeyupArrow()"
|
||||
@keyup.left="handleKeyupArrow()" @keyup.right="handleKeyupArrow()">
|
||||
<input :type="isMultiSelect ? 'checkbox' : 'radio'" :id="buttonID" :name="groupName" :value="value"
|
||||
:aria-required="isRequired" v-model="checkValue" @change="handleInputChange()" />
|
||||
<label tabindex="-1" :for="buttonID" :aria-labelledby="buttonID"
|
||||
class="d-flex flex-column justify-content-center p-3" @mouseup="triggerButton()">
|
||||
<span class="m-0" :class="textPosition">
|
||||
{{ buttonLabel }}
|
||||
<div
|
||||
class="list-group list-button-horizontal d-flex flex-column w-100"
|
||||
:class="[(errors.length > 0 || hasError) ? 'has-error' : '']"
|
||||
@keyup.space="triggerButton()"
|
||||
@keyup.up="handleKeyupArrow()"
|
||||
@keyup.down="handleKeyupArrow()"
|
||||
@keyup.left="handleKeyupArrow()"
|
||||
@keyup.right="handleKeyupArrow()"
|
||||
>
|
||||
<input
|
||||
:type="isMultiSelect ? 'checkbox' : 'radio'"
|
||||
:id="buttonID"
|
||||
:name="groupName"
|
||||
:aria-required="isRequired"
|
||||
v-model="checkValue"
|
||||
:checked="checkValue"
|
||||
@change="handleInputChange()"
|
||||
/>
|
||||
<label
|
||||
tabindex="-1"
|
||||
:for="buttonID"
|
||||
:aria-label="buttonLabel"
|
||||
class="d-flex flex-column justify-content-center py-3 px-4"
|
||||
@mouseup="triggerButton()"
|
||||
>
|
||||
<span
|
||||
class="m-0"
|
||||
:class="textPosition"
|
||||
>
|
||||
{{buttonLabel}}
|
||||
</span>
|
||||
<span v-if="buttonLabelSubCopy" class="m-0 small" :class="textPosition">
|
||||
{{ buttonLabelSubCopy }}
|
||||
|
|
@ -58,14 +78,11 @@ export default {
|
|||
checkValue: Boolean,
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
if (Array.isArray(this.validateValue)) {
|
||||
this.checkValue = this.isValueSelectedByArray(this.selectedValues);
|
||||
const isSelectedByValidator = this.isValueSelectedByArray(this.validateValue);
|
||||
|
||||
if (this.checkValue != isSelectedByValidator) {
|
||||
this.handleChange(this.value);
|
||||
}
|
||||
created() {
|
||||
if (Array.isArray(this.selectedValues)) {
|
||||
this.checkValue = this.isMultiSelect
|
||||
? this.selectedValues.includes(this.value)
|
||||
: this.selectedValues[0] == this.value;
|
||||
}
|
||||
else {
|
||||
this.checkValue = this.selectedValues;
|
||||
|
|
@ -105,7 +122,7 @@ export default {
|
|||
handleCheckChange() {
|
||||
const emitEvent = {
|
||||
checkValue: this.checkValue, // only read on checkboxes, on handleCheckedChanged on button-question
|
||||
value: this.value.toString(),
|
||||
value: this.value,
|
||||
buttonId: this.buttonID && this.buttonID.toString(),
|
||||
};
|
||||
|
||||
|
|
@ -136,7 +153,7 @@ export default {
|
|||
handleChange,
|
||||
errors,
|
||||
value
|
||||
} = useField(toRef(props, "groupName"), toRef(props, "validationRules"), fieldOptions);
|
||||
} = useField(toRef(props, "groupName"), toRef(props, "validationRules"), fieldOptions);
|
||||
|
||||
const validateValue = value;
|
||||
return {
|
||||
|
|
@ -265,6 +282,12 @@ export default {
|
|||
}
|
||||
}
|
||||
}
|
||||
&.list-button-horizontal {
|
||||
height: 100%;
|
||||
label {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.col {
|
||||
|
|
|
|||
|
|
@ -21,7 +21,10 @@
|
|||
<p class="sub-label m-0">most popular</p>
|
||||
<ul>
|
||||
<li>New replacement windshield</li>
|
||||
<li>Expert installation and <textLink linkType="dashedUnderline" text="recalibration" href="#!" data-bs-toggle="modal" data-bs-target="#EstimateRainDefenseModal" aria-label="Modal window" /></li>
|
||||
<li><textLink linkType="dashedUnderline" text="Rain Defense" href="#!" data-bs-toggle="modal" data-bs-target="#EstimateRainDefenseModal" aria-label="Modal window" /> treatment</li>
|
||||
<li>New <textLink linkType="dashedUnderline" text="front wiper blades" href="#!" data-bs-toggle="modal" data-bs-target="#EstimateFrontWiperModal" aria-label="Modal window" /></li>
|
||||
<li>New <textLink linkType="dashedUnderline" text="rear wiper blade" href="#!" data-bs-toggle="modal" data-bs-target="#EstimateRearWiperModal" aria-label="Modal window" /></li>
|
||||
<li>Expert <textLink linkType="dashedUnderline" text="recalibration" href="#!" data-bs-toggle="modal" data-bs-target="#EstimateRecalModal" aria-label="Modal window" /></li>
|
||||
<li>Nationwide lifetime warranty</li>
|
||||
<li>New wiper blades</li>
|
||||
</ul>
|
||||
|
|
@ -46,6 +49,15 @@
|
|||
<modal
|
||||
cmsWidgetName="EstimateRainDefenseModal"
|
||||
/>
|
||||
<modal
|
||||
cmsWidgetName="EstimateFrontWiperModal"
|
||||
/>
|
||||
<modal
|
||||
cmsWidgetName="EstimateRearWiperModal"
|
||||
/>
|
||||
<modal
|
||||
cmsWidgetName="EstimateRecalModal"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
process.env.VUE_APP_CONSUMER_CF_DISTRO =
|
||||
"https://consumerapidev.safelite.com";
|
||||
"https://digitalapi.dev.sagaws.net";
|
||||
process.env.VUE_APP_HERITAGE_FUNNEL =
|
||||
"http://localhost:38000/default.aspx";
|
||||
process.env.VUE_APP_GOOGLE_PLACES_API_KEY =
|
||||
|
|
|
|||
Loading…
Reference in a new issue