import { test, expect } from "@playwright/test" import { backup, setupBackups } from "./fixtures/backups" for (const invalid of ["API", "malformed", "duplicate"]) test("Бэкапы: первоначальный отказ " + invalid + " и повтор", async ({ page }) => { await setupBackups(page); let fail = true await page.route("**/api/backups", r => r.fulfill(fail ? invalid === "API" ? { status: 503, json: { error: "История отказала" } } : { json: invalid === "duplicate" ? [backup, backup] : {} } : { json: [backup] })) await page.goto("/backups") await expect(page.getByRole("alert", { name: "Данные резервных копий: backups" })).toContainText("Данные пока неизвестны") await expect(page.getByText("Нет бэкапов", { exact: true })).toHaveCount(0) fail = false; await page.getByRole("button", { name: "Обновить", exact: true }).click() await expect(page.getByText(backup.filename, { exact: true })).toBeVisible() }) test("Бэкапы: подтверждённая пустая история", async ({ page }) => { await setupBackups(page); await page.route("**/api/backups", r => r.fulfill({ json: [] })); await page.goto("/backups") await expect(page.getByText("Нет бэкапов", { exact: true })).toBeVisible() }) for (const resource of ["servers", "backups/schedule", "backups/storage"]) test("Бэкапы: отказ зависимости " + resource + " сохраняет историю", async ({ page }) => { await setupBackups(page); await page.route("**/api/" + resource, r => r.fulfill({ status: 503, json: { error: "Зависимость отказала" } })) await page.goto("/backups"); await expect(page.getByText(backup.filename, { exact: true })).toBeVisible() await expect(page.getByText("0 Б", { exact: true })).toBeVisible() if (resource !== "backups/schedule") await expect(page.getByRole("button", { name: "Новый бэкап", exact: true })).toBeDisabled() }) test("Бэкапы: поздняя зависимость не задерживает историю", async ({ page }) => { await setupBackups(page); let release!: () => void; const gate = new Promise(r => release = r) await page.route("**/api/backups/storage", async r => { await gate; await r.fallback().catch(() => {}) }) await page.goto("/backups"); await expect(page.getByText(backup.filename, { exact: true })).toBeVisible() release(); await expect(page.getByRole("button", { name: "Обновить", exact: true })).toBeEnabled() }) test("Бэкапы: до ответа нет ложного empty, фоновая ошибка сохраняет строки", async ({ page }) => { await setupBackups(page); let release!: () => void; const gate = new Promise(r => release = r); let fail = false await page.route("**/api/backups", async r => { await gate; await r.fulfill(fail ? { status: 503, json: { error: "Фоновый отказ" } } : { json: [backup] }).catch(() => {}) }) await page.goto("/backups"); await expect(page.getByText("Загрузка резервных копий…", { exact: true })).toBeVisible() await expect(page.getByText("Нет бэкапов", { exact: true })).toHaveCount(0) release(); await expect(page.getByText(backup.filename, { exact: true })).toBeVisible() fail = true; await page.getByRole("button", { name: "Обновить", exact: true }).click() await expect(page.getByRole("alert", { name: "Данные резервных копий: backups" })).toContainText("Последние данные сохранены") await expect(page.getByText(backup.filename, { exact: true })).toBeVisible() }) for (const serverRead of [false, true]) test("Бэкапы: read-only и server read " + serverRead, async ({ page }) => { await setupBackups(page) await page.addInitScript(({ serverRead }) => localStorage.setItem("mmapp_token", "header." + btoa(JSON.stringify({ sub: "test", apps: ["mm"], permissions: ["mm:backups:read", ...(serverRead ? ["mm:servers:read"] : [])], exp: Math.floor(Date.now() / 1000) + 3600 })) + ".signature"), { serverRead }) await page.route("**/api/auth/config", r => r.fulfill({ json: { required: true } })) let serverRequests = 0; await page.route("**/api/servers", r => { serverRequests++; return r.fallback() }) await page.goto("/backups"); await expect(page.getByText(backup.filename, { exact: true })).toBeVisible() await expect(page.getByRole("button", { name: "Новый бэкап", exact: true })).toBeDisabled() await expect(page.getByRole("button", { name: "Удалить " + backup.filename, exact: true })).toBeDisabled() await page.getByRole("tab", { name: "Настройки", exact: true }).click() await expect(page.getByRole("button", { name: "Сохранить расписание", exact: true })).toBeDisabled() if (!serverRead) expect(serverRequests).toBe(0) }) test("Бэкапы: фоновое чтение сохраняет черновик хранилища", async ({ page }) => { await setupBackups(page); await page.goto("/backups") await expect(page.getByText(backup.filename, { exact: true })).toBeVisible() await page.getByRole("tab", { name: "Настройки", exact: true }).click() await page.getByRole("button", { name: "S3", exact: true }).click() await page.getByPlaceholder("mikrotik-backups", { exact: true }).fill("draft-bucket") await page.getByRole("button", { name: "Обновить", exact: true }).click() await expect(page.getByRole("button", { name: "Обновить", exact: true })).toBeEnabled() await expect(page.getByPlaceholder("mikrotik-backups", { exact: true })).toHaveValue("draft-bucket") })