Merge pull request #1290 from Safelite/feature/jzimmerman/INSR-10208

INSR-10208: Moved policy lookup to after save session.
This commit is contained in:
Jeremy-Z 2026-07-08 12:01:26 -04:00 committed by GitHub
commit 52b1bb3a28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 218 additions and 148 deletions

View file

@ -22,4 +22,3 @@ npm run test:unit
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View file

@ -13,6 +13,7 @@ import settleAllPromises from '@/helpers/layout-helper.js';
import { fetchCmsContentForPage } from '@/helpers/cms-content-helper';
import coverageStatuses from '@/constants/coverage-statuses';
import coverageType from '@/constants/coverage-type';
import { saveSession } from '@/helpers/order-helper';
// Mock fetchCmsContentForPage
jest.mock('@/helpers/cms-content-helper', () => ({
@ -22,6 +23,9 @@ jest.mock('@/helpers/cms-content-helper', () => ({
// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => jest.fn());
jest.mock('@/helpers/order-helper', () => ({
saveSession: jest.fn().mockResolvedValue({})
}));
function getMountedComponent(mainInitialState = {}, initialData = {}, methodToRunAfterInitializingStore = () => {}) {
const mountOptions = getMountOptions({
@ -278,6 +282,36 @@ describe('duplicateCheck.vue', () => {
});
describe('forwardButtonAction', () => {
beforeEach(() => {
jest.clearAllMocks();
});
test('No selected duplicate => calls saveSession and getCoveragePolicyInfo, not loadSessionFromDuplicate', async () => {
// Arrange
const { wrapper } = getMountedComponent({
applicationUser: {
duplicateOrders: [
{
referralNumber: getRandomString(6, 6),
correlationId: getRandomString(6, 6)
}
]
}
});
wrapper.vm.mainStore.loadSessionFromDuplicate = jest.fn().mockResolvedValue({});
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(saveSession).toHaveBeenCalledTimes(1);
expect(wrapper.vm.mainStore.loadSessionFromDuplicate).not.toHaveBeenCalled();
expect(wrapper.vm.mainStore.getCoveragePolicyInfo).toHaveBeenCalledTimes(1);
expect(saveSession.mock.invocationCallOrder[0]).toBeLessThan(
wrapper.vm.mainStore.getCoveragePolicyInfo.mock.invocationCallOrder[0]
);
});
test('Selected duplicate => load session called', async () => {
// Arrange
const selectedAnswer = getRandomString(6, 6);
@ -296,17 +330,48 @@ describe('duplicateCheck.vue', () => {
}
});
wrapper.setData({ selectedAnswer });
useMainStore().loadSessionFromDuplicate = jest.fn().mockImplementation(() => Promise.resolve({}));
await wrapper.setData({ selectedAnswer });
wrapper.vm.mainStore.loadSessionFromDuplicate = jest.fn().mockResolvedValue({});
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(wrapper.vm.mainStore.loadSessionFromDuplicate).toHaveBeenCalledTimes(1);
expect(saveSession).not.toHaveBeenCalled();
expect(wrapper.vm.mainStore.getCoveragePolicyInfo).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
});
test('Selected new order => load session not called', async () => {
test('Selected NewClaim => calls saveSession, not loadSessionFromDuplicate', async () => {
// Arrange
const { wrapper } = getMountedComponent({
applicationUser: {
duplicateOrders: [
{
vehicleYear: 'YEAR',
vehicleMake: 'make',
vehicleModel: 'MoDel',
responseDate: '2018-03-01T01:12:34',
referralNumber: getRandomString(6, 6),
correlationId: getRandomString(6, 6)
}
]
}
});
await wrapper.setData({ selectedAnswer: 'NewClaim' });
wrapper.vm.mainStore.loadSessionFromDuplicate = jest.fn().mockResolvedValue({});
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(saveSession).toHaveBeenCalledTimes(1);
expect(wrapper.vm.mainStore.loadSessionFromDuplicate).toHaveBeenCalledTimes(0);
expect(wrapper.vm.mainStore.getCoveragePolicyInfo).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
});
test('Unmatched selection => calls saveSession, not loadSessionFromDuplicate', async () => {
// Arrange
const newOrderSelectionName = getRandomString(6, 6);
const { wrapper } = getMountedComponent({
@ -324,49 +389,19 @@ describe('duplicateCheck.vue', () => {
}
});
wrapper.setData({ selectedAnswer: newOrderSelectionName });
useMainStore().loadSessionFromDuplicate = jest.fn().mockImplementation(() => Promise.resolve({}));
await wrapper.setData({ selectedAnswer: newOrderSelectionName });
wrapper.vm.mainStore.loadSessionFromDuplicate = jest.fn().mockResolvedValue({});
// Act
await wrapper.vm.forwardButtonAction();
// Assert
// When selection doesn't match any duplicate, saveSession should be called to create a new referral
expect(saveSession).toHaveBeenCalledTimes(1);
expect(wrapper.vm.mainStore.loadSessionFromDuplicate).toHaveBeenCalledTimes(0);
expect(wrapper.vm.mainStore.getCoveragePolicyInfo).toHaveBeenCalledTimes(1);
expect(wrapper.vm.$router.navigate).toHaveBeenCalledTimes(1);
});
test('Load session throws error => still navigate forward', async () => {
// Arrange
const selectedAnswer = getRandomString(6, 6);
const { wrapper } = getMountedComponent({
applicationUser: {
duplicateOrders: [
{
vehicleYear: 'YEAR',
vehicleMake: 'make',
vehicleModel: 'MoDel',
responseDate: '2018-03-01T01:12:34',
referralNumber: getRandomString(6, 6),
correlationId: selectedAnswer
}
]
}
});
wrapper.setData({ selectedAnswer });
const error = 'load session error';
useMainStore().loadSessionFromDuplicate = jest.fn().mockImplementation(() => Promise.reject(error));
// Act
// Assert
expect.assertions(2);
try {
await wrapper.vm.forwardButtonAction();
} catch (e) {
expect(e).toMatch(error);
}
expect(wrapper.vm.mainStore.loadSessionFromDuplicate).toHaveBeenCalledTimes(1);
});
test('coverageType deductible and policy vehicles returned => CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES', async () => {
// Arrange
const { wrapper } = getMountedComponent({

View file

@ -62,6 +62,8 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js';
import { useMainStore } from '@/store/index.js';
import globalRules from '@/constants/global-rules.js';
import { formatDate, toTitleCase } from '@/helpers/text-helper.js';
import { saveSession } from '@/helpers/order-helper';
import showIssLoadingModal from '@/helpers/loading-modal-helper';
const dupeCheckDateFormatter = new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', month: '2-digit', day: '2-digit', year: 'numeric' });
@ -134,23 +136,75 @@ export default {
* @summary Steps to perform when forward button clicked.
*/
async forwardButtonAction() {
// If user clicked foward without selecting an option or user click on "start a new claim" button.
showIssLoadingModal(true);
let callSaveSession = false;
// If user clicked forward without selecting an option or user click on "start a new claim" button.
if (this.selectedAnswer == null || this.selectedAnswer === 'NewClaim') {
this.navigateForward();
return;
// User did not pick a duplicate, so call SaveSession to create referral.
callSaveSession = true;
}
else
{
const selectedReferral =
this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer);
if (selectedReferral) {
// LoadSession will bailout on error.
try
{
await this.mainStore.loadSessionFromDuplicate(selectedReferral);
}
catch (error)
{
// No error handling here needed. Error is already logged.
// If LoadSession fails, user gets redirected to bailout page. No need to call save session.
// Return here to stop current navigation.
return;
}
}
else {
// If duplicate not found in list (unable to load), then call SaveSession to create referral.
callSaveSession = true;
}
}
const selectedReferral =
this.mainStore.applicationUser.duplicateOrders.find((o) => o.correlationId === this.selectedAnswer);
try
{
// Call SaveSession to create referral.
if ( callSaveSession ) {
// SaveSession will bailout on error.
await saveSession({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true });
}
}
catch (error) {
// No error handling here needed. Error is already logged.
// Return here to stop current navigation.
return;
}
// Call getCoveragePolicyInfo to get the policy info.
// GetCoveragePolicyInfo handles exception / error internally. We do not care if it fails, user continues in unverified path.
await this.mainStore.getCoveragePolicyInfo();
if (selectedReferral) {
await this.mainStore.loadSessionFromDuplicate(selectedReferral);
this.pushEventToGA("policy_search", "policy_found", this.mainStore.isPolicyLookupSuccessful ? "Yes" : "No", true);
if ( this.mainStore.isPolicyLookupSuccessful) {
this.pushEventToGA("zip_validation", "success", "N/A", true);
}
else {
if ( this.mainStore.order.policy.policyLookupErrorCode === 2) {
this.pushEventToGA("zip_validation", "fail", "N/A", true);
}
}
this.navigateForward();
},
navigateForward() {
this.mainStore.updateDuplicateCheckVisited(true);
if (!this.mainStore.isPolicyLookupSuccessful) {
this.$router.navigate(
this.navigationScenarios.CLICKED_FORWARD_POLICY_UNVERIFIED,

View file

@ -12,6 +12,7 @@ import { useMainStore } from '@/store';
import navigationScenarios from '@/router/router-constants/navigation-scenarios';
import routerParams from '@/router/router-constants/router-params';
import coverageType from '@/constants/coverage-type';
import { saveSession } from '@/helpers/order-helper';
// Mock our module for promises.
jest.mock('@/helpers/layout-helper.js', () => jest.fn());
@ -23,6 +24,9 @@ jest.mock('@/helpers/cms-content-helper', () => ({
jest.mock('@/global-methods', () => ({
callHttpClient: jest.fn()
}));
jest.mock('@/helpers/order-helper', () => ({
saveSession: jest.fn().mockResolvedValue({})
}));
/** @ignore */
function setupMocks({
@ -133,6 +137,10 @@ describe('welcome-page.vue', () => {
});
describe('navigation', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('When zip code check fails', () => {
test('Navigation should not happen', async () => {
// Arrange
@ -255,6 +263,51 @@ describe('navigation', () => {
{ [routerParams.SKIP_SAVE_SESSION]: true }
);
});
test('if no duplicates, calls saveSession and getCoveragePolicyInfo', async () => {
// Arrange
const { wrapper } = setupMocks({});
useMainStore().getDuplicateReferrals = jest.fn().mockImplementation(() => Promise.resolve({}));
useMainStore().applicationUser.duplicateOrders = [];
useMainStore().order.loadedFromCookie = false;
useMainStore().order.visitedDuplicateCheckPage = false;
wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.resolve({
data: {
isValid: true
}
}));
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(saveSession).toHaveBeenCalledTimes(1);
expect(wrapper.vm.mainStore.getCoveragePolicyInfo).toHaveBeenCalledTimes(1);
expect(saveSession.mock.invocationCallOrder[0]).toBeLessThan(
wrapper.vm.mainStore.getCoveragePolicyInfo.mock.invocationCallOrder[0]
);
});
test('if duplicates found, does not call saveSession or getCoveragePolicyInfo', async () => {
// Arrange
const { wrapper } = setupMocks({});
useMainStore().getDuplicateReferrals = jest.fn().mockImplementation(() => Promise.resolve({}));
useMainStore().applicationUser.duplicateOrders = [{ test: 'duplicate-order' }];
useMainStore().order.loadedFromCookie = false;
useMainStore().order.visitedDuplicateCheckPage = false;
wrapper.vm.mainStore.validateZip = jest.fn().mockImplementation(() => Promise.resolve({
data: {
isValid: true
}
}));
// Act
await wrapper.vm.forwardButtonAction();
// Assert
expect(saveSession).not.toHaveBeenCalled();
expect(wrapper.vm.mainStore.getCoveragePolicyInfo).not.toHaveBeenCalled();
});
test('if policy is found, but no vehicles and no duplicates, navigate to vehicle-selection page', async () => {
// Arrange
const { wrapper } = setupMocks({});

View file

@ -350,24 +350,41 @@ export default {
this.mainStore.clearDuplicateOrders();
}
const promises = [
this.mainStore.getBillToInfo()
];
await 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);
// getDuplicateReferrals handles exception / error internally. We do not care if it fails, user continues with creating new referral.
await this.mainStore.getDuplicateReferrals();
this.pushEventToGA("policy_search", "policy_found", this.mainStore.isPolicyLookupSuccessful ? "Yes" : "No", true);
// If we do not have any duplicates.
// Call SaveSession to create referral.
// Then call getCoveragePolicyInfo to get the policy info.
if (this.mainStore.applicationUser.duplicateOrders?.length === 0) {
try {
// SaveSession will bailout on error.
await saveSession({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true });
}
catch (error) {
// No error handling here needed. Error is already logged.
// Return here to stop current navigation.
return;
}
if ( this.mainStore.isPolicyLookupSuccessful) {
this.pushEventToGA("zip_validation", "success", "N/A", true);
}
else {
if ( this.mainStore.order.policy.policyLookupErrorCode === 2) {
this.pushEventToGA("zip_validation", "fail", "N/A", true);
// Call getCoveragePolicyInfo to get the policy info.
// GetCoveragePolicyInfo handles exception / error internally. We do not care if it fails, user continues in unverified path.
await this.mainStore.getCoveragePolicyInfo();
this.pushEventToGA("policy_search", "policy_found", this.mainStore.isPolicyLookupSuccessful ? "Yes" : "No", true);
if (this.mainStore.isPolicyLookupSuccessful) {
this.pushEventToGA("zip_validation", "success", "N/A", true);
}
else {
if (this.mainStore.order.policy.policyLookupErrorCode === 2) {
this.pushEventToGA("zip_validation", "fail", "N/A", true);
}
}
}
}

View file

@ -157,23 +157,6 @@ router.beforeEach(async (to, from) => {
showIssLoadingModal(true);
}
const callSaveSession = !store.order?.referralNumber && (to.name === issPageValues.VEHICLE_SELECTION || to.name === issPageValues.POLICY_VEHICLES);
if (callSaveSession) {
// Forcing a synchronous savesession call here to create the referral for the first time.
// AfterEach does not support synchronous calls that block navigation. Which is why this is in the beforeEach method.
// This only needs to be called once in a certain location, all other saveSession calls are async in afterEach (except the last one on order submission)
// NOTE: This call should be creating the referral number. After this point in the site flow, referral number is critical for several pieces of logic and logging.
// Therefore, we want to force a bailout here if it errors out.
showIssLoadingModal(true);
try {
await saveSession({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true });
}
catch (error) {
// Only remove spinner on error. Successfull new page load will remove it by default.
showIssLoadingModal(false);
}
}
const toQueryPage = to.query?.issPage;
const notToPayInAdvanceReturn = toQueryPage !== issPageValues.PAYMENT_RETURN;
const isInIframe = window !== window.top || fromQueryPage === issPageValues.PAYMENT_PAGE;

View file

@ -1,71 +0,0 @@
import issPageValues from '@/router/router-constants/issPage-values';
let router;
let saveSession;
let showIssLoadingModal;
let mockBeforeEachHandler;
let mockStore;
jest.mock('vue-router', () => ({
createWebHistory: jest.fn(() => ({ state: {} })),
createRouter: jest.fn((options) => ({
options,
beforeEach: jest.fn((handler) => {
mockBeforeEachHandler = handler;
}),
afterEach: jest.fn(),
addRoute: jest.fn(),
getRoutes: jest.fn(() => []),
hasRoute: jest.fn(() => true),
push: jest.fn(),
go: jest.fn(),
currentRoute: { value: { query: { issPage: 'welcome-page' } } }
}))
}));
jest.mock('@/helpers/order-helper.js', () => ({
saveSession: jest.fn(() => Promise.resolve())
}));
jest.mock('@/helpers/loading-modal-helper', () => jest.fn());
jest.mock('@/store', () => ({
useMainStore: jest.fn(() => mockStore)
}));
describe('Router beforeEach callSaveSession', () => {
beforeAll(() => {
router = require('@/router/').default;
({ saveSession } = require('@/helpers/order-helper.js'));
const loadingModalModule = require('@/helpers/loading-modal-helper');
showIssLoadingModal = loadingModalModule.default || loadingModalModule;
});
beforeEach(() => {
mockStore = {
order: {},
isBailout: false
};
jest.clearAllMocks();
});
it('calls saveSession synchronously when entering vehicle-selection without a referral number', async () => {
const result = await mockBeforeEachHandler(
{
name: issPageValues.VEHICLE_SELECTION,
query: { issPage: issPageValues.VEHICLE_SELECTION },
href: '/?issPage=vehicle-selection'
},
{
name: issPageValues.WELCOME_PAGE,
query: { issPage: issPageValues.WELCOME_PAGE }
}
);
expect(saveSession).toHaveBeenCalledTimes(1);
expect(saveSession).toHaveBeenCalledWith({ shouldAwaitSaveSessionQueue: true, bailoutOnError: true });
expect(showIssLoadingModal).toHaveBeenNthCalledWith(1, true);
expect(result).toBe(true);
});
});