64 lines
6.0 KiB
TypeScript
64 lines
6.0 KiB
TypeScript
import { test, expect } from "@playwright/test"
|
||
import { writeFileSync } from "node:fs"
|
||
import axe from "axe-core"
|
||
import { optimizerServersSchema } from "../../lib/route-optimizer-response"
|
||
|
||
test("Оптимизатор1000 маршрутов: WAN10 × JH10 × Exit10", async ({ page }, info) => {
|
||
test.setTimeout(90000)
|
||
const common = { host: "192.0.2.1", enabled: true, status: "online", latency: 10, country: "RU", site: "TEST", model: "CHR" }
|
||
let servers = optimizerServersSchema.parse([
|
||
{ ...common, id: 1, name: "Домашний роутер", type: "home-router", wanUplinks: Array.from({ length: 10 }, (_, i) => ({ id: `wan${i+1}`, name: `WAN${i+1}`, isp: "ISP", iface: `ether${i+1}`, ip: `198.51.100.${i+1}`, maxDl: 100, maxUl: 100 })) },
|
||
...Array.from({ length: 10 }, (_, i) => ({ ...common, id: i+2, name: `Jump ${i+1}`, type: "jump-host" })),
|
||
...Array.from({ length: 10 }, (_, i) => ({ ...common, id: i+12, name: `Exit ${i+1}`, type: "exit-node" })),
|
||
])
|
||
let reads = 0
|
||
const writes: string[] = []
|
||
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
|
||
await page.route("**/api/**", r => {
|
||
const path = new URL(r.request().url()).pathname
|
||
if (path === "/api/servers") reads++
|
||
if (r.request().method() !== "GET" && !path.endsWith("/preview")) writes.push(path)
|
||
return r.fulfill({ json: path === "/api/auth/config" ? { required: false } : path === "/api/servers" ? servers : path === "/api/filters/rules" ? { rulesets: [] } : path === "/api/uptime/speed-probes" ? { probes: [] } : path === "/api/ospf/catalog" ? { interfaces: [], instances: [], neighbors: [], bfdSessions: [], routes: [], failures: [] } : path.endsWith("/ospf/optimize/preview") ? { serverId: 1, serverName: "Домашний роутер", pingWeight: 60, interfacesTotal: 0, changedCount: 0, unchangedCount: 0, interfaces: [], changes: [] } : {} })
|
||
})
|
||
const start = Date.now(); await page.goto("/route-optimizer?server=1&keep=1")
|
||
await page.getByRole("button", { name: "Маршруты (1000)", exact: true }).click()
|
||
const table = page.getByRole("table").filter({ hasText: "# Маршрут" })
|
||
await expect(table.locator("tr[data-row-id]")).toHaveCount(5)
|
||
await page.getByRole("button", { name: "Открыть все 1000 комбинаций", exact: true }).click()
|
||
await expect(table.locator("tr[data-row-id]")).toHaveCount(50)
|
||
const profile = { combinations: 1000, servers: servers.length, bytes: Buffer.byteLength(JSON.stringify(servers)), rendered: await table.locator("tr[data-row-id]").count(), tableNodes: await table.locator("*").count(), pageNodes: await page.locator("*").count(), readyMs: Date.now()-start }
|
||
console.log("Optimizer full routes", JSON.stringify(profile)); writeFileSync(info.outputPath("optimizer-routes-profile.json"), JSON.stringify(profile, null, 2))
|
||
const root = page.getByRole("region", { name: "Полные маршруты оптимизатора" })
|
||
const rows = table.locator("tr[data-row-id]")
|
||
const next = root.getByRole("button", { name: "Следующая страница", exact: true })
|
||
await next.click(); const previous = await rows.first().getAttribute("data-row-id")
|
||
await expect(rows.first().getByRole("cell").first()).toContainText("51")
|
||
const before = reads; await page.getByRole("button", { name: "Обновить", exact: true }).click()
|
||
await expect.poll(() => reads).toBeGreaterThan(before); await expect(rows.first()).toHaveAttribute("data-row-id", previous!)
|
||
for (let i=1; i<19; i++) await next.click()
|
||
await expect(next).toBeDisabled(); await expect(rows.first().getByRole("cell").first()).toContainText("951")
|
||
const minimum = await rows.last().getByRole("cell").nth(6).innerText()
|
||
await expect(rows.last().getByRole("cell").first()).toContainText("1000")
|
||
await root.getByRole("button", { name: "Свернуть", exact: true }).click(); await expect(rows).toHaveCount(5)
|
||
await root.getByRole("button", { name: "Открыть все 1000 комбинаций", exact: true }).click(); await expect(rows).toHaveCount(50)
|
||
await table.getByRole("button", { name: "Индекс", exact: true }).click()
|
||
await table.getByRole("button", { name: "Индекс", exact: true }).click()
|
||
await expect(rows.first().getByRole("cell").nth(6)).toHaveText(minimum)
|
||
expect(Number(await rows.last().getByRole("cell").first().innerText())).toBeGreaterThan(50)
|
||
await page.setViewportSize({ width: 320, height: 1000 })
|
||
const scroll = root.locator('[data-slot="scroll-area-viewport"]')
|
||
await scroll.evaluate(el => { el.scrollLeft = el.scrollWidth })
|
||
expect(await rows.first().getByRole("cell").first().evaluate(el => { const a=el.getBoundingClientRect(), v=el.closest('[data-slot="scroll-area-viewport"]')!.getBoundingClientRect(); return a.left>=v.left-1 && a.right<=v.right+1 })).toBe(true)
|
||
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(320)
|
||
await scroll.evaluate(el => { el.scrollLeft = 0 })
|
||
await root.evaluate(el => el.scrollIntoView({ block: "start" }))
|
||
await page.addStyleTag({ content: "nextjs-portal{display:none!important}" })
|
||
await page.screenshot({ path: info.outputPath("optimizer-routes-pages-320.png"), animations: "disabled" })
|
||
await page.addScriptTag({ content: axe.source })
|
||
expect((await page.evaluate(async () => (window as unknown as { axe: typeof axe }).axe.run(document.querySelector('[aria-label="Полные маршруты оптимизатора"]')!, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] } }))).violations.map(v => ({ id: v.id, nodes: v.nodes.map(n => n.target) }))).toEqual([])
|
||
await next.click(); servers = servers.filter(server => server.id === 1 || server.id === 2 || server.id === 12).map(server => server.id === 1 ? { ...server, wanUplinks: server.wanUplinks!.slice(0, 1) } : server)
|
||
await page.getByRole("button", { name: "Обновить", exact: true }).click()
|
||
await expect(rows).toHaveCount(1); await expect(rows.first().getByRole("cell").first()).toContainText("1")
|
||
expect(writes).toEqual([])
|
||
})
|