新增:照护提醒接入与疫苗流转收尾(T2-14 提醒半边 + T2-13 遗留)
CI / flutter-gates (push) Successful in 2m12s

- 照护提醒页:status 过滤(服务端白名单视图)、due_at ASC、逾期红标
  双通道标识,四态齐备;创建表单(四类)+ 完成(completedAt 必带、
  支持补记)/ 忽略(禁带 completedAt)流转,42202/40902 兜底重拉
- 档案页:「健康提醒」卡改真实待办数据驱动(取代 demo 硬编码文案,
  最近到期一条 + 逾期警示形态),照护提醒导航入口带待办数副行
- 疫苗列表(25 号报告遗留①②):scheduled 行「标记完成/取消登记」
  PATCH 流转;完成对话框补录厂商/批号(契约可选字段);
  42201/40902/40402 兜底
- 埋点:create 三事件 + viewed(recordType=reminder)挂通;
  edit_succeeded/failed 挂 vaccine 流转(failureReason 含 conflict);
  提醒完成/忽略按 06 §7 缺口 3 既定取舍不埋
- 测试 250 → 272 全绿(+22,较基线 +48);analyze 0 问题;format 无 diff

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-08 14:01:17 +08:00
parent e186ba3da9
commit ba503327f5
10 changed files with 2241 additions and 48 deletions
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:patbond_flutter/core/network/api_exception.dart';
import 'package:patbond_flutter/core/theme/app_theme.dart';
import 'package:patbond_flutter/features/pets/care_reminders_page.dart';
import 'package:patbond_flutter/features/pets/health_events_page.dart';
import 'package:patbond_flutter/features/pets/health_record_display.dart';
import 'package:patbond_flutter/features/pets/pet_detail_page.dart';
@@ -348,4 +349,107 @@ void main() {
expect(find.byIcon(Icons.add), findsNothing);
});
});
group('T2-14 · 健康提醒真实数据驱动(取代 demo 硬编码)', () {
testWidgets('有待办:alert 卡显示最近到期提醒;入口副行显示待办数;点卡进提醒页并回拉', (tester) async {
repository.getPetHandler = (petId) async => buildPet('p-1');
final due = DateTime.now().add(const Duration(days: 30));
var reminderCalls = 0;
final capturedStatus = <CareReminderStatus?>[];
repository.listCareRemindersHandler = (petId, status) async {
reminderCalls++;
capturedStatus.add(status);
return [
buildReminder(
'r-1',
overrides: {
'title': '已经半年没有进行体内外驱虫',
'reminderType': 'deworming',
'dueAt': due.toUtc().toIso8601String(),
},
),
buildReminder('r-2'),
];
};
await pumpDetail(tester);
await tester.pumpAndSettle();
// alert 卡取 due_at ASC 首条(最近到期)真实数据。
expect(
find.text('健康提醒:已经半年没有进行体内外驱虫(${dateToJson(due)} 到期)'),
findsOneWidget,
);
expect(find.text('2 条待办'), findsOneWidget);
// 详情页只拉待办视图。
expect(capturedStatus.first, CareReminderStatus.pending);
final alert = find.textContaining('健康提醒:');
await tester.ensureVisible(alert);
await tester.tap(alert);
await tester.pumpAndSettle();
expect(find.byType(CareRemindersPage), findsOneWidget);
await tester.pageBack();
await tester.pumpAndSettle();
// 返回后重拉待办(详情页 1 次 + 提醒页自身 1 次 + 返回重拉 1 次)。
expect(reminderCalls, 3);
});
testWidgets('逾期待办:alert 卡切警示形态', (tester) async {
repository.getPetHandler = (petId) async => buildPet('p-1');
repository.listCareRemindersHandler = (petId, status) async => [
buildReminder(
'r-1',
overrides: {
'dueAt': DateTime.now()
.subtract(const Duration(days: 7))
.toUtc()
.toIso8601String(),
},
),
];
await pumpDetail(tester);
await tester.pumpAndSettle();
expect(find.text('健康提醒:年度体检(已逾期)'), findsOneWidget);
});
testWidgets('无待办:不渲染 alert 卡(无 demo 占位),入口副行「暂无待办提醒」', (tester) async {
repository.getPetHandler = (petId) async => buildPet('p-1');
repository.listCareRemindersHandler = (petId, status) async => const [];
await pumpDetail(tester);
await tester.pumpAndSettle();
expect(find.textContaining('健康提醒:'), findsNothing);
expect(find.text('暂无待办提醒'), findsOneWidget);
});
testWidgets('待办加载失败:主链路不受阻,入口副行降级提示且仍可进提醒页', (tester) async {
repository.getPetHandler = (petId) async => buildPet('p-1');
var calls = 0;
repository.listCareRemindersHandler = (petId, status) async {
calls++;
if (calls == 1) throw const ApiNetworkException('断网');
return const [];
};
await pumpDetail(tester);
await tester.pumpAndSettle();
expect(find.text('豆豆'), findsOneWidget);
expect(find.text('提醒加载失败,点击查看'), findsOneWidget);
final tile = find.widgetWithText(ListTile, '照护提醒');
await tester.ensureVisible(tile);
await tester.tap(tile);
await tester.pumpAndSettle();
expect(find.byType(CareRemindersPage), findsOneWidget);
});
});
}