Files
MikrotikManager/tests/e2e/system-database-form.spec.ts

24 lines
5.5 KiB
TypeScript

import {test,expect,type Page} from "@playwright/test"
async function setup(page:Page,restore:{status?:number;json?:unknown}={json:{ok:true}}){
let posts=0,backups=0
await page.route("**/health",r=>r.fulfill({json:{status:"ok"}}))
await page.route("**/api/**",r=>{const path=new URL(r.request().url()).pathname;if(path==="/api/auth/config")return r.fulfill({json:{required:false}});if(path==="/api/system/database/restore"){posts++;return r.fulfill(restore)}if(path==="/api/system/database/backup"){backups++;return r.fulfill({body:Buffer.from("PGDMPfixture"),contentType:"application/octet-stream",headers:{"Content-Disposition":"attachment; filename=fixture.dump","X-System-Database-Outcome":encodeURIComponent(JSON.stringify({listenerError:"Listener restart failed"}))}})}return r.fulfill({status:503,json:{error:"outside fixture"}})})
await page.goto("/settings");return {posts:()=>posts,backups:()=>backups}
}
async function choose(page:Page,bytes="PGDMPfixture"){
await page.getByRole("button",{name:"Выбрать файл",exact:true}).click();await page.getByRole("dialog").locator('input[type="file"]').setInputFiles({name:"fixture.dump",mimeType:"application/octet-stream",buffer:Buffer.from(bytes)});await page.getByRole("button",{name:"Продолжить",exact:true}).click();await expect(page.getByRole("alertdialog")).toBeVisible()
}
test("System DB: unknown checkpoint survives cancel, section navigation and reload; explicit review enables retry",async({page})=>{
const f=await setup(page,{status:503,json:{error:"Receipt lost"}});await choose(page);await page.getByRole("button",{name:"Восстановить",exact:true}).click();await expect(page.getByRole("alert")).toHaveText("Receipt lost");await expect(page.getByRole("button",{name:"Восстановить",exact:true})).toBeDisabled();expect(f.posts()).toBe(1)
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.getByRole("button",{name:"Выбрать файл",exact:true})).toBeDisabled();await page.reload();await expect(page.getByRole("button",{name:"Выбрать файл",exact:true})).toBeDisabled();await expect(page.getByText(/Запрос мог выполниться на сервере/)).toBeVisible();expect(f.posts()).toBe(1)
await page.getByRole("button",{name:"Я проверил состояние — разрешить новый запрос",exact:true}).click();await expect(page.getByRole("button",{name:"Выбрать файл",exact:true})).toBeEnabled()
})
test("System DB: invalid archive sends no POST and preserves selected file",async({page})=>{const f=await setup(page);await choose(page,"not a dump");await page.getByRole("button",{name:"Восстановить",exact:true}).click();await expect(page.getByRole("alert")).toContainText("PGDMP");expect(f.posts()).toBe(0);await expect(page.getByRole("alertdialog")).toContainText("fixture.dump");await expect(page.getByRole("button",{name:"Восстановить",exact:true})).toBeEnabled()})
test("System DB: conflict is a rejection without unknown checkpoint",async({page})=>{const f=await setup(page,{status:409,json:{error:"Операция уже выполняется"}});await choose(page);await page.getByRole("button",{name:"Восстановить",exact:true}).click();await expect(page.getByRole("alert")).toContainText("уже выполняется");await expect(page.getByRole("button",{name:"Восстановить",exact:true})).toBeEnabled();expect(f.posts()).toBe(1)})
test("System DB: prepared download keeps secondary warning and makes one request",async({page})=>{const f=await setup(page);await page.getByRole("region",{name:"Операции базы приложения"}).getByRole("button",{name:"Скачать",exact:true}).click();await expect(page.getByText("Бэкап базы приложения подготовлен к скачиванию",{exact:true})).toBeVisible();await expect(page.getByText("Listener restart failed",{exact:true})).toBeVisible();expect(f.backups()).toBe(1)})
test("System DB: synchronous restore lock prevents double dispatch and cancellation while pending",async({page})=>{
await setup(page);let calls=0,release!:()=>void;const gate=new Promise<void>(resolve=>release=resolve);await page.route("**/api/system/database/restore",async r=>{calls++;await gate;await r.fulfill({json:{ok:true}})})
await choose(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(()=>calls).toBe(1);release();await expect(page.getByText("База приложения восстановлена",{exact:true})).toBeVisible();expect(calls).toBe(1)
})
for(const width of [1440,390,320])test("System DB: adaptive confirmation/"+width,async({page})=>{await page.setViewportSize({width,height:900});await setup(page);await choose(page);await expect(page.getByRole("button",{name:"Восстановить",exact:true})).toBeVisible();expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth)).toBe(true);await page.screenshot({path:"docs/ux-ui/screens/system-database-confirm-"+width+".png",fullPage:true,animations:"disabled"})})