import { test, expect, type Page } from "@playwright/test" import type { EvoBgpCatalogSnapshot } from "../../lib/evobgp-context" import { statisticsDtoSchema } from "../../packages/contracts/src/statistics" const catalog: EvoBgpCatalogSnapshot = { fetchedAt: "2026-09-13T00:00:00Z", modules: [], asns: Array.from({ length: 12 }, (_, i) => ({ id: String(i + 1), asn: `AS${65001 + i}`, org: `Организация ${i + 1}`, country: "RU", prefixes: i + 1, filter: "", updated: "2026-09-13", enabled: true, })), domains: Array.from({ length: 12 }, (_, i) => ({ id: String(i + 1), domain: `test${i + 1}.example`, resolvedIp: `192.0.2.${i + 1}`, asn: "AS65001", purpose: "Тест", filter: "", updated: "2026-09-13", enabled: true, })), ipRanges: Array.from({ length: 12 }, (_, i) => ({ id: String(i + 1), cidr: `192.0.${i + 1}.0/24`, asn: "AS65001", country: "RU", purpose: "Тест", filter: "", updated: "2026-09-13", enabled: true, })), communities: Array.from({ length: 12 }, (_, i) => ({ id: String(i + 1), value: `65001:${i + 1}`, name: `Community ${i + 1}`, description: `Описание ${i + 1}`, type: "standard", filterIds: [], serverCount: i + 1, prefixCount: i + 1, action: "permit", enabled: true, })), } async function setup(page: Page, path: string) { 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/auth/config" ? { required: false } : path === "/api/evobgp/settings" ? { enabled: true, baseUrl: "", secretConfigured: true } : path === "/api/evobgp/catalog" ? catalog : path === "/api/servers" ? [] : {} return route.fulfill({ json }) }) await page.goto(path) } for (const [path, placeholder, last] of [ ["/asns", "Поиск по ASN, имени, префиксам…", "AS65012"], ["/domains", "Поиск по домену…", "test12.example"], ["/ip-ranges", "Поиск по CIDR, ASN…", "192.0.12.0/24"], ]) { test(`${path}: страницы и поиск с последней страницы`, async ({ page }) => { const errors: string[] = [] page.on("pageerror", e => errors.push(e.message)) await setup(page, path) const rows = page.locator("tr[data-row-id]") await expect(page.getByText("1–10 из 12", { exact: true })).toBeVisible() await expect(rows).toHaveCount(10) await page.getByRole("button", { name: "Следующая страница", exact: true }).click() await expect(rows).toHaveCount(2) await expect(rows.last()).toContainText(last) await page.getByPlaceholder(placeholder).fill(last) await expect(rows).toHaveCount(1) await expect(rows.first()).toContainText(last) await expect(page.getByText("1–1 из 1", { exact: true })).toBeVisible() await page.getByPlaceholder(placeholder).fill("несуществующее значение") await expect(rows).toHaveCount(0) await page.getByPlaceholder(placeholder).fill("") await expect(rows).toHaveCount(10) expect(errors).toEqual([]) }) } test("ASN: числовая сортировка охватывает обе страницы", async ({ page }) => { await setup(page, "/asns") await expect(page.getByText("1–10 из 12", { exact: true })).toBeVisible() await page.getByRole("button", { name: "Префиксов", exact: true }).click() await expect(page.locator("tr[data-row-id]").first()).toContainText("AS65012") await page.getByRole("button", { name: "Следующая страница", exact: true }).click() await expect(page.locator("tr[data-row-id]").last()).toContainText("AS65001") }) test("Communities: все 12 строк и выбор деталей", async ({ page }) => { await setup(page, "/communities") await expect(page.locator("tr[data-row-id]")).toHaveCount(12) await page.locator('tr[data-row-id="12"]').click() await expect(page.locator('tr[data-row-id="12"]')).toHaveAttribute("data-state", "selected") await expect(page.getByText("Описание 12", { exact: true })).toHaveCount(2) await expect(page.getByText("Описание 12", { exact: true }).last()).toBeVisible() await page.screenshot({ path: "test-results/communities-migrated.png", fullPage: true }) }) test("Статистика: CompactDataGrid сохраняет 12 строк, сортировку и переход в срез", async ({ page }) => { const dto = statisticsDtoSchema.parse({ from: "2026-09-01", to: "2026-09-13", grain: "day", kpis: { bytes: 7800, packets: 78, avgBps: 100, users: 12, servers: 1, ifaces: 1, topCountry: "RU", topService: "https" }, series: [], servers: [], interfaces: [], countries: [], services: [], asns: [], users: Array.from({ length: 12 }, (_, i) => ({ id: `user-${i + 1}`, label: `Пользователь ${i + 1}`, bytes: (i + 1) * 100, packets: i + 1, bps: i + 1, percent: i + 1, })), }) await page.route("**/api/statistics?**", route => route.fulfill({ json: dto })) // The general API handler falls back to the previously registered statistics route. await page.route("**/health", route => route.fulfill({ json: { ok: true } })) await page.route("**/api/**", route => { const path = new URL(route.request().url()).pathname if (path === "/api/statistics") return route.fallback() return route.fulfill({ json: path === "/api/auth/config" ? { required: false } : path === "/api/servers" ? [] : {} }) }) await page.goto("/statistics?from=2026-09-01&to=2026-09-13&dim=users") const rows = page.locator("tr[data-row-id]") await expect(rows).toHaveCount(12) await page.getByRole("button", { name: "Байты", exact: true }).first().click() await expect(rows.first()).toContainText("Пользователь 12") await rows.first().click() await expect(page).toHaveURL(/userId=user-12/) })