import { test, expect, type Page } from "@playwright/test" const ids = Array.from({ length: 12 }, (_, i) => i + 1) const host = { serverId: 1, serverName: "Тестовый роутер", serverSite: "TEST", serverCountry: "RU" } const ospf = { instances: [], interfaces: [], neighbors: ids.map(i => ({ ...host, serverId: i, id: String(i), address: `192.0.2.${i}`, routerId: `192.0.2.${i}`, instance: "default", area: "0.0.0.0", areaId: "0.0.0.0", interface: `ether${i}`, state: "Full", uptime: "1h", stateChanges: 0, priority: i })), routes: ids.map(i => ({ ...host, id: String(i), destination: `10.0.${i}.0/24`, type: "O", cost: i, nextHop: "192.0.2.1", via: "ether1", area: "0.0.0.0" })), bfdSessions: ids.map(i => ({ ...host, id: String(i), localAddr: "192.0.2.100", remoteAddr: `192.0.2.${i}`, interface: `ether${i}`, state: "Up", uptime: "1h", multihop: false, multiplier: 3, txInterval: 200, rxInterval: 200, holdTime: 600, packetsRx: i, packetsTx: i, stateChanges: 0 })), } const bgp = ids.map(i => ({ ...host, id: String(i), name: `Пир ${i}`, peerIp: `192.0.2.${i}`, remoteAs: 65000 + i, localAs: 64512, localId: "192.0.2.100", remoteId: `192.0.2.${i}`, state: i > 6 ? "Active" : "Established", type: "eBGP", uptime: "1h", holdTime: 90, keepalive: 30, prefixesRx: i, prefixesTx: i, inputMessages: i, outputMessages: i, capabilities: ["route-refresh"], lastError: null })) async function setup(page: Page, path: string, empty = false) { await page.route("**/health", route => route.fulfill({ json: { ok: true } })) await page.route("**/api/**", route => { const path = new URL(route.request().url()).pathname const json = path === "/api/ospf/catalog" ? (empty ? { instances: [], interfaces: [], neighbors: [], routes: [], bfdSessions: [], failures: [] } : { ...ospf, failures: [] }) : path === "/api/bgp/catalog" ? { sessions: empty ? [] : bgp, failures: [] } : path === "/api/auth/config" ? { required: false } : path === "/api/servers" ? ids.map(id => ({ id, name: "Тестовый роутер " + id, host: "192.0.2." + id, country: "RU", enabled: true, status: null })) : {} return route.fulfill({ json }) }) await page.goto(path) } test("OSPF: все строки соседей, маршрутов и BFD; выбор и числовая сортировка", async ({ page }) => { const errors: string[] = [] page.on("pageerror", e => errors.push(e.message)) await setup(page, "/ospf") await page.getByRole("button", { name: "Соседи", exact: true }).click() const rows = page.locator("tr[data-row-id]") await expect(rows).toHaveCount(12) await rows.last().click() await expect(page.locator('tr[data-row-id="12-12"]')).toHaveAttribute("data-state", "selected") await page.getByRole("button", { name: "Маршруты", exact: true }).click() await expect(rows).toHaveCount(12) await page.getByRole("button", { name: "Cost", exact: true }).click() await expect(rows.first()).toContainText("10.0.12.0/24") await page.getByRole("button", { name: "BFD", exact: true }).click() await expect(rows).toHaveCount(12) await page.getByRole("button", { name: "Пакеты Rx", exact: true }).click() await expect(rows.first()).toContainText("192.0.2.12") expect(errors).toEqual([]) }) test("BGP: 12 сессий, сортировка и раскрытие после сортировки", async ({ page }) => { await setup(page, "/bgp") const rows = page.locator("tr[data-row-id]") await expect(rows).toHaveCount(12) await page.getByRole("button", { name: "Remote AS", exact: true }).click() await expect(rows.first()).toContainText("65012") await rows.first().click() await expect(page.getByText("route-refresh", { exact: true })).toBeVisible() await rows.first().click() await expect(page.getByText("route-refresh", { exact: true })).toBeHidden() }) test("Пустые ответы API показывают пустые состояния BGP и OSPF", async ({ page }) => { await setup(page, "/bgp", true) await expect(page.getByText("Нет BGP-сессий", { exact: true })).toBeVisible() await page.goto("/ospf") for (const [tab, message] of [["Соседи", "Нет OSPF-соседей"], ["Маршруты", "Нет OSPF-маршрутов"], ["BFD", "BFD-сессий не обнаружено ни на одном сервере"]]) { await page.getByRole("button", { name: tab, exact: true }).click() await expect(page.getByText(message, { exact: true })).toBeVisible() } }) test("BGP FilterQuery: выбор, AND и сброс", async ({ page }) => { await setup(page, "/bgp") const rows = page.locator("tr[data-row-id]") await expect(rows).toHaveCount(12) await page.getByRole("button", { name: "Фильтры", exact: true }).click() await page.getByRole("option", { name: "Состояние", exact: true }).click() await page.getByRole("option", { name: "равно", exact: true }).click() await page.getByRole("option", { name: "Active", exact: true }).click() await expect(rows).toHaveCount(6) await expect(rows.first()).toContainText("Пир 7") await page.getByRole("button", { name: "Фильтры", exact: true }).click() await page.getByRole("option", { name: "Тип", exact: true }).click() await page.getByRole("option", { name: "равно", exact: true }).click() await page.getByRole("option", { name: "iBGP", exact: true }).click() await expect(rows).toHaveCount(0) await page.getByRole("button", { name: "Очистить", exact: true }).click() await expect(rows).toHaveCount(12) }) test("BGP: фильтры и поиск из URL переживают reload и возврат", async ({ page }) => { await setup(page, "/bgp?keep=x&state=Active&type=eBGP&q=Пир%207") const rows = page.locator("tr[data-row-id]") await expect(rows).toHaveCount(1) await expect(rows.first()).toContainText("Пир 7") await page.reload() await expect(rows).toHaveCount(1) await page.getByRole("button", { name: "Сбросить фильтры BGP", exact: true }).click() await expect(rows).toHaveCount(12) await page.goBack() await expect(rows).toHaveCount(1) await page.getByRole("button", { name: "Фильтры", exact: true }).click() await page.getByRole("option", { name: "Состояние", exact: true }).click() await page.getByRole("option", { name: "равно", exact: true }).click() await page.getByRole("option", { name: "Active", exact: true }).click() await expect.poll(() => new URL(page.url()).searchParams.has("filters")).toBe(true) await page.reload() await expect(rows).toHaveCount(1) expect(new URL(page.url()).searchParams.get("keep")).toBe("x") }) for (const invalid of ["state=broken", "type=broken", "q=a&q=b", "filters=%7B", "filters=" + encodeURIComponent(JSON.stringify({ v: 1, query: { id: "root", type: "group", combinator: "and", rules: [{ id: "x", type: "rule", path: ["password"], operator: "is", value: "x" }] } }))]) { test("BGP: некорректный URL-фильтр не расширяет срез " + invalid.slice(0, 30), async ({ page }) => { await setup(page, "/bgp?keep=x&" + invalid) await expect(page.getByRole("alert").filter({ hasText: "ссылк" })).toBeVisible() await expect(page.locator("tr[data-row-id]")).toHaveCount(0) await page.getByRole("button", { name: "Сбросить фильтры BGP", exact: true }).click() await expect(page.locator("tr[data-row-id]")).toHaveCount(12) expect(new URL(page.url()).searchParams.get("keep")).toBe("x") }) }