Initial work on moving components to the new bailout system.

This commit is contained in:
Josh Dassinger 2026-02-18 14:50:35 -06:00
parent 4b4df0ec46
commit c7b9137975
34 changed files with 374 additions and 700 deletions

View file

@ -21,7 +21,8 @@ global.$logger = {
function setupMocksForHttpClient({
endpoint = null,
isError = false,
additionalData = null
additionalData = null,
bailoutOnError = false
}) {
// Clear node module
axios.mockClear();
@ -57,7 +58,8 @@ function setupMocksForHttpClient({
return {
endpoint,
logApiCall: true
logApiCall: true,
bailoutOnError
};
}
@ -92,3 +94,21 @@ it('Global Methods - Call Http Client - Should Reject Promise', () => {
expect(err.status).toEqual(500);
});
});
it('Global Methods - Call Http Client - Rejected Promised - bailoutOnError: true - Calls global.bailoutOnAxiosError', () => {
// Arrange
const endpoint = 'https://mock.safelite.com';
const httpArgs = setupMocksForHttpClient({
endpoint,
isError: true,
bailoutOnError: true
});
analyticsMixIn.methods.pushEventToGA = jest.fn();
global.bailoutOnAxiosError = jest.fn();
// Act
globalMethods.callHttpClient(httpArgs).catch((err) => {
// Assert
expect(global.bailoutOnAxiosError).toHaveBeenCalledTimes(1);
});
});

View file

@ -4,8 +4,8 @@ import submitType from '@/constants/submit-type';
/*
Encapsulates asynchronous Save Session logic inside a promise to allow for Save Session queuing
*/
async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }) {
const savedSessionInfo = await store.saveSession({ submitAfterSave, createWorkOrderNumberForPIA });
async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }) {
const savedSessionInfo = await store.saveSession({ submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError });
if (savedSessionInfo) {
store.setSaveSessionInfo(savedSessionInfo.data);
}
@ -16,11 +16,11 @@ async function saveSessionHelper(store, { submitAfterSave, createWorkOrderNumber
This will also set Referral information in the store after saving, and then
update the cookie. To force synchronous behavior pass in 'true' for shouldAwaitSaveSessionQueue
*/
export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createWorkOrderNumberForPIA = false }) {
export async function saveSession({ shouldAwaitSaveSessionQueue = false, submitAfterSave = false, createWorkOrderNumberForPIA = false, bailoutOnError = false }) {
const store = useMainStore();
const saveSessionPromise = store.applicationUser.saveSessionPromise
? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA }))
: saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA });
? store.applicationUser.saveSessionPromise.then(() => saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }))
: saveSessionHelper(store, { submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError });
store.setSaveSessionPromise(saveSessionPromise);
@ -38,7 +38,8 @@ export async function submitWorkOrder({ submitType }) {
store.resetSubmittedOrder();
await saveSession({
shouldAwaitSaveSessionQueue: true,
submitAfterSave: true
submitAfterSave: true,
bailoutOnError: true
});
await store.createSubmittedOrder(submitType);
}

View file

@ -157,8 +157,7 @@ export default {
this.navigateForward(this.partsOrQuestionsData, null);
},
requestCallbackBailout() {
this.mainStore.setBailout(bailoutMessage.RequestCallback());
this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route);
this.$router.navigateBailout(bailoutMessage.RequestCallback());
}
}
};

View file

