feat(app): новый визуальный язык, верхняя навигация, портфели, создание счетов и ручные события
Тема и общие виджеты (AssetIcon, ServiceMark, HelpTip, глоссарий), верхняя панель TopNav и вкладки Аналитики вместо NavSidebar, иконки PWA. Экраны: портфели, создание брокерского счёта, ручное событие, правка инструмента, Обзор карточками по скоупам и таблица активов, пересчёт метрик с опросом /metrics/status. design-system.md обновлён.
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
import 'package:fintracker_api/fintracker_api.dart';
|
||||
import 'package:fintracker_app/core/cache/cached.dart';
|
||||
import 'package:fintracker_app/features/accounts/providers.dart';
|
||||
import 'package:fintracker_app/features/portfolios/portfolios_page.dart';
|
||||
import 'package:fintracker_app/features/portfolios/providers.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
AccountOut _account(int id, String name, AccountKind kind) => AccountOut(
|
||||
id: id,
|
||||
kind: kind,
|
||||
source_: 'test',
|
||||
sourceId: 'acc-$id',
|
||||
broker: null,
|
||||
name: name,
|
||||
currency: 'RUB',
|
||||
role: AccountRole.investment,
|
||||
includeInNetWorth: true,
|
||||
mirrorOfAccountId: null,
|
||||
primaryEventSource: null,
|
||||
archived: false,
|
||||
disabled: false,
|
||||
openedAt: null,
|
||||
balance: null,
|
||||
balanceAsOf: null,
|
||||
startBalance: null,
|
||||
creditLimit: null,
|
||||
);
|
||||
|
||||
Widget _app(List<PortfolioOut> portfolios, List<AccountOut> accounts) =>
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
portfolioListProvider.overrideWith((ref) async => portfolios),
|
||||
accountsProvider.overrideWith((ref) async => Cached(accounts)),
|
||||
],
|
||||
child: const MaterialApp(home: PortfoliosPage()),
|
||||
);
|
||||
|
||||
void main() {
|
||||
testWidgets('an empty list explains what a portfolio is', (tester) async {
|
||||
await tester.pumpWidget(_app(const [], const []));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.textContaining('Портфелей пока нет'), findsOneWidget);
|
||||
expect(find.text('Новый портфель'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('a portfolio shows its accounts by name', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
_app(
|
||||
[
|
||||
PortfolioOut(
|
||||
id: 1,
|
||||
name: 'Основной',
|
||||
baseCurrency: 'RUB',
|
||||
accountIds: [10, 11],
|
||||
),
|
||||
],
|
||||
[
|
||||
_account(10, 'Брокер А', AccountKind.broker),
|
||||
_account(11, 'Брокер Б', AccountKind.broker),
|
||||
],
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('Основной'), findsOneWidget);
|
||||
expect(find.text('2 счёта: Брокер А, Брокер Б'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('the new-portfolio dialog needs a name and lists brokers first', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
_app(const [], [
|
||||
_account(1, 'Карта', AccountKind.zmCard),
|
||||
_account(2, 'Брокер', AccountKind.broker),
|
||||
]),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
await tester.tap(find.text('Новый портфель'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final titles = tester
|
||||
.widgetList<CheckboxListTile>(find.byType(CheckboxListTile))
|
||||
.toList();
|
||||
expect((titles.first.title as Text).data, 'Брокер');
|
||||
|
||||
await tester.tap(find.text('Сохранить'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Обязательное поле'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user