import 'dart:async'; 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/pet_detail_page.dart'; import 'package:patbond_flutter/features/pets/pet_exceptions.dart'; import 'package:patbond_flutter/features/pets/pet_form_page.dart'; import 'package:patbond_flutter/features/pets/pet_models.dart'; import 'package:patbond_flutter/features/pets/pets_controller.dart'; import '../../helpers/pet_test_helpers.dart'; void main() { late FakePetsRepository repository; late PetsController controller; setUp(() { repository = FakePetsRepository(); controller = PetsController(repository: repository); }); Future pumpDetail(WidgetTester tester, {String petId = 'p-1'}) async { await tester.pumpWidget( MaterialApp( theme: buildAppTheme(), home: const Scaffold(body: Text('列表基底')), ), ); final navigator = tester.state(find.byType(Navigator)); unawaited( navigator.push( MaterialPageRoute( builder: (_) => PetDetailPage(controller: controller, petId: petId), ), ), ); await tester.pump(); } testWidgets('ready:档案字段齐备,owner 可见编辑入口', (tester) async { repository.getPetHandler = (petId) async => buildPet( 'p-1', overrides: { 'microchipNo': '985112000000001', 'personality': '活泼', 'sterilizedOn': '2025-06-01', }, ); await pumpDetail(tester); await tester.pumpAndSettle(); expect(find.text('豆豆'), findsOneWidget); expect(find.text('基本资料'), findsOneWidget); expect(find.text('柴犬'), findsOneWidget); expect(find.text('男孩'), findsOneWidget); expect(find.text('2024-03-15'), findsOneWidget); expect(find.text('985112000000001'), findsOneWidget); expect(find.text('活泼'), findsOneWidget); expect(find.text('2025-06-01'), findsOneWidget); expect(find.byIcon(Icons.edit_outlined), findsWidgets); expect(find.text('编辑资料'), findsOneWidget); }); testWidgets('生日估算标记与未填写字段兜底', (tester) async { repository.getPetHandler = (petId) async => buildPet('p-1', overrides: {'birthDateEstimated': true}); await pumpDetail(tester); await tester.pumpAndSettle(); expect(find.text('2024-03-15(估算)'), findsOneWidget); // 芯片号 / 性格 / 绝育日期未填。 expect(find.text('未填写'), findsNWidgets(3)); }); testWidgets('四态 · loading:无内存副本时居中转圈', (tester) async { final completer = Completer(); repository.getPetHandler = (petId) => completer.future; await pumpDetail(tester); await tester.pump(); expect(find.byType(CircularProgressIndicator), findsOneWidget); completer.complete(buildPet('p-1')); await tester.pumpAndSettle(); expect(find.text('豆豆'), findsOneWidget); }); testWidgets('四态 · error/retry:无副本加载失败给横幅与重试', (tester) async { var calls = 0; repository.getPetHandler = (petId) async { calls++; if (calls == 1) throw const ApiNetworkException('断网'); return buildPet('p-1'); }; await pumpDetail(tester); await tester.pumpAndSettle(); expect(find.text('网络异常,请检查网络后重试'), findsOneWidget); await tester.tap(find.text('重试')); await tester.pumpAndSettle(); expect(find.text('豆豆'), findsOneWidget); }); testWidgets('四态 · notFound(40401):提示不存在,返回列表并触发刷新', (tester) async { repository.getPetHandler = (petId) async => throw const PetNotFoundException(message: '不存在'); var listCalls = 0; repository.listPetsHandler = () async { listCalls++; return const []; }; await pumpDetail(tester); await tester.pumpAndSettle(); expect(find.text('宠物不存在或已被删除'), findsOneWidget); await tester.tap(find.text('返回列表')); await tester.pumpAndSettle(); expect(find.byType(PetDetailPage), findsNothing); expect(find.text('列表基底'), findsOneWidget); expect(listCalls, 1); }); testWidgets('有内存副本时先渲染副本;后台刷新失败降级 SnackBar', (tester) async { repository.listPetsHandler = () async => [buildPet('p-1')]; await controller.refresh(); final completer = Completer(); repository.getPetHandler = (petId) => completer.future; await pumpDetail(tester); await tester.pump(); // 副本即时渲染,无整页 loading。 expect(find.text('豆豆'), findsOneWidget); expect(find.byType(CircularProgressIndicator), findsNothing); completer.completeError(const ApiNetworkException('断网')); await tester.pumpAndSettle(); expect(find.text('豆豆'), findsOneWidget); expect(find.text('网络异常,请检查网络后重试'), findsOneWidget); }); testWidgets('viewer 角色隐藏全部编辑入口(40300 语义)', (tester) async { repository.getPetHandler = (petId) async => buildPet('p-1', overrides: {'myRole': 'viewer'}); await pumpDetail(tester); await tester.pumpAndSettle(); expect(find.byIcon(Icons.edit_outlined), findsNothing); expect(find.text('编辑资料'), findsNothing); expect(find.byIcon(Icons.edit), findsNothing); }); testWidgets('owner 点编辑 → 进入编辑表单并预填', (tester) async { repository.getPetHandler = (petId) async => buildPet('p-1'); await pumpDetail(tester); await tester.pumpAndSettle(); await tester.tap(find.text('编辑资料')); await tester.pumpAndSettle(); expect(find.byType(PetFormPage), findsOneWidget); expect(find.text('编辑宠物资料'), findsOneWidget); expect(find.text('豆豆'), findsOneWidget); }); }