Merge pull request #3252 from Safelite/feature/CASH-2916
CASH-2916 | Consume new v2 endpoints for scheduling
This commit is contained in:
commit
1f0003be07
6 changed files with 367 additions and 32 deletions
|
|
@ -112,10 +112,18 @@ const endpoints = {
|
||||||
url: "/schedule/api/v1/schedule/shop-time-slots",
|
url: "/schedule/api/v1/schedule/shop-time-slots",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
},
|
},
|
||||||
|
GetShopTimeSlotsV2: {
|
||||||
|
url: "/schedule/api/v2/schedule/shop-time-slots",
|
||||||
|
method: "POST",
|
||||||
|
},
|
||||||
GetMobileTimeSlots: {
|
GetMobileTimeSlots: {
|
||||||
url: "/schedule/api/v1/schedule/mobile-time-slots",
|
url: "/schedule/api/v1/schedule/mobile-time-slots",
|
||||||
method: "POST",
|
method: "POST",
|
||||||
},
|
},
|
||||||
|
GetMobileTimeSlotsV2: {
|
||||||
|
url: "/schedule/api/v2/schedule/mobile-time-slots",
|
||||||
|
method: "POST",
|
||||||
|
},
|
||||||
GetMobilePremiumFee: {
|
GetMobilePremiumFee: {
|
||||||
url: "/parts/api/v1/parts/mobile-premium-fee",
|
url: "/parts/api/v1/parts/mobile-premium-fee",
|
||||||
method: "GET",
|
method: "GET",
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,9 @@ const storeActions = {
|
||||||
GET_PRICING_BY_DAY_PART: "getPricingByDayPart",
|
GET_PRICING_BY_DAY_PART: "getPricingByDayPart",
|
||||||
GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails",
|
GET_SERVICEABILITY_DETAILS: "getServiceabilityDetails",
|
||||||
GET_SHOP_TIME_SLOTS: "getShopTimeSlots",
|
GET_SHOP_TIME_SLOTS: "getShopTimeSlots",
|
||||||
|
GET_SHOP_TIME_SLOTS_V2: "getShopTimeSlotsV2",
|
||||||
GET_MOBILE_TIME_SLOTS: "getMobileTimeSlots",
|
GET_MOBILE_TIME_SLOTS: "getMobileTimeSlots",
|
||||||
|
GET_MOBILE_TIME_SLOTS_V2: "getMobileTimeSlotsV2",
|
||||||
GET_PROVIDERS: "getProviders",
|
GET_PROVIDERS: "getProviders",
|
||||||
GET_MOBILE_PREMIUM_FEE: "getMobilePremiumFee",
|
GET_MOBILE_PREMIUM_FEE: "getMobilePremiumFee",
|
||||||
SAVE_SESSION: "saveSession",
|
SAVE_SESSION: "saveSession",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import { shallowMount } from "@vue/test-utils";
|
||||||
import scheduling from "./scheduling";
|
import scheduling from "./scheduling";
|
||||||
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
import { getMountOptions } from "@/helpers/unit-test-helper.js";
|
||||||
import { settleAllPromises } from "@/helpers/layout-helper";
|
import { settleAllPromises } from "@/helpers/layout-helper";
|
||||||
|
import store from "@/store";
|
||||||
|
|
||||||
jest.mock("@/store", () => ({
|
jest.mock("@/store", () => ({
|
||||||
dispatch: jest.fn().mockResolvedValue(null),
|
dispatch: jest.fn().mockResolvedValue(null),
|
||||||
|
|
@ -107,5 +108,72 @@ describe("scheduling.vue", () => {
|
||||||
await expect(wrapper.vm.handleRequestMoreDates()).resolves.toBeUndefined();
|
await expect(wrapper.vm.handleRequestMoreDates()).resolves.toBeUndefined();
|
||||||
wrapper.unmount();
|
wrapper.unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("batches inshop provider requests into a single getShopTimeSlotsV2 call", async () => {
|
||||||
|
store.dispatch.mockClear();
|
||||||
|
settleAllPromises.mockResolvedValueOnce({
|
||||||
|
inshopTimeSlots: {
|
||||||
|
providerTimeSlots: [
|
||||||
|
{ providerNumber: "05018", days: [{ date: "2026-07-22", timeSlots: [] }] },
|
||||||
|
{ providerNumber: "05019", days: [{ date: "2026-07-22", timeSlots: [] }] },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
wrapper.vm.inShopProvidersAndTimeslots = [
|
||||||
|
{ provider: { providerNumber: "05018" }, timeSlots: { days: [] } },
|
||||||
|
{ provider: { providerNumber: "05019" }, timeSlots: { days: [] } },
|
||||||
|
];
|
||||||
|
wrapper.vm.datePickerEndDate = "2026-07-21";
|
||||||
|
|
||||||
|
await wrapper.vm.handleRequestMoreDates();
|
||||||
|
|
||||||
|
const shopTimeSlotDispatches = store.dispatch.mock.calls.filter(
|
||||||
|
([actionName]) => actionName === "getShopTimeSlotsV2"
|
||||||
|
);
|
||||||
|
expect(shopTimeSlotDispatches).toHaveLength(1);
|
||||||
|
expect(shopTimeSlotDispatches[0][1].payload.providerNumbers).toEqual([
|
||||||
|
"05018",
|
||||||
|
"05019",
|
||||||
|
]);
|
||||||
|
expect(shopTimeSlotDispatches[0][1].payload.endDate).toBeDefined();
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("maps batched inshop providerTimeSlots back onto provider entries", async () => {
|
||||||
|
settleAllPromises.mockResolvedValueOnce({
|
||||||
|
inshopTimeSlots: {
|
||||||
|
providerTimeSlots: [
|
||||||
|
{
|
||||||
|
providerNumber: "05018",
|
||||||
|
days: [{ date: "2026-07-22", timeSlots: [{ id: "slot-a" }] }],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
providerNumber: "05019",
|
||||||
|
days: [{ date: "2026-07-22", timeSlots: [{ id: "slot-b" }] }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const { wrapper } = setupMocks();
|
||||||
|
wrapper.vm.inShopProvidersAndTimeslots = [
|
||||||
|
{ provider: { providerNumber: "05018" }, timeSlots: { days: [] } },
|
||||||
|
{ provider: { providerNumber: "05019" }, timeSlots: { days: [] } },
|
||||||
|
];
|
||||||
|
wrapper.vm.datePickerEndDate = "2026-07-21";
|
||||||
|
|
||||||
|
await wrapper.vm.handleRequestMoreDates();
|
||||||
|
|
||||||
|
expect(wrapper.vm.inShopProvidersAndTimeslots[0].timeSlots.days).toHaveLength(1);
|
||||||
|
expect(
|
||||||
|
wrapper.vm.inShopProvidersAndTimeslots[0].timeSlots.days[0].timeSlots[0].id
|
||||||
|
).toBe("slot-a");
|
||||||
|
expect(
|
||||||
|
wrapper.vm.inShopProvidersAndTimeslots[1].timeSlots.days[0].timeSlots[0].id
|
||||||
|
).toBe("slot-b");
|
||||||
|
wrapper.unmount();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -111,16 +111,45 @@ function appendDays(entry, newSlots) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a promise for inshop time slots for a single provider.
|
* Assigns time slots from a v2 multi-provider response onto in-shop provider entries.
|
||||||
* @param {{ startDate: string, endDate: string, providerNumber: string, pageNameToLog: string }} params
|
* @param {Array<{ provider: { providerNumber: string }, timeSlots: any }>} entries
|
||||||
|
* @param {{ providerTimeSlots?: Array<{ providerNumber: string, days: any[], provisionalTriggers?: string[] }> } | null | undefined} multiProviderResponse
|
||||||
*/
|
*/
|
||||||
function fetchInshopTimeSlots({ startDate, endDate, providerNumber, pageNameToLog }) {
|
function assignInshopTimeSlotsFromV2Response(entries, multiProviderResponse) {
|
||||||
return store.dispatch("getShopTimeSlots", {
|
const providerTimeSlots = multiProviderResponse?.providerTimeSlots ?? [];
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
entry.timeSlots =
|
||||||
|
providerTimeSlots.find((pts) => pts.providerNumber === entry.provider.providerNumber) ??
|
||||||
|
null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Appends days from a v2 multi-provider response onto existing in-shop provider entries.
|
||||||
|
* @param {Array<{ provider: { providerNumber: string }, timeSlots: any }>} entries
|
||||||
|
* @param {{ providerTimeSlots?: Array<{ providerNumber: string, days: any[] }> } | null | undefined} multiProviderResponse
|
||||||
|
*/
|
||||||
|
function appendInshopTimeSlotsFromV2Response(entries, multiProviderResponse) {
|
||||||
|
const providerTimeSlots = multiProviderResponse?.providerTimeSlots ?? [];
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
const newSlots = providerTimeSlots.find(
|
||||||
|
(pts) => pts.providerNumber === entry.provider.providerNumber
|
||||||
|
);
|
||||||
|
appendDays(entry, newSlots);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a promise for inshop time slots for multiple providers.
|
||||||
|
* @param {{ startDate: string, endDate: string, providerNumbers: string[], pageNameToLog: string }} params
|
||||||
|
*/
|
||||||
|
function fetchInshopTimeSlots({ startDate, endDate, providerNumbers, pageNameToLog }) {
|
||||||
|
return store.dispatch("getShopTimeSlotsV2", {
|
||||||
payload: {
|
payload: {
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
shopAppointmentType: "InshopOrDropoff",
|
shopAppointmentType: "InshopOrDropoff",
|
||||||
providerNumber,
|
providerNumbers,
|
||||||
},
|
},
|
||||||
pageNameToLog,
|
pageNameToLog,
|
||||||
});
|
});
|
||||||
|
|
@ -131,8 +160,12 @@ function fetchInshopTimeSlots({ startDate, endDate, providerNumber, pageNameToLo
|
||||||
* @param {{ startDate: string, endDate: string, zipCode: string, pageNameToLog: string }} params
|
* @param {{ startDate: string, endDate: string, zipCode: string, pageNameToLog: string }} params
|
||||||
*/
|
*/
|
||||||
function fetchMobileTimeSlots({ startDate, endDate, zipCode, pageNameToLog }) {
|
function fetchMobileTimeSlots({ startDate, endDate, zipCode, pageNameToLog }) {
|
||||||
return store.dispatch("getMobileTimeSlots", {
|
return store.dispatch("getMobileTimeSlotsV2", {
|
||||||
payload: { startDate, endDate, zipCode },
|
payload: {
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
zipCode,
|
||||||
|
},
|
||||||
pageNameToLog,
|
pageNameToLog,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -176,16 +209,21 @@ export default {
|
||||||
: null;
|
: null;
|
||||||
const startDate = toDateString(0);
|
const startDate = toDateString(0);
|
||||||
const endDate = toDateString(SCHEDULE_FETCH_DAYS - 1);
|
const endDate = toDateString(SCHEDULE_FETCH_DAYS - 1);
|
||||||
|
const providerNumbers = providers.map((provider) => provider.providerNumber);
|
||||||
const timeSlotsPromiseResultMap = [
|
const timeSlotsPromiseResultMap = [
|
||||||
...providers.map((provider, i) => ({
|
...(providerNumbers.length
|
||||||
resultKey: `inshopTimeSlots_${i}`,
|
? [
|
||||||
promise: fetchInshopTimeSlots({
|
{
|
||||||
startDate,
|
resultKey: "inshopTimeSlots",
|
||||||
endDate,
|
promise: fetchInshopTimeSlots({
|
||||||
providerNumber: provider.providerNumber,
|
startDate,
|
||||||
pageNameToLog: to.name,
|
endDate,
|
||||||
}),
|
providerNumbers,
|
||||||
})),
|
pageNameToLog: to.name,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
// Get the mobile time slots if a mobile provider number is available
|
// Get the mobile time slots if a mobile provider number is available
|
||||||
...(mobileProviderNumber
|
...(mobileProviderNumber
|
||||||
? [
|
? [
|
||||||
|
|
@ -202,9 +240,10 @@ export default {
|
||||||
: []),
|
: []),
|
||||||
];
|
];
|
||||||
const timeSlotsResultMap = await settleAllPromises(timeSlotsPromiseResultMap);
|
const timeSlotsResultMap = await settleAllPromises(timeSlotsPromiseResultMap);
|
||||||
vm.inShopProvidersAndTimeslots.forEach((entry, i) => {
|
assignInshopTimeSlotsFromV2Response(
|
||||||
entry.timeSlots = timeSlotsResultMap[`inshopTimeSlots_${i}`] ?? null;
|
vm.inShopProvidersAndTimeslots,
|
||||||
});
|
timeSlotsResultMap.inshopTimeSlots
|
||||||
|
);
|
||||||
if (vm.mobileProviderAndTimeSlot) {
|
if (vm.mobileProviderAndTimeSlot) {
|
||||||
vm.mobileProviderAndTimeSlot.timeSlots = timeSlotsResultMap.mobileTimeSlots ?? null;
|
vm.mobileProviderAndTimeSlot.timeSlots = timeSlotsResultMap.mobileTimeSlots ?? null;
|
||||||
}
|
}
|
||||||
|
|
@ -332,17 +371,24 @@ export default {
|
||||||
|
|
||||||
const newStartDate = toDateString(1, this.datePickerEndDate);
|
const newStartDate = toDateString(1, this.datePickerEndDate);
|
||||||
const newEndDate = toDateString(SCHEDULE_FETCH_DAYS, this.datePickerEndDate);
|
const newEndDate = toDateString(SCHEDULE_FETCH_DAYS, this.datePickerEndDate);
|
||||||
|
const providerNumbers = this.inShopProvidersAndTimeslots.map(
|
||||||
|
({ provider }) => provider.providerNumber
|
||||||
|
);
|
||||||
|
|
||||||
const promiseResultMap = [
|
const promiseResultMap = [
|
||||||
...this.inShopProvidersAndTimeslots.map(({ provider }, i) => ({
|
...(providerNumbers.length
|
||||||
resultKey: `inshopTimeSlots_${i}`,
|
? [
|
||||||
promise: fetchInshopTimeSlots({
|
{
|
||||||
startDate: newStartDate,
|
resultKey: "inshopTimeSlots",
|
||||||
endDate: newEndDate,
|
promise: fetchInshopTimeSlots({
|
||||||
providerNumber: provider.providerNumber,
|
startDate: newStartDate,
|
||||||
pageNameToLog: this.pageName,
|
endDate: newEndDate,
|
||||||
}),
|
providerNumbers,
|
||||||
})),
|
pageNameToLog: this.pageName,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []),
|
||||||
...(this.mobileProviderAndTimeSlot
|
...(this.mobileProviderAndTimeSlot
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
|
|
@ -360,10 +406,10 @@ export default {
|
||||||
|
|
||||||
const resultMap = await settleAllPromises(promiseResultMap);
|
const resultMap = await settleAllPromises(promiseResultMap);
|
||||||
|
|
||||||
// Provider order is fixed after beforeRouteEnter, so index keys are stable.
|
appendInshopTimeSlotsFromV2Response(
|
||||||
this.inShopProvidersAndTimeslots.forEach((entry, index) => {
|
this.inShopProvidersAndTimeslots,
|
||||||
appendDays(entry, resultMap[`inshopTimeSlots_${index}`]);
|
resultMap.inshopTimeSlots
|
||||||
});
|
);
|
||||||
|
|
||||||
if (this.mobileProviderAndTimeSlot) {
|
if (this.mobileProviderAndTimeSlot) {
|
||||||
appendDays(this.mobileProviderAndTimeSlot, resultMap.mobileTimeSlots);
|
appendDays(this.mobileProviderAndTimeSlot, resultMap.mobileTimeSlots);
|
||||||
|
|
|
||||||
|
|
@ -2543,6 +2543,149 @@ export const actions = {
|
||||||
return globalMethods.callHttpClient(options);
|
return globalMethods.callHttpClient(options);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getShopTimeSlotsV2(
|
||||||
|
context,
|
||||||
|
{ payload: { startDate, endDate, shopAppointmentType, providerNumbers }, pageNameToLog }
|
||||||
|
) {
|
||||||
|
const order = context.state.order;
|
||||||
|
const vehicle = context.state.order.vehicle;
|
||||||
|
const payment = context.state.order.payment;
|
||||||
|
|
||||||
|
const lineItems = getFlattenedLineItemsWithGlassPartTag(order.lineItems);
|
||||||
|
|
||||||
|
const glassPieces = order.damage.glassToReplace
|
||||||
|
? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
|
||||||
|
: [];
|
||||||
|
var payload = {
|
||||||
|
providerNumbers: providerNumbers,
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: endDate,
|
||||||
|
shopAppointmentType: shopAppointmentType,
|
||||||
|
applicationName: applicationConfig.APPLICATION_NAME,
|
||||||
|
parentAccountNumber: payment.parentAccountNumber,
|
||||||
|
carId: vehicle.carId,
|
||||||
|
lineItems: lineItems,
|
||||||
|
glassPieces: glassPieces,
|
||||||
|
eon: order.eon,
|
||||||
|
billToAccountNumber: context.getters.payment.billToAccountNumber,
|
||||||
|
coverage: {
|
||||||
|
status: payment.insuranceCoverage.coverageStatus,
|
||||||
|
deductible: order.policy.currentDeductible,
|
||||||
|
additionalAuthFlag: order.policy.additionalAuthFlag,
|
||||||
|
},
|
||||||
|
partSelection: {
|
||||||
|
hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length,
|
||||||
|
hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length,
|
||||||
|
hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length,
|
||||||
|
hasManuallySelectedParts:
|
||||||
|
!!context.state.applicationUser.pageData["vehicle-parts"]?.partsOrQuestions
|
||||||
|
.length,
|
||||||
|
},
|
||||||
|
vehicle: {
|
||||||
|
year: vehicle.year,
|
||||||
|
make: vehicle.make,
|
||||||
|
model: vehicle.model,
|
||||||
|
style: vehicle.style,
|
||||||
|
vin: vehicle.vin ?? "",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let hasCalled = timeSlotCallFlags.shopV2;
|
||||||
|
if (!hasCalled) {
|
||||||
|
timeSlotCallFlags.shopV2 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: endpoints.GetShopTimeSlotsV2.method,
|
||||||
|
endpoint: endpoints.GetShopTimeSlotsV2.url,
|
||||||
|
payload: payload,
|
||||||
|
logApiCall: true,
|
||||||
|
pageNameToLog: pageNameToLog,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only set handler if this is the very first call in this session
|
||||||
|
if (!hasCalled) {
|
||||||
|
options.additionalSuccessEventDataHandler = (response) => {
|
||||||
|
const firstProviderTimeSlots = response.data.providerTimeSlots?.[0];
|
||||||
|
return getTimeSlotsAdditionalEventData(
|
||||||
|
firstProviderTimeSlots?.provisionalTriggers,
|
||||||
|
order.serviceLocation.zipCode,
|
||||||
|
firstProviderTimeSlots?.days?.[0]?.date,
|
||||||
|
shopAppointmentType
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return globalMethods.callHttpClient(options);
|
||||||
|
},
|
||||||
|
|
||||||
|
getMobileTimeSlotsV2(context, { payload: { startDate, endDate, zipCode }, pageNameToLog }) {
|
||||||
|
const order = context.state.order;
|
||||||
|
const vehicle = context.state.order.vehicle;
|
||||||
|
const payment = context.state.order.payment;
|
||||||
|
const lineItems = getFlattenedLineItemsWithGlassPartTag(order.lineItems);
|
||||||
|
const glassPieces = order.damage.glassToReplace
|
||||||
|
? convertGlassPieceToBackEndCompatibleFormat(order.damage.glassToReplace)
|
||||||
|
: [];
|
||||||
|
var payload = {
|
||||||
|
startDate: startDate,
|
||||||
|
endDate: endDate,
|
||||||
|
applicationName: applicationConfig.APPLICATION_NAME,
|
||||||
|
parentAccountNumber: payment.parentAccountNumber,
|
||||||
|
carId: vehicle.carId,
|
||||||
|
lineItems: lineItems,
|
||||||
|
glassPieces: glassPieces,
|
||||||
|
eon: order.eon,
|
||||||
|
billToAccountNumber: context.getters.payment.billToAccountNumber,
|
||||||
|
coverage: {
|
||||||
|
status: payment.insuranceCoverage.coverageStatus,
|
||||||
|
deductible: order.policy.currentDeductible,
|
||||||
|
additionalAuthFlag: order.policy.additionalAuthFlag,
|
||||||
|
},
|
||||||
|
partSelection: {
|
||||||
|
hasAnsweredPartQuestions: !!order.damage.partQuestionAnswers?.length,
|
||||||
|
hasAnsweredMoldingQuestions: !!order.damage.moldingQuestionAnswers?.length,
|
||||||
|
hasAnsweredCapabilityQuestions: !!order.damage.capabilityQuestionAnswers?.length,
|
||||||
|
hasManuallySelectedParts:
|
||||||
|
!!context.state.applicationUser.pageData["vehicle-parts"]?.partsOrQuestions
|
||||||
|
.length,
|
||||||
|
},
|
||||||
|
vehicle: {
|
||||||
|
year: vehicle.year,
|
||||||
|
make: vehicle.make,
|
||||||
|
model: vehicle.model,
|
||||||
|
style: vehicle.style,
|
||||||
|
vin: vehicle.vin ?? "",
|
||||||
|
},
|
||||||
|
zipCode: zipCode ?? order.serviceLocation.zipCode,
|
||||||
|
};
|
||||||
|
|
||||||
|
let hasCalled = timeSlotCallFlags.mobileV2;
|
||||||
|
if (!hasCalled) {
|
||||||
|
timeSlotCallFlags.mobileV2 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
method: endpoints.GetMobileTimeSlotsV2.method,
|
||||||
|
endpoint: endpoints.GetMobileTimeSlotsV2.url,
|
||||||
|
payload: payload,
|
||||||
|
logApiCall: true,
|
||||||
|
pageNameToLog: pageNameToLog,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only set handler if this is the very first call in this session
|
||||||
|
if (!hasCalled) {
|
||||||
|
options.additionalSuccessEventDataHandler = (response) =>
|
||||||
|
getTimeSlotsAdditionalEventData(
|
||||||
|
response.data.provisionalTriggers,
|
||||||
|
order.serviceLocation.zipCode,
|
||||||
|
response.data.days?.[0]?.date
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return globalMethods.callHttpClient(options);
|
||||||
|
},
|
||||||
|
|
||||||
getMobilePremiumFee(context, { pageNameToLog }) {
|
getMobilePremiumFee(context, { pageNameToLog }) {
|
||||||
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
|
const damageType = context.getters.damage.isRepair ? "Repair" : "Replace";
|
||||||
const paymentType = context.getters.order.payment.isInsurance ? "Insurance" : "Cash";
|
const paymentType = context.getters.order.payment.isInsurance ? "Insurance" : "Cash";
|
||||||
|
|
@ -4530,6 +4673,8 @@ function saveExternalParameterState(externalParameterState) {
|
||||||
const timeSlotCallFlags = {
|
const timeSlotCallFlags = {
|
||||||
shop: false,
|
shop: false,
|
||||||
mobile: false,
|
mobile: false,
|
||||||
|
shopV2: false,
|
||||||
|
mobileV2: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
function checkIfMiscParts(partsOrQuestions) {
|
function checkIfMiscParts(partsOrQuestions) {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import store from "@/store";
|
||||||
import { mutations, state, actions, getters } from "@/store";
|
import { mutations, state, actions, getters } from "@/store";
|
||||||
import { storeMutations } from "@/constants/store-mutations";
|
import { storeMutations } from "@/constants/store-mutations";
|
||||||
import { storeActions } from "@/constants/store-actions";
|
import { storeActions } from "@/constants/store-actions";
|
||||||
|
import { endpoints } from "@/constants/endpoints.js";
|
||||||
import { sessionStorageKeyConstants } from "@/constants/session-storage";
|
import { sessionStorageKeyConstants } from "@/constants/session-storage";
|
||||||
import { experimentTriggers } from "@/constants/experiments";
|
import { experimentTriggers } from "@/constants/experiments";
|
||||||
import { routeData } from "@/router/constants/routes";
|
import { routeData } from "@/router/constants/routes";
|
||||||
|
|
@ -3631,6 +3632,71 @@ describe("Actions", () => {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("v2 time slot actions", () => {
|
||||||
|
const buildTimeSlotContext = () => ({
|
||||||
|
state,
|
||||||
|
getters: {
|
||||||
|
payment: { billToAccountNumber: "87291" },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
it("getShopTimeSlotsV2 calls v2 shop endpoint with providerNumbers and endDate", async () => {
|
||||||
|
const context = buildTimeSlotContext();
|
||||||
|
|
||||||
|
globalMethods.callHttpClient.mockResolvedValue({ data: { providerTimeSlots: [] } });
|
||||||
|
|
||||||
|
await actions.getShopTimeSlotsV2(context, {
|
||||||
|
payload: {
|
||||||
|
startDate: "2026-07-07",
|
||||||
|
endDate: "2026-07-21",
|
||||||
|
shopAppointmentType: "InshopOrDropoff",
|
||||||
|
providerNumbers: ["05018", "05019"],
|
||||||
|
},
|
||||||
|
pageNameToLog: "scheduling",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(globalMethods.callHttpClient).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
method: endpoints.GetShopTimeSlotsV2.method,
|
||||||
|
endpoint: endpoints.GetShopTimeSlotsV2.url,
|
||||||
|
payload: expect.objectContaining({
|
||||||
|
startDate: "2026-07-07",
|
||||||
|
endDate: "2026-07-21",
|
||||||
|
providerNumbers: ["05018", "05019"],
|
||||||
|
shopAppointmentType: "InshopOrDropoff",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("getMobileTimeSlotsV2 calls v2 mobile endpoint with endDate", async () => {
|
||||||
|
const context = buildTimeSlotContext();
|
||||||
|
|
||||||
|
globalMethods.callHttpClient.mockResolvedValue({ data: { days: [] } });
|
||||||
|
|
||||||
|
await actions.getMobileTimeSlotsV2(context, {
|
||||||
|
payload: {
|
||||||
|
startDate: "2026-07-07",
|
||||||
|
endDate: "2026-07-21",
|
||||||
|
zipCode: "43235",
|
||||||
|
},
|
||||||
|
pageNameToLog: "scheduling",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(globalMethods.callHttpClient).toHaveBeenCalledWith(
|
||||||
|
expect.objectContaining({
|
||||||
|
method: endpoints.GetMobileTimeSlotsV2.method,
|
||||||
|
endpoint: endpoints.GetMobileTimeSlotsV2.url,
|
||||||
|
payload: expect.objectContaining({
|
||||||
|
startDate: "2026-07-07",
|
||||||
|
endDate: "2026-07-21",
|
||||||
|
zipCode: "43235",
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("submittedStateRevision", () => {
|
describe("submittedStateRevision", () => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue