Files
ignis_app/test/forms_widget_test.dart
2026-05-16 11:22:02 +07:00

287 lines
9.0 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:ignis_app/models/home_config.dart';
import 'package:ignis_app/screens/api_keys_screen.dart';
import 'package:ignis_app/screens/group_edit_screen.dart';
import 'package:ignis_app/screens/home_edit_screen.dart';
import 'package:ignis_app/screens/schedules_screen.dart';
import 'package:ignis_app/services/settings_service.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'test_support.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
testWidgets('schedules screen creates one-shot schedule for selected group', (
tester,
) async {
final api = FakeIgnisApi(
tasksData: {'tasks': <Object>[]},
groupsData: {
'bedroom': {'name': 'Спальня'},
},
);
await pumpTestApp(tester, child: const SchedulesScreen(), api: api);
await tester.pumpAndSettle();
await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();
await tester.tap(find.byType(DropdownButtonFormField<String>));
await tester.pumpAndSettle();
await tester.tap(find.text('Спальня').last);
await tester.pumpAndSettle();
final createScheduleButton = find.widgetWithText(
ElevatedButton,
'СОЗДАТЬ РАСПИСАНИЕ',
);
await tester.ensureVisible(createScheduleButton);
await tester.tap(createScheduleButton);
await tester.pumpAndSettle();
expect(api.scheduledOnceParams, isNotNull);
expect(api.scheduledOnceParams, containsPair('target_id', 'bedroom'));
expect(api.scheduledOnceParams, containsPair('is_group', true));
expect(api.scheduledOnceParams?['run_at'], isA<String>());
});
testWidgets('group edit screen auto-generates id and creates group', (
tester,
) async {
final api = FakeIgnisApi(
devicesData: {
'devices': [
{'mac': 'AA:BB', 'name': 'Лампа 1'},
],
},
groupsData: <Object>[],
);
await pumpTestApp(tester, child: const GroupEditScreen(), api: api);
await tester.pumpAndSettle();
await tester.enterText(
find.widgetWithText(TextFormField, 'Название группы'),
'Спальня родителей',
);
await tester.pump();
expect(find.text('spalnya-roditeley'), findsOneWidget);
await tester.tap(find.text('Лампа 1'));
await tester.pump();
await tester.tap(find.text('СОЗДАТЬ ГРУППУ'));
await tester.pumpAndSettle();
expect(api.createdGroupId, 'spalnya-roditeley');
expect(api.createdGroupName, 'Спальня родителей');
expect(api.createdGroupMacs, ['AA:BB']);
});
testWidgets('group edit screen shows backend rescan summary', (tester) async {
final api =
FakeIgnisApi(
devicesData: {
'devices': [
{'mac': 'AA:BB', 'name': 'Лампа 1'},
],
},
groupsData: <Object>[],
)
..rescanNetworkData = {
'status': 'ok',
'found': 3,
'added': 1,
'updated': 2,
'removed_offline': 1,
'pending_removal': 0,
'online': 3,
};
await pumpTestApp(tester, child: const GroupEditScreen(), api: api);
await tester.pumpAndSettle();
await tester.tap(find.byTooltip('Пересканировать сеть'));
await tester.pumpAndSettle();
expect(
find.text(
'Сканирование завершено: найдено 3, новых 1, обновлено 2, убрано 1',
),
findsOneWidget,
);
expect(api.rescanCalls, 1);
});
testWidgets('api keys screen validates and shows created key banner', (
tester,
) async {
final api = FakeIgnisApi(apiKeysData: {'keys': <Object>[]});
await pumpTestApp(tester, child: const ApiKeysScreen(), api: api);
await tester.pumpAndSettle();
await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();
await tester.tap(find.text('Создать'));
await tester.pumpAndSettle();
expect(find.text('Укажите имя ключа'), findsOneWidget);
await tester.enterText(
find.widgetWithText(TextFormField, 'Имя ключа'),
'Guest',
);
await tester.tap(find.text('Дать права администратора'));
await tester.pumpAndSettle();
await tester.tap(find.text('Создать'));
await tester.pumpAndSettle();
expect(api.createdApiKeyName, 'Guest');
expect(api.createdApiKeyIsAdmin, isTrue);
expect(find.textContaining('Новый ключ создан'), findsOneWidget);
expect(find.textContaining('Guest_token'), findsOneWidget);
});
testWidgets('home edit screen validates fields and saves normalized home', (
tester,
) async {
tester.view.physicalSize = const Size(800, 1400);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
SharedPreferences.setMockInitialValues({});
final settingsService = SettingsService(
credentialsStorage: InMemoryCredentialsStorage(),
);
final api = FakeIgnisApi(authData: {'is_admin': true, 'name': 'owner'});
await pumpTestApp(
tester,
api: api,
settingsService: settingsService,
child: Builder(
builder: (context) => Scaffold(
body: Center(
child: ElevatedButton(
onPressed: () => Navigator.of(
context,
).push(MaterialPageRoute(builder: (_) => const HomeEditScreen())),
child: const Text('open'),
),
),
),
),
);
await tester.tap(find.text('open'));
await tester.pumpAndSettle();
final saveHomeButton = find.widgetWithText(ElevatedButton, 'ДОБАВИТЬ');
await tester.tap(saveHomeButton);
await tester.pumpAndSettle();
expect(find.text('Укажите название дома'), findsOneWidget);
expect(find.text('Укажите адрес сервера'), findsOneWidget);
expect(find.text('Укажите API key'), findsOneWidget);
await tester.enterText(
find.widgetWithText(TextFormField, 'Название'),
'Квартира',
);
await tester.enterText(
find.widgetWithText(TextFormField, 'Адрес сервера'),
'ignis.akokos.ru/',
);
await tester.enterText(
find.widgetWithText(TextFormField, 'API Key'),
'secret-key',
);
await tester.enterText(
find.widgetWithText(TextFormField, 'Широта'),
'55.75',
);
await tester.enterText(
find.widgetWithText(TextFormField, 'Долгота'),
'37.61',
);
await tester.pump();
await tester.tap(saveHomeButton);
await tester.pumpAndSettle();
final homes = await settingsService.getHomes();
final savedHome = homes.single;
final savedApiKey = await settingsService.getHomeApiKey(savedHome.id);
expect(homes, hasLength(1));
expect(savedHome.name, 'Квартира');
expect(savedHome.url, 'https://ignis.akokos.ru');
expect(savedHome.latitude, 55.75);
expect(savedHome.longitude, 37.61);
expect(savedHome.geofenceEnabled, isFalse);
expect(
savedHome.geofenceRadiusMeters,
HomeConfig.defaultGeofenceRadiusMeters,
);
expect(savedApiKey, 'secret-key');
});
testWidgets('home edit screen keeps stored api key hidden on edit', (
tester,
) async {
SharedPreferences.setMockInitialValues({});
final settingsService = SettingsService(
credentialsStorage: InMemoryCredentialsStorage(),
);
final existingHome = HomeConfig(
id: 'home-1',
name: 'Квартира',
url: 'https://ignis.akokos.ru',
);
await settingsService.upsertHome(existingHome, apiKey: 'stored-secret');
final api = FakeIgnisApi(authData: {'is_admin': true, 'name': 'owner'});
await pumpTestApp(
tester,
api: api,
settingsService: settingsService,
child: HomeEditScreen(home: existingHome),
);
await tester.pumpAndSettle();
final keyField = tester.widget<EditableText>(
find.byWidgetPredicate(
(widget) => widget is EditableText && widget.obscureText,
),
);
expect(keyField.controller.text, isEmpty);
expect(
find.text('Сохранённый ключ хранится отдельно и не показывается в поле'),
findsOneWidget,
);
await tester.enterText(
find.widgetWithText(TextFormField, 'Адрес сервера'),
'ignis.example.com',
);
await tester.pump();
await tester.tap(find.widgetWithText(ElevatedButton, 'СОХРАНИТЬ'));
await tester.pumpAndSettle();
expect(api.validateCredentialsCalls, 1);
expect(api.validatedBaseUrl, 'https://ignis.example.com');
expect(api.validatedApiKey, 'stored-secret');
expect(
await settingsService.getHomeApiKey(existingHome.id),
'stored-secret',
);
});
}