import { test as base, expect, type Page, type BrowserContext, } from '@playwright/test'; /** * Shared fixtures and helpers. * * Works against either the mock plugin (VITE_USE_MOCK_API=true) or the real * Django backend (stratoflights). Mock accepts any credentials; Django * expects demo/demo and enforces CSRF. * * The `TEST_USERNAME` / `TEST_PASSWORD` envs let CI override the default. * * Critical detail: login/logout must go through `page.context().request` so * cookies are shared between API calls and the page. Using the plain * `request` fixture gives you a separate cookie jar. */ const USERNAME = process.env.TEST_USERNAME ?? 'demo'; const PASSWORD = process.env.TEST_PASSWORD ?? 'demo'; export async function login(context: BrowserContext): Promise { const request = context.request; await request.get('/api/csrf/'); const state = await context.storageState(); const csrf = state.cookies.find((c) => c.name === 'csrftoken')?.value ?? ''; const res = await request.post('/api/login/', { data: { username: USERNAME, password: PASSWORD }, headers: { 'X-CSRFToken': csrf, 'Content-Type': 'application/json' }, }); if (!res.ok()) { throw new Error( `Login failed: ${res.status()} ${await res.text()}. Is stratoflights running on :8000 with a user "${USERNAME}" / "${PASSWORD}"?`, ); } } export async function logout(context: BrowserContext): Promise { const state = await context.storageState(); const csrf = state.cookies.find((c) => c.name === 'csrftoken')?.value ?? ''; await context.request.post('/api/logout/', { headers: { 'X-CSRFToken': csrf, 'Content-Type': 'application/json' }, }); // Clear cookies so the page navigator is fully anonymous next goto. await context.clearCookies(); } export const test = base.extend({ page: async ({ page }, use) => { const hardErrors: string[] = []; page.on('pageerror', (e) => hardErrors.push(`[pageerror] ${e.message}`)); page.on('console', (msg) => { if (msg.type() === 'error') { // eslint-disable-next-line no-console console.log(' [page console.error]', msg.text()); } }); await use(page); if (hardErrors.length > 0) throw new Error('Page errors:\n' + hardErrors.join('\n')); }, }); export { expect }; export async function openPredict(page: Page) { await page.goto('/predict'); await page .locator('.map-container canvas') .first() .waitFor({ state: 'attached', timeout: 15_000 }); }