import {test,expect,type Page} from '@playwright/test' const sections=['wireguard','ipsec','gre','firewall','filters','recursive-routes'] as const function token(writer:boolean){return [Buffer.from('{}').toString('base64url'),Buffer.from(JSON.stringify({sub:'own207',apps:['mm'],permissions:['mm:dashboard:read','mm:servers:read','mm:network:read','mm:filters:read',...(writer?['mm:network:write','mm:filters:write']:[])],exp:Math.floor(Date.now()/1000)+600})).toString('base64url'),'fixture'].join('.')} async function setup(page:Page,section:string,writer:boolean,enabled=true){let writes=0 await page.addInitScript(t=>localStorage.setItem('mmapp_token',t),token(writer)) await page.route('**/health',r=>r.fulfill({json:{ok:true}})) await page.route('**/api/**',r=>{const path=new URL(r.request().url()).pathname;if(r.request().method()!=='GET'){writes++;return r.fulfill({status:403,json:{error:'Unexpected mutation in read-only history fixture'}})} const json=path.endsWith('/revisions')?{revisions:[{id:'own207-revision',serverId:'1',section,source:'apply',fingerprint:'fixture',createdAt:'2026-10-13T00:00:00Z',note:null,itemCount:1}]}: path==='/api/auth/config'?{required:true,portal_url:'http://portal.own.invalid'}: path==='/api/servers'?[{id:1,name:'Own207 router',host:'192.0.2.207',type:'exit-node',enabled,country:'RU',site:'OWN',asn:'',status:'online',latency:1}]: path==='/api/wireguard'?{interfaces:[]}: path==='/api/ipsec'?{servers:[],clients:[]}: path==='/api/gre/tunnels'||path==='/api/filters/gre-tunnels'?{tunnels:[]}: path==='/api/firewall/all'?{rules:[],addressLists:[]}: path==='/api/filters/rules'?{rulesets:[{serverId:'1',rules:[]}],greTunnels:[],live:true}: path==='/api/recursive-routes/catalog'?{serverId:1,routes:[],live:true,stale:false}: path==='/api/recursive-routes/gateways/catalog'?{serverId:1,gateways:[]}: path==='/api/evobgp/settings'?{enabled:false,baseUrl:'',secretConfigured:false}:{} return r.fulfill({json})}) await page.goto('/'+section+'?server=1');return ()=>writes } for(const width of [1440,320])for(const section of sections)test(`История207 reader не может откатить ${section} ${width}`,async({page})=>{ await page.setViewportSize({width,height:1000});const writes=await setup(page,section,false) const history=page.getByRole('button',{name:'История',exact:true});await expect(history).toBeEnabled();await history.click();const sheet=page.getByRole('dialog');const restore=sheet.getByRole('button',{name:'Откатить',exact:true});await expect(restore).toBeDisabled();await expect(sheet.getByRole('status')).toContainText('Откат недоступен');await restore.evaluate(b=>(b as HTMLButtonElement).click());expect(writes()).toBe(0);expect(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth+1)).toBe(true) }) for(const section of sections){test(`История207 writer сохраняет подтверждение ${section}`,async({page})=>{ const writes=await setup(page,section,true);const history=page.getByRole('button',{name:'История',exact:true});await expect(history).toBeEnabled();await history.click();const restore=page.getByRole('dialog').getByRole('button',{name:'Откатить',exact:true});await expect(restore).toBeEnabled();await restore.click();await expect(page.getByRole('alertdialog')).toBeVisible();await page.getByRole('alertdialog').getByRole('button',{name:'Отмена',exact:true}).click();expect(writes()).toBe(0) });test(`История207 выключенный сервер блокирует откат ${section}`,async({page})=>{ const writes=await setup(page,section,true,false);const history=page.getByRole('button',{name:'История',exact:true});await expect(history).toBeVisible();if(await history.isEnabled()){await history.click();await expect(page.getByRole('dialog').getByRole('button',{name:'Откатить',exact:true})).toBeDisabled()}expect(writes()).toBe(0) })}