import { expect, test, type Page } from '@playwright/test'; const PASSWORD = 'Sup3rSecure!123'; test.describe.configure({ mode: 'serial' }); function uniqueEmail(prefix: string) { return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}@example.com`; } async function gotoAuthPage(page: Page, path: string) { await page.goto(path, { waitUntil: 'domcontentloaded' }); await page.waitForLoadState('networkidle'); } async function signUp(page: Page, email: string) { await gotoAuthPage(page, '/auth/signup'); await page.locator('input[autocomplete="name"]').fill('Playwright Watchlist User'); await page.locator('input[autocomplete="email"]').fill(email); await page.locator('input[autocomplete="new-password"]').first().fill(PASSWORD); await page.locator('input[autocomplete="new-password"]').nth(1).fill(PASSWORD); await page.getByRole('button', { name: 'Create account' }).click(); } async function expectStableDashboard(page: Page) { await expect(page.getByRole('heading', { name: 'Command Center' })).toBeVisible({ timeout: 30_000 }); await expect(page).toHaveURL(/\/$/, { timeout: 30_000 }); } async function countSyncTasks(page: Page, ticker: string) { return await page.evaluate(async (requestedTicker) => { const response = await fetch('/api/tasks?limit=20', { credentials: 'include', cache: 'no-store' }); if (!response.ok) { throw new Error(`Unable to load tasks: ${response.status}`); } const payload = await response.json() as { tasks?: Array<{ task_type?: string; payload?: { ticker?: string; }; }>; }; return (payload.tasks ?? []).filter((task) => ( task.task_type === 'sync_filings' && task.payload?.ticker === requestedTicker )).length; }, ticker); } test('coverage save stays metadata-only until sync filings is clicked', async ({ page }) => { await signUp(page, uniqueEmail('playwright-watchlist-sync')); await expectStableDashboard(page); await page.goto('/watchlist', { waitUntil: 'domcontentloaded' }); await expect(page.getByRole('heading', { name: 'Coverage', exact: true })).toBeVisible({ timeout: 30_000 }); await page.getByLabel('Coverage ticker').fill('NVDA'); await page.getByLabel('Coverage company name').fill('NVIDIA Corporation'); await page.getByLabel('Coverage sector').fill('Technology'); await page.getByLabel('Coverage category').fill('core'); await page.getByLabel('Coverage tags').fill('semis, ai'); await page.getByRole('button', { name: 'Save coverage' }).click(); const coverageRow = page.locator('tr').filter({ hasText: 'NVDA' }); await expect(coverageRow).toContainText('NVIDIA Corporation'); const notice = page.getByTestId('watchlist-post-create-notice'); await expect(notice).toContainText('NVDA added to coverage. Filing sync has not started yet.'); await expect(notice.getByRole('button', { name: 'Sync filings' })).toBeVisible(); await expect(coverageRow.getByRole('button', { name: 'Sync filings' })).toBeVisible(); await page.waitForTimeout(1_000); expect(await countSyncTasks(page, 'NVDA')).toBe(0); await notice.getByRole('button', { name: 'Sync filings' }).click(); await expect(notice).toContainText('NVDA added to coverage. Filing sync is queued.'); await expect.poll(async () => await countSyncTasks(page, 'NVDA')).toBe(1); });