Files
MikrotikManager/tests/e2e/large-users.spec.ts
T

57 lines
6.0 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 { appUserReadSchema } from "../../packages/contracts/src/users"
import { user, server, catalogResponse } from "./fixtures/users"
import axe from "axe-core"
import { mkdirSync } from "node:fs"
test("Users1000: DOM, весь поиск, детали/ошибка refresh и правильное действие", async ({ page }, info) => {
test.setTimeout(90000)
let users = Array.from({ length: 1000 }, (_, i) => appUserReadSchema.parse({ ...user, id: "u-" + (i + 1), name: "Клиент " + String(i + 1).padStart(4, "0"), login: "user" + (i + 1), email: "user" + (i + 1) + "@example.test" }))
let calls = 0, failed = false; const writes: string[] = []
await page.route("**/health", r => r.fulfill({ json: { ok: true } }))
await page.route("**/api/**", r => {
const path = new URL(r.request().url()).pathname
if (r.request().method() !== "GET") writes.push(path)
if (path === "/api/users") { calls++; if (failed) return r.fulfill({ status: 503, json: { error: "Каталог временно недоступен" } }) }
return catalogResponse(r, { json: path === "/api/auth/config" ? { required: false } : path === "/api/users" ? { users } : path === "/api/servers" ? [server] : { interfaces: [] } })
})
const start = Date.now(); await page.goto("/users")
const rows = page.locator("tr[data-row-id]")
await expect(page.getByText("Клиент 0001", { exact: true })).toBeVisible()
const sample = { fixtureRows: 1000, fixtureBytes: Buffer.byteLength(JSON.stringify(users)), renderedRows: await rows.count(), domNodes: await page.locator("*").count(), readyMs: Date.now() - start }
console.log(JSON.stringify(sample)); await info.attach("large-users-metrics", { body: JSON.stringify(sample), contentType: "application/json" })
expect(sample.renderedRows).toBe(50); expect(sample.domNodes).toBeLessThan(5000)
for (let i = 0; i < 3; i++) await page.getByRole("button", { name: "…", exact: true }).last().click()
await page.getByRole("button", { name: "Перейти на страницу 20", exact: true }).click()
const detail = page.getByRole("button", { name: "Доступ и привязки пользователя Клиент 1000", exact: true })
await detail.focus(); await page.keyboard.press("Enter"); await expect(detail).toHaveAttribute("aria-expanded", "true")
const refresh = page.getByRole("button", { name: "Обновить пользователей", exact: true })
const before = calls; await refresh.click(); await expect.poll(() => calls).toBeGreaterThan(before); await expect(refresh).toBeEnabled()
await expect(page.getByText("951–1000 из 1000", { exact: true })).toBeVisible(); await expect(detail).toHaveAttribute("aria-expanded", "true")
failed = true; await refresh.click(); await expect(page.getByRole("alert", { name: "Пользователи", exact: true })).toBeVisible()
await expect(rows).toHaveCount(50); await expect(detail).toHaveAttribute("aria-expanded", "true")
failed = false; await refresh.click(); await expect(page.getByRole("alert", { name: "Пользователи", exact: true })).toHaveCount(0)
const search = page.getByRole("textbox", { name: "Поиск по имени, логину или email", exact: true })
for (const q of ["Клиент 1000", "[email protected]"]) { await search.fill(q); await expect(rows).toHaveCount(1); await expect(rows.first()).toHaveAttribute("data-row-id", "u-1000") }
await search.fill("нет совпадений"); await expect(page.getByText("Нет совпадений", { exact: true })).toBeVisible()
await search.fill(""); await expect(rows).toHaveCount(50)
const sort = page.getByRole("table").getByRole("button", { name: "Пользователь", exact: true }); await sort.click(); await sort.click()
await expect(rows.first()).toHaveAttribute("data-row-id", "u-1000")
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 })
const edit = rows.first().getByRole("button", { name: "Редактировать пользователя Клиент 1000", exact: true })
expect(await edit.evaluate(el => { const value = el.getBoundingClientRect(), viewport = el.closest('[data-slot="scroll-area-viewport"]')!.getBoundingClientRect(), pinned = el.closest("tr")!.querySelector("td")!.getBoundingClientRect(); return value.left >= pinned.right - 1 && value.right <= viewport.right + 1 })).toBe(true)
await edit.focus(); await page.keyboard.press("Enter")
await expect(page.getByRole("textbox", { name: "Полное имя", exact: true })).toHaveValue("Клиент 1000")
await page.getByRole("dialog").getByRole("button", { name: "Отмена", exact: true }).click(); await expect(edit).toBeFocused()
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
await page.addScriptTag({ content: axe.source })
const audit = 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"] } })); expect(audit.violations).toEqual([])
await page.addStyleTag({ content: "nextjs-portal { display: none !important; }" }); mkdirSync("docs/ux-ui/screens", { recursive: true })
await page.getByRole("table").getByRole("button", { name: "Пользователь", exact: true }).scrollIntoViewIfNeeded(); await page.screenshot({ path: "docs/ux-ui/screens/large-users-320.png", animations: "disabled" })
await page.getByRole("button", { name: "Следующая страница", exact: true }).click(); users = [users[999]]; const previous = calls; await refresh.click()
await expect.poll(() => calls).toBeGreaterThan(previous); await expect(rows).toHaveCount(1); await expect(rows.first()).toHaveAttribute("data-row-id", "u-1000")
expect(writes).toEqual([])
})