- Introduced `.prettierrc.json` to enforce consistent code formatting across the project. - Updated unit and e2e test files to address formatting issues, improve readability, and ensure alignment with the new Prettier configuration.
250 lines
7.6 KiB
JavaScript
250 lines
7.6 KiB
JavaScript
import { nextTick } from "vue";
|
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
|
|
const getOverviewMock = vi.hoisted(() => vi.fn());
|
|
|
|
vi.mock("@/components/session/token/SessionUser.vue", () => ({
|
|
SessionUser: {
|
|
objects: {
|
|
department_daily_reports: {
|
|
functions: {
|
|
getOverview: getOverviewMock,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}));
|
|
|
|
const flushAll = async () => {
|
|
await nextTick();
|
|
await Promise.resolve();
|
|
await nextTick();
|
|
await Promise.resolve();
|
|
};
|
|
|
|
const deferred = () => {
|
|
let resolve;
|
|
let reject;
|
|
const promise = new Promise((nextResolve, nextReject) => {
|
|
resolve = nextResolve;
|
|
reject = nextReject;
|
|
});
|
|
|
|
return { promise, resolve, reject };
|
|
};
|
|
|
|
const makeOverview = ({
|
|
transactions = 0,
|
|
productsSold = 0,
|
|
revenue = 0,
|
|
washes = 0,
|
|
waterUsage = 0,
|
|
bookings = 0,
|
|
bookingsOutOf = 0,
|
|
complaints = 0,
|
|
complaintsState = "ready",
|
|
complaintsMessage = null,
|
|
nightWashesState = "ready",
|
|
nightWashes = 0,
|
|
nightWashesBySource = { orders: 0, xlvask: 0, selfserve: 0 },
|
|
nightWashesMissingDepartmentIds = [],
|
|
overtimeState = "ready",
|
|
overtime = 0,
|
|
} = {}) => ({
|
|
data: {
|
|
data: {
|
|
metrics: {
|
|
transactions: { state: "ready", value: transactions, out_of: null, message: null },
|
|
products_sold: { state: "ready", value: productsSold, out_of: null, message: null },
|
|
revenue: { state: "ready", value: revenue, out_of: null, message: null },
|
|
washes: { state: "ready", value: washes, out_of: null, message: null },
|
|
water_usage: { state: "ready", value: waterUsage, out_of: null, message: null },
|
|
bookings: { state: "ready", value: bookings, out_of: bookingsOutOf, message: null },
|
|
complaints: { state: complaintsState, value: complaints, out_of: null, message: complaintsMessage },
|
|
night_washes: {
|
|
state: nightWashesState,
|
|
value: nightWashes,
|
|
out_of: null,
|
|
message: null,
|
|
by_source: nightWashesBySource,
|
|
has_missing_opening_hours: nightWashesMissingDepartmentIds.length > 0,
|
|
missing_department_ids: nightWashesMissingDepartmentIds,
|
|
},
|
|
overtime: {
|
|
state: overtimeState,
|
|
value: overtime,
|
|
out_of: null,
|
|
message: overtimeState === "ready" ? null : "Overarbejde kræver Workfeed-kobling.",
|
|
},
|
|
},
|
|
products: [
|
|
{
|
|
product_id: 24,
|
|
slug: "spot-free-lastbil",
|
|
title: "Spot Free (Lastbil)",
|
|
state: "ready",
|
|
value: 3,
|
|
out_of: 9,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
});
|
|
|
|
describe("DepartmentDailyReportObject behavior", () => {
|
|
beforeEach(() => {
|
|
vi.resetModules();
|
|
getOverviewMock.mockReset();
|
|
});
|
|
|
|
it("fetches once per filter change and lets the latest overview response win", async () => {
|
|
const firstRequest = deferred();
|
|
const secondRequest = deferred();
|
|
getOverviewMock.mockReturnValueOnce(firstRequest.promise).mockReturnValueOnce(secondRequest.promise);
|
|
|
|
const module = await import(
|
|
"@/views/dashboards/departmentDashboard/modules/daily-report/DepartmentDailyReportObject.vue"
|
|
);
|
|
|
|
module.initializeDailyReportFilters({
|
|
department_ids: [1],
|
|
date: "2026-03-23",
|
|
date_to: "2026-03-23",
|
|
});
|
|
await flushAll();
|
|
|
|
expect(getOverviewMock).toHaveBeenCalledTimes(1);
|
|
expect(getOverviewMock).toHaveBeenLastCalledWith([1], "2026-03-23", "2026-03-23");
|
|
|
|
module.selectDate("2026-03-23", "2026-03-24");
|
|
await flushAll();
|
|
|
|
expect(getOverviewMock).toHaveBeenCalledTimes(2);
|
|
expect(getOverviewMock).toHaveBeenLastCalledWith([1], "2026-03-23", "2026-03-24");
|
|
|
|
secondRequest.resolve(
|
|
makeOverview({
|
|
transactions: 9,
|
|
productsSold: 18,
|
|
revenue: 4200,
|
|
washes: 11,
|
|
waterUsage: 34,
|
|
bookings: 5,
|
|
bookingsOutOf: 7,
|
|
complaints: 2,
|
|
nightWashes: 2,
|
|
nightWashesBySource: { orders: 1, xlvask: 0, selfserve: 1 },
|
|
nightWashesMissingDepartmentIds: [7],
|
|
overtime: 1.25,
|
|
})
|
|
);
|
|
await flushAll();
|
|
|
|
firstRequest.resolve(
|
|
makeOverview({
|
|
transactions: 1,
|
|
productsSold: 2,
|
|
revenue: 300,
|
|
washes: 1,
|
|
waterUsage: 3,
|
|
bookings: 1,
|
|
bookingsOutOf: 1,
|
|
nightWashes: 0,
|
|
nightWashesBySource: { orders: 0, xlvask: 0, selfserve: 0 },
|
|
overtime: 0.25,
|
|
})
|
|
);
|
|
await flushAll();
|
|
|
|
expect(module.count_transactions.value).toBe(9);
|
|
expect(module.count_products_sold.value).toBe(18);
|
|
expect(module.count_earnings.value).toBe(4200);
|
|
expect(module.count_washes.value).toBe(11);
|
|
expect(module.count_water_usage.value).toBe(34);
|
|
expect(module.count_bookings.value).toBe(5);
|
|
expect(module.count_bookings_out_of.value).toBe(7);
|
|
expect(module.count_complaints.value).toBe(2);
|
|
expect(module.count_night_washes.value).toBe(2);
|
|
expect(module.night_washes_by_source.value).toEqual({ orders: 1, xlvask: 0, selfserve: 1 });
|
|
expect(module.night_washes_has_missing_opening_hours.value).toBe(true);
|
|
expect(module.night_washes_missing_department_ids.value).toEqual([7]);
|
|
expect(module.count_overtime.value).toBe(1.25);
|
|
});
|
|
|
|
it("keeps unavailable complaint and overtime tiles out of fake zero state", async () => {
|
|
getOverviewMock.mockResolvedValue(
|
|
makeOverview({
|
|
transactions: 4,
|
|
productsSold: 12,
|
|
revenue: 1800,
|
|
washes: 6,
|
|
waterUsage: 22,
|
|
bookings: 3,
|
|
bookingsOutOf: 4,
|
|
complaintsState: "unavailable",
|
|
complaintsMessage: "Kundeklager er ikke tilgængelig endnu.",
|
|
nightWashesState: "ready",
|
|
nightWashes: 1,
|
|
nightWashesBySource: { orders: 0, xlvask: 1, selfserve: 0 },
|
|
overtimeState: "unavailable",
|
|
overtime: 0,
|
|
})
|
|
);
|
|
|
|
const module = await import(
|
|
"@/views/dashboards/departmentDashboard/modules/daily-report/DepartmentDailyReportObject.vue"
|
|
);
|
|
|
|
module.initializeDailyReportFilters({
|
|
department_ids: [2],
|
|
date: "2026-03-25",
|
|
date_to: "2026-03-25",
|
|
});
|
|
await flushAll();
|
|
|
|
expect(module.complaints_metric_state.value.state).toBe("unavailable");
|
|
expect(module.complaints_metric_state.value.message).toContain("ikke tilgængelig");
|
|
expect(module.count_complaints.value).toBe(0);
|
|
expect(module.overtime_metric_state.value.state).toBe("unavailable");
|
|
expect(module.count_overtime.value).toBeNull();
|
|
expect(module.count_night_washes.value).toBe(1);
|
|
expect(module.night_washes_by_source.value).toEqual({ orders: 0, xlvask: 1, selfserve: 0 });
|
|
expect(module.night_washes_has_missing_opening_hours.value).toBe(false);
|
|
expect(module.night_washes_missing_department_ids.value).toEqual([]);
|
|
expect(module.daily_report_products.value[24]).toMatchObject({
|
|
slug: "spot-free-lastbil",
|
|
value: 3,
|
|
out_of: 9,
|
|
});
|
|
});
|
|
|
|
it("exposes an explicit overview refresh helper", async () => {
|
|
getOverviewMock.mockResolvedValue(
|
|
makeOverview({
|
|
transactions: 7,
|
|
complaints: 3,
|
|
})
|
|
);
|
|
|
|
const module = await import(
|
|
"@/views/dashboards/departmentDashboard/modules/daily-report/DepartmentDailyReportObject.vue"
|
|
);
|
|
|
|
module.initializeDailyReportFilters({
|
|
department_ids: [3],
|
|
date: "2026-03-26",
|
|
date_to: "2026-03-26",
|
|
});
|
|
await flushAll();
|
|
|
|
getOverviewMock.mockClear();
|
|
|
|
await module.refreshOverview();
|
|
await flushAll();
|
|
|
|
expect(getOverviewMock).toHaveBeenCalledTimes(1);
|
|
expect(getOverviewMock).toHaveBeenCalledWith([3], "2026-03-26", "2026-03-26");
|
|
expect(module.count_complaints.value).toBe(3);
|
|
});
|
|
});
|