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(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"})})