66 lines
5.9 KiB
TypeScript
66 lines
5.9 KiB
TypeScript
import {test,expect,type Page} from "@playwright/test"
|
|
const iface=(name="ether1")=>({id:"i",interface:name,currentCost:10,optimalCost:20,score:50,pingMs:10,dlMbps:100,ulMbps:50})
|
|
const preview=(id=1,weight=60,name="ether1")=>({serverId:id,serverName:"Роутер "+id,pingWeight:weight,interfacesTotal:1,changedCount:1,unchangedCount:0,interfaces:[iface(name)],changes:[iface(name)]})
|
|
async function base(page:Page){
|
|
await page.route("**/health",r=>r.fulfill({json:{ok:true}}))
|
|
await page.route("**/api/**",r=>{const p=new URL(r.request().url()).pathname;return r.fulfill({json:p==="/api/servers"?[1,2,3].map(id=>({id,name:"Роутер "+id,host:"192.0.2."+id,type:"home-router",country:"RU",enabled:id!==3,status:null})):p==="/api/auth/config"?{required:false}:p==="/api/filters/rules"?{rulesets:[]}:p==="/api/uptime/speed-probes"?{probes:[]}:p==="/api/ospf/catalog"?{interfaces:[],instances:[],neighbors:[],bfdSessions:[],routes:[],failures:[]}:preview(Number(p.split("/")[3]),r.request().postDataJSON()?.pingWeight??60)})})
|
|
}
|
|
for(const failure of ["http","foreign","weight","count","missing","duplicate"] as const)test("Оптимизатор: OSPF preview блокирует "+failure,async({page})=>{
|
|
await base(page);let failing=true
|
|
await page.route("**/ospf/optimize/preview",r=>{
|
|
const good=preview(),bad=failure==="foreign"?{...good,serverId:2}:failure==="weight"?{...good,pingWeight:20}:failure==="count"?{...good,changedCount:0}:failure==="duplicate"?{...good,interfaces:[iface(),iface()],interfacesTotal:2}:{}
|
|
return r.fulfill(failing?(failure==="http"?{status:503,json:{error:"Preview недоступен"}}:{json:bad}):{json:good})
|
|
})
|
|
await page.goto("/route-optimizer")
|
|
await expect(page.getByRole("alert").filter({hasText:"Ошибка preview"})).toBeVisible()
|
|
const apply=page.getByRole("button",{name:"Оптимизировать OSPF",exact:true});await expect(apply).toBeDisabled()
|
|
await expect(page.getByRole("table").filter({hasText:"Speed (dl/ul)"})).toHaveCount(0)
|
|
failing=false;await page.getByRole("button",{name:"Обновить preview OSPF",exact:true}).click()
|
|
await expect(page.getByRole("table").filter({hasText:"Speed (dl/ul)"}).locator("tr[data-row-id]")).toHaveCount(1)
|
|
await expect(apply).toBeEnabled()
|
|
})
|
|
test("Оптимизатор: preview сохраняется при фоновом отказе",async({page})=>{
|
|
await base(page);let failing=false
|
|
await page.route("**/ospf/optimize/preview",r=>r.fulfill(failing?{status:503,json:{error:"Фоновый отказ"}}:{json:preview()}))
|
|
await page.goto("/route-optimizer");const grid=page.getByRole("table").filter({hasText:"Speed (dl/ul)"});await expect(grid.locator("tr[data-row-id]")).toHaveCount(1)
|
|
failing=true;await page.getByRole("button",{name:"Обновить preview OSPF",exact:true}).click()
|
|
await expect(page.getByRole("alert").filter({hasText:"Последний снимок"})).toBeVisible();await expect(grid.locator("tr[data-row-id]")).toHaveCount(1)
|
|
await expect(page.getByRole("button",{name:"Оптимизировать OSPF",exact:true})).toBeDisabled()
|
|
})
|
|
test("Оптимизатор: поздний preview прежнего устройства игнорируется",async({page})=>{
|
|
await base(page);let started=false,release!:()=>void;const gate=new Promise<void>(resolve=>{release=resolve})
|
|
await page.route("**/ospf/optimize/preview",async r=>{const id=Number(new URL(r.request().url()).pathname.split("/")[3]);if(id===1){started=true;await gate}return r.fulfill({json:preview(id,60,"ether"+id)})})
|
|
await page.goto("/route-optimizer");await expect.poll(()=>started).toBe(true)
|
|
await page.locator("select").selectOption("2")
|
|
const grid=page.getByRole("table").filter({hasText:"Speed (dl/ul)"});await expect(grid).toContainText("ether2")
|
|
const response=page.waitForResponse(r=>new URL(r.url()).pathname==="/api/servers/1/ospf/optimize/preview");release();await(await response).finished()
|
|
await page.evaluate(()=>new Promise<void>(resolve=>requestAnimationFrame(()=>requestAnimationFrame(()=>resolve()))))
|
|
await expect(grid).toContainText("ether2");await expect(grid).not.toContainText("ether1")
|
|
})
|
|
|
|
test("Оптимизатор: URL устройства, reload и история",async({page})=>{
|
|
await base(page)
|
|
await page.goto("/route-optimizer?server=2&keep=value")
|
|
const select=page.getByRole("combobox",{name:"Устройство OSPF",exact:true})
|
|
await expect(select).toHaveValue("2")
|
|
await expect(page.getByText("0 iface · 0 area",{exact:true})).toBeVisible()
|
|
await select.selectOption("1");await expect(page).toHaveURL(/server=1&keep=value/)
|
|
await expect(select).toHaveValue("1");await page.reload();await expect(select).toHaveValue("1")
|
|
await expect(page.getByText("0 iface · 0 area",{exact:true})).toBeVisible()
|
|
await page.goBack();await expect(select).toHaveValue("2")
|
|
await expect(page.getByText("0 iface · 0 area",{exact:true})).toBeVisible()
|
|
})
|
|
for(const server of ["999","3","abc","01",""])test("Оптимизатор: недоступный URL не выбирает другой сервер "+server,async({page})=>{
|
|
await base(page);let reads=0
|
|
await page.route("**/ospf/optimize/preview",r=>{reads++;return r.fulfill({json:preview(Number(new URL(r.request().url()).pathname.split("/")[3]))})})
|
|
await page.goto("/route-optimizer?server="+server)
|
|
await expect(page.getByRole("alert").filter({hasText:"Устройство из ссылки"})).toBeVisible()
|
|
await expect(page.getByRole("combobox",{name:"Устройство OSPF",exact:true})).toHaveValue(server)
|
|
await expect(page.getByRole("button",{name:"Оптимизировать OSPF",exact:true})).toBeDisabled()
|
|
await expect(page.getByRole("button",{name:"Обновить preview OSPF",exact:true})).toBeDisabled()
|
|
expect(reads).toBe(0)
|
|
await page.getByRole("combobox",{name:"Устройство OSPF",exact:true}).selectOption("2")
|
|
await expect(page.getByText("0 iface · 0 area",{exact:true})).toBeVisible()
|
|
await expect(page.getByRole("button",{name:"Оптимизировать OSPF",exact:true})).toBeEnabled()
|
|
})
|