Files
Neon-Desk/e2e/watchlist.spec.ts

87 lines
3.3 KiB
TypeScript

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);
});