Files
pleno-vue/tests/unit/pos-last-scanned-license-plates.spec.js
Jeppe Bundgaard b39b458f4f Add .prettierrc.json and refactor test files for improved formatting consistency:
- 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.
2026-04-13 09:13:27 +02:00

345 lines
9.5 KiB
JavaScript

// @vitest-environment jsdom
import { nextTick, ref } from "vue";
import { beforeEach, afterEach, describe, expect, it, vi } from "vitest";
import { mountWithApp } from "./helpers/mountWithApp.js";
const paginationState = vi.hoisted(() => ({
list: null,
metaCurrentPage: null,
metaItemsPerPage: null,
metaTotalItems: null,
metaSearch: null,
loadList: vi.fn(),
setEndpoint: vi.fn(),
setMetaItemsPerPage: vi.fn(),
setPage: vi.fn(),
search: vi.fn(),
setFilter: vi.fn(),
setOrder: vi.fn(),
}));
const posState = vi.hoisted(() => ({
departmentId: null,
getDepartment: vi.fn(() => 12),
selectScan: vi.fn(),
}));
const requestMock = vi.hoisted(() => vi.fn());
const lookupMock = vi.hoisted(() => vi.fn());
vi.mock("@/components/pagination/paginatedList.vue", async () => {
paginationState.list = ref([]);
paginationState.metaCurrentPage = ref(1);
paginationState.metaItemsPerPage = ref(10);
paginationState.metaTotalItems = ref(0);
paginationState.metaSearch = ref("");
return {
list: paginationState.list,
metaCurrentPage: paginationState.metaCurrentPage,
metaItemsPerPage: paginationState.metaItemsPerPage,
metaTotalItems: paginationState.metaTotalItems,
metaSearch: paginationState.metaSearch,
loadList: paginationState.loadList,
setEndpoint: paginationState.setEndpoint,
setMetaItemsPerPage: paginationState.setMetaItemsPerPage,
setPage: paginationState.setPage,
search: paginationState.search,
setFilter: paginationState.setFilter,
setOrder: paginationState.setOrder,
};
});
vi.mock("@/components/shop/POSDepartmentProcess.vue", async () => {
posState.departmentId = ref(12);
return {
department_id: posState.departmentId,
getDepartment: posState.getDepartment,
selectScan: posState.selectScan,
};
});
vi.mock("@/components/session/token/SessionUser.vue", () => ({
SessionUser: {
request: requestMock,
functions: {
date: {
toLocalTimeHours: vi.fn((value) => String(value).slice(11, 16)),
},
},
objects: {
order_bookings: {
meta: {
endpoint: "/order-bookings",
},
},
orders: {
columns: {
reg_1: {
label: "License plate",
},
},
},
global: {
language: {
nothing_left_to_show: "Nothing left to show",
},
},
},
superUser: {
modules: {
motorapi: {
functions: {
lookup: lookupMock,
},
},
},
},
},
}));
vi.mock("@/ThemeConfig.vue", () => ({
Colors: {
inputs: {
default: {
backgroundColor: "#ffffff",
textColor: "#22364d",
},
},
},
}));
vi.mock("@vueuse/core", async () => {
const { ref } = await import("vue");
return {
useElementVisibility: vi.fn(() => ref(false)),
};
});
import PosLastScannedLicensePlatesV2 from "@/components/displays/department/pos/PosLastScannedLicensePlatesV2.vue";
let consoleErrorSpy;
const messages = {
en: {
admin: {
pos: {
customer: "Customer",
customer_number: "Customer number",
booking: "Booking",
license_plate: "License plate",
loading: "Loading...",
make: "Make",
model: "Model",
no_plates_found: "No license plates found",
recent_scans: "Recent scans",
recent_scans_helper: "Select a license plate to view vehicle details.",
recent_scan_details_empty: "No vehicle details are available for this scan.",
recent_scan_details_error: "Vehicle details could not be loaded. Please try another scan.",
recent_scan_details_loading: "Loading vehicle details...",
scanner: "Scanner",
status_barred: "Barred",
status_booking: "Booking",
status_unknown: "New",
try_again: "Try again",
type: "Type",
use: "Use",
variant: "Variant",
},
},
global: {
search_license_plate: "Search for license plate",
},
},
};
const scans = [
{
id: 801,
plate: "AB12345",
plate_scanner_id: 1,
created_at: "2026-04-08 08:44:07",
customer_number: 12345679,
customer_name: "Pleno Logistics",
seen_before: true,
barred: false,
},
{
id: 802,
plate: "CD67890",
plate_scanner_id: 2,
created_at: "2026-04-08 08:31:05",
customer_number: null,
customer_name: "",
seen_before: false,
barred: false,
},
];
const flushUi = async () => {
await Promise.resolve();
await Promise.resolve();
await nextTick();
};
function buildLookupResponse(overrides = {}) {
return {
data: {
data: {
make: "RENAULT",
model: "Captur",
variant: "dCi 90",
type: "Personbil",
use: "Privat personkørsel",
...overrides,
},
},
};
}
describe("PosLastScannedLicensePlatesV2", () => {
beforeEach(() => {
vi.useFakeTimers();
consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
paginationState.list.value = [];
paginationState.metaCurrentPage.value = 1;
paginationState.metaItemsPerPage.value = 10;
paginationState.metaTotalItems.value = scans.length;
paginationState.metaSearch.value = "";
paginationState.loadList.mockClear();
paginationState.setEndpoint.mockClear();
paginationState.setMetaItemsPerPage.mockClear();
paginationState.setPage.mockClear();
paginationState.search.mockClear();
paginationState.setFilter.mockClear();
paginationState.setOrder.mockClear();
posState.selectScan.mockClear();
posState.getDepartment.mockReturnValue(12);
posState.departmentId.value = 12;
requestMock.mockReset();
requestMock.mockImplementation((url) => {
if (String(url).startsWith("/department/numberplatescanners")) {
return Promise.resolve({
data: {
data: [
{ id: 1, name: "North scanner" },
{ id: 2, name: "South scanner" },
],
},
});
}
return Promise.resolve({
data: {
data: [{ id: 501, reg_1: "AB12345", reg_2: "" }],
},
});
});
lookupMock.mockReset();
lookupMock.mockImplementation((plate) => {
if (plate === "AB12345") {
return Promise.resolve(buildLookupResponse());
}
return Promise.resolve(
buildLookupResponse({
make: "SCANIA",
model: "R500",
variant: "Highline",
type: "Lastbil",
use: "Godstransport",
})
);
});
});
afterEach(() => {
vi.runOnlyPendingTimers();
consoleErrorSpy.mockRestore();
vi.useRealTimers();
});
it("clicking a row selects the scan and reveals inline details", async () => {
const wrapper = mountWithApp(PosLastScannedLicensePlatesV2, { messages });
await vi.advanceTimersByTimeAsync(1000);
paginationState.list.value = scans;
await flushUi();
await wrapper.get('[data-testid="pos-recent-scan-row-801"]').trigger("click");
await flushUi();
expect(posState.selectScan).toHaveBeenCalledWith(expect.objectContaining({ id: 801, plate: "AB12345" }));
expect(lookupMock).toHaveBeenCalledWith("AB12345");
expect(wrapper.get('[data-testid="pos-recent-scan-details-801"]').text()).toContain("RENAULT");
expect(wrapper.get('[data-testid="pos-recent-scan-details-801"]').text()).toContain("Customer number");
});
it("keeps only one inline detail panel expanded at a time", async () => {
const wrapper = mountWithApp(PosLastScannedLicensePlatesV2, { messages });
await vi.advanceTimersByTimeAsync(1000);
paginationState.list.value = scans;
await flushUi();
await wrapper.get('[data-testid="pos-recent-scan-row-801"]').trigger("click");
await flushUi();
expect(wrapper.find('[data-testid="pos-recent-scan-details-801"]').exists()).toBe(true);
await wrapper.get('[data-testid="pos-recent-scan-row-802"]').trigger("click");
await flushUi();
expect(wrapper.find('[data-testid="pos-recent-scan-details-801"]').exists()).toBe(false);
expect(wrapper.find('[data-testid="pos-recent-scan-details-802"]').exists()).toBe(true);
});
it("caches MotorAPI lookups by plate when the user revisits a scan", async () => {
const wrapper = mountWithApp(PosLastScannedLicensePlatesV2, { messages });
await vi.advanceTimersByTimeAsync(1000);
paginationState.list.value = scans;
await flushUi();
await wrapper.get('[data-testid="pos-recent-scan-row-801"]').trigger("click");
await flushUi();
await wrapper.get('[data-testid="pos-recent-scan-row-802"]').trigger("click");
await flushUi();
await wrapper.get('[data-testid="pos-recent-scan-row-801"]').trigger("click");
await flushUi();
expect(lookupMock.mock.calls.filter(([plate]) => plate === "AB12345")).toHaveLength(1);
expect(lookupMock.mock.calls.filter(([plate]) => plate === "CD67890")).toHaveLength(1);
});
it("shows an inline error message when MotorAPI lookup fails", async () => {
lookupMock.mockImplementation((plate) => {
if (plate === "CD67890") {
return Promise.reject(new Error("Lookup failed"));
}
return Promise.resolve(buildLookupResponse());
});
const wrapper = mountWithApp(PosLastScannedLicensePlatesV2, { messages });
await vi.advanceTimersByTimeAsync(1000);
paginationState.list.value = scans;
await flushUi();
await wrapper.get('[data-testid="pos-recent-scan-row-802"]').trigger("click");
await flushUi();
expect(wrapper.get('[data-testid="pos-recent-scan-details-802"]').text()).toContain(
"Vehicle details could not be loaded"
);
});
});