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_reminder_form_page.dart'; import 'package:patbond_flutter/features/pets/health_record_analytics.dart'; import 'package:patbond_flutter/features/pets/pet_exceptions.dart'; import 'package:patbond_flutter/features/pets/pet_models.dart'; import '../../helpers/pet_test_helpers.dart'; void main() { late FakePetsRepository repository; late List<(String, Map?)> events; late HealthRecordAnalytics analytics; setUp(() { repository = FakePetsRepository(); events = []; analytics = HealthRecordAnalytics( (name, [props]) async => events.add((name, props)), ); }); List?> eventsOf(String name) => [ for (final e in events) if (e.$1 == name) e.$2, ]; Future pumpForm(WidgetTester tester) async { tester.view.physicalSize = const Size(700, 1600); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.reset); await tester.pumpWidget( MaterialApp( theme: buildAppTheme(), home: Builder( builder: (context) => Scaffold( body: Center( child: TextButton( onPressed: () => Navigator.of(context).push( MaterialPageRoute( builder: (_) => CareReminderFormPage( repository: repository, petId: 'p-1', analytics: analytics, ), ), ), child: const Text('打开表单'), ), ), ), ), ), ); await tester.tap(find.text('打开表单')); await tester.pumpAndSettle(); } Future fillValid(WidgetTester tester) async { await tester.tap(find.text('用药')); await tester.pump(); await tester.enterText( find.widgetWithText(TextFormField, '提醒内容(如:体内外驱虫)'), '心丝虫预防药', ); await tester.tap(find.text('到期日期')); await tester.pumpAndSettle(); await tester.tap(find.text('OK')); await tester.pumpAndSettle(); } testWidgets('started 去重:首次输入一次;四类类型可选', (tester) async { await pumpForm(tester); await fillValid(tester); await tester.enterText( find.widgetWithText(TextFormField, '提醒内容(如:体内外驱虫)'), '改个名字', ); await tester.pump(); final started = eventsOf('health_record_create_started'); expect(started.single, { 'recordType': 'reminder', 'entryPoint': 'record_list', }); // 四类类型齐备。 for (final label in ['驱虫', '体检', '用药', '其他']) { expect(find.text(label), findsOneWidget); } }); testWidgets('40300 横幅 / 网络 SnackBar 兜底 + 失败事件', (tester) async { var attempt = 0; repository.createCareReminderHandler = (petId, request) async { attempt++; if (attempt == 1) { throw const PetAccessDeniedException(message: '无权限'); } throw const ApiNetworkException('断网'); }; await pumpForm(tester); await fillValid(tester); await tester.tap(find.text('保存提醒')); await tester.pumpAndSettle(); expect(find.text('你没有权限为该宠物添加提醒'), findsOneWidget); await tester.tap(find.text('保存提醒')); await tester.pumpAndSettle(); expect(find.text('网络异常,请检查网络后重试'), findsOneWidget); final failed = eventsOf('health_record_create_failed'); expect(failed.length, 2); expect(failed[0], { 'recordType': 'reminder', 'failureReason': 'permission_denied', 'attemptSeq': 1, 'errorCode': 40300, 'httpStatus': 403, }); expect(failed[1]!['failureReason'], 'network_error'); expect(failed[1]!['attemptSeq'], 2); }); }