213 lines
7.9 KiB
TypeScript
213 lines
7.9 KiB
TypeScript
import { expect, test, type Page } from "@playwright/test";
|
|
import { bookingTestData } from "./fixtures";
|
|
import {
|
|
completeBookingCreationFlow,
|
|
fillBookingField,
|
|
goToBookingProductSelectionStep,
|
|
goToBookingProductSelectionStepWithOptions,
|
|
selectBookingDateTime,
|
|
} from "./support/bookingFlow";
|
|
import { mockApi, primeMockSession } from "./support/network.js";
|
|
import { isCompactProject } from "./support/projects";
|
|
|
|
async function prepareBookingPage(
|
|
page: Page,
|
|
pos: true | Record<string, unknown> = true,
|
|
sessionData: Record<string, unknown> = {}
|
|
) {
|
|
await mockApi(page, {
|
|
authenticated: true,
|
|
sessionData: {
|
|
display_name: "Booking User",
|
|
customer_number: 12345679,
|
|
permissions: ["user"],
|
|
...sessionData,
|
|
},
|
|
pos,
|
|
});
|
|
|
|
await primeMockSession(page, { bootPath: "/user" });
|
|
}
|
|
|
|
test("[BOOKINGS][User][Creation] should create a new booking", async ({ page }) => {
|
|
await prepareBookingPage(page);
|
|
await completeBookingCreationFlow(page, bookingTestData);
|
|
});
|
|
|
|
test("[BOOKINGS][User][Departments] should hide departments without time booking enabled", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await prepareBookingPage(page, {
|
|
departments: [
|
|
{
|
|
id: bookingTestData.departmentId,
|
|
name: "Bookable Demo",
|
|
description: "Bookable address",
|
|
exclude_from_invoicing: false,
|
|
bookingsystem_time_based_enabled: true,
|
|
},
|
|
{
|
|
id: 77,
|
|
name: "Disabled Booking Hall",
|
|
description: "Disabled address",
|
|
exclude_from_invoicing: false,
|
|
bookingsystem_time_based_enabled: false,
|
|
},
|
|
],
|
|
});
|
|
|
|
await page.goto("/user/bookings/book");
|
|
|
|
if (isCompactProject(testInfo)) {
|
|
const mobileTrigger = page.getByTestId("booking-mobile-department-trigger");
|
|
await expect(mobileTrigger).toBeVisible();
|
|
await mobileTrigger.click();
|
|
await expect(page.getByTestId(`booking-mobile-department-option-${bookingTestData.departmentId}`)).toBeVisible();
|
|
await expect(page.getByTestId("booking-mobile-department-option-77")).toHaveCount(0);
|
|
} else {
|
|
await expect(page.locator(`#department-option-${bookingTestData.departmentId}`)).toBeVisible();
|
|
await expect(page.locator("#department-option-77")).toHaveCount(0);
|
|
}
|
|
|
|
await expect(page.getByText("Disabled Booking Hall")).toHaveCount(0);
|
|
});
|
|
|
|
test("[BOOKINGS][User][Creation] should show interior wash products on the interior category", async ({ page }) => {
|
|
await prepareBookingPage(page);
|
|
await goToBookingProductSelectionStepWithOptions(page, bookingTestData, {
|
|
selectInitialProduct: false,
|
|
});
|
|
|
|
const interiorCategoryTab = page.locator('[data-testid="pos-product-category-tab-2"]:visible').first();
|
|
if ((await interiorCategoryTab.count()) > 0) {
|
|
await interiorCategoryTab.click();
|
|
} else {
|
|
await page.locator('[data-testid="pos-product-category-select"]:visible').first().selectOption("2");
|
|
}
|
|
|
|
const interiorProductCard = page.locator('[data-testid="pos-product-card-63"]:visible').first();
|
|
await expect(interiorProductCard).toBeVisible();
|
|
await expect(interiorProductCard).toContainText("Indvendig vask Forvogn");
|
|
});
|
|
|
|
test("[BOOKINGS][User][Creation] should show desktop loading states while categories and products resolve", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.skip(!testInfo.project.name.toLowerCase().includes("desktop"), "Desktop only");
|
|
|
|
await prepareBookingPage(page, {
|
|
departmentCategoriesDelayMs: 700,
|
|
productsDelayMs: 900,
|
|
});
|
|
|
|
await goToBookingProductSelectionStepWithOptions(page, bookingTestData, {
|
|
selectInitialProduct: false,
|
|
});
|
|
|
|
await expect(page.locator('[data-testid="pos-product-categories-loading"]:visible').first()).toBeVisible();
|
|
await expect(page.locator('[data-testid="pos-products-loading"]:visible').first()).toBeVisible();
|
|
|
|
await expect(page.locator('[data-testid="pos-product-categories-loading"]:visible')).toHaveCount(0, {
|
|
timeout: 10_000,
|
|
});
|
|
await expect(page.locator('[data-testid="pos-product-card-53"]:visible').first()).toBeVisible({ timeout: 10_000 });
|
|
await expect(page.locator('[data-testid="pos-products-loading"]:visible')).toHaveCount(0, { timeout: 10_000 });
|
|
|
|
await page.locator('[data-testid="pos-product-category-tab-2"]:visible').first().click();
|
|
await expect(page.locator('[data-testid="pos-products-loading"]:visible').first()).toBeVisible();
|
|
await expect(page.locator('[data-testid="pos-product-card-63"]:visible').first()).toBeVisible({ timeout: 10_000 });
|
|
await expect(page.locator('[data-testid="pos-products-loading"]:visible')).toHaveCount(0, { timeout: 10_000 });
|
|
});
|
|
|
|
test("[BOOKINGS][User][Creation] should submit resolved wash certificate price for visible-price customers", async ({
|
|
page,
|
|
}) => {
|
|
await prepareBookingPage(
|
|
page,
|
|
{
|
|
extraProducts: [
|
|
{
|
|
id: 41,
|
|
name: "Vaskecertifikat",
|
|
description: "Wash certificate",
|
|
price: 0,
|
|
subscription_allowed: false,
|
|
category: 8,
|
|
piktogram: "certificate",
|
|
apply_category_discount: false,
|
|
requires_note: false,
|
|
is_wash: false,
|
|
display_in_booking_form: false,
|
|
order_priority: 5,
|
|
addons: [],
|
|
},
|
|
],
|
|
productFinalPricesById: {
|
|
41: 175,
|
|
},
|
|
},
|
|
{
|
|
permissions: ["user", "has_attribute_showPricesOnBookingPage"],
|
|
}
|
|
);
|
|
|
|
const { departmentId, registrationNumber, reference, poNumber, notes, mobileWizard } =
|
|
await goToBookingProductSelectionStepWithOptions(page, bookingTestData, {
|
|
selectInitialProduct: false,
|
|
});
|
|
|
|
const interiorCategoryTab = page.locator('[data-testid="pos-product-category-tab-2"]:visible').first();
|
|
if ((await interiorCategoryTab.count()) > 0) {
|
|
await interiorCategoryTab.click();
|
|
} else {
|
|
await page.locator('[data-testid="pos-product-category-select"]:visible').first().selectOption("2");
|
|
}
|
|
|
|
const interiorProductCard = page.locator('[data-testid="pos-product-card-63"]:visible').first();
|
|
await expect(interiorProductCard).toBeVisible();
|
|
await interiorProductCard.click();
|
|
|
|
await expect(page.locator("body")).toContainText("Vaskecertifikat");
|
|
await expect(page.locator("body")).toContainText(/175,00\s*kr\./);
|
|
|
|
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);
|
|
const summaryOverlay = page.locator(".booking-summary-overlay:visible").first();
|
|
await expect(summaryOverlay).toContainText("Vaskecertifikat");
|
|
await expect(summaryOverlay).toContainText(/175,00\s*kr\./);
|
|
|
|
const [request] = await Promise.all([
|
|
page.waitForRequest((request) => request.method() === "POST" && request.url().includes("/order-bookings"), {
|
|
timeout: 10_000,
|
|
}),
|
|
summaryOverlay.getByTestId("booking-summary-confirm-body").click({ force: true }),
|
|
]);
|
|
const payload = request.postDataJSON();
|
|
const certificateItem = payload.items.find((item: { id?: number }) => Number(item.id) === 41);
|
|
|
|
expect(certificateItem).toBeTruthy();
|
|
expect(Number(certificateItem.price)).toBe(175);
|
|
expect(Number(certificateItem.quantity)).toBe(1);
|
|
expect(Number(payload.department)).toBe(departmentId);
|
|
expect(payload.reg_1).toBe(registrationNumber);
|
|
});
|