import { test, expect, type Page } from "@playwright/test" import { geoip } from "./fixtures/geoip" const headers = { "access-control-allow-origin": "*", "access-control-allow-methods": "GET,POST,PUT,OPTIONS", "access-control-allow-headers": "Content-Type,Authorization" } const common = { enabled: true, intervalSec: 45, retentionDays: 14, lastCollectedAt: null, lastDurationMs: 0, lastError: null } const job = { jobKey: "backups", enabled: true, intervalSec: 60, running: false, lastFinishedAt: null, lastStatus: null, lastDurationMs: 0, lastError: null } const snapshot = { v: 1, job: "backups", sampledAt: "2026-10-03T00:00:00Z", due: true, created: 1, failures: 0, pruned: 0 } async function setup(page: Page) { await page.route("**/health", r => r.fulfill({ headers, json: { ok: true } })) await page.route("**/api/**", r => { const path = new URL(r.request().url()).pathname if (path === "/api/auth/config") return r.fulfill({ headers, json: { required: false } }) if (path === "/api/geoip") return r.fulfill({ headers, json: geoip }) if (path === "/api/scheduler/status") return r.fulfill({ headers, json: { jobs: [job] } }) if (path === "/api/scheduler/runs") return r.fulfill({ headers, json: { runs: [] } }) if (path === "/api/traffic/settings") return r.fulfill({ headers, json: { ...common, intervalSec: new URL(r.request().url()).port === "8101" ? 77 : 45 } }) return r.fulfill({ headers, status: 503, json: { error: "outside context fixture" } }) }) } for (const action of ["read", "put", "run"] as const) for (const fails of [false, true]) test("Сбор данных: поздний ответ после смены backend/" + action + "/" + fails, async ({ page }) => { await setup(page); let started = false, completed = false, release!: () => void; const gate = new Promise(resolve => release = resolve) const path = action === "run" ? "/api/scheduler/jobs/backups/run-now" : "/api/traffic/settings" await page.route("**" + path, async r => { if (new URL(r.request().url()).port !== "8000" || (action === "read" ? r.request().method() !== "GET" : action === "put" ? r.request().method() !== "PUT" : false)) return r.fallback() started = true; await gate try { await r.fulfill(fails ? { headers, status: 503, json: { error: "СТАРАЯ ОШИБКА" } } : { headers, json: action === "run" ? { ok: true, result: { status: "ok", error: null, result: snapshot }, status: { jobs: [job] } } : action === "put" ? { ok: true, settings: { ...common, intervalSec: 99 } } : { ...common, intervalSec: 99 } }) } catch {} finally { completed = true } }) try { await page.goto("/data-collection") if (action === "put") { const field = page.getByRole("textbox", { name: "Интервал: Трафик", exact: true }); await expect(field).toHaveValue("45"); await field.fill("99"); await page.getByRole("button", { name: "Сохранить настройки планировщика" }).click() } if (action === "run") { const run = page.getByRole("button", { name: "Запустить: Бэкапы", exact: true }); await expect(run).toBeEnabled(); await run.click() } await expect.poll(() => started).toBe(true); await page.goto("/settings"); const url = page.getByPlaceholder("http://localhost:8000"); await url.fill("http://localhost:8101"); await url.press("Enter"); await page.goto("/data-collection") await expect(page.getByRole("textbox", { name: "Интервал: Трафик", exact: true })).toHaveValue("77"); release(); await expect.poll(() => completed).toBe(true) await expect(page.getByRole("textbox", { name: "Интервал: Трафик", exact: true })).toHaveValue("77"); await expect(page.getByText(/СТАРАЯ ОШИБКА/)).toHaveCount(0); await expect(page.getByText("Сохранение подтверждено: Трафик", { exact: true })).toHaveCount(0); await expect(page.getByText("Запуск подтверждён: Бэкапы", { exact: true })).toHaveCount(0) } finally { release() } })