fix(backups): подтверждать операции в текущем контексте
This commit is contained in:
1 parent
714af7e872
commit
4c5604bf87
9 files changed
+174
-49
No files matched your search
@@ -63,3 +63,32 @@ for (const phase of ["POST", "poll"]) for (const change of ["backend", "mode"])
|
||||
await expect(page.getByText("API подтвердил создание всех резервных копий", { exact: true })).toHaveCount(0)
|
||||
await expect(page.getByRole("button", { name: "Задача резервного копирования", exact: true })).toHaveCount(0)
|
||||
})
|
||||
|
||||
for (const operation of ["delete", "restore", "download"]) for (const change of ["backend", "mode"]) for (const fails of [false, true]) test("Бэкапы: поздняя операция/" + operation + "/" + change + "/" + fails, async ({ page }) => {
|
||||
await setupBackups(page, headers)
|
||||
let started = false, completed = false, downloads = 0, release!: () => void; const gate = new Promise<void>(r => release = r)
|
||||
page.on("download", () => downloads++)
|
||||
const suffix = operation === "delete" ? "" : "/" + operation
|
||||
await page.route("**/api/backups/backup-1" + suffix, async r => {
|
||||
started = true; await gate
|
||||
try {
|
||||
if (fails) await r.fulfill({ headers, status: 503, json: { error: "СТАРАЯ ОПЕРАЦИЯ ОТКАЗАЛА" } })
|
||||
else if (operation === "delete") await r.fulfill({ headers, status: 204 })
|
||||
else if (operation === "restore") await r.fulfill({ headers, json: { filename: backup.filename, serverName: "СТАРЫЙ РОУТЕР" } })
|
||||
else await r.fulfill({ headers, contentType: "text/plain", body: "old config" })
|
||||
} catch {} finally { completed = true }
|
||||
})
|
||||
await page.goto("/backups")
|
||||
const label = operation === "delete" ? "Удалить" : operation === "restore" ? "Восстановить" : "Скачать"
|
||||
await page.getByRole("button", { name: label + " " + backup.filename, exact: true }).click()
|
||||
if (operation !== "download") await page.getByRole("alertdialog").getByRole("button", { name: label, exact: true }).click()
|
||||
await expect.poll(() => started).toBe(true); await page.goto("/settings")
|
||||
if (change === "backend") { const input = page.getByPlaceholder("http://localhost:8000"); await input.fill("http://localhost:8101"); await input.press("Enter") }
|
||||
else await page.getByRole("button", { name: "Моковые", exact: true }).click()
|
||||
await page.goto("/backups"); release(); await expect.poll(() => completed).toBe(true)
|
||||
if (change === "backend") await expect(page.getByText(backup.filename, { exact: true })).toBeVisible()
|
||||
else await expect(page.getByText("Демонстрационные данные. Операции и сохранение недоступны.", { exact: true })).toBeVisible()
|
||||
await expect(page.getByText(/СТАРАЯ ОПЕРАЦИЯ ОТКАЗАЛА|СТАРЫЙ РОУТЕР/)).toHaveCount(0)
|
||||
await expect(page.getByText("API подтвердил удаление резервной копии", { exact: true })).toHaveCount(0)
|
||||
await expect(page.getByRole("alertdialog")).toHaveCount(0); expect(downloads).toBe(0)
|
||||
})
|
||||
@@ -0,0 +1,39 @@
|
||||
import { test, expect } from "@playwright/test"
|
||||
import { backup, setupBackups } from "./fixtures/backups"
|
||||
|
||||
test("Бэкапы: single-flight удаление, отказ refresh не отменяет 204", async ({ page }) => {
|
||||
await setupBackups(page); let deletes = 0, deleted = false, release!: () => void; const gate = new Promise<void>(r => release = r)
|
||||
await page.route("**/api/backups", r => r.fulfill(deleted ? { status: 503, json: { error: "История после удаления отказала" } } : { json: [backup] }))
|
||||
await page.route("**/api/backups/backup-1", async r => { deletes++; await gate; deleted = true; await r.fulfill({ status: 204 }).catch(() => {}) })
|
||||
await page.goto("/backups"); await page.getByRole("button", { name: "Удалить " + backup.filename, exact: true }).click()
|
||||
const dialog = page.getByRole("alertdialog"); await dialog.getByRole("button", { name: "Удалить", exact: true }).dblclick()
|
||||
await expect(dialog.getByRole("button", { name: "Отмена", exact: true })).toBeDisabled(); await page.keyboard.press("Escape"); await expect(dialog).toBeVisible()
|
||||
release(); await expect(dialog).toBeHidden(); await expect(page.getByText(backup.filename, { exact: true })).toHaveCount(0)
|
||||
await expect(page.getByText("API подтвердил удаление резервной копии", { exact: true })).toBeVisible(); expect(deletes).toBe(1)
|
||||
await expect(page.getByRole("alert", { name: "Данные резервных копий: backups" })).toContainText("История после удаления отказала")
|
||||
})
|
||||
for (const status of [403, 503, 200]) test("Бэкапы: удаление " + status + " сохраняет цель и диалог", async ({ page }) => {
|
||||
await setupBackups(page); await page.route("**/api/backups/backup-1", r => r.fulfill({ status, json: { error: "Удаление не подтверждено" } }))
|
||||
await page.goto("/backups"); await page.getByRole("button", { name: "Удалить " + backup.filename, exact: true }).click()
|
||||
const dialog = page.getByRole("alertdialog"); await dialog.getByRole("button", { name: "Удалить", exact: true }).click()
|
||||
await expect(dialog.getByRole("alert")).toBeVisible(); await expect(dialog).toContainText(backup.filename)
|
||||
await expect(dialog.getByRole("button", { name: "Удалить", exact: true })).toBeEnabled()
|
||||
await expect(page.getByText("API подтвердил удаление резервной копии", { exact: true })).toHaveCount(0)
|
||||
})
|
||||
test("Бэкапы: ошибка restore и чужой filename сохраняют диалог", async ({ page }) => {
|
||||
await setupBackups(page); let failure = true
|
||||
await page.route("**/api/backups/backup-1/restore", r => r.fulfill(failure ? { status: 503, json: { error: "Импорт отказал" } } : { json: { filename: "other.rsc", serverName: "router" } }))
|
||||
await page.goto("/backups"); await page.getByRole("button", { name: "Восстановить " + backup.filename, exact: true }).click()
|
||||
const dialog = page.getByRole("alertdialog"); await dialog.getByRole("button", { name: "Восстановить", exact: true }).click()
|
||||
await expect(dialog.getByRole("alert").filter({ hasText: "Импорт отказал" })).toBeVisible()
|
||||
failure = false; await dialog.getByRole("button", { name: "Восстановить", exact: true }).click()
|
||||
await expect(dialog.getByRole("alert").filter({ hasText: "другому файлу" })).toBeVisible()
|
||||
await expect(dialog).toContainText(backup.filename)
|
||||
})
|
||||
test("Бэкапы: download получает файл, HTML 200 показывает ошибку", async ({ page }) => {
|
||||
await setupBackups(page); let html = false
|
||||
await page.route("**/api/backups/backup-1/download", r => r.fulfill({ contentType: html ? "text/html" : "text/plain; charset=utf-8", body: html ? "<html>Error</html>" : "config" }))
|
||||
await page.goto("/backups"); const button = page.getByRole("button", { name: "Скачать " + backup.filename, exact: true })
|
||||
const download = page.waitForEvent("download"); await button.click(); expect((await download).suggestedFilename()).toBe(backup.filename)
|
||||
html = true; await button.click(); await expect(page.getByRole("alert", { name: "Операция резервных копий" })).toContainText("не подтверждён API")
|
||||
})
|
||||
Reference in new issue
Block a user