import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mobile/main.dart'; import 'package:mobile/widgets/grouped_list_section.dart'; /// 在导航栏内按标签定位,避免和页面标题的相同文字混淆。 Finder navLabel(String label) { return find.descendant( of: find.byType(NavigationBar), matching: find.text(label), ); } /// 手机尺寸(400 × 800 dp),避免默认 800×600 掩盖溢出问题。 void usePhoneSurface(WidgetTester tester) { tester.view.physicalSize = const Size(400, 800); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.reset); } Future pumpApp(WidgetTester tester) async { usePhoneSurface(tester); await tester.pumpWidget(const KiriMemoApp()); await tester.pumpAndSettle(); } void main() { testWidgets('底部导航提供学习 / 管理 / 统计 / 账户四个入口', (WidgetTester tester) async { await pumpApp(tester); expect(find.byType(NavigationBar), findsOneWidget); expect(find.byType(NavigationDestination), findsNWidgets(4)); for (final String label in ['学习', '管理', '统计', '账户']) { expect(navLabel(label), findsOneWidget); } }); testWidgets('Android 端使用水平滑入转场,而非预测式返回', (WidgetTester tester) async { await pumpApp(tester); final MaterialApp app = tester.widget(find.byType(MaterialApp)); final PageTransitionsTheme transitions = app.theme!.pageTransitionsTheme; expect( transitions.builders[TargetPlatform.android], isA(), ); }); testWidgets('默认停留在学习页,点击其它入口会切换', (WidgetTester tester) async { await pumpApp(tester); NavigationBar bar = tester.widget(find.byType(NavigationBar)); expect(bar.selectedIndex, 0); await tester.tap(navLabel('统计')); await tester.pumpAndSettle(); bar = tester.widget(find.byType(NavigationBar)); expect(bar.selectedIndex, 2); await tester.tap(navLabel('账户')); await tester.pumpAndSettle(); bar = tester.widget(find.byType(NavigationBar)); expect(bar.selectedIndex, 3); }); testWidgets('学习首页展示今日任务与唯一主行动', (WidgetTester tester) async { await pumpApp(tester); expect(find.text('今日计划'), findsOneWidget); expect(find.text('待复习'), findsOneWidget); expect(find.text('待新识'), findsOneWidget); expect(find.text('开始学习'), findsOneWidget); // 顶部状态标识:接力用闪电、同步用云图标,不再用文字表达状态。 expect(find.byIcon(Icons.bolt_rounded), findsOneWidget); expect(find.text('今日记忆'), findsOneWidget); expect(find.text('59'), findsOneWidget); expect(find.byIcon(Icons.cloud_done_rounded), findsOneWidget); expect(find.byTooltip('搜索条目'), findsOneWidget); expect(find.byTooltip('学习日志'), findsOneWidget); // 学习域的四个快捷入口。 for (final String label in ['收藏夹', '自我测试', '附带资料', '计划设置']) { expect(find.text(label), findsOneWidget); } }); testWidgets('未接入内核的入口点击后给出反馈', (WidgetTester tester) async { await pumpApp(tester); await tester.tap(find.text('开始学习')); await tester.pump(); await tester.pump(const Duration(milliseconds: 300)); expect(find.text('学习会话 尚未接入内核'), findsOneWidget); }); testWidgets('管理页有三个维度,各自列出卡片', (WidgetTester tester) async { await pumpApp(tester); await tester.tap(navLabel('管理')); await tester.pumpAndSettle(); for (final String label in ['单元集', '计划', '算法']) { expect(find.text(label), findsOneWidget); } expect(find.text('高考必背古诗文'), findsOneWidget); expect(find.text('标识 cngk-t'), findsOneWidget); await tester.tap(find.text('计划')); await tester.pumpAndSettle(); expect(find.text('每日计划'), findsOneWidget); expect(find.text('周末冲刺'), findsOneWidget); expect(find.text('新识记'), findsNWidgets(2)); await tester.tap(find.text('算法')); await tester.pumpAndSettle(); expect(find.text('NSP-0'), findsOneWidget); expect(find.text('默认'), findsOneWidget); }); testWidgets('账户页展示资料、学习数据与四个功能入口', (WidgetTester tester) async { await pumpApp(tester); await tester.tap(navLabel('账户')); await tester.pumpAndSettle(); expect(find.text('zhiyu'), findsOneWidget); expect(find.text('ID KM-1024 · 大一'), findsOneWidget); expect(find.text('累计学习'), findsOneWidget); expect(find.text('坚持'), findsOneWidget); // 四个入口是同一张分组卡片里的四行。 expect(find.byType(GroupedListSection), findsOneWidget); expect(find.byType(GroupedListTile), findsNWidgets(4)); for (final String label in [ '设置', '文档与帮助', '反馈问题', '关于 KiriMemo', ]) { expect(find.text(label), findsOneWidget); } }); testWidgets('账户页可以进入设置页,设置项按分组排列', (WidgetTester tester) async { await pumpApp(tester); await tester.tap(navLabel('账户')); await tester.pumpAndSettle(); await tester.tap(find.text('设置')); await tester.pumpAndSettle(); expect(find.text('每日新识记上限'), findsOneWidget); expect(find.text('答错后重新排队'), findsOneWidget); // 分组在下方,需要滚动到可见。 await tester.scrollUntilVisible(find.text('版本'), 200); expect(find.text('版本'), findsOneWidget); expect(find.text('1.0.0'), findsOneWidget); }); testWidgets('关于页声明 AGPL-3.0 授权', (WidgetTester tester) async { await pumpApp(tester); await tester.tap(navLabel('账户')); await tester.pumpAndSettle(); await tester.tap(find.text('关于 KiriMemo')); await tester.pumpAndSettle(); expect(find.text('GNU AGPL v3.0'), findsOneWidget); }); testWidgets('文档与帮助页底部有可上滑的助手', (WidgetTester tester) async { await pumpApp(tester); await tester.tap(navLabel('账户')); await tester.pumpAndSettle(); await tester.tap(find.text('文档与帮助')); await tester.pumpAndSettle(); expect(find.text('文档内容将在这里显示'), findsOneWidget); expect(find.text('问问 KiriMemo'), findsOneWidget); // 收起时是横栏,点开后出现输入框。 expect(find.byType(TextField), findsNothing); await tester.tap(find.text('问问 KiriMemo')); await tester.pumpAndSettle(); expect(find.byType(TextField), findsOneWidget); }); }