import 'package:flutter_test/flutter_test.dart'; 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/semester.dart'; import 'package:hepan_everyday/domain/school_term.dart'; import 'package:hepan_everyday/domain/exam_schedule.dart'; import 'package:hepan_everyday/domain/course.dart'; import 'package:hepan_everyday/data/json_schedule_import.dart'; import '../support/fakes.dart'; import '../domain/schedule_engine_test.dart' show course; void main() { test('term labels and both query URLs follow the school numbering', () { expect(SchoolTerm(73).label, '2026-2027年秋'); expect(SchoolTerm(72).label, '2025-2026年春'); expect(SchoolTerm(71).label, '2025-2026年秋'); expect(SchoolTerm(74).label, '2026-2027年春'); expect(SchoolTerm(75).label, '2027-2028年秋'); expect(SchoolTerm(73).timetableUri.path, '/pyxx/pygl/xskbcx/index/73'); expect(SchoolTerm(72).examUri.queryParameters, {'xq': '72'}); }); test('selected school term survives settings copies and restart', () { final saved = AppSnapshot(schoolTerm: SchoolTerm(72)); final loaded = AppSnapshot.fromJson( saved .copyWith(settings: saved.settings.copyWith(reminderMinutes: 30)) .toJson(), ); expect(loaded.schoolTerm, SchoolTerm(72)); final legacy = saved.toJson()..remove('schoolTerm'); expect(AppSnapshot.fromJson(legacy).schoolTerm, isNull); expect(() => SchoolTerm(0), throwsArgumentError); }); test( 'previous app saves recover the actual HTML term for subsequent refresh', () { final legacy = AppSnapshot( examSchedule: ExamSchedule( semesterLabel: '2026-2027秋', termId: '73', records: [], ), ).toJson()..remove('schoolTerm'); expect(AppSnapshot.fromJson(legacy).schoolTerm, SchoolTerm(73)); }, ); for (final exams in [false, true]) { test( 'successful new term ${exams ? 'exam' : 'course'} import isolates old dates and data', () async { final old = AppSnapshot( schoolTerm: SchoolTerm(73), courses: [course()], examSchedule: ExamSchedule( semesterLabel: 'old', termId: '73', records: [], ), settings: AppSettings( semester: Semester(startDate: DateTime(2026, 9, 7), totalWeeks: 20), ), ); final store = MemoryStore(old); final c = AppController( store, FakeNotifications(), FakeCalendar(), () => DateTime(2026, 10, 13), ); await c.initialize(); if (exams) { await c.applyExams( ExamSchedule( semesterLabel: SchoolTerm(72).label, termId: '72', records: [], ), ); expect(c.snapshot.courses, isEmpty); } else { await c.applyImport( ImportDraft( courses: [course()], explicitlyEmpty: false, source: CourseSource.file, parserVersion: 'test', schoolTerm: SchoolTerm(72), ), confirmedEmpty: false, ); expect(c.snapshot.examSchedule, isNull); } expect(c.lastError, isNull); expect(c.snapshot.schoolTerm, SchoolTerm(72)); expect(c.snapshot.settings.semester, isNull); c.dispose(); }, ); } test('same-term refresh keeps date and exams; failed term change keeps all old data', () async { final old = AppSnapshot( schoolTerm: SchoolTerm(73), courses: [course()], examSchedule: ExamSchedule( semesterLabel: 'old', termId: '73', records: [], ), settings: AppSettings( semester: Semester(startDate: DateTime(2026, 9, 7), totalWeeks: 20), ), ); final store = MemoryStore(old); final c = AppController( store, FakeNotifications(), FakeCalendar(), () => DateTime(2026, 10, 13), ); await c.initialize(); ImportDraft draft(int id) => ImportDraft( courses: [course()], explicitlyEmpty: false, source: CourseSource.file, parserVersion: 'test', schoolTerm: SchoolTerm(id), ); await c.applyImport(draft(73), confirmedEmpty: false); expect(c.snapshot.settings.semester, old.settings.semester); expect(c.snapshot.examSchedule, old.examSchedule); store.failSave = true; await c.applyImport(draft(72), confirmedEmpty: false); expect(c.lastError, isNotNull); expect(c.snapshot.schoolTerm, SchoolTerm(73)); expect(c.snapshot.settings.semester, old.settings.semester); expect(c.snapshot.examSchedule, old.examSchedule); c.dispose(); }); }