@ -63,7 +63,8 @@ const loadingModalStub = {
function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) {
const mountOptions = getMountOptions({
router: {
navigate: jest.fn()
navigate: jest.fn(),
navigateBailout: jest.fn()
}
});
@ -578,7 +579,7 @@ describe('coverageStatement.vue', () => {
undefined
);
});
test('If Verified ITAC, selected Cancel, navigate forward w/ CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP', () => {
test('If Verified ITAC, selected Cancel, navigateBailout', () => {
// Arrange
const deductible = servicePrice + 1;
const mainInitialState = {
@ -604,11 +605,7 @@ describe('coverageStatement.vue', () => {
wrapper.vm.cancelClaim();
// Assert
expect(wrapper.vm.$router.navigate)
.toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP,
undefined
);
expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled()
});
test('If No Comp and selected Safelite, navigate forward with CLICKED_FORWARD_WITH_SAFELITE scenario', () => {
// Arrange
@ -641,7 +638,7 @@ describe('coverageStatement.vue', () => {
undefined
);
});
test('If No comp and selected other shop, navigate forward with CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP ', () => {
test('If No comp and selected other shop, navigateBailout ', () => {
// Arrange
const mainInitialState = {
order: {
@ -666,11 +663,7 @@ describe('coverageStatement.vue', () => {
wrapper.vm.cancelClaim();
// Assert
expect(wrapper.vm.$router.navigate)
.toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP,
undefined
);
expect(wrapper.vm.$router.navigateBailout).toHaveBeenCalled()
});
});
describe('openCancelClaimModal', () => {

View file

@ -381,17 +381,6 @@ export default {
// We only call the ITAC pricing endpoint if we are not repair or we are NoComp
if (!this.isRepair || this.mainStore.isNoComp) {
const pricingResults = await useMainStore().getITACPriceOrderItems(availableLineItems)
.catch((err) => {
useMainStore().setBailout(bailoutMessage.pricingResponseError(
availableLineItems.map((li) => li.partNumber),
{
code: err.code,
message: err.message,
data: err.data
}
));
this.navigateWithScenario(navigationScenarios.PRICING_LOOKUP_ERROR);
});
this.setBaseServiceLineItems(pricingResults);
}
},
@ -402,8 +391,7 @@ export default {
this.mainStore.updateIsSafeliteProvider(true);
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE);
} else {
this.mainStore.setBailout(bailoutMessage.coverageStatementInvalidState());
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE);
this.$router.navigateBailout(bailoutMessage.coverageStatementInvalidState())
}
},
navigateWithScenario(scenario) {
@ -457,8 +445,7 @@ export default {
},
cancelClaim() {
this.mainStore.updateIsSafeliteProvider(false);
this.mainStore.setBailout(bailoutMessage.RequestCallback());
this.navigateWithScenario(navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP);
this.$router.navigateBailout(bailoutMessage.RequestCallback())
}
}
};

View file

@ -357,11 +357,15 @@ describe('duplicateCheck.vue', () => {
useMainStore().loadSession = jest.fn().mockImplementation(() => Promise.reject(error));
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect.assertions(2);
try {
await wrapper.vm.forwardButtonAction();
} catch (e) {
expect(e).toMatch(error);
}
expect(wrapper.vm.mainStore.loadSession).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
});
test('coverageType deductible and policy vehicles returned => CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES', async () => {
// Arrange

View file

@ -140,18 +140,14 @@ export default {
return;
}
try {
const selectedReferral =
this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer);
const selectedReferral =
this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer);
if (selectedReferral) {
await this.mainStore.loadSession(selectedReferral);
}
} catch (err) {
console.error(`Error on loading session from duplicate check ${err}`);
} finally {
this.navigateForward();
if (selectedReferral) {
await this.mainStore.loadSession(selectedReferral);
}
this.navigateForward();
},
navigateForward() {
this.mainStore.updateDuplicateCheckVisited(true);

View file

@ -151,37 +151,33 @@ export default {
this.mainStore.issConfig.isCoverageEnabled = data.coverageEnabled;
this.mainStore.issConfig.siteType = data.siteType;
try {
if (data.clientFlags) {
const clientFlags = JSON.parse(data.clientFlags);
if (data.clientFlags) {
const clientFlags = JSON.parse(data.clientFlags);
if (clientFlags.TPAEnabled) {
this.mainStore.issConfig.enableTPAFlow = true;
}
if (clientFlags.ClientFullName != null) {
this.mainStore.issConfig.clientFullName = clientFlags.ClientFullName;
}
if (clientFlags.ClientDisplayName != null) {
this.mainStore.issConfig.clientDisplayName = clientFlags.ClientDisplayName;
this.mainStore.issConfig.clientPossessiveName = toPossessive(clientFlags.ClientDisplayName);
}
if (clientFlags.ClientPossessiveName != null) {
this.mainStore.issConfig.clientPossessiveName = clientFlags.ClientPossessiveName;
}
if (clientFlags.ClaimRegistrationRequired) {
this.mainStore.issConfig.isClaimRegistrationRequired = true;
}
if (clientFlags.EnableNoCompQuote) {
this.mainStore.issConfig.enableNoCompQuote = true;
}
if (clientFlags.TPAEnabled) {
this.mainStore.issConfig.enableTPAFlow = true;
}
if (clientFlags.ClientFullName != null) {
this.mainStore.issConfig.clientFullName = clientFlags.ClientFullName;
}
if (clientFlags.ClientDisplayName != null) {
this.mainStore.issConfig.clientDisplayName = clientFlags.ClientDisplayName;
this.mainStore.issConfig.clientPossessiveName = toPossessive(clientFlags.ClientDisplayName);
}
if (clientFlags.ClientPossessiveName != null) {
this.mainStore.issConfig.clientPossessiveName = clientFlags.ClientPossessiveName;
}
if (clientFlags.ClaimRegistrationRequired) {
this.mainStore.issConfig.isClaimRegistrationRequired = true;
}
if (clientFlags.EnableNoCompQuote) {
this.mainStore.issConfig.enableNoCompQuote = true;
}
} catch (e) {
console.error(`Error parsing client flags: ${e}`);
}
},
combineClientParameters(configParams, queryStringParams) {

View file

@ -148,8 +148,7 @@ export default {
this.navigateForward(partsOrQuestions, null);
},
requestCallbackBailout() {
this.mainStore.setBailout(bailoutMessage.RequestCallback());
this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route);
this.$router.navigateBailout(bailoutMessage.RequestCallback());
}
}
};

View file

@ -148,8 +148,7 @@ export default {
this.navigateForward(glassPartsForStore, null);
},
requestCallbackBailout() {
this.mainStore.setBailout(bailoutMessage.RequestCallback());
this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route);
this.$router.navigateBailout(bailoutMessage.RequestCallback());
}
},
};

View file

@ -260,25 +260,16 @@ export default {
async forwardButtonAction() {
useMainStore().savePaymentMethodChoice(this.paymentMethod);
if (this.paymentMethod === paymentMethods.PAY_AT_TIME_OF_SERVICE) {
try {
await submitWorkOrder({ submitType: submitType.SAFELITE });
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD,
this.$route
);
} catch (error) {
useMainStore().setBailout(bailoutMessage.saveSessionError(error.data));
this.$router.navigate(
this.navigationScenarios.SAVE_SESSION_FAILED,
this.$route,
{ issPage: issPageValues.PAYMENT_METHOD }
);
console.error(`error: response from submit work order:${error.message}`);
}
await submitWorkOrder({ submitType: submitType.SAFELITE });
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD,
this.$route
);
} else {
await saveSession({
createWorkOrderNumberForPIA: true,
shouldAwaitSaveSessionQueue: true
shouldAwaitSaveSessionQueue: true,
bailoutOnError: true
});
this.$router.navigate(

View file

@ -124,13 +124,7 @@ export default {
},
async saveAndSubmitWorkOrder() {
// Final work order submit after returning from pay in advance.
try {
await submitWorkOrder({ submitType: submitType.SAFELITE });
} catch (error) {
console.error(`error: response from submit work order:${error.message}`);
this.navigateOnPayInAdvanceError();
return;
}
await submitWorkOrder({ submitType: submitType.SAFELITE });
this.$router.navigate(
this.navigationScenarios.PAY_IN_ADVANCE_SUCCESS,

View file

@ -252,12 +252,12 @@ describe('policy-vehicles.vue', () => {
);
test(
'Error in lookupVehicleByVin call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario.',
'Error in lookupVehicleByVin call => error thrown in forwardButtonAction.',
async () => {
// Arrange
const { wrapper } = setupMocks({});
const lookupReturnValue = { error: true, status: 500, data: 'error' };
wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(lookupReturnValue);
wrapper.vm.lookupVehicleByVin = jest.fn().mockReturnValue(Promise.reject(lookupReturnValue));
const vin = getRandomString(17, 17);
await wrapper.setData({
@ -270,21 +270,14 @@ describe('policy-vehicles.vue', () => {
});
// Act
wrapper.vm.mainStore.applicationUser.pageData[issPageValues.BAILOUT_PAGE] = {
'bailout-page': {
bailoutCode: bailoutCode.VehicleVinLookupError
}
};
await wrapper.vm.forwardButtonAction();
// Assert
expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalledWith(bailoutMessage.vehicleVinLookupError(vin, lookupReturnValue.data));
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
undefined,
{},
{}
);
expect.assertions(1);
try {
await wrapper.vm.forwardButtonAction();
} catch (e) {
expect(e).toBe(lookupReturnValue);
}
}
);

View file

@ -59,7 +59,6 @@ import vehicleSelectionOptions from '@/constants/vehicle-selection-options.js';
import endorsementOptions from '@/constants/endorsement-options.js';
import globalRules from '@/constants/global-rules.js';
import { useMainStore } from '@/store/index.js';
import bailoutMessage from '@/constants/bailoutMessage';
import {
deductibleForSelectedVehicle,
endorsementsForSelectedVehicle,
@ -144,10 +143,6 @@ export default {
},
repairWaivedForSelectedVehicle() {
return repairWaivedForSelectedVehicle(this.selectedPolicyVehicle);
},
selectedVehicle() {
const vehicle = this.mainStore.lookupVehicleByVin(this.selectedVehicleVin);
return vehicle;
}
},
watch: {
@ -158,24 +153,23 @@ export default {
this.mainStore.resetVehicleState();
} else {
// get vehicle details from selected VIN
const vehicle = await this.lookupVehicleByVin(value);
try {
const vehicle = await this.lookupVehicleByVin(value);
// handle error in case vehicle info doesn't come back for selected VIN
if (vehicle?.error === true) {
if (!vehicle?.data.canSafeliteService) {
this.displayNoServiceAlert = true;
return;
}
if (vehicle) {
// save selected vehicle to the store
useMainStore().updateVehicle({
...vehicle.data,
policyVehicleId: this.selectedPolicyVehicle?.id,
vin: value
});
}
} catch (e) {
this.mainStore.resetVehicleState();
return;
}
if (!vehicle?.data.canSafeliteService) {
this.displayNoServiceAlert = true;
return;
}
if (vehicle) {
// save selected vehicle to the store
useMainStore().updateVehicle({
...vehicle.data,
policyVehicleId: this.selectedPolicyVehicle?.id,
vin: value
});
}
}
}
@ -217,11 +211,7 @@ export default {
return this.navigateForward();
}
this.mainStore.setBailout(bailoutMessage.vehicleVinLookupError(
vehicle.vin,
vehicleLookupResponse.data
));
return this.navigateForward();
return;
}
useMainStore().updateVehicle({
@ -242,14 +232,7 @@ export default {
return this.navigateForward();
},
navigateForward() {
if (this.mainStore.isBailout) {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route,
{},
{}
);
} else if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
if (this.selectedVehicleVin === vehicleSelectionOptions.VEHICLE_NOT_LISTED) {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_NON_LISTED_VEHICLE,
this.$route,

View file

@ -20,7 +20,8 @@ jest.mock('@/helpers/cms-content-helper', () => ({
function setupMocks(mockApiResponses) {
const mountOptions = getMountOptions({
router: {
navigate: jest.fn()
navigate: jest.fn(),
navigateBailout: jest.fn()
},
route: 'provider-preference'
});
@ -43,7 +44,7 @@ function setupMocks(mockApiResponses) {
}
describe('provider-preference.vue', () => {
test('Should navigate to tpa disabled route page when TPAOption selected and TPA Flow disabled', () => {
test('Should navigateBailout when TPAOption selected and TPA Flow disabled', () => {
// Arrange
const { wrapper } = setupMocks();
@ -52,7 +53,7 @@ describe('provider-preference.vue', () => {
wrapper.vm.findAnotherShopClicked();
// Test
expect(wrapper.vm.$router.navigate).toBeCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, 'provider-preference');
expect(wrapper.vm.$router.navigateBailout).toBeCalled();
});
test('Should navigate to safelite flow when navigateWithTPARecalAnswer is called with SafeliteOption', () => {

View file

@ -186,9 +186,7 @@ export default {
this.scheduleWithTPA();
}
} else {
this.mainStore.setBailout(bailoutMessage.TPANotEnabled());
const scenario = this.navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED;
this.navigateForward(scenario);
this.$router.navigateBailout(bailoutMessage.TPANotEnabled());
}
},
openStateSteeringModal() {

View file

@ -53,7 +53,8 @@ const loaderStub = {
function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRun = () => {}) {
const mountOptions = getMountOptions({
router: {
navigate: jest.fn()
navigate: jest.fn(),
navigateBailout: jest.fn()
},
route: {
query: { issPage: 'tpa-search' }
@ -787,7 +788,7 @@ describe('tpa-search.vue', () => {
});
describe('needHelpLinkClick', () => {
test('sets bailout and navigates', () => {
test('navigatesBailout', () => {
// Arrange
const { wrapper } = getMountedComponent();
@ -795,11 +796,7 @@ describe('tpa-search.vue', () => {
wrapper.vm.needHelpLinkClick();
// Assert
expect(wrapper.vm.mainStore.setBailout).toHaveBeenCalled();
expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith(
navigationScenarios.CLICKED_NEED_HELP,
expect.anything()
);
expect(wrapper.vm.$router.navigateBailout).toBeCalled();
});
});

View file

@ -358,11 +358,7 @@ export default {
return getTpaProvidersResult?.data ?? [];
},
needHelpLinkClick() {
this.mainStore.setBailout(bailoutMessage.RequestCallback());
this.$router.navigate(
this.navigationScenarios.CLICKED_NEED_HELP,
this.$route
);
this.$router.navigateBailout(bailoutMessage.RequestCallback());
},
async searchClick() {
if (isNaN(this.tpaSearchValue)) {

View file

@ -315,20 +315,8 @@ export default {
};
},
async forwardButtonAction() {
try {
await submitWorkOrder({ submitType: submitType.TPA }).then(() => {
this.navigate(this.navigationScenarios.CLICKED_FORWARD);
}).catch((submitError) => {
this.mainStore.setBailout(bailoutMessage.saveSessionError(submitError.data));
this.navigate(
this.navigationScenarios.SAVE_SESSION_FAILED,
this.$route,
{ issPage: this.issPageValues.TPA_SUBMIT }
);
});
} catch (error) {
console.error(`error: response from submit work order:${error.message}`);
}
await submitWorkOrder({ submitType: submitType.TPA })
this.navigate(this.navigationScenarios.CLICKED_FORWARD);
},
navigate(scenario) {
this.$router.navigate(scenario, this.$route);

View file

@ -139,7 +139,7 @@ describe('vehicle-damage.vue', () => {
expect(vehicleQuestionsMixin.methods.getPartsOrQuestions).toHaveBeenCalledTimes(1);
expect(vehicleQuestionsMixin.methods.navigateForward).toHaveBeenCalledTimes(1);
});
test('Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario', async () => {
test('Error in getPartsOrQuestions call => error thrown in forwardButtonAction', async () => {
mountOptions.global.plugins = [createTestingPinia({
initialState: {
main: {
@ -154,23 +154,21 @@ describe('vehicle-damage.vue', () => {
}
}
})];
mountOptions.data = () => ({
hasBailedOut: true
});
mountOptions.data = () => ({});
const wrapper = mount(VehicleDamageComponent, mountOptions);
const siteFooterWrapper = wrapper.getComponent({ ref: 'siteFooter' });
const partsQuestionsErrorResponse = {
error: 'Error getting parts'
};
vehicleQuestionsMixin.methods.getPartsOrQuestions.mockImplementation(() => (
partsQuestionsErrorResponse
Promise.reject(partsQuestionsErrorResponse)
));
siteFooterWrapper.vm.$emit('forwardClicked');
await flushPromises();
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate)
.toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT, mockRoute);
expect.assertions(1);
try {
await wrapper.vm.forwardButtonAction();
} catch (e) {
expect(e).toBe(partsQuestionsErrorResponse);
}
});
});

View file

@ -189,8 +189,7 @@ export default {
this.getPassengerSideReplaceOptionsFromStore()
},
selectedWindshieldOptions: this.getWindshieldOptionsFromStore(),
selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore(),
hasBailedOut: false
selectedRearReplaceOptions: this.getRearReplaceOptionsFromStore()
};
},
computed: {
@ -429,23 +428,12 @@ export default {
// If vin already exists or not replacing windshield, get parts/questions and navigate forward
const partsOrQuestionsResponse = await this.getPartsOrQuestions();
if (partsOrQuestionsResponse.error) {
this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data));
window.console.error('Error on retrieving PartsOrQuestions');
this.hasBailedOut = true;
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
}
// Comes from vehicleQuestionsMixin.navigateForward()
if (!this.hasBailedOut) {
await this.navigateForward(
partsOrQuestionsResponse.data.partsOrQuestions,
this
);
}
await this.navigateForward(
partsOrQuestionsResponse.data.partsOrQuestions,
this
);
} else {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,

View file

@ -113,21 +113,10 @@ export default {
break;
case vinLookupMethodSelections.NOVIN:
const partsOrQuestionsResponse = await this.getPartsOrQuestions();
if (partsOrQuestionsResponse.error) {
this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data));
window.console.error('Error on retrieving PartsOrQuestions');
this.hasBailedOut = true;
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
}
else {
await this.navigateForward(
partsOrQuestionsResponse.data.partsOrQuestions,
this
);
}
await this.navigateForward(
partsOrQuestionsResponse.data.partsOrQuestions,
this
);
break;
default:
break;

View file

@ -64,6 +64,7 @@ import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin';
import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store';
import widgetFields from '@/constants/cms-widget-fields';
import bailoutMessage from '@/constants/bailoutMessage';
export default {
name: 'vehicle-parts',
@ -213,8 +214,7 @@ export default {
});
},
requestCallbackBailout() {
this.mainStore.setBailout(bailoutMessage.RequestCallback());
this.$router.navigate(navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT, this.$route);
this.$router.navigateBailout(bailoutMessage.RequestCallback());
}
}
};

View file

@ -206,77 +206,25 @@ export default {
},
navigateForward() {
this.mainStore.setVehicle().then(
() => {
if (this.mainStore.isBailout) {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
return;
}
this.mainStore.setVehicle().then(() => {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD,
this.$route
);
},
(error) => {
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, this.mainStore.vehicle.style, error));
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
}
);
},
async updateYearValues() {
return this.mainStore.getVehicleYears().then(
(response) => response,
(error) => {
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(null, null, null, null, error));
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
}
);
return this.mainStore.getVehicleYears();
},
async updateMakeValues() {
return this.mainStore.getVehicleMakes().then(
(response) => response,
(error) => {
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, null, null, null, error));
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
}
);
return this.mainStore.getVehicleMakes();
},
async updateModelValues() {
return this.mainStore.getVehicleModels().then(
(response) => response,
(error) => {
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, null, null, error));
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
}
);
return this.mainStore.getVehicleModels();
},
async updateStyleValues() {
return this.mainStore.getVehicleStyles().then(
(response) => response,
(error) => {
this.mainStore.setBailout(bailoutMessage.vehicleYMMSLookupError(this.mainStore.vehicle.year, this.mainStore.vehicle.make, this.mainStore.vehicle.model, null, error));
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
}
);
return this.mainStore.getVehicleStyles();
},
resetAlert() {
this.displayNoServiceAlert = false;

View file

@ -148,7 +148,8 @@ const mockRoute = {
};
const mockRouter = {
navigate: jest.fn(),
navigateWithSpinner: jest.fn()
navigateWithSpinner: jest.fn(),
navigateBailout: jest.fn()
};
const maska = jest.fn();
jest.mock('@/helpers/damage-helper', () => ({
@ -553,45 +554,6 @@ describe('vin-lookup.vue', () => {
);
});
});
test(
'Error in getPartsOrQuestions call => bailout true and navigate forward with CLICKED_FORWARD_WITH_BAILOUT scenario',
async () => {
const user = userEvent.setup();
mountOptions.global.stubs.vinQuestion = false;
mountOptions.data = () => ({
vinWithNonMatchingCarId: false,
isCarIdDifferentFromTheStore: false,
vin: mockValidVin,
hasBailedOut: true
});
getPartsOrQuestions.mockResponse = partsOrQuestionsErrorMockResponse;
jest.spyOn(VinLookupComponent.methods, lookupVehicleByVin.methodName)
.mockResolvedValue(lookupVehicleByVin.mockResponse);
jest.spyOn(vehicleQuestionsMixin.methods, getPartsOrQuestions.methodName)
.mockResolvedValue(getPartsOrQuestions.mockResponse);
const { container } = render(VinLookupComponent, mountOptions);
const vinInput = container.querySelector(vinInputSelector);
await user.type(vinInput, mockValidVin);
const continueButton = container.querySelector(continueButtonQuerySelector);
await user.click(continueButton);
await flushPromises();
await waitFor(() => {
expect(mockRouter.navigate).toHaveBeenCalledTimes(1);
expect(mockRouter.navigate)
.toHaveBeenCalledWith(
navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
mockRoute
);
});
}
);
});
});
});

View file

@ -112,8 +112,7 @@ export default {
vinWithNonMatchingCarId: vin?.length > 0 && !this.hasValidCarId(),
vin,
forwardButtonCarStyle: '',
vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId(),
hasBailedOut: false
vinPopulatedOnPageLoad: vin?.length > 0 && this.hasValidCarId()
};
},
computed: {
@ -176,35 +175,37 @@ export default {
showIssLoadingModal(true);
if (this.needToLookupVehicle) {
const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin);
try {
const vehicleLookupResponse = await this.lookupVehicleByVin(this.vin);
if (vehicleLookupResponse.error) {
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND;
this.mainStore.setBailout(bailoutMessage.vehicleNotFound(this.vin));
this.resetVehicleFromLookup();
// Temp solution to turn on 'disabled' style on the Continue button
// because the form itself actually passes its client-side validation.
// SSR-189 Scenario #4.
this.$refs.siteFooter.enableForwardAction();
showIssLoadingModal(false);
return;
}
if (!vehicleLookupResponse.data.canSafeliteService) {
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NO_SERVICE;
this.resetVehicleFromLookup();
this.$refs.siteFooter.disableForwardButton();
showIssLoadingModal(false);
return;
}
// Add vin bcs the response from the service doesn't contain vin
this.vehicleFromLookup = Object.assign(
vehicleLookupResponse.data,
{
vin: this.vin
if (!vehicleLookupResponse.data.canSafeliteService) {
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NO_SERVICE;
this.resetVehicleFromLookup();
this.$refs.siteFooter.disableForwardButton();
showIssLoadingModal(false);
return;
}
);
// Add vin bcs the response from the service doesn't contain vin
this.vehicleFromLookup = Object.assign(
vehicleLookupResponse.data,
{
vin: this.vin
}
);
} catch (e) {
if (e.status === 404) {
this.activeVehicleLookupAlertType = vehicleLookupAlertTypes.NOT_FOUND;
this.resetVehicleFromLookup();
// Temp solution to turn on 'disabled' style on the Continue button
// because the form itself actually passes its client-side validation.
// SSR-189 Scenario #4.
this.$refs.siteFooter.enableForwardAction();
showIssLoadingModal(false);
return;
}
throw e;
}
}
if (this.needToLookupVehicle && this.isCarIdDifferentFromTheStore) {
@ -259,34 +260,15 @@ export default {
}
const partsOrQuestionsResponse = await this.getPartsOrQuestions();
if (partsOrQuestionsResponse.error) {
this.mainStore.setBailout(bailoutMessage.PartsServiceError(partsOrQuestionsResponse.error.data));
window.console.error('Error on retrieving PartsOrQuestions');
this.hasBailedOut = true;
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
}
// Comes from vehicleQuestionsMixin.navigateForward()
if (!this.hasBailedOut) {
await this.navigateForward(
partsOrQuestionsResponse.data.partsOrQuestions,
this
);
}
await this.navigateForward(
partsOrQuestionsResponse.data.partsOrQuestions,
this
);
},
async lookupVehicleByVin(vin) {
try {
return await this.mainStore.lookupVehicleByVin(vin);
} catch (responseError) {
return {
error: {
status: responseError.status
}
};
}
return this.mainStore.lookupVehicleByVin(vin);
},
resetActiveAlert() {
this.activeVehicleLookupAlertType = null;

View file

@ -72,7 +72,8 @@ function setupMocks({
const mockDataMountOptions = {
...mountOptionsMockData,
router: {
navigate: jest.fn()
navigate: jest.fn(),
navigateBailout: jest.fn()
}
};
@ -166,7 +167,7 @@ describe('navigation', () => {
const { wrapper } = setupMocks({});
wrapper.vm.mainStore.getDuplicateReferrals = jest.fn().mockImplementation(() => Promise.resolve({}));
wrapper.vm.mainStore.applicationUser.duplicateOrders = [{ test: 'a' }];
wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.reject());
wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.reject({ isAxiosError: false }));
// Act
await wrapper.vm.forwardButtonAction();
@ -316,10 +317,14 @@ describe('navigation', () => {
}));
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(wrapper.vm.$router.navigate).toHaveBeenCalled();
expect.assertions(1);
try {
await wrapper.vm.forwardButtonAction();
} catch (e) {
expect(e).toMatch(error);
}
});
});
});

View file

@ -303,54 +303,47 @@ export default {
},
methods: {
async forwardButtonAction() {
try {
this.mainStore.updatePolicyData(this.welcomePageModel);
const promises = [];
promises.push(this.configureZip().then(async () => await this.mainStore.getBillToInfo()));
this.mainStore.updatePolicyData(this.welcomePageModel);
// Clear duplicate orders if navigating away from the welcome page after visiting duplicate check page.
if (this.mainStore.order.visitedDuplicateCheckPage) {
this.mainStore.clearDuplicateOrders();
}
// Skip duplicate check if loaded from cookie or already visited duplicate check page.
if (!this.mainStore.order.loadedFromCookie && !this.mainStore.order.visitedDuplicateCheckPage) {
promises.push(this.mainStore.getDuplicateReferrals());
}
promises.push(this.mainStore.getCoveragePolicyInfo());
await Promise.allSettled(promises);
} catch (e) {
console.error(e);
// TODO: Bailout?
} finally {
if (!this.displayInvalidZipAlert) {
await saveSession({ shouldAwaitSaveSessionQueue: true })
.catch((error) => {
this.mainStore.setBailout(bailoutMessage.saveSessionError(error.data));
})
.finally(() => this.navigateForward());
}
// We want to await this separately. It's quick and if this has an invalid zip we don't want to be waiting on the slower API calls
await this.configureZip();
if (this.displayInvalidZipAlert) {
showIssLoadingModal(false);
return;
}
// Clear duplicate orders if navigating away from the welcome page after visiting duplicate check page.
if (this.mainStore.order.visitedDuplicateCheckPage) {
this.mainStore.clearDuplicateOrders();
}
const promises = [
// Suppress error from API call we can try again later in the flow
this.mainStore.getBillToInfo()
];
// Skip duplicate check if loaded from cookie or already visited duplicate check page.
if (!this.mainStore.order.loadedFromCookie && !this.mainStore.order.visitedDuplicateCheckPage) {
promises.push(this.mainStore.getDuplicateReferrals());
}
promises.push(this.mainStore.getCoveragePolicyInfo());
await Promise.all(promises);
await saveSession({shouldAwaitSaveSessionQueue: true})
.catch((error) => this.mainStore.setBailout(bailoutMessage.saveSessionError(error)));
this.navigateForward();
},
async configureZip() {
try {
this.displayInvalidZipAlert = false;
await this.mainStore.validateZip({ zip: this.welcomePageModel.policyZipCode });
return Promise.resolve();
} catch (e) {
if (e.isAxiosError) {
throw e;
}
this.displayInvalidZipAlert = true;
return Promise.reject(e);
}
},
navigateForward() {
if (this.mainStore.isBailout) {
this.$router.navigate(
this.navigationScenarios.SAVE_SESSION_FAILED,
this.$route,
{},
{ [routerParams.SKIP_SAVE_SESSION]: true }
);
} else if ((this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false)
if ((this.mainStore.applicationUser.duplicateOrders?.length > 0 ?? false)
&& !this.answeredContinueModal) {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_DUPLICATES,
@ -445,12 +438,7 @@ export default {
.then((response) => {
this.answeredContinueModal = true;
if (response && !response.provider?.isSafeliteProvider) {
this.mainStore.setBailout(bailoutMessage.SafeliteNotTheProvider());
this.hasBailedOut = true;
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
this.$route
);
this.$router.navigateBailout(bailoutMessage.SafeliteNotTheProvider());
} else {
this.updateWelcomePageModel(response);
}

View file

@ -353,16 +353,7 @@ export default {
},
async getPartsOrQuestions() {
try {
return await useMainStore().getPartsOrQuestions();
} catch (responseError) {
return {
error: {
status: responseError.status,
data: responseError.data
}
};
}
return useMainStore().getPartsOrQuestions();
},
// Can't use `this` because navigateForward is also called from vin-pages-mixin

View file

@ -25,100 +25,95 @@ const routes = [
path: '/',
name: 'root',
async beforeEnter(to, from, next) {
try {
const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage;
const fromQueryPage = from.query?.issPage;
const issPageToUse = !to.query.issPage ? issPageValues.WELCOME_PAGE : to.query.issPage;
const fromQueryPage = from.query?.issPage;
if ((issPageToUse === issPageValues.ACCESS_DENIED
if ((issPageToUse === issPageValues.ACCESS_DENIED
|| (issPageToUse !== issPageValues.ENTRY_PAGE && !useMainStore().issConfig.parentAccountNumber))
&& process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost'
) {
return await GoToAccessIsDenied(next);
&& process.env.VUE_APP_CURRENT_ENVIRONMENT !== 'Localhost'
) {
return await GoToAccessIsDenied(next);
}
// Do not run these for the main entry page - as it is not part of the user flow.
if (issPageToUse !== issPageValues.ENTRY_PAGE) {
if (analyticsMixin.methods.noSession()) {
await analyticsMixin.methods.initSession();
} else {
updateSessionIdCookie();
}
// Do not run these for the main entry page - as it is not part of the user flow.
if (issPageToUse !== issPageValues.ENTRY_PAGE) {
if (analyticsMixin.methods.noSession()) {
await analyticsMixin.methods.initSession();
} else {
updateSessionIdCookie();
}
await runExperiments(issPageToUse); // fmg has this further down
}
await runExperiments(issPageToUse); // fmg has this further down
// Intercept all navigation if a submitted order exists in storage
if (useMainStore().hasSubmittedOrder()) {
if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
return await GoToConfirmationPage(next, useMainStore().getSubmittedOrder());
}
}
// If the saved session has timed out, clear the session, execute 404 logic.
if (getISSCookie() !== null && !isSavedSessionStillActive()) {
// await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
await GoToStartOn404(next);
}
// Process ISS cookie.
// Skip if Entry Page or Refreshing Welcome page
if (issPageToUse !== issPageValues.ENTRY_PAGE
&& !(issPageToUse === issPageValues.WELCOME_PAGE && fromQueryPage === undefined)) {
updateOrCreateISSCookie();
}
if (router.hasRoute(issPageToUse)) {
// Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function.
let component = router.getRoutes().filter((x) => x.name === issPageToUse)[0].components;
// If the component hasn't been loaded fully, load it before we check prerequisites.
if (component.default.methods === undefined) {
component = await component.default();
}
// Intercept all navigation if a submitted order exists in storage
if (useMainStore().hasSubmittedOrder()) {
if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
return await GoToConfirmationPage(next, useMainStore().getSubmittedOrder());
}
}
// If the saved session has timed out, clear the session, execute 404 logic.
if (getISSCookie() !== null && !isSavedSessionStillActive()) {
// await baseMixin.methods.dispatchStoreAction(storeActions.RESET_STATE);
if (!arePagePrerequisitesValid(component)) {
await GoToStartOn404(next);
}
// Process ISS cookie.
// Skip if Entry Page or Refreshing Welcome page
if (issPageToUse !== issPageValues.ENTRY_PAGE
&& !(issPageToUse === issPageValues.WELCOME_PAGE && fromQueryPage === undefined)) {
updateOrCreateISSCookie();
}
if (router.hasRoute(issPageToUse)) {
// Since our route is already in scope, we can grab the component and call the arePagePrerequisitesValid function.
let component = router.getRoutes().filter((x) => x.name === issPageToUse)[0].components;
// If the component hasn't been loaded fully, load it before we check prerequisites.
if (component.default.methods === undefined) {
component = await component.default();
}
if (!arePagePrerequisitesValid(component)) {
await GoToStartOn404(next);
}
return next({ name: issPageToUse, query: to.query, params: to.params });
}
const routeData = await GetRouteInfoFromPageName(issPageToUse);
if (routeData[0].name.toLowerCase() === 'error') {
throw new Error('Page not found!');
}
// Add our dynamic route.
router.addRoute({
path: routeData[0].path, // Always the same path, because we control it with query strings.
name: routeData[0].name,
component: routeData[0].component
});
// Call the next components arePagePrerequisitesValid method before load.
// If it returns false, use the 404 logic.
const nextComponent = await router
.getRoutes()
.filter((x) => x.name === routeData[0].name)[0]
.components.default();
if (!arePagePrerequisitesValid(nextComponent)) {
const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.';
const tempMsgHeadline = `${issPageToUse}: pre-req failed...`;
await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline);
}
// Assign current query string parameters, as well as our issPage one.
next({
name: routeData[0].name,
query: Object.assign(to.query, { issPage: routeData[0].name }),
params: to.params
});
} catch (error) {
window.console.warn(error);
await GoToStartOn404(next);
return next({name: issPageToUse, query: to.query, params: to.params});
}
const routeData = await GetRouteInfoFromPageName(issPageToUse);
if (routeData[0].name.toLowerCase() === 'error') {
throw new Error('Page not found!');
}
// Add our dynamic route.
router.addRoute({
path: routeData[0].path, // Always the same path, because we control it with query strings.
name: routeData[0].name,
component: routeData[0].component
});
// Call the next components arePagePrerequisitesValid method before load.
// If it returns false, use the 404 logic.
const nextComponent = await router
.getRoutes()
.filter((x) => x.name === routeData[0].name)[0]
.components.default();
if (!arePagePrerequisitesValid(nextComponent)) {
const tempMsgCopy = 'Pre Requisites failed, need to handle. Default is Welcome Page.';
const tempMsgHeadline = `${issPageToUse}: pre-req failed...`;
await GoToStartOn404(next, tempMsgCopy, tempMsgHeadline);
}
// Assign current query string parameters, as well as our issPage one.
next({
name: routeData[0].name,
query: Object.assign(to.query, {issPage: routeData[0].name}),
params: to.params
});
return null;
}
}
@ -177,7 +172,7 @@ router.afterEach(async (to, from) => {
const skipSaveSession = !!router.options.history.state[routerParams.SKIP_SAVE_SESSION];
if (from.name !== undefined && !skipSaveSession && !store.hasSubmittedOrder()) {
await saveSession({});
await saveSession({ bailoutOnError: from.name === issPageValues.ENTRY_PAGE});
}
if (to.query.issPage !== issPageValues.ENTRY_PAGE) {
@ -350,17 +345,12 @@ router.navigateBailout = (bailoutData = null) => {
// Get navigation map depending on the scenario and the current 'page' you're on.
function getNavigationMap(scenario, currentRoute) {
const issPageValue = currentRoute.query.issPage;
try {
const matchedQueryValue = routingTable(useMainStore())
.filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0);
const matchedQueryValue = routingTable(useMainStore())
.filter((item) => item.issPageValue === issPageValue && item.maps.filter((map) => map.scenario === scenario).length > 0);
const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined;
const maps = matchedQueryValue ? matchedQueryValue.map((m) => m.maps.filter((map) => map.scenario === scenario))[0] : undefined;
return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined;
} catch (e) {
window.console.error(e);
return undefined;
}
return maps ? maps.filter((x) => x.filter === true || x.filter === undefined)[0] : undefined;
}
async function GoToAccessIsDenied(next) {

View file

@ -12,7 +12,6 @@ const navigationScenarios = Object.freeze({
CLICKED_FORWARD_POLICY_UNVERIFIED: 'CLICKED_FORWARD_POLICY_UNVERIFIED',
CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES: 'CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES',
CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES: 'CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES',
SAVE_SESSION_FAILED: 'SAVE_SESSION_FAILED',
// Duplicate Check
CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE: 'CLICKED_FORWARD_LOADED_DUPLICATE_WITH_POLICY_VEHICLE',
@ -70,11 +69,8 @@ const navigationScenarios = Object.freeze({
// Coverage Statement
CLICKED_BACK_WITH_REPAIR: 'CLICKED_BACK_WITH_REPAIR',
CLICKED_FORWARD_WITH_INVALID_STATE: 'CLICKED_FORWARD_WITH_INVALID_STATE',
PRICING_LOOKUP_ERROR: 'PRICING_LOOKUP_ERROR',
// TPA Search
CLICKED_NEED_HELP: 'CLICKED_NEED_HELP',
CLICKED_FORWARD_WITH_SAFELITE_SHOP: 'CLICKED_FORWARD_WITH_SAFELITE_SHOP',
CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP: 'CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP',
@ -88,7 +84,6 @@ const navigationScenarios = Object.freeze({
// Provider Preference
CLICKED_FORWARD_WITH_SAFELITE: 'CLICKED_FORWARD_WITH_SAFELITE',
CLICKED_FORWARD_WITH_TPA_ENABLED: 'CLICKED_FORWARD_WITH_TPA_ENABLED',
CLICKED_FORWARD_WITH_TPA_DISABLED: 'CLICKED_FORWARD_WITH_TPA_DISABLED',
CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES: 'CLICKED_FORWARD_WITH_POLICY_AND_VEHICLES',
// Service Package
@ -112,8 +107,6 @@ const navigationScenarios = Object.freeze({
PAY_IN_ADVANCE_SUCCESS: 'PAY_IN_ADVANCE_SUCCESS',
// Bailout
CLICKED_FORWARD_WITH_BAILOUT: 'CLICKED_FORWARD_WITH_BAILOUT',
CLICKED_NEED_HELP_WITH_BAILOUT: 'CLICKED_NEED_HELP_WITH_BAILOUT',
BAILOUT: 'BAILOUT'
});

View file

@ -14,10 +14,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.POLICY_HOLDER_DETAILS
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -55,10 +51,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD_WITHOUT_VIN,
destinationIssPageValue: issPageValues.VEHICLE_LOOKUP
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -141,14 +133,6 @@ const routingTable = () => [
{
scenario: issPageValues.VEHICLE_LOOKUP,
destinationIssPageValue: issPageValues.VEHICLE_LOOKUP
},
{
scenario: issPageValues.BAILOUT_PAGE, // TODO is this a bug
destinationIssPageValue: issPageValues.BAILOUT_PAGE
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -215,10 +199,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
destinationIssPageValue: issPageValues.COVERAGE_STATEMENT
},
{
scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -256,10 +236,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
destinationIssPageValue: issPageValues.COVERAGE_STATEMENT
},
{
scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -293,10 +269,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NO_MORE_QUESTIONS,
destinationIssPageValue: issPageValues.COVERAGE_STATEMENT
},
{
scenario: navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -455,14 +427,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES,
destinationIssPageValue: issPageValues.POLICY_VEHICLES
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
},
{
scenario: navigationScenarios.SAVE_SESSION_FAILED,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -539,10 +503,6 @@ const routingTable = () => [
scenario: navigationScenarios.CLICKED_FORWARD_WITH_CAR_ID_NOT_FOUND,
destinationIssPageValue: issPageValues.VIN_LOOKUP
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_BAILOUT,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_ENDORSEMENTS,
destinationIssPageValue: issPageValues.POLICY_ENDORSEMENTS
@ -591,18 +551,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE,
destinationIssPageValue: issPageValues.SCHEDULE_PAGE
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
},
{
scenario: navigationScenarios.PRICING_LOOKUP_ERROR,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -620,10 +568,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED,
destinationIssPageValue: issPageValues.TPA_SEARCH
},
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -692,10 +636,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_PAY_NOW,
destinationIssPageValue: issPageValues.PAYMENT_PAGE
},
{
scenario: navigationScenarios.SAVE_SESSION_FAILED,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -768,10 +708,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD,
destinationIssPageValue: issPageValues.TPA_CONFIRMATION
},
{
scenario: navigationScenarios.SAVE_SESSION_FAILED,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},
@ -798,10 +734,6 @@ const routingTable = () => [
{
scenario: navigationScenarios.CLICKED_FORWARD_WITH_NON_SAFELITE_SHOP,
destinationIssPageValue: issPageValues.TPA_SUBMIT
},
{
scenario: navigationScenarios.CLICKED_NEED_HELP,
destinationIssPageValue: issPageValues.BAILOUT_PAGE
}
]
},

View file

@ -4,6 +4,8 @@ import issPageValues from '@/router/router-constants/issPage-values';
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from '@/App.vue';
import bailoutMessage from '@/constants/bailoutMessage';
import { useMainStore } from '@/store';
describe('Router', () => {
beforeAll(() => {
@ -37,18 +39,15 @@ describe('Router', () => {
expect(router.push.mock.calls[0][0].state).toBe(parameters);
});
it('Should route from CAPABILITY_QUESTIONS to BAILOUT_PAGE on CLICKED_NEED_HELP_WITH_BAILOUT', () => {
const scenario = navigationScenarios.CLICKED_NEED_HELP_WITH_BAILOUT;
const currentRoute = { query: { issPage: issPageValues.CAPABILITY_QUESTIONS } };
it('Should set bailout and navigate to bailout page when calling navigateBailout', () => {
// Arrange
router.push = jest.fn();
// Act
router.navigate(scenario, currentRoute);
router.navigateBailout(bailoutMessage.unknown({}));
// Assert
expect(router.push).toHaveBeenCalled();
expect(router.push.mock.calls[0][0].query.issPage).toBe(issPageValues.BAILOUT_PAGE);
expect(useMainStore().isBailout).toBeTruthy()
});
});

View file

@ -1,7 +1,6 @@
import { defineStore } from 'pinia';
import applicationConfig from '@/constants/application-config';
import bailoutCode from '@/constants/bailoutCode';
import bailoutMessage from '@/constants/bailoutMessage';
import coverageStatuses from '@/constants/coverage-statuses';
import coverageType from '@/constants/coverage-type';
import damageLocationsSelected from '@/constants/damage-locations-selected';
@ -22,11 +21,7 @@ import {
noCoverageForSelectedVehicle,
repairWaivedForSelectedVehicle
} from '@/helpers/policy-vehicle-helper';
import {
buildURLSearchParams,
getPartNumbersListForQueryString,
getTaxLineItemQueryString
} from '@/helpers/querystring-helper';
import { buildURLSearchParams, getPartNumbersListForQueryString } from '@/helpers/querystring-helper';
import { getTopLevelGlassPartsWithRecal } from '@/helpers/recal-helper';
import { getDateForSavedSessionTimeout } from '@/helpers/session-helper';
import issPageValues from '@/router/router-constants/issPage-values';
@ -534,20 +529,11 @@ export const useMainStore = defineStore({
});
},
getIsVinbyAddressPermissible() {
try {
const response = globalMethods.callHttpClient({
method: endpoints.IsVinbyAddressPermissible.method,
endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`,
payload: {}
});
return response;
} catch (responseError) {
return {
error: {
status: responseError.status
}
};
}
return globalMethods.callHttpClient({
method: endpoints.IsVinbyAddressPermissible.method,
endpoint: `${endpoints.IsVinbyAddressPermissible.url}?state=${this.order.customer.address.state}`,
payload: {}
});
},
async getCoveragePolicyInfo() {
const { order, issConfig, applicationUser } = this;
@ -561,49 +547,43 @@ export const useMainStore = defineStore({
this.applicationUser.coverageAttempts += 1;
console.log(`Coverage lookup attempt #${this.applicationUser.coverageAttempts}. Max attempts allowed: 10.`);
try {
const response = await globalMethods.callHttpClient({
method: endpoints.CoveragePolicyInfo.method,
endpoint: endpoints.CoveragePolicyInfo.url,
payload: {
accountNumber: order.parentAccountNumber?.toString(),
policyNumber: policy.policyNumber,
dateOfLoss: policy.dateOfLoss,
zipCode: policy.policyZipCode,
referralCorrelationId: order.referralCorrelationId
}
});
const responsePolicy = response?.data?.policies?.[0];
if (responsePolicy) {
this.updateCoverageType(coverageType.Deductible);
const insured = responsePolicy.insureds?.[0];
// populate parent account number
if (response.data.accountNumber) {
order.parentAccountNumber = parseInt(response.data.accountNumber, 10);
}
// populate policy holder details from policy lookup
order.customer.address.streetAddress = insured?.address;
order.customer.address.city = insured?.city;
order.customer.address.state = insured?.state;
order.customer.address.zipCode = insured?.zipCode?.toString();
order.customer.firstName = insured?.firstName;
order.customer.lastName = insured?.lastName;
// populate additional fields
order.policy.policyData = responsePolicy.policyData;
// populate vehicles
order.policy.vehicles = responsePolicy.vehicles ?? [];
} else {
this.updateCoverageType(coverageType.NONE);
const response = await globalMethods.callHttpClient({
method: endpoints.CoveragePolicyInfo.method,
endpoint: endpoints.CoveragePolicyInfo.url,
payload: {
accountNumber: order.parentAccountNumber?.toString(),
policyNumber: policy.policyNumber,
dateOfLoss: policy.dateOfLoss,
zipCode: policy.policyZipCode,
referralCorrelationId: order.referralCorrelationId
}
return Promise.resolve();
} catch (e) {
});
const responsePolicy = response?.data?.policies?.[0];
if (responsePolicy) {
this.updateCoverageType(coverageType.Deductible);
const insured = responsePolicy.insureds?.[0];
// populate parent account number
if (response.data.accountNumber) {
order.parentAccountNumber = parseInt(response.data.accountNumber, 10);
}
// populate policy holder details from policy lookup
order.customer.address.streetAddress = insured?.address;
order.customer.address.city = insured?.city;
order.customer.address.state = insured?.state;
order.customer.address.zipCode = insured?.zipCode?.toString();
order.customer.firstName = insured?.firstName;
order.customer.lastName = insured?.lastName;
// populate additional fields
order.policy.policyData = responsePolicy.policyData;
// populate vehicles
order.policy.vehicles = responsePolicy.vehicles ?? [];
} else {
this.updateCoverageType(coverageType.NONE);
return Promise.reject(e);
}
},
clearDuplicateOrders() {
@ -1360,7 +1340,8 @@ export const useMainStore = defineStore({
endpoint: endpoints.LookupVehicleByVin.url,
payload: {
vin
}
},
bailoutOnError: false
});
},
@ -1392,7 +1373,7 @@ export const useMainStore = defineStore({
this.applicationUser.crmCustomerId = response.crmCustomerId.toString();
},
saveSession({ submitAfterSave, createWorkOrderNumberForPIA }) {
saveSession({ submitAfterSave, createWorkOrderNumberForPIA, bailoutOnError }) {
const { vehicle, damage, policy, customer, contactInfo, payment,
lineItems, serviceLocation, schedule, insuranceCoverage } = this.order;
@ -1544,7 +1525,7 @@ export const useMainStore = defineStore({
method: endpoints.SaveSession.method,
endpoint: endpoints.SaveSession.url,
payload,
bailoutOnError: false
bailoutOnError
}).then((response) => {
if (loadedFromDupeCheck) {
this.order.loadedSessionClearedPreviousData = true;
@ -2461,6 +2442,7 @@ export const useMainStore = defineStore({
populateInitialState(forceReset) {
if (!sessionStorage.getItem(storeId) || forceReset) {
this.$state = getDefaultState();
sessionStorage.removeItem(webStorageConstants.SUBMITTED_ORDER);
}
},
@ -2699,34 +2681,26 @@ export const useMainStore = defineStore({
async getBillToInfo(componentProviderNumber = null) {
const { order, issConfig } = this;
try {
const params = new URLSearchParams({
parentAccountNumber: order.parentAccountNumber.toString(),
providerNumber: componentProviderNumber || this.providerNumber,
typeOfClaim: 'GLASS ONLY',
lineOfBusiness: 'PERSONAL',
isItac: this.isITAC
});
const params = new URLSearchParams({
parentAccountNumber: order.parentAccountNumber.toString(),
providerNumber: componentProviderNumber || this.providerNumber,
typeOfClaim: 'GLASS ONLY',
lineOfBusiness: 'PERSONAL',
isItac: this.isITAC
});
const response = await globalMethods.callHttpClient({
method: endpoints.GetBillToInfo.method,
endpoint: `${endpoints.GetBillToInfo.url}?${params.toString()}`
});
const response = await globalMethods.callHttpClient({
method: endpoints.GetBillToInfo.method,
endpoint: `${endpoints.GetBillToInfo.url}?${params.toString()}`,
bailoutOnError: false
});
const billToInfo = response.data;
if (billToInfo != null) {
issConfig.billToAccountNumber = billToInfo.toString();
return Promise.resolve();
}
return Promise.reject(new Error('Invalid billToInfo'));
} catch (e) {
return Promise.reject(e);
}
const billToInfo = response.data;
issConfig.billToAccountNumber = billToInfo.toString();
},
async validateClientTag(clientTag) {
return await globalMethods.callHttpClient({
return globalMethods.callHttpClient({
method: endpoints.ValidateClientTag.method,
endpoint: `${endpoints.ValidateClientTag.url}/${clientTag}`
});