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/core/widgets/empty_state_illustration.dart'; import 'package:patbond_flutter/features/pets/pet_detail_page.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 'package:patbond_flutter/features/pets/pets_page.dart'; import '../../helpers/pet_test_helpers.dart'; class _RecordingNavObserver extends NavigatorObserver { final pushedNames = []; @override void didPush(Route route, Route? previousRoute) { pushedNames.add(route.settings.name); } } void main() { late FakePetsRepository repository; late PetsController controller; late _RecordingNavObserver observer; setUp(() { repository = FakePetsRepository(); controller = PetsController(repository: repository); observer = _RecordingNavObserver(); }); Future pumpPage(WidgetTester tester) { return tester.pumpWidget( MaterialApp( theme: buildAppTheme(), navigatorObservers: [observer], home: Scaffold(body: PetsPage(controller: controller)), ), ); } testWidgets('四态 · loading:拉取期间居中转圈', (tester) async { final completer = Completer>(); repository.listPetsHandler = () => completer.future; await pumpPage(tester); await tester.pump(); expect(find.byType(CircularProgressIndicator), findsOneWidget); completer.complete(const []); await tester.pumpAndSettle(); expect(find.byType(CircularProgressIndicator), findsNothing); }); testWidgets('四态 · empty:空态插画 + 建档 CTA 打开 pet_form 表单页', (tester) async { await pumpPage(tester); await tester.pumpAndSettle(); expect(find.byType(EmptyStateIllustration), findsOneWidget); expect(find.text('还没有宠物档案'), findsOneWidget); await tester.tap(find.text('添加宠物')); await tester.pumpAndSettle(); expect(find.byType(PetFormPage), findsOneWidget); // page_viewed(pet_form) 接线依据:路由名进入既有 RouteObserver 采集。 expect(observer.pushedNames.last, 'pet_form'); }); testWidgets('四态 · error/retry:横幅 + 重试按钮,重试后恢复列表', (tester) async { var calls = 0; repository.listPetsHandler = () async { calls++; if (calls == 1) throw const ApiNetworkException('断网'); return [buildPet('p-1')]; }; await pumpPage(tester); await tester.pumpAndSettle(); expect(find.text('网络异常,请检查网络后重试'), findsOneWidget); expect(find.text('重试'), findsOneWidget); await tester.tap(find.text('重试')); await tester.pumpAndSettle(); expect(find.text('豆豆'), findsOneWidget); }); testWidgets('四态 · ready:列表卡渲染名字与元信息,含添加入口', (tester) async { repository.listPetsHandler = () async => [ buildPet('p-1'), buildPet( 'p-2', name: '咪咪', overrides: { 'species': 'cat', 'sex': 'female', 'breedId': null, 'breedDisplayName': null, 'customBreedName': '狸花', }, ), ]; await pumpPage(tester); await tester.pumpAndSettle(); expect(find.text('我的宠物'), findsOneWidget); expect(find.text('豆豆'), findsOneWidget); expect(find.text('咪咪'), findsOneWidget); expect(find.textContaining('柴犬 · 男孩'), findsOneWidget); expect(find.textContaining('狸花 · 女孩'), findsOneWidget); expect(find.text('+ 添加宠物'), findsOneWidget); expect(find.text('添加'), findsOneWidget); }); testWidgets('点宠物卡 → 以 pet_detail 路由名进入详情页', (tester) async { repository.listPetsHandler = () async => [buildPet('p-1')]; repository.getPetHandler = (petId) async => buildPet('p-1'); await pumpPage(tester); await tester.pumpAndSettle(); await tester.tap(find.text('豆豆')); await tester.pumpAndSettle(); expect(find.byType(PetDetailPage), findsOneWidget); expect(observer.pushedNames.last, 'pet_detail'); }); testWidgets('非 active 宠物显示状态标签(走失中)', (tester) async { repository.listPetsHandler = () async => [ buildPet('p-1', overrides: {'status': 'lost'}), ]; await pumpPage(tester); await tester.pumpAndSettle(); expect(find.text('走失中'), findsOneWidget); }); }