51 lines
10 KiB
TypeScript
51 lines
10 KiB
TypeScript
import {test,expect,type Page,type Route} from "@playwright/test"
|
|
import {emptyAlerts} from "./fixtures/alerts"
|
|
const telegram={chatId:"-100fixture",tokenConfigured:true,connected:true,messageThreadId:9}
|
|
async function setup(page:Page,handler:(route:Route,path:string)=>Promise<unknown>,required=false){
|
|
await page.route("**/health",r=>r.fulfill({json:{status:"ok"}}))
|
|
await page.route("**/api/**",r=>{const path=new URL(r.request().url()).pathname;if(path==="/api/auth/config")return r.fulfill({json:{required}});if(path==="/api/alerts"||path.startsWith("/api/alerts/telegram"))return handler(r,path);return r.fulfill({json:path==="/api/filters/gre-tunnels"?{tunnels:[],failures:[]}:[]})})
|
|
await page.goto("/alerts");await expect(page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true})).toHaveValue(telegram.chatId)
|
|
}
|
|
const form=(page:Page)=>page.getByRole("region",{name:"Параметры Telegram",exact:true})
|
|
test("Telegram: dirty fields survive refresh and selective save leaves refreshed fields untouched",async({page})=>{
|
|
let current={...telegram},payload:unknown,calls=0
|
|
await setup(page,(r,path)=>{if(path==="/api/alerts")return r.fulfill({json:{...emptyAlerts,telegram:current}});calls++;payload=r.request().postDataJSON();current={...current,...payload as object};return r.fulfill({json:{ok:true,telegram:current}})})
|
|
await page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true}).fill("-100draft");current={...current,chatId:"-100external",messageThreadId:17};await page.getByRole("button",{name:"Обновить",exact:true}).click();await expect(page.getByRole("textbox",{name:"ID темы (message_thread_id)",exact:true})).toHaveValue("17");await expect(page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true})).toHaveValue("-100draft")
|
|
await form(page).getByRole("button",{name:"Сохранить в БД",exact:true}).click();await expect(form(page).getByText("Параметры Telegram сохранены в БД",{exact:true})).toBeVisible();expect(payload).toEqual({chatId:"-100draft"});expect(calls).toBe(1);await expect(form(page).getByRole("button",{name:"Сохранить в БД",exact:true})).toBeDisabled();await expect(form(page)).not.toContainText("Подключён")
|
|
})
|
|
test("Telegram: invalid thread is preserved and no PUT or POST is sent",async({page})=>{
|
|
let calls=0;await setup(page,(r,path)=>{if(path==="/api/alerts")return r.fulfill({json:{...emptyAlerts,telegram}});calls++;return r.fulfill({json:{ok:true}})})
|
|
const input=page.getByRole("textbox",{name:"ID темы (message_thread_id)",exact:true});for(const value of ["12wrong","1.5","0","2147483648"]){await input.fill(value);await form(page).getByRole("button",{name:"Сохранить в БД",exact:true}).click();await expect(form(page).getByRole("alert")).toContainText("ID темы");await expect(input).toHaveValue(value);await form(page).getByRole("button",{name:"Отправить тестовое сообщение",exact:true}).click();await expect(form(page).getByRole("alert")).toContainText("ID темы")};expect(calls).toBe(0)
|
|
})
|
|
test("Telegram: malformed save remains unknown across refresh and reload without secret storage",async({page})=>{
|
|
let calls=0;await setup(page,(r,path)=>{if(path==="/api/alerts")return r.fulfill({json:{...emptyAlerts,telegram}});calls++;return r.fulfill({json:{ok:false}})})
|
|
await page.locator("#alerts-bot-token").fill("fixture-secret");await form(page).getByRole("button",{name:"Сохранить в БД",exact:true}).click();await expect(form(page).getByRole("alert")).toContainText("не подтверждено");await expect(page.locator("#alerts-bot-token")).toHaveValue("fixture-secret");await expect(form(page).getByRole("button",{name:"Отправить тестовое сообщение",exact:true})).toBeDisabled();await page.getByRole("button",{name:"Обновить",exact:true}).click();await expect(page.locator("#alerts-bot-token")).toHaveValue("fixture-secret");expect(await page.evaluate(()=>JSON.stringify(sessionStorage))).not.toContain("fixture-secret");await page.reload();await expect(form(page)).toContainText("Результат запроса неизвестен");await expect(form(page).getByRole("button",{name:"Отправить тестовое сообщение",exact:true})).toBeDisabled();expect(calls).toBe(1)
|
|
})
|
|
test("Telegram: confirmed rejection preserves draft and allows corrected save",async({page})=>{
|
|
let calls=0;await setup(page,(r,path)=>{if(path==="/api/alerts")return r.fulfill({json:{...emptyAlerts,telegram}});calls++;return r.fulfill({status:403,json:{error:"Fixture denied"}})})
|
|
await page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true}).fill("-100draft");await form(page).getByRole("button",{name:"Сохранить в БД",exact:true}).click();await expect(form(page).getByRole("alert")).toHaveText("Fixture denied");await expect(page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true})).toHaveValue("-100draft");await expect(form(page).getByRole("button",{name:"Сохранить в БД",exact:true})).toBeEnabled();await expect(form(page)).not.toContainText("Результат запроса неизвестен");expect(calls).toBe(1)
|
|
})
|
|
test("Telegram: one pending write excludes save/test and refresh until strict receipt",async({page})=>{
|
|
let calls=0,release!:()=>void;const gate=new Promise<void>(resolve=>release=resolve)
|
|
try{await setup(page,async(r,path)=>{if(path==="/api/alerts")return r.fulfill({json:{...emptyAlerts,telegram}});calls++;await gate;return r.fulfill({json:{ok:true,telegram:{...telegram,chatId:"-100draft"}}})});await page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true}).fill("-100draft");await form(page).getByRole("button",{name:"Сохранить в БД",exact:true}).click();await expect.poll(()=>calls).toBe(1);await expect(form(page).getByRole("button",{name:"Отправить тестовое сообщение",exact:true})).toBeDisabled();await expect(page.getByRole("button",{name:"Обновить",exact:true})).toBeDisabled();await expect(page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true})).toBeDisabled();release();await expect(form(page).getByText("Параметры Telegram сохранены в БД",{exact:true})).toBeVisible();expect(calls).toBe(1)}finally{release()}
|
|
})
|
|
test("Telegram: strict test receipt acknowledges API acceptance without claiming delivery",async({page})=>{
|
|
let calls=0,payload:unknown;await setup(page,(r,path)=>{if(path==="/api/alerts")return r.fulfill({json:{...emptyAlerts,telegram}});calls++;payload=r.request().postDataJSON();return r.fulfill({json:{ok:true}})})
|
|
await form(page).getByRole("button",{name:"Отправить тестовое сообщение",exact:true}).click();await expect(form(page)).toContainText("Telegram API подтвердил приём тестового сообщения. Проверьте его в чате.");expect(payload).toEqual({chatId:telegram.chatId,messageThreadId:9});expect(calls).toBe(1);await expect(form(page)).not.toContainText("Сообщение отправлено")
|
|
})
|
|
test("Telegram: alerts read does not grant config save or test",async({page})=>{
|
|
const token="header."+Buffer.from(JSON.stringify({sub:"fixture",apps:["mm"],permissions:["mm:alerts:read"],exp:Math.floor(Date.now()/1000)+3600})).toString("base64url")+".fixture";await page.addInitScript(value=>localStorage.setItem("mmapp_token",value),token)
|
|
let calls=0;await setup(page,(r,path)=>{if(path==="/api/alerts")return r.fulfill({json:{...emptyAlerts,telegram}});calls++;return r.fulfill({json:{ok:true}})},true);await expect(form(page)).toContainText("Нет права изменять оповещения");await expect(page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true})).toBeDisabled();await expect(form(page).getByRole("button",{name:"Отправить тестовое сообщение",exact:true})).toBeDisabled();expect(calls).toBe(0)
|
|
})
|
|
|
|
test("Telegram: compact form remains within 320px viewport",async({page})=>{await page.setViewportSize({width:320,height:900});await setup(page,r=>r.fulfill({json:{...emptyAlerts,telegram}}));await form(page).scrollIntoViewIfNeeded();const box=await form(page).boundingBox();expect(box).not.toBeNull();expect(box!.x).toBeGreaterThanOrEqual(0);expect(box!.x+box!.width).toBeLessThanOrEqual(320);await page.screenshot({path:"test-results/alerts-telegram-320.png",fullPage:true})})
|
|
|
|
test("Telegram: clearing thread is explicit and test cannot silently use the saved topic",async({page})=>{
|
|
let payload:unknown,calls=0;await setup(page,(r,path)=>{if(path==="/api/alerts")return r.fulfill({json:{...emptyAlerts,telegram}});calls++;payload=r.request().postDataJSON();return r.fulfill({json:{ok:true,telegram:{...telegram,messageThreadId:null}}})})
|
|
await page.getByRole("textbox",{name:"ID темы (message_thread_id)",exact:true}).fill("");await form(page).getByRole("button",{name:"Отправить тестовое сообщение",exact:true}).click();await expect(form(page).getByRole("alert")).toContainText("Сначала сохраните очистку ID темы");expect(calls).toBe(0);await form(page).getByRole("button",{name:"Сохранить в БД",exact:true}).click();await expect(form(page)).toContainText("Параметры Telegram сохранены в БД");expect(payload).toEqual({messageThreadId:null});expect(calls).toBe(1)
|
|
})
|
|
test("Telegram: late overview GET cannot replace a confirmed save",async({page})=>{
|
|
let delayed=false,started=false,completed=false,release!:()=>void;const gate=new Promise<void>(resolve=>release=resolve)
|
|
try{await setup(page,async(r,path)=>{if(path==="/api/alerts"){if(delayed){started=true;await gate;try{await r.fulfill({json:{...emptyAlerts,telegram:{...telegram,chatId:"OLD CHAT"}}})}catch{}finally{completed=true};return}return r.fulfill({json:{...emptyAlerts,telegram}})}return r.fulfill({json:{ok:true,telegram:{...telegram,chatId:"-100draft"}}})});await page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true}).fill("-100draft");delayed=true;await page.getByRole("button",{name:"Обновить",exact:true}).click();await expect.poll(()=>started).toBe(true);await form(page).getByRole("button",{name:"Сохранить в БД",exact:true}).click();await expect(form(page)).toContainText("Параметры Telegram сохранены в БД");release();await expect.poll(()=>completed).toBe(true);await expect(page.getByRole("textbox",{name:"Chat ID по умолчанию",exact:true})).toHaveValue("-100draft")}finally{release()}
|
|
})
|