feat: add Documents and Companies interaction tests (121 total)
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/finalize unknown status
ci/woodpecker/push/build-3 Pipeline failed
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-2 Pipeline was successful
ci/woodpecker/push/build-1 Pipeline was successful
ci/woodpecker/push/finalize unknown status
ci/woodpecker/push/build-3 Pipeline failed
Build and Push / lint-and-test (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.adapters.broker_adapter name:broker-adapter]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.aggregation.worker name:aggregation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.extractor.worker name:extractor]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.ingestion.worker name:ingestion]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.lake_publisher.worker name:lake-publisher]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.parser.worker name:parser]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.recommendation.worker name:recommendation]) (push) Has been cancelled
Build and Push / build-services (map[cmd:python -m services.scheduler.app name:scheduler]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.api.app:app --host 0.0.0.0 --port 8000 name:query-api]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.risk.app:app --host 0.0.0.0 --port 8000 name:risk]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.symbol_registry.app:app --host 0.0.0.0 --port 8000 name:symbol-registry]) (push) Has been cancelled
Build and Push / build-services (map[cmd:uvicorn services.trading.app:app --host 0.0.0.0 --port 8000 name:trading-engine]) (push) Has been cancelled
Build and Push / build-dashboard (push) Has been cancelled
Build and Push / build-superset (push) Has been cancelled
Build and Push / integration-test (push) Has been cancelled
Build and Push / beta-gate (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,125 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { screen, waitFor } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { HttpResponse, http } from 'msw';
|
||||||
|
import { renderRoute } from './render';
|
||||||
|
import { server } from './mocks/server';
|
||||||
|
|
||||||
|
describe('Companies page', () => {
|
||||||
|
it('renders page title', async () => {
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('heading', { name: 'Companies' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders company list with tickers', async () => {
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('AAPL')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('MSFT')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows company names', async () => {
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Apple Inc.')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('Microsoft Corporation')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows sector column', async () => {
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('AAPL')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
const row = screen.getByText('AAPL').closest('tr')!;
|
||||||
|
expect(row).toHaveTextContent('Technology');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows active status badge', async () => {
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('AAPL')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
const row = screen.getByText('AAPL').closest('tr')!;
|
||||||
|
expect(row).toHaveTextContent('active');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows source count', async () => {
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('AAPL')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
const row = screen.getByText('AAPL').closest('tr')!;
|
||||||
|
expect(row).toHaveTextContent('3');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders Add Company button', async () => {
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Add Company')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows add company form on button click', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Add Company')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.click(screen.getByText('Add Company'));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Ticker')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByLabelText('Legal Name')).toBeInTheDocument();
|
||||||
|
expect(screen.getByLabelText('Sector')).toBeInTheDocument();
|
||||||
|
expect(screen.getByLabelText('Exchange')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('submits new company form', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Add Company')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.click(screen.getByText('Add Company'));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Ticker')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.type(screen.getByLabelText('Ticker'), 'GOOG');
|
||||||
|
await user.type(screen.getByLabelText('Legal Name'), 'Alphabet Inc.');
|
||||||
|
await user.click(screen.getByText('Create'));
|
||||||
|
// Form should close on success
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByLabelText('Ticker')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('cancel closes the add form', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Add Company')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.click(screen.getByText('Add Company'));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Cancel')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.click(screen.getByText('Cancel'));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByLabelText('Ticker')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows empty state when no companies', async () => {
|
||||||
|
server.use(
|
||||||
|
http.get('/api/companies', () => HttpResponse.json([])),
|
||||||
|
);
|
||||||
|
renderRoute('/companies');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('No data')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,94 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { screen, waitFor } from '@testing-library/react';
|
||||||
|
import { HttpResponse, http } from 'msw';
|
||||||
|
import { renderRoute } from './render';
|
||||||
|
import { server } from './mocks/server';
|
||||||
|
|
||||||
|
describe('Documents page', () => {
|
||||||
|
it('renders page title', async () => {
|
||||||
|
renderRoute('/documents');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('heading', { name: 'Documents' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders document list with title', async () => {
|
||||||
|
renderRoute('/documents');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Apple Q4 Earnings Beat')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows document type and source type', async () => {
|
||||||
|
renderRoute('/documents');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Apple Q4 Earnings Beat')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
const row = screen.getByText('Apple Q4 Earnings Beat').closest('tr')!;
|
||||||
|
expect(row).toHaveTextContent('news');
|
||||||
|
expect(row).toHaveTextContent('news_api');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows parse confidence badge', async () => {
|
||||||
|
renderRoute('/documents');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('high')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows status badge', async () => {
|
||||||
|
renderRoute('/documents');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Apple Q4 Earnings Beat')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
const row = screen.getByText('Apple Q4 Earnings Beat').closest('tr')!;
|
||||||
|
expect(row).toHaveTextContent('extracted');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders status filter dropdown', async () => {
|
||||||
|
renderRoute('/documents');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Filter by status')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('All statuses')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders ticker filter', async () => {
|
||||||
|
renderRoute('/documents');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByPlaceholderText('Ticker…')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows empty state when no documents', async () => {
|
||||||
|
server.use(
|
||||||
|
http.get('/api/documents', () => HttpResponse.json([])),
|
||||||
|
);
|
||||||
|
renderRoute('/documents');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('No data')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders multiple documents', async () => {
|
||||||
|
server.use(
|
||||||
|
http.get('/api/documents', () => HttpResponse.json([
|
||||||
|
{ id: 'd1', document_type: 'news', source_type: 'news_api', publisher: 'Reuters', url: null, title: 'Apple Q4 Earnings Beat', published_at: '2026-04-10T12:00:00Z', retrieved_at: '2026-04-10T12:05:00Z', language: 'en', content_hash: 'abc123', parse_quality_score: 0.92, parse_confidence: 'high', status: 'extracted', created_at: '2026-04-10T12:05:00Z' },
|
||||||
|
{ id: 'd2', document_type: 'filing', source_type: 'sec_api', publisher: 'SEC', url: null, title: 'MSFT 10-K Annual Report', published_at: '2026-04-09T08:00:00Z', retrieved_at: '2026-04-09T08:05:00Z', language: 'en', content_hash: 'def456', parse_quality_score: 0.88, parse_confidence: 'medium', status: 'parsed', created_at: '2026-04-09T08:05:00Z' },
|
||||||
|
])),
|
||||||
|
);
|
||||||
|
renderRoute('/documents');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Apple Q4 Earnings Beat')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('MSFT 10-K Annual Report')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('filing')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('navigates to document detail on row click', async () => {
|
||||||
|
renderRoute('/documents/d1');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Apple Q4 Earnings Beat')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user