feat: add detail page tests for Documents, Recommendations, Trends, Events, Companies (176 total)
This commit is contained in:
@@ -0,0 +1,268 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { renderRoute } from './render';
|
||||
|
||||
describe('Document Detail page', () => {
|
||||
it('renders document title', async () => {
|
||||
renderRoute('/documents/d1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Apple Q4 Earnings Beat')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows document type and source type', async () => {
|
||||
renderRoute('/documents/d1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('news')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('news_api')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows publisher', async () => {
|
||||
renderRoute('/documents/d1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Reuters')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders metadata section', async () => {
|
||||
renderRoute('/documents/d1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Metadata')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('Language')).toBeInTheDocument();
|
||||
expect(screen.getByText('en')).toBeInTheDocument();
|
||||
expect(screen.getByText('Content Hash')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders parse quality score', async () => {
|
||||
renderRoute('/documents/d1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Parse Quality')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('0.92')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows no intelligence message when null', async () => {
|
||||
renderRoute('/documents/d1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('No intelligence extraction available')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('renders storage references section', async () => {
|
||||
renderRoute('/documents/d1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Storage References')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Recommendation Detail page', () => {
|
||||
it('renders ticker as heading', async () => {
|
||||
renderRoute('/recommendations/r1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('heading', { name: 'AAPL' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows action and mode badges', async () => {
|
||||
renderRoute('/recommendations/r1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('buy')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('paper')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows time horizon', async () => {
|
||||
renderRoute('/recommendations/r1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Horizon')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('7d')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows risk classification', async () => {
|
||||
renderRoute('/recommendations/r1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Risk')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('moderate')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows portfolio and max loss percentages', async () => {
|
||||
renderRoute('/recommendations/r1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Portfolio %')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('5.0%')).toBeInTheDocument();
|
||||
expect(screen.getByText('2.00%')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows thesis', async () => {
|
||||
renderRoute('/recommendations/r1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Thesis')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('Strong earnings momentum')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows evidence section with zero count', async () => {
|
||||
renderRoute('/recommendations/r1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Evidence (0)')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('No evidence linked')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Trend Detail page', () => {
|
||||
it('renders entity ID as heading', async () => {
|
||||
renderRoute('/trends/t1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('heading', { name: 'AAPL' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows trend direction', async () => {
|
||||
renderRoute('/trends/t1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('bullish')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows window badge', async () => {
|
||||
renderRoute('/trends/t1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('AAPL')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getAllByText('7d').length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it('shows contradiction score', async () => {
|
||||
renderRoute('/trends/t1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Contradiction')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('10%')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows dominant catalysts', async () => {
|
||||
renderRoute('/trends/t1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Dominant Catalysts')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('earnings')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows contributing evidence section', async () => {
|
||||
renderRoute('/trends/t1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Contributing Evidence (0)')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('No evidence records')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows trend projection panel', async () => {
|
||||
renderRoute('/trends/t1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Trend Projection')).toBeInTheDocument();
|
||||
});
|
||||
// Mock projection: projected_direction: 'bearish', diverges_from_current: true
|
||||
expect(screen.getByText('DIVERGENCE')).toBeInTheDocument();
|
||||
expect(screen.getByText('bearish')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows macro contribution percentage', async () => {
|
||||
renderRoute('/trends/t1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Macro Contribution')).toBeInTheDocument();
|
||||
});
|
||||
// macro_contribution_pct: 0.3 → 30%
|
||||
expect(screen.getByText('30%')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Global Event Detail page', () => {
|
||||
it('renders page heading', async () => {
|
||||
renderRoute('/macro/events/me1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('heading', { name: 'Global Event' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows severity badge', async () => {
|
||||
renderRoute('/macro/events/me1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('heading', { name: 'Global Event' })).toBeInTheDocument();
|
||||
});
|
||||
// severity: 'high'
|
||||
expect(screen.getByText('high')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows event summary', async () => {
|
||||
renderRoute('/macro/events/me1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Summary')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('US tariffs on Chinese semiconductors')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows key facts', async () => {
|
||||
renderRoute('/macro/events/me1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Key Facts')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('25% tariff')).toBeInTheDocument();
|
||||
expect(screen.getByText('Effective in 30 days')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows affected regions', async () => {
|
||||
renderRoute('/macro/events/me1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Regions')).toBeInTheDocument();
|
||||
});
|
||||
expect(screen.getByText('US')).toBeInTheDocument();
|
||||
expect(screen.getByText('CN')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows affected companies table', async () => {
|
||||
renderRoute('/macro/events/me1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText(/Affected Companies/)).toBeInTheDocument();
|
||||
});
|
||||
// Mock has 1 impact: AAPL with negative direction
|
||||
expect(screen.getByText('AAPL')).toBeInTheDocument();
|
||||
expect(screen.getByText('negative')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('shows estimated duration', async () => {
|
||||
renderRoute('/macro/events/me1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('medium term')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Company Detail page', () => {
|
||||
it('renders company ticker as heading', async () => {
|
||||
renderRoute('/companies/1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByRole('heading', { name: 'AAPL' })).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows company name', async () => {
|
||||
renderRoute('/companies/1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('Apple Inc.')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('shows sector and exchange', async () => {
|
||||
renderRoute('/companies/1');
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('NASDAQ')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user