Merge pull request #3095 from Safelite/feature/page-prereqs-refactor-CASH-2456

Feature/page prereqs refactor cash 2456
This commit is contained in:
matthew-sykes 2026-03-10 09:37:48 -04:00 committed by GitHub
commit bda64fc4b4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 1663 additions and 424 deletions

View file

@ -0,0 +1,281 @@
import { debugLog } from "@/helpers/debug-log-helper.js";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import { coverageStatus } from "@/constants/insurance";
import { applicationConfig } from "@/constants/application-config";
/**
* Logs the start of the page prerequisites check.
* Should be used on pages before the page prerequisites check is executed.
* @param {string} source - The source of the page prerequisites check.
* @param {boolean} preReqResult - The result of the page prerequisites check.
*/
export function logPagePrereqsStart(source, preReqResult) {
// prettier-ignore
{
debugLog(`--- ${source} pagePrereqs start ---`, null, !preReqResult);
}
}
/**
* Logs the end of the page prerequisites check.
* Should be used on pages after the page prerequisites check is executed.
* @param {string} source - The source of the page prerequisites check.
* @param {boolean} preReqResult - The result of the page prerequisites check.
*/
export function logPagePrereqsEnd(source, preReqResult) {
// prettier-ignore
{
debugLog(`--- ${source} pagePrereqs end ---`, null, !preReqResult);
}
}
/**
* Flushes the page prerequisites logs.
* @param {string} source - The source of the page prerequisites check. This is the page name most places.
* @param {boolean} preReqResult - The result of the page prerequisites check.
* @param {Array} logQueue - The queue of log functions.
*/
export function flushPagePrereqsLogs(source, preReqResult, logQueue) {
logPagePrereqsStart(source, preReqResult);
logQueue.forEach((fn) => fn());
logPagePrereqsEnd(source, preReqResult);
}
/**
* Pushes a log function to the log queue.
* @param {Array} logQueue - The queue of log functions.
* @param {Function} logFn - The log function to be executed.
*/
function queueLogging(logQueue, logFn) {
if (logQueue) {
logQueue.push(logFn);
}
}
/**
* Checks whether the service zip code information is valid for the order.
* Should be used on pages after service zip code collection is complete.
* Returns true if the zip code and zip code CTU are present on serviceLocation.
*/
export function hasServiceZipInfo(order, logQueue = null) {
const serviceLocation = order.serviceLocation;
const result = !!(serviceLocation.zipCode && serviceLocation.zipCodeCtu);
queueLogging(logQueue, () => {
// prettier-ignore
{
debugLog("hasServiceZipInfo:", result, !result);
debugLog("serviceLocation.zipCode:", serviceLocation.zipCode, !result);
debugLog("serviceLocation.zipCodeCtu:", serviceLocation.zipCodeCtu, !result);
debugLog("", null, !result);
}
});
return result;
}
/**
* Checks whether the service location information is valid for the appointment.
* Returns true if the required info is present for the chosen appointment type.
* For MOBILE: address, city, state, and zipCode must be present on serviceLocation.
* For INSHOP/DROPOFF: all provider address fields must be present.
*/
export function hasServiceLocationInfo(order, logQueue = null) {
const serviceLocation = order.serviceLocation;
const mobileReqs = !!(
serviceLocation.address &&
serviceLocation.city &&
serviceLocation.state &&
serviceLocation.zipCode
);
const providerLocation = serviceLocation.provider.address;
const dropOffInshopReqs = !!(
providerLocation.streetAddress &&
providerLocation.city &&
providerLocation.state &&
providerLocation.zipCode
);
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
const result = (isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
queueLogging(logQueue, () => {
// prettier-ignore
{
debugLog("hasServiceLocationInfo mobileReqs:", mobileReqs, !result);
debugLog("serviceLocation.address:", serviceLocation.address, !result);
debugLog("serviceLocation.city:", serviceLocation.city, !result);
debugLog("serviceLocation.state:", serviceLocation.state, !result);
debugLog("serviceLocation.zipCode:", serviceLocation.zipCode, !result);
debugLog("dropOffInshopReqs:", dropOffInshopReqs, !result);
debugLog("providerLocation.streetAddress:", providerLocation.streetAddress, !result);
debugLog("providerLocation.city:", providerLocation.city, !result);
debugLog("providerLocation.state:", providerLocation.state, !result);
debugLog("providerLocation.zipCode:", providerLocation.zipCode, !result);
debugLog("", null, !result);
}
});
return result;
}
/**
* Checks whether the insurance information is valid for the order. To be used after all insurance info collection is complete.
* Returns true if cash selected or if insurance is selected and some insurance fields are present.
*/
export function hasInsuranceInfo(order, logQueue = null) {
const isInsurance = order.payment?.isInsurance;
const insuranceCoverageStatus = order.payment?.insuranceCoverage?.coverageStatus;
const currentDeductible = order.policy?.currentDeductible;
const parentAccountNumber = order.payment?.parentAccountNumber;
const policyNumber = order.policy?.policyNumber;
let result = false;
if (isInsurance !== null) {
if (isInsurance === false) {
result = true; // Cash selected - no need for policy number or parent account
} else {
const parentAccountNotCash =
parentAccountNumber !== applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
const hasPolicyNumber = !!policyNumber;
if (!parentAccountNotCash || !hasPolicyNumber) {
result = false;
} else if (
insuranceCoverageStatus === coverageStatus.VERIFIED &&
!(currentDeductible === 0 || currentDeductible > 0)
) {
result = false; // if coverageStatus is verified there must be a valid deductible also
} else {
result = true;
}
}
}
queueLogging(logQueue, () => {
// prettier-ignore
{
debugLog("hasInsuranceInfo:", result, !result);
if (isInsurance === true) {
debugLog("hasInsuranceInfo parentAccountNumber:", parentAccountNumber, !result);
debugLog("hasInsuranceInfo policyNumber:", policyNumber, !result);
}
if (insuranceCoverageStatus === coverageStatus.VERIFIED) {
debugLog("hasInsuranceInfo coverageStatus:", insuranceCoverageStatus, !result);
debugLog("hasInsuranceInfo currentDeductible:", currentDeductible, !result);
}
debugLog("", null, !result);
}
});
return result;
}
/**
* Checks whether the scheduling information is valid for the order.
* Should be used on pages after scheduling info collection is complete.
* Returns true if the required info is present for the chosen appointment type.
* For MOBILE: date, startTime, endTime, jobMaxMinutes, and jobMinMinutes must be present on schedule.
* For INSHOP/DROPOFF: all provider scheduling fields must be present.
*/
export function hasSchedulingInfo(order, logQueue = null) {
const schedule = order.schedule;
const result = !!(
schedule.date &&
schedule.startTime &&
schedule.endTime &&
schedule.jobMaxMinutes &&
schedule.jobMinMinutes
);
queueLogging(logQueue, () => {
// prettier-ignore
{
debugLog("hasSchedulingInfo:", result, !result);
debugLog("schedule.date:", schedule.date, !result);
debugLog("schedule.startTime:", schedule.startTime, !result);
debugLog("schedule.endTime:", schedule.endTime, !result);
debugLog("schedule.jobMaxMinutes:", schedule.jobMaxMinutes, !result);
debugLog("schedule.jobMinMinutes:", schedule.jobMinMinutes, !result);
debugLog("", null, !result);
}
});
return result;
}
/**
* Checks whether the customer information is valid for the order.
* Should be used on pages after customer info collection is complete.
* Returns true if the firstName, lastName, phoneNumber, and emailAddress are present on customer.
*/
export function hasCustomerInfo(order, logQueue = null) {
const customer = order.customer;
const result = !!(
customer.firstName &&
customer.lastName &&
customer.phoneNumber &&
customer.emailAddress
);
queueLogging(logQueue, () => {
// prettier-ignore
{
debugLog("hasCustomerInfo:", result, !result);
debugLog("customer.firstName:", customer.firstName, !result);
debugLog("customer.lastName:", customer.lastName, !result);
debugLog("customer.phoneNumber:", customer.phoneNumber, !result);
debugLog("customer.emailAddress:", customer.emailAddress, !result);
debugLog("", null, !result);
}
});
return result;
}
/**
* Checks whether the glass parts or repair information is valid for the order.
* Should be used on pages after glass parts or repair info collection is complete.
* Returns true if the isRepair is true or if the glassParts array is not empty.
*/
export function hasGlassPartsOrRepairInfo(order, logQueue = null) {
const isRepair = order.damage?.isRepair;
const glassParts = order.lineItems?.glassParts;
const hasGlassParts = glassParts != null && glassParts.length > 0;
const result = !!isRepair || hasGlassParts;
queueLogging(logQueue, () => {
// prettier-ignore
{
debugLog("hasGlassPartsOrRepairInfo:", result, !result);
debugLog("damage.isRepair:", isRepair, !result);
debugLog("lineItems.glassParts:", glassParts, !result);
debugLog("", null, !result);
}
});
return result;
}
/**
* Checks whether the payment method information is valid for the order.
* Should only be used on pages after payment method info collection is complete which is only the payment collection pages.
* Returns true if the isPia is true or if the piaType is set.
*/
export function hasPaymentMethodInfo(order, logQueue = null) {
const payment = order.payment;
const result = payment.isPia !== null && (payment.isPia || !!payment.piaType);
queueLogging(logQueue, () => {
// prettier-ignore
{
debugLog("hasPaymentMethodInfo:", result, !result);
debugLog("payment.isPia:", payment?.isPia, !result);
debugLog("payment.piaType:", payment?.piaType, !result);
debugLog("", null, !result);
}
});
return result;
}

View file

@ -0,0 +1,716 @@
import * as pagePrereqsHelper from "@/helpers/page-prerequisites-helper";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import { coverageStatus } from "@/constants/insurance";
jest.mock("@/helpers/debug-log-helper.js", () => ({
debugLog: jest.fn(),
}));
const { debugLog } = require("@/helpers/debug-log-helper.js");
describe("page-prerequisites-helper.js", () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe("logPagePrereqsStart", () => {
test("calls debugLog with source and forceLog when preReqResult is false", () => {
pagePrereqsHelper.logPagePrereqsStart("payment.vue", false);
expect(debugLog).toHaveBeenCalledWith(
"--- payment.vue pagePrereqs start ---",
null,
true
);
});
test("calls debugLog with forceLog false when preReqResult is true", () => {
pagePrereqsHelper.logPagePrereqsStart("payment.vue", true);
expect(debugLog).toHaveBeenCalledWith(
"--- payment.vue pagePrereqs start ---",
null,
false
);
});
});
describe("flushPagePrereqsLogs", () => {
test("calls logPagePrereqsStart, executes logQueue, and logPagePrereqsEnd", () => {
const logQueue = [jest.fn(), jest.fn()];
pagePrereqsHelper.flushPagePrereqsLogs("payment.vue", true, logQueue);
expect(debugLog).toHaveBeenCalledWith(
"--- payment.vue pagePrereqs start ---",
null,
false
);
expect(debugLog).toHaveBeenCalledWith(
"--- payment.vue pagePrereqs end ---",
null,
false
);
expect(logQueue[0]).toHaveBeenCalled();
expect(logQueue[1]).toHaveBeenCalled();
});
});
describe("logPagePrereqsEnd", () => {
test("calls debugLog with source and forceLog when preReqResult is false", () => {
pagePrereqsHelper.logPagePrereqsEnd("payment-method.vue", false);
expect(debugLog).toHaveBeenCalledWith(
"--- payment-method.vue pagePrereqs end ---",
null,
true
);
});
test("calls debugLog with forceLog false when preReqResult is true", () => {
pagePrereqsHelper.logPagePrereqsEnd("payment-method.vue", true);
expect(debugLog).toHaveBeenCalledWith(
"--- payment-method.vue pagePrereqs end ---",
null,
false
);
});
});
describe("hasServiceZipInfo", () => {
test("returns true when zipCode and zipCodeCtu are set", () => {
const order = {
serviceLocation: { zipCode: "12345", zipCodeCtu: "12345" },
};
const result = pagePrereqsHelper.hasServiceZipInfo(order);
expect(result).toBe(true);
});
test("returns false when zipCode is missing", () => {
const order = {
serviceLocation: { zipCode: null, zipCodeCtu: "12345" },
};
const result = pagePrereqsHelper.hasServiceZipInfo(order);
expect(result).toBe(false);
});
test("returns false when zipCodeCtu is missing", () => {
const order = {
serviceLocation: { zipCode: "12345", zipCodeCtu: null },
};
const result = pagePrereqsHelper.hasServiceZipInfo(order);
expect(result).toBe(false);
});
test("returns false when both are empty strings", () => {
const order = {
serviceLocation: { zipCode: "", zipCodeCtu: "" },
};
const result = pagePrereqsHelper.hasServiceZipInfo(order);
expect(result).toBe(false);
});
test("does not add to logQueue when logQueue is null", () => {
const order = {
serviceLocation: { zipCode: "12345", zipCodeCtu: "12345" },
};
pagePrereqsHelper.hasServiceZipInfo(order, null);
expect(debugLog).not.toHaveBeenCalled();
});
test("adds log functions to logQueue when logQueue is provided", () => {
const order = {
serviceLocation: { zipCode: "12345", zipCodeCtu: "12345" },
};
const logQueue = [];
pagePrereqsHelper.hasServiceZipInfo(order, logQueue);
expect(logQueue).toHaveLength(1);
logQueue[0]();
expect(debugLog).toHaveBeenCalled();
});
});
describe("hasServiceLocationInfo", () => {
const baseOrder = {
serviceLocation: {
provider: {
address: {
streetAddress: "123 Provider St",
city: "ProviderCity",
state: "OH",
zipCode: "00000",
},
},
},
};
test("returns true for mobile when address, city, state, zipCode are set", () => {
const order = {
...baseOrder,
serviceLocation: {
...baseOrder.serviceLocation,
address: "123 Main St",
city: "Anytown",
state: "OH",
zipCode: "12345",
appointmentType: AppointmentTypeStrings.MOBILE,
provider: baseOrder.serviceLocation.provider,
},
};
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
expect(result).toBe(true);
});
test("returns false for mobile when address is missing", () => {
const order = {
...baseOrder,
serviceLocation: {
...baseOrder.serviceLocation,
address: null,
city: "Anytown",
state: "OH",
zipCode: "12345",
appointmentType: AppointmentTypeStrings.MOBILE,
provider: baseOrder.serviceLocation.provider,
},
};
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
expect(result).toBe(false);
});
test("returns true for drop-off when provider address has all fields", () => {
const order = {
...baseOrder,
serviceLocation: {
...baseOrder.serviceLocation,
appointmentType: AppointmentTypeStrings.DROP_OFF,
},
};
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
expect(result).toBe(true);
});
test("returns false for drop-off when provider streetAddress is missing", () => {
const order = {
...baseOrder,
serviceLocation: {
...baseOrder.serviceLocation,
appointmentType: AppointmentTypeStrings.DROP_OFF,
provider: {
address: {
streetAddress: null,
city: "ProviderCity",
state: "OH",
zipCode: "00000",
},
},
},
};
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
expect(result).toBe(false);
});
test("returns true for in-shop when provider address has all fields", () => {
const order = {
...baseOrder,
serviceLocation: {
...baseOrder.serviceLocation,
appointmentType: AppointmentTypeStrings.IN_SHOP,
},
};
const result = pagePrereqsHelper.hasServiceLocationInfo(order);
expect(result).toBe(true);
});
test("does not add to logQueue when logQueue is null", () => {
const order = {
...baseOrder,
serviceLocation: {
...baseOrder.serviceLocation,
appointmentType: AppointmentTypeStrings.MOBILE,
address: "123",
city: "City",
state: "OH",
zipCode: "12345",
provider: baseOrder.serviceLocation.provider,
},
};
pagePrereqsHelper.hasServiceLocationInfo(order, null);
expect(debugLog).not.toHaveBeenCalled();
});
test("adds log functions to logQueue when logQueue is provided", () => {
const order = {
...baseOrder,
serviceLocation: {
...baseOrder.serviceLocation,
appointmentType: AppointmentTypeStrings.MOBILE,
address: "123",
city: "City",
state: "OH",
zipCode: "12345",
provider: baseOrder.serviceLocation.provider,
},
};
const logQueue = [];
pagePrereqsHelper.hasServiceLocationInfo(order, logQueue);
expect(logQueue).toHaveLength(1);
expect(typeof logQueue[0]).toBe("function");
logQueue[0]();
expect(debugLog).toHaveBeenCalled();
});
});
describe("hasInsuranceInfo", () => {
test("returns false when isInsurance is null", () => {
const order = { payment: { isInsurance: null } };
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(false);
});
test("returns true when isInsurance is false (cash selected)", () => {
const order = {
payment: {
isInsurance: false,
insuranceCoverage: { coverageStatus: coverageStatus.PENDING },
},
};
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(true);
});
test("returns true when isInsurance is true and coverage is not verified", () => {
const order = {
payment: {
isInsurance: true,
insuranceCoverage: { coverageStatus: coverageStatus.PENDING },
parentAccountNumber: 999999,
},
policy: { policyNumber: "POL123" },
};
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(true);
});
test("returns true when isInsurance is true and coverage is verified with valid deductible", () => {
const order = {
payment: {
isInsurance: true,
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
parentAccountNumber: 999999,
},
policy: { policyNumber: "POL123", currentDeductible: 100 },
};
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(true);
});
test("returns true when coverage is verified and deductible is 0", () => {
const order = {
payment: {
isInsurance: true,
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
parentAccountNumber: 999999,
},
policy: { policyNumber: "POL123", currentDeductible: 0 },
};
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(true);
});
test("returns false when isInsurance is true but parentAccountNumber is CASH_PARENT_ACCOUNT_NUMBER", () => {
const order = {
payment: {
isInsurance: true,
insuranceCoverage: { coverageStatus: coverageStatus.PENDING },
parentAccountNumber: 167132,
},
policy: { policyNumber: "POL123" },
};
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(false);
});
test("returns false when isInsurance is true but policyNumber is missing", () => {
const order = {
payment: {
isInsurance: true,
insuranceCoverage: { coverageStatus: coverageStatus.PENDING },
parentAccountNumber: 999999,
},
policy: { policyNumber: null },
};
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(false);
});
test("returns false when coverage is verified but deductible is null", () => {
const order = {
payment: {
isInsurance: true,
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
parentAccountNumber: 999999,
},
policy: { policyNumber: "POL123", currentDeductible: null },
};
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(false);
});
test("returns false when coverage is verified but deductible is undefined", () => {
const order = {
payment: {
isInsurance: true,
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
parentAccountNumber: 999999,
},
policy: { policyNumber: "POL123", currentDeductible: undefined },
};
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(false);
});
test("returns false when coverage is verified but deductible is negative", () => {
const order = {
payment: {
isInsurance: true,
insuranceCoverage: { coverageStatus: coverageStatus.VERIFIED },
parentAccountNumber: 999999,
},
policy: { policyNumber: "POL123", currentDeductible: -1 },
};
const result = pagePrereqsHelper.hasInsuranceInfo(order);
expect(result).toBe(false);
});
test("adds log functions to logQueue when logQueue is provided", () => {
const order = { payment: { isInsurance: null } };
const logQueue = [];
pagePrereqsHelper.hasInsuranceInfo(order, logQueue);
expect(logQueue).toHaveLength(1);
logQueue[0]();
expect(debugLog).toHaveBeenCalled();
});
});
describe("hasSchedulingInfo", () => {
const validSchedule = {
date: "2024-01-15",
startTime: "09:00",
endTime: "17:00",
jobMinMinutes: "30",
jobMaxMinutes: "45",
};
test("returns true when all schedule fields are set", () => {
const order = { schedule: validSchedule };
const result = pagePrereqsHelper.hasSchedulingInfo(order);
expect(result).toBe(true);
});
test("returns false when date is missing", () => {
const order = {
schedule: { ...validSchedule, date: null },
};
const result = pagePrereqsHelper.hasSchedulingInfo(order);
expect(result).toBe(false);
});
test("returns false when startTime is missing", () => {
const order = {
schedule: { ...validSchedule, startTime: null },
};
const result = pagePrereqsHelper.hasSchedulingInfo(order);
expect(result).toBe(false);
});
test("returns false when jobMinMinutes is missing", () => {
const order = {
schedule: { ...validSchedule, jobMinMinutes: null },
};
const result = pagePrereqsHelper.hasSchedulingInfo(order);
expect(result).toBe(false);
});
test("does not add to logQueue when logQueue is null", () => {
const order = { schedule: validSchedule };
pagePrereqsHelper.hasSchedulingInfo(order, null);
expect(debugLog).not.toHaveBeenCalled();
});
test("adds log functions to logQueue when logQueue is provided", () => {
const order = { schedule: validSchedule };
const logQueue = [];
pagePrereqsHelper.hasSchedulingInfo(order, logQueue);
expect(logQueue).toHaveLength(1);
logQueue[0]();
expect(debugLog).toHaveBeenCalled();
});
});
describe("hasCustomerInfo", () => {
const validCustomer = {
firstName: "John",
lastName: "Doe",
phoneNumber: "555-555-5555",
emailAddress: "john@example.com",
};
test("returns true when all customer fields are set", () => {
const order = { customer: validCustomer };
const result = pagePrereqsHelper.hasCustomerInfo(order);
expect(result).toBe(true);
});
test("returns false when firstName is missing", () => {
const order = {
customer: { ...validCustomer, firstName: null },
};
const result = pagePrereqsHelper.hasCustomerInfo(order);
expect(result).toBe(false);
});
test("returns false when emailAddress is missing", () => {
const order = {
customer: { ...validCustomer, emailAddress: "" },
};
const result = pagePrereqsHelper.hasCustomerInfo(order);
expect(result).toBe(false);
});
test("returns false when phoneNumber is empty", () => {
const order = {
customer: { ...validCustomer, phoneNumber: "" },
};
const result = pagePrereqsHelper.hasCustomerInfo(order);
expect(result).toBe(false);
});
test("does not add to logQueue when logQueue is null", () => {
const order = { customer: validCustomer };
pagePrereqsHelper.hasCustomerInfo(order, null);
expect(debugLog).not.toHaveBeenCalled();
});
});
describe("hasGlassPartsOrRepairInfo", () => {
test("returns true when isRepair is true", () => {
const order = {
damage: { isRepair: true },
lineItems: { glassParts: [] },
};
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
expect(result).toBe(true);
});
test("returns true when glassParts has items", () => {
const order = {
damage: { isRepair: false },
lineItems: { glassParts: [{ id: "1" }] },
};
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
expect(result).toBe(true);
});
test("returns false when isRepair is false and glassParts is empty", () => {
const order = {
damage: { isRepair: false },
lineItems: { glassParts: [] },
};
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
expect(result).toBe(false);
});
test("returns false when isRepair is false and glassParts is null", () => {
const order = {
damage: { isRepair: false },
lineItems: { glassParts: null },
};
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
expect(result).toBe(false);
});
test("returns false when damage and lineItems are undefined", () => {
const order = {};
const result = pagePrereqsHelper.hasGlassPartsOrRepairInfo(order);
expect(result).toBe(false);
});
test("does not add to logQueue when logQueue is null", () => {
const order = {
damage: { isRepair: true },
lineItems: { glassParts: [] },
};
pagePrereqsHelper.hasGlassPartsOrRepairInfo(order, null);
expect(debugLog).not.toHaveBeenCalled();
});
test("adds log functions to logQueue when logQueue is provided", () => {
const order = {
damage: { isRepair: true },
lineItems: { glassParts: [] },
};
const logQueue = [];
pagePrereqsHelper.hasGlassPartsOrRepairInfo(order, logQueue);
expect(logQueue).toHaveLength(1);
logQueue[0]();
expect(debugLog).toHaveBeenCalled();
});
});
describe("hasPaymentMethodInfo", () => {
test("returns true when isPia is true and piaType is set", () => {
const order = {
payment: { isPia: true, piaType: "Afterpay" },
};
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
expect(result).toBe(true);
});
test("returns true when isPia is false and piaType is set", () => {
const order = {
payment: { isPia: false, piaType: "PayLater" },
};
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
expect(result).toBe(true);
});
test("returns false when isPia is null", () => {
const order = {
payment: { isPia: null, piaType: "Afterpay" },
};
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
expect(result).toBe(false);
});
test("returns false when isPia is false and piaType is null", () => {
const order = {
payment: { isPia: false, piaType: null },
};
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
expect(result).toBe(false);
});
test("returns false when isPia is false and piaType is empty string", () => {
const order = {
payment: { isPia: false, piaType: "" },
};
const result = pagePrereqsHelper.hasPaymentMethodInfo(order);
expect(result).toBe(false);
});
test("does not add to logQueue when logQueue is null", () => {
const order = { payment: { isPia: true, piaType: "Afterpay" } };
pagePrereqsHelper.hasPaymentMethodInfo(order, null);
expect(debugLog).not.toHaveBeenCalled();
});
test("adds log functions to logQueue when logQueue is provided", () => {
const order = { payment: { isPia: true, piaType: "Afterpay" } };
const logQueue = [];
pagePrereqsHelper.hasPaymentMethodInfo(order, logQueue);
expect(logQueue).toHaveLength(1);
logQueue[0]();
expect(debugLog).toHaveBeenCalled();
});
});
});

