import { test, expect } from "@playwright/test" import axe from "axe-core" for (const path of ["/bgp", "/gre", "/vxlan", "/ospf"]) for (const width of [320, 390]) test(`Network KPI labels remain readable ${path}/${width}`, async ({ page }) => { await page.setViewportSize({ width, height: 1000 }) await page.route("**/health", r => r.fulfill({ json: { ok: true } })) await page.route("**/api/**", r => { const endpoint = new URL(r.request().url()).pathname return r.fulfill({ json: endpoint === "/api/auth/config" ? { required: false } : endpoint === "/api/servers" ? [{ id: 1, name: "Роутер", host: "192.0.2.1", enabled: true, country: "RU", status: null }] : endpoint === "/api/bgp/catalog" ? { sessions: [], failures: [] } : endpoint === "/api/ospf/catalog" ? { interfaces: [], neighbors: [], instances: [], bfdSessions: [], routes: [], failures: [] } : endpoint === "/api/gre/tunnels" ? { tunnels: [], failures: [] } : endpoint === "/api/vxlan" ? { tunnels: [], failures: [] } : {} }) }) await page.goto(path) const title = path === "/bgp" ? "Сводка BGP" : path === "/gre" ? "Сводка GRE" : path === "/vxlan" ? "Сводка VXLAN" : "Сводка OSPF" const summary = page.getByLabel(title, { exact: true }) await expect(summary).toBeVisible() const labels = summary.locator('.text-muted-foreground.truncate') await expect.poll(() => labels.count()).toBeGreaterThan(0) // Compare actual rendered text bounds, rather than testing particular grid classes. await expect.poll(() => labels.evaluateAll(elements => elements.every(el => el.scrollWidth <= el.clientWidth + 1))).toBe(true) expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true) }) for (const theme of ["light", "dark"]) test(`ECMP draft: disabled instructions remain readable and enable is local/${theme}`, async ({ page }) => { await page.addInitScript(value => localStorage.setItem("rl-theme", value), theme) let writes = 0 await page.route("**/health", r => r.fulfill({ json: { ok: true } })) await page.route("**/api/**", r => { const path = new URL(r.request().url()).pathname if (r.request().method() !== "GET" && !path.endsWith("/preview")) writes++ return r.fulfill({ json: path === "/api/auth/config" ? { required: false } : path === "/api/servers" ? [{ id: 1, name: "Роутер", host: "192.0.2.1", type: "home-router", country: "RU", enabled: true, status: null }] : path === "/api/filters/rules" ? { rulesets: [] } : path === "/api/uptime/speed-probes" ? { probes: [] } : path === "/api/ospf/catalog" ? { interfaces: [], instances: [], neighbors: [], bfdSessions: [], routes: [], failures: [] } : path.endsWith("/preview") ? { serverId: 1, serverName: "Роутер", pingWeight: 60, interfacesTotal: 0, changedCount: 0, unchangedCount: 0, interfaces: [], changes: [] } : {} }) }) await page.goto("/route-optimizer") const toggle = page.getByRole("switch", { name: "Черновик ECMP", exact: true }) const group = page.getByRole("group", { name: "Параметры черновика ECMP", exact: true }) await expect(toggle).toHaveAttribute("aria-checked", "false") const range = group.getByRole("slider", { name: "Максимальное число путей ECMP", exact: true }) await expect(range).toBeDisabled() await group.scrollIntoViewIfNeeded() await page.addScriptTag({ content: axe.source }) for (const enabled of [false, true]) { if (enabled) { await toggle.click(); await expect(range).toBeEnabled(); await range.focus(); await range.press("ArrowRight"); await expect(range).toHaveValue("5") } const violations = await group.evaluate(async element => (await (window as unknown as { axe: typeof axe }).axe.run(element, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "wcag22aa"] }, })).violations.map(v => ({ id: v.id, targets: v.nodes.map(n => n.target) }))) expect(violations).toEqual([]) } expect(writes).toBe(0) })