import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:timezone/data/latest.dart' as tzdata; import 'package:hepan_everyday/app/app_controller.dart'; import 'package:hepan_everyday/domain/app_snapshot.dart'; import 'package:hepan_everyday/domain/app_settings.dart'; import 'package:hepan_everyday/domain/school_identity.dart'; import 'package:hepan_everyday/domain/school_term.dart'; import 'package:hepan_everyday/domain/schedule_engine.dart'; import 'package:hepan_everyday/features/timetable/timetable_page.dart'; import 'package:hepan_everyday/features/timetable/week_grid.dart'; import 'package:hepan_everyday/services/local_notification_service.dart'; import 'package:hepan_everyday/services/android_calendar_service.dart'; import '../support/fakes.dart'; import '../domain/schedule_engine_test.dart' show course; import '../services/notification_service_test.dart' show AlarmGateway; import '../services/calendar_service_test.dart' show CalendarProvider; void main() { setUpAll(tzdata.initializeTimeZones); testWidgets('row count follows imported school across empty weeks and demo', ( tester, ) async { tester.view.physicalSize = const Size(360, 800); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final controller = AppController( MemoryStore(), FakeNotifications(), FakeCalendar(), () => DateTime.utc(2026, 9, 7), ); addTearDown(controller.dispose); final settings = AppSettings( semester: Semester(startDate: DateTime(2026, 9, 7), totalWeeks: 20), ); controller.snapshot = AppSnapshot( settings: settings, schoolTerm: SchoolTerm(523, school: SupportedSchool.uestcUndergraduate), courses: [ course(first: 9, last: 12, weeks: {2}), ], selectedSchool: SupportedSchool.uestcGraduate, ); await tester.pumpWidget( MaterialApp( home: Scaffold(body: TimetablePage(controller: controller)), ), ); await tester.pumpAndSettle(); expect(tester.widget(find.byType(WeekGrid)).periodCount, 12); expect( find.byWidgetPredicate( (w) => w is Text && w.data == '12' && w.style?.color == null, ), findsOneWidget, ); expect(tester.getRect(find.text('周日')).right, lessThanOrEqualTo(360)); await controller.showDemo(); await tester.pumpWidget( MaterialApp( home: Scaffold(body: TimetablePage(controller: controller)), ), ); await tester.pumpAndSettle(); expect(tester.widget(find.byType(WeekGrid)).periodCount, 11); }); test('period twelve drives reminders and calendar ending at 22:45', () async { final term = Semester(startDate: DateTime(2026, 9, 7), totalWeeks: 1), events = ScheduleEngine.occurrences([ course(day: 1, first: 12, last: 12), ], Semester(startDate: DateTime(2026, 9, 7), totalWeeks: 1)); expect(events.single.start.hour, 22); expect(events.single.end.hour, 22); expect(events.single.end.minute, 45); final night = ScheduleEngine.occurrences([ course(day: 1, first: 9, last: 12), ], term).single; expect(night.start.hour, 19); expect(night.start.minute, 30); expect(night.end.minute, 45); final gateway = AlarmGateway(); await LocalNotificationService(gateway).reconcile( events, AppSettings(semester: term, reminderOffsets: [5, 15]), DateTime.utc(2026, 9, 6), ); expect( gateway.alarms.values .map((a) => '${a.fireAt.hour}:${a.fireAt.minute}') .toSet(), {'21:55', '21:45'}, ); final provider = CalendarProvider(); await AndroidCalendarService(provider).sync(events, 'local', {}); expect( provider.drafts.values.single.endMs, events.single.end.millisecondsSinceEpoch, ); }); }