import { expect, test, type Page } from '@playwright/test'; const PASSWORD = 'Sup3rSecure!123'; test.describe.configure({ mode: 'serial' }); type FilingFixture = { id: number; ticker: string; filing_type: '10-K' | '10-Q' | '8-K'; filing_date: string; accession_number: string; cik: string; company_name: string; filing_url: string | null; submission_url: string | null; primary_document: string | null; metrics: { revenue: number | null; netIncome: number | null; totalAssets: number | null; cash: number | null; debt: number | null; } | null; analysis: null; created_at: string; updated_at: string; }; function uniqueEmail(prefix: string) { return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}@example.com`; } function createFiling(input: { id: number; ticker: string; accessionNumber: string; filingType: '10-K' | '10-Q' | '8-K'; filingDate: string; companyName: string; revenue?: number | null; }): FilingFixture { return { id: input.id, ticker: input.ticker, filing_type: input.filingType, filing_date: input.filingDate, accession_number: input.accessionNumber, cik: '0001045810', company_name: input.companyName, filing_url: `https://www.sec.gov/Archives/${input.accessionNumber}.htm`, submission_url: `https://www.sec.gov/submissions/${input.accessionNumber}.json`, primary_document: `${input.accessionNumber}.htm`, metrics: input.revenue === undefined ? null : { revenue: input.revenue, netIncome: null, totalAssets: null, cash: null, debt: null }, analysis: null, created_at: '2026-03-14T12:00:00.000Z', updated_at: '2026-03-14T12:00:00.000Z' }; } async function signUp(page: Page, email: string) { await page.goto('/auth/signup'); await page.locator('input[autocomplete="name"]').fill('Playwright Filings 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(); await expect(page.getByRole('heading', { name: 'Command Center' })).toBeVisible({ timeout: 30_000 }); await expect(page).toHaveURL(/\/$/, { timeout: 30_000 }); } async function installFilingsRouteStub( page: Page, options?: { unscopedDelayMs?: number; scopedDelayMs?: number; } ) { const nvdaFilings = [ createFiling({ id: 1, ticker: 'NVDA', accessionNumber: '0001045810-26-000001', filingType: '10-K', filingDate: '2026-03-13', companyName: 'NVIDIA Corporation', revenue: 130_500_000_000 }) ]; const msftFilings = [ createFiling({ id: 2, ticker: 'MSFT', accessionNumber: '0000789019-26-000002', filingType: '10-Q', filingDate: '2026-03-12', companyName: 'Microsoft Corporation', revenue: 71_000_000_000 }) ]; const mixedFilings = [...nvdaFilings, ...msftFilings]; await page.route(/\/api\/filings(\?.*)?$/, async (route) => { const url = new URL(route.request().url()); const ticker = url.searchParams.get('ticker')?.trim().toUpperCase() ?? null; const delay = ticker === 'NVDA' ? options?.scopedDelayMs ?? 0 : options?.unscopedDelayMs ?? 0; if (delay > 0) { await page.waitForTimeout(delay); } const filings = ticker === 'NVDA' ? nvdaFilings : mixedFilings; await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ filings }) }); }); } async function filingsLedger(page: Page) { return page.locator('section').filter({ has: page.getByRole('heading', { name: 'Filing Ledger' }) }).first(); } test('direct URL entry keeps the filings ledger scoped to the URL ticker', async ({ page }) => { await signUp(page, uniqueEmail('playwright-filings-direct')); await installFilingsRouteStub(page); await page.goto('/filings?ticker=NVDA', { waitUntil: 'domcontentloaded' }); const ledger = await filingsLedger(page); await expect(page).toHaveURL(/\/filings\?ticker=NVDA$/); await expect(ledger.getByText('1 records loaded for NVDA. Values shown in Millions (M).')).toBeVisible(); await expect(ledger.getByRole('cell', { name: 'NVIDIA Corporation' })).toBeVisible(); await expect(ledger.getByRole('cell', { name: 'Microsoft Corporation' })).toHaveCount(0); }); test('apply and clear keep the URL and visible filings rows aligned', async ({ page }) => { await signUp(page, uniqueEmail('playwright-filings-apply-clear')); await installFilingsRouteStub(page); await page.goto('/filings', { waitUntil: 'domcontentloaded' }); const ledger = await filingsLedger(page); await expect(page).toHaveURL(/\/filings$/); await expect(ledger.getByText('2 records loaded. Values shown in Millions (M).')).toBeVisible(); await expect(ledger.getByRole('cell', { name: 'NVIDIA Corporation' })).toBeVisible(); await expect(ledger.getByRole('cell', { name: 'Microsoft Corporation' })).toBeVisible(); await page.getByPlaceholder('Ticker filter').fill('nvda'); await page.getByRole('button', { name: 'Apply' }).click(); await expect(page).toHaveURL(/\/filings\?ticker=NVDA$/); await expect(ledger.getByText('1 records loaded for NVDA. Values shown in Millions (M).')).toBeVisible(); await expect(ledger.getByRole('cell', { name: 'NVIDIA Corporation' })).toBeVisible(); await expect(ledger.getByRole('cell', { name: 'Microsoft Corporation' })).toHaveCount(0); await page.getByRole('button', { name: 'Clear' }).click(); await expect(page).toHaveURL(/\/filings$/); await expect(ledger.getByText('2 records loaded. Values shown in Millions (M).')).toBeVisible(); await expect(ledger.getByRole('cell', { name: 'NVIDIA Corporation' })).toBeVisible(); await expect(ledger.getByRole('cell', { name: 'Microsoft Corporation' })).toBeVisible(); }); test('a stale global filings response cannot overwrite a newer scoped ledger', async ({ page }) => { await signUp(page, uniqueEmail('playwright-filings-stale')); await installFilingsRouteStub(page, { unscopedDelayMs: 900, scopedDelayMs: 50 }); await page.goto('/filings', { waitUntil: 'domcontentloaded' }); const ledger = await filingsLedger(page); await page.getByPlaceholder('Ticker filter').fill('NVDA'); await page.getByRole('button', { name: 'Apply' }).click(); await expect(page).toHaveURL(/\/filings\?ticker=NVDA$/); await expect(ledger.getByText('1 records loaded for NVDA. Values shown in Millions (M).')).toBeVisible(); await page.waitForTimeout(1_100); await expect(ledger.getByRole('cell', { name: 'NVIDIA Corporation' })).toBeVisible(); await expect(ledger.getByRole('cell', { name: 'Microsoft Corporation' })).toHaveCount(0); });