// Auth fixture — logs in once via the real UI and saves the browser state so // authenticated specs can reuse it (docs/QA_PLAN.md §4.4). Runs as the `setup` // project; browser projects depend on it. Driving the real form (not a crafted // API call) means CSRF/cookies are handled exactly as a user experiences them. const fs = require('fs'); const path = require('path'); const { test: setup, expect } = require('@playwright/test'); const { E2E_USER, E2E_PASS, STORAGE_STATE } = require('./constants'); setup('authenticate', async ({ page }) => { await page.goto('/login'); await page.locator('#username').fill(E2E_USER); await page.locator('#password').fill(E2E_PASS); await page.getByRole('button', { name: /sign in/i }).click(); // A regular user lands on the Tracker ('/'). Wait until we've left /login. await page.waitForURL((url) => !url.pathname.startsWith('/login'), { timeout: 15_000 }); // Force a fresh authenticated load so the version check runs — the "What's new" // dialog shows on a real session fetch, not the in-SPA post-login transition. // Dismiss it if present ("Got it" calls acknowledgeVersion(), persisted // server-side) so it won't reappear in reused contexts. Best-effort: on a DB // where this user already acknowledged the version, the dialog won't appear. await page.goto('/'); await page .getByRole('button', { name: 'Got it' }) .click({ timeout: 8000 }) .catch(() => {}); // Sanity: the authenticated Tracker shows its primary action. await expect(page.getByRole('button', { name: 'Add Bill' })).toBeVisible(); fs.mkdirSync(path.dirname(STORAGE_STATE), { recursive: true }); await page.context().storageState({ path: STORAGE_STATE }); });