Лента брокерского леджера: фильтры по счёту, типу, датам и поиск, бесконечная прокрутка с явной кнопкой «Ещё», карточка события в bottom sheet с переходом на инструмент. Паттерн контроллера и пагинации взят у транзакций один в один — это тот же список с фильтрами, и второй способ делать одно и то же был бы просто вторым способом его чинить. Пункт навигации стоит между «Портфелем» и «Потоками», а не рядом с «Операциями»: это инвестиционная лента, а в операциях лежит ZenMoney. eventKindLabels переиспользован из portfolio/labels.dart, где он уже жил ради карточки инструмента, а не скопирован: два словаря подписей для одного енума разъезжаются на первом же новом типе события. Бэкенд не менялся — /events с фильтрами и пагинацией закрывает экран целиком, клиент не перегенерировался.
84 lines
2.7 KiB
Dart
84 lines
2.7 KiB
Dart
import 'package:fintracker_api/fintracker_api.dart';
|
|
import 'package:fintracker_app/core/widgets/money_text.dart';
|
|
import 'package:fintracker_app/features/events/event_row.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
EventOut event({
|
|
EventKind kind = EventKind.buy,
|
|
String currency = 'RUB',
|
|
String amount = '-10000',
|
|
String? amountRub = '-10000',
|
|
String? ticker = 'SBER',
|
|
String? quantity = '100',
|
|
EventStatus status = EventStatus.confirmed,
|
|
bool externalFlow = false,
|
|
}) {
|
|
return EventOut(
|
|
accountId: 46,
|
|
accruedInterest: null,
|
|
amount: amount,
|
|
amountRub: amountRub,
|
|
currency: currency,
|
|
description: 'Покупка ценных бумаг',
|
|
externalFlow: externalFlow,
|
|
fee: '30',
|
|
id: 1,
|
|
instrumentId: 311,
|
|
kind: kind,
|
|
price: '100',
|
|
priceCurrency: currency,
|
|
quantity: quantity,
|
|
status: status,
|
|
tax: null,
|
|
ticker: ticker,
|
|
tradeDate: DateTime(2026, 9, 10),
|
|
ts: DateTime(2026, 9, 10, 12, 30),
|
|
);
|
|
}
|
|
|
|
Widget wrap(EventOut e) => MaterialApp(
|
|
home: Scaffold(
|
|
body: EventRow(event: e, accountName: 'T-Invest ИИС', onTap: () {}),
|
|
),
|
|
);
|
|
|
|
void main() {
|
|
testWidgets('shows kind, ticker, account and the native amount', (tester) async {
|
|
await tester.pumpWidget(wrap(event()));
|
|
|
|
expect(find.text('Покупка · SBER'), findsOneWidget);
|
|
expect(find.textContaining('T-Invest ИИС'), findsOneWidget);
|
|
expect(find.textContaining('100 шт.'), findsOneWidget);
|
|
expect(find.text(MoneyText.format('-10000', 'RUB')), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('adds the RUB equivalent only for a foreign currency', (tester) async {
|
|
await tester.pumpWidget(wrap(event(currency: 'USD', amount: '-100', amountRub: '-9500')));
|
|
|
|
expect(find.text(MoneyText.format('-100', 'USD')), findsOneWidget);
|
|
expect(find.text(MoneyText.format('-9500', 'RUB')), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('leaves the RUB line out when no rate was available', (tester) async {
|
|
await tester.pumpWidget(wrap(event(currency: 'USD', amount: '-100', amountRub: null)));
|
|
|
|
expect(find.text(MoneyText.format('-100', 'USD')), findsOneWidget);
|
|
expect(find.textContaining('₽'), findsNothing);
|
|
});
|
|
|
|
testWidgets('names a non-confirmed status in the subtitle', (tester) async {
|
|
await tester.pumpWidget(wrap(event(status: EventStatus.pending)));
|
|
|
|
expect(find.textContaining('Ожидает'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('drops the separator for an event with no instrument', (tester) async {
|
|
await tester.pumpWidget(
|
|
wrap(event(kind: EventKind.deposit, ticker: null, quantity: null, externalFlow: true)),
|
|
);
|
|
|
|
expect(find.text('Пополнение'), findsOneWidget);
|
|
});
|
|
}
|