Files
pleno-vue/tests/e2e/support/bookingFlow.ts
T
Jeppe Bundgaard 983585ede7 Refactor and cleanup:
- Migrate postinstall script to `postinstall-sync-playwright-root-links.mjs` for streamlined path resolution.
- Replace `axios` v1.15.0 with v1.13.5 and downgrade `vite` from v8.0.5 to v7.1.11.
- Update testing code for consistent formatting and enhanced readability (e.g., `poll` and `catch` calls).
- Remove unused or redundant dependency flags and align `package-lock.json` with new configuration.
2026-04-14 14:17:28 +02:00

223 lines
7.1 KiB
TypeScript

import { expect, type Page } from "@playwright/test";
type BookingFlowData = {
departmentId: number;
registrationNumber: string;
reference: string;
poNumber: string;
notes: string;
};
function addDays(baseDate: Date, days: number) {
return new Date(baseDate.getFullYear(), baseDate.getMonth(), baseDate.getDate() + days, 0, 0, 0, 0);
}
function formatDateKey(value: Date) {
const year = value.getFullYear();
const month = String(value.getMonth() + 1).padStart(2, "0");
const day = String(value.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}
function formatTriggerDate(value: Date) {
const day = String(value.getDate()).padStart(2, "0");
const month = String(value.getMonth() + 1).padStart(2, "0");
const year = value.getFullYear();
return `${day}.${month}.${year}`;
}
function formatSummaryDate(value: Date) {
return value.toLocaleDateString("da-DK", {
year: "numeric",
month: "long",
day: "numeric",
});
}
async function selectBookingDateTime(page: Page) {
const selectedDate = addDays(new Date(), 2);
const selectedDayTestId = `booking-date-time-day-${formatDateKey(selectedDate)}`;
const selectedSlotTestId = "booking-date-time-slot-14-00";
const selectorConfirm = page.getByTestId("booking-date-time-confirm");
if (!(await selectorConfirm.isVisible().catch(() => false))) {
await page.getByTestId("booking-date-time-trigger-date").click();
}
await expect(page.getByTestId(selectedDayTestId)).toBeVisible();
await page.getByTestId(selectedDayTestId).click();
await page.getByTestId(selectedSlotTestId).click();
await selectorConfirm.click();
await expect(page.getByTestId("booking-date-time-trigger-date")).toContainText(formatTriggerDate(selectedDate));
await expect(page.getByTestId("booking-date-time-trigger-time")).toContainText("~14.00");
return {
dateLabel: formatTriggerDate(selectedDate),
summaryDateLabel: formatSummaryDate(selectedDate),
summaryTimeLabel: "14.00",
yearLabel: String(selectedDate.getFullYear()),
};
}
async function isMobileBookingWizard(page: Page) {
return page
.getByTestId("booking-mobile-department-trigger")
.isVisible()
.catch(() => false);
}
async function clickDesktopNext(page: Page) {
const nextButtons = [
page.getByRole("button", { name: /Næste trin/i }),
page.getByRole("button", { name: /Next step/i }),
page.getByRole("button", { name: /^Next$/i }),
];
for (const candidate of nextButtons) {
if ((await candidate.count().catch(() => 0)) === 0) {
continue;
}
const button = candidate.first();
if (await button.isVisible().catch(() => false)) {
await button.click();
return;
}
}
throw new Error("Could not find a visible desktop next-step button in the booking flow.");
}
async function ensureVehicleInput(page: Page) {
const reg1Input = page.locator('input[id="reg1-input"]:visible').first();
if (await reg1Input.isVisible().catch(() => false)) {
return reg1Input;
}
await page.locator('button[id="reg1-button"]:visible').first().click();
await expect(reg1Input).toBeVisible();
return reg1Input;
}
async function ensureVehicleTypeSelection(page: Page) {
const reg1TypeSelect = page.locator('select[id="reg1-type-select"]:visible').first();
if (!(await reg1TypeSelect.isVisible().catch(() => false))) {
return;
}
await expect
.poll(
async () =>
reg1TypeSelect
.locator("option")
.count()
.catch(() => 0),
{
timeout: 10_000,
}
)
.toBeGreaterThan(1);
const selectedIndex = await reg1TypeSelect
.evaluate((element) => (element as HTMLSelectElement).selectedIndex)
.catch(() => -1);
if (selectedIndex > 0) {
return;
}
await reg1TypeSelect.selectOption({ index: 1 });
await expect
.poll(
async () => reg1TypeSelect.evaluate((element) => (element as HTMLSelectElement).selectedIndex).catch(() => -1),
{
timeout: 10_000,
}
)
.toBeGreaterThan(0);
}
async function fillBookingField(page: Page, toggleTestId: string, inputTestId: string, value: string) {
const input = page.locator(`[data-testid="${inputTestId}"]:visible`).first();
const toggle = page.locator(`[data-testid="${toggleTestId}"]:visible`).first();
if (!(await input.isVisible().catch(() => false))) {
await toggle.click();
await expect(input).toBeVisible();
}
await input.fill(value);
await input.press("Enter");
}
export async function completeBookingCreationFlow(page: Page, data: BookingFlowData) {
const { departmentId, registrationNumber, reference, poNumber, notes } = data;
const bookWashButton = page.locator("#book-wash-button");
if (!(await bookWashButton.isVisible().catch(() => false))) {
await page.goto("/user");
}
await expect(bookWashButton).toBeVisible();
await page.click("#book-wash-button");
await expect(page).toHaveURL("/user/bookings/book");
const mobileWizard = await isMobileBookingWizard(page);
if (mobileWizard) {
await page.getByTestId("booking-mobile-department-trigger").click();
await page.getByTestId(`booking-mobile-department-option-${departmentId}`).click();
await expect(page.getByTestId("booking-mobile-next")).toBeEnabled();
await page.getByTestId("booking-mobile-next").click();
} else {
await page.click(`#department-option-${departmentId}`);
}
const reg1Input = await ensureVehicleInput(page);
await reg1Input.fill(registrationNumber);
await reg1Input.press("Enter");
if (!mobileWizard) {
await ensureVehicleTypeSelection(page);
}
if (mobileWizard) {
await expect(page.getByTestId("booking-mobile-next")).toBeEnabled();
await page.getByTestId("booking-mobile-next").click();
} else {
await clickDesktopNext(page);
}
const productCard = page.locator('[data-testid^="pos-product-card-"]:visible').first();
await expect(productCard).toBeVisible();
await productCard.click();
if (mobileWizard) {
await expect(page.getByTestId("booking-mobile-next")).toBeEnabled();
await page.getByTestId("booking-mobile-next").click();
}
const selectedDateTime = await selectBookingDateTime(page);
await fillBookingField(page, "booking-reference-toggle", "booking-reference-input", reference);
await fillBookingField(page, "booking-po-toggle", "booking-po-input", poNumber);
await fillBookingField(page, "booking-notes-toggle", "booking-notes-input", notes);
if (mobileWizard) {
await page.check("#pickup-mobile");
await expect(page.getByTestId("booking-mobile-next")).toBeEnabled();
await page.getByTestId("booking-mobile-next").click();
} else {
await page.check("#pickup");
await page.click("#go-to-confirmation-button");
}
await expect(page.locator("#booking-summary-title")).toBeVisible();
await expect(page.getByTestId("booking-summary-date-time")).toContainText(selectedDateTime.summaryDateLabel);
await expect(page.getByTestId("booking-summary-date-time")).toContainText(selectedDateTime.summaryTimeLabel);
await expect(page.getByTestId("booking-summary-date-time")).toContainText(selectedDateTime.yearLabel);
await page.click("#confirm-booking-button", { force: true });
await expect(page).toHaveURL(/\/user/);
}