88 lines
7.6 KiB
TypeScript
88 lines
7.6 KiB
TypeScript
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)
|
||
})
|