Files
MikrotikManager/tests/e2e/large-optimizer-ospf-preview.spec.ts
T

65 lines
6.5 KiB
TypeScript

import { test, expect } from "@playwright/test"
import { writeFileSync } from "node:fs"
import axe from "axe-core"
import { optimizerOspfPreviewSchema } from "../../lib/route-optimizer-response"
test("Оптимизатор1000 preview: полный apply при поиске, страницы и кэш", async ({ page }, info) => {
test.setTimeout(90000)
let interfaces = Array.from({ length: 1000 }, (_, i) => ({ id: `i${i+1}`, interface: `ether${i+1}`, currentCost: i+1, optimalCost: i+1001, score: i+1, pingMs: 10, dlMbps: 100, ulMbps: 50 }))
let applied = false, failed = false, previews = 0, writes = 0
const preview = () => optimizerOspfPreviewSchema.parse({ serverId: 1, serverName: "Роутер 1", pingWeight: 60, interfacesTotal: interfaces.length, changedCount: applied ? 0 : interfaces.length, unchangedCount: applied ? interfaces.length : 0, interfaces: interfaces.map(row => applied ? { ...row, currentCost: row.optimalCost } : row), changes: applied ? [] : interfaces })
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.endsWith("/ospf/optimize/preview")) { previews++; return r.fulfill(failed ? { status: 503, json: { error: "Preview недоступен1000" } } : { json: preview() }) }
if (path.endsWith("/ospf/optimize")) {
writes++; expect(r.request().postDataJSON()).toEqual({ pingWeight: 60 }); applied = true
return r.fulfill({ json: { serverId: 1, serverName: "Роутер 1", pingWeight: 60, optimizedCount: interfaces.length, applied: interfaces.map(row => ({ interface: row.interface, from: row.currentCost, to: row.optimalCost })), interfaces } })
}
return r.fulfill({ json: path === "/api/auth/config" ? { required: false } : path === "/api/servers" ? [{ id: 1, name: "Роутер 1", 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: [] } : {} })
})
await page.goto("/route-optimizer?server=1&keep=1")
const grid = page.getByRole("table").filter({ hasText: "Speed (dl/ul)" })
const rows = grid.locator("tr[data-row-id]")
await expect(rows).toHaveCount(50)
await expect(rows.first()).toHaveAttribute("data-row-id", "i1")
const root = page.getByRole("region", { name: "Preview интерфейсов OSPF", exact: true })
const profile = { count: interfaces.length, bytes: Buffer.byteLength(JSON.stringify(preview())), rendered: await rows.count(), gridNodes: await root.locator("*").count(), pageNodes: await page.locator("*").count() }
console.log("Optimizer preview", JSON.stringify(profile)); writeFileSync(info.outputPath("optimizer-ospf-preview-profile.json"), JSON.stringify(profile, null, 2))
await root.getByRole("button", { name: "Следующая страница", exact: true }).click()
await expect(rows.first()).toHaveAttribute("data-row-id", "i51")
const before = previews; await page.getByRole("button", { name: "Обновить preview OSPF", exact: true }).click()
await expect.poll(() => previews).toBeGreaterThan(before); await expect(rows.first()).toHaveAttribute("data-row-id", "i51")
const search = page.getByRole("textbox", { name: "Поиск интерфейсов preview OSPF" })
await search.fill("ether1000"); await expect(rows).toHaveCount(1); await expect(rows.first()).toHaveAttribute("data-row-id", "i1000")
await expect(page.getByRole("status").filter({ hasText: "Фильтр меняет только отображение" })).toContainText("Найдено 1 из 1000")
await page.getByRole("button", { name: "Оптимизировать OSPF", exact: true }).click()
await expect(page.getByRole("status").filter({ hasText: "Последнее подтверждение API" })).toContainText("изменено интерфейсов — 1000")
expect(writes).toBe(1); await expect(page.getByText("0 из 1000 изменятся", { exact: true })).toBeVisible()
await expect(rows).toHaveCount(1)
await search.fill(""); await expect(rows).toHaveCount(50)
await grid.getByRole("button", { name: "Cost", exact: true }).click()
await expect(rows.first()).toHaveAttribute("data-row-id", "i1000")
await root.getByRole("button", { name: "Следующая страница", exact: true }).click()
const previous = await rows.first().getAttribute("data-row-id")
failed = true; await page.getByRole("button", { name: "Обновить preview OSPF", exact: true }).click()
await expect(page.getByRole("alert").filter({ hasText: "Последний снимок" })).toBeVisible()
await expect(rows.first()).toHaveAttribute("data-row-id", previous!)
await expect(page.getByRole("button", { name: "Оптимизировать OSPF", exact: true })).toBeDisabled()
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 grid.evaluate(el => el.scrollIntoView({ block: "start" }))
await page.addStyleTag({ content: "nextjs-portal{display:none!important}" })
await page.screenshot({ path: info.outputPath("optimizer-ospf-preview-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="Preview интерфейсов OSPF"]')!, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] } }))).violations.map(v => ({ id: v.id, nodes: v.nodes.map(n => n.target) }))).toEqual([])
failed = false; interfaces = interfaces.slice(0, 1)
await page.getByRole("button", { name: "Обновить preview OSPF", exact: true }).click()
await expect(rows).toHaveCount(1); await expect(rows.first()).toHaveAttribute("data-row-id", "i1")
await expect(page).toHaveURL(url => url.searchParams.get("server") === "1" && url.searchParams.get("keep") === "1")
})