fix(containers): подтверждать остановку и удаление контейнера
This commit is contained in:
1 parent
b3d11276dd
commit
74151176d7
20 files changed
+414
-28
No files matched your search
@@ -1,8 +1,13 @@
|
||||
import {test,expect,type Page} from "@playwright/test"
|
||||
import {container,server,state} from "./fixtures/containers"
|
||||
async function routes(page:Page){await page.route("**/health",r=>r.fulfill({json:{ok:true}}));await page.route("**/api/**",r=>r.fulfill({json:new URL(r.request().url()).pathname==="/api/auth/config"?{required:false}:new URL(r.request().url()).pathname==="/api/servers"?[server]:state()}))}
|
||||
test.beforeEach(async({page})=>routes(page))
|
||||
test.beforeEach(async({page}, info)=>{await routes(page); if(info.title.startsWith("Удаление контейнера:")) await page.route("**/api/containers/state",r=>r.fulfill({json:state([{...container,status:"stopped"}])}))})
|
||||
async function action(page:Page,name:string){await page.getByRole("button",{name:"Действия контейнера "+container.name}).click();await page.getByRole("menuitem",{name,exact:true}).click()}
|
||||
for(const status of ["running","unknown","error"] as const)test("Удаление требует остановки: "+status,async({page})=>{
|
||||
let calls=0;await page.route("**/api/containers/state",r=>r.fulfill({json:state([{...container,status}])}));await page.route("**/api/servers/1/containers/remove",r=>{calls++;return r.fulfill({json:{ok:true}})})
|
||||
await page.goto("/containers");await action(page,"Удалить");const dialog=page.getByRole("alertdialog");await expect(dialog).toContainText("после подтверждённой остановки");await expect(dialog.getByRole("button",{name:"Удалить",exact:true})).toBeDisabled();expect(calls).toBe(0)
|
||||
await page.route("**/api/containers/state",r=>r.fulfill({json:state([{...container,status:"stopped"}])}));await dialog.getByRole("button",{name:"Обновить каталог",exact:true}).click();await expect(dialog.getByRole("button",{name:"Удалить",exact:true})).toBeEnabled();expect(calls).toBe(0)
|
||||
})
|
||||
for(const kind of ["403","502","malformed"] as const)test("Удаление контейнера: ошибка сохраняет target "+kind,async({page})=>{
|
||||
let calls=0;await page.route("**/api/servers/1/containers/remove",r=>{calls++;return r.fulfill(kind==="malformed"?{json:{ok:false}}:{status:Number(kind),json:{error:"УДАЛЕНИЕ ОТКАЗ"}})})
|
||||
await page.goto("/containers");await action(page,"Удалить");const dialog=page.getByRole("alertdialog");await dialog.getByRole("button",{name:"Удалить",exact:true}).click();await expect(dialog.getByRole("alert",{name:"Удаление контейнера"})).toBeVisible();await expect(dialog).toContainText(container.name);expect(calls).toBe(1)
|
||||
@@ -10,7 +15,7 @@ for(const kind of ["403","502","malformed"] as const)test("Удаление ко
|
||||
})
|
||||
test("Удаление контейнера: lock, Escape, один запрос и refresh failure",async({page})=>{
|
||||
let release!:()=>void,calls=0,removed=false;const gate=new Promise<void>(r=>release=r)
|
||||
await page.route("**/api/containers/state",r=>r.fulfill(removed?{status:503,json:{error:"READ AFTER DELETE FAIL"}}:{json:state()}));await page.route("**/api/servers/1/containers/remove",async r=>{calls++;await gate;removed=true;await r.fulfill({json:{ok:true}})})
|
||||
await page.route("**/api/containers/state",r=>r.fulfill(removed?{status:503,json:{error:"READ AFTER DELETE FAIL"}}:{json:state([{...container,status:"stopped"}])}));await page.route("**/api/servers/1/containers/remove",async r=>{calls++;await gate;removed=true;await r.fulfill({json:{ok:true}})})
|
||||
await page.goto("/containers");await action(page,"Удалить");const dialog=page.getByRole("alertdialog");const remove=dialog.getByRole("button",{name:"Удалить",exact:true});await remove.click();await expect(dialog.getByRole("button",{name:"Удаление…",exact:true})).toBeDisabled();await expect(dialog.getByRole("button",{name:"Отмена",exact:true})).toBeDisabled();await page.keyboard.press("Escape");await expect(dialog).toBeVisible();expect(calls).toBe(1);release();await expect(dialog).toHaveCount(0);await expect(page.getByRole("status").filter({hasText:"API подтвердил команду"})).toContainText("Обновление каталогов не удалось");await expect(page.getByText(container.name,{exact:true})).toHaveCount(0)
|
||||
})
|
||||
for(const [name,path] of [["Остановить","stop"],["Запустить","start"],["Перезапустить","restart"]] as const)test("Команда контейнера: подтверждение отдельно от состояния "+path,async({page})=>{
|
||||
@@ -27,6 +32,6 @@ test("Команда контейнера: общий lock блокирует д
|
||||
await page.goto("/containers");await action(page,"Остановить");await expect(page.getByRole("button",{name:"Действия контейнера "+other.name})).toBeDisabled();await expect(page.getByRole("button",{name:"Обновить",exact:true})).toBeDisabled();expect(calls).toBe(1);release();await expect(page.getByRole("button",{name:"Действия контейнера "+other.name})).toBeEnabled()
|
||||
})
|
||||
test("Удаление контейнера: исчезнувший target не отправляется повторно",async({page})=>{
|
||||
let exists=true;await page.route("**/api/containers/state",r=>r.fulfill({json:state(exists?[container]:[])}));await page.route("**/api/servers/1/containers/remove",r=>{exists=false;return r.fulfill({status:502,json:{error:"Связь потеряна"}})})
|
||||
let exists=true;await page.route("**/api/containers/state",r=>r.fulfill({json:state(exists?[{...container,status:"stopped"}]:[])}));await page.route("**/api/servers/1/containers/remove",r=>{exists=false;return r.fulfill({status:502,json:{error:"Связь потеряна"}})})
|
||||
await page.goto("/containers");await action(page,"Удалить");const dialog=page.getByRole("alertdialog");await dialog.getByRole("button",{name:"Удалить",exact:true}).click();await dialog.getByRole("button",{name:"Обновить каталог",exact:true}).click();await expect(dialog.getByRole("button",{name:"Удалить",exact:true})).toBeDisabled()
|
||||
})
|
||||
Reference in new issue
Block a user