Files
MikrotikManager/tests/e2e/accessibility-shell.spec.ts
T

34 lines
5.9 KiB
TypeScript

import {test,expect,type Page} from "@playwright/test"
import axe from "axe-core"
import {alertsWithRule,emptyOutbox} from "./fixtures/alerts"
import {evoSettings,evoCatalog} from "./fixtures/evobgp"
async function setup(page:Page,theme:string){
await page.addInitScript(value=>localStorage.setItem("rl-theme",value),theme)
await page.route("**/health",r=>r.fulfill({json:{status:"ok"}}))
await page.route("**/api/**",r=>{const path=new URL(r.request().url()).pathname;const body=path==="/api/auth/config"?{required:false}:path==="/api/alerts"?alertsWithRule:path==="/api/alerts/outbox"?emptyOutbox:path==="/api/evobgp/settings"?evoSettings:path==="/api/evobgp/catalog"?evoCatalog:path==="/api/sidebar-counts"?{servers:0,filterRules:0,uptimeProbes:0,uptimeSpeedProbes:0,monitoringItems:0,recursiveRoutes:0}:path==="/api/filters/gre-tunnels"?{tunnels:[],failures:[]}:path==="/api/servers"||path==="/api/bgp/sessions"?[]:null;return body!==null?r.fulfill({json:body}):r.fulfill({status:503,json:{error:"Fixture unavailable"}})})
}
async function settleAnimations(page:Page){await page.evaluate(async()=>{await Promise.allSettled(document.getAnimations().filter(a=>a.effect?.getComputedTiming().iterations!==Infinity).map(a=>a.finished))})}
for(const theme of ["dark","light"])for(const width of [320,1440])for(const path of ["/alerts","/settings","/releases"])test("Automated WCAG tagged audit/"+theme+"/"+width+path,async({page},info)=>{
await setup(page,theme);await page.setViewportSize({width,height:900});await page.goto(path)
if(path==="/alerts")await expect(page.getByRole("textbox",{name:"Поиск правил",exact:true})).toBeVisible()
else if(path==="/settings"){await expect(page.getByRole("button",{name:"Тёмная",exact:true})).toBeEnabled();await expect(page.getByRole("button",{name:"Скачать",exact:true})).toBeEnabled()}
else await expect(page.getByText("Текущая версия",{exact:true})).toBeVisible()
await settleAnimations(page);await page.addScriptTag({content:axe.source})
const result=await page.evaluate(async()=>{const engine=(window as unknown as {axe:typeof axe}).axe;return engine.run({include:["#main-content",'[aria-label="Основная навигация"]']},{runOnly:{type:"tag",values:["wcag2a","wcag2aa","wcag21a","wcag21aa","wcag22aa"]}})})
await info.attach("axe-results",{body:JSON.stringify(result,null,2),contentType:"application/json"})
expect(result.violations.map(v=>({id:v.id,impact:v.impact,nodes:v.nodes.map(n=>({target:n.target,html:n.html,summary:n.failureSummary}))}))).toEqual([])
})
test("Shell keyboard skip link moves focus to main and menus restore focus",async({page})=>{
await setup(page,"dark");await page.goto("/releases");await expect(page.getByText("Текущая версия",{exact:true})).toBeVisible();await page.keyboard.press("Tab");const skip=page.getByRole("link",{name:"К содержимому",exact:true});await expect(skip).toBeFocused();await page.keyboard.press("Enter");await expect(page.locator("#main-content")).toBeFocused()
const apps=page.getByRole("button",{name:"Приложения",exact:true});await apps.focus();await page.keyboard.press("Enter");await expect(page.getByRole("menu")).toBeVisible();await page.keyboard.press("Escape");await expect(apps).toBeFocused()
const monitor=page.getByRole("button",{name:"Монитор системы",exact:true});await monitor.focus();await page.keyboard.press("Enter");await expect(page.locator('[data-slot="popover-content"]')).toBeVisible();await page.keyboard.press("Escape");await expect(monitor).toBeFocused()
})
test("System monitor recognizes confirmed legacy health consistently",async({page})=>{await setup(page,"dark");await page.route("**/health",r=>r.fulfill({json:{ok:true}}));await page.goto("/releases");await expect(page.getByRole("button",{name:"Монитор системы",exact:true})).toContainText("API доступен")})
for(const theme of ["dark","light"])for(const width of [320,1440])test("Open shell layers audit/"+theme+"/"+width,async({page},info)=>{
await setup(page,theme);await page.setViewportSize({width,height:900});await page.goto("/releases");await expect(page.getByRole("button",{name:"Монитор системы",exact:true})).toContainText("API доступен");await page.getByRole("button",{name:"Монитор системы",exact:true}).click();await expect(page.locator('[data-slot="popover-content"]')).toBeVisible();await page.addScriptTag({content:axe.source});
const result=await page.evaluate(async()=>{const engine=(window as unknown as {axe:typeof axe}).axe;return engine.run('[data-slot="popover-content"]',{runOnly:{type:"tag",values:["wcag2a","wcag2aa","wcag21a","wcag21aa","wcag22aa"]}})});await info.attach("axe-open-monitor",{body:JSON.stringify(result,null,2),contentType:"application/json"});expect(result.violations.map(v=>({id:v.id,nodes:v.nodes.map(n=>({target:n.target,html:n.html,summary:n.failureSummary}))}))).toEqual([]);
await page.screenshot({path:"docs/ux-ui/screens/shell-monitor-"+theme+"-"+width+".png",animations:"disabled"});await page.keyboard.press("Escape");await page.getByRole("button",{name:"Приложения",exact:true}).click();await expect(page.getByRole("menu")).toBeVisible();await settleAnimations(page);const menu=await page.evaluate(async()=>{const engine=(window as unknown as {axe:typeof axe}).axe;return engine.run('[role="menu"]',{runOnly:{type:"tag",values:["wcag2a","wcag2aa","wcag21a","wcag21aa","wcag22aa"]}})});expect(menu.violations.map(v=>({id:v.id,nodes:v.nodes.map(n=>({target:n.target,html:n.html,summary:n.failureSummary}))}))).toEqual([]);
})
for(const theme of ["dark","light"])test("Unconfirmed health remains unavailable/"+theme,async({page})=>{await setup(page,theme);await page.route("**/health",r=>r.fulfill({json:{ok:false,status:"unknown"}}));await page.goto("/releases");await expect(page.getByRole("button",{name:"Монитор системы",exact:true})).toContainText("Нет связи")})