Files
MikrotikManager/tests/e2e/terminal-data.spec.ts
T

49 lines
7.1 KiB
TypeScript

import {test,expect,type Page} from "@playwright/test"
const devices=[{id:1,name:"Первый",host:"192.0.2.1",country:"RU",enabled:true,status:"online",os:null},{id:2,name:"Второй",host:"192.0.2.2",country:"RU",enabled:true,status:null,os:"7.20"}]
async function routes(page:Page){await page.route("**/health",r=>r.fulfill({json:{ok:true}}));await page.route("**/api/**",r=>r.fulfill({json:new URL(r.request().url()).pathname==="/api/servers"?devices:new URL(r.request().url()).pathname==="/api/auth/config"?{required:false}:{}}))}
test.beforeEach(async({page})=>routes(page))
const command=(page:Page)=>page.getByRole("textbox",{name:/^Команда ·/})
for(const malformed of [false,true])test("Терминал: первоначальный отказ каталога и повтор "+malformed,async({page})=>{
let fail=true;await page.route("**/api/servers",r=>r.fulfill(fail?(malformed?{json:{}}:{status:403,json:{error:"Каталог запрещён"}}):{json:devices}))
await page.goto("/terminal?server=1");await expect(page.getByRole("alert").filter({hasText:"Устройства пока неизвестны"})).toBeVisible();await expect(command(page)).toHaveCount(0)
fail=false;await page.getByRole("button",{name:"Обновить устройства",exact:true}).click();await expect(command(page)).toBeEnabled();await expect(page.getByRole("region",{name:"Вывод терминала"})).not.toContainText("Connected")
})
for(const q of ["server=99","host=missing.example"])test("Терминал: неизвестный URL не выбирает другой сервер "+q,async({page})=>{await page.goto("/terminal?"+q);await expect(page.getByRole("alert").filter({hasText:"Устройство терминала недоступно"})).toBeVisible();await expect(command(page)).toHaveCount(0)})
test("Терминал: host из карты, точная история server и reload",async({page})=>{
await page.goto("/terminal?host=192.0.2.2&ref=map#return");await expect(command(page)).toHaveAccessibleName("Команда · Второй")
await page.getByRole("option",{name:/Первый/}).click();await expect(page).toHaveURL(/ref=map&server=1#return$/);await expect(command(page)).toHaveAccessibleName("Команда · Первый")
await page.reload();await expect(command(page)).toHaveAccessibleName("Команда · Первый");await page.goBack();await expect(command(page)).toHaveAccessibleName("Команда · Второй")
})
for(const payload of [{status:403,json:{error:"Команда запрещена"}},{json:{}},{json:{output:"error: BFD недоступен"}}])test("Терминал: отказ сохраняет команду "+JSON.stringify(payload),async({page})=>{
let fail=true;await page.route("**/api/servers/1/exec",r=>r.fulfill(fail?payload:{json:{output:"получен реальный ответ"}}))
await page.goto("/terminal?server=1");await command(page).fill("routing bfd session print");await page.getByRole("button",{name:"Выполнить",exact:true}).click()
await expect(page.getByRole("alert").filter({hasText:"Команда сохранена"})).toBeVisible();await expect(command(page)).toHaveValue("routing bfd session print");await expect(command(page)).toBeEnabled()
fail=false;await page.getByRole("button",{name:"Выполнить",exact:true}).click();await expect(page.getByRole("region",{name:"Вывод терминала"})).toContainText("получен реальный ответ");await expect(command(page)).toHaveValue("");await expect(command(page)).toBeFocused()
})
test("Терминал: один запрос, поздний ответ прежнего устройства не смешивается",async({page})=>{
let calls=0,release!:()=>void;const gate=new Promise<void>(resolve=>release=resolve)
await page.route("**/api/servers/1/exec",async r=>{calls++;await gate;await r.fulfill({json:{output:"прежний секретный вывод"}}).catch(()=>{})})
await page.goto("/terminal?server=1");await command(page).fill("interface print");await command(page).press("Enter");await page.keyboard.press("Enter")
await expect.poll(()=>calls).toBe(1);await expect(page.getByRole("button",{name:"Ожидание…"})).toBeDisabled()
await page.getByRole("option",{name:/Второй/}).click();await expect(command(page)).toHaveAccessibleName("Команда · Второй");release()
await expect(page.getByRole("region",{name:"Вывод терминала"})).not.toContainText("прежний секретный вывод");await expect(command(page)).toBeEnabled()
})
test("Терминал: фоновый отказ сохраняет черновик и блокирует выполнение",async({page})=>{
let fail=false;await page.route("**/api/servers",r=>r.fulfill(fail?{status:503,json:{error:"Каталог недоступен"}}:{json:devices}))
await page.goto("/terminal?server=1");await command(page).fill("ip route print");fail=true;await page.getByRole("button",{name:"Обновить устройства",exact:true}).click()
await expect(page.getByRole("alert").filter({hasText:"Каталог не обновлён"})).toBeVisible();await expect(command(page)).toHaveValue("ip route print");await expect(command(page)).toBeDisabled()
fail=false;await page.getByRole("button",{name:"Обновить устройства",exact:true}).click();await expect(command(page)).toBeEnabled();await expect(command(page)).toHaveValue("ip route print")
})
test("Терминал: быстрый шаблон подставляет, история работает без повторной отправки",async({page})=>{
let calls=0;await page.route("**/api/servers/1/exec",r=>{calls++;return r.fulfill({json:{output:""}})})
await page.goto("/terminal?server=1");await page.getByRole("combobox",{name:"Быстрая команда"}).selectOption("interface print");await expect(command(page)).toHaveValue("interface print");expect(calls).toBe(0)
await command(page).press("Enter");await expect(command(page)).toHaveValue("");await expect(page.getByRole("region",{name:"Вывод терминала"})).toContainText("Пустой вывод.")
await command(page).press("ArrowUp");await expect(command(page)).toHaveValue("interface print");await command(page).press("ArrowDown");await expect(command(page)).toHaveValue("");expect(calls).toBe(1)
await command(page).fill("clear");await command(page).press("Enter");await expect(page.getByRole("region",{name:"Вывод терминала"})).toContainText("Вывод очищен.");expect(calls).toBe(1)
})
test("Терминал: reader видит устройство, выполнение запрещено",async({page})=>{
await page.addInitScript(()=>localStorage.setItem("mmapp_token","header."+btoa(JSON.stringify({sub:"test",apps:["mm"],permissions:["mm:servers:read"],exp:Math.floor(Date.now()/1000)+3600}))+".signature"))
await page.route("**/api/auth/config",r=>r.fulfill({json:{required:true}}))
await page.goto("/terminal?server=1");await expect(command(page)).toBeDisabled();await expect(page.getByRole("status").filter({hasText:"Для выполнения требуется mm:servers:write"})).toBeVisible()
})