/** * Sprint 16: UI helpers для regression-flow тестов. * * `attachSession(page, sess)` — устанавливает access_token в localStorage * и идёт на нужный путь. Без UI-логина (быстрее). * * `watchPage(page)` — слушатель console-error + network-4xx/5xx с * фильтром на ожидаемое (см. `expectNoErrors`). */ import { type Page, type ConsoleMessage } from '@playwright/test' import type { OrgSession } from '../factories/types.js' export async function attachSession(page: Page, sess: OrgSession, gotoPath = '/dashboard'): Promise { // Открываем /login — там SPA уже сделал init и слушает localStorage. await page.goto('/login') await page.evaluate((tok) => localStorage.setItem('fm.access_token', tok), sess.accessToken) await page.goto(gotoPath, { waitUntil: 'domcontentloaded' }) } export interface CollectedErrors { console: string[] network: string[] } export function watchPage(page: Page, opts?: { expectedConsoleContains?: string[] expected4xxContains?: string[] }): CollectedErrors { const acc: CollectedErrors = { console: [], network: [] } page.on('console', (msg: ConsoleMessage) => { if (msg.type() !== 'error') return const t = msg.text() if (/^Failed to load resource: the server responded with a status of \d+/i.test(t)) return if (/net::ERR_(NETWORK_CHANGED|INTERNET_DISCONNECTED|CONNECTION_RESET|NAME_NOT_RESOLVED|CONNECTION_REFUSED|TIMED_OUT|ABORTED)/i.test(t)) return if ((opts?.expectedConsoleContains ?? []).some(s => t.includes(s))) return acc.console.push(t) }) page.on('response', (resp) => { const status = resp.status() if (status < 400) return const url = resp.url() if (status === 401 && /\/(api|connect)\//.test(url)) return if ((opts?.expected4xxContains ?? []).some(s => url.includes(s))) return acc.network.push(`${status} ${resp.request().method()} ${url}`) }) return acc } export function expectNoErrors(acc: CollectedErrors, where: string): void { if (acc.console.length || acc.network.length) { const msg = [ `Errors on ${where}:`, ...acc.console.map(c => ` CONSOLE: ${c}`), ...acc.network.map(n => ` NET: ${n}`), ].join('\n') throw new Error(msg) } }