444 lines
25 KiB
TypeScript
444 lines
25 KiB
TypeScript
import { test, expect, type Page, type Route } from "@playwright/test"
|
|
import { serverReadSchema, type ServerRead } from "../../packages/contracts/src/servers"
|
|
|
|
function server(id: number): ServerRead {
|
|
return serverReadSchema.parse({
|
|
id, name: `Проверка ${id}`, host: `192.0.2.${id}`, port: 443,
|
|
username: "test-user", password: "fixture-only", useSsl: true, verifySsl: true,
|
|
type: "exit-node", site: "TEST", country: id === 2 ? "DE" : "RU", asn: "", comment: "",
|
|
enabled: true, lanSubnet: "", wanUplinks: [],
|
|
createdAt: "2026-09-13T00:00:00Z", updatedAt: "2026-09-13T00:00:00Z",
|
|
status: "offline", latency: null, os: "7.20", model: "Test router",
|
|
uptime: null, cpuLoad: null, freeMemory: null, totalMemory: null,
|
|
identityName: null, sessions: 0, polledAt: null,
|
|
})
|
|
}
|
|
|
|
async function setup(page: Page, intercept?: (route: Route, path: string) => Promise<boolean>, count = 2, url = "/servers") {
|
|
const rows = Array.from({ length: count }, (_, i) => server(i + 1))
|
|
await page.route("**/health", route => route.fulfill({ json: { ok: true } }))
|
|
await page.route("**/api/**", async route => {
|
|
const path = new URL(route.request().url()).pathname
|
|
if (await intercept?.(route, path)) return
|
|
const method = route.request().method()
|
|
let body: unknown = {}
|
|
if (path === "/api/auth/config") body = { required: false }
|
|
else if (path === "/api/servers") {
|
|
if (method === "POST") {
|
|
const added = { ...server(rows.length + 1), ...route.request().postDataJSON() }
|
|
rows.push(added); body = added
|
|
} else body = rows
|
|
} else if (/^\/api\/servers\/\d+$/.test(path)) {
|
|
const index = rows.findIndex(s => s.id === Number(path.split("/").pop()))
|
|
if (method === "DELETE") { rows.splice(index, 1); await route.fulfill({ status: 204 }); return }
|
|
if (method === "PUT") rows[index] = { ...rows[index], ...route.request().postDataJSON() }
|
|
body = rows[index]
|
|
}
|
|
await route.fulfill({ json: body })
|
|
})
|
|
await page.goto(url)
|
|
if (url === "/servers") await expect(page.getByText("Проверка 1", { exact: true })).toBeVisible()
|
|
}
|
|
|
|
async function edit(page: Page, id = 1) {
|
|
await page.getByRole("button", { name: `Действия: Проверка ${id}`, exact: true }).click()
|
|
await page.getByRole("menuitem", { name: "Редактировать", exact: true }).click()
|
|
}
|
|
|
|
test("ссылка на сервер: выбор, опрос одного устройства, сброс и возврат", async ({ page }) => {
|
|
const calls: string[] = []
|
|
await setup(page, async (route, path) => {
|
|
if (!path.endsWith("/poll")) return false
|
|
calls.push(path)
|
|
await route.fulfill({ json: {} })
|
|
return true
|
|
}, 2, "/servers?server=2&keep=1")
|
|
const context = page.getByRole("status", { name: "Сервер по ссылке" })
|
|
await expect(context).toContainText("Выбран сервер: Проверка 2")
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
|
|
await page.getByRole("button", { name: "Проверить сервер", exact: true }).click()
|
|
await expect(page.getByText("Опрос: успешно 1 из 1", { exact: true })).toBeVisible()
|
|
expect(calls).toEqual(["/api/servers/2/poll"])
|
|
await context.getByRole("link", { name: "Все серверы", exact: true }).click()
|
|
await expect(page).toHaveURL(/\/servers\?keep=1$/)
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(2)
|
|
await page.goBack()
|
|
await expect(context).toContainText("Выбран сервер: Проверка 2")
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
|
|
await page.reload()
|
|
await expect(context).toContainText("Выбран сервер: Проверка 2")
|
|
await page.setViewportSize({ width: 390, height: 844 })
|
|
await context.scrollIntoViewIfNeeded()
|
|
await page.screenshot({ path: "test-results/server-link-mobile.png", animations: "disabled" })
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
|
})
|
|
|
|
for (const apiStatus of [200, 403, 500]) {
|
|
test(`ссылка на отсутствующий сервер: ответ ${apiStatus} не выбирает другое устройство`, async ({ page }) => {
|
|
await setup(page, async (route, path) => {
|
|
if (path !== "/api/servers" || apiStatus === 200) return false
|
|
await route.fulfill({ status: apiStatus, json: { error: "Список недоступен" } })
|
|
return true
|
|
}, 2, "/servers?server=999")
|
|
const context = page.getByRole("status", { name: "Сервер по ссылке" })
|
|
await expect(context).toContainText(apiStatus === 200 ? "Сервер не найден или недоступен" : "Не удалось загрузить сервер")
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(0)
|
|
await expect(page.getByRole("button", { name: "Проверить сервер", exact: true })).toBeDisabled()
|
|
})
|
|
}
|
|
|
|
test("таблица v9 сохраняет все строки, сортировку и раскрытие деталей", async ({ page }) => {
|
|
await setup(page, undefined, 12)
|
|
const rows = page.locator('tr[data-row-id]')
|
|
await expect(rows).toHaveCount(12)
|
|
await page.getByRole("button", { name: "Имя / Хост", exact: true }).click()
|
|
await expect(rows.first()).toContainText("Проверка 1")
|
|
await page.getByRole("button", { name: "Имя / Хост", exact: true }).click()
|
|
await expect(rows.first()).toContainText("Проверка 12")
|
|
await page.getByText("Проверка 12", { exact: true }).click()
|
|
await expect(page.getByText("Модель: Test router", { exact: true })).toBeVisible()
|
|
await page.getByText("Проверка 12", { exact: true }).click()
|
|
await expect(page.getByText("Модель: Test router", { exact: true })).toBeHidden()
|
|
})
|
|
|
|
async function finalStep(page: Page) {
|
|
for (let i = 0; i < 3; i++) await page.getByRole("button", { name: "Далее", exact: true }).click()
|
|
}
|
|
|
|
test("фильтры сервера: применение, AND, изменение и сброс", async ({ page }) => {
|
|
const errors: string[] = []
|
|
page.on("pageerror", error => errors.push(error.message))
|
|
await setup(page)
|
|
await page.getByRole("button", { name: "Фильтры", exact: true }).click()
|
|
await page.getByRole("option", { name: "Статус", exact: true }).click()
|
|
await page.getByRole("option", { name: "равно", exact: true }).click()
|
|
await page.getByRole("option", { name: "Online", exact: true }).click()
|
|
await expect(page.getByText("Нет совпадений", { exact: true })).toBeVisible()
|
|
await page.getByRole("button", { name: "Online", exact: true }).click()
|
|
await page.getByRole("option", { name: "Offline", exact: true }).click()
|
|
await expect(page.locator('tr[data-row-id]')).toHaveCount(2)
|
|
await page.getByRole("button", { name: "Фильтры", exact: true }).click()
|
|
await page.getByRole("option", { name: "Страна", exact: true }).click()
|
|
await page.getByRole("option", { name: "равно", exact: true }).click()
|
|
await page.getByRole("option", { name: "RU", exact: true }).click()
|
|
await expect(page.locator('tr[data-row-id]')).toHaveCount(1)
|
|
await expect(page.getByText("Проверка 1", { exact: true })).toBeVisible()
|
|
await page.screenshot({ path: "test-results/servers-desktop.png", fullPage: true, animations: "disabled" })
|
|
await page.getByRole("button", { name: "Очистить", exact: true }).click()
|
|
await expect(page.locator('tr[data-row-id]')).toHaveCount(2)
|
|
expect(errors).toEqual([])
|
|
})
|
|
|
|
test("мобильный эталон: клавиатура, Escape и раскрытие", async ({ page }) => {
|
|
await page.setViewportSize({ width: 390, height: 844 })
|
|
await setup(page)
|
|
const filters = page.getByRole("button", { name: "Фильтры", exact: true })
|
|
await filters.focus()
|
|
await page.keyboard.press("Enter")
|
|
await expect(page.getByRole("combobox", { name: "Поиск поля…" })).toBeFocused()
|
|
await page.keyboard.press("Escape")
|
|
await expect(filters).toBeFocused()
|
|
const expand = page.getByRole("button", { name: "Раскрыть: Проверка 1", exact: true })
|
|
await expand.focus()
|
|
await page.keyboard.press("Enter")
|
|
await expect(page.getByText("Модель: Test router", { exact: true })).toBeVisible()
|
|
await page.getByRole("button", { name: "Свернуть: Проверка 1", exact: true }).press("Enter")
|
|
await expect(page.getByText("Модель: Test router", { exact: true })).toBeHidden()
|
|
await page.screenshot({ path: "test-results/servers-mobile.png", fullPage: true, animations: "disabled" })
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth <= window.innerWidth)).toBe(true)
|
|
})
|
|
|
|
for (const status of [400, 403, 500]) {
|
|
test(`ошибка сохранения ${status} сохраняет форму; повтор завершается успешно`, async ({ page }) => {
|
|
let fail = true
|
|
await setup(page, async (route, path) => {
|
|
if (path === "/api/servers/1" && route.request().method() === "PUT" && fail) {
|
|
await route.fulfill({ status, json: { error: "Сохранение отклонено" } }); return true
|
|
}
|
|
return false
|
|
})
|
|
await edit(page)
|
|
await expect(page.getByRole("button", { name: "Далее", exact: true })).toBeEnabled()
|
|
await finalStep(page)
|
|
await page.getByRole("button", { name: "Сохранить", exact: true }).click()
|
|
await expect(page.getByRole("alert")).toContainText(status === 403 ? "Недостаточно прав" : "Сохранение отклонено")
|
|
await expect(page.getByRole("dialog")).toBeVisible()
|
|
fail = false
|
|
await page.getByRole("button", { name: "Сохранить", exact: true }).click()
|
|
await expect(page.getByRole("dialog")).toBeHidden()
|
|
await expect(page.getByText("Сервер сохранён.", { exact: true })).toBeVisible()
|
|
})
|
|
}
|
|
|
|
test("двойное сохранение отправляет один запрос и блокирует форму", async ({ page }) => {
|
|
let requests = 0
|
|
let finish!: () => void
|
|
const pending = new Promise<void>(resolve => { finish = resolve })
|
|
await setup(page, async (route, path) => {
|
|
if (path === "/api/servers/1" && route.request().method() === "PUT") {
|
|
requests++; await pending
|
|
await route.fulfill({ json: server(1) }); return true
|
|
}
|
|
return false
|
|
})
|
|
await edit(page); await finalStep(page)
|
|
await page.getByRole("button", { name: "Сохранить", exact: true }).evaluate((button: HTMLButtonElement) => { button.click(); button.click() })
|
|
await expect(page.getByRole("button", { name: "Сохранение…", exact: true })).toBeDisabled()
|
|
await expect(page.getByRole("button", { name: "Отмена", exact: true })).toBeDisabled()
|
|
await expect.poll(() => requests).toBe(1)
|
|
finish()
|
|
await expect(page.getByRole("dialog")).toBeHidden()
|
|
expect(requests).toBe(1)
|
|
})
|
|
|
|
test("ошибка удаления оставляет строку и подтверждение; повтор удаляет", async ({ page }) => {
|
|
let fail = true
|
|
await setup(page, async (route, path) => {
|
|
if (path === "/api/servers/1" && route.request().method() === "DELETE" && fail) {
|
|
await route.fulfill({ status: 500, json: { error: "Удаление отклонено" } }); return true
|
|
}
|
|
return false
|
|
})
|
|
await page.getByRole("button", { name: "Действия: Проверка 1", exact: true }).click()
|
|
await page.getByRole("menuitem", { name: "Удалить", exact: true }).click()
|
|
await expect(page.getByRole("alertdialog")).toContainText("Проверка 1")
|
|
await page.getByRole("button", { name: "Удалить", exact: true }).click()
|
|
await expect(page.getByRole("alertdialog")).toContainText("Удаление отклонено")
|
|
await expect(page.getByText("Проверка 1", { exact: true })).toBeVisible()
|
|
fail = false
|
|
await page.getByRole("button", { name: "Удалить", exact: true }).click()
|
|
await expect(page.getByRole("alertdialog")).toBeHidden()
|
|
await expect(page.getByText("Проверка 1", { exact: true })).toHaveCount(0)
|
|
})
|
|
|
|
test("отключение сохраняет offline-статус и отправляет enabled через API", async ({ page }) => {
|
|
let payload: unknown
|
|
await setup(page, async (route, path) => {
|
|
if (path === "/api/servers/1" && route.request().method() === "PUT") payload = route.request().postDataJSON()
|
|
return false
|
|
})
|
|
await page.getByRole("button", { name: "Действия: Проверка 1", exact: true }).click()
|
|
await page.getByRole("menuitem", { name: "Отключить", exact: true }).click()
|
|
await expect(page.getByText("Проверка 1: отключён.", { exact: true })).toBeVisible()
|
|
expect(payload).toEqual({ enabled: false })
|
|
await expect(page.getByText("Оффлайн", { exact: true })).toHaveCount(2)
|
|
})
|
|
|
|
test("ошибка загрузки деталей блокирует сохранение и позволяет повтор", async ({ page }) => {
|
|
let fail = true
|
|
await setup(page, async (route, path) => {
|
|
if (path === "/api/servers/1" && route.request().method() === "GET" && fail) {
|
|
await route.fulfill({ status: 403, json: { error: "Forbidden" } }); return true
|
|
}
|
|
return false
|
|
})
|
|
await edit(page)
|
|
await expect(page.getByRole("alert")).toContainText("Недостаточно прав")
|
|
await expect(page.getByRole("button", { name: "Далее", exact: true })).toBeDisabled()
|
|
fail = false
|
|
await page.getByRole("button", { name: "Повторить загрузку", exact: true }).click()
|
|
await expect(page.getByRole("button", { name: "Далее", exact: true })).toBeEnabled()
|
|
})
|
|
|
|
test("поздний ответ первого сервера не перезаписывает второй", async ({ page }) => {
|
|
let finish!: () => void
|
|
const pending = new Promise<void>(resolve => { finish = resolve })
|
|
await setup(page, async (route, path) => {
|
|
if (path === "/api/servers/1" && route.request().method() === "GET") {
|
|
await pending; await route.fulfill({ json: { ...server(1), username: "wrong-user" } }); return true
|
|
}
|
|
return false
|
|
})
|
|
await edit(page)
|
|
await expect(page.getByText("Загрузка параметров сервера…")).toBeVisible()
|
|
await page.getByRole("button", { name: "Отмена", exact: true }).click()
|
|
await edit(page, 2)
|
|
await expect(page.getByRole("button", { name: "Далее", exact: true })).toBeEnabled()
|
|
finish()
|
|
await page.getByRole("button", { name: "Далее", exact: true }).click()
|
|
await page.getByRole("button", { name: "Далее", exact: true }).click()
|
|
await expect(page.getByPlaceholder("api-user", { exact: true })).toHaveValue("test-user")
|
|
})
|
|
|
|
test("массовый опрос ограничен четырьмя запросами и повторяет только ошибки", async ({ page }) => {
|
|
let active = 0, maximum = 0
|
|
const calls: string[] = []
|
|
let fail = true
|
|
await setup(page, async (route, path) => {
|
|
if (path.endsWith("/poll")) {
|
|
calls.push(path); maximum = Math.max(maximum, ++active)
|
|
await new Promise(resolve => setTimeout(resolve, 100))
|
|
active--
|
|
await route.fulfill({ status: fail && path === "/api/servers/2/poll" ? 500 : 200, json: { error: "Опрос недоступен" } })
|
|
return true
|
|
}
|
|
return false
|
|
}, 6)
|
|
await page.getByRole("button", { name: "Проверить все", exact: true }).click()
|
|
await expect(page.getByText("Опрос: успешно 5 из 6", { exact: true })).toBeVisible()
|
|
expect(maximum).toBeLessThanOrEqual(4)
|
|
expect(calls).toHaveLength(6)
|
|
fail = false
|
|
await page.getByRole("button", { name: "Повторить неуспешные", exact: true }).click()
|
|
await expect(page.getByText("Опрос: успешно 1 из 1", { exact: true })).toBeVisible()
|
|
expect(calls.slice(6)).toEqual(["/api/servers/2/poll"])
|
|
})
|
|
|
|
test("таймаут сохранения сохраняет форму и показывает неопределённый результат", async ({ page }) => {
|
|
// Speed up only the browser's server-operation deadline, keeping real fetch/AbortSignal.
|
|
await page.addInitScript(() => {
|
|
const timeout = AbortSignal.timeout.bind(AbortSignal)
|
|
AbortSignal.timeout = (ms: number) => timeout(ms === 30_000 ? 500 : ms)
|
|
})
|
|
await setup(page, async (route, path) => {
|
|
if (path === "/api/servers/1" && route.request().method() === "PUT") {
|
|
await new Promise(resolve => setTimeout(resolve, 1500))
|
|
await route.fulfill({ json: server(1) }).catch(() => {})
|
|
return true
|
|
}
|
|
return false
|
|
})
|
|
await edit(page); await finalStep(page)
|
|
await page.getByRole("button", { name: "Сохранить", exact: true }).click()
|
|
await expect(page.getByRole("alert")).toContainText("Результат операции неизвестен")
|
|
await expect(page.getByRole("button", { name: "Сохранить", exact: true })).toBeEnabled()
|
|
})
|
|
|
|
test("создание сервера проходит через API и добавляет подтверждённую запись", async ({ page }) => {
|
|
await setup(page)
|
|
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 page.getByPlaceholder("203.0.113.1", { exact: true }).fill("192.0.2.99")
|
|
await page.getByPlaceholder("api-user", { exact: true }).fill("fixture-user")
|
|
await page.getByRole("button", { name: "Далее", exact: true }).click()
|
|
await page.getByRole("dialog").getByRole("button", { name: "Добавить сервер", exact: true }).click()
|
|
await expect(page.getByRole("dialog")).toBeHidden()
|
|
await expect(page.getByRole("button", { name: "Действия: 192.0.2.99", exact: true })).toHaveCount(1)
|
|
})
|
|
|
|
test("успешный опрос и ошибка обновления списка имеют раздельные результаты", async ({ page }) => {
|
|
let polled = false
|
|
await setup(page, async (route, path) => {
|
|
if (path.endsWith("/poll")) { polled = true; await route.fulfill({ json: {} }); return true }
|
|
if (path === "/api/servers" && polled) {
|
|
await route.fulfill({ status: 500, json: { error: "Список недоступен" } }); return true
|
|
}
|
|
return false
|
|
})
|
|
await page.getByRole("button", { name: "Действия: Проверка 1", exact: true }).click()
|
|
await page.getByRole("menuitem", { name: "Опросить", exact: true }).click()
|
|
await expect(page.getByText("Опрос: успешно 1 из 1", { exact: true })).toBeVisible()
|
|
await expect(page.getByRole("alert").filter({ hasText: "Действие не завершено" })).toContainText("Опрос завершён, но список не обновлён")
|
|
await expect(page.getByText("Проверка 1", { exact: true })).toBeVisible()
|
|
})
|
|
|
|
test("ошибка отключения не меняет enabled", async ({ page }) => {
|
|
await setup(page, async (route, path) => {
|
|
if (path === "/api/servers/1" && route.request().method() === "PUT") {
|
|
await route.fulfill({ status: 403, json: { error: "Forbidden" } }); return true
|
|
}
|
|
return false
|
|
})
|
|
await page.getByRole("button", { name: "Действия: Проверка 1", exact: true }).click()
|
|
await page.getByRole("menuitem", { name: "Отключить", exact: true }).click()
|
|
await expect(page.getByRole("alert").filter({ hasText: "Действие не завершено" })).toContainText("Недостаточно прав")
|
|
await page.getByRole("button", { name: "Действия: Проверка 1", exact: true }).click()
|
|
await expect(page.getByRole("menuitem", { name: "Отключить", exact: true })).toBeVisible()
|
|
})
|
|
|
|
test("изменение подключения сбрасывает результат его проверки", async ({ page }) => {
|
|
await setup(page, async (route, path) => {
|
|
if (path.endsWith("/test-connection")) {
|
|
await route.fulfill({ json: { success: true, message: "Проверено тестовым API" } }); return true
|
|
}
|
|
return false
|
|
})
|
|
await edit(page)
|
|
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("Проверено тестовым API", { exact: true })).toBeVisible()
|
|
await page.getByPlaceholder("api-user", { exact: true }).fill("changed-user")
|
|
await expect(page.getByText("Проверено тестовым API", { exact: true })).toHaveCount(0)
|
|
})
|
|
|
|
test("URL серверов: поиск, тип, история, reload и сохранение контекста", async ({ page }) => {
|
|
const writes: string[] = []
|
|
await setup(page, async (route) => { if (!["GET", "HEAD"].includes(route.request().method())) writes.push(route.request().method()); return false }, 2, "/servers?server=2&keep=1")
|
|
const search = page.getByPlaceholder("Поиск по имени, хосту…")
|
|
await search.fill("Проверка 2")
|
|
await page.getByRole("button", { name: /JumpHost/ }).click()
|
|
await expect(page).toHaveURL(/type=jump-host/)
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(0)
|
|
await page.goBack()
|
|
await expect(search).toHaveValue("Проверка 2")
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
|
|
await page.goForward()
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(0)
|
|
await page.reload()
|
|
await expect(search).toHaveValue("Проверка 2")
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(0)
|
|
await page.setViewportSize({ width: 390, height: 844 })
|
|
const reset = page.getByRole("button", { name: "Сбросить фильтры списка", exact: true })
|
|
await reset.scrollIntoViewIfNeeded()
|
|
const bounds = await reset.boundingBox()
|
|
expect(bounds!.x + bounds!.width).toBeLessThanOrEqual(390)
|
|
await page.screenshot({ path: "test-results/server-url-mobile.png", animations: "disabled" })
|
|
await page.getByRole("button", { name: "Сбросить фильтры списка", exact: true }).click()
|
|
await expect(page).toHaveURL(/\/servers\?server=2&keep=1$/)
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
|
|
expect(writes).toEqual([])
|
|
})
|
|
|
|
test("URL серверов: дерево OR и negated восстанавливает строки и контролы", async ({ page }) => {
|
|
const query = { id: "root", type: "group", combinator: "or", rules: [
|
|
{ id: "country", type: "rule", path: ["country"], operator: "is", value: "DE", negated: true },
|
|
{ id: "status", type: "rule", path: ["status"], operator: "is", value: "online" },
|
|
] }
|
|
await setup(page, undefined, 2, "/servers?" + new URLSearchParams({ filters: JSON.stringify({ v: 1, query }) }))
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
|
|
await expect(page.locator("tr[data-row-id]")).toContainText("Проверка 1")
|
|
await page.reload()
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
|
|
await expect(page.getByRole("button", { name: "DE", exact: true })).toBeVisible()
|
|
await page.getByRole("button", { name: "Очистить", exact: true }).click()
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(2)
|
|
await expect(page).toHaveURL(/\/servers$/)
|
|
})
|
|
|
|
for (const value of ["{", JSON.stringify({ v: 99 }), "x".repeat(8193)]) {
|
|
test("URL серверов: ошибочный filters длиной " + value.length, async ({ page }) => {
|
|
await setup(page, undefined, 2, "/servers?" + new URLSearchParams({ filters: value, server: "1", keep: "1" }))
|
|
await expect(page.getByRole("alert").filter({ hasText: "Фильтры требуют внимания" })).toContainText("Не удалось прочитать фильтры")
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(0)
|
|
await page.getByRole("button", { name: "Сбросить фильтры списка", exact: true }).click()
|
|
await expect(page).toHaveURL(/server=1&keep=1$/)
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
|
|
})
|
|
}
|
|
|
|
test("URL серверов: черновик не публикуется, быстрый поиск не затирает фильтр", async ({ page }) => {
|
|
let reads = 0
|
|
await setup(page, async (_route, path) => { if (path === "/api/servers") reads++; return false })
|
|
const initialReads = reads
|
|
await page.getByRole("button", { name: "Фильтры", exact: true }).click()
|
|
await page.getByRole("option", { name: "Страна", exact: true }).click()
|
|
await page.getByRole("option", { name: "равно", exact: true }).click()
|
|
expect(new URL(page.url()).searchParams.has("filters")).toBe(false)
|
|
await page.getByRole("option", { name: "DE", exact: true }).click()
|
|
await expect.poll(() => new URL(page.url()).searchParams.has("filters")).toBe(true)
|
|
const filters = new URL(page.url()).searchParams.get("filters")
|
|
const search = page.getByPlaceholder("Поиск по имени, хосту…")
|
|
await search.pressSequentially("192.0.2.2", { delay: 0 })
|
|
await expect(search).toHaveValue("192.0.2.2")
|
|
await expect.poll(() => new URL(page.url()).searchParams.get("q")).toBe("192.0.2.2")
|
|
expect(new URL(page.url()).searchParams.get("filters")).toBe(filters)
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
|
|
expect(reads).toBe(initialReads)
|
|
await page.reload()
|
|
await expect(page.getByRole("button", { name: "DE", exact: true })).toBeVisible()
|
|
await expect(search).toHaveValue("192.0.2.2")
|
|
await expect(page.locator("tr[data-row-id]")).toHaveCount(1)
|
|
})
|