Files
fin-tracker/app/test/help_tip_test.dart
T
Dmitry 62d36aa3e8 feat(app): новый визуальный язык, верхняя навигация, портфели, создание счетов и ручные события
Тема и общие виджеты (AssetIcon, ServiceMark, HelpTip, глоссарий), верхняя панель TopNav и вкладки Аналитики вместо NavSidebar, иконки PWA. Экраны: портфели, создание брокерского счёта, ручное событие, правка инструмента, Обзор карточками по скоупам и таблица активов, пересчёт метрик с опросом /metrics/status. design-system.md обновлён.
2026-09-19 22:14:21 +03:00

122 lines
3.7 KiB
Dart

import 'package:fintracker_app/core/glossary.dart';
import 'package:fintracker_app/core/widgets/help_tip.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
Widget _app(Widget child) => MaterialApp(
home: Scaffold(body: Center(child: child)),
);
void main() {
test('the glossary finds a term regardless of case, spaces and «ё»', () {
expect(glossaryHint('XIRR'), isNotNull);
expect(glossaryHint(' xirr '), glossaryHint('XIRR'));
expect(glossaryHint('Запас хода (runway)'), isNotNull);
expect(glossaryHint('Внешний поток'), isNotNull);
expect(
glossaryHint('Счёт'),
isNull,
reason: 'an ordinary word is not a term',
);
});
test('every explanation is a real sentence, not a stub', () {
for (final label in [
'XIRR',
'TWR',
'НКД',
'ЛДВ',
'Пассивный доход',
'Запас хода (runway)',
]) {
final text = glossaryHint(label)!;
expect(text.length, greaterThan(40), reason: label);
expect(text.trim().endsWith('.'), isTrue, reason: label);
}
});
testWidgets('a known term carries a «?», an unknown label stays plain text', (
tester,
) async {
await tester.pumpWidget(
_app(
const Column(
mainAxisSize: MainAxisSize.min,
children: [TermLabel('TWR'), TermLabel('Название')],
),
),
);
expect(find.text('TWR'), findsOneWidget);
expect(find.text('Название'), findsOneWidget);
expect(find.byIcon(Icons.help_outline), findsOneWidget);
});
testWidgets('an explicit hint wins over the glossary', (tester) async {
await tester.pumpWidget(
_app(const TermLabel('Прибыль', hint: 'Своё пояснение для налогов.')),
);
final tip = tester.widget<Tooltip>(find.byType(Tooltip));
expect(tip.message, 'Своё пояснение для налогов.');
});
testWidgets('hovering the «?» with a mouse shows the explanation', (
tester,
) async {
await tester.pumpWidget(_app(const TermLabel('XIRR')));
final mouse = await tester.createGesture(kind: PointerDeviceKind.mouse);
await mouse.addPointer(location: Offset.zero);
addTearDown(mouse.removePointer);
await mouse.moveTo(tester.getCenter(find.byIcon(Icons.help_outline)));
await tester.pump(const Duration(milliseconds: 300));
await tester.pump();
expect(
find.textContaining('годовая доходность с учётом дат'),
findsOneWidget,
);
});
testWidgets('a tap shows it too, for a phone with no hover', (tester) async {
await tester.pumpWidget(_app(const TermLabel('НКД')));
await tester.tap(find.byIcon(Icons.help_outline));
await tester.pump(const Duration(milliseconds: 300));
expect(find.textContaining('накопленный купонный доход'), findsOneWidget);
});
testWidgets('a numeric header keeps its label at the end of the cell', (
tester,
) async {
await tester.pumpWidget(
_app(
const SizedBox(
width: 200,
child: TermLabel(
'XIRR',
alignment: MainAxisAlignment.end,
textAlign: TextAlign.end,
),
),
),
);
final label = tester.getRect(find.text('XIRR'));
final icon = tester.getRect(find.byIcon(Icons.help_outline));
expect(
icon.left,
greaterThanOrEqualTo(label.right),
reason: 'the «?» follows the text',
);
expect(
icon.right,
greaterThan(150),
reason: 'and the pair hugs the right edge',
);
});
}