CASH-2563

This commit is contained in:
credelinghuys 2026-05-07 12:52:02 -04:00
parent 1fb36e7ee7
commit dd291a980c
9 changed files with 172 additions and 34 deletions

View file

@ -1,5 +1,5 @@
const bailoutCodes = {
PARTS_NOT_FOUND: "PartsNotFound",
PARTS_NOT_FOUND: 10,
};
export { bailoutCodes };

View file

@ -57,15 +57,13 @@ export async function saveSession({
shouldAwaitSaveSessionQueue = false,
submitAfterSave = false,
createUnscheduledStatusWorkOrderForPIA = false,
bailoutCode = null,
}) {
// Add next save session to end of queue
const saveSessionPromise = flushSaveSessionQueue().then(() => {
return saveSessionHelper(
pageNameToLog,
submitAfterSave,
createUnscheduledStatusWorkOrderForPIA,
bailoutCode
createUnscheduledStatusWorkOrderForPIA
);
});
@ -108,12 +106,11 @@ export async function saveQuote({ pageNameToLog }) {
return;
}
export async function submitBailout({ pageNameToLog, bailoutCode }) {
export async function submitBailout({ pageNameToLog }) {
await saveSession({
pageNameToLog: pageNameToLog,
shouldAwaitSaveSessionQueue: true,
submitAfterSave: false,
bailoutCode: bailoutCode,
});
}
@ -158,15 +155,13 @@ async function loadSession(
async function saveSessionHelper(
pageNameToLog,
submitAfterSave = false,
createUnscheduledStatusWorkOrderForPIA = false,
bailoutCode = null
createUnscheduledStatusWorkOrderForPIA = false
) {
const savedSessionInfo = await baseMixin.methods.dispatchStoreActionWithLogging(
storeActions.SAVE_SESSION,
{
submitAfterSave: submitAfterSave,
createUnscheduledStatusWorkOrderForPIA: createUnscheduledStatusWorkOrderForPIA,
bailoutCode: bailoutCode,
},
pageNameToLog
);

View file

@ -0,0 +1,56 @@
// Components
import bailoutSuccess from "@/layouts/bailout-success/bailout-success.vue";
// Supporting Files
import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper.js";
// Mock our module for promises.
jest.mock("@/helpers/layout-helper.js", () => ({
settleAllPromises: jest.fn(),
}));
// Mock fetchCmsContentForPage
jest.mock("@/helpers/cms-content-helper", () => ({
fetchCmsContentForPage: jest.fn(),
}));
describe("bailout-success.vue", () => {
test("renders funnelHeader component", () => {
const { wrapper } = setupMocks();
expect(wrapper.findComponent({ name: "funnelHeader" }).exists()).toBe(true);
});
test("renders funnelSubHeader component", () => {
const { wrapper } = setupMocks();
expect(wrapper.findComponent({ name: "funnelSubHeader" }).exists()).toBe(true);
});
test("renders Form component", () => {
const { wrapper } = setupMocks();
expect(wrapper.findComponent({ name: "Form" }).exists()).toBe(true);
});
test("renders buttonMain component", () => {
const { wrapper } = setupMocks();
expect(wrapper.findComponent({ name: "buttonMain" }).exists()).toBe(true);
});
});
function setupMocks() {
const mountOptions = getMountOptions({});
//Mock props
const mockMixin = {
methods: {
getCmsContent: jest.fn(),
},
};
mountOptions.mixins = [mockMixin];
const wrapper = shallowMount(bailoutSuccess, mountOptions);
wrapper.vm.setCmsContent = jest.fn();
return { wrapper };
}

View file

@ -22,7 +22,7 @@
</template>
<script>
import Form from "vee-validate";
import { Form } from "vee-validate";
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import buttonMain from "@/ux-components/button-main/button-main";
@ -62,7 +62,7 @@ export default {
async forwardButtonAction() {
this.$router.navigateWithoutSaving(
this.navigationScenarios.CLICKED_BACK_TO_HOMEPAGE,
"bailout-success"
this.pageName
);
},
arePagePrerequisitesValid() {

View file

@ -16,6 +16,26 @@ jest.mock("@/helpers/cms-content-helper", () => ({
}));
describe("bailout.vue", () => {
test("renders funnelHeader component", () => {
const { wrapper } = setupMocks();
expect(wrapper.findComponent({ name: "funnelHeader" }).exists()).toBe(true);
});
test("renders funnelSubHeader component", () => {
const { wrapper } = setupMocks();
expect(wrapper.findComponent({ name: "funnelSubHeader" }).exists()).toBe(true);
});
test("renders Form component", () => {
const { wrapper } = setupMocks();
expect(wrapper.findComponent({ name: "Form" }).exists()).toBe(true);
});
test("renders navbar component", () => {
const { wrapper } = setupMocks();
expect(wrapper.findComponent({ name: "navbar" }).exists()).toBe(true);
});
test("arePagePrerequisitesValid should be true ", async () => {
//Arrange
const { wrapper } = setupMocks();

View file

@ -42,6 +42,23 @@
v-model="phoneNumber"
validationRules="phone-number-required" />
<textboxQuestion
class="mb-4"
cmsWidgetName="ServiceZipQuestionWidget"
v-model="serviceZipCode"
ref="serviceZip"
customInputId="serviceZip"
mask="#####"
validationRules="service-zip-required|service-zip-format" />
<alert
ref="alertInvalidZip"
v-if="displayInvalidZipAlert"
class="mb-4"
cmsWidgetName="AlertInvalidZipWidget"
alertClass="alert-danger"
v-bind:isDismissible="false" />
<checkboxQuestion
class="mb-5"
cmsWidgetName="TextMeQuestionWidget"
@ -69,6 +86,7 @@ import { Form, defineRule } from "vee-validate";
import funnelHeader from "@/fmg-components/funnel-header/funnel-header";
import navbar from "@/fmg-components/nav-bar/nav-bar";
import funnelSubHeader from "@/fmg-components/funnel-sub-header/funnel-sub-header";
import alert from "@/ux-components/alert/alert";
import textboxQuestion from "@/digital-components/textbox-question/textbox-question";
import phoneNumberQuestion from "@/digital-components/phone-number-question/phone-number-question";
import textBlock from "@/digital-components/text-block/text-block";
@ -95,6 +113,11 @@ defineRule(
errorMessages.EMAIL_ADDRESS_FORMAT
)
);
defineRule("service-zip-required", required(errorMessages.SERVICE_ZIP_REQUIRED));
defineRule(
"service-zip-format",
regex(/(^\d{5}$)|(^\d{5}-\d{4}$)/, errorMessages.SERVICE_ZIP_FORMAT)
);
export default {
name: "bailout",
@ -106,7 +129,9 @@ export default {
lastName: this.getLastNameFromStore(),
emailAddress: this.getEmailAddressFromStore(),
phoneNumber: this.getPhoneNumberFromStore(),
serviceZipCode: this.getServiceZipFromStore(),
isSmsOptIn: this.getIsSmsOptInFromStore(),
displayInvalidZipAlert: false,
};
},
@ -146,6 +171,9 @@ export default {
getPhoneNumberFromStore() {
return store.getters.order.customer.phoneNumber;
},
getServiceZipFromStore() {
return store.getters.order.serviceLocation.zipCode;
},
getIsSmsOptInFromStore() {
return store.getters.order.customer.isSmsOptIn;
},
@ -153,7 +181,17 @@ export default {
this.$router.go(-1);
},
async forwardButtonAction() {
await this.dispatchStoreAction(
this.displayInvalidZipAlert = false;
const validateZipResponse = this.dispatchStoreActionWithLogging(
this.storeActions.VALIDATE_ZIP,
{
zip: this.serviceZipCode,
},
this.pageName
);
const saveCustomerDetailsResponse = this.dispatchStoreAction(
this.storeActions.SAVE_CUSTOMER_DETAILS,
{
firstName: this.firstName,
@ -163,15 +201,45 @@ export default {
isSmsOptIn: this.isSmsOptIn,
},
false
).then(() => {
this.$router.navigateWithPageData(
this.navigationScenarios.CLICKED_FORWARD,
this.pageName,
);
const promiseResultMap = [
{
resultKey: "validateZipResponse",
promise: validateZipResponse,
},
{
resultKey: "saveCustomerDetailsResponse",
promise: saveCustomerDetailsResponse,
},
];
const resultMap = await settleAllPromises(promiseResultMap);
const isZipValid = resultMap.validateZipResponse.isValid;
if (isZipValid) {
await this.dispatchStoreActionWithLogging(
this.storeActions.SAVE_SERVICE_ZIP_CODE_INFO,
{
bailoutCode: this.bailoutCode,
}
);
});
zipCode: this.serviceZipCode,
state: resultMap.validateZipResponse.state,
zipCodeCtu: resultMap.validateZipResponse.zipCodeCtu,
},
false
).then(() => {
this.$router.navigateWithPageData(
this.navigationScenarios.CLICKED_FORWARD,
this.pageName,
{
bailoutCode: this.bailoutCode,
submit: true,
}
);
});
} else {
this.displayInvalidZipAlert = true;
return this.$refs.navbar.removeLoader();
}
},
arePagePrerequisitesValid() {
return true;
@ -192,6 +260,7 @@ export default {
Form,
funnelHeader,
funnelSubHeader,
alert,
navbar,
textBlock,
phoneNumberQuestion,

View file

@ -816,7 +816,7 @@ const routingTable = function () {
maps: [
{
scenario: navigationScenarios.CLICKED_BACK_TO_HOMEPAGE,
destinationPageData: routeData.VEHICLE,
destinationPageData: routeData.RESTART,
},
],
},

View file

@ -7,13 +7,7 @@ import store from "@/store";
import { handleSoftError } from "@/router/methods/error";
async function navigate(
scenario,
currentPageName,
withSaving = false,
forceTopLevelNav = false,
bailoutCode = null
) {
async function navigate(scenario, currentPageName, withSaving = false, forceTopLevelNav = false) {
// Check to see if calling page is same as current page.
// If not, cancel navigation before it begins.
const callingRoute = currentPageName;
@ -45,7 +39,7 @@ async function navigate(
store.getters?.applicationUser?.savedSessionId ||
store.getters?.order?.customer?.emailAddress
) {
await saveSession({ pageNameToLog: nextPage.name, bailoutCode: bailoutCode });
await saveSession({ pageNameToLog: nextPage.name });
}
}
@ -76,13 +70,15 @@ export async function navigateWithSaving(scenario, currentPageName) {
export async function navigateWithPageData(scenario, currentPageName, pageData = {}) {
const nextPage = getDestination(currentPageName, scenario);
if (pageData && pageData.bailoutCode) {
pageData.AppName = "FixMyGlass";
await savePageData(currentPageName, pageData);
return await navigate(scenario, currentPageName, true, false, pageData.bailoutCode);
} else {
await savePageData(nextPage.name, pageData);
return await navigate(scenario, currentPageName, true);
}
return await navigate(scenario, currentPageName, true);
}
export async function navigateAndForceTopLevelNavigation(

View file

@ -462,9 +462,11 @@ export const mutations = {
state.order.vehicle.registration.licensePlate = registrationInfo?.licensePlate;
},
updateServiceZip(state, serviceZipInfo) {
state.order.serviceLocation.state = serviceZipInfo.state;
state.order.serviceLocation.zipCode = serviceZipInfo.zipCode;
state.order.serviceLocation.zipCodeCtu = serviceZipInfo.zipCodeCtu;
state.order.serviceLocation.state = serviceZipInfo.state || serviceZipInfo.payload?.state;
state.order.serviceLocation.zipCode =
serviceZipInfo.zipCode || serviceZipInfo.payload?.zipCode;
state.order.serviceLocation.zipCodeCtu =
serviceZipInfo.zipCodeCtu || serviceZipInfo.payload?.zipCodeCtu;
},
updateServiceLocation(state, serviceLocationInfo) {
state.order.serviceLocation.address = serviceLocationInfo.address;