111 lines
5.8 KiB
TypeScript
111 lines
5.8 KiB
TypeScript
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/)
|
||
})
|