diff --git a/src/constants/coverage-statuses.js b/src/constants/coverage-statuses.js index 5f794cc9..4142121a 100644 --- a/src/constants/coverage-statuses.js +++ b/src/constants/coverage-statuses.js @@ -1,7 +1,7 @@ const coverageStatuses = Object.freeze({ - PENDING: 'Pending', - NO_COMP: 'No Comp', - VERIFIED: 'Verified' + PENDING: 0, + NO_COMP: 1, + VERIFIED: 2 }); export default coverageStatuses; diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index 955ccb08..a213e2f4 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -129,6 +129,10 @@ const endpoints = Object.freeze({ RegisterClaim: { url: '/coverage/api/v1/coverage/register-claim', method: 'POST' + }, + SaveSession: { + url: '/order/api/v1/order/save-session/iss', + method: 'POST' } }); diff --git a/src/helpers/cookie-helper.js b/src/helpers/cookie-helper.js index 5b858cfa..18546702 100644 --- a/src/helpers/cookie-helper.js +++ b/src/helpers/cookie-helper.js @@ -125,7 +125,8 @@ export function updateOrCreateISSCookie() { ReferralNumber: store.order.referralNumber, ReferralDate: store.order.referralDate, ReferralCorrelationId: store.order.referralCorrelationId, - ReferralParentAccountNumber: store.order.accountNumber + ReferralParentAccountNumber: store.order.accountNumber, + SavedSessionId: store.applicationUser.savedSessionId }); } diff --git a/src/helpers/order-helper.js b/src/helpers/order-helper.js new file mode 100644 index 00000000..0b89d8e5 --- /dev/null +++ b/src/helpers/order-helper.js @@ -0,0 +1,29 @@ +import { useMainStore } from '@/store'; +import { updateOrCreateISSCookie } from '@/helpers/cookie-helper'; + +/* + Will call API to save existing order, or create new one depending where it's called from. + 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 }) { + const store = useMainStore(); + var saveSessionPromise = store.applicationUser.saveSessionPromise + ? store.applicationUser.saveSessionPromise.then(() => { return saveSessionHelper(store); }) + : saveSessionHelper(store); + + store.setSaveSessionPromise(saveSessionPromise); + + if (!store.applicationUser.savedSessionId || shouldAwaitSaveSessionQueue) { + await saveSessionPromise; + } +} + +/* + Encapsulates asynchronous Save Session logic inside a promise to allow for Save Session queuing +*/ +async function saveSessionHelper(store) { + const savedSessionInfo = await store.saveSession(); + store.setSaveSessionInfo(savedSessionInfo.data); + updateOrCreateISSCookie(); +} \ No newline at end of file diff --git a/src/layouts/coverage-statement/coverage-statement.spec.js b/src/layouts/coverage-statement/coverage-statement.spec.js index 389444f5..857d1dcb 100644 --- a/src/layouts/coverage-statement/coverage-statement.spec.js +++ b/src/layouts/coverage-statement/coverage-statement.spec.js @@ -9,6 +9,7 @@ import navigationScenarios from '@/router/router-constants/navigation-scenarios. import { getRandomString, getRandomInt } from '@/helpers/data-generation.js'; import settleAllPromises from '@/helpers/layout-helper.js'; import { fetchCmsContentForPage } from '@/helpers/cms-content-helper'; +import routerParams from '@/router/router-constants/router-params'; jest.mock('@/helpers/layout-helper.js', () => jest.fn()); @@ -457,7 +458,11 @@ describe('coverageStatement.vue', () => { // Assert expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined); + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD, + undefined, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true }); }); test('If Verified Deductible, navigate forward with CLICKED_FORWARD scenario', () => { // Arrange @@ -493,7 +498,11 @@ describe('coverageStatement.vue', () => { // Assert expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD, undefined); + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD, + undefined, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true }); }); test('If Verified ITAC and selected Safelite, navigate forward with CLICKED_FORWARD_WITH_SAFELITE scenario', () => { // Arrange @@ -530,7 +539,11 @@ describe('coverageStatement.vue', () => { // Assert expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, undefined); + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, + undefined, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true }); }); test('If Verified ITAC, selected other shop, and TPA enabled, navigate forward w/ CLICKED_FORWARD_WITH_TPA_ENABLED', () => { // Arrange @@ -570,7 +583,11 @@ describe('coverageStatement.vue', () => { // Assert expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED, undefined); + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED, + undefined, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true }); }); test('If Verified ITAC, selected other shop, and TPA disabled, navigate forward w/ CLICKED_FORWARD_WITH_TPA_DISABLED', () => { // Arrange @@ -610,7 +627,11 @@ describe('coverageStatement.vue', () => { // Assert expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, undefined); + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, + undefined, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true }); }); test('If No Comp and selected Safelite, navigate forward with CLICKED_FORWARD scenario', () => { // Arrange @@ -636,7 +657,11 @@ describe('coverageStatement.vue', () => { // Assert expect(wrapper.vm.$router.navigate) - .toHaveBeenCalledWith(navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, undefined); + .toHaveBeenCalledWith( + navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, + undefined, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true }); }); }); describe('ADAS', () => { diff --git a/src/layouts/coverage-statement/coverage-statement.vue b/src/layouts/coverage-statement/coverage-statement.vue index f27b2208..ccd03999 100644 --- a/src/layouts/coverage-statement/coverage-statement.vue +++ b/src/layouts/coverage-statement/coverage-statement.vue @@ -120,6 +120,7 @@ import vehicleQuestionsMixin from '@/mixins/vehicle-questions-mixin.js'; import globalRules from '@/constants/global-rules.js'; import baseFormMixin from '@/mixins/base-form-mixin.js'; import navigationScenarios from '@/router/router-constants/navigation-scenarios.js'; +import routerParams from '@/router/router-constants/router-params'; export default { name: 'coverage-statement', @@ -337,29 +338,39 @@ export default { if (this.unverified || this.verifiedDeductible) { this.$router.navigate( navigationScenarios.CLICKED_FORWARD, - this.$route + this.$route, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); } else if (this.verifiedITAC || this.verifiedNoComp) { if (this.selectedProvider === 'Safelite') { this.$router.navigate( navigationScenarios.CLICKED_FORWARD_WITH_SAFELITE, - this.$route + this.$route, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); } else if (useMainStore().issConfig.enableTPAFlow) { this.$router.navigate( navigationScenarios.CLICKED_FORWARD_WITH_TPA_ENABLED, - this.$route + this.$route, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); } else { this.$router.navigate( navigationScenarios.CLICKED_FORWARD_WITH_TPA_DISABLED, - this.$route + this.$route, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); } } else { this.$router.navigate( navigationScenarios.CLICKED_FORWARD_WITH_INVALID_STATE, - this.$route + this.$route, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); } }, diff --git a/src/layouts/welcome-page/welcome-page.spec.js b/src/layouts/welcome-page/welcome-page.spec.js index ac4e132e..697451c8 100644 --- a/src/layouts/welcome-page/welcome-page.spec.js +++ b/src/layouts/welcome-page/welcome-page.spec.js @@ -10,6 +10,7 @@ import { getMountOptions } from '@/helpers/unit-test-helper.js'; import applicationConfig from '@/constants/application-config'; import { useMainStore } from '@/store'; import navigationScenarios from '@/router/router-constants/navigation-scenarios'; +import routerParams from '@/router/router-constants/router-params'; // Mock our module for promises. jest.mock('@/helpers/layout-helper.js', () => jest.fn()); @@ -182,7 +183,7 @@ describe('navigation', () => { navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES, undefined, {}, - {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true }, mockvehicles ); }); @@ -208,7 +209,9 @@ describe('navigation', () => { // Assert expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES, - undefined + undefined, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); }); test('if policy is not found, navigate to policy-holder-details page', async () => { @@ -228,7 +231,9 @@ describe('navigation', () => { // Assert expect(wrapper.vm.$router.navigate).toHaveBeenCalledWith( navigationScenarios.CLICKED_FORWARD_POLICY_UNVERIFIED, - undefined + undefined, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); }); }); diff --git a/src/layouts/welcome-page/welcome-page.vue b/src/layouts/welcome-page/welcome-page.vue index e9943ba6..c1964c7f 100644 --- a/src/layouts/welcome-page/welcome-page.vue +++ b/src/layouts/welcome-page/welcome-page.vue @@ -184,6 +184,7 @@ import BaseFormMixin from '@/mixins/base-form-mixin.js'; import { useMainStore } from '@/store'; import states from '@/constants/states'; import globalRules from '@/constants/global-rules'; +import routerParams from '@/router/router-constants/router-params'; // define validation rules defineRule('loss-date-required', required(errorMessages.LOSS_DATE_REQUIRED)); @@ -351,7 +352,7 @@ export default { this.navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES, this.$route, {}, - {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true }, this.vehiclesFound ); } else { @@ -359,14 +360,18 @@ export default { // navigate to vehicle-selection page (manual entry) this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_NO_VEHICLES, - this.$route + this.$route, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); } } else { // if policy lookup is unsuccessful, navigate to policy-holder-details page this.$router.navigate( this.navigationScenarios.CLICKED_FORWARD_POLICY_UNVERIFIED, - this.$route + this.$route, + {}, + { [routerParams.SAVE_SESSION_SYNCHRONOUS]: true } ); } }, diff --git a/src/router/index.js b/src/router/index.js index b500aab0..30539023 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -14,6 +14,8 @@ import applicationConfig from '@/constants/application-config'; import analyticsMixin from '@/mixins/analytics-mixin'; import navigationScenarios from './router-constants/navigation-scenarios'; +import { saveSession } from "@/helpers/order-helper.js"; +import routerParams from '@/router/router-constants/router-params'; const routes = [ { @@ -116,11 +118,25 @@ const router = createRouter({ } }); -router.afterEach((to) => { +router.afterEach(async (to, from) => { + /*eslint-disable-line*/ const store = useMainStore(); // Update lastPageVisited in the store store.updateLastPageVisited(to.name); + + if (from.redirectedFrom === undefined){ + store.clearSaveSessionPromise(); + } + const saveSessionSynchronous = !!from.params[routerParams.SAVE_SESSION_SYNCHRONOUS]; + await saveSession({shouldAwaitSaveSessionQueue: saveSessionSynchronous}).catch((error) => { + if (from.name === issPageValues.WELCOME_PAGE) { + router.navigate( + navigationScenarios.SAVE_SESSION_FAILED, + {query: {issPage: issPageValues.WELCOME_PAGE}}); + } + }); + if (to.query.issPage !== issPageValues.ENTRY_PAGE) { // Push page view to GA analyticsMixin.methods.pushPageViewToGA(); diff --git a/src/router/router-constants/navigation-scenarios.js b/src/router/router-constants/navigation-scenarios.js index f732786a..38b59ba9 100644 --- a/src/router/router-constants/navigation-scenarios.js +++ b/src/router/router-constants/navigation-scenarios.js @@ -11,6 +11,7 @@ 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', // YMMS SELECTED_YEAR: 'SELECTED_YEAR', diff --git a/src/router/router-constants/router-params.js b/src/router/router-constants/router-params.js index 37b1300d..2465f965 100644 --- a/src/router/router-constants/router-params.js +++ b/src/router/router-constants/router-params.js @@ -1,5 +1,6 @@ const routerParams = Object.freeze({ - DISPLAY_VEHICLE_CHANGE_ALERT: 'displayVehicleChangeAlert' + DISPLAY_VEHICLE_CHANGE_ALERT: 'displayVehicleChangeAlert', + SAVE_SESSION_SYNCHRONOUS: 'saveSessionSynchronous' }); export default routerParams; diff --git a/src/router/router-constants/routing-table.js b/src/router/router-constants/routing-table.js index c3fbe633..0d999da5 100644 --- a/src/router/router-constants/routing-table.js +++ b/src/router/router-constants/routing-table.js @@ -363,6 +363,10 @@ const routingTable = () => [ { scenario: navigationScenarios.CLICKED_FORWARD_POLICY_VERIFIED_WITH_VEHICLES, destinationIssPageValue: issPageValues.POLICY_VEHICLES + }, + { + scenario: navigationScenarios.SAVE_SESSION_FAILED, + destinationIssPageValue: issPageValues.BAILOUT_PAGE } ] }, diff --git a/src/store/index.js b/src/store/index.js index 01d36851..a2bfbcd7 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -87,14 +87,12 @@ const getDefaultState = () => ({ vaps: null }, payment: { - isInsurance: true, + isInsurance: true, // TODO delete; irrelevant to ISS insuranceCoverage: { isVerified: false, coverageStatus: coverageStatuses.PENDING } }, - referralNumber: null, - referralDate: null, contactInfo: { firstName: null, lastName: null, @@ -102,7 +100,22 @@ const getDefaultState = () => ({ phoneNumber: null, requestTextUpdates: false, notesForTechnician: '' - } + }, + schedule: { + date: null, + startTime: null, + endTime: null, + routeCode: null, + jobMaxMinutes: null + }, + referralNumber: null, + referralDate: null, + referralCorrelationId: '00000000-0000-0000-0000-000000000000', + referralSequenceNumber: null, + eon: null, + workOrderNumber: null, + originalDeductible: null, + currentDeductible: null }, applicationUser: { experiments: [], @@ -335,8 +348,6 @@ export const useMainStore = defineStore({ } }, getCoveragePolicyInfo({ accountNumber, policyNumber, dateOfLoss, zipCode }) { - // TODO: replace place holder correlationId with the real thing - const placeHolderCorrelationId = '00000000-0000-0000-0000-000000000000'; const { policy } = this.order; try { const response = globalMethods.callHttpClient({ @@ -347,7 +358,7 @@ export const useMainStore = defineStore({ policyNumber, dateOfLoss, zipCode, - correlationId: placeHolderCorrelationId + correlationId: this.order.referralCorrelationId } }).then((r) => { const responsePolicy = r.data.policies?.[0]; @@ -368,8 +379,6 @@ export const useMainStore = defineStore({ } }, registerClaim() { - // TODO: replace place holder correlationId with the real thing - const placeHolderCorrelationId = '00000000-0000-0000-0000-000000000000'; const nonNumberCharRegex = /[^0-9]/g; const { order } = this; return new Promise((resolve, reject) => { @@ -378,7 +387,7 @@ export const useMainStore = defineStore({ endpoint: endpoints.RegisterClaim.url, payload: { - correlationId: placeHolderCorrelationId, + correlationId: this.order.referralCorrelationId, accountNumber: this.issConfig.accountNumber?.toString() ?? '', insured: { firstName: this.order.customer.firstName, @@ -682,6 +691,132 @@ export const useMainStore = defineStore({ }); }, + setSaveSessionInfo(response){ + this.order.referralNumber = response.referralNumber; + this.order.referralSequenceNumber = response.referralSequenceNumber; + this.order.referralDate = response.referralDate; + this.order.referralCorrelationId = response.referralCorrelationId; + this.order.eon = response.eon; + this.order.workOrderNumber = response.workOrderNumber; + this.applicationUser.savedSessionId = response.savedSessionId; + this.applicationUser.crmCustomerId = response.crmCustomerId.toString(); + }, + + saveSession() { + const { vehicle, damage, policy, customer, contactInfo, payment, + lineItems, serviceLocation, schedule } = this.order; + const newGlassToReplace = convertGlassPieceNamingForApi(damage.glassToReplace); + + return globalMethods.callHttpClient({ + method: endpoints.SaveSession.method, + endpoint: endpoints.SaveSession.url, + payload: { + applicationUser: { + crmCustomerId: this.applicationUser.crmCustomerId, + experiments: this.applicationUser.experiments, + lastPage: this.applicationUser.lastPageVisited, + pageData: this.applicationUser.pageData, + savedSessionId: this.applicationUser.savedSessionId + }, + vehicle: { + year: vehicle.year, + make: vehicle.make, + model: vehicle.model, + style: vehicle.style, + vin: vehicle.vin, + carId: vehicle.carId, + licensePlateNumber: vehicle.registration?.licensePlate + }, + damage: { + numberOfChips: damage.numberOfChips, + glassToReplace: newGlassToReplace, + isRepair: damage.isRepair, + partQuestionAnswers: damage.partQuestionAnswers, + moldingQuestionAnswers: damage.moldingQuestionAnswers, + capabilityQuestionAnswers: damage.capabilityQuestionAnswers, + dateOfLoss: policy.dateOfLoss, + damageCause: policy.damageCause, + damageState: policy.damageState, + damageCity: policy.damageCity, + isDamageGlassOnly: policy.isDamageGlassOnly + }, + policy: { + policyHolder: { + policyFirstName: customer.firstName, + policyLastName: customer.lastName, + policyPhoneNumber: customer.phoneNumber, + policyEmail: customer.emailAddress + }, + policyNumber: policy.policyNumber, + policyZipCode: policy.policyZipCode, + noCoverage: policy.noCoverage, + policyLookupSuccessful: policy.policyLookupSuccessful, + originalDeductible: this.order.originalDeductible, + currentDeductible: this.order.currentDeductible + }, + customer: { + address: { + streetAddress: customer.address?.streetAddress, + streetAddress2: customer.address?.streetAddress2, + city: customer.address?.city, + state: customer.address?.state, + zipCode: customer.address?.zipCode + }, + emailAddress: contactInfo.emailAddress, + firstName: contactInfo.firstName, + lastName: contactInfo.lastName, + phoneNumber: contactInfo.phoneNumber, + optInSms: contactInfo.requestTextUpdates ?? false + }, + lineItems: { + glassParts: lineItems.glassParts, + supportingItems: lineItems.supportingItems, + vaps: lineItems.vaps + }, + payment: { + InsuranceCoverage: { + isVerified: payment.insuranceCoverage?.isVerified ?? false, + coverageStatus: payment.insuranceCoverage?.coverageStatus + }, + isInsurance: payment.isInsurance ?? true, + parentAccountNumber: this.issConfig.accountNumber + }, + serviceLocation: { + address: { + streetAddress: serviceLocation.address, + city: serviceLocation.city, + state: serviceLocation.state, + zipCode: serviceLocation.zipCode, + zipCodeCtu: serviceLocation.zipCodeCtu + }, + techNotes: contactInfo.notesForTechnician + }, + schedule: { + date: schedule.date, + startTime: schedule.startTime, + endTime: schedule.endTime, + routeCode: schedule.routeCode, + jobMaxMinutes: schedule.jobMaxMinutes + }, + referralDate: this.order.referralDate, + referralNumber: this.order.referralNumber?.toString(), + referralCorrelationId: this.order.referralCorrelationId, + referralSequenceNumber: this.order.referralSequenceNumber, + eon: this.order.eon + }, + additionalSuccessEventDataHandler: (response) => + `Email provided: ${customer.emailAddress ? 'true' : 'false'}` + }); + }, + + setSaveSessionPromise(promise){ + this.applicationUser.saveSessionPromise = promise; + }, + + clearSaveSessionPromise(){ + this.applicationUser.saveSessionPromise = null; + }, + saveVehicleDamage(isWindshieldRepair, selectedGlassToReplace, selectedWindshieldChipCount) { const selectedGlassPassedInSorted = selectedGlassToReplace.slice().sort(); const isGlassToReplaceTheSame = this.order.damage.glassToReplace?.length === selectedGlassToReplace.length @@ -765,6 +900,10 @@ export const useMainStore = defineStore({ this.order.policy.deductible.replace = vehicle.deductible; this.order.policy.deductible.repair = vehicle?.repairWaived ?? false ? 0 : vehicle.deductible; + // TODO logic should be more complicated later on + this.order.originalDeductible = parseFloat(vehicle.deductible); + this.order.currentDeductible = parseFloat(vehicle.deductible); + this.resetSupportingItemsState(); this.resetVapsState(); }, diff --git a/src/store/store.spec.js b/src/store/store.spec.js index 2e05b9a4..d03784f5 100644 --- a/src/store/store.spec.js +++ b/src/store/store.spec.js @@ -3,6 +3,7 @@ import { setActivePinia, createPinia } from 'pinia'; import globalMethods from '@/global-methods.js'; import { getRandomString, getRandomGuid, getRandomInt, getRandomBoolean } from '@/helpers/data-generation.js'; import coverageStatuses from '@/constants/coverage-statuses.js'; +import { endpoints } from '@/constants/endpoints'; describe('Store', () => { let store; @@ -480,4 +481,478 @@ describe('Store', () => { expect(store.contactInfo.notesForTechnician).toEqual(''); }); }); + + describe('saveSession method', () => { + describe('successful method call', () => { + it('calls save session api endpoint', () => { + // Arrange + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + method: endpoints.SaveSession.method, + endpoint: endpoints.SaveSession.url + } + )); + }); + it('Returns expected response object', async () => { + // Arrange + var response = { ReferralNumber: getRandomString(6, 6) }; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve(response)); + + // Act + const result = store.saveSession(); + + // Asserts + await expect(result).resolves.toBe(response); + }) + it('calls api with expected application user data', () => { + // Arrange + var applicationUser = { + crmCustomerId: getRandomString(6, 6), + experiments: getRandomString(6, 6), + lastPageVisited: getRandomString(6, 6), + pageData: getRandomString(6, 6), + savedSessionId: getRandomString(6, 6) + }; + store.applicationUser = applicationUser; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + const result = store.saveSession(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + applicationUser: expect.objectContaining({ + crmCustomerId: applicationUser.crmCustomerId, + experiments: applicationUser.experiments, + lastPage: applicationUser.lastPageVisited, + pageData: applicationUser.pageData, + savedSessionId: applicationUser.savedSessionId + }) + }) + }) + ); + }); + it('calls api with expected vehicle', () => { + var vehicle = { + year: getRandomString(6, 6), + make: getRandomString(6, 6), + model: getRandomString(6, 6), + style: getRandomString(6, 6), + carId: getRandomString(6, 6), + vin: getRandomString(6, 6), + registration: { licensePlate: getRandomString(6, 6) } + }; + store.order.vehicle = vehicle; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + vehicle: expect.objectContaining({ + year: vehicle.year, + make: vehicle.make, + model: vehicle.model, + style: vehicle.style, + carId: vehicle.carId, + vin: vehicle.vin, + licensePlateNumber: vehicle.registration.licensePlate + }) + }) + } + )); + }); + it('calls api with expected damage', () => { + // Arrange + var damage = { + isRepair: getRandomString(6, 6), + numberOfChips: getRandomString(6, 6), + glassToReplace: [], + partQuestionAnswers: getRandomString(6, 6), + moldingQuestionAnswers: getRandomString(6, 6), + capabilityQuestionAnswers: getRandomString(6, 6) + }; + var policy = { + dateOfLoss: getRandomString(6, 6), + damageCause: getRandomString(6, 6), + damageState: getRandomString(6, 6), + damageCity: getRandomString(6, 6), + isDamageGlassOnly: getRandomString(6, 6) + }; + store.order.damage = damage; + store.order.policy = policy; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + damage: expect.objectContaining({ + numberOfChips: damage.numberOfChips, + isRepair: damage.isRepair, + partQuestionAnswers: damage.partQuestionAnswers, + moldingQuestionAnswers: damage.moldingQuestionAnswers, + capabilityQuestionAnswers: damage.capabilityQuestionAnswers, + dateOfLoss: policy.dateOfLoss, + damageCause: policy.damageCause, + damageState: policy.damageState, + damageCity: policy.damageCity, + isDamageGlassOnly: policy.isDamageGlassOnly + }) + }) + }) + ); + }); + it('calls api with expected policy', () => { + // Arrange + var customer = { + firstName: getRandomString(6, 6), + lastName: getRandomString(6, 6), + emailAddress: getRandomString(6, 6), + phoneNumber: getRandomString(6, 6) + }; + var policy = { + policyNumber: getRandomString(6, 6), + policyZipCode: getRandomString(6, 6), + policyLookupSuccessful: getRandomString(6, 6), + noCoverage: getRandomString(6, 6) + }; + var originalDeductible = getRandomString(6, 6); + var currentDeductible = getRandomString(6, 6); + store.order.originalDeductible = originalDeductible; + store.order.currentDeductible = currentDeductible; + store.order.customer = customer; + store.order.policy = policy; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + policy: expect.objectContaining({ + policyHolder: expect.objectContaining({ + policyFirstName: customer.firstName, + policyLastName: customer.lastName, + policyPhoneNumber: customer.phoneNumber, + policyEmail: customer.emailAddress + }), + policyNumber: policy.policyNumber, + policyZipCode: policy.policyZipCode, + noCoverage: policy.noCoverage, + policyLookupSuccessful: policy.policyLookupSuccessful, + originalDeductible, + currentDeductible + }) + }) + }) + ); + }); + it('calls api with expected customer', () => { + // Arrange + var customer = { + address: { + streetAddress: getRandomString(6, 6), + streetAddress2: getRandomString(6, 6), + city: getRandomString(6, 6), + state: getRandomString(6, 6), + zipCode: getRandomString(6, 6) + } + }; + var contactInfo = { + firstName: getRandomString(6, 6), + lastName: getRandomString(6, 6), + emailAddress: getRandomString(6, 6), + phoneNumber: getRandomString(6, 6), + requestTextUpdates: getRandomBoolean() + }; + store.order.contactInfo = contactInfo; + store.order.customer = customer; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + customer: expect.objectContaining({ + address: expect.objectContaining({ + streetAddress: customer.address.streetAddress, + streetAddress2: customer.address.streetAddress2, + city: customer.address.city, + state: customer.address.state, + zipCode: customer.address.zipCode + }), + emailAddress: contactInfo.emailAddress, + firstName: contactInfo.firstName, + lastName: contactInfo.lastName, + phoneNumber: contactInfo.phoneNumber, + optInSms: contactInfo.requestTextUpdates + }) + }) + }) + ); + }); + it('calls api with expected lineItems', () => { + // Arrange + var lineItems = { + glassParts: getRandomString(6, 6), + supportingItems: getRandomString(6, 6), + vaps: getRandomString(6, 6) + }; + store.order.lineItems = lineItems; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + lineItems: expect.objectContaining({ + glassParts: lineItems.glassParts, + supportingItems: lineItems.supportingItems, + vaps: lineItems.vaps + }) + }) + }) + ); + }); + it.each([ + [coverageStatuses.PENDING], + [coverageStatuses.NO_COMP], + [coverageStatuses.VERIFIED] + ])('calls api with expected payment', (coverageStatus) => { + // Arrange + const accountNumber = getRandomString(6, 6); + const payment = { + isInsurance: getRandomBoolean(), + insuranceCoverage: { + isVerified: getRandomBoolean(), + coverageStatus + } + }; + store.issConfig.accountNumber = accountNumber; + store.order.payment = payment; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + payment: expect.objectContaining({ + InsuranceCoverage: expect.objectContaining({ + isVerified: payment.insuranceCoverage.isVerified, + coverageStatus: coverageStatus + }), + isInsurance: payment.isInsurance, + parentAccountNumber: accountNumber + }) + }) + }) + ); + }); + it('calls api with expected service location', () => { + // Arrange + var notesForTechnician = getRandomString(6, 6); + var serviceLocation = { + address: getRandomString(6, 6), + city: getRandomString(6, 6), + state: getRandomString(6, 6), + zipCode: getRandomString(6, 6), + zipCodeCtu: getRandomString(6, 6) + }; + store.order.contactInfo.notesForTechnician = notesForTechnician; + store.order.serviceLocation = serviceLocation; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + serviceLocation: expect.objectContaining({ + address: expect.objectContaining({ + streetAddress: serviceLocation.address, + city: serviceLocation.city, + state: serviceLocation.state, + zipCode: serviceLocation.zipCode, + zipCodeCtu: serviceLocation.zipCodeCtu + }), + techNotes: notesForTechnician + }) + }) + }) + ); + }); + it('calls api with expected schedule', () => { + // Arrange + var schedule = { + date: getRandomString(6, 6), + startTime: getRandomString(6, 6), + endTime: getRandomString(6, 6), + routeCode: getRandomString(6, 6), + jobMaxMinutes: getRandomString(6, 6) + }; + store.order.schedule = schedule; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + schedule: expect.objectContaining({ + date: schedule.date, + startTime: schedule.startTime, + endTime: schedule.endTime, + routeCode: schedule.routeCode, + jobMaxMinutes: schedule.jobMaxMinutes + }) + }) + }) + ); + }); + it('calls api with expected referral date', () => { + // Arrange + var referralDate = getRandomString(6, 6); + store.order.referralDate = referralDate; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ referralDate: referralDate }) + }) + ); + }); + it('calls api with expected referral number', () => { + // Arrange + var referralNumber = getRandomString(6, 6); + store.order.referralNumber = referralNumber; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + store.saveSession(); + + // Assert + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ referralNumber: referralNumber }) + } + )); + }); + }); + it('No glassArray => empty list', async () => { + store.damage.glassToReplace = null; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + await store.saveSession(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + damage: expect.objectContaining({ + glassToReplace: [] + }) + }) + }) + ); + }); + it('glassArray empty => empty list', async () => { + store.damage.glassToReplace = []; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + await store.saveSession(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + damage: expect.objectContaining({ + glassToReplace: [] + }) + }) + }) + ); + }); + it('Nonempty glass array => expected glass array sent', async () => { + const location1 = getRandomString(5); + const location2 = getRandomString(5); + const name1 = getRandomString(10); + const name2 = getRandomString(10); + store.damage.glassToReplace = [ + {glassLocation: location1, glassName: name1}, + {glassLocation: location2, glassName: name2} + ], + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.resolve({})); + + // Act + await store.saveSession(); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + payload: expect.objectContaining({ + damage: expect.objectContaining({ + glassToReplace: expect.arrayContaining([ + {location: location1, name: name1}, + {location: location2, name: name2} + ]) + }) + }) + }) + ); + }); + it('api call throws exception', async () => { + expect.assertions(2); + const error = 'this is the error'; + globalMethods.callHttpClient = jest.fn().mockReturnValue(Promise.reject(error)); + + // Act + await store.saveSession().catch((e) => { + expect(e).toEqual(error); + }); + + // Asserts + expect(globalMethods.callHttpClient).toHaveBeenCalledWith(expect.objectContaining( + { + method: endpoints.SaveSession.method, + endpoint: endpoints.SaveSession.url + }) + ); + }); + }); });