import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:record/record.dart'; import 'dart:async'; import 'package:flutter_app/core/network/api_repository.dart'; import 'package:flutter_app/features/chat/presentation/cubit/chat_cubit.dart'; import 'package:flutter_app/features/chat/presentation/cubit/chat_state.dart'; import 'package:flutter_app/features/audio/presentation/cubit/voice_cubit.dart'; import 'package:flutter_app/main.dart'; class _TestApiRepository extends ApiRepository { _TestApiRepository() : super(baseUrl: 'http://127.0.0.1:8000'); @override Future getModelName() async => 'gemma4:e2b'; @override Future> getAvailableModels() async => ['gemma4:e2b']; @override Future> listConversations() async => []; } class _CancellableWidgetApi extends _TestApiRepository { final controller = StreamController(); @override Future saveConversation(SavedConversation conversation) async {} @override Stream completeStream(List messages, {String? model}) => controller.stream; @override void cancelActiveRequest() { controller.close(); } } void main() { testWidgets('chat page fits a compact Windows window', (tester) async { tester.view.physicalSize = const Size(800, 600); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final api = _TestApiRepository(); await tester.pumpWidget( MultiRepositoryProvider( providers: [RepositoryProvider.value(value: api)], child: MultiBlocProvider( providers: [ BlocProvider(create: (_) => ChatCubit(api)), BlocProvider(create: (_) => VoiceCubit(api, AudioRecorder())), ], child: const SovereignApp(), ), ), ); await tester.pumpAndSettle(); expect(find.text('مساعدك المحلي'), findsOneWidget); expect(tester.takeException(), isNull); }); testWidgets('stop button cancels an active answer', (tester) async { tester.view.physicalSize = const Size(1000, 700); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final api = _CancellableWidgetApi(); final cubit = ChatCubit(api); await tester.pumpWidget( MultiRepositoryProvider( providers: [RepositoryProvider.value(value: api)], child: MultiBlocProvider( providers: [ BlocProvider.value(value: cubit), BlocProvider(create: (_) => VoiceCubit(api, AudioRecorder())), ], child: const SovereignApp(), ), ), ); await tester.pumpAndSettle(); await tester.enterText(find.byType(TextField), 'أوقف هذا الرد'); await tester.tap(find.byTooltip('إرسال')); await tester.pump(); await tester.pump(const Duration(milliseconds: 30)); expect(find.byTooltip('إيقاف التوليد'), findsOneWidget); await tester.tap(find.byTooltip('إيقاف التوليد')); await tester.pump(); await tester.pumpAndSettle(const Duration(milliseconds: 50)); expect(cubit.state.requestStatus, RequestStatus.cancelled); expect(find.byTooltip('إرسال'), findsOneWidget); expect(tester.takeException(), isNull); await cubit.close(); await api.controller.close(); }); }