Files
govoplan-core/webui/conformance/tests/navigation-layout.spec.ts
T
zemion 6d37aa527f fix(ui): unify heading help, table sizing and navigation contracts
Verified with the coordinated workspace changes by devkit full run
2026-09-08T225814-186389-0000-3e3ed7cd (all seven phases passed).
This shared UI pass does not mark the individual module reviews complete.
2026-09-09 02:03:16 +02:00

93 lines
6.1 KiB
TypeScript

import { expect, test } from "@playwright/test";
test("collapsed rail keeps visible group separators; opening settings is clean", async ({ page }) => {
await page.goto("/?navigation-layout");
const rail = page.locator(".icon-rail");
await expect(rail).not.toHaveClass(/expanded/);
await expect(rail.getByRole("separator")).toHaveCount(2);
for (const separator of await rail.getByRole("separator").all()) {
await expect(separator).toBeVisible();
expect((await separator.boundingBox())!.width).toBeGreaterThan(20);
}
await expect(page.getByTestId("navigation-draft")).toHaveText("null");
await expect(page.locator('[data-navigation-layout-status="inherited"]')).toContainText("standard layout groups available modules by product area");
const spacing = await page.locator(".navigation-preference-list > li").first().evaluate((row) => {
const style = getComputedStyle(row);
return { padding: Number.parseFloat(style.paddingLeft), gap: Number.parseFloat(style.columnGap) };
});
expect(spacing.padding).toBeGreaterThanOrEqual(8);
expect(spacing.gap).toBeGreaterThanOrEqual(8);
await page.getByRole("button", { name: "Expand navigation", exact: true }).click();
await expect(rail.getByText("Documents", { exact: true })).toBeVisible();
await expect(rail.getByRole("separator")).toHaveCount(0);
await page.getByRole("button", { name: "Collapse navigation", exact: true }).click();
await expect(rail.getByRole("separator")).toHaveCount(2);
await expect(rail.getByText("Documents", { exact: true })).toBeHidden();
});
for (const scope of ["system", "tenant", "user", "view"]) {
test(`same editor supports add/remove, keyboard reorder and inheritance at ${scope} scope`, async ({ page }) => {
await page.goto(`/?navigation-layout&scope=${scope}`);
const list = page.getByRole("list", { name: "Navigation layout" });
const files = list.locator('[data-navigation-id="files"]');
await files.getByRole("button", { name: "Remove Files", exact: true }).click();
await expect(files).toHaveCount(0);
await page.getByRole("button", { name: "Add module", exact: true }).click();
await expect(list.locator("li").last()).toHaveAttribute("data-navigation-id", "files");
const handle = files.getByRole("button", { name: "Reorder Files", exact: true });
const before = await list.locator("li").evaluateAll((rows) => rows.map((row) => row.getAttribute("data-navigation-id")));
await handle.press("Space");
await handle.press("ArrowUp");
await expect(list.locator("li").last()).not.toHaveAttribute("data-navigation-id", "files");
await handle.press("Escape");
expect(await list.locator("li").evaluateAll((rows) => rows.map((row) => row.getAttribute("data-navigation-id")))).toEqual(before);
await handle.press("Space"); await handle.press("ArrowUp"); await handle.press("Enter");
await page.getByRole("button", { name: "Add separator", exact: true }).click();
const separator = list.locator("li").last();
await expect(separator).toHaveAttribute("data-navigation-kind", "separator");
await separator.getByRole("textbox").fill("Custom group");
const saved = JSON.parse(await page.getByTestId("navigation-draft").textContent() ?? "null");
expect(saved.separators.at(-1).label).toBe("Custom group");
await separator.getByRole("button", { name: "Remove Custom group", exact: true }).click();
if (scope !== "system") await expect(list.getByRole("button", { name: "Remove Dashboard", exact: true })).toBeDisabled();
await page.getByRole("button", { name: "Use inherited layout", exact: true }).click();
await expect(page.getByTestId("navigation-draft")).toHaveText("null");
});
}
test("native pointer drag reorders modules and separators", async ({ page }) => {
await page.goto("/?navigation-layout");
const list = page.getByRole("list", { name: "Navigation layout" });
const files = list.locator('[data-navigation-id="files"]');
await files.getByRole("button", { name: "Reorder Files", exact: true }).dragTo(list.locator("li").first(), { targetPosition: { x: 15, y: 3 } });
await expect(list.locator("li").first()).toHaveAttribute("data-navigation-id", "files");
const separator = list.locator('[data-navigation-kind="separator"]').first();
const separatorId = await separator.getAttribute("data-navigation-id");
await separator.getByRole("button", { name: /^Reorder / }).dragTo(list.locator("li").first(), { targetPosition: { x: 15, y: 3 } });
await expect(list.locator("li").first()).toHaveAttribute("data-navigation-id", separatorId!);
});
test("no-op reordering stays clean and optional module positions survive other edits", async ({ page }) => {
await page.goto("/?navigation-layout");
const handle = page.getByRole("button", { name: "Reorder Files", exact: true });
await handle.press("Space"); await handle.press("Enter");
await expect(page.getByTestId("navigation-draft")).toHaveText("null");
await page.goto("/?navigation-layout&unavailable");
await expect(page.locator('[data-navigation-layout-status="flat"]')).toContainText("without group headings or dividers");
await expect(page.locator(".icon-rail").getByRole("separator")).toHaveCount(0);
await page.getByRole("button", { name: "Move Mail up", exact: true }).click();
const draft = JSON.parse(await page.getByTestId("navigation-draft").textContent() ?? "null");
expect(draft.order).toContain("optional.navigation.absent");
await page.getByRole("button", { name: "Use inherited layout", exact: true }).click();
await expect(page.locator(".icon-rail").getByRole("separator")).toHaveCount(2);
await expect(page.getByTestId("navigation-draft")).toHaveText("null");
});
test("German narrow editor does not overflow and read-only controls cannot mutate", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 900 });
await page.goto("/?navigation-layout&language=de&disabled");
await expect(page.getByRole("button", { name: "Trennlinie hinzufügen", exact: true })).toBeDisabled();
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true);
await expect(page.getByTestId("navigation-draft")).toHaveText("null");
});