codex/broker-operations-ui #18

Merged
ksv741 merged 22 commits from codex/broker-operations-ui into main 2026-06-18 06:34:55 +03:00
Showing only changes of commit 5ccd421259 - Show all commits

View File

@ -1,5 +1,5 @@
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { render, screen } from '@testing-library/react';
import { render, screen, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { type ReactElement } from 'react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
@ -7,6 +7,7 @@ import { describe, expect, it, vi } from 'vitest';
import * as accountHook from '../../hooks/useBrokerAccounts';
import * as operationsHook from '../../hooks/useBrokerOperations';
import * as portfolioHook from '../../hooks/useBrokerPortfolio';
import * as positionsHook from '../../hooks/useBrokerPositions';
import { BrokerAccountDetailPage } from './BrokerAccountDetailPage';
import { BrokerAccountsPage } from './BrokerAccountsPage';
@ -66,24 +67,6 @@ describe('Broker pages', () => {
yields: { expectedPercent: 5, daily: null, dailyPercent: null },
cash: [{ currency: 'RUB', units: '100', nano: 0, value: 100 }],
blockedCash: [],
positions: [
{
figi: null,
instrumentUid: 'uid-1',
positionUid: null,
ticker: 'SBER',
classCode: 'TQBR',
instrumentType: 'share',
name: 'Sberbank',
quantity: 10,
blockedLots: null,
currentPrice: null,
currentValue: { currency: 'RUB', units: '1000', nano: 0, value: 1000 },
averagePositionPrice: null,
expectedYieldPercent: null,
dailyYield: null,
},
],
asOf: '2026-06-16T00:00:00.000Z',
},
isLoading: false,
@ -124,6 +107,34 @@ describe('Broker pages', () => {
isLoading: false,
error: null,
} as any);
vi.spyOn(positionsHook, 'useBrokerPositions').mockReturnValue({
data: {
accountId: 'acc-1',
items: [
{
figi: null,
instrumentUid: 'uid-1',
positionUid: null,
ticker: 'SBER',
classCode: 'TQBR',
instrumentType: 'share',
name: 'Sberbank',
quantity: 10,
blockedLots: null,
currentPrice: null,
currentValue: { currency: 'RUB', units: '1000', nano: 0, value: 1000 },
averagePositionPrice: null,
expectedYieldPercent: null,
dailyYield: null,
},
],
nextCursor: null,
hasNext: false,
asOf: '2026-06-16T00:00:00.000Z',
},
isLoading: false,
error: null,
} as any);
renderWithClient(
<Routes>
@ -151,7 +162,26 @@ describe('Broker pages', () => {
yields: { expectedPercent: 5, daily: null, dailyPercent: null },
cash: [],
blockedCash: [],
positions: [
asOf: '2026-06-17T00:00:00.000Z',
},
isLoading: false,
error: null,
} as any);
vi.spyOn(operationsHook, 'useBrokerOperations').mockReturnValue({
data: {
accountId: 'acc-1',
items: [],
nextCursor: null,
hasNext: false,
asOf: '2026-06-17T00:00:00.000Z',
},
isLoading: false,
error: null,
} as any);
vi.spyOn(positionsHook, 'useBrokerPositions').mockReturnValue({
data: {
accountId: 'acc-1',
items: [
{
figi: null,
instrumentUid: 'share-uid',
@ -185,15 +215,6 @@ describe('Broker pages', () => {
dailyYield: null,
},
],
asOf: '2026-06-17T00:00:00.000Z',
},
isLoading: false,
error: null,
} as any);
vi.spyOn(operationsHook, 'useBrokerOperations').mockReturnValue({
data: {
accountId: 'acc-1',
items: [],
nextCursor: null,
hasNext: false,
asOf: '2026-06-17T00:00:00.000Z',
@ -237,7 +258,6 @@ describe('Broker pages', () => {
yields: { expectedPercent: null, daily: null, dailyPercent: null },
cash: [],
blockedCash: [],
positions: [],
asOf: '2026-06-17T00:00:00.000Z',
},
isLoading: false,
@ -301,6 +321,17 @@ describe('Broker pages', () => {
isLoading: false,
error: null,
} as any);
vi.spyOn(positionsHook, 'useBrokerPositions').mockReturnValue({
data: {
accountId: 'acc-1',
items: [],
nextCursor: null,
hasNext: false,
asOf: '2026-06-17T00:00:00.000Z',
},
isLoading: false,
error: null,
} as any);
renderWithClient(
<Routes>
@ -334,7 +365,6 @@ describe('Broker pages', () => {
yields: { expectedPercent: null, daily: null, dailyPercent: null },
cash: [],
blockedCash: [],
positions: [],
asOf: '2026-06-17T00:00:00.000Z',
},
isLoading: false,
@ -411,6 +441,17 @@ describe('Broker pages', () => {
error: null,
}) as any,
);
vi.spyOn(positionsHook, 'useBrokerPositions').mockReturnValue({
data: {
accountId: 'acc-1',
items: [],
nextCursor: null,
hasNext: false,
asOf: '2026-06-17T00:00:00.000Z',
},
isLoading: false,
error: null,
} as any);
renderWithClient(
<Routes>
@ -421,9 +462,11 @@ describe('Broker pages', () => {
expect(operationsSpy).toHaveBeenLastCalledWith('acc-1', { limit: 10, cursor: undefined });
// Find pagination buttons by their text content (← and →)
const nextButton = screen.getByRole('button', { name: '→' });
const prevButton = screen.getByRole('button', { name: '←' });
// Scope pagination queries to the operations section (positions section also has pagination now)
const operationsSection = screen.getByRole('heading', { name: 'Операции' }).closest('section')!;
const withinOperations = within(operationsSection);
const nextButton = withinOperations.getByRole('button', { name: '→' });
const prevButton = withinOperations.getByRole('button', { name: '←' });
expect(prevButton).toBeDisabled();
expect(nextButton).not.toBeDisabled();
@ -435,11 +478,11 @@ describe('Broker pages', () => {
});
// Page number is shown as just a number (without "Страница" label)
expect(screen.getByText('2')).toBeInTheDocument();
expect(withinOperations.getByText('2')).toBeInTheDocument();
await user.click(screen.getByRole('button', { name: '←' }));
await user.click(prevButton);
expect(operationsSpy).toHaveBeenLastCalledWith('acc-1', { limit: 10, cursor: undefined });
expect(screen.getByText('1')).toBeInTheDocument();
expect(withinOperations.getByText('1')).toBeInTheDocument();
});
});