95 lines
5.2 KiB
TypeScript
95 lines
5.2 KiB
TypeScript
import { existsSync, readdirSync } from "node:fs"
|
|
import { join } from "node:path"
|
|
import { test, expect, type Page } from "@playwright/test"
|
|
import { navigationItems } from "../../lib/navigation"
|
|
|
|
async function setup(page: Page) {
|
|
await page.route("**/health", route => route.fulfill({ json: { ok: true } }))
|
|
await page.route("**/api/**", route => {
|
|
const path = new URL(route.request().url()).pathname
|
|
return route.fulfill({ json: path === "/api/servers" ? []
|
|
: path === "/api/recursive-routes" ? { routes: [] }
|
|
: path === "/api/recursive-routes/gateways" ? { gateways: [] }
|
|
: path === "/api/auth/config" ? { required: false }
|
|
: path === "/api/evobgp/settings" ? { enabled: false, baseUrl: "", secretConfigured: false } : {} })
|
|
})
|
|
await page.goto("/recursive-routes")
|
|
await expect(page.getByRole("navigation", { name: "Путь страницы" })).toBeVisible()
|
|
}
|
|
|
|
test("каталог навигации покрывает существующие разделы без повторов и мёртвых ссылок", () => {
|
|
const root = join(process.cwd(), "app/(main)")
|
|
const pages = readdirSync(root).filter(name => existsSync(join(root, name, "page.tsx"))).map(name => `/${name}`)
|
|
expect(navigationItems.map(item => item.url).sort()).toEqual(pages.sort())
|
|
expect(new Set(navigationItems.map(item => item.id)).size).toBe(navigationItems.length)
|
|
expect(new Set(navigationItems.map(item => item.title)).size).toBe(navigationItems.length)
|
|
})
|
|
|
|
test("поиск страниц: одинаковые названия, синонимы, клавиатура и переход", async ({ page }) => {
|
|
await setup(page)
|
|
const sidebar = page.getByRole("navigation", { name: "Основная навигация" })
|
|
for (const item of navigationItems) {
|
|
await expect(sidebar.locator(`a[href="${item.url}"]`)).toContainText(item.title)
|
|
}
|
|
const opener = page.getByRole("button", { name: "Приложения", exact: true })
|
|
await opener.focus()
|
|
await page.keyboard.press("Control+k")
|
|
const dialog = page.getByRole("dialog", { name: "Поиск страниц", exact: true })
|
|
const input = dialog.getByRole("combobox", { name: "Поиск страниц", exact: true })
|
|
await expect(input).toBeFocused()
|
|
await expect(dialog.getByRole("option")).toHaveCount(navigationItems.length)
|
|
await input.press("ArrowDown")
|
|
await expect(dialog.getByRole("option", { name: "Трафик", exact: true })).toHaveAttribute("data-selected", "true")
|
|
await input.press("ArrowUp")
|
|
await expect(dialog.getByRole("option", { name: "Дашборд", exact: true })).toHaveAttribute("data-selected", "true")
|
|
await input.fill("uptime")
|
|
await expect(dialog.getByRole("option", { name: "Мониторинг", exact: true })).toBeVisible()
|
|
await input.fill("Несуществующийраздел999")
|
|
await expect(dialog.getByRole("status")).toContainText("Ничего не найдено")
|
|
await input.press("ArrowDown")
|
|
await input.press("Enter")
|
|
await expect(dialog).toBeVisible()
|
|
await page.keyboard.press("Tab")
|
|
await expect.poll(() => dialog.evaluate(element => element.contains(document.activeElement))).toBe(true)
|
|
await page.keyboard.press("Escape")
|
|
await expect(dialog).toBeHidden()
|
|
await expect(opener).toBeFocused()
|
|
await page.keyboard.press("Control+k")
|
|
await expect(input).toHaveValue("")
|
|
await input.fill("mikrotik")
|
|
await expect(dialog.getByRole("option", { name: "Серверы", exact: true })).toBeVisible()
|
|
await input.press("Enter")
|
|
await expect(page).toHaveURL(/\/servers$/)
|
|
await expect(dialog).toBeHidden()
|
|
await expect(sidebar.locator('a[href="/servers"]')).toHaveAttribute("aria-current", "page")
|
|
await page.goBack()
|
|
await expect(sidebar.locator('a[href="/recursive-routes"]')).toHaveAttribute("aria-current", "page")
|
|
})
|
|
|
|
for (const theme of ["dark", "light"]) {
|
|
test(`мобильное меню и поиск: тема ${theme}, низкий экран`, async ({ page }) => {
|
|
await page.addInitScript(value => localStorage.setItem("rl-theme", value), theme)
|
|
await page.setViewportSize({ width: 390, height: 600 })
|
|
await setup(page)
|
|
const menu = page.getByRole("button", { name: "Боковое меню", exact: true })
|
|
await menu.click()
|
|
const sidebar = page.getByRole("navigation", { name: "Основная навигация" })
|
|
await sidebar.locator('a[href="/servers"]').click()
|
|
await expect(page).toHaveURL(/\/servers$/)
|
|
await expect(page.getByRole("dialog")).toBeHidden()
|
|
await menu.click()
|
|
await expect(sidebar.locator('a[href="/servers"]')).toHaveAttribute("aria-current", "page")
|
|
await sidebar.locator('a[href="/servers"]').click()
|
|
await expect(page.getByRole("dialog")).toBeHidden()
|
|
await page.keyboard.press("Control+k")
|
|
const search = page.getByRole("dialog", { name: "Поиск страниц", exact: true })
|
|
await expect(search).toBeVisible()
|
|
const bounds = await search.boundingBox()
|
|
expect(bounds!.x).toBeGreaterThanOrEqual(0)
|
|
expect(bounds!.x + bounds!.width).toBeLessThanOrEqual(390)
|
|
expect(bounds!.y).toBeGreaterThanOrEqual(0)
|
|
expect(bounds!.y + bounds!.height).toBeLessThanOrEqual(600)
|
|
await page.screenshot({ path: `test-results/navigation-mobile-${theme}.png`, animations: "disabled" })
|
|
})
|
|
}
|