67 lines
9.3 KiB
TypeScript
67 lines
9.3 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()
|
|
})
|
|
|
|
for(const q of ["server=1&server=2","server=1&host=192.0.2.1","server=bad%20id"])test("Терминал: неверный URL не читает каталог "+q,async({page})=>{
|
|
let reads=0;await page.route("**/api/servers",r=>{reads++;return r.fulfill({json:devices})})
|
|
await page.goto("/terminal?"+q);await expect(page.getByRole("alert").filter({hasText:"Некорректная ссылка"})).toBeVisible();expect(reads).toBe(0)
|
|
await page.getByRole("button",{name:"Сбросить контекст терминала"}).click();await expect(page.getByRole("option",{name:/Первый/})).toBeVisible();expect(reads).toBe(1)
|
|
})
|
|
test("Терминал: disabled и известный пустой каталог",async({page})=>{
|
|
await page.route("**/api/servers",r=>r.fulfill({json:[{...devices[0],enabled:false}]}));await page.goto("/terminal?server=1")
|
|
await expect(command(page)).toBeDisabled();await expect(page.getByRole("status").filter({hasText:"Устройство отключено"})).toBeVisible()
|
|
await page.route("**/api/servers",r=>r.fulfill({json:[]}));await page.goto("/terminal");await expect(page.getByText("Устройств пока нет",{exact:true})).toBeVisible();await expect(command(page)).toHaveCount(0)
|
|
})
|
|
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.clock.install();await page.goto("/terminal?server=1");await command(page).fill("interface print");await command(page).press("Enter");await expect.poll(()=>calls).toBe(1)
|
|
await page.clock.fastForward(60010);await expect(page.getByRole("alert").filter({hasText:"Время ожидания истекло"})).toBeVisible();await expect(command(page)).toHaveValue("interface print");await expect(command(page)).toBeEnabled();expect(calls).toBe(1)
|
|
release();await expect(page.getByRole("region",{name:"Вывод терминала"})).not.toContainText("запоздалый ответ")
|
|
})
|