import { Page, expect } from '@playwright/test'; /** * Tier 3 (Gitea #220): regression tests against a throwaway Synapse the job * starts itself (`scripts/dev-homeserver.sh start`). No prod secrets. Set * E2E_LOCAL_HS (default http://localhost:8008) — the spec skips when the * server isn't reachable. */ export const HS = process.env.E2E_LOCAL_HS || 'http://localhost:8008'; export const PASSWORD = 'password123'; export const enc = encodeURIComponent; export async function hsReachable(): Promise { try { const res = await fetch(`${HS}/_matrix/client/versions`); return res.ok; } catch { return false; } } export async function api>( method: string, path: string, token?: string, body?: unknown, ): Promise { const res = await fetch(`${HS}${path}`, { method, headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: `Bearer ${token}` } : {}), }, body: body === undefined ? undefined : JSON.stringify(body), }); return (await res.json()) as T; } export type TestUser = { localpart: string; userId: string; token: string }; /** Registers (open registration) or logs in a test user. */ export async function ensureUser(localpart: string): Promise { const reg = await api<{ user_id?: string; access_token?: string }>( 'POST', '/_matrix/client/v3/register', undefined, { username: localpart, password: PASSWORD, auth: { type: 'm.login.dummy' } }, ); if (reg.access_token && reg.user_id) { return { localpart, userId: reg.user_id, token: reg.access_token }; } const login = await api<{ user_id: string; access_token: string }>( 'POST', '/_matrix/client/v3/login', undefined, { type: 'm.login.password', identifier: { type: 'm.id.user', user: localpart }, password: PASSWORD, }, ); return { localpart, userId: login.user_id, token: login.access_token }; } export async function createRoom( owner: TestUser, name: string, opts: { invite?: string[]; preset?: string } = {}, ): Promise { const res = await api<{ room_id: string }>('POST', '/_matrix/client/v3/createRoom', owner.token, { name, preset: opts.preset ?? 'public_chat', invite: opts.invite, }); return res.room_id; } export async function joinRoom(user: TestUser, roomId: string): Promise { await api('POST', `/_matrix/client/v3/rooms/${enc(roomId)}/join`, user.token, {}); } let txn = 0; export async function sendText( user: TestUser, roomId: string, body: string, extra: Record = {}, ): Promise { txn += 1; const res = await api<{ event_id: string }>( 'PUT', `/_matrix/client/v3/rooms/${enc(roomId)}/send/m.room.message/e2e${Date.now()}_${txn}`, user.token, { msgtype: 'm.text', body, ...extra }, ); return res.event_id; } /** Password login through the UI against the local homeserver. */ export async function loginUI(page: Page, user: TestUser): Promise { await page.goto(`/login/${enc(HS)}/`); await page.getByLabel('Username or email').fill(user.localpart); await page.getByLabel('Password', { exact: true }).fill(PASSWORD); await page.getByRole('button', { name: 'Login' }).click(); await page.waitForURL(/\/home/, { timeout: 60_000 }); } export async function openRoom(page: Page, roomId: string): Promise { await page.goto(`/home/${enc(roomId)}`); await expect(page.locator('[data-slate-editor]').first()).toBeVisible({ timeout: 30_000 }); } export const uniq = (prefix: string): string => `${prefix}${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`;