828 lines
30 KiB
TypeScript
828 lines
30 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { mockApi, seedAuthenticatedState } from "./support/network.js";
|
|
import { isCompactProject, isDesktopProject } from "./support/projects";
|
|
|
|
const adminPermissions = [
|
|
"admin",
|
|
"list_department_daily_reports",
|
|
"department_access_1",
|
|
"department_access_2",
|
|
"department_access_3",
|
|
"department_access_4",
|
|
"department_access_5",
|
|
];
|
|
|
|
const defaultDepartments = [
|
|
{ id: 1, name: "Visible North", visible: true, order_priority: 20 },
|
|
{ id: 2, name: "Hidden South", visible: false },
|
|
{ id: 3, name: "Legacy East", order_priority: 10 },
|
|
{ id: 4, name: "Numeric Hidden", visible: 0 },
|
|
{ id: 5, name: "Archived West", visible: true, archived: true },
|
|
{ id: 6, name: "Unassigned West", visible: true, order_priority: 5 },
|
|
];
|
|
|
|
const json = (body: unknown, status = 200) => ({
|
|
status,
|
|
contentType: "application/json",
|
|
body: JSON.stringify(body),
|
|
});
|
|
|
|
const formatLocalDateTime = (value: Date) => {
|
|
const pad = (segment: number) => String(segment).padStart(2, "0");
|
|
|
|
return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())} ${pad(value.getHours())}:${pad(
|
|
value.getMinutes()
|
|
)}:${pad(value.getSeconds())}`;
|
|
};
|
|
|
|
const buildOverviewPayload = () => ({
|
|
data: {
|
|
department_ids: [1],
|
|
date: "2026-04-08",
|
|
date_to: "2026-04-08",
|
|
metrics: {
|
|
bookings: { state: "ready", value: 4, out_of: 6, message: null },
|
|
complaints: {
|
|
state: "unavailable",
|
|
value: null,
|
|
out_of: null,
|
|
message: "Complaints data is not available yet.",
|
|
},
|
|
night_washes: { state: "ready", value: 1, out_of: null, message: null },
|
|
revenue: { state: "ready", value: 2400, out_of: null, message: null },
|
|
washes: { state: "ready", value: 8, out_of: null, message: null },
|
|
products_sold: { state: "ready", value: 19, out_of: null, message: null },
|
|
transactions: { state: "ready", value: 11, out_of: null, message: null },
|
|
water_usage: { state: "ready", value: 32, out_of: null, message: null },
|
|
overtime: { state: "ready", value: 1.5, out_of: null, message: null },
|
|
},
|
|
products: [],
|
|
},
|
|
});
|
|
|
|
async function mockAdminDepartmentDependencies(
|
|
page,
|
|
options: {
|
|
departmentDelayMs?: number;
|
|
departments?: Array<Record<string, unknown>>;
|
|
permissions?: string[];
|
|
sessionData?: Record<string, unknown>;
|
|
} = {}
|
|
) {
|
|
await seedAuthenticatedState(page);
|
|
await mockApi(page, {
|
|
authenticated: true,
|
|
permissions: options.permissions || adminPermissions,
|
|
sessionData: options.sessionData,
|
|
});
|
|
|
|
await page.route(/\/departments(\?.*)?$/i, async (route) => {
|
|
if (options.departmentDelayMs) {
|
|
await new Promise((resolve) => setTimeout(resolve, options.departmentDelayMs));
|
|
}
|
|
|
|
await route.fulfill(
|
|
json({
|
|
data: options.departments || defaultDepartments,
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.route(/\/departments\/weather(\?.*)?$/i, async (route) => {
|
|
await route.fulfill(json({ data: [] }));
|
|
});
|
|
|
|
await page.route(/\/departments\/daily-reports\/get(\?.*)?$/i, async (route) => {
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
id: 10,
|
|
department_id: 1,
|
|
water_usage: 32,
|
|
notes: "",
|
|
filled_by: 1,
|
|
created_at: "2026-04-08 00:00:00",
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.route(/\/departments\/daily-reports(?:\/overview)?(\?.*)?$/i, async (route) => {
|
|
const url = new URL(route.request().url());
|
|
if (url.pathname.endsWith("/departments/daily-reports/overview")) {
|
|
await route.fulfill(json(buildOverviewPayload()));
|
|
return;
|
|
}
|
|
|
|
await route.fulfill(json({ data: [] }));
|
|
});
|
|
}
|
|
|
|
async function expandDepartmentFiltersOnMobile(page, testInfo) {
|
|
if (!isCompactProject(testInfo)) {
|
|
return;
|
|
}
|
|
|
|
if (await page.getByTestId("daily-report-department-controls").count()) {
|
|
return;
|
|
}
|
|
|
|
await page.getByTestId("daily-report-mobile-departments-toggle").click();
|
|
await expect(page.getByTestId("daily-report-department-controls")).toBeVisible();
|
|
}
|
|
|
|
async function expectDailyReportPageVisible(page) {
|
|
await expect(page.getByTestId("daily-report-page")).toBeVisible({ timeout: 20_000 });
|
|
}
|
|
|
|
test.describe("Admin department visibility", () => {
|
|
test("hides placeholder departments from desktop admin navigation pickers", async ({ page }, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "department_access_5"],
|
|
departments: [
|
|
{ id: 1, name: "Visible North", visible: true, order_priority: 20 },
|
|
{ id: 2, name: "Ingen data", visible: true, order_priority: 1 },
|
|
{ id: 3, name: " ", visible: true, order_priority: 2 },
|
|
{ id: 4, name: "Visible East", visible: true, order_priority: 10 },
|
|
],
|
|
});
|
|
|
|
await page.goto("/admin");
|
|
|
|
await expectDailyReportPageVisible(page);
|
|
|
|
const desktopNavigation = page.getByTestId("desktop-buefy-navigation");
|
|
const desktopDepartmentSelect = page.getByTestId("desktop-header-department-select");
|
|
|
|
await expect(desktopNavigation).toContainText("Visible North");
|
|
await expect(desktopNavigation).toContainText("Visible East");
|
|
await expect(desktopNavigation).not.toContainText(/Ingen data/i);
|
|
const desktopNavigationText = await desktopNavigation.innerText();
|
|
expect(desktopNavigationText.indexOf("Visible East")).toBeLessThan(desktopNavigationText.indexOf("Visible North"));
|
|
|
|
await expect(desktopDepartmentSelect.locator("option")).toHaveCount(3);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Visible North" })).toHaveCount(1);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Visible East" })).toHaveCount(1);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Ingen data" })).toHaveCount(0);
|
|
const desktopDepartmentOptionTexts = await desktopDepartmentSelect
|
|
.locator("option")
|
|
.evaluateAll((options) => options.map((option) => option.textContent?.trim() || ""));
|
|
expect(desktopDepartmentOptionTexts.slice(1)).toEqual(["Visible East", "Visible North"]);
|
|
});
|
|
|
|
test("hides invisible departments from both the header selector and the daily report department controls", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await mockAdminDepartmentDependencies(page);
|
|
|
|
await page.goto("/admin/1/modules/daily-report?departmentIds=1&dateFrom=2026-04-08&dateTo=2026-04-08");
|
|
|
|
await expectDailyReportPageVisible(page);
|
|
await expandDepartmentFiltersOnMobile(page, testInfo);
|
|
|
|
const departmentControls = page.getByTestId("daily-report-department-controls");
|
|
await expect(departmentControls.getByRole("button", { name: "Visible North" })).toBeVisible();
|
|
await expect(departmentControls.getByRole("button", { name: "Legacy East" })).toBeVisible();
|
|
await expect(departmentControls.getByRole("button", { name: "Hidden South" })).toHaveCount(0);
|
|
await expect(departmentControls.getByRole("button", { name: "Numeric Hidden" })).toHaveCount(0);
|
|
await expect(departmentControls.getByRole("button", { name: "Archived West" })).toHaveCount(0);
|
|
await expect(departmentControls.getByRole("button", { name: "Unassigned West" })).toHaveCount(0);
|
|
const departmentControlTexts = await departmentControls
|
|
.getByRole("button")
|
|
.evaluateAll((buttons) => buttons.map((button) => button.textContent?.trim() || ""));
|
|
expect(departmentControlTexts.filter((text) => ["Visible North", "Legacy East"].includes(text))).toEqual([
|
|
"Legacy East",
|
|
"Visible North",
|
|
]);
|
|
|
|
if (isDesktopProject(test.info())) {
|
|
const desktopDepartmentSelect = page.getByTestId("desktop-header-department-select");
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Visible North" })).toHaveCount(1);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Legacy East" })).toHaveCount(1);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Hidden South" })).toHaveCount(0);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Numeric Hidden" })).toHaveCount(0);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Archived West" })).toHaveCount(0);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Unassigned West" })).toHaveCount(0);
|
|
}
|
|
});
|
|
|
|
test("does not expose unassigned departments to superuser admin department pickers", async ({ page }, testInfo) => {
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [
|
|
"superuser",
|
|
"admin",
|
|
"list_department_daily_reports",
|
|
"department_access_1",
|
|
"department_access_3",
|
|
],
|
|
departments: defaultDepartments,
|
|
});
|
|
|
|
await page.goto("/admin/1/modules/daily-report?departmentIds=1&dateFrom=2026-04-08&dateTo=2026-04-08");
|
|
|
|
await expectDailyReportPageVisible(page);
|
|
await expandDepartmentFiltersOnMobile(page, testInfo);
|
|
|
|
const departmentControls = page.getByTestId("daily-report-department-controls");
|
|
await expect(departmentControls.getByRole("button", { name: "Legacy East" })).toBeVisible();
|
|
await expect(departmentControls.getByRole("button", { name: "Visible North" })).toBeVisible();
|
|
await expect(departmentControls.getByRole("button", { name: "Unassigned West" })).toHaveCount(0);
|
|
|
|
if (isDesktopProject(test.info())) {
|
|
const desktopNavigation = page.getByTestId("desktop-buefy-navigation");
|
|
const desktopDepartmentSelect = page.getByTestId("desktop-header-department-select");
|
|
|
|
await expect(desktopNavigation).not.toContainText("Unassigned West");
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Legacy East" })).toHaveCount(1);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Visible North" })).toHaveCount(1);
|
|
await expect(desktopDepartmentSelect.locator("option", { hasText: "Unassigned West" })).toHaveCount(0);
|
|
}
|
|
});
|
|
|
|
test("does not expose unassigned departments in the overview list tab", async ({ page }, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [
|
|
"superuser",
|
|
"admin",
|
|
"list_department_daily_reports",
|
|
"department_access_1",
|
|
"department_access_3",
|
|
],
|
|
departments: defaultDepartments,
|
|
});
|
|
|
|
await page.goto("/admin");
|
|
await expectDailyReportPageVisible(page);
|
|
await page.getByText("Liste", { exact: true }).click();
|
|
|
|
const overviewList = page.getByTestId("admin-overview-list");
|
|
await expect(overviewList.getByTestId("admin-overview-list-department-3")).toBeVisible();
|
|
await expect(overviewList.getByTestId("admin-overview-list-department-1")).toBeVisible();
|
|
await expect(overviewList.getByTestId("admin-overview-list-department-6")).toHaveCount(0);
|
|
await expect(overviewList.getByTestId("admin-overview-list-department-2")).toHaveCount(0);
|
|
await expect(overviewList.getByTestId("admin-overview-list-department-5")).toHaveCount(0);
|
|
});
|
|
|
|
test("renders department controls after a delayed departments response", async ({ page }, testInfo) => {
|
|
await mockAdminDepartmentDependencies(page, { departmentDelayMs: 400 });
|
|
|
|
await page.goto("/admin/1/modules/daily-report?departmentIds=1&dateFrom=2026-04-08&dateTo=2026-04-08");
|
|
await expectDailyReportPageVisible(page);
|
|
await expandDepartmentFiltersOnMobile(page, testInfo);
|
|
|
|
const departmentControls = page.getByTestId("daily-report-department-controls");
|
|
await expect(departmentControls.getByRole("button", { name: "Visible North" })).toBeVisible();
|
|
await expect(departmentControls.getByRole("button", { name: "Legacy East" })).toBeVisible();
|
|
});
|
|
|
|
test("shows the draft transactions navigation item in the desktop buefy menu when a draft customer is configured", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "add_order", "list_orders"],
|
|
departments: [
|
|
{ id: 1, name: "Visible North", visible: true },
|
|
{ id: 2, name: "Visible South", visible: true },
|
|
],
|
|
sessionData: {
|
|
runtime_config: {
|
|
economic: {
|
|
transaction_draft_customer_number: 6001,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
await page.goto("/admin/1/modules/daily-report");
|
|
|
|
const desktopNavigation = page.getByTestId("desktop-buefy-navigation");
|
|
await expect(desktopNavigation).toContainText("Kladder");
|
|
|
|
await desktopNavigation.getByText("Kladder", { exact: true }).click();
|
|
await expect(page).toHaveURL(/\/admin\/1\/modules\/pos\/drafts$/);
|
|
await expect(page.getByTestId("department-pos-drafts-page")).toBeVisible();
|
|
});
|
|
|
|
test("shows a department-scoped draft count badge in the desktop buefy menu", async ({ page }, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "add_order", "list_orders"],
|
|
sessionData: {
|
|
runtime_config: {
|
|
economic: {
|
|
transaction_draft_customer_number: 6001,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
await page.route(/\/orders(\?.*)?$/i, async (route) => {
|
|
const url = new URL(route.request().url());
|
|
const filters = url.searchParams.get("filters") || "";
|
|
const isDraftRequest = filters.includes("customer_id:6001");
|
|
const isDepartmentOne = filters.includes("department_id:1");
|
|
const isDepartmentTwo = filters.includes("department_id:2");
|
|
const total = isDraftRequest ? (isDepartmentOne ? 3 : isDepartmentTwo ? 0 : 0) : 0;
|
|
const perPage = Number(url.searchParams.get("limit") || "100");
|
|
const drafts =
|
|
isDraftRequest && isDepartmentOne
|
|
? [
|
|
{ id: 501, department_id: 1, customer_id: 6001, cashier_id: 5 },
|
|
{ id: 502, department_id: 1, customer_id: 6001, cashier_id: 5 },
|
|
{ id: 503, department_id: 1, customer_id: 6001, cashier_id: 5 },
|
|
]
|
|
: [];
|
|
|
|
await route.fulfill(
|
|
json({
|
|
data: drafts.slice(0, Math.min(perPage, drafts.length)),
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: perPage,
|
|
total,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.goto("/admin/1/modules/daily-report");
|
|
|
|
const draftsLabel = page.getByTestId("desktop-buefy-nav-drafts-label");
|
|
const draftsBadge = page.getByTestId("desktop-buefy-nav-drafts-badge");
|
|
|
|
await expect(draftsLabel).toContainText("Kladder");
|
|
await expect(draftsBadge).toHaveText("3");
|
|
await draftsBadge.hover();
|
|
await expect(page.getByTestId("desktop-buefy-nav-drafts-badge-tooltip")).toContainText("Kladder");
|
|
|
|
await page.goto("/admin/2/modules/daily-report");
|
|
|
|
await expect(draftsLabel).toContainText("Kladder");
|
|
await expect(draftsBadge).toHaveCount(0);
|
|
|
|
await draftsLabel.click();
|
|
await expect(page).toHaveURL(/\/admin\/2\/modules\/pos\/drafts$/);
|
|
await expect(page.getByTestId("department-pos-drafts-page")).toBeVisible();
|
|
});
|
|
|
|
test("shows a light grey loading indicator while desktop buefy draft counts are fetching", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
const requestedFilters: string[] = [];
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "add_order", "list_orders"],
|
|
sessionData: {
|
|
runtime_config: {
|
|
economic: {
|
|
transaction_draft_customer_number: 6001,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
|
|
await page.route(/\/orders(\?.*)?$/i, async (route) => {
|
|
const url = new URL(route.request().url());
|
|
const filters = url.searchParams.get("filters") || "";
|
|
requestedFilters.push(filters);
|
|
const isDraftRequest = filters.includes("customer_id:6001") && filters.includes("department_id:1");
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
|
|
await route.fulfill(
|
|
json({
|
|
data: isDraftRequest ? [{ id: 501, department_id: 1, customer_id: 6001, cashier_id: 5 }] : [],
|
|
meta: {
|
|
pagination: {
|
|
page: 1,
|
|
per_page: Number(url.searchParams.get("limit") || "100"),
|
|
total: isDraftRequest ? 1 : 0,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.goto("/admin/1/modules/daily-report");
|
|
|
|
const draftsLabel = page.getByTestId("desktop-buefy-nav-drafts-label");
|
|
const loadingBadge = page.getByTestId("desktop-buefy-nav-drafts-badge-loading");
|
|
const draftsBadge = page.getByTestId("desktop-buefy-nav-drafts-badge");
|
|
|
|
await expect(draftsLabel).toContainText("Kladder");
|
|
await expect(loadingBadge).toBeVisible();
|
|
await expect(loadingBadge).toHaveCSS("background-color", "rgb(229, 231, 235)");
|
|
await expect(draftsBadge).toHaveCount(0);
|
|
|
|
const loadingBadgeStyles = await loadingBadge.evaluate((element) => {
|
|
const styles = getComputedStyle(element);
|
|
return {
|
|
backgroundImage: styles.backgroundImage,
|
|
};
|
|
});
|
|
expect(loadingBadgeStyles.backgroundImage).not.toBe("none");
|
|
|
|
await expect(loadingBadge).toHaveCount(0);
|
|
await expect(draftsBadge).toHaveText("1");
|
|
expect(
|
|
requestedFilters.some((filters) => filters.includes("department_id:1") && filters.includes("customer_id:6001"))
|
|
).toBe(true);
|
|
});
|
|
|
|
test("fetches desktop buefy bookings counts from the department-wide navigation request", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
const requestedDepartments: string[] = [];
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "list_bookings"],
|
|
});
|
|
|
|
await page.route(/\/order-bookings\/counts(\?.*)?$/i, async (route) => {
|
|
const url = new URL(route.request().url());
|
|
const department = url.searchParams.get("department") || "";
|
|
requestedDepartments.push(department);
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
past: 0,
|
|
current: department === "1" ? 1 : 0,
|
|
future: 0,
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.goto("/admin/1/modules/daily-report");
|
|
|
|
const bookingsLabel = page.getByTestId("desktop-buefy-nav-bookings-label");
|
|
const currentBookingsBadge = page.getByTestId("desktop-buefy-nav-bookings-badge");
|
|
|
|
await expect(bookingsLabel).toContainText("Bookinger");
|
|
await expect(currentBookingsBadge).toHaveText("1");
|
|
await currentBookingsBadge.hover();
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-badge-tooltip")).toContainText("I dag");
|
|
expect(requestedDepartments).toContain("1");
|
|
});
|
|
|
|
test("resolves the desktop buefy bookings loader even when the count request is slower than the poll interval", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "list_bookings"],
|
|
});
|
|
|
|
await page.route(/\/order-bookings\/counts(\?.*)?$/i, async (route) => {
|
|
const url = new URL(route.request().url());
|
|
const department = url.searchParams.get("department") || "";
|
|
const isDepartmentOneNavigationCountRequest = department === "1";
|
|
|
|
if (isDepartmentOneNavigationCountRequest) {
|
|
await new Promise((resolve) => setTimeout(resolve, 5500));
|
|
}
|
|
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
past: 0,
|
|
current: isDepartmentOneNavigationCountRequest ? 2 : 0,
|
|
future: 0,
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.goto("/admin/1/modules/daily-report");
|
|
|
|
const loadingBadge = page.getByTestId("desktop-buefy-nav-bookings-badge-loading");
|
|
const currentBookingsBadge = page.getByTestId("desktop-buefy-nav-bookings-badge");
|
|
|
|
await expect(loadingBadge).toBeVisible();
|
|
await expect(currentBookingsBadge).toHaveCount(0);
|
|
await expect(currentBookingsBadge).toHaveText("2", { timeout: 8000 });
|
|
await expect(loadingBadge).toHaveCount(0);
|
|
});
|
|
|
|
test("shows the Bookinger-style loader on the self-serve module while its state is fetching", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "list_bookings"],
|
|
});
|
|
|
|
await page.route(/\/admin\/bookings\/department\/count(\?.*)?$/i, async (route) => {
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
data: {
|
|
message: 0,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.route(/\/order-bookings\/counts(\?.*)?$/i, async (route) => {
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
past: 0,
|
|
current: 0,
|
|
future: 0,
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.route(/\/departments\/self-serve\/enabled(\?.*)?$/i, async (route) => {
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
data: {
|
|
enabled: true,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.goto("/admin/1");
|
|
|
|
const loadingBadge = page.getByTestId("department-module-self-serve-state-loading");
|
|
await expect(loadingBadge).toBeVisible();
|
|
await expect(loadingBadge).toHaveCSS("background-color", "rgb(229, 231, 235)");
|
|
|
|
const loadingBadgeStyles = await loadingBadge.evaluate((element) => {
|
|
const styles = getComputedStyle(element);
|
|
return {
|
|
backgroundImage: styles.backgroundImage,
|
|
};
|
|
});
|
|
expect(loadingBadgeStyles.backgroundImage).not.toBe("none");
|
|
|
|
await expect(loadingBadge).toHaveCount(0);
|
|
});
|
|
|
|
test("right-aligns the self-serve module switch in the card header", async ({ page }, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "list_bookings"],
|
|
});
|
|
|
|
await page.route(/\/admin\/bookings\/department\/count(\?.*)?$/i, async (route) => {
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
data: {
|
|
message: 0,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.route(/\/order-bookings\/counts(\?.*)?$/i, async (route) => {
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
past: 0,
|
|
current: 0,
|
|
future: 0,
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.route(/\/departments\/self-serve\/enabled(\?.*)?$/i, async (route) => {
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
data: {
|
|
enabled: true,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.goto("/admin/1");
|
|
|
|
const selfServeCard = page
|
|
.locator(".department-module-card")
|
|
.filter({ hasText: /Selvvask|Self-serve/i })
|
|
.first();
|
|
await expect(selfServeCard).toBeVisible();
|
|
await expect(selfServeCard.locator(".department-module-card__switch")).toBeVisible();
|
|
|
|
const layout = await selfServeCard.evaluate((card) => {
|
|
const header = card.querySelector(".card-header");
|
|
const actions = card.querySelector(".department-module-card__actions");
|
|
const switchButton = card.querySelector(".department-module-card__switch");
|
|
|
|
if (
|
|
!(header instanceof HTMLElement) ||
|
|
!(actions instanceof HTMLElement) ||
|
|
!(switchButton instanceof HTMLElement)
|
|
) {
|
|
throw new Error("Expected self-serve module header elements to be present");
|
|
}
|
|
|
|
const headerBox = header.getBoundingClientRect();
|
|
const actionsBox = actions.getBoundingClientRect();
|
|
const switchBox = switchButton.getBoundingClientRect();
|
|
|
|
return {
|
|
actionsRightGap: headerBox.right - actionsBox.right,
|
|
switchRightGap: headerBox.right - switchBox.right,
|
|
};
|
|
});
|
|
|
|
expect(Math.abs(layout.actionsRightGap)).toBeLessThanOrEqual(1);
|
|
expect(layout.switchRightGap).toBeGreaterThanOrEqual(8);
|
|
expect(layout.switchRightGap).toBeLessThanOrEqual(20);
|
|
});
|
|
|
|
test("shows only the current order-booking badge inline and exposes past and future in a tooltip", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
const requestedDepartments: string[] = [];
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "list_bookings"],
|
|
});
|
|
|
|
await page.route(/\/order-bookings\/counts(\?.*)?$/i, async (route) => {
|
|
const url = new URL(route.request().url());
|
|
const department = url.searchParams.get("department") || "";
|
|
requestedDepartments.push(department);
|
|
|
|
await route.fulfill(
|
|
json({
|
|
data:
|
|
department === "1"
|
|
? {
|
|
past: 2,
|
|
current: 3,
|
|
future: 1,
|
|
}
|
|
: {
|
|
past: 0,
|
|
current: 0,
|
|
future: 0,
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.goto("/admin/1/modules/daily-report");
|
|
|
|
const bookingsLabel = page.getByTestId("desktop-buefy-nav-bookings-label");
|
|
const overdueBookingsBadge = page.getByTestId("desktop-buefy-nav-bookings-badge-overdue");
|
|
const currentBookingsBadge = page.getByTestId("desktop-buefy-nav-bookings-badge");
|
|
const futureBookingsBadge = page.getByTestId("desktop-buefy-nav-bookings-badge-future");
|
|
const bookingsTooltip = page.getByTestId("desktop-buefy-nav-bookings-tooltip");
|
|
|
|
await expect(bookingsLabel).toContainText("Bookinger");
|
|
await expect(currentBookingsBadge).toHaveText("3");
|
|
await expect(overdueBookingsBadge).toHaveCount(0);
|
|
await expect(futureBookingsBadge).toHaveCount(0);
|
|
expect(requestedDepartments).toContain("1");
|
|
|
|
await currentBookingsBadge.hover();
|
|
await expect(bookingsTooltip).toBeVisible();
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-tooltip-overdue")).toContainText("2");
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-tooltip-future")).toContainText("1");
|
|
|
|
await page.goto("/admin/2/modules/daily-report");
|
|
|
|
await expect(bookingsLabel).toContainText("Bookinger");
|
|
await expect(overdueBookingsBadge).toHaveCount(0);
|
|
await expect(currentBookingsBadge).toHaveCount(0);
|
|
await expect(futureBookingsBadge).toHaveCount(0);
|
|
|
|
await bookingsLabel.click();
|
|
await expect(page).toHaveURL(/\/admin\/2\/modules\/bookings$/);
|
|
});
|
|
|
|
test("keeps the bookings navigation row full width when only the fallback tooltip is available", async ({
|
|
page,
|
|
}, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "list_bookings"],
|
|
});
|
|
|
|
await page.route(/\/order-bookings\/counts(\?.*)?$/i, async (route) => {
|
|
const url = new URL(route.request().url());
|
|
const department = url.searchParams.get("department") || "";
|
|
|
|
await route.fulfill(
|
|
json({
|
|
data:
|
|
department === "1"
|
|
? {
|
|
past: 2,
|
|
current: 0,
|
|
future: 1,
|
|
}
|
|
: {
|
|
past: 0,
|
|
current: 0,
|
|
future: 0,
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.goto("/admin/1/modules/daily-report");
|
|
|
|
const bookingsLabel = page.getByTestId("desktop-buefy-nav-bookings-label");
|
|
await expect(bookingsLabel).toContainText("Bookinger");
|
|
|
|
const bookingsLabelBox = await bookingsLabel.boundingBox();
|
|
expect(bookingsLabelBox?.width ?? 0).toBeGreaterThan(250);
|
|
|
|
await bookingsLabel.hover();
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-tooltip-overdue")).toContainText("2");
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-tooltip-future")).toContainText("1");
|
|
});
|
|
|
|
test("uses the cached bookings counts endpoint for desktop buefy menu badges", async ({ page }, testInfo) => {
|
|
test.skip(!isDesktopProject(testInfo), "Desktop only");
|
|
|
|
const countRequests: string[] = [];
|
|
const listRequests: string[] = [];
|
|
|
|
await mockAdminDepartmentDependencies(page, {
|
|
permissions: [...adminPermissions, "list_bookings"],
|
|
});
|
|
|
|
await page.route(/\/order-bookings\/counts(\?.*)?$/i, async (route) => {
|
|
const url = new URL(route.request().url());
|
|
countRequests.push(url.searchParams.get("department") || "");
|
|
|
|
await route.fulfill(
|
|
json({
|
|
data: {
|
|
past: 1,
|
|
current: 2,
|
|
future: 3,
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.route(/\/order-bookings(\?.*)?$/i, async (route) => {
|
|
listRequests.push(route.request().url());
|
|
await route.fulfill(
|
|
json({
|
|
success: true,
|
|
data: [],
|
|
meta: {
|
|
pagination: {
|
|
total: 0,
|
|
},
|
|
},
|
|
})
|
|
);
|
|
});
|
|
|
|
await page.goto("/admin/1/modules/daily-report");
|
|
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-badge-overdue")).toHaveCount(0);
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-badge")).toHaveText("2");
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-badge-future")).toHaveCount(0);
|
|
|
|
await page.getByTestId("desktop-buefy-nav-bookings-badge").hover();
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-tooltip-overdue")).toContainText("1");
|
|
await expect(page.getByTestId("desktop-buefy-nav-bookings-tooltip-future")).toContainText("3");
|
|
|
|
expect(countRequests).toContain("1");
|
|
expect(listRequests).toEqual([]);
|
|
});
|
|
});
|