53 lines
5.5 KiB
TypeScript
53 lines
5.5 KiB
TypeScript
import { test, expect } from "@playwright/test"
|
|
import { ospfCatalogSchema } from "../../lib/ospf-response"
|
|
import axe from "axe-core"
|
|
test("OSPF BFD1000: catalog search/sort, pagination/cache/refresh and320px", async ({ page }, info) => {
|
|
test.setTimeout(90000)
|
|
const server = { id: 1, name: "Роутер 1", host: "192.0.2.1", country: "RU", enabled: true, status: null }
|
|
let sessions = Array.from({ length: 1000 }, (_, i) => ({ id: "*" + (i + 1), serverId: 1, serverName: "Роутер 1", serverSite: "", serverCountry: "RU", localAddr: "192.0.2.1", remoteAddr: "10." + Math.floor(i / 256) + "." + (i % 256) + ".1", interface: "ether" + (i + 1), state: "Up", uptime: "1h", multihop: false, multiplier: 3, txInterval: 100, rxInterval: 100, holdTime: 300, packetsRx: i + 1, packetsTx: 0, stateChanges: i + 1 }))
|
|
let calls = 0, failed = false; const writes: string[] = []
|
|
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
|
|
await page.route("**/api/**", r => {
|
|
const p = new URL(r.request().url()).pathname
|
|
if (r.request().method() !== "GET") writes.push(p)
|
|
if (p === "/api/ospf/catalog") { calls++; return r.fulfill(failed ? { status: 503, json: { error: "BFD catalog unavailable" } } : { json: ospfCatalogSchema.parse({ neighbors: [], interfaces: [], instances: [], routes: [], bfdSessions: sessions, failures: [] }) }) }
|
|
return r.fulfill({ json: p === "/api/auth/config" ? { required: false } : p === "/api/servers" ? [server] : {} })
|
|
})
|
|
const started = Date.now(); await page.goto("/ospf?tab=bfd&server=1&keep=1")
|
|
const rows = page.locator("tr[data-row-id]"); await expect(rows).toHaveCount(50)
|
|
const profile = { count: sessions.length, jsonBytes: Buffer.byteLength(JSON.stringify(sessions)), renderedRows: await rows.count(), nodes: await page.locator("*").count(), readyMs: Date.now() - started }
|
|
console.log("BFD profile", JSON.stringify(profile)); await info.attach("profile", { body: JSON.stringify(profile), contentType: "application/json" })
|
|
const next = page.getByRole("button", { name: "Следующая страница", exact: true })
|
|
for (let i=0;i<19;i++) await next.click()
|
|
await expect(next).toBeDisabled(); await expect(rows.first()).toHaveAttribute("data-row-id", "1-*951")
|
|
const before = calls; await page.getByRole("button", { name: "Обновить", exact: true }).click()
|
|
await expect.poll(() => calls).toBeGreaterThan(before); await expect(rows.first()).toHaveAttribute("data-row-id", "1-*951")
|
|
const search = page.getByPlaceholder("Поиск в текущем разделе OSPF…")
|
|
await search.fill(sessions[999].remoteAddr); await expect(rows).toHaveCount(1); await expect(rows.first()).toHaveAttribute("data-row-id", "1-*1000")
|
|
await expect(page).toHaveURL(url => url.searchParams.get("q") === sessions[999].remoteAddr && url.searchParams.get("keep") === "1")
|
|
await page.reload(); await expect(rows).toHaveCount(1); await search.fill(""); await expect(rows).toHaveCount(50)
|
|
await page.getByRole("table").getByRole("button", { name: "Пакеты Rx", exact: true }).click()
|
|
await expect(rows.first()).toHaveAttribute("data-row-id", "1-*1000")
|
|
await next.click(); const previous = await rows.first().getAttribute("data-row-id")
|
|
failed = true; await page.getByRole("button", { name: "Обновить", exact: true }).click()
|
|
const alert = page.getByRole("alert").filter({ hasText: "Не удалось загрузить OSPF" })
|
|
await expect(alert).toContainText("Показаны последние полученные данные"); await expect(rows).toHaveCount(50); await expect(rows.first()).toHaveAttribute("data-row-id", previous!)
|
|
await expect(rows.first()).toContainText("Неизвестно")
|
|
await page.setViewportSize({ width: 320, height: 1000 })
|
|
const scroll = page.locator('[data-slot="data-grid"] [data-slot="scroll-area-viewport"]')
|
|
await expect(scroll).toHaveCount(1); await scroll.evaluate(el => { el.scrollLeft = el.scrollWidth })
|
|
await expect.poll(() => scroll.evaluate(el => el.scrollLeft)).toBeGreaterThan(0)
|
|
const cells = rows.first().getByRole("cell")
|
|
expect(await cells.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 cells.last().locator("span").evaluate(el => {const a=el.getBoundingClientRect(),v=el.closest('[data-slot="scroll-area-viewport"]')!.getBoundingClientRect(),p=el.closest("tr")!.querySelector("td")!.getBoundingClientRect();return a.left>=p.right-1&&a.right<=v.right+1})).toBe(true)
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
await page.addScriptTag({ content: axe.source })
|
|
expect((await page.evaluate(async () => (window as unknown as { axe: typeof axe }).axe.run(document.querySelector('[data-slot="data-grid"]')!, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] } }))).violations).toEqual([])
|
|
await page.addStyleTag({ content: "nextjs-portal { display: none !important; }" })
|
|
await page.getByRole("table").getByRole("button", { name: "Роутер", exact: true }).scrollIntoViewIfNeeded()
|
|
await page.screenshot({ path: info.outputPath("ospf-bfd-320.png"), animations: "disabled" })
|
|
failed = false; sessions = [sessions[999]]; await alert.getByRole("button", { name: "Повторить загрузку OSPF" }).click()
|
|
await expect(rows).toHaveCount(1); await expect(alert).toHaveCount(0); await expect(rows.first()).toHaveAttribute("data-row-id", "1-*1000")
|
|
expect(writes).toEqual([])
|
|
})
|