test(webui): cover contextual help in the browser

This commit is contained in:
2026-08-21 17:36:06 +02:00
parent fbea74a74b
commit 5a9e8f79f9
7 changed files with 124 additions and 12 deletions
+28
View File
@@ -26,6 +26,7 @@ import WorkspaceFrame from "../src/components/WorkspaceFrame";
import WorkspaceLayout from "../src/components/WorkspaceLayout";
import WorkspaceActionBar from "../src/components/WorkspaceActionBar";
import BreadcrumbBar from "../src/layout/BreadcrumbBar";
import HelpMenu from "../src/layout/HelpMenu";
import { useGuardedNavigate } from "../src/components/UnsavedChangesGuard";
import {
createQuickAccessLaunchContext,
@@ -149,6 +150,7 @@ export default function ConformanceApp() {
</section>
<LaunchContextScenario />
{new URLSearchParams(location.search).has("help") ? <HelpConformanceScenario /> : null}
</PageLayout>
<Dialog
@@ -170,6 +172,32 @@ export default function ConformanceApp() {
);
}
function HelpConformanceScenario() {
return (
<section className="conformance-section" aria-labelledby="help-heading">
<h2 id="help-heading">Kontextsensitive Hilfe</h2>
<p>F1 löst den Hilfekontext des fokussierten Bedienelements auf und kehrt nach dem Schließen dorthin zurück.</p>
<ActionToolbar surface="subtle">
<Button
variant="danger"
data-testid="f1-retention-action"
helpContextId="policy.retention.action.apply"
helpModuleId="policy"
>
Aufbewahrungsregeln anwenden
</Button>
<Button
data-testid="f1-fallback-action"
interfaceId="core.conformance.action.review"
>
Allgemeine Aktion prüfen
</Button>
<HelpMenu auth={null} />
</ActionToolbar>
</section>
);
}
function QuickAccessScenario() {
const location = useLocation();
const mode = new URLSearchParams(location.search).get("quick-access");
+3
View File
@@ -0,0 +1,3 @@
const installedModules: Array<never> = [];
export default installedModules;
+22 -3
View File
@@ -3,6 +3,9 @@ import ReactDOM from "react-dom/client";
import { BrowserRouter } from "react-router";
import ConformanceApp from "./ConformanceApp";
import { UnsavedChangesProvider } from "../src/components/UnsavedChangesGuard";
import { PlatformLanguageProvider } from "../src/i18n/LanguageContext";
import { PlatformModulesProvider } from "../src/platform/ModuleContext";
import type { PlatformWebModule } from "../src/types";
import "../src/styles/tokens.css";
import "../src/styles/layout.css";
import "../src/styles/forms.css";
@@ -16,12 +19,28 @@ import "./conformance.css";
const theme = new URLSearchParams(window.location.search).get("theme");
if (theme === "dark" || theme === "light") document.documentElement.dataset.theme = theme;
const CONFORMANCE_MODULES: PlatformWebModule[] = [{
id: "policy",
label: "Richtlinien",
version: "1",
helpContexts: [{
id: "policy.retention.action.apply",
topic_id: "policy.admin.retention-and-disposition",
title: "Aufbewahrungsregeln sicher anwenden",
documentation_types: ["admin"]
}]
}];
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<BrowserRouter>
<UnsavedChangesProvider>
<ConformanceApp />
</UnsavedChangesProvider>
<PlatformModulesProvider modules={CONFORMANCE_MODULES}>
<PlatformLanguageProvider preferredLanguageCode="de">
<UnsavedChangesProvider>
<ConformanceApp />
</UnsavedChangesProvider>
</PlatformLanguageProvider>
</PlatformModulesProvider>
</BrowserRouter>
</React.StrictMode>
);
@@ -110,6 +110,44 @@ test("Quick Access preserves focus and adapts to a narrow viewport", async ({ pa
await expect(filesTrigger).toBeFocused();
});
test("F1 resolves focused high-risk help and restores focus accessibly", async ({ page }) => {
await page.setViewportSize({ width: 390, height: 844 });
await page.goto("/?theme=light&help=1");
const retentionAction = page.getByTestId("f1-retention-action");
await retentionAction.focus();
await page.keyboard.press("F1");
const dialog = page.getByRole("dialog", { name: "Kontexthilfe" });
await expect(dialog).toBeVisible();
await expect(dialog.locator("[data-help-context]"))
.toHaveAttribute("data-help-context", "policy.retention.action.apply");
await expect(dialog.getByRole("heading", { level: 3 }))
.toHaveText("Aufbewahrungsregeln anwenden");
await expect(dialog).toContainText("topic=policy.admin.retention-and-disposition");
await expect(dialog.getByRole("button", { name: "Administrator-Dokumentation öffnen" }))
.toBeVisible();
await expectNoAccessibilityViolations(page);
const bounds = await dialog.boundingBox();
expect(bounds?.x).toBeGreaterThanOrEqual(0);
expect((bounds?.x ?? 0) + (bounds?.width ?? 0)).toBeLessThanOrEqual(390);
await page.keyboard.press("Escape");
await expect(dialog).toBeHidden();
await expect(retentionAction).toBeFocused();
const fallbackAction = page.getByTestId("f1-fallback-action");
await fallbackAction.focus();
await page.keyboard.press("F1");
const fallbackDialog = page.getByRole("dialog", { name: "Kontexthilfe" });
await expect(fallbackDialog.locator("[data-help-context]"))
.toHaveAttribute("data-help-context", "core.conformance.action.review");
await expect(fallbackDialog).toContainText("fallback_context=campaigns.list");
await page.keyboard.press("Escape");
await expect(fallbackAction).toBeFocused();
});
test("View focus has a deliberate permission-derived all-tools escape", async ({ page }) => {
await page.route("**/api/v1/quick-access/effective*", async (route) => {
await route.fulfill({