View file

@ -89,18 +89,53 @@ describe("mobile-details.vue", () => {
// Assert
expect(wrapper.vm.$router.navigateWithSaving).toHaveBeenCalled();
});
test("arePagePrerequisitesValid should be true ", async () => {
//Arrange
const { wrapper } = setupMocks({
mixins: [mockMixin],
attachTo: document.body,
describe("arePagePrerequisitesValid", () => {
test("returns true when all prerequisites are valid", () => {
const { wrapper } = setupMocks({
mixins: [mockMixin],
attachTo: document.body,
});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(true);
});
//Act
let arePagePrerequisitesValid = wrapper.vm.arePagePrerequisitesValid();
test("returns falsy when service zip info is missing", () => {
store.getters.order.serviceLocation.zipCode = null;
const { wrapper } = setupMocks({
mixins: [mockMixin],
attachTo: document.body,
});
//Assert
expect(arePagePrerequisitesValid).toBe(true);
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBeFalsy();
});
test("returns false when appointment type is not mobile", () => {
store.getters.order.serviceLocation.appointmentType = "Inshop";
const { wrapper } = setupMocks({
mixins: [mockMixin],
attachTo: document.body,
});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when scheduling info is missing", () => {
store.getters.order.schedule.date = null;
const { wrapper } = setupMocks({
mixins: [mockMixin],
attachTo: document.body,
});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
});
test("if the back button is clicked, navigate back", async () => {

View file

@ -78,6 +78,7 @@ import { settleAllPromises } from "@/helpers/layout-helper";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import textLink from "@/ux-components/text-link/text-link";
import analyticsMixin from "@/mixins/analytics-mixin";
import { flushPagePrereqsLogs, hasSchedulingInfo } from "@/helpers/page-prerequisites-helper";
export default {
name: "MobileDetails",
data() {
@ -110,6 +111,9 @@ export default {
},
methods: {
arePagePrerequisitesValid() {
const logQueue = [];
const order = store.getters.order;
const serviceLocation = store.getters.order.serviceLocation;
const serviceLocationPreReqs =
serviceLocation.zipCode &&
@ -118,16 +122,12 @@ export default {
serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
// Schedule
const schedule = store.getters.order.schedule;
const scheduleReqs = !!(
schedule.date &&
schedule.startTime &&
schedule.endTime &&
schedule.jobMaxMinutes &&
schedule.jobMinMinutes
);
const scheduleInfoPreReqs = hasSchedulingInfo(order, logQueue);
const preReqResult = serviceLocationPreReqs && scheduleInfoPreReqs;
flushPagePrereqsLogs("mobile-details.vue", preReqResult, logQueue);
const preReqResult = serviceLocationPreReqs && scheduleReqs;
return preReqResult;
},
getServiceAddressFromStore() {

View file

@ -0,0 +1,214 @@
import paymentAdyen from "@/layouts/payment-adyen/payment-adyen.vue";
import { shallowMount } from "@vue/test-utils";
import { getMountOptions } from "@/helpers/unit-test-helper";
import store from "@/store";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
jest.mock("@/helpers/pricing-helper.js", () => ({
getAmountDue: jest.fn().mockReturnValue(100),
}));
jest.mock("@/helpers/cms-content-helper", () => ({
fetchCmsContentForPage: () => Promise.resolve("content"),
}));
jest.mock("@/helpers/layout-helper", () => ({
settleAllPromises: jest.fn().mockResolvedValue({ cmsContent: "content" }),
}));
jest.mock("@/helpers/loading-modal-helper", () => ({
showFmgLoadingModal: jest.fn(),
}));
jest.mock("@/helpers/heritage-integration/order-helper.js", () => ({
submitWorkOrder: jest.fn(),
}));
jest.mock("@/helpers/debug-log-helper.js", () => ({
debugLog: jest.fn(),
}));
// Component that skips Adyen initialization in mounted (only needed for arePagePrerequisitesValid tests)
const PaymentAdyenTestComponent = {
...paymentAdyen,
mounted() {
// Stub - skip initializeAdyen to avoid Adyen/API setup
},
};
const createValidOrder = () => ({
vehicle: {
year: "2020",
make: "acura",
model: "mdx",
carId: "dummyCarId",
},
serviceLocation: {
address: "123 Main St",
address2: "",
city: "Columbus",
state: "OH",
zipCode: "43235",
zipCodeCtu: "43235",
appointmentType: AppointmentTypeStrings.IN_SHOP,
provider: {
address: {
streetAddress: "456 Provider St",
city: "Columbus",
state: "OH",
zipCode: "43235",
zipCodeCtu: "43235",
},
},
},
customer: {
firstName: "John",
lastName: "Doe",
emailAddress: "john.doe@example.com",
phoneNumber: "555-555-5555",
},
payment: {
isInsurance: false,
isPia: true,
piaType: "CREDIT_CARD",
insuranceCoverage: {},
},
policy: {
currentDeductible: 0,
policyNumber: null,
},
schedule: {
date: "2024-01-15",
startTime: "09:00",
endTime: "10:00",
jobMinMinutes: "30",
jobMaxMinutes: "45",
},
workOrderNumber: "WO-123456",
referralCorrelationId: "ref-123",
referralSequenceNumber: 1,
damage: { isRepair: false },
lineItems: {
glassParts: [{ id: "part1", partType: "WINDSHIELD" }],
},
});
describe("payment-adyen.vue", () => {
beforeEach(() => {
store.getters = {
order: createValidOrder(),
damage: {},
lineItems: { glassParts: [] },
payment: {},
policy: {},
};
});
afterEach(() => {
jest.clearAllMocks();
});
describe("arePagePrerequisitesValid", () => {
test("returns true when all prerequisites are valid", () => {
const wrapper = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(true);
});
test("returns false when service location info is missing", () => {
store.getters.order.serviceLocation.address = null;
store.getters.order.serviceLocation.provider.address.streetAddress = null;
const wrapper = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when insurance info is invalid", () => {
store.getters.order.payment.isInsurance = null;
const wrapper = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when scheduling info is missing", () => {
store.getters.order.schedule.date = null;
const wrapper = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when customer info is missing", () => {
store.getters.order.customer.firstName = null;
const wrapper = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when payment method info is invalid", () => {
store.getters.order.payment.isPia = null;
store.getters.order.payment.piaType = null;
const wrapper = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when glass parts or repair info is missing", () => {
store.getters.order.damage.isRepair = false;
store.getters.order.lineItems.glassParts = [];
const wrapper = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns true for mobile appointment type when address fields are present", () => {
store.getters.order.serviceLocation.appointmentType = AppointmentTypeStrings.MOBILE;
store.getters.order.serviceLocation.address = "123 Mobile St";
store.getters.order.serviceLocation.city = "Columbus";
store.getters.order.serviceLocation.state = "OH";
store.getters.order.serviceLocation.zipCode = "43235";
const wrapper = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(true);
});
});
});
function setupMocks({ customMountOptions } = {}) {
const mountOptions = getMountOptions({
...customMountOptions,
store,
route: { name: "payment-adyen" },
router: {
navigateWithSaving: jest.fn(),
navigateWithoutSaving: jest.fn(),
navigateWithPageData: jest.fn(),
},
});
mountOptions.global.mocks["navigationScenarios"] = {};
mountOptions.mixins = [
{
methods: {
getCmsContent: jest.fn(),
setCmsContent: jest.fn(),
},
},
];
return shallowMount(PaymentAdyenTestComponent, mountOptions);
}

View file

@ -83,7 +83,15 @@ import cart from "@/fmg-components/cart/cart";
import { coverageStatus } from "@/constants/insurance";
import { deepClone } from "@/helpers/object-helper";
import store from "@/store";
import { debugLog } from "@/helpers/debug-log-helper.js";
import {
flushPagePrereqsLogs,
hasServiceLocationInfo,
hasInsuranceInfo,
hasSchedulingInfo,
hasCustomerInfo,
hasPaymentMethodInfo,
hasGlassPartsOrRepairInfo,
} from "@/helpers/page-prerequisites-helper.js";
export default {
name: "payment-adyen",
@ -136,110 +144,17 @@ export default {
},
arePagePrerequisitesValid() {
// Service Location
const serviceLocation = store.getters.order.serviceLocation;
const mobileReqs = !!(
serviceLocation.address &&
serviceLocation.city &&
serviceLocation.state &&
serviceLocation.zipCode
);
const providerLocation = serviceLocation.provider.address;
const dropOffInshopReqs = !!(
providerLocation.streetAddress &&
providerLocation.city &&
providerLocation.state &&
providerLocation.zipCode
);
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
const serviceLocationReqs =
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
// Insurance
const isInsuranceSet = store.getters.order.payment.isInsurance !== null;
// Schedule
const schedule = store.getters.order.schedule;
const scheduleReqs = !!(
schedule.date &&
schedule.startTime &&
schedule.endTime &&
schedule.jobMaxMinutes &&
schedule.jobMinMinutes
);
// Customer
const customer = store.getters.order.customer;
const customerReqs = !!(
customer.firstName &&
customer.lastName &&
customer.phoneNumber &&
customer.emailAddress
);
const paymentMethodReqs =
store.getters.order.payment.isPia !== null &&
(store.getters.order.payment.isPia || !!store.getters.order.payment.piaType);
const preReqResult =
serviceLocationReqs &&
isInsuranceSet &&
scheduleReqs &&
customerReqs &&
paymentMethodReqs;
// prettier-ignore
{
debugLog("--- payment-adyen.vue pagePrereqs start ---", null, !preReqResult);
debugLog("mobileReqs:", mobileReqs, !preReqResult);
debugLog("serviceLocation.address:", serviceLocation.address, !preReqResult);
debugLog("serviceLocation.city:", serviceLocation.city, !preReqResult);
debugLog("serviceLocation.state:", serviceLocation.state, !preReqResult);
debugLog("serviceLocation.zipCode:", serviceLocation.zipCode, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("dropOffInshopReqs:", dropOffInshopReqs, !preReqResult);
debugLog("serviceLocationReqs:", serviceLocationReqs, !preReqResult);
debugLog("providerLocation.streetAddress:", providerLocation.streetAddress, !preReqResult);
debugLog("providerLocation.city:", providerLocation.city, !preReqResult);
debugLog("providerLocation.state:", providerLocation.state, !preReqResult);
debugLog("providerLocation.zipCode:", providerLocation.zipCode, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("isInsuranceSet:", isInsuranceSet, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("scheduleReqs:", scheduleReqs, !preReqResult);
debugLog("schedule.date:", schedule.date, !preReqResult);
debugLog("schedule.startTime:", schedule.startTime, !preReqResult);
debugLog("schedule.endTime:", schedule.endTime, !preReqResult);
debugLog("schedule.jobMaxMinutes:", schedule.jobMaxMinutes, !preReqResult);
debugLog("schedule.jobMinMinutes:", schedule.jobMinMinutes, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("customerReqs:", customerReqs, !preReqResult);
debugLog("customer.firstName:", customer.firstName, !preReqResult);
debugLog("customer.lastName:", customer.lastName, !preReqResult);
debugLog("customer.phoneNumber:", customer.phoneNumber, !preReqResult);
debugLog("customer.emailAddress:", customer.emailAddress, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("paymentMethodReqs:", paymentMethodReqs, !preReqResult);
debugLog("store.getters.order.payment.isPia:", store.getters.order?.payment?.isPia, !preReqResult);
debugLog("store.getters.order.payment.piaType:", store.getters.order?.payment?.piaType, !preReqResult);
debugLog("--- payment-adyen.vue pagePrereqs end ---", null, !preReqResult);
}
return preReqResult;
const order = store.getters.order;
const logQueue = [];
const result =
hasServiceLocationInfo(order, logQueue) &&
hasInsuranceInfo(order, logQueue) &&
hasSchedulingInfo(order, logQueue) &&
hasCustomerInfo(order, logQueue) &&
hasPaymentMethodInfo(order, logQueue) &&
hasGlassPartsOrRepairInfo(order, logQueue);
flushPagePrereqsLogs("payment-adyen.vue", result, logQueue);
return result;
},
async initializeAdyen() {

View file

@ -8,6 +8,7 @@ import store from "@/store";
import { experimentSettings } from "@/constants/experiments";
import { storeMutations } from "@/constants/store-mutations";
import globalMethods from "@/global-methods";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
globalMethods.callHttpClient = jest.fn();
@ -29,8 +30,64 @@ jest.mock("@/helpers/pricing-helper.js", () => ({
getAmountDue: jest.fn(),
}));
jest.mock("@/helpers/debug-log-helper.js", () => ({
debugLog: jest.fn(),
}));
let piaDisabledFlag = false;
const createValidOrderForPaymentMethod = () => ({
payment: {
isPia: false,
isInsurance: false,
insuranceCoverage: { isVerified: true },
},
lineItems: {
glassParts: [{ id: "part1", partType: "WINDSHIELD" }],
supportingItems: [],
vaps: [],
promos: [],
},
policy: {
currentDeductible: 123,
isNoComp: false,
isItac: false,
},
serviceLocation: {
appointmentType: AppointmentTypeStrings.MOBILE,
address: "123 Test Ave",
address2: "",
city: "Anytown",
state: "OH",
zipCode: "00000",
zipCodeCtu: "00000",
provider: {
address: {
streetAddress: "456 Provider St",
city: "Anytown",
state: "OH",
zipCode: "00000",
zipCodeCtu: "00000",
},
},
},
customer: {
firstName: "John",
lastName: "Doe",
emailAddress: "john@example.com",
phoneNumber: "555-555-5555",
},
schedule: {
date: "2024-01-15",
startTime: "09:00",
endTime: "10:00",
jobMinMinutes: "30",
jobMaxMinutes: "45",
},
vehicle: { year: 2020, make: "Toyota", model: "Camry", carId: "12345" },
damage: { isRepair: false },
});
describe("payment-method.vue", () => {
describe("navigation", () => {
test("if the back button is clicked, navigate back", async () => {
@ -54,6 +111,63 @@ describe("payment-method.vue", () => {
expect(wrapper.vm.$router.navigateWithoutSaving).toHaveBeenCalled();
});
});
describe("arePagePrerequisitesValid", () => {
test("returns true when all prerequisites are valid", () => {
const { wrapper } = setupMocks();
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(true);
});
test("returns false when service location info is missing", () => {
const { wrapper } = setupMocks();
store.getters.order.serviceLocation.address = null;
store.getters.order.serviceLocation.provider.address.streetAddress = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when insurance info is invalid", () => {
const { wrapper } = setupMocks();
store.getters.order.payment.isInsurance = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when scheduling info is missing", () => {
const { wrapper } = setupMocks();
store.getters.order.schedule.date = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when customer info is missing", () => {
const { wrapper } = setupMocks();
store.getters.order.customer.firstName = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when glass parts or repair info is missing", () => {
const { wrapper } = setupMocks();
store.getters.order.damage.isRepair = false;
store.getters.order.lineItems.glassParts = [];
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
});
});
function setupMocks() {
@ -65,35 +179,7 @@ function setupMocks() {
vaps: [],
promos: [],
},
order: {
payment: {
isPia: false,
insuranceCoverage: {
isVerified: true,
},
},
lineItems: {
glassParts: [],
supportingItems: [],
vaps: [],
promos: [],
},
policy: {
currentDeductible: 123,
isNoComp: false,
isItac: false,
},
serviceLocation: {
appointmentType: "Mobile",
address: "123 Test Ave",
city: "Anytown",
state: "OH",
zipCode: "00000",
provider: { providerNumber: "0000567" },
},
vehicle: { year: 2020, make: "Toyota", model: "Camry", carId: "12345" },
damage: { isRepair: false },
},
order: createValidOrderForPaymentMethod(),
applicationUser: {
experiments: [],
},

View file

@ -178,7 +178,14 @@ import { containsRecalParts } from "@/helpers/recal-helper";
import { getBoolFromString } from "@/helpers/boolean-helper";
import { getAmountDue, addPricesToLineItems } from "@/helpers/pricing-helper.js";
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
import { debugLog } from "@/helpers/debug-log-helper";
import {
flushPagePrereqsLogs,
hasServiceLocationInfo,
hasInsuranceInfo,
hasSchedulingInfo,
hasCustomerInfo,
hasGlassPartsOrRepairInfo,
} from "@/helpers/page-prerequisites-helper.js";
import { ErrorMessage } from "vee-validate";
import { Field } from "vee-validate";
import experimentMixin from "../../mixins/experiment-mixin";
@ -381,91 +388,16 @@ export default {
this.$router.navigateWithoutSaving(scenarioName, this.pageName);
},
arePagePrerequisitesValid() {
// Service Location
const serviceLocation = store.getters.order.serviceLocation;
const mobileReqs = !!(
serviceLocation.address &&
serviceLocation.city &&
serviceLocation.state &&
serviceLocation.zipCode
);
const providerLocation = serviceLocation.provider.address;
const dropOffInshopReqs = !!(
providerLocation.streetAddress &&
providerLocation.city &&
providerLocation.state &&
providerLocation.zipCode
);
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
const serviceLocationReqs =
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
// Insurance
const isInsurance = store.getters.order.payment.isInsurance;
const insuranceCoverageStatus = store.getters.payment.insuranceCoverage.coverageStatus;
const currentDeductible = store.getters.policy.currentDeductible;
const isInsuranceSet = () => {
if (isInsurance !== null) {
if (
insuranceCoverageStatus === coverageStatus.VERIFIED &&
!(currentDeductible === 0 || currentDeductible > 0)
) {
return false; // if coverageStatus is verified there must be a valid deductible also
}
return true;
}
return false;
};
// Schedule
const schedule = store.getters.order.schedule;
const scheduleReqs = !!(
schedule.date &&
schedule.startTime &&
schedule.endTime &&
schedule.jobMaxMinutes &&
schedule.jobMinMinutes
);
// Customer
const customer = store.getters.order.customer;
const customerReqs = !!(
customer.firstName &&
customer.lastName &&
customer.phoneNumber &&
customer.emailAddress
);
const preReqResult =
serviceLocationReqs && isInsuranceSet() && scheduleReqs && customerReqs;
// prettier-ignore
{
debugLog("--- payment-method.vue pagePrereqs start ---", null, !preReqResult);
debugLog("serviceLocationRequs::isMobile:", isMobile, !preReqResult);
debugLog("serviceLocationReqs::mobileReqs:", mobileReqs, !preReqResult);
debugLog("serviceLocationReqs::dropOffInshopReqs:", dropOffInshopReqs, !preReqResult);
debugLog("serviceLocationReqs:: result", serviceLocationReqs, !preReqResult);
debugLog("isInsuranceSet::", isInsuranceSet(), !preReqResult);
debugLog("scheduleReqs::schedule.date:", schedule.date, !preReqResult);
debugLog("scheduleReqs::schedule.startTime:", schedule.startTime, !preReqResult);
debugLog("scheduleReqs::schedule.endTime:", schedule.endTime, !preReqResult);
debugLog("scheduleReqs::schedule.jobMaxMinutes:", schedule.jobMaxMinutes, !preReqResult);
debugLog("scheduleReqs::schedule.jobMinMinutes:", schedule.jobMinMinutes, !preReqResult);
debugLog("customerReqs::customer.firstName:", customer.firstName, !preReqResult);
debugLog("customerReqs::customer.lastName:", customer.lastName, !preReqResult);
debugLog("customerReqs::customer.phoneNumber:", customer.phoneNumber, !preReqResult);
debugLog("customerReqs::customer.emailAddress:", customer.emailAddress, !preReqResult);
debugLog("--- payment-method.vue pagePrereqs end ---", null, !preReqResult);
}
return preReqResult;
const order = store.getters.order;
const logQueue = [];
const result =
hasServiceLocationInfo(order, logQueue) &&
hasInsuranceInfo(order, logQueue) &&
hasSchedulingInfo(order, logQueue) &&
hasCustomerInfo(order, logQueue) &&
hasGlassPartsOrRepairInfo(order, logQueue);
flushPagePrereqsLogs("payment-method.vue", result, logQueue);
return result;
},
getPaymentMethodFromStore() {
const piaType = store.getters.order.payment.piaType;

View file

@ -258,6 +258,52 @@ describe("payment.vue", () => {
expect(result).toBe(true);
});
test("Returns false when service location info is missing", () => {
const wrapper = setupMocks({});
store.getters.order.serviceLocation.provider.address.streetAddress = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("Returns false when insurance info is invalid", () => {
const wrapper = setupMocks({});
store.getters.order.payment.isInsurance = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("Returns false when scheduling info is missing", () => {
const wrapper = setupMocks({});
store.getters.order.schedule.date = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("Returns false when customer info is missing", () => {
const wrapper = setupMocks({});
store.getters.order.customer.firstName = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("Returns false when glass parts or repair info is missing", () => {
const wrapper = setupMocks({});
store.getters.order.damage.isRepair = false;
store.getters.order.lineItems.glassParts = [];
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
describe("Payment method cases", () => {
test("Returns false if pia options are not valid", () => {
// Arrange

View file

@ -235,7 +235,6 @@ import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import { applicationConfig } from "@/constants/application-config";
import { paymentMethods } from "@/constants/payment-method-constants";
import { AppointmentTypeStrings } from "@/constants/schedule-constants";
import baseMixin from "@/mixins/base-mixin.js";
import { deepClone } from "@/helpers/object-helper";
import { submitWorkOrder } from "@/helpers/heritage-integration/order-helper.js";
@ -243,6 +242,15 @@ import iframeResize from "../../../node_modules/iframe-resizer/js/iframeResizer.
import { coverageStatus } from "@/constants/insurance";
import { getDisplayAmountDue, getAmountDue } from "@/helpers/pricing-helper.js";
import { debugLog } from "@/helpers/debug-log-helper.js";
import {
flushPagePrereqsLogs,
hasServiceLocationInfo,
hasInsuranceInfo,
hasSchedulingInfo,
hasCustomerInfo,
hasPaymentMethodInfo,
hasGlassPartsOrRepairInfo,
} from "@/helpers/page-prerequisites-helper.js";
import buttonQuestion from "@/digital-components/button-question/button-question";
import paymentMethodListButton from "@/layouts/payment-method/payment-method-question/payment-method-list-button/payment-method-list-button";
@ -435,110 +443,17 @@ export default {
},
methods: {
arePagePrerequisitesValid() {
// Service Location
const serviceLocation = store.getters.order.serviceLocation;
const mobileReqs = !!(
serviceLocation.address &&
serviceLocation.city &&
serviceLocation.state &&
serviceLocation.zipCode
);
const providerLocation = serviceLocation.provider.address;
const dropOffInshopReqs = !!(
providerLocation.streetAddress &&
providerLocation.city &&
providerLocation.state &&
providerLocation.zipCode
);
const isMobile = serviceLocation.appointmentType === AppointmentTypeStrings.MOBILE;
const serviceLocationReqs =
(isMobile && mobileReqs) || (!isMobile && dropOffInshopReqs);
// Insurance
const isInsuranceSet = store.getters.order.payment.isInsurance !== null;
// Schedule
const schedule = store.getters.order.schedule;
const scheduleReqs = !!(
schedule.date &&
schedule.startTime &&
schedule.endTime &&
schedule.jobMaxMinutes &&
schedule.jobMinMinutes
);
// Customer
const customer = store.getters.order.customer;
const customerReqs = !!(
customer.firstName &&
customer.lastName &&
customer.phoneNumber &&
customer.emailAddress
);
const paymentMethodReqs =
store.getters.order.payment.isPia !== null &&
(store.getters.order.payment.isPia || !!store.getters.order.payment.piaType);
const preReqResult =
serviceLocationReqs &&
isInsuranceSet &&
scheduleReqs &&
customerReqs &&
paymentMethodReqs;
// prettier-ignore
{
debugLog("--- payment.vue pagePrereqs start ---", null, !preReqResult);
debugLog("mobileReqs:", mobileReqs, !preReqResult);
debugLog("serviceLocation.address:", serviceLocation.address, !preReqResult);
debugLog("serviceLocation.city:", serviceLocation.city, !preReqResult);
debugLog("serviceLocation.state:", serviceLocation.state, !preReqResult);
debugLog("serviceLocation.zipCode:", serviceLocation.zipCode, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("dropOffInshopReqs:", dropOffInshopReqs, !preReqResult);
debugLog("serviceLocationReqs:", serviceLocationReqs, !preReqResult);
debugLog("providerLocation.streetAddress:", providerLocation.streetAddress, !preReqResult);
debugLog("providerLocation.city:", providerLocation.city, !preReqResult);
debugLog("providerLocation.state:", providerLocation.state, !preReqResult);
debugLog("providerLocation.zipCode:", providerLocation.zipCode, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("isInsuranceSet:", isInsuranceSet, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("scheduleReqs:", scheduleReqs, !preReqResult);
debugLog("schedule.date:", schedule.date, !preReqResult);
debugLog("schedule.startTime:", schedule.startTime, !preReqResult);
debugLog("schedule.endTime:", schedule.endTime, !preReqResult);
debugLog("schedule.jobMaxMinutes:", schedule.jobMaxMinutes, !preReqResult);
debugLog("schedule.jobMinMinutes:", schedule.jobMinMinutes, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("customerReqs:", customerReqs, !preReqResult);
debugLog("customer.firstName:", customer.firstName, !preReqResult);
debugLog("customer.lastName:", customer.lastName, !preReqResult);
debugLog("customer.phoneNumber:", customer.phoneNumber, !preReqResult);
debugLog("customer.emailAddress:", customer.emailAddress, !preReqResult);
debugLog("", null, !preReqResult);
debugLog("paymentMethodReqs:", paymentMethodReqs, !preReqResult);
debugLog("store.getters.order.payment.isPia:", store.getters.order?.payment?.isPia, !preReqResult);
debugLog("store.getters.order.payment.piaType:", store.getters.order?.payment?.piaType, !preReqResult);
debugLog("--- payment.vue pagePrereqs end ---", null, !preReqResult);
}
return preReqResult;
const order = store.getters.order;
const logQueue = [];
const result =
hasServiceLocationInfo(order, logQueue) &&
hasInsuranceInfo(order, logQueue) &&
hasSchedulingInfo(order, logQueue) &&
hasCustomerInfo(order, logQueue) &&
hasPaymentMethodInfo(order, logQueue) &&
hasGlassPartsOrRepairInfo(order, logQueue);
flushPagePrereqsLogs("payment.vue", result, logQueue);
return result;
},
getAnswersNullSafe(widgetName) {
const rawData = this.getCmsContent(widgetName, "Answers");

View file

@ -295,6 +295,47 @@ describe("quote.vue", () => {
expect(arePagePrerequisitesValid).toBe(false);
});
test("should fail arePagePrerequisitesValid when service zip info is missing", () => {
store.getters = {
order: {
lineItems: { glassParts: ["item"] },
serviceLocation: {
zipCode: null,
zipCodeCtu: "value",
},
damage: { isRepair: false },
payment: { insuranceCoverage: { isVerified: false } },
referralNumber: "1234567",
},
};
const { wrapper } = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("should fail arePagePrerequisitesValid when insurance coverage is verified", () => {
store.getters = {
order: {
lineItems: { glassParts: ["item"] },
serviceLocation: {
zipCode: "12345",
zipCodeCtu: "value",
},
damage: { isRepair: false },
payment: { insuranceCoverage: { isVerified: true } },
referralNumber: "1234567",
},
};
const { wrapper } = setupMocks({});
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("should have non-null values for necessary data members after 'beforeRouteEnter'", async () => {
//Arrange
store.getters = {

View file

@ -183,6 +183,11 @@ import { routeData } from "@/router/constants/routes";
import { consumeQueryFromStash } from "@/router/methods/helpers/querystring-stash";
import { storeMutations } from "@/constants/store-mutations";
import { debugLog } from "@/helpers/debug-log-helper";
import {
flushPagePrereqsLogs,
hasServiceZipInfo,
hasGlassPartsOrRepairInfo,
} from "@/helpers/page-prerequisites-helper.js";
import { savePageData } from "@/router/methods/helpers/save-page-data";
import { quotePageDiscountTable } from "../../constants/quote-page-discounts";
@ -728,28 +733,26 @@ export default {
this.$refs[modalName].openModal();
},
arePagePrerequisitesValid() {
const payment = store.getters.order.payment;
const order = store.getters.order;
const logQueue = [];
const isVerifiedOk =
order.payment?.insuranceCoverage?.isVerified == null ||
order.payment?.insuranceCoverage?.isVerified === false;
const preReqResult =
store.getters.order.serviceLocation.zipCode &&
store.getters.order.serviceLocation.zipCodeCtu &&
(store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0)) &&
(payment.insuranceCoverage?.isVerified == null ||
payment.insuranceCoverage?.isVerified === false);
hasServiceZipInfo(order, logQueue) &&
hasGlassPartsOrRepairInfo(order, logQueue) &&
isVerifiedOk;
// prettier-ignore
{
debugLog("--- quote.vue pagePrereqs start ---", null, !preReqResult);
debugLog("store.getters.order.serviceLocation.zipCode:", store.getters.order.serviceLocation?.zipCode, !preReqResult);
debugLog("store.getters.order.serviceLocation.zipCodeCtu:", store.getters.order.serviceLocation?.zipCodeCtu, !preReqResult);
debugLog("store.getters.order.damage.isRepair:", store.getters.order.damage?.isRepair, !preReqResult);
debugLog("store.getters.order.lineItems?.glassParts:", store.getters.order.lineItems?.glassParts, !preReqResult);
debugLog("store.getters.order.payemt.insuranceCoverage?.isVerified:", store.getters.order.payment?.insuranceCoverage?.isVerified, !preReqResult);
debugLog("--- quote.vue pagePrereqs end ---", null, !preReqResult);
}
logQueue.push(() => {
debugLog(
"payment.insuranceCoverage?.isVerified:",
order.payment?.insuranceCoverage?.isVerified,
!preReqResult
);
});
flushPagePrereqsLogs("quote.vue", preReqResult, logQueue);
return preReqResult;
},
vapsItemsSelectedAction(vapsItemsSelected) {

View file

@ -57,6 +57,40 @@ jest.mock("@/helpers/heritage-integration/navigation-helper", () => ({
navigateToHeritageFunnel: jest.fn(),
}));
jest.mock("@/helpers/debug-log-helper.js", () => ({
debugLog: jest.fn(),
}));
const createValidOrderForSchedule = () => ({
payment: {
isInsurance: false,
insuranceCoverage: { isVerified: false },
},
referralNumber: "",
serviceLocation: {
address: "",
address2: "",
city: "",
state: "",
zipCode: "00000",
zipCodeCtu: "000",
appointmentType: null,
provider: {},
isVehicleProtected: false,
},
schedule: {
date: "",
routeCode: "",
startTime: "",
endTime: "",
jobMinMinutes: null,
jobMaxMinutes: null,
},
policy: { isItac: false, isNoComp: false },
damage: { isRepair: true },
lineItems: { glassParts: [] },
});
describe("schedule.vue", () => {
describe("navigation", () => {
test("backButtonAction => non-insurance: navigateWithoutSaving called", () => {
@ -149,8 +183,67 @@ describe("schedule.vue", () => {
expect(wrapper.vm.$router.navigateWithSaving).not.toHaveBeenCalled();
});
});
describe("arePagePrerequisitesValid", () => {
test("returns true when all prerequisites are valid", () => {
const { wrapper } = setupMocksForArePagePrerequisitesValid();
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(true);
});
test("returns false when service zip info is missing", () => {
const { wrapper } = setupMocksForArePagePrerequisitesValid();
store.getters.order.serviceLocation.zipCode = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when glass parts or repair info is missing", () => {
const { wrapper } = setupMocksForArePagePrerequisitesValid();
store.getters.order.damage.isRepair = false;
store.getters.order.lineItems.glassParts = [];
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when insurance info is invalid", () => {
const { wrapper } = setupMocksForArePagePrerequisitesValid();
store.getters.order.payment.isInsurance = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
test("returns false when supporting items missing for cash order", () => {
const { wrapper } = setupMocksForArePagePrerequisitesValid();
store.getters.order.payment.isInsurance = false;
store.getters.lineItems.supportingItems = null;
const result = wrapper.vm.arePagePrerequisitesValid();
expect(result).toBe(false);
});
});
});
function setupMocksForArePagePrerequisitesValid() {
store.getters = {
...storeMocked.getters,
order: createValidOrderForSchedule(),
lineItems: {
supportingItems: [],
},
};
return setupMocks({ store });
}
function setupMocks(mountOptionsMockData = {}) {
const route = { name: "schedule" };
const defaultMountOptions = {

View file

@ -263,6 +263,12 @@ import { getAmountDue, getPricingByDayPartWithPrice } from "@/helpers/pricing-he
import { getItemsWithoutRecalParts } from "@/helpers/recal-helper";
import { deepClone } from "@/helpers/object-helper";
import { debugLog } from "@/helpers/debug-log-helper";
import {
flushPagePrereqsLogs,
hasServiceZipInfo,
hasGlassPartsOrRepairInfo,
hasInsuranceInfo,
} from "@/helpers/page-prerequisites-helper.js";
import {
getSessionKeyValue,
getUserIdValue,
@ -985,76 +991,32 @@ export default {
methods: {
splitCopyOnCMSPlaceHolder,
arePagePrerequisitesValid() {
const paymentInfo = store.getters.payment.isInsurance !== null;
const damageInfo =
store.getters.order.damage.isRepair ||
(store.getters.order.lineItems?.glassParts != null &&
store.getters.order.lineItems.glassParts.length > 0);
const parentAccountNumberInfo =
store.getters.payment.parentAccountNumber !==
applicationConfig.CASH_PARENT_ACCOUNT_NUMBER;
const policyNumberInfo = store.getters.order.policy.policyNumber;
const zipCodeInfo = store.getters.order.serviceLocation.zipCode;
const supportingItemsInfo = store.getters.lineItems.supportingItems;
const order = store.getters.order;
const logQueue = [];
const serviceZip = hasServiceZipInfo(order, logQueue);
const glassPartsOrRepair = hasGlassPartsOrRepairInfo(order, logQueue);
const insuranceInfo = hasInsuranceInfo(order, logQueue);
// insurance drops the recycle fee on replace orders so it won't be in supportingItems
const insurancePreReqResult =
parentAccountNumberInfo &&
policyNumberInfo &&
zipCodeInfo &&
paymentInfo &&
damageInfo;
const cashPreReqResult =
supportingItemsInfo && zipCodeInfo && paymentInfo && damageInfo;
const supportingItemsInfo = store.getters.lineItems?.supportingItems;
const supportingItemsPreReqs = order.payment?.isInsurance
? true
: !!supportingItemsInfo;
const outputDebugLog = (preReqResult) => {
// prettier-ignore
debugLog("--- schedule.vue pagePrereqs start ---", null, !preReqResult);
debugLog(
"store.getters.payment.isInsurance:",
store.getters.payment?.isInsurance,
!preReqResult
);
debugLog(
"store.getters.order.damage.isRepair:",
store.getters.order.damage?.isRepair,
!preReqResult
);
debugLog(
"store.getters.order.lineItems.glassParts:",
store.getters.order.lineItems?.glassParts,
!preReqResult
);
debugLog(
"store.getters.payment.parentAccountNumber:",
store.getters.payment.parentAccountNumber,
!preReqResult
);
debugLog(
"store.getters.order.policy.policyNumber:",
store.getters.order.policy.policyNumber,
!preReqResult
);
debugLog(
"store.getters.order.serviceLocation.zipCode:",
store.getters.order.serviceLocation.zipCode,
!preReqResult
);
const preReqResult =
serviceZip && insuranceInfo && glassPartsOrRepair && supportingItemsPreReqs;
logQueue.push(() => {
debugLog(
"store.getters.lineItems.supportingItems:",
store.getters.lineItems.supportingItems,
!preReqResult
store.getters.lineItems?.supportingItems,
!supportingItemsPreReqs
);
debugLog("--- schedule.vue pagePrereqs end ---", null, !preReqResult);
};
});
if (store.getters.payment.isInsurance) {
outputDebugLog(insurancePreReqResult);
return insurancePreReqResult;
} else {
outputDebugLog(cashPreReqResult);
return cashPreReqResult;
}
flushPagePrereqsLogs("schedule.vue", preReqResult, logQueue);
return preReqResult;
},
setDataOnLoad(zipCodeData, serviceabilityDetails, mobileFeePart, shopProviderData) {