190 lines
6.8 KiB
TypeScript
190 lines
6.8 KiB
TypeScript
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();
|
|
});
|