Files
MikrotikManager/tests/e2e/wireguard-links.spec.ts
T

130 lines
8.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, type Page } from "@playwright/test"
import { wgListResponseSchema } from "../../packages/contracts/src/wireguard"
async function setup(page: Page, url: string, failed = 0) {
const control = { partial: false, failed, posts: [] as { path: string; body: unknown }[] }
const servers = [{ id: 1, name: "Источник", host: "192.0.2.1", type: "exit-node", enabled: true, country: "RU", site: "TEST", asn: "64512", status: "online", latency: 1, os: "7", model: "CHR", wanUplinks: [] }]
const interfaces = wgListResponseSchema.parse({ interfaces: ["1", "2"].flatMap(serverId => ["*/2", "other"].map(id => ({ id, rosId: id, serverId, serverName: serverId === "1" ? "Источник" : "Второй сервер", name: "wg-same", address: "192.0.2." + (serverId === "1" ? "11" : "22"), listenPort: 13231, mtu: 1420, comment: "", enabled: true, status: "down", peers: [{ id: "peer", rosId: "*P", publicKey: "fixture-key", allowedIps: [serverId === "1" ? "10.0.1.1/32" : "10.0.2.1/32"] }] }))) }).interfaces
servers.push({ ...servers[0], id: 2, name: "Второй сервер", host: "192.0.2.2" });
const probes = ["p1", "p/2"].map(id => ({ id, srcServerId: "1", name: "Проверка сети", target: "192.0.2.10", filter: "main", rtt: null, loss: 100, status: "down", series: [0], enabled: true, showOnDashboard: false }))
await page.route("**/health", route => route.fulfill({ json: { ok: true } }))
await page.route("**/api/**", route => {
const request = route.request()
const path = new URL(request.url()).pathname
if (!["GET", "HEAD"].includes(request.method())) control.posts.push({ path, body: request.postDataJSON() })
if (path === "/api/wireguard" && control.failed) return route.fulfill({ status: control.failed, json: { error: "Мониторинг недоступен" } })
const json = path === "/api/auth/config" ? { required: false }
: path === "/api/evobgp/settings" ? { enabled: false, baseUrl: "", secretConfigured: false }
: path === "/api/servers" ? servers
: path === "/api/uptime/overview" ? { probes, resources: [] }
: path === "/api/uptime/speed-probes" ? { probes: [] }
: path === "/api/uptime/speed-test/runs" ? { runs: [] }
: path === "/api/bgp/sessions" ? []
: path === "/api/wireguard" ? { interfaces: control.partial ? interfaces.filter(t => t.serverId !== "2") : interfaces, failures: control.partial ? [{ serverId: "2", error: "Unavailable" }] : [] }
: path === "/api/filters/gre-tunnels" ? { tunnels: [] }
: path === "/api/vxlan" ? { tunnels: [] }
: path === "/api/traffic/servers" ? { servers: [] }
: path === "/api/events" ? { events: [] }
: path === "/api/internet-path/latest" ? { snapshot: { sampledAt: "2026-09-20T00:00:00Z", servers, greTunnels: [], filtersRulesets: [], speedProbes: [], routeLookupByServerId: {}, wanRuntimeByHomeId: {} } }
: {}
return route.fulfill({ json })
})
await page.goto(url)
return control
}
test("WireGuard: переход из очереди выбирает сервер и точный ID без записи", async ({ page }) => {
const control = await setup(page, "/dashboard")
const link = page.getByRole("region", { name: "Требует внимания", exact: true }).locator('a[href="/wireguard?server=2&interface=*%2F2"]')
await expect(link).toBeVisible()
await link.click()
const context = page.getByRole("status", { name: "WireGuard по ссылке" })
await expect(context).toContainText("Второй сервер")
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
await expect(page.getByRole("status", { name: "WireGuard по ссылке" })).toContainText("192.0.2.22")
await page.getByRole("tab", { name: /Пиры/ }).click()
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
await expect(page.locator("tr[data-row-id]")).toContainText("10.0.2.1/32")
expect(control.posts).toEqual([])
await context.getByRole("link", { name: "К дашборду", exact: true }).click()
await expect(link).toBeVisible()
})
test("WireGuard: прямая ссылка, сброс, назад, reload и ошибка обновления", async ({ page }) => {
const control = await setup(page, "/wireguard?server=2&interface=*%2F2&keep=1")
const context = page.getByRole("status", { name: "WireGuard по ссылке" })
const rows = page.locator("tr[data-row-id]")
await expect(rows).toHaveCount(1)
await context.getByRole("link", { name: "Все интерфейсы сервера", exact: true }).click()
await expect(page).toHaveURL(/server=2&keep=1$/)
await expect(rows).toHaveCount(2)
await page.goBack()
await expect(rows).toHaveCount(1)
await page.reload()
await expect(context).toContainText("Второй сервер")
control.failed = 503
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(context).toContainText("сохранённые данные могут быть устаревшими")
await expect(rows).toHaveCount(1)
await page.locator("tr[data-row-id]").getByRole("button", { name: "Действия: wg-same" }).click()
await expect(page.getByRole("menuitem", { name: /Удалить/ })).toHaveCount(0)
await page.keyboard.press("Escape")
control.failed = 0
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(context).toContainText("Список, пиры и сводка ограничены")
control.partial = true
await page.getByRole("button", { name: "Обновить", exact: true }).click()
await expect(context).toContainText("Не удалось получить данные выбранного сервера")
await expect(rows).toHaveCount(0)
expect(control.posts).toEqual([])
await page.setViewportSize({ width: 390, height: 844 })
await context.scrollIntoViewIfNeeded()
const box = await context.boundingBox()
expect(box!.x + box!.width).toBeLessThanOrEqual(390)
await page.screenshot({ path: "test-results/wireguard-link-mobile.png", animations: "disabled" })
})
test("WireGuard: ручной выбор сервера снимает выбор туннеля и сохраняет query", async ({ page }) => {
const control = await setup(page, "/wireguard?server=2&interface=*%2F2&keep=1")
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
await page.getByRole("option").filter({ hasText: "Источник" }).click()
await expect(page).toHaveURL(/server=1&keep=1$/)
await expect(page.locator("tr[data-row-id]")).toHaveCount(2)
await expect(page.getByRole("status", { name: "WireGuard по ссылке" })).toHaveCount(0)
await page.goBack()
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
await expect(page.getByRole("status", { name: "WireGuard по ссылке" })).toContainText("192.0.2.22")
expect(control.posts).toEqual([])
})
test("WireGuard: одинаковые ID сохраняют отдельные строки интерфейсов и пиров", async ({ page }) => {
await setup(page, "/wireguard")
const rows = page.locator("tr[data-row-id]")
await expect(rows).toHaveCount(4)
expect(new Set(await rows.evaluateAll(elements => elements.map(el => el.getAttribute("data-row-id")))).size).toBe(4)
await rows.nth(2).click()
await expect(page.getByText("10.0.2.1/32", { exact: true })).toBeVisible()
await expect(page.getByText("10.0.1.1/32", { exact: true })).toHaveCount(0)
await page.getByRole("tab", { name: /Пиры/ }).click()
await expect(rows).toHaveCount(4)
expect(new Set(await rows.evaluateAll(elements => elements.map(el => el.getAttribute("data-row-id")))).size).toBe(4)
})
for (const query of ["server=missing&interface=*%2F2", "server=2&interface=missing", "interface=*%2F2"]) {
test("WireGuard: нет подмены объекта " + query, async ({ page }) => {
const control = await setup(page, "/wireguard?" + query)
await expect(page.getByRole("status", { name: "WireGuard по ссылке" })).toContainText("Интерфейс не найден или недоступен")
await expect(page.locator("tr[data-row-id]")).toHaveCount(0)
expect(control.posts).toEqual([])
})
}
for (const status of [403, 500]) {
test("WireGuard: ошибка API " + status, async ({ page }) => {
const control = await setup(page, "/wireguard?server=2&interface=*%2F2", status)
await expect(page.getByRole("status", { name: "WireGuard по ссылке" })).toContainText("Не удалось получить данные выбранного сервера")
await expect(page.locator("tr[data-row-id]")).toHaveCount(0)
expect(control.posts).toEqual([])
})
}