843 lines
24 KiB
JavaScript
843 lines
24 KiB
JavaScript
// @vitest-environment jsdom
|
|
import { computed, nextTick } from "vue";
|
|
import { mount } from "@vue/test-utils";
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const {
|
|
requestMock,
|
|
startDateRef,
|
|
endDateRef,
|
|
routeState,
|
|
periodRefreshSignalRef,
|
|
finishPeriodRefreshMock,
|
|
routerReplaceMock,
|
|
availableViewNamesRef,
|
|
currentViewRef,
|
|
sharedVariablesRef,
|
|
} = vi.hoisted(() => {
|
|
const { reactive, ref } = require("vue");
|
|
|
|
return {
|
|
requestMock: vi.fn(),
|
|
startDateRef: ref(new Date("2026-04-01T00:00:00.000Z")),
|
|
endDateRef: ref(new Date("2026-04-30T23:59:59.000Z")),
|
|
routeState: reactive({
|
|
query: {
|
|
activeTab: "period",
|
|
},
|
|
}),
|
|
periodRefreshSignalRef: ref({
|
|
sequence: 0,
|
|
customerNumbers: [],
|
|
invoiceCollectionIds: [],
|
|
}),
|
|
finishPeriodRefreshMock: vi.fn(),
|
|
routerReplaceMock: vi.fn(() => Promise.resolve()),
|
|
availableViewNamesRef: ref({}),
|
|
currentViewRef: ref("all"),
|
|
sharedVariablesRef: ref({}),
|
|
};
|
|
});
|
|
|
|
vi.mock("vue-router", () => ({
|
|
useRoute: () => routeState,
|
|
useRouter: () => ({
|
|
replace: routerReplaceMock,
|
|
}),
|
|
}));
|
|
|
|
vi.mock("@/components/session/token/SessionUser.vue", () => ({
|
|
SessionUser: {
|
|
request: requestMock,
|
|
objects: {
|
|
global: {
|
|
language: {
|
|
all: "All",
|
|
invoice_per_order: "Invoice per order",
|
|
fixed_price_arrangements: "Fixed pricing",
|
|
only_tank_cleaning: "Tank cleaning",
|
|
special_arrangements: "Special arrangements",
|
|
possible_duplicates: "Possible duplicates",
|
|
},
|
|
},
|
|
vehicles: {
|
|
columns: {
|
|
wash_subscription: {
|
|
label: "Vehicle subscriptions",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}));
|
|
|
|
vi.mock(
|
|
"@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/InvoicingBillingPeriodImportDates.vue",
|
|
() => ({
|
|
dates: {
|
|
variables: {
|
|
start: startDateRef,
|
|
end: endDateRef,
|
|
},
|
|
computed: {
|
|
isEntireMonth: computed(() => true),
|
|
formattedStartDate: computed(() => startDateRef.value.toISOString().split("T")[0]),
|
|
formattedEndDate: computed(() => endDateRef.value.toISOString().split("T")[0]),
|
|
},
|
|
},
|
|
})
|
|
);
|
|
|
|
vi.mock(
|
|
"@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/InvoicingBillingPeriodImportView.vue",
|
|
() => ({
|
|
view: {
|
|
variables: {
|
|
sharedVariables: sharedVariablesRef,
|
|
currentView: currentViewRef,
|
|
availableViewNames: availableViewNamesRef,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
|
|
vi.mock(
|
|
"@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/InvoicingBillingPeriodImportInvoiceQueue.vue",
|
|
() => ({
|
|
invoiceQueue: {
|
|
periodRefreshSignal: periodRefreshSignalRef,
|
|
finishPeriodRefresh: finishPeriodRefreshMock,
|
|
},
|
|
})
|
|
);
|
|
|
|
import Right from "@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/displays/layout/Right.vue";
|
|
import {
|
|
periodPaging,
|
|
resetPeriodPagingState,
|
|
} from "@/views/dashboards/superUserDashboard/InvoicingBillingPeriod/imports/InvoicingBillingPeriodImportPaging.js";
|
|
|
|
const flushAll = async () => {
|
|
await nextTick();
|
|
await Promise.resolve();
|
|
await nextTick();
|
|
};
|
|
|
|
let mountedWrappers = [];
|
|
|
|
const mountRight = () => {
|
|
const wrapper = mount(Right, {
|
|
global: {
|
|
stubs: {
|
|
WhiteBox: { template: "<div><slot /></div>" },
|
|
ColorIndicator: { props: ["label"], template: "<div>{{ label?.text }}</div>" },
|
|
},
|
|
},
|
|
});
|
|
mountedWrappers.push(wrapper);
|
|
return wrapper;
|
|
};
|
|
|
|
const emptyPeriodResponse = () => ({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const selfWashResponse = (rows = [], total = rows.length) => ({
|
|
data: {
|
|
data: rows,
|
|
meta: {
|
|
pagination: {
|
|
total,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const mockPeriodResponses = (...responses) => {
|
|
let periodResponseIndex = 0;
|
|
requestMock.mockImplementation((url) => {
|
|
if (url === "/superuser/invoicing/period") {
|
|
const response = responses[periodResponseIndex] ?? responses[responses.length - 1] ?? emptyPeriodResponse();
|
|
periodResponseIndex += 1;
|
|
return Promise.resolve(response);
|
|
}
|
|
|
|
return Promise.resolve(selfWashResponse());
|
|
});
|
|
};
|
|
|
|
const periodCalls = () => requestMock.mock.calls.filter(([url]) => url === "/superuser/invoicing/period");
|
|
const selfWashCalls = () => requestMock.mock.calls.filter(([url]) => url === "/modules/xlvask/services/usage/orders");
|
|
|
|
describe("Invoicing period queue-driven refresh", () => {
|
|
beforeEach(() => {
|
|
requestMock.mockReset();
|
|
mockPeriodResponses(emptyPeriodResponse());
|
|
routeState.query.activeTab = "period";
|
|
delete routeState.query.periodView;
|
|
delete routeState.query.periodSearch;
|
|
delete routeState.query.periodPage;
|
|
delete routeState.query.periodLimit;
|
|
periodRefreshSignalRef.value = {
|
|
sequence: 0,
|
|
customerNumbers: [],
|
|
invoiceCollectionIds: [],
|
|
};
|
|
finishPeriodRefreshMock.mockReset();
|
|
routerReplaceMock.mockClear();
|
|
resetPeriodPagingState();
|
|
sharedVariablesRef.value = {};
|
|
availableViewNamesRef.value = {};
|
|
currentViewRef.value = "all";
|
|
});
|
|
|
|
afterEach(() => {
|
|
mountedWrappers.forEach((wrapper) => wrapper.unmount());
|
|
mountedWrappers = [];
|
|
});
|
|
|
|
it("refreshes the current paginated page when queue activity reaches a terminal state", async () => {
|
|
mockPeriodResponses(
|
|
{
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [
|
|
{ customer_number: 1001, customer_name: "Keep Customer", transactions: [], expanded: true },
|
|
{ customer_number: 1002, customer_name: "Old Customer", transactions: [], expanded: true },
|
|
{ customer_number: 1003, customer_name: "After Customer", transactions: [] },
|
|
],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [{ customer_number: 1001, customer_name: "Keep Customer", transactions: [] }],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [
|
|
{ customer_number: 1001, customer_name: "Keep Customer", transactions: [], expanded: true },
|
|
{ customer_number: 1002, customer_name: "Updated Customer", transactions: [], expanded: true },
|
|
{ customer_number: 1003, customer_name: "After Customer", transactions: [] },
|
|
],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [{ customer_number: 1001, customer_name: "Keep Customer", transactions: [] }],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
},
|
|
}
|
|
);
|
|
|
|
mountRight();
|
|
await flushAll();
|
|
|
|
expect(periodCalls()).toHaveLength(1);
|
|
|
|
periodRefreshSignalRef.value = {
|
|
sequence: 1,
|
|
customerNumbers: [1002],
|
|
invoiceCollectionIds: [3002],
|
|
};
|
|
await flushAll();
|
|
|
|
expect(periodCalls()).toHaveLength(2);
|
|
expect(periodCalls().at(-1)).toEqual([
|
|
"/superuser/invoicing/period",
|
|
"GET",
|
|
{
|
|
dateFrom: "2026-04-01",
|
|
dateTo: "2026-04-30",
|
|
periodView: "all",
|
|
page: 1,
|
|
limit: 100,
|
|
search: "",
|
|
flagTab: "all",
|
|
includeRequiresAction: 1,
|
|
includeBooked: 1,
|
|
},
|
|
]);
|
|
expect(sharedVariablesRef.value.types.all.map((customer) => customer.customer_number)).toEqual([1001, 1002, 1003]);
|
|
expect(sharedVariablesRef.value.types.all[0].expanded).toBe(true);
|
|
expect(sharedVariablesRef.value.types.all[1].customer_name).toBe("Updated Customer");
|
|
expect(sharedVariablesRef.value.types.all[1].expanded).toBe(true);
|
|
expect(sharedVariablesRef.value.types.fixed_pricing.map((customer) => customer.customer_number)).toEqual([1001]);
|
|
expect(finishPeriodRefreshMock).toHaveBeenCalledWith([1002], [3002]);
|
|
});
|
|
|
|
it("requests the active period page and renders selector counts from backend type_counts", async () => {
|
|
mockPeriodResponses({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [{ customer_number: 2001, customer_name: "Visible Customer", transactions: [] }],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
type_counts: {
|
|
all: {
|
|
requires_action: 2,
|
|
draft: 1,
|
|
manual_flags: 0,
|
|
automatic_flags: 0,
|
|
completed: 37,
|
|
total: 40,
|
|
},
|
|
},
|
|
},
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: 100,
|
|
total: 40,
|
|
search: "",
|
|
filters: {
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
},
|
|
order: null,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const wrapper = mountRight();
|
|
await flushAll();
|
|
|
|
expect(periodCalls()).toContainEqual([
|
|
"/superuser/invoicing/period",
|
|
"GET",
|
|
{
|
|
dateFrom: "2026-04-01",
|
|
dateTo: "2026-04-30",
|
|
periodView: "all",
|
|
page: 1,
|
|
limit: 100,
|
|
search: "",
|
|
flagTab: "all",
|
|
includeRequiresAction: 1,
|
|
includeBooked: 1,
|
|
},
|
|
]);
|
|
expect(wrapper.get("[data-testid='invoicing-period-view-selector-all']").text()).toContain("All (37/40)");
|
|
});
|
|
|
|
it("preloads the next active page and keeps current plus previous month cache warm", async () => {
|
|
vi.useFakeTimers();
|
|
vi.setSystemTime(new Date("2026-05-12T10:00:00.000Z"));
|
|
try {
|
|
mockPeriodResponses({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [{ customer_number: 2501, customer_name: "Warm Customer", transactions: [] }],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: 100,
|
|
total: 250,
|
|
search: "",
|
|
filters: {
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
},
|
|
order: null,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
mountRight();
|
|
await flushAll();
|
|
expect(periodCalls()).toHaveLength(1);
|
|
|
|
await vi.advanceTimersByTimeAsync(100);
|
|
await flushAll();
|
|
|
|
const requestedParams = periodCalls().map(([, , params]) => params);
|
|
expect(requestedParams).toContainEqual({
|
|
dateFrom: "2026-04-01",
|
|
dateTo: "2026-04-30",
|
|
periodView: "all",
|
|
page: 2,
|
|
limit: 100,
|
|
search: "",
|
|
flagTab: "all",
|
|
includeRequiresAction: 1,
|
|
includeBooked: 1,
|
|
periodWarm: 1,
|
|
});
|
|
expect(requestedParams).toContainEqual({
|
|
dateFrom: "2026-05-01",
|
|
dateTo: "2026-05-31",
|
|
periodView: "all",
|
|
page: 1,
|
|
limit: 100,
|
|
search: "",
|
|
includeRequiresAction: 1,
|
|
includeBooked: 1,
|
|
periodWarm: 1,
|
|
});
|
|
} finally {
|
|
vi.useRealTimers();
|
|
}
|
|
});
|
|
|
|
it("renders cached period data while a fresh request is still pending", async () => {
|
|
mockPeriodResponses({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [{ customer_number: 3001, customer_name: "Cached Customer", transactions: [] }],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
type_counts: {
|
|
all: {
|
|
requires_action: 0,
|
|
draft: 0,
|
|
manual_flags: 0,
|
|
automatic_flags: 0,
|
|
completed: 1,
|
|
total: 1,
|
|
},
|
|
},
|
|
},
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: 100,
|
|
total: 1,
|
|
search: "",
|
|
filters: {
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
},
|
|
order: null,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
const firstWrapper = mountRight();
|
|
await flushAll();
|
|
firstWrapper.unmount();
|
|
mountedWrappers = mountedWrappers.filter((wrapper) => wrapper !== firstWrapper);
|
|
|
|
sharedVariablesRef.value = {};
|
|
requestMock.mockImplementation((url) => {
|
|
if (url === "/superuser/invoicing/period") {
|
|
return new Promise(() => {});
|
|
}
|
|
return Promise.resolve(selfWashResponse());
|
|
});
|
|
|
|
const secondWrapper = mountRight();
|
|
await flushAll();
|
|
|
|
expect(periodCalls()).toHaveLength(2);
|
|
expect(sharedVariablesRef.value.types.all[0].customer_name).toBe("Cached Customer");
|
|
expect(secondWrapper.get("[data-testid='invoicing-period-view-selector-all']").text()).toContain("All (1/1)");
|
|
});
|
|
|
|
it("keeps the current customer list visible while an uncached reload is pending", async () => {
|
|
let resolveReload;
|
|
requestMock.mockImplementation((url, _method, params = {}) => {
|
|
if (url !== "/superuser/invoicing/period") {
|
|
return Promise.resolve(selfWashResponse());
|
|
}
|
|
|
|
if (params.search === "Beta") {
|
|
return new Promise((resolve) => {
|
|
resolveReload = resolve;
|
|
});
|
|
}
|
|
|
|
return Promise.resolve({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [{ customer_number: 3101, customer_name: "Current Customer", transactions: [] }],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: 100,
|
|
total: 1,
|
|
search: "",
|
|
filters: {
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
},
|
|
order: null,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
mountRight();
|
|
await flushAll();
|
|
|
|
periodPaging.search = "Beta";
|
|
await flushAll();
|
|
|
|
expect(sharedVariablesRef.value.types.all[0].customer_name).toBe("Current Customer");
|
|
expect(periodPaging.isRefreshing).toBe(true);
|
|
|
|
resolveReload({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [{ customer_number: 3102, customer_name: "Reloaded Customer", transactions: [] }],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: 100,
|
|
total: 1,
|
|
search: "Beta",
|
|
filters: {
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
},
|
|
order: null,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await flushAll();
|
|
|
|
expect(sharedVariablesRef.value.types.all[0].customer_name).toBe("Reloaded Customer");
|
|
expect(periodPaging.isRefreshing).toBe(false);
|
|
});
|
|
|
|
it("keeps the current right-menu view rendered until the switched view is ready", async () => {
|
|
let resolveViewSwitch;
|
|
requestMock.mockImplementation((url, _method, params = {}) => {
|
|
if (url !== "/superuser/invoicing/period") {
|
|
return Promise.resolve(selfWashResponse());
|
|
}
|
|
|
|
if (params.periodView === "fixed_pricing") {
|
|
return new Promise((resolve) => {
|
|
resolveViewSwitch = resolve;
|
|
});
|
|
}
|
|
|
|
return Promise.resolve({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [{ customer_number: 3201, customer_name: "All Customer", transactions: [] }],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: 100,
|
|
total: 1,
|
|
search: "",
|
|
filters: {
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
},
|
|
order: null,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
const wrapper = mountRight();
|
|
await flushAll();
|
|
|
|
await wrapper.get("[data-testid='invoicing-period-view-selector-fixed_pricing']").trigger("click");
|
|
await flushAll();
|
|
|
|
expect(currentViewRef.value).toBe("all");
|
|
expect(sharedVariablesRef.value.types.all[0].customer_name).toBe("All Customer");
|
|
expect(periodPaging.isRefreshing).toBe(true);
|
|
|
|
resolveViewSwitch({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [{ customer_number: 3202, customer_name: "Fixed Customer", transactions: [] }],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: 100,
|
|
total: 1,
|
|
search: "",
|
|
filters: {
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
},
|
|
order: null,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await flushAll();
|
|
|
|
expect(currentViewRef.value).toBe("fixed_pricing");
|
|
expect(sharedVariablesRef.value.types.fixed_pricing[0].customer_name).toBe("Fixed Customer");
|
|
expect(periodPaging.isRefreshing).toBe(false);
|
|
});
|
|
|
|
it("keeps the current right-menu view when a background view switch fails", async () => {
|
|
let rejectViewSwitch;
|
|
requestMock.mockImplementation((url, _method, params = {}) => {
|
|
if (url !== "/superuser/invoicing/period") {
|
|
return Promise.resolve(selfWashResponse());
|
|
}
|
|
|
|
if (params.periodView === "fixed_pricing") {
|
|
return new Promise((_resolve, reject) => {
|
|
rejectViewSwitch = reject;
|
|
});
|
|
}
|
|
|
|
return Promise.resolve({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [{ customer_number: 3211, customer_name: "Stable Customer", transactions: [] }],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: 100,
|
|
total: 1,
|
|
search: "",
|
|
filters: {
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
},
|
|
order: null,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
const wrapper = mountRight();
|
|
await flushAll();
|
|
|
|
await wrapper.get("[data-testid='invoicing-period-view-selector-fixed_pricing']").trigger("click");
|
|
await flushAll();
|
|
|
|
rejectViewSwitch(new Error("Reload failed"));
|
|
await flushAll();
|
|
|
|
expect(currentViewRef.value).toBe("all");
|
|
expect(sharedVariablesRef.value.types.all[0].customer_name).toBe("Stable Customer");
|
|
expect(periodPaging.isRefreshing).toBe(false);
|
|
});
|
|
|
|
it("reuses cached Selvvask selector counts without repeating the broad usage-order request", async () => {
|
|
requestMock.mockImplementation((url) => {
|
|
if (url === "/superuser/invoicing/period") {
|
|
return Promise.resolve(emptyPeriodResponse());
|
|
}
|
|
|
|
return Promise.resolve(
|
|
selfWashResponse(
|
|
[
|
|
{ id: 8101, linked_order_id: 7001 },
|
|
{ id: 8102, linked_order_id: null },
|
|
],
|
|
2
|
|
)
|
|
);
|
|
});
|
|
|
|
const firstWrapper = mountRight();
|
|
await flushAll();
|
|
|
|
expect(firstWrapper.get("[data-testid='invoicing-period-view-selector-self_wash']").text()).toContain(
|
|
"Selvvask (1/2)"
|
|
);
|
|
expect(selfWashCalls()).toHaveLength(1);
|
|
firstWrapper.unmount();
|
|
mountedWrappers = mountedWrappers.filter((wrapper) => wrapper !== firstWrapper);
|
|
|
|
requestMock.mockImplementation((url) => {
|
|
if (url === "/superuser/invoicing/period") {
|
|
return new Promise(() => {});
|
|
}
|
|
|
|
return Promise.resolve(selfWashResponse([], 0));
|
|
});
|
|
|
|
const secondWrapper = mountRight();
|
|
await flushAll();
|
|
|
|
expect(secondWrapper.get("[data-testid='invoicing-period-view-selector-self_wash']").text()).toContain(
|
|
"Selvvask (1/2)"
|
|
);
|
|
expect(selfWashCalls()).toHaveLength(1);
|
|
});
|
|
|
|
it("ignores stale period responses that resolve after a newer search request", async () => {
|
|
const deferred = [];
|
|
requestMock.mockImplementation((url) => {
|
|
if (url !== "/superuser/invoicing/period") {
|
|
return Promise.resolve(selfWashResponse());
|
|
}
|
|
return new Promise((resolve) => {
|
|
deferred.push(resolve);
|
|
});
|
|
});
|
|
|
|
mountRight();
|
|
await flushAll();
|
|
|
|
const callsBeforeSearch = periodCalls().length;
|
|
periodPaging.search = "new";
|
|
await flushAll();
|
|
|
|
expect(periodCalls().length).toBeGreaterThan(callsBeforeSearch);
|
|
const latestDeferredIndex = deferred.length - 1;
|
|
|
|
deferred[latestDeferredIndex]({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [{ customer_number: 4002, customer_name: "New Result", transactions: [] }],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: 100,
|
|
total: 1,
|
|
search: "new",
|
|
filters: {
|
|
includeRequiresAction: true,
|
|
includeBooked: true,
|
|
},
|
|
order: null,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
await flushAll();
|
|
|
|
for (let index = 0; index < latestDeferredIndex; index += 1) {
|
|
deferred[index]({
|
|
data: {
|
|
data: {
|
|
types: {
|
|
all: [{ customer_number: 4001, customer_name: "Old Result", transactions: [] }],
|
|
invoice_per_order: [],
|
|
fixed_pricing: [],
|
|
tank_cleaning: [],
|
|
special_arrangements: [],
|
|
vehicle_subscriptions: [],
|
|
possible_duplicates: [],
|
|
},
|
|
},
|
|
},
|
|
});
|
|
}
|
|
await flushAll();
|
|
|
|
expect(sharedVariablesRef.value.types.all[0].customer_name).toBe("New Result");
|
|
});
|
|
|
|
it("refreshes when the Period tab becomes active after being in another tab", async () => {
|
|
routeState.query.activeTab = "overview";
|
|
|
|
mountRight();
|
|
await flushAll();
|
|
|
|
const initialCallCount = requestMock.mock.calls.length;
|
|
expect(initialCallCount).toBeGreaterThanOrEqual(1);
|
|
|
|
routeState.query.activeTab = "period";
|
|
await flushAll();
|
|
|
|
expect(requestMock.mock.calls.length).toBeGreaterThan(initialCallCount);
|
|
});
|
|
});
|