feat: add 25 tests for Trading Controls and Global Events pages (100 total)
This commit is contained in:
@@ -0,0 +1,135 @@
|
|||||||
|
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('Global Events page', () => {
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 1. Renders event list with summary
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders event list with summary', async () => {
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(/US tariffs on Chinese semiconductors/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 2. Shows severity badge
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('shows severity badge', async () => {
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(/US tariffs/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
const row = screen.getByText(/US tariffs/).closest('tr')!;
|
||||||
|
expect(row).toHaveTextContent('high');
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 3. Shows event type tags
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('shows event type tags', async () => {
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
// event_types: ['trade_barrier', 'cost_increase'] — underscores replaced with spaces
|
||||||
|
expect(screen.getByText('trade barrier')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('cost increase')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 4. Shows affected regions
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('shows affected regions', async () => {
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('US')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('CN')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 5. Shows affected sectors
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('shows affected sectors', async () => {
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Technology')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 6. Severity filter buttons render
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders severity filter buttons', async () => {
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('group', { name: 'Severity filter' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
const group = screen.getByRole('group', { name: 'Severity filter' });
|
||||||
|
expect(group).toHaveTextContent('All');
|
||||||
|
expect(group).toHaveTextContent('low');
|
||||||
|
expect(group).toHaveTextContent('moderate');
|
||||||
|
expect(group).toHaveTextContent('high');
|
||||||
|
expect(group).toHaveTextContent('critical');
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 7. Empty state
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('shows empty state when no events', async () => {
|
||||||
|
server.use(
|
||||||
|
http.get('/api/macro/events', () => HttpResponse.json([])),
|
||||||
|
);
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('No data')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 8. Page title renders
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders page title', async () => {
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('heading', { name: 'Global Events' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 9. Severity filter is clickable
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('severity filter buttons are clickable', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('group', { name: 'Severity filter' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
// Click "critical" filter — should not crash
|
||||||
|
await user.click(screen.getByRole('button', { name: 'critical' }));
|
||||||
|
// Page should still be rendered
|
||||||
|
expect(screen.getByRole('heading', { name: 'Global Events' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 10. Multiple events render
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders multiple events', async () => {
|
||||||
|
server.use(
|
||||||
|
http.get('/api/macro/events', () => HttpResponse.json([
|
||||||
|
{ id: 'me1', event_types: ['trade_barrier'], severity: 'high', affected_regions: ['US'], affected_sectors: ['Technology'], affected_commodities: [], summary: 'Tariff event', key_facts: [], estimated_duration: 'short_term', confidence: 0.85, source_document_id: 'd1', created_at: '2026-05-15T14:00:00Z' },
|
||||||
|
{ id: 'me2', event_types: ['monetary_policy'], severity: 'moderate', affected_regions: ['EU'], affected_sectors: ['Financial Services'], affected_commodities: [], summary: 'ECB rate decision', key_facts: [], estimated_duration: 'medium_term', confidence: 0.70, source_document_id: 'd2', created_at: '2026-05-16T10:00:00Z' },
|
||||||
|
])),
|
||||||
|
);
|
||||||
|
renderRoute('/macro/events');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Tariff event')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('ECB rate decision')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('EU')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,197 @@
|
|||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
import { screen, waitFor } from '@testing-library/react';
|
||||||
|
import userEvent from '@testing-library/user-event';
|
||||||
|
import { renderRoute } from './render';
|
||||||
|
|
||||||
|
describe('Trading Controls page', () => {
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 1. Page title renders
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders page title', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('heading', { name: 'Trading Controls' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 2. Trading mode buttons render with paper active
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders trading mode buttons with paper active', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('button', { name: 'paper', pressed: true })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByRole('button', { name: 'live', pressed: false })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: 'disabled', pressed: false })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 3. Live mode shows confirmation dialog
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('shows confirmation dialog when clicking live mode', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('button', { name: 'live' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.click(screen.getByRole('button', { name: 'live' }));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(/Are you sure you want to switch to/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('Confirm')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Cancel')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 4. Cancel dismisses live mode confirmation
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('dismisses live mode confirmation on cancel', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByRole('button', { name: 'live' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.click(screen.getByRole('button', { name: 'live' }));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Cancel')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.click(screen.getByText('Cancel'));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.queryByText(/Are you sure you want to switch to/)).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 5. Reset card renders with button
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders paper trading reset card', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Paper Trading Account')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('Reset Everything')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 6. Reset shows confirmation dialog
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('shows reset confirmation dialog', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Reset Everything')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.click(screen.getByText('Reset Everything'));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(/permanently delete/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('Yes, Reset Everything')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 7. Macro signal layer toggle renders
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders macro signal layer toggle', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Macro Signal Layer')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByLabelText('Toggle macro signal layer')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 8. Competitive signal layer toggle renders
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders competitive signal layer toggle', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Competitive Signal Layer')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByLabelText('Toggle competitive signal layer')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 9. Macro toggle shows confirmation
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('shows confirmation when toggling macro layer', async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Toggle macro signal layer')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
await user.click(screen.getByLabelText('Toggle macro signal layer'));
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText(/Are you sure you want to/)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 10. Pending approvals section renders
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders pending approvals section with zero count', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Pending Approvals (0)')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('No pending approvals')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 11. Active lockouts section renders
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders active lockouts section with zero count', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Active Lockouts (0)')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByText('No active lockouts')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 12. Lockout form renders with fields
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders lockout creation form', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByLabelText('Ticker')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByLabelText('Reason')).toBeInTheDocument();
|
||||||
|
expect(screen.getByLabelText('Minutes')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('Add Lockout')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 13. Risk tier buttons render
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders risk tier buttons', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Risk Tier')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByRole('button', { name: 'conservative' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: 'moderate' })).toBeInTheDocument();
|
||||||
|
expect(screen.getByRole('button', { name: 'aggressive' })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 14. Override Trade button links to engine
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders Override Trade button', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByTestId('override-trade-button')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// 15. Paper order approval toggle renders
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
it('renders paper order approval toggle', async () => {
|
||||||
|
renderRoute('/trading');
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(screen.getByText('Paper Order Approval')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
expect(screen.getByLabelText('Toggle paper order approval requirement')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user