Files

69 lines
7.0 KiB
TypeScript

import { test, expect, type Page } from "@playwright/test"
const host=(id:number)=>({serverId:id,serverName:"Роутер "+id,serverSite:"",serverCountry:"RU"})
const iface=(id:number)=>({...host(id),id:"*1",instance:"default",area:"backbone",areaId:"0.0.0.0",interface:"ether"+id,cost:20,type:"broadcast",disabled:false,inactive:false,priority:1,helloInterval:10,deadInterval:40,useBfd:false})
const catalog={interfaces:[iface(1),iface(2)],neighbors:[],instances:[],bfdSessions:[],routes:[],failures:[]}
async function setup(page:Page){await page.route("**/health",r=>r.fulfill({json:{ok:true}}));await page.route("**/api/**",r=>{const path=new URL(r.request().url()).pathname;return r.fulfill({json:path==="/api/ospf/catalog"?catalog:path==="/api/servers"?[1,2].map(id=>({id,name:"Роутер "+id,host:"192.0.2."+id,country:"RU",enabled:true,status:null})):path==="/api/auth/config"?{required:false}:{}})})}
test("OSPF: устройство и вкладка переживают reload/back",async({page})=>{
await setup(page);await page.goto("/ospf?server=2&tab=bfd&keep=x")
await expect(page.getByRole("button",{name:"BFD",exact:true})).toHaveAttribute("aria-pressed","true")
await expect(page.getByRole("option").filter({hasText:"Роутер 2"})).toHaveAttribute("aria-selected","true")
await page.getByRole("button",{name:"Интерфейсы",exact:true}).click()
await expect(page.getByText("ether2",{exact:true})).toBeVisible();await expect(page.getByText("ether1",{exact:true})).toHaveCount(0)
await page.reload();await expect(page.getByText("ether2",{exact:true})).toBeVisible()
await page.getByRole("option").filter({hasText:"Роутер 1"}).click()
await expect(page).toHaveURL(/server=1/);await expect(page).toHaveURL(/keep=x/)
await page.goBack();await expect(page.getByText("ether2",{exact:true})).toBeVisible()
await page.goBack();await expect(page.getByRole("button",{name:"BFD",exact:true})).toHaveAttribute("aria-pressed","true")
})
for(const context of ["server=999","tab=broken","server=1&server=2","tab=bfd&tab=routes"])test("OSPF: недоступный URL не показывает общий каталог "+context,async({page})=>{
await setup(page);await page.goto("/ospf?"+context+"&keep=x")
const alert=page.getByRole("alert").filter({hasText:"Контекст OSPF недоступен"});await expect(alert).toBeVisible()
await expect(page.getByText("ether1",{exact:true})).toHaveCount(0)
await expect(page.getByLabel("Сводка OSPF",{exact:true})).toContainText("неизвестно")
await alert.getByRole("button",{name:"Сбросить контекст OSPF"}).click()
await expect(page).toHaveURL(/keep=x/);await expect(alert).toHaveCount(0);await expect(page.getByText("ether1",{exact:true})).toBeVisible()
})
for(const fails of [false,true])test("OSPF: history во время записи освобождает lock "+fails,async({page})=>{
await setup(page);let writes=0,started=false,reads=0;let release!:()=>void;const gate=new Promise<void>(resolve=>{release=resolve})
await page.route("**/api/ospf/catalog",r=>{reads++;return r.fulfill({json:catalog})})
await page.route("**/api/servers/1/ospf/optimize",async r=>{writes++;started=true;await gate;return r.fulfill(fails?{status:503,json:{error:"Старая ошибка"}}:{json:{serverId:1,serverName:"Роутер 1",pingWeight:60,optimizedCount:0,applied:[],interfaces:[]}})})
await page.goto("/ospf");await page.getByRole("option").filter({hasText:"Роутер 1"}).click()
await page.getByRole("button",{name:"Оптимизировать OSPF",exact:true}).click();await expect.poll(()=>started).toBe(true)
await page.goBack();await expect(page).not.toHaveURL(/server=1/)
await page.goForward();const optimize=page.getByRole("button",{name:"Оптимизировать OSPF",exact:true});await expect(optimize).toBeDisabled()
const before=reads;const response=page.waitForResponse(r=>r.url().includes("/api/servers/1/ospf/optimize"));release();await (await response).finished()
await expect.poll(()=>reads).toBe(before+1);await expect(optimize).toBeEnabled();expect(writes).toBe(1)
await expect(page.getByText("Старая ошибка",{exact:false})).toHaveCount(0)
await expect(page.locator("[data-sonner-toast]").filter({hasText:"API подтвердил оптимизацию OSPF"})).toHaveCount(0)
})
test("OSPF: поиск из URL не выдаёт пустой срез за отсутствие настройки",async({page})=>{
await setup(page);await page.goto("/ospf?server=2&q=ether2&keep=x")
await expect(page.getByPlaceholder("Поиск в текущем разделе OSPF…")).toHaveValue("ether2")
await expect(page.getByText("ether2",{exact:true})).toBeVisible();await expect(page.getByText("ether1",{exact:true})).toHaveCount(0)
await page.getByPlaceholder("Поиск в текущем разделе OSPF…").fill("несуществующий")
await expect(page.getByText("Нет интерфейсов по текущему поиску",{exact:true})).toBeVisible()
await expect(page.getByText("OSPF интерфейсы не настроены ни на одном сервере",{exact:true})).toHaveCount(0)
await page.reload();await expect(page.getByText("Нет интерфейсов по текущему поиску",{exact:true})).toBeVisible()
await page.getByRole("button",{name:"Сбросить поиск",exact:true}).click()
await expect(page).toHaveURL(/server=2/);await expect(page).toHaveURL(/keep=x/);await expect(page.getByText("ether2",{exact:true})).toBeVisible()
})
test("OSPF: недоступный узел из URL исправляется без сброса устройства",async({page})=>{
await setup(page);await page.goto("/ospf?server=2&tab=neighbors&node=999&keep=x")
const alert=page.getByRole("alert").filter({hasText:"Узел OSPF недоступен"});await expect(alert).toBeVisible()
await alert.getByRole("button",{name:"Закрыть узел OSPF"}).click()
await expect(alert).toHaveCount(0);await expect(page).toHaveURL(/server=2/);await expect(page).toHaveURL(/keep=x/)
})
test("OSPF: выбранный узел и поиск сохраняются после reload/back",async({page})=>{
await setup(page)
await page.route("**/api/ospf/catalog",r=>r.fulfill({json:{...catalog,neighbors:[1,2].map(id=>({...host(id),id:"n",address:"198.51.100."+id,routerId:"198.51.100."+id,instance:"default",area:"backbone",areaId:"0.0.0.0",interface:"ether"+id,state:"Full",uptime:"1h",priority:1,stateChanges:0}))}}))
await page.goto("/ospf?tab=neighbors&q=ether1&keep=x")
const rows=page.locator("tr[data-row-id]");await expect(rows).toHaveCount(1)
await rows.click();await expect(page).toHaveURL(/node=1/)
const details=page.getByRole("region",{name:"Детали узла OSPF"});await expect(details).toBeVisible()
await page.reload();await expect(details).toBeVisible();await expect(rows).toHaveCount(1)
await page.getByRole("button",{name:"Закрыть",exact:true}).click();await expect(details).toHaveCount(0)
await page.goBack();await expect(details).toBeVisible();await expect(page).toHaveURL(/keep=x/)
})