feat: polish phase 7 forms and schedules
This commit is contained in:
37
test/group_form_logic_test.dart
Normal file
37
test/group_form_logic_test.dart
Normal file
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:ignis_app/features/groups/group_form_logic.dart';
|
||||
import 'package:ignis_app/models/ignis_group.dart';
|
||||
|
||||
void main() {
|
||||
test('slugify transliterates russian names into stable ids', () {
|
||||
expect(slugifyGroupId('Спальня родителей'), 'spalnya-roditeley');
|
||||
expect(slugifyGroupId(' Kitchen + Hall '), 'kitchen-hall');
|
||||
});
|
||||
|
||||
test('group id validation rejects duplicates and invalid chars', () {
|
||||
const existing = [IgnisGroup(id: 'bedroom', name: 'Спальня')];
|
||||
|
||||
expect(validateGroupId('', existing), 'Укажите ID группы');
|
||||
expect(
|
||||
validateGroupId('спальня', existing),
|
||||
'Только латиница, цифры, "-" и "_"',
|
||||
);
|
||||
expect(
|
||||
validateGroupId('bedroom', existing),
|
||||
'Группа с таким ID уже существует',
|
||||
);
|
||||
expect(validateGroupId('hall_way', existing), isNull);
|
||||
});
|
||||
|
||||
test('membership conflicts are detected by device ids', () {
|
||||
const groups = [
|
||||
IgnisGroup(id: 'bedroom', name: 'Спальня', macs: ['AA:BB', 'CC:DD']),
|
||||
IgnisGroup(id: 'kitchen', name: 'Кухня', macs: ['EE:FF']),
|
||||
];
|
||||
|
||||
final conflicts = findGroupMembershipConflicts({'EE:FF', '11:22'}, groups);
|
||||
|
||||
expect(conflicts, hasLength(1));
|
||||
expect(conflicts.single.id, 'kitchen');
|
||||
});
|
||||
}
|
||||
43
test/schedule_form_logic_test.dart
Normal file
43
test/schedule_form_logic_test.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:ignis_app/features/schedules/schedule_form_logic.dart';
|
||||
|
||||
void main() {
|
||||
test('default once time rounds forward and stays in future', () {
|
||||
final now = DateTime(2026, 5, 1, 10, 2);
|
||||
|
||||
final result = defaultOnceScheduleTime(now: now);
|
||||
|
||||
expect(result, DateTime(2026, 5, 1, 14, 5));
|
||||
expect(result.isAfter(now), isTrue);
|
||||
});
|
||||
|
||||
test('once validation rejects missing and past dates', () {
|
||||
final now = DateTime(2026, 5, 1, 10, 0);
|
||||
|
||||
expect(validateOnceSchedule(null, now: now), 'Выберите дату и время');
|
||||
expect(
|
||||
validateOnceSchedule(now.add(const Duration(seconds: 30)), now: now),
|
||||
'Нужна дата хотя бы на минуту вперёд',
|
||||
);
|
||||
expect(
|
||||
validateOnceSchedule(now.add(const Duration(minutes: 2)), now: now),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
|
||||
test('cron weekday serialization and descriptions stay human readable', () {
|
||||
expect(serializeCronWeekdays({0, 1, 2, 3, 4, 5, 6}), '*');
|
||||
expect(serializeCronWeekdays({1, 5, 0}), '0,1,5');
|
||||
expect(describeCronWeekdays({1, 2, 3, 4, 5}), 'Пн, Вт, Ср, Чт, Пт');
|
||||
expect(describeCronWeekdaysExpression('*'), 'каждый день');
|
||||
expect(describeCronWeekdaysExpression('1,5,0'), 'Пн, Пт, Вс');
|
||||
});
|
||||
|
||||
test('format runAt converts utc timestamp to local label', () {
|
||||
final label = formatRunAtLabel('2026-05-01T12:30:00Z');
|
||||
|
||||
expect(label, isNotEmpty);
|
||||
expect(label.contains('2026'), isTrue);
|
||||
expect(label.contains('12:30') || label.contains('19:30'), isTrue);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user