Files
MikrotikManager/tests/e2e/large-recursive-routes.spec.ts

53 lines
7.2 KiB
TypeScript

import { test, expect, type Page } from "@playwright/test"
import axe from "axe-core"
import { recursiveReadSchema } from "../../lib/recursive-route-response"
const row = (i: number) => ({ id: "r" + i, dstAddress: "10." + Math.floor((i-1)/256) + "." + ((i-1)%256) + ".0/24", gateway: "192.0.2.1%gw-" + i, distance: 1, scope: 30, targetScope: 10, routingTable: "t" + String(i).padStart(4,"0"), checkGateway: "ping", comment: "route-" + String(i).padStart(4,"0"), disabled: false, country: "RU" })
async function setup(page: Page, control: { routes: ReturnType<typeof row>[]; failed: boolean; calls: number }) {
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
await page.route("**/api/**", r => {
const p = new URL(r.request().url()).pathname
if (p === "/api/recursive-routes/catalog") { control.calls++; return r.fulfill(control.failed ? { status: 503, json: { error: "Чтение маршрутов недоступно тест" } } : { json: recursiveReadSchema.parse({ serverId: 1, routes: control.routes, live: true, stale: false }) }) }
return r.fulfill({ json: p === "/api/auth/config" ? { required: false } : p === "/api/servers" ? [{ id: 1, name: "Роутер", host: "192.0.2.1", country: "RU", enabled: true, status: null }] : p === "/api/recursive-routes/gateways/catalog" ? { serverId: 1, gateways: [] } : {} })
})
}
test("Recursive1000 groups: URL page/search/sort/refresh/cache/320px", async ({ page }, info) => {
test.setTimeout(90000)
const control = { routes: Array.from({length:1000},(_,i)=>row(i+1)), failed: false, calls: 0 }
await setup(page, control); const last = control.routes[999]
const started = Date.now(); await page.goto("/recursive-routes?server=1&route=" + encodeURIComponent(last.dstAddress) + "&table=t1000&keep=x")
const rows = page.locator("tr[data-row-id]"); await expect(rows).toHaveCount(50)
await expect(page.getByRole("button", { name: "Раскрыть маршрут " + last.dstAddress + " · t1000", exact: true })).toHaveAttribute("aria-expanded", "true")
await expect(page.getByText("Endpoint 1", { exact: true })).toBeVisible()
const profile = { count:1000, jsonBytes:Buffer.byteLength(JSON.stringify(control.routes)), rows:await rows.count(), nodes:await page.locator("*").count(), readyMs:Date.now()-started }; console.log("Recursive groups profile",JSON.stringify(profile)); await info.attach("profile",{body:JSON.stringify(profile),contentType:"application/json"})
const next = page.getByRole("button", { name:"Следующая страница",exact:true }); await expect(next).toBeDisabled()
const refresh = page.getByRole("button", { name:"Обновить",exact:true }); const before=control.calls; await refresh.click(); await expect.poll(()=>control.calls).toBeGreaterThan(before); await expect(next).toBeDisabled()
await page.getByRole("button",{name:"Раскрыть маршрут " + last.dstAddress + " · t1000",exact:true}).click()
const search = page.getByRole("textbox",{name:"Поиск маршрутов",exact:true}); await search.fill("route-1000"); await expect(rows).toHaveCount(1); await expect(rows.first()).toContainText(last.dstAddress)
await search.fill(""); await expect(rows).toHaveCount(50); await page.getByRole("table").getByRole("button",{name:"Table",exact:true}).click(); await page.getByRole("table").getByRole("button",{name:"Table",exact:true}).click(); await expect(rows.first()).toContainText("t1000")
await next.click(); const id=await rows.first().getAttribute("data-row-id"); control.failed=true; await refresh.click(); await expect(page.getByRole("alert").filter({hasText:"Чтение маршрутов недоступно тест"})).toBeVisible(); await expect(rows.first()).toHaveAttribute("data-row-id",id!)
const edit=rows.first().getByRole("button",{name:/Редактировать маршрут/}); await expect(edit).toBeDisabled()
await page.setViewportSize({width:320,height:1000}); await edit.scrollIntoViewIfNeeded(); const viewport=page.locator('[data-slot="data-grid"] [data-slot="scroll-area-viewport"]'); await viewport.evaluate(el=>{el.scrollLeft=el.scrollWidth}); await expect(edit).toBeInViewport()
await expect.poll(()=>page.evaluate(()=>document.documentElement.scrollWidth)).toBeLessThanOrEqual(320)
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.screenshot({path:info.outputPath("recursive-groups-320.png"),animations:"disabled"})
control.failed=false; control.routes=[last]; await refresh.click(); await expect(rows).toHaveCount(1); await expect(rows.first()).toContainText(last.dstAddress)
await expect(page).toHaveURL(/keep=x/)
})
test("Recursive1000 endpoints: bounded preview/detail/last page/refresh/clamp/320px", async ({page},info)=>{
test.setTimeout(90000)
const control={routes:Array.from({length:1000},(_,i)=>({...row(i+1),dstAddress:"203.0.113.0/24",routingTable:"main",distance:Math.floor((999-i)/4)})),failed:false,calls:0}
await setup(page,control); await page.goto("/recursive-routes?server=1&route=203.0.113.0%2F24&table=main")
const region=page.getByRole("region",{name:"Конечные точки: 203.0.113.0/24 · main",exact:true}); const cards=region.locator('[data-recursive-endpoint]'); await expect(cards).toHaveCount(50)
await expect(page.getByText("Ещё 997 · раскройте маршрут",{exact:true})).toBeVisible()
await expect(cards.first()).toContainText("gw-997"); await expect(cards.first()).toContainText("distance: 0")
const profile={count:1000,cards:await cards.count(),nodes:await page.locator("*").count()};console.log("Recursive endpoints profile",JSON.stringify(profile));await info.attach("profile",{body:JSON.stringify(profile),contentType:"application/json"})
const next=region.getByRole("button",{name:"Следующие конечные точки",exact:true});for(let i=0;i<19;i++)await next.click()
await expect(next).toBeDisabled();await expect(cards.last()).toContainText("Endpoint 1000");await expect(cards.last()).toContainText("gw-4")
await page.getByRole("button",{name:"Обновить",exact:true}).click();await expect(cards.last()).toContainText("Endpoint 1000")
await page.setViewportSize({width:320,height:1000}); await page.locator('[data-slot="data-grid"] [data-slot="scroll-area-viewport"]').evaluate(el=>{el.scrollLeft=0}); await cards.first().scrollIntoViewIfNeeded()
await expect.poll(()=>region.evaluate(el=>el.scrollWidth<=el.clientWidth+1)).toBe(true)
await expect.poll(()=>page.evaluate(()=>document.documentElement.scrollWidth)).toBeLessThanOrEqual(320)
await page.addStyleTag({content:"nextjs-portal{display:none!important}"});await page.screenshot({path:info.outputPath("recursive-endpoints-320.png"),animations:"disabled"})
control.routes=[control.routes[999]];await page.getByRole("button",{name:"Обновить",exact:true}).click();await expect(cards).toHaveCount(1);await expect(cards.first()).toContainText("Endpoint 1");await expect(cards.first()).toContainText("gw-1000")
})