import { expect, test, type Locator } from "@playwright/test"; async function expectHelpBesideHeading(anchor: Locator) { const heading = anchor.locator(":scope > :is(h1,h2,h3)"); const link = anchor.getByRole("link"); await expect(heading).toBeVisible(); await expect(link).toBeVisible(); await expect(heading.locator("a,button,[role=status]")).toHaveCount(0); const geometry = await anchor.evaluate(element => { const heading = element.querySelector(":scope > :is(h1,h2,h3)")!.getBoundingClientRect(); const link = element.querySelector("a")!.getBoundingClientRect(); return { gap: link.left - heading.right, right: link.right, viewport: window.innerWidth }; }); expect(geometry.gap).toBeGreaterThanOrEqual(4); expect(geometry.gap).toBeLessThanOrEqual(8); expect(geometry.right).toBeLessThanOrEqual(geometry.viewport); } for (const width of [390, 3085]) { for (const language of ["en", "de"]) { test(`documentation remains beside headings during loading and long-title wrapping (${width}px, ${language})`, async ({ page }) => { await page.setViewportSize({ width, height: 1100 }); await page.goto(`/?heading-help&language=${language}`); const pageTitle = page.locator(".page-title-with-loader"); await expect(page.getByRole("heading", { level: 1, name: "Dashboard", exact: true })).toBeVisible(); await expect(pageTitle.getByRole("status")).toBeVisible(); await expect(pageTitle.getByRole("link")).toHaveAccessibleName(language === "de" ? "Benutzerdokumentation öffnen" : "Open user documentation"); for (const anchor of await page.locator(".text-with-help:has(> :is(h1,h2,h3))").all()) await expectHelpBesideHeading(anchor); const rawLabel = page.getByTestId("raw-text-help"); const rawGeometry = await rawLabel.evaluate(element => ({ width: element.clientWidth, scrollWidth: element.scrollWidth, right: element.getBoundingClientRect().right, helpRight: element.querySelector("a")!.getBoundingClientRect().right })); expect(rawGeometry.scrollWidth).toBeLessThanOrEqual(rawGeometry.width); expect(rawGeometry.helpRight).toBeLessThanOrEqual(rawGeometry.right); await page.getByRole("button", { name: "Toggle loading", exact: true }).click(); await expect(pageTitle.getByRole("status")).toHaveCount(0); await expectHelpBesideHeading(pageTitle.locator(".text-with-help")); await expect(page.locator('[data-page-action-slot="help"]')).toHaveCount(0); expect(await page.evaluate(() => document.documentElement.scrollWidth - window.innerWidth)).toBeLessThanOrEqual(1); }); } } test("card help stays available when collapsed and opening it does not toggle content", async ({ page, context }) => { await context.route("https://govoplan.add-ideas.de/**", route => route.fulfill({ body: "Documentation fixture" })); await page.goto("/?heading-help&language=en"); const card = page.getByTestId("help-card"); const popupPromise = page.waitForEvent("popup"); await card.getByRole("link").click(); const popup = await popupPromise; await popup.close(); await expect(page.getByTestId("help-card-content")).toBeVisible(); await card.getByRole("button", { name: "Show header only", exact: true }).click(); await expect(page.getByTestId("help-card-content")).toHaveCount(0); await expectHelpBesideHeading(card.locator(".text-with-help")); await card.getByRole("button", { name: "Show content", exact: true }).click(); await expect(page.getByTestId("help-card-content")).toBeVisible(); }); test("dialog help has its own name, remains in keyboard order, and does not dismiss the dialog", async ({ page, context }) => { await context.route("https://govoplan.add-ideas.de/**", route => route.fulfill({ body: "Documentation fixture" })); await page.setViewportSize({ width: 390, height: 1000 }); await page.goto("/?heading-help&language=en"); await page.getByTestId("open-help-dialog").click(); const dialog = page.getByRole("dialog"); await expect(dialog).toHaveAccessibleName(/^Dashboard DokumentationsüberschriftOhneLeerzeichen/); await expectHelpBesideHeading(dialog.locator(".text-with-help")); const help = dialog.getByRole("link"); await expect(help).toBeFocused(); await help.press("Shift+Tab"); await expect(page.getByTestId("dialog-last-action")).toBeFocused(); await page.getByTestId("dialog-last-action").press("Tab"); await expect(help).toBeFocused(); const popupPromise = page.waitForEvent("popup"); await help.click(); const popup = await popupPromise; await popup.close(); await expect(dialog).toBeVisible(); await dialog.getByRole("button", { name: "Close", exact: true }).click(); await expect(dialog).toHaveCount(0); await expect(page.getByTestId("open-help-dialog")).toBeFocused(); }); test("widget documentation stays beside its one existing title in display, configuration, and drag markup", async ({ page }) => { await page.goto("/?heading-help&widgets&language=en"); const title = page.getByRole("heading", { name: "Scheduling requests", exact: true }); const anchor = page.locator(".card-title-with-help"); await expect(title).toHaveCount(1); await expectHelpBesideHeading(anchor); await expect(anchor.getByRole("link")).toHaveAttribute("href", "/docs?type=user&topic=scheduling.find-and-decide-meeting-time"); await page.getByRole("button", { name: "Toggle configuration", exact: true }).click(); await expect(title).toHaveCount(1); await expectHelpBesideHeading(anchor); await expect(page.locator(".card-actions .documentation-help-link")).toHaveCount(0); await page.getByRole("button", { name: "Toggle drag preview", exact: true }).click(); // A drag placeholder retains hidden content only to preserve the widget height. const preview = page.locator(".dashboard-widget-placeholder-content"); await expect(preview.locator("h2")).toHaveCount(1); await expect(preview.locator(".card-title-with-help .documentation-help-link")).toHaveAttribute("href", "/docs?type=user&topic=scheduling.find-and-decide-meeting-time"); await expect(preview).toBeHidden(); await page.getByRole("button", { name: "Toggle drag preview", exact: true }).click(); await expect(title).toHaveCount(1); await expectHelpBesideHeading(anchor); });