import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:ignis_app/models/ignis_group.dart'; import 'package:ignis_app/providers/providers.dart'; import 'package:ignis_app/screens/remote_screen.dart'; import 'package:ignis_app/services/settings_service.dart'; import 'package:ignis_app/widgets/group_card.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'test_support.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); testWidgets('remote screen shows api keys menu for admin only', ( tester, ) async { final adminApi = FakeIgnisApi( authData: {'is_admin': true, 'name': 'owner'}, groupsData: [], ); final adminContainer = await _pumpRemoteScreen( tester, api: adminApi, settingsService: await _seedSettingsService(), ); await adminContainer.read(authInfoProvider.notifier).load(); await tester.pump(); await tester.tap(find.byIcon(Icons.more_vert)); await tester.pumpAndSettle(); expect(find.text('API-ключи'), findsOneWidget); await tester.tapAt(const Offset(10, 10)); await tester.pumpAndSettle(); final guestApi = FakeIgnisApi( authData: {'is_admin': false, 'name': 'guest'}, groupsData: [], ); final guestContainer = await _pumpRemoteScreen( tester, api: guestApi, settingsService: await _seedSettingsService(), ); await guestContainer.read(authInfoProvider.notifier).load(); await tester.pump(); await tester.tap(find.byIcon(Icons.more_vert)); await tester.pumpAndSettle(); expect(find.text('API-ключи'), findsNothing); }); testWidgets('remote screen deletes group after confirmation', (tester) async { final api = FakeIgnisApi( authData: {'is_admin': true, 'name': 'owner'}, groupsData: { 'kitchen': { 'name': 'Kitchen', 'macs': ['AA:BB'], }, }, ); final container = await _pumpRemoteScreen( tester, api: api, settingsService: await _seedSettingsService(), ); await container.read(groupsProvider.notifier).refresh(); await tester.pump(const Duration(milliseconds: 300)); expect(find.text('Kitchen'), findsOneWidget); await tester.drag(find.text('Kitchen'), const Offset(-500, 0)); await tester.pumpAndSettle(); expect(find.text('Удалить группу?'), findsOneWidget); await tester.tap(find.text('Удалить')); await tester.pumpAndSettle(); expect(api.deletedGroupId, 'kitchen'); expect(find.text('Kitchen'), findsNothing); }); testWidgets('group card toggles power and creates 4 hour timer', ( tester, ) async { final api = FakeIgnisApi(tasksData: {'tasks': []}); await pumpTestApp( tester, api: api, child: Scaffold( body: GroupCard( group: const IgnisGroup( id: 'hall', name: 'Hall', state: IgnisGroupState(isOn: true, brightness: 50), ), ), ), ); await tester.tap(find.byIcon(Icons.timer)); await tester.pumpAndSettle(); expect(api.controlledGroupId, 'hall'); expect(api.controlGroupParams, containsPair('state', true)); expect(api.scheduledOnceParams, containsPair('target_id', 'hall')); expect(api.scheduledOnceParams, containsPair('hours_from_now', 4)); await tester.tap(find.byType(Switch)); await tester.pumpAndSettle(); expect(api.controlGroupParams, containsPair('state', false)); }); testWidgets('group card loads scenes and applies selected scene', ( tester, ) async { final api = FakeIgnisApi(scenesData: {'party': 'Party'}); await pumpTestApp( tester, api: api, child: Scaffold( body: GroupCard( group: const IgnisGroup( id: 'hall', name: 'Hall', state: IgnisGroupState(isOn: true), ), ), ), ); await tester.tap(find.text('Сцена')); await tester.pump(); await tester.pump(const Duration(milliseconds: 100)); expect(find.text('Party'), findsOneWidget); await tester.tap(find.text('Party')); await tester.pumpAndSettle(); expect(api.controlledGroupId, 'hall'); expect(api.controlGroupParams, containsPair('scene', 'party')); }); testWidgets('group card shows retry when scenes fail to load', ( tester, ) async { final api = FakeIgnisApi(scenesData: {'party': 'Party'}); api.scenesError = DioException( requestOptions: RequestOptions(path: '/devices/scenes'), type: DioExceptionType.connectionError, message: 'No route to host', ); await pumpTestApp( tester, api: api, child: Scaffold( body: GroupCard( group: const IgnisGroup( id: 'hall', name: 'Hall', state: IgnisGroupState(isOn: true), ), ), ), ); await tester.tap(find.text('Сцена')); await tester.pump(); await tester.pump(const Duration(milliseconds: 100)); expect(find.text('Не удалось загрузить сцены'), findsOneWidget); api.scenesError = null; await tester.tap(find.text('Повторить')); await tester.pumpAndSettle(); expect(find.text('Party'), findsOneWidget); }); } Future _pumpRemoteScreen( WidgetTester tester, { required FakeIgnisApi api, required SettingsService settingsService, }) async { final container = createTestContainer(api, settingsService: settingsService); await container.read(currentHomeProvider.notifier).load(); await tester.pumpWidget( UncontrolledProviderScope( container: container, child: const MaterialApp(home: RemoteScreen()), ), ); await tester.pump(); await tester.pump(const Duration(milliseconds: 100)); return container; } Future _seedSettingsService() async { SharedPreferences.setMockInitialValues({ 'ignis_homes': '[{"id":"home-1","name":"Home 1","url":"https://one.example","geofenceEnabled":false}]', 'ignis_current_home_id': 'home-1', }); final settingsService = SettingsService( credentialsStorage: InMemoryCredentialsStorage(), ); await settingsService.setHomeApiKey('home-1', 'key-1'); return settingsService; }