import 'package:flutter_test/flutter_test.dart'; import 'package:marianum_mobile/api/marianumconnect/queries/timetable_get_element_week/timetable_element_type.dart'; import 'package:marianum_mobile/api/marianumconnect/queries/timetable_get_holidays/timetable_get_holidays_response.dart'; import 'package:marianum_mobile/api/marianumconnect/queries/timetable_get_rooms/timetable_get_rooms_response.dart'; import 'package:marianum_mobile/api/marianumconnect/queries/timetable_get_subjects/timetable_get_subjects_response.dart'; import 'package:marianum_mobile/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart'; import 'package:marianum_mobile/state/app/modules/capabilities/bloc/capabilities_state.dart'; import 'package:marianum_mobile/state/app/modules/timetable/bloc/timetable_state.dart'; import 'package:marianum_mobile/state/app/modules/timetable/policy/timetable_policy.dart'; import 'package:marianum_mobile/state/app/modules/timetable/subject/timetable_subject.dart'; ElementTimetable _element(TimetableElementType type, [int id = 7]) => ElementTimetable((type: type, id: id, label: 'X')); void main() { group('TimetableSubject', () { test('own plan keeps the legacy storage slot and persists', () { const own = OwnTimetable(); expect(own.storageId, ''); expect(own.persistent, isTrue); expect(own.supportsCustomEvents, isTrue); }); test('element plans get distinct slots and do not persist', () { final room = _element(TimetableElementType.room, 1); final teacher = _element(TimetableElementType.teacher, 1); expect(room.storageId, isNot(teacher.storageId)); expect(room.storageId, isNot(const OwnTimetable().storageId)); expect(room.persistent, isFalse); expect(room.supportsCustomEvents, isFalse); }); test('element equality ignores the display label', () { final a = ElementTimetable(( type: TimetableElementType.room, id: 3, label: 'A', )); final b = ElementTimetable(( type: TimetableElementType.room, id: 3, label: 'B', )); expect(a, b); expect(a.hashCode, b.hashCode); expect(a, isNot(_element(TimetableElementType.schoolClass, 3))); }); }); group('TimetablePolicy', () { const student = CapabilitiesState(userType: 'STUDENT'); const teacher = CapabilitiesState( userType: 'TEACHER', viewForeignTimetables: true, ); test('own plan offers custom events and subject colours', () { final p = TimetablePolicy.resolve( subject: const OwnTimetable(), capabilities: student, ); expect(p.canManageCustomEvents, isTrue); expect(p.canEditSubjectColors, isTrue); expect(p.canOpenForeign, isFalse); expect(p.showClassInsteadOfTeacher, isFalse); }); test('own teacher plan shows classes and may open foreign plans', () { final p = TimetablePolicy.resolve( subject: const OwnTimetable(), capabilities: teacher, ); expect(p.showClassInsteadOfTeacher, isTrue); expect(p.canOpenForeign, isTrue); }); test('foreign plans are read-only', () { final p = TimetablePolicy.resolve( subject: _element(TimetableElementType.room), capabilities: teacher, ); expect(p.canManageCustomEvents, isFalse); expect(p.canEditSubjectColors, isFalse); expect(p.canOpenForeign, isTrue); expect(p.showClassInsteadOfTeacher, isFalse); }); test('class labels follow the viewed element, not the viewer', () { expect( TimetablePolicy.resolve( subject: _element(TimetableElementType.teacher), capabilities: student, ).showClassInsteadOfTeacher, isTrue, ); expect( TimetablePolicy.resolve( subject: _element(TimetableElementType.student), capabilities: teacher, ).showClassInsteadOfTeacher, isFalse, ); }); }); group('TimetableState.isReady', () { final empty = TimetableState( startDate: DateTime(2026, 9, 14), endDate: DateTime(2026, 9, 18), ); final withReference = empty.copyWith( rooms: TimetableGetRoomsResponse(result: const []), subjects: TimetableGetSubjectsResponse(result: const []), schoolHolidays: TimetableGetHolidaysResponse(result: const []), ); test('is false without reference data', () { expect(empty.isReady(needsCustomEvents: false), isFalse); }); test('only waits for custom events when the subject needs them', () { expect(withReference.isReady(needsCustomEvents: false), isTrue); expect(withReference.isReady(needsCustomEvents: true), isFalse); expect( withReference .copyWith(customEvents: GetCustomTimetableEventResponse(const [])) .isReady(needsCustomEvents: true), isTrue, ); }); }); }