import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:social_app/core/api/api_exception.dart'; import 'package:social_app/features/home/data/voice_recorder.dart'; import 'package:social_app/features/home/ui/screens/home_screen.dart'; class _FakeVoiceRecorder implements VoiceRecorder { bool started = false; String? stoppedPath; @override Future start() async { started = true; } @override Future stop() async { started = false; stoppedPath = '/tmp/test-audio.wav'; return stoppedPath; } @override Future dispose() async {} } void main() { group('HomeScreen Widget Tests', () { testWidgets('displays input field', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp(home: HomeScreen(autoLoadHistory: false)), ); await tester.pumpAndSettle(); expect(find.byType(TextField), findsOneWidget); expect(find.text('输入消息...'), findsOneWidget); }); testWidgets('displays header icons', (WidgetTester tester) async { await tester.pumpWidget( const MaterialApp(home: HomeScreen(autoLoadHistory: false)), ); await tester.pumpAndSettle(); expect(find.byIcon(LucideIcons.settings), findsOneWidget); expect(find.byIcon(LucideIcons.calendar), findsOneWidget); expect(find.byIcon(LucideIcons.messageSquare), findsOneWidget); }); testWidgets('displays send or mic icon based on input', ( WidgetTester tester, ) async { await tester.pumpWidget( const MaterialApp(home: HomeScreen(autoLoadHistory: false)), ); await tester.pumpAndSettle(); expect(find.byIcon(LucideIcons.mic), findsOneWidget); }); testWidgets('tap mic starts recording and shows listening state', ( WidgetTester tester, ) async { final fakeRecorder = _FakeVoiceRecorder(); await tester.pumpWidget( MaterialApp( home: HomeScreen(voiceRecorder: fakeRecorder, autoLoadHistory: false), ), ); await tester.pumpAndSettle(); await tester.tap(find.byIcon(LucideIcons.mic)); await tester.pump(); expect(fakeRecorder.started, true); expect(find.text('正在聆听...'), findsOneWidget); expect(find.byIcon(LucideIcons.square), findsOneWidget); expect(find.byIcon(LucideIcons.send), findsOneWidget); }); testWidgets('tap send while recording transcribes and auto sends message', ( WidgetTester tester, ) async { final fakeRecorder = _FakeVoiceRecorder(); String? sentTranscript; await tester.pumpWidget( MaterialApp( home: HomeScreen( voiceRecorder: fakeRecorder, autoLoadHistory: false, onTranscribeAudio: (filePath) async { expect(filePath, '/tmp/test-audio.wav'); return '语音自动发送'; }, onAutoSendTranscript: (transcript) async { sentTranscript = transcript; }, ), ), ); await tester.pumpAndSettle(); await tester.tap(find.byIcon(LucideIcons.mic)); await tester.pump(); await tester.tap(find.byIcon(LucideIcons.send)); await tester.pumpAndSettle(); expect(sentTranscript, '语音自动发送'); expect(find.byIcon(LucideIcons.plus), findsOneWidget); }); testWidgets('tap stop transcribes audio and fills input', ( WidgetTester tester, ) async { final fakeRecorder = _FakeVoiceRecorder(); await tester.pumpWidget( MaterialApp( home: HomeScreen( voiceRecorder: fakeRecorder, autoLoadHistory: false, onTranscribeAudio: (filePath) async { expect(filePath, '/tmp/test-audio.wav'); return '语音转文字结果'; }, ), ), ); await tester.pumpAndSettle(); await tester.tap(find.byIcon(LucideIcons.mic)); await tester.pump(); await tester.tap(find.byIcon(LucideIcons.square)); await tester.pumpAndSettle(); expect(find.text('语音转文字结果'), findsOneWidget); expect(find.byIcon(LucideIcons.plus), findsOneWidget); }); testWidgets('tap stop shows readable unauthorized message', ( WidgetTester tester, ) async { final fakeRecorder = _FakeVoiceRecorder(); await tester.pumpWidget( MaterialApp( home: HomeScreen( voiceRecorder: fakeRecorder, autoLoadHistory: false, onTranscribeAudio: (_) async { throw const UnauthorizedException(); }, ), ), ); await tester.pumpAndSettle(); await tester.tap(find.byIcon(LucideIcons.mic)); await tester.pump(); await tester.tap(find.byIcon(LucideIcons.square)); await tester.pumpAndSettle(); expect(find.text('请重新登录'), findsOneWidget); await tester.pump(const Duration(seconds: 3)); }); }); }