import {test,expect,type Page} from "@playwright/test" const hosts=(name="Роутер")=>(["home-router","jump-host","exit-node"] as const).map((type,index)=>({id:index+1,name:name+" "+(index+1),host:"192.0.2."+(index+1),type,site:"TEST",country:"RU",enabled:true,status:"online",latency:10,model:"CHR",...(index===0?{wanUplinks:[{id:"wan",name:"WAN",isp:"ISP",iface:"ether1",ip:"198.51.100.1",maxDl:100,maxUl:100}]}:{})})) const paths={servers:"/api/servers",rules:"/api/filters/rules",probes:"/api/uptime/speed-probes"} as const const body=(p:string,name="Роутер")=>p===paths.servers?hosts(name):p===paths.rules?{rulesets:[{serverId:"1",rules:[{id:"r",community:"65001:1",communityName:"Сервис",gateway:"192.0.2.2",gatewayTunnelId:"",description:""}]}]}:p===paths.probes?{probes:[]}:p==="/api/auth/config"?{required:false}:p.endsWith("/preview")?{serverId:1,serverName:"Роутер 1",pingWeight:60,unchangedCount:0,interfaces:[],changes:[],changedCount:0,interfacesTotal:0}:p==="/api/ospf/catalog"?{interfaces:[],instances:[],neighbors:[],bfdSessions:[],routes:[],failures:[]}:{} async function health(page:Page){await page.route("**/health",r=>r.fulfill({json:{ok:true}}))} for(const part of ["servers","rules","probes"] as const)for(const malformed of [false,true])test("Оптимизатор: первоначальная ошибка и повтор "+part+"/"+malformed,async({page})=>{ let failing=true;await health(page) await page.route("**/api/**",r=>{const p=new URL(r.request().url()).pathname;return p===paths[part]&&failing?r.fulfill(malformed?{json:{}}:{status:503,json:{error:"Источник недоступен"}}):r.fulfill({json:body(p)})}) await page.goto("/route-optimizer") await expect(page.getByRole("alert").filter({hasText:"Данные оптимизатора неизвестны"})).toBeVisible() const stats=page.getByLabel("Сводка оптимизатора");await expect(stats).toContainText("Расчёт пока неизвестен");await expect(stats).not.toContainText("модель не предлагает переключений") await expect(page.getByRole("table").filter({hasText:"WAN-аплинк"})).toHaveCount(0) failing=false;await page.getByRole("button",{name:"Обновить",exact:true}).click() await expect(page.getByRole("table").filter({hasText:"WAN-аплинк"}).locator("tr[data-row-id]")).toHaveCount(1) await expect(page.getByRole("alert").filter({hasText:"Данные оптимизатора неизвестны"})).toHaveCount(0) }) for(const part of ["servers","rules","probes"] as const)test("Оптимизатор: частичный сбой сохраняет полный снимок "+part,async({page})=>{ let failing=false;await health(page) await page.route("**/api/**",r=>{const p=new URL(r.request().url()).pathname;return p===paths[part]&&failing?r.fulfill({status:503,json:{error:"Отказ части"}}):r.fulfill({json:body(p,failing?"Новый каталог":"Исходный каталог")})}) await page.goto("/route-optimizer") const grid=page.getByRole("table").filter({hasText:"WAN-аплинк"});await expect(grid.locator("tr[data-row-id]")).toHaveCount(1) await expect(grid.getByRole("columnheader",{name:/Исходный каталог 2/})).toBeVisible() failing=true;await page.getByRole("button",{name:"Обновить",exact:true}).click() await expect(page.getByRole("alert").filter({hasText:"Последний полный снимок"})).toBeVisible() await expect(grid.getByRole("columnheader",{name:/Исходный каталог 2/})).toBeVisible() await expect(page.getByRole("button",{name:"Оптимизировать OSPF",exact:true})).toBeDisabled() }) test("Оптимизатор: поздний расчёт не заменяет новый",async({page})=>{ let version="Исходный",reads=0,started=false,release!:()=>void const gate=new Promise(resolve=>{release=resolve});await health(page) await page.route("**/api/**",async r=>{const p=new URL(r.request().url()).pathname,value=body(p,version) if(p===paths.rules&&++reads===2){started=true;await gate} return r.fulfill({json:value})}) await page.goto("/route-optimizer");const grid=page.getByRole("table").filter({hasText:"WAN-аплинк"});await expect(grid.locator("tr[data-row-id]")).toHaveCount(1) await page.getByRole("button",{name:"Настройки оптимизатора",exact:true}).click() version="Старый";await page.getByRole("button",{name:"Обновить",exact:true}).click();await expect.poll(()=>started).toBe(true) version="Новый";await page.getByRole("button",{name:"Пересчитать",exact:true}).click() await expect(grid.getByRole("columnheader",{name:/Новый 2/})).toBeVisible() const response=page.waitForResponse(r=>new URL(r.url()).pathname===paths.rules);release();await(await response).finished() await page.evaluate(()=>new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(()=>resolve())))) await expect(grid.getByRole("columnheader",{name:/Новый 2/})).toBeVisible() })