Files
MikrotikManager/tests/e2e/large-firewall-scenario.spec.ts
T

88 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect } from "@playwright/test"
import axe from "axe-core"
test("Сценарий1000: граница страниц, полный draft и симуляция скрытого правила", async ({ page }, info) => {
test.setTimeout(90000)
const packet = { chain: "forward", proto: "tcp", srcAddr: "10.10.0.100", dstAddr: "142.250.74.110", srcPort: "54321", dstPort: "443", inIface: "lan", outIface: "wan", connState: "new", srcAddrList: "", dstAddrList: "", tlsHost: "" }
const rules = Array.from({ length: 1000 }, (_, i) => ({ id: `s${i + 1}`, chain: "forward", action: i === 999 ? "drop" : "passthrough", proto: "all", src: "", dst: "", port: "", iface: "", comment: `scenario-${i + 1}`, enabled: true }))
const scenarios = [{ id: "big", name: "Сценарий1000", description: "", packet, rules, createdAt: 1 }]
await page.addInitScript(value => localStorage.setItem("routerlists-fw-scenarios", JSON.stringify(value)), scenarios)
let writes = 0
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
await page.route("**/api/**", r => {
if (r.request().method() !== "GET") writes++
const p = new URL(r.request().url()).pathname
return r.fulfill({ json: p === "/api/auth/config" ? { required: false } : p === "/api/firewall/all" ? { rules: [], addressLists: [] } : p === "/api/servers" ? [{ id: 1, name: "Роутер", host: "192.0.2.1", type: "exit-node", enabled: true, country: "RU", status: "online" }] : {} })
})
await page.goto("/firewall?server=1")
await page.getByRole("button", { name: "Симулятор", exact: true }).click()
await page.getByRole("button", { name: "Редактировать сценарий Сценарий1000", exact: true }).click()
const dialog = page.getByRole("dialog"), rows = dialog.locator("tr[data-row-id]")
await expect(rows).toHaveCount(50)
console.log("Scenario profile", JSON.stringify({ count: 1000, rows: await rows.count(), modalNodes: await dialog.locator("*").count() }))
await expect(dialog.getByRole("button", { name: "Поднять правило 1", exact: true })).toBeDisabled()
await dialog.getByRole("button", { name: "Опустить правило 50", exact: true }).click()
await expect(rows.last()).toHaveAttribute("data-row-id", "s51")
await dialog.getByRole("button", { name: "Следующая страница", exact: true }).click()
await expect(rows.first()).toHaveAttribute("data-row-id", "s50")
await dialog.getByRole("button", { name: "Поднять правило 51", exact: true }).focus(); await page.keyboard.press("Enter")
await expect(rows.first()).toHaveAttribute("data-row-id", "s51")
await dialog.getByRole("button", { name: "Удалить правило 51", exact: true }).click()
await expect(rows.first()).toHaveAttribute("data-row-id", "s52")
await page.setViewportSize({ width: 320, height: 1000 })
const remove = rows.first().getByRole("button", { name: "Удалить правило 51", exact: true })
await remove.scrollIntoViewIfNeeded()
await dialog.locator('[data-slot="scroll-area-viewport"]').evaluate(el => { el.scrollLeft = el.scrollWidth })
await expect(remove).toBeInViewport(); const box = (await remove.boundingBox())!
expect(box.height).toBeGreaterThanOrEqual(44); expect(box.width).toBeGreaterThanOrEqual(44); expect(box.x + box.width).toBeLessThanOrEqual(320)
expect((await dialog.boundingBox())?.width).toBe(320)
expect(await 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('[role="dialog"] [data-slot="data-grid"]')!, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] } }))).violations.map(v => ({ id: v.id, nodes: v.nodes.map(n => n.target) }))).toEqual([])
await page.addStyleTag({ content: "nextjs-portal{display:none!important}" })
await page.screenshot({ path: info.outputPath("scenario-320.png"), animations: "disabled" })
await dialog.getByRole("button", { name: "Сохранить изменения", exact: true }).click()
await expect(dialog).toHaveCount(0)
const saved = await page.evaluate(() => JSON.parse(localStorage.getItem("routerlists-fw-scenarios")!))
expect(saved[0].rules).toHaveLength(999)
expect(saved[0].rules.slice(48,52).map((r: { id: string }) => r.id)).toEqual(["s49", "s50", "s52", "s53"])
expect(saved[0].rules[998]).toEqual(rules[999])
await expect(page.getByRole("button", { name: "Активен", exact: true })).toBeVisible()
await page.getByRole("button", { name: "Мгновенно", exact: true }).click()
await page.getByRole("button", { name: "Запустить", exact: true }).click()
await expect(page.getByText("— правило #999", { exact: true })).toBeVisible()
await expect(page.getByText("Пакет заблокирован", { exact: false })).toBeVisible()
const trace = page.getByRole("region", { name: "Трассировка Firewall", exact: true })
const traceRows = trace.locator("[data-firewall-trace-step]")
await expect(traceRows).toHaveCount(50)
await expect(traceRows.first()).toHaveAttribute("data-firewall-trace-step", "1")
console.log("Scenario simulation nodes", await page.locator("*").count())
await trace.getByRole("button", { name: "Следующая страница трассировки", exact: true }).click()
await expect(traceRows.first()).toHaveAttribute("data-firewall-trace-step", "51")
await trace.getByRole("button", { name: "К правилу результата", exact: true }).focus(); await page.keyboard.press("Enter")
await expect(traceRows).toHaveCount(49)
await expect(traceRows.last()).toHaveAttribute("data-firewall-trace-step", "999")
await expect(trace.getByRole("status")).toHaveText("Шаги 951–999 из 999")
await expect(trace.getByRole("button", { name: "Следующая страница трассировки", exact: true })).toBeDisabled()
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(320)
expect((await page.evaluate(async () => (window as unknown as { axe: typeof axe }).axe.run(document.querySelector('[aria-label="Трассировка Firewall"]')!, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa"] } }))).violations.map(v => ({ id: v.id, nodes: v.nodes.map(n => n.target) }))).toEqual([])
await traceRows.last().scrollIntoViewIfNeeded()
await page.screenshot({ path: info.outputPath("trace-320.png"), animations: "disabled" })
await page.clock.install()
await page.getByRole("button", { name: "Быстро", exact: true }).click()
await page.getByRole("button", { name: "Снова", exact: true }).click()
await expect(traceRows.first()).toHaveAttribute("data-firewall-trace-step", "1")
await page.getByRole("button", { name: "Стоп", exact: true }).click()
await page.getByRole("button", { name: "Мгновенно", exact: true }).click()
await page.getByRole("button", { name: "Снова", exact: true }).click()
await expect(page.getByText("— правило #999", { exact: true })).toBeVisible()
await expect(traceRows.first()).toHaveAttribute("data-firewall-trace-step", "1")
await page.clock.runFor(1000)
await expect(trace.getByText("проверяется…", { exact: true })).toHaveCount(0)
await expect(page.getByText("— правило #999", { exact: true })).toBeVisible()
await page.getByRole("button", { name: "Сбросить", exact: true }).click()
await expect(trace).toHaveCount(0)
await expect(page.getByText("— правило #999", { exact: true })).toHaveCount(0)
expect(writes).toBe(0)
})