import { expect, test, type Page, type TestInfo } from '@playwright/test'; test.describe.configure({ mode: 'serial' }); const PASSWORD = 'Sup3rSecure!123'; function toSlug(value: string) { return value .toLowerCase() .replace(/[^a-z0-9]+/g, '-') .replace(/^-+|-+$/g, '') .slice(0, 48); } async function signUp(page: Page, testInfo: TestInfo) { const email = `playwright-analysis-${testInfo.workerIndex}-${toSlug(testInfo.title)}-${Date.now()}@example.com`; await page.goto('/auth/signup'); await page.locator('input[autocomplete="name"]').fill('Playwright Analysis 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 }); } function buildMockAnalysisPayload(overrides: { ticker?: string; companyName?: string; quote?: { value: number | null; stale: boolean }; priceHistory?: { value: Array<{ date: string; close: number }> | null; stale: boolean }; benchmarkHistory?: { value: Array<{ date: string; close: number }> | null; stale: boolean }; } = {}) { return { company: { ticker: overrides.ticker ?? 'MSFT', companyName: overrides.companyName ?? 'Microsoft Corporation', sector: 'Technology', category: null, tags: [], cik: '0000789019' }, quote: overrides.quote ?? { value: 425.12, stale: false }, position: null, priceHistory: overrides.priceHistory ?? { value: [ { date: '2025-01-01T00:00:00.000Z', close: 380 }, { date: '2026-01-01T00:00:00.000Z', close: 425.12 } ], stale: false }, benchmarkHistory: overrides.benchmarkHistory ?? { value: [ { date: '2025-01-01T00:00:00.000Z', close: 5000 }, { date: '2026-01-01T00:00:00.000Z', close: 5400 } ], stale: false }, financials: [], filings: [], aiReports: [], coverage: null, journalPreview: [], recentAiReports: [], latestFilingSummary: null, keyMetrics: { referenceDate: null, revenue: null, netIncome: null, totalAssets: null, cash: null, debt: null, netMargin: null }, companyProfile: { description: 'Microsoft builds cloud and software products worldwide.', exchange: 'NASDAQ', industry: 'Software', country: 'United States', website: 'https://www.microsoft.com', fiscalYearEnd: '06/30', employeeCount: 220000, source: 'sec_derived' }, valuationSnapshot: { sharesOutstanding: 7430000000, marketCap: 3150000000000, enterpriseValue: 3200000000000, trailingPe: 35, evToRevenue: 12, evToEbitda: null, source: 'derived' }, bullBear: { source: 'memo_fallback', bull: ['Azure and Copilot demand remain durable.'], bear: ['Valuation leaves less room for execution misses.'], updatedAt: '2026-03-13T00:00:00.000Z' }, recentDevelopments: { status: 'ready', items: [{ id: 'msft-1', kind: '8-K', title: 'Microsoft filed an 8-K', url: 'https://www.sec.gov/Archives/test.htm', source: 'SEC filings', publishedAt: '2026-03-10', summary: 'The company disclosed a current report with updated commercial details.', accessionNumber: '0000000000-26-000001' }], weeklySnapshot: { summary: 'The week centered on filing-driven updates.', highlights: ['An 8-K added current commercial context.'], itemCount: 1, startDate: '2026-03-07', endDate: '2026-03-13', updatedAt: '2026-03-13T00:00:00.000Z', source: 'heuristic' } } }; } test('shows the overview skeleton while analysis is loading', async ({ page }, testInfo) => { await signUp(page, testInfo); await page.route('**/api/analysis/company**', async (route) => { await new Promise((resolve) => setTimeout(resolve, 700)); await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ analysis: buildMockAnalysisPayload() }) }); }); await page.goto('/analysis?ticker=MSFT'); await expect(page.getByTestId('analysis-overview-skeleton')).toBeVisible(); await expect(page.getByRole('heading', { name: 'Microsoft Corporation' })).toBeVisible(); await expect(page.getByText('Bull vs Bear')).toBeVisible(); }); test('shows price chart with live data when quote and history are available', async ({ page }, testInfo) => { await signUp(page, testInfo); await page.route('**/api/analysis/company**', async (route) => { await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ analysis: buildMockAnalysisPayload() }) }); }); await page.goto('/analysis?ticker=MSFT'); await expect(page.getByRole('heading', { name: 'Microsoft Corporation' })).toBeVisible({ timeout: 30_000 }); await expect(page.getByText('Spot price $425.12')).toBeVisible(); await expect(page.locator('[data-testid="interactive-price-chart"]')).toBeVisible(); }); test('shows unavailable message when price data is null', async ({ page }, testInfo) => { await signUp(page, testInfo); await page.route('**/api/analysis/company**', async (route) => { await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ analysis: buildMockAnalysisPayload({ quote: { value: null, stale: false }, priceHistory: { value: null, stale: false }, benchmarkHistory: { value: null, stale: false } }) }) }); }); await page.goto('/analysis?ticker=FAIL'); await expect(page.getByRole('heading', { name: 'Microsoft Corporation' })).toBeVisible({ timeout: 30_000 }); await expect(page.getByText('Spot price unavailable')).toBeVisible(); await expect(page.getByText('Price data unavailable')).toBeVisible(); await expect(page.locator('[data-testid="interactive-price-chart"]')).not.toBeVisible(); }); test('shows stale indicator when quote data is stale', async ({ page }, testInfo) => { await signUp(page, testInfo); await page.route('**/api/analysis/company**', async (route) => { await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ analysis: buildMockAnalysisPayload({ quote: { value: 425.12, stale: true }, priceHistory: { value: [ { date: '2025-01-01T00:00:00.000Z', close: 380 }, { date: '2026-01-01T00:00:00.000Z', close: 425.12 } ], stale: true } }) }) }); }); await page.goto('/analysis?ticker=STALE'); await expect(page.getByRole('heading', { name: 'Microsoft Corporation' })).toBeVisible({ timeout: 30_000 }); await expect(page.getByText(/Spot price.*\(stale\)/)).toBeVisible(); await expect(page.locator('[data-testid="interactive-price-chart"]')).toBeVisible(); }); test('shows chart when price history is available but benchmark is null', async ({ page }, testInfo) => { await signUp(page, testInfo); await page.route('**/api/analysis/company**', async (route) => { await route.fulfill({ contentType: 'application/json', body: JSON.stringify({ analysis: buildMockAnalysisPayload({ benchmarkHistory: { value: null, stale: false } }) }) }); }); await page.goto('/analysis?ticker=MSFT'); await expect(page.getByRole('heading', { name: 'Microsoft Corporation' })).toBeVisible({ timeout: 30_000 }); await expect(page.locator('[data-testid="interactive-price-chart"]')).toBeVisible(); });