288 lines
7.9 KiB
TypeScript
288 lines
7.9 KiB
TypeScript
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||
import { render, screen } from '@testing-library/react';
|
||
import { type ReactNode } from 'react';
|
||
import { MemoryRouter } from 'react-router-dom';
|
||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||
import { BrokerEventsOverview } from './BrokerEventsOverview';
|
||
|
||
vi.mock('@/entities/broker-event', () => ({
|
||
useBrokerEvents: vi.fn(),
|
||
}));
|
||
|
||
import { useBrokerEvents } from '@/entities/broker-event';
|
||
|
||
vi.mock('@moex-vibe/design-system', () => ({
|
||
Heading: ({ children }: { children: ReactNode }) => <h3>{children}</h3>,
|
||
Text: ({ children }: { children: ReactNode }) => <span>{children}</span>,
|
||
}));
|
||
|
||
function createWrapper() {
|
||
const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } });
|
||
|
||
return function Wrapper({ children }: { children: ReactNode }) {
|
||
return (
|
||
<QueryClientProvider client={queryClient}>
|
||
<MemoryRouter>{children}</MemoryRouter>
|
||
</QueryClientProvider>
|
||
);
|
||
};
|
||
}
|
||
|
||
const mockItems = [
|
||
{
|
||
id: 'ev-1',
|
||
type: 'dividend',
|
||
ticker: 'SBER',
|
||
name: 'Сбер Банк',
|
||
eventDate: '2026-06-25',
|
||
estimatedAmount: 150,
|
||
currency: 'RUB' as const,
|
||
},
|
||
{
|
||
id: 'ev-2',
|
||
type: 'coupon',
|
||
ticker: 'SU26238RMFS5',
|
||
name: 'ОФЗ 26238',
|
||
eventDate: '2026-06-27',
|
||
estimatedAmount: 36.9,
|
||
currency: 'RUB' as const,
|
||
},
|
||
];
|
||
|
||
describe('BrokerEventsOverview', () => {
|
||
beforeEach(() => {
|
||
vi.clearAllMocks();
|
||
});
|
||
|
||
it('renders loading state', () => {
|
||
vi.mocked(useBrokerEvents).mockReturnValue({
|
||
data: undefined,
|
||
isLoading: true,
|
||
isError: false,
|
||
error: null,
|
||
isSuccess: false,
|
||
isPending: true,
|
||
dataUpdatedAt: 0,
|
||
errorUpdatedAt: 0,
|
||
failureCount: 0,
|
||
failureReason: null,
|
||
errorUpdateCount: 0,
|
||
isFetched: false,
|
||
isFetchedAfterMount: false,
|
||
isFetching: true,
|
||
isInitialLoading: true,
|
||
isPaused: false,
|
||
isLoadingError: false,
|
||
isRefetchError: false,
|
||
isPlaceholderData: false,
|
||
isStale: false,
|
||
refetch: vi.fn(),
|
||
promise: new Promise<never>(() => {}),
|
||
status: 'pending',
|
||
fetchStatus: 'fetching',
|
||
} as unknown as ReturnType<typeof useBrokerEvents>);
|
||
|
||
render(<BrokerEventsOverview accountId="acc-1" />, { wrapper: createWrapper() });
|
||
expect(screen.getByText('Загрузка событий…')).toBeInTheDocument();
|
||
});
|
||
|
||
it('returns null on error', () => {
|
||
vi.mocked(useBrokerEvents).mockReturnValue({
|
||
data: undefined,
|
||
isLoading: false,
|
||
isError: true,
|
||
error: new Error('fail'),
|
||
isSuccess: false,
|
||
isPending: false,
|
||
dataUpdatedAt: 0,
|
||
errorUpdatedAt: 0,
|
||
failureCount: 1,
|
||
failureReason: null,
|
||
errorUpdateCount: 1,
|
||
isFetched: true,
|
||
isFetchedAfterMount: true,
|
||
isFetching: false,
|
||
isInitialLoading: false,
|
||
isPaused: false,
|
||
isLoadingError: true,
|
||
isRefetchError: false,
|
||
isPlaceholderData: false,
|
||
isStale: false,
|
||
refetch: vi.fn(),
|
||
promise: Promise.reject(new Error('fail')),
|
||
status: 'error',
|
||
fetchStatus: 'idle',
|
||
} as unknown as ReturnType<typeof useBrokerEvents>);
|
||
|
||
const { container } = render(<BrokerEventsOverview accountId="acc-1" />, {
|
||
wrapper: createWrapper(),
|
||
});
|
||
expect(container).toBeEmptyDOMElement();
|
||
});
|
||
|
||
it('returns null when items array is empty', () => {
|
||
vi.mocked(useBrokerEvents).mockReturnValue({
|
||
data: {
|
||
summary: {
|
||
eventCount: 0,
|
||
totalEstimatedCashflow: 0,
|
||
nearestEventDate: null,
|
||
currency: 'RUB',
|
||
},
|
||
items: [],
|
||
},
|
||
isLoading: false,
|
||
isError: false,
|
||
error: null,
|
||
isSuccess: true,
|
||
isPending: false,
|
||
dataUpdatedAt: Date.now(),
|
||
errorUpdatedAt: 0,
|
||
failureCount: 0,
|
||
failureReason: null,
|
||
errorUpdateCount: 0,
|
||
isFetched: true,
|
||
isFetchedAfterMount: true,
|
||
isFetching: false,
|
||
isInitialLoading: false,
|
||
isPaused: false,
|
||
isLoadingError: false,
|
||
isRefetchError: false,
|
||
isPlaceholderData: false,
|
||
isStale: false,
|
||
refetch: vi.fn(),
|
||
promise: Promise.resolve({
|
||
summary: {
|
||
eventCount: 0,
|
||
totalEstimatedCashflow: 0,
|
||
nearestEventDate: null,
|
||
currency: 'RUB',
|
||
},
|
||
items: [],
|
||
}),
|
||
status: 'success',
|
||
fetchStatus: 'idle',
|
||
} as unknown as ReturnType<typeof useBrokerEvents>);
|
||
|
||
const { container } = render(<BrokerEventsOverview accountId="acc-1" />, {
|
||
wrapper: createWrapper(),
|
||
});
|
||
expect(container).toBeEmptyDOMElement();
|
||
});
|
||
|
||
it('renders event items and link to full page', () => {
|
||
vi.mocked(useBrokerEvents).mockReturnValue({
|
||
data: {
|
||
summary: {
|
||
eventCount: 2,
|
||
totalEstimatedCashflow: 186.9,
|
||
nearestEventDate: '2026-06-25',
|
||
currency: 'RUB',
|
||
},
|
||
items: mockItems,
|
||
},
|
||
isLoading: false,
|
||
isError: false,
|
||
error: null,
|
||
isSuccess: true,
|
||
isPending: false,
|
||
dataUpdatedAt: Date.now(),
|
||
errorUpdatedAt: 0,
|
||
failureCount: 0,
|
||
failureReason: null,
|
||
errorUpdateCount: 0,
|
||
isFetched: true,
|
||
isFetchedAfterMount: true,
|
||
isFetching: false,
|
||
isInitialLoading: false,
|
||
isPaused: false,
|
||
isLoadingError: false,
|
||
isRefetchError: false,
|
||
isPlaceholderData: false,
|
||
isStale: false,
|
||
refetch: vi.fn(),
|
||
promise: Promise.resolve({
|
||
summary: {
|
||
eventCount: 2,
|
||
totalEstimatedCashflow: 186.9,
|
||
nearestEventDate: '2026-06-25',
|
||
currency: 'RUB',
|
||
},
|
||
items: mockItems,
|
||
}),
|
||
status: 'success',
|
||
fetchStatus: 'idle',
|
||
} as unknown as ReturnType<typeof useBrokerEvents>);
|
||
|
||
render(<BrokerEventsOverview accountId="acc-1" />, { wrapper: createWrapper() });
|
||
|
||
expect(screen.getByText('Ближайшие события')).toBeInTheDocument();
|
||
expect(screen.getByText('SBER')).toBeInTheDocument();
|
||
expect(screen.getByText('SU26238RMFS5')).toBeInTheDocument();
|
||
expect(screen.getByText('Все события')).toBeInTheDocument();
|
||
expect(screen.getByRole('link', { name: 'Все события' })).toHaveAttribute(
|
||
'href',
|
||
'/broker/acc-1/events',
|
||
);
|
||
});
|
||
|
||
it('renders at most 5 items', () => {
|
||
const manyItems = Array.from({ length: 7 }, (_, i) => ({
|
||
id: `ev-${i}`,
|
||
type: 'dividend' as const,
|
||
ticker: `TICKER${i}`,
|
||
name: null as string | null,
|
||
eventDate: '2026-06-25',
|
||
estimatedAmount: 100,
|
||
currency: 'RUB' as const,
|
||
}));
|
||
|
||
vi.mocked(useBrokerEvents).mockReturnValue({
|
||
data: {
|
||
summary: {
|
||
eventCount: 7,
|
||
totalEstimatedCashflow: 700,
|
||
nearestEventDate: '2026-06-25',
|
||
currency: 'RUB',
|
||
},
|
||
items: manyItems,
|
||
},
|
||
isLoading: false,
|
||
isError: false,
|
||
error: null,
|
||
isSuccess: true,
|
||
isPending: false,
|
||
dataUpdatedAt: Date.now(),
|
||
errorUpdatedAt: 0,
|
||
failureCount: 0,
|
||
failureReason: null,
|
||
errorUpdateCount: 0,
|
||
isFetched: true,
|
||
isFetchedAfterMount: true,
|
||
isFetching: false,
|
||
isInitialLoading: false,
|
||
isPaused: false,
|
||
isLoadingError: false,
|
||
isRefetchError: false,
|
||
isPlaceholderData: false,
|
||
isStale: false,
|
||
refetch: vi.fn(),
|
||
promise: Promise.resolve({
|
||
summary: {
|
||
eventCount: 7,
|
||
totalEstimatedCashflow: 700,
|
||
nearestEventDate: '2026-06-25',
|
||
currency: 'RUB',
|
||
},
|
||
items: manyItems,
|
||
}),
|
||
status: 'success',
|
||
fetchStatus: 'idle',
|
||
} as unknown as ReturnType<typeof useBrokerEvents>);
|
||
|
||
render(<BrokerEventsOverview accountId="acc-1" />, { wrapper: createWrapper() });
|
||
|
||
expect(screen.getAllByText(/TICKER/)).toHaveLength(5);
|
||
});
|
||
});
|