import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:timezone/data/latest.dart' as tzdata; import 'package:webview_flutter_platform_interface/webview_flutter_platform_interface.dart'; import 'package:hepan_everyday/app/hepan_app.dart'; import 'package:hepan_everyday/app/app_controller.dart'; import 'package:hepan_everyday/domain/app_snapshot.dart'; import 'package:hepan_everyday/domain/school_identity.dart'; import 'package:hepan_everyday/domain/exam_schedule.dart'; import 'package:hepan_everyday/domain/undergraduate_exam_query.dart'; import 'package:hepan_everyday/features/school/school_session.dart'; import 'package:hepan_everyday/features/school/undergraduate/session.dart'; import '../support/fakes.dart'; import '../support/fake_school_web.dart'; void main() { setUpAll(tzdata.initializeTimeZones); testWidgets( 'undergraduate selection persists and enters its manual workflow', (tester) async { final platform = FakeSchoolPlatform(); WebViewPlatform.instance = platform; final store = MemoryStore(), controller = AppController( MemoryStore(), FakeNotifications(), FakeCalendar(), () => DateTime.utc(2026, 10, 14), ); final under = UndergraduateSession(); addTearDown(controller.dispose); addTearDown(under.dispose); await controller.initialize(); await tester.pumpWidget( HepanApp( controller: controller, undergraduate: under, undergraduateBuilder: () => const Scaffold(body: Text('本科网页')), ), ); await tester.pumpAndSettle(); expect(platform.controllerCreations, 0); await tester.tap(find.bySemanticsLabel('个人设置')); await tester.pumpAndSettle(); await tester.tap(find.widgetWithText(FilledButton, '选择学校')); await tester.pumpAndSettle(); expect(find.text('电子科技大学(研究生)'), findsOneWidget); expect(find.text('电子科技大学(本科生)'), findsOneWidget); await tester.tap(find.text('电子科技大学(本科生)')); await tester.pumpAndSettle(); expect(find.text('本科网页'), findsOneWidget); expect( controller.snapshot.selectedSchool, SupportedSchool.uestcUndergraduate, ); expect(platform.web.loads, ['https://eams.uestc.edu.cn/eams/']); expect(under.requestingExams, isFalse); store.value = AppSnapshot.fromJson(controller.snapshot.toJson()); expect(store.value!.selectedSchool, SupportedSchool.uestcUndergraduate); }, ); testWidgets('failed school preference does not create a browser', ( tester, ) async { final platform = FakeSchoolPlatform(); WebViewPlatform.instance = platform; final store = MemoryStore(), under = UndergraduateSession(); final controller = AppController( store, FakeNotifications(), FakeCalendar(), DateTime.now, ); addTearDown(controller.dispose); addTearDown(under.dispose); await controller.initialize(); store.failSave = true; await tester.pumpWidget( HepanApp( controller: controller, undergraduate: under, undergraduateBuilder: () => const Scaffold(body: Text('本科网页')), ), ); await tester.pumpAndSettle(); await tester.tap(find.bySemanticsLabel('个人设置')); await tester.pumpAndSettle(); await tester.tap(find.widgetWithText(FilledButton, '选择学校')); await tester.pumpAndSettle(); await tester.tap(find.text('电子科技大学(本科生)')); await tester.pumpAndSettle(); expect(platform.controllerCreations, 0); expect(controller.snapshot.selectedSchool, isNull); expect(find.text('本科网页'), findsNothing); }); testWidgets('exam refresh reuses the successful undergraduate query', ( tester, ) async { final platform = FakeSchoolPlatform(); WebViewPlatform.instance = platform; final under = UndergraduateSession(); final store = MemoryStore( AppSnapshot( selectedSchool: SupportedSchool.uestcUndergraduate, examSchedule: ExamSchedule( semesterLabel: '2025-2026第二学期', termId: '503', school: SupportedSchool.uestcUndergraduate, requestedExamTypes: {1, 2}, records: [], ), ), ); final controller = AppController( store, FakeNotifications(), FakeCalendar(), DateTime.now, ); addTearDown(controller.dispose); addTearDown(under.dispose); await controller.initialize(); await tester.pumpWidget( HepanApp( controller: controller, undergraduate: under, undergraduateBuilder: () => const Scaffold(body: Text('本科网页')), ), ); await tester.pumpAndSettle(); await tester.tap(find.bySemanticsLabel('考试')); await tester.pumpAndSettle(); await tester.tap(find.byTooltip('从学校更新考试')); await tester.pumpAndSettle(); expect(find.text('本科网页'), findsOneWidget); expect(under.query!.term.id, 503); expect(under.query!.types, { UndergraduateExamType.finalExam, UndergraduateExamType.midterm, }); }); testWidgets('query option cancellation starts no login', (tester) async { final platform = FakeSchoolPlatform(); WebViewPlatform.instance = platform; final under = UndergraduateSession(); final controller = AppController( MemoryStore( AppSnapshot(selectedSchool: SupportedSchool.uestcUndergraduate), ), FakeNotifications(), FakeCalendar(), DateTime.now, ); addTearDown(controller.dispose); addTearDown(under.dispose); await controller.initialize(); await tester.pumpWidget( HepanApp( controller: controller, undergraduate: under, undergraduateBuilder: () => const Scaffold(body: Text('本科网页')), ), ); await tester.pumpAndSettle(); await tester.tap(find.bySemanticsLabel('考试')); await tester.pumpAndSettle(); await tester.tap(find.byTooltip('查询选项')); await tester.pumpAndSettle(); await tester.tap(find.text('取消')); await tester.pumpAndSettle(); expect(platform.controllerCreations, 0); }); testWidgets( 'shared logout invalidates both schools without creating another browser', (tester) async { final platform = FakeSchoolPlatform(); WebViewPlatform.instance = platform; final under = UndergraduateSession(), grad = SchoolSession(); await grad.openTimetable(); await under.openTimetable(); final controller = AppController( MemoryStore( AppSnapshot(selectedSchool: SupportedSchool.uestcUndergraduate), ), FakeNotifications(), FakeCalendar(), DateTime.now, ); addTearDown(controller.dispose); addTearDown(under.dispose); addTearDown(grad.dispose); await controller.initialize(); await tester.pumpWidget( HepanApp(controller: controller, school: grad, undergraduate: under), ); await tester.pumpAndSettle(); await tester.tap(find.bySemanticsLabel('个人设置')); await tester.pumpAndSettle(); await tester.tap(find.text('退出学校登录')); await tester.pumpAndSettle(); await tester.tap(find.text('保留课表和考试')); await tester.pumpAndSettle(); expect(platform.controllerCreations, 2); expect(under.canLogout, isFalse); expect(grad.canLogout, isFalse); }, ); }