Enhance POS booking flow logic and tests:
- Added support for trailers (`reg_2`) in booking selection and vehicle matching logic. - Implemented logic to prioritize bookings from registered vehicles and trailers. - Enhanced booking dropdown with date labels and validation for booked vehicles. - Improved e2e tests to cover booking date, trailer registration, and vehicle-search scenarios. - Refactored Vue components for consistency in booking handling and dropdown interaction.
This commit is contained in:
@@ -331,6 +331,18 @@ async function mockPosApi(page, fixture) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/vehicles/search") && method === "GET") {
|
||||
const search = (parsedUrl.searchParams.get("search") || "").toUpperCase();
|
||||
const matches = fixture.vehicles.filter((vehicle) => vehicle.reg.includes(search));
|
||||
await route.fulfill(
|
||||
json({
|
||||
success: true,
|
||||
data: matches,
|
||||
})
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathname.endsWith("/vehicles") && method === "GET") {
|
||||
const search = (parsedUrl.searchParams.get("search") || "").toUpperCase();
|
||||
const matches = fixture.vehicles.filter((vehicle) => vehicle.reg.includes(search));
|
||||
@@ -805,6 +817,22 @@ test.describe("POS flow", () => {
|
||||
expect(consoleProblems.join("\n")).not.toContain("reg_1");
|
||||
});
|
||||
|
||||
test("desktop hides the Sidste vask section when the selected vehicle has no last_order_id", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
test.skip(testInfo.project.name !== "chromium-desktop", "Desktop POS step 1 is validated on chromium-desktop.");
|
||||
|
||||
const fixture = createPosFixture();
|
||||
|
||||
await setupDesktopPosPage(page, fixture, { token: "pos-no-last-wash-token" });
|
||||
|
||||
await page.locator("#reg_1").fill("AB12345");
|
||||
await expect(page.getByTestId("pos-step-1").getByText("Pleno Logistics").first()).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
await expect(page.getByText(/Sidste vask/i)).toHaveCount(0);
|
||||
await expect(page.getByRole("button", { name: /Kopier sidste vask/i })).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("desktop flow creates transaction, renders cart, and reaches completion step", async ({ page }, testInfo) => {
|
||||
test.skip(testInfo.project.name !== "chromium-desktop", "Desktop POS flow is validated on chromium-desktop.");
|
||||
|
||||
@@ -889,6 +917,50 @@ test.describe("POS flow", () => {
|
||||
expect(consoleProblems.join("\n")).not.toContain("Extraneous non-props attributes");
|
||||
});
|
||||
|
||||
test("desktop marks booked vehicles in the dropdown when the vehicle payload already carries a booking", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
test.skip(
|
||||
testInfo.project.name !== "chromium-desktop",
|
||||
"Desktop POS booking flow is validated on chromium-desktop."
|
||||
);
|
||||
|
||||
const fixture = createPosFixture();
|
||||
fixture.vehicles = [
|
||||
{
|
||||
...fixture.vehicles[0],
|
||||
reg: "EC21235",
|
||||
booking_id: 8991,
|
||||
status: "booked",
|
||||
},
|
||||
{
|
||||
...fixture.vehicles[0],
|
||||
id: 7002,
|
||||
reg: "EC21234",
|
||||
booking_id: 8992,
|
||||
status: "booked",
|
||||
},
|
||||
];
|
||||
fixture.orderBookings = [
|
||||
buildOrderBooking(8991, {
|
||||
reg_1: "EC21235",
|
||||
datetime: "2026-01-03T07:00:00.000Z",
|
||||
}),
|
||||
buildOrderBooking(8992, {
|
||||
reg_1: "EC21234",
|
||||
datetime: "2026-01-04T09:00:00.000Z",
|
||||
}),
|
||||
];
|
||||
|
||||
await setupDesktopPosPage(page, fixture, { token: "pos-desktop-booked-dropdown" });
|
||||
|
||||
await page.locator("#reg_1").fill("EC212");
|
||||
await expect(page.getByTestId("desktop-booked-icon-7001")).toBeVisible({ timeout: 10_000 });
|
||||
await expect(page.getByTestId("desktop-booked-icon-7002")).toBeVisible({ timeout: 10_000 });
|
||||
await expect(page.getByTestId("desktop-booked-date-7001")).toContainText(/\d/, { timeout: 10_000 });
|
||||
await expect(page.getByTestId("desktop-booked-date-7002")).toContainText(/\d/, { timeout: 10_000 });
|
||||
});
|
||||
|
||||
test("desktop auto-applies a single matching order booking and completes that booking", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
@@ -902,6 +974,7 @@ test.describe("POS flow", () => {
|
||||
buildOrderBooking(8101, {
|
||||
reference: "SINGLE-BOOKING-REF",
|
||||
reference_number: "SINGLE-BOOKING-REF",
|
||||
reg_2: "TRAILER-1",
|
||||
notes: "Single desktop booking",
|
||||
note: "Single desktop booking",
|
||||
items: [
|
||||
@@ -920,6 +993,7 @@ test.describe("POS flow", () => {
|
||||
const activeBookingSelector = page.locator('[data-testid="default-object-selector"].is-active');
|
||||
await page.locator("#reg_1").fill("AB12345");
|
||||
await expect(activeBookingSelector).toHaveCount(0);
|
||||
await expect(page.locator("#reg_2")).toHaveValue("TRAILER-1");
|
||||
|
||||
await page.locator('[data-testid="pos-next-step"]:visible').click();
|
||||
await expect(page.getByTestId("pos-step-2")).toBeVisible({ timeout: 10_000 });
|
||||
@@ -927,6 +1001,7 @@ test.describe("POS flow", () => {
|
||||
await expect
|
||||
.poll(() => fixture.ordersById[9300]?.reference || null, { timeout: 10_000 })
|
||||
.toBe("SINGLE-BOOKING-REF");
|
||||
await expect.poll(() => fixture.ordersById[9300]?.reg_2 || null, { timeout: 10_000 }).toBe("TRAILER-1");
|
||||
await expect
|
||||
.poll(() => (fixture.orderItemsByOrderId[9300] || []).map((item) => Number(item.product_id)), { timeout: 10_000 })
|
||||
.toEqual([53, 63]);
|
||||
@@ -960,6 +1035,7 @@ test.describe("POS flow", () => {
|
||||
datetime: "2026-01-01T07:00:00.000Z",
|
||||
reference: "BOOKING-A-REF",
|
||||
reference_number: "BOOKING-A-REF",
|
||||
reg_2: "TRAILER-A",
|
||||
items: [
|
||||
{ id: 53, name: "Tankvogn med hænger", price: 599, quantity: 1 },
|
||||
{ id: 63, name: "Dolly", price: 275, quantity: 1 },
|
||||
@@ -973,6 +1049,7 @@ test.describe("POS flow", () => {
|
||||
datetime: "2026-01-01T09:00:00.000Z",
|
||||
reference: "BOOKING-B-REF",
|
||||
reference_number: "BOOKING-B-REF",
|
||||
reg_2: "TRAILER-B",
|
||||
items: [{ id: 63, name: "Dolly", price: 275, quantity: 1 }],
|
||||
parsed_services: {
|
||||
string: "Dolly",
|
||||
@@ -995,10 +1072,12 @@ test.describe("POS flow", () => {
|
||||
|
||||
await activeBookingSelector.getByTestId("pos-desktop-order-booking-use-8102").click();
|
||||
await expect(page.locator("#reference")).toHaveValue("BOOKING-A-REF");
|
||||
await expect(page.locator("#reg_2")).toHaveValue("TRAILER-A");
|
||||
|
||||
await page.locator('[data-testid="pos-next-step"]:visible').click();
|
||||
await expect(page.getByTestId("pos-step-2")).toBeVisible({ timeout: 10_000 });
|
||||
|
||||
await expect.poll(() => fixture.ordersById[9300]?.reg_2 || null, { timeout: 10_000 }).toBe("TRAILER-A");
|
||||
await expect
|
||||
.poll(() => (fixture.orderItemsByOrderId[9300] || []).map((item) => Number(item.product_id)), { timeout: 10_000 })
|
||||
.toEqual([53, 63]);
|
||||
@@ -1018,6 +1097,75 @@ test.describe("POS flow", () => {
|
||||
expect(fixture.completedBookingIds).not.toContain(8103);
|
||||
});
|
||||
|
||||
test("desktop opens the booking chooser for a booked plate even without a vehicle match", async ({
|
||||
page,
|
||||
}, testInfo) => {
|
||||
test.skip(
|
||||
testInfo.project.name !== "chromium-desktop",
|
||||
"Desktop POS booking flow is validated on chromium-desktop."
|
||||
);
|
||||
|
||||
const fixture = createPosFixture();
|
||||
fixture.orderBookings = [
|
||||
buildOrderBooking(8111, {
|
||||
reg_1: "BOOKONLY1",
|
||||
datetime: "2026-01-01T07:00:00.000Z",
|
||||
reference: "BOOK-ONLY-A",
|
||||
reference_number: "BOOK-ONLY-A",
|
||||
items: [
|
||||
{ id: 53, name: "Tankvogn med hænger", price: 599, quantity: 1 },
|
||||
{ id: 63, name: "Dolly", price: 275, quantity: 1 },
|
||||
],
|
||||
parsed_services: {
|
||||
string: "Tankvogn med hænger, Dolly",
|
||||
array: ["Tankvogn med hænger", "Dolly"],
|
||||
},
|
||||
}),
|
||||
buildOrderBooking(8112, {
|
||||
reg_1: "BOOKONLY1",
|
||||
datetime: "2026-01-01T09:00:00.000Z",
|
||||
reference: "BOOK-ONLY-B",
|
||||
reference_number: "BOOK-ONLY-B",
|
||||
items: [{ id: 63, name: "Dolly", price: 275, quantity: 1 }],
|
||||
parsed_services: {
|
||||
string: "Dolly",
|
||||
array: ["Dolly"],
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
await setupDesktopPosPage(page, fixture, { token: "pos-desktop-booking-only" });
|
||||
|
||||
const activeBookingSelector = page.locator('[data-testid="default-object-selector"].is-active');
|
||||
await page.locator("#reg_1").fill("BOOKONLY1");
|
||||
await expect(activeBookingSelector).toBeVisible({ timeout: 10_000 });
|
||||
await activeBookingSelector.getByTestId("pos-desktop-order-booking-use-8111").click();
|
||||
await expect(page.locator("#reference")).toHaveValue("BOOK-ONLY-A");
|
||||
|
||||
await page.locator('[data-testid="pos-next-step"]:visible').click();
|
||||
await expect(page.getByTestId("pos-step-2")).toBeVisible({ timeout: 10_000 });
|
||||
await expect
|
||||
.poll(() => (fixture.orderItemsByOrderId[9300] || []).map((item) => Number(item.product_id)), { timeout: 10_000 })
|
||||
.toEqual([53, 63]);
|
||||
|
||||
await page.locator('[data-testid="pos-next-step"]:visible').click();
|
||||
await expect(page.getByTestId("pos-step-3")).toBeVisible({ timeout: 10_000 });
|
||||
await page.locator('[data-testid="pos-next-step"]:visible').click();
|
||||
|
||||
await expect.poll(() => fixture.ordersById[9300]?.booking_id || null, { timeout: 10_000 }).toBe(8111);
|
||||
await expect
|
||||
.poll(
|
||||
() =>
|
||||
fixture.bookingOrderAssignments.some(
|
||||
(entry) => Number(entry?.id) === 8111 && Number(entry?.order_id) === 9300
|
||||
),
|
||||
{ timeout: 10_000 }
|
||||
)
|
||||
.toBe(true);
|
||||
await expect.poll(() => fixture.completedBookingIds, { timeout: 10_000 }).toContain(8111);
|
||||
expect(fixture.completedBookingIds).not.toContain(8112);
|
||||
});
|
||||
|
||||
test("desktop continue without booking skips hydration and completion requests", async ({ page }, testInfo) => {
|
||||
test.skip(
|
||||
testInfo.project.name !== "chromium-desktop",
|
||||
|
||||
Reference in New Issue
Block a user