Implement fiscal-style research MVP flows
Some checks failed
PR Checks / typecheck-and-build (push) Has been cancelled

This commit is contained in:
2026-03-07 09:51:18 -05:00
parent f69e5b671b
commit 52136271d3
26 changed files with 2719 additions and 243 deletions

189
e2e/research-mvp.spec.ts Normal file
View File

@@ -0,0 +1,189 @@
import { expect, test, type Page, type TestInfo } from '@playwright/test';
import { execFileSync } from 'node:child_process';
import { join } from 'node:path';
const PASSWORD = 'Sup3rSecure!123';
const E2E_DATABASE_PATH = join(process.cwd(), 'data', 'e2e.sqlite');
test.describe.configure({ mode: 'serial' });
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-research-${testInfo.workerIndex}-${toSlug(testInfo.title)}-${Date.now()}@example.com`;
await page.goto('/auth/signup');
await page.locator('input[autocomplete="name"]').fill('Playwright Research 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).toHaveURL(/\/$/);
return email;
}
function seedFiling(input: {
ticker: string;
companyName: string;
accessionNumber: string;
filingType: '10-K' | '10-Q';
filingDate: string;
summary: string;
}) {
const now = new Date().toISOString();
execFileSync('python3', [
'-c',
`
import json
import sqlite3
import sys
db_path, ticker, filing_type, filing_date, accession, company_name, now, summary = sys.argv[1:]
connection = sqlite3.connect(db_path)
try:
connection.execute(
"""
INSERT INTO filing (
ticker,
filing_type,
filing_date,
accession_number,
cik,
company_name,
filing_url,
submission_url,
primary_document,
metrics,
analysis,
created_at,
updated_at
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""",
(
ticker,
filing_type,
filing_date,
accession,
"0001045810",
company_name,
f"https://www.sec.gov/Archives/{accession}.htm",
f"https://www.sec.gov/submissions/{accession}.json",
f"{accession}.htm",
json.dumps({
"revenue": 61000000000,
"netIncome": 29000000000,
"totalAssets": 98000000000,
"cash": 27000000000,
"debt": 11000000000,
}),
json.dumps({
"provider": "playwright",
"model": "fixture",
"text": summary,
}),
now,
now,
),
)
connection.commit()
finally:
connection.close()
`,
E2E_DATABASE_PATH,
input.ticker,
input.filingType,
input.filingDate,
input.accessionNumber,
input.companyName,
now,
input.summary
]);
}
test('supports the core coverage-to-research workflow', async ({ page }, testInfo) => {
const accessionNumber = `0001045810-26-${String(Date.now()).slice(-6)}`;
await signUp(page, testInfo);
seedFiling({
ticker: 'NVDA',
companyName: 'NVIDIA Corporation',
accessionNumber,
filingType: '10-K',
filingDate: '2026-02-18',
summary: 'AI datacenter demand remained the central upside driver with expanding operating leverage.'
});
await page.goto('/watchlist');
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('AI, semis');
await page.getByRole('button', { name: 'Save coverage' }).click();
await expect(page.getByRole('cell', { name: 'NVIDIA Corporation' })).toBeVisible();
await page.getByLabel('NVDA status').selectOption('active');
await expect(page.getByLabel('NVDA status')).toHaveValue('active');
await page.getByLabel('NVDA priority').selectOption('high');
await expect(page.getByLabel('NVDA priority')).toHaveValue('high');
await page.getByRole('link', { name: /^Analyze/ }).first().click();
await expect(page).toHaveURL(/\/analysis\?ticker=NVDA/);
await expect(page.getByText('Coverage Workflow')).toBeVisible();
await page.getByLabel('Journal title').fill('Own-the-stack moat check');
await page.getByLabel('Journal body').fill('Monitor hyperscaler concentration, gross margin durability, and Blackwell shipment cadence.');
await page.getByRole('button', { name: 'Save note' }).click();
await expect(page.getByText('Own-the-stack moat check')).toBeVisible();
await page.getByRole('link', { name: 'Open summary' }).first().click();
await expect(page).toHaveURL(/\/analysis\/reports\/NVDA\//);
await page.getByRole('button', { name: 'Add to journal' }).click();
await expect(page.getByText('Saved to the company research journal.')).toBeVisible();
await page.getByRole('link', { name: 'Back to analysis' }).click();
await expect(page).toHaveURL(/\/analysis\?ticker=NVDA/);
await expect(page.getByText('10-K AI memo')).toBeVisible();
await page.getByRole('link', { name: 'Open financials' }).click();
await expect(page).toHaveURL(/\/financials\?ticker=NVDA/);
await page.getByRole('link', { name: 'Filings' }).first().click();
await expect(page).toHaveURL(/\/filings\?ticker=NVDA/);
await expect(page.getByRole('cell', { name: 'NVIDIA Corporation' })).toBeVisible();
await expect(page.getByRole('button', { name: /journal/i }).first()).toBeVisible();
});
test('supports add, edit, and delete holding flows with summary refresh', async ({ page }, testInfo) => {
await signUp(page, testInfo);
await page.goto('/portfolio');
await page.getByLabel('Holding ticker').fill('MSFT');
await page.getByLabel('Holding company name').fill('Microsoft Corporation');
await page.getByLabel('Holding shares').fill('10');
await page.getByLabel('Holding average cost').fill('100');
await page.getByLabel('Holding current price').fill('110');
await page.getByRole('button', { name: 'Save holding' }).click();
await expect(page.getByText('Microsoft Corporation')).toBeVisible();
await expect(page.getByRole('cell', { name: '$1,100.00' })).toBeVisible();
await page.getByRole('button', { name: /^Edit$/ }).first().click();
await page.getByLabel('Holding company name').fill('Microsoft Corp.');
await page.getByLabel('Holding current price').fill('120');
await page.getByRole('button', { name: 'Update holding' }).click();
await expect(page.getByText('Microsoft Corp.')).toBeVisible();
await expect(page.getByRole('cell', { name: '$1,200.00' })).toBeVisible();
await page.getByRole('button', { name: /^Delete$/ }).first().click();
await expect(page.getByText('No holdings added yet.')).toBeVisible();
});