150 lines
5.4 KiB
Dart
150 lines
5.4 KiB
Dart
import 'dart:io';
|
|
|
|
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/data/file_snapshot_store.dart';
|
|
import 'package:hepan_everyday/domain/app_snapshot.dart';
|
|
import 'package:hepan_everyday/features/profile/settings_page.dart';
|
|
import 'package:hepan_everyday/features/timetable/week_grid.dart';
|
|
import 'package:hepan_everyday/domain/schedule_engine.dart';
|
|
|
|
import '../support/fakes.dart';
|
|
import '../domain/schedule_engine_test.dart' show course;
|
|
|
|
void main() {
|
|
setUpAll(tzdata.initializeTimeZones);
|
|
test('old saved preferences gain longer default and malformed length is rejected', () {
|
|
final old = AppSnapshot().toJson();
|
|
(old['settings'] as Map).remove('courseHeightScale');
|
|
expect(
|
|
(AppSnapshot.fromJson(old).toJson()['settings']
|
|
as Map)['courseHeightScale'],
|
|
1.25,
|
|
);
|
|
for (final value in [null, 'long', 0.5, 2.5, double.nan, double.infinity]) {
|
|
final json = AppSnapshot().toJson();
|
|
(json['settings'] as Map)['courseHeightScale'] = value;
|
|
expect(() => AppSnapshot.fromJson(json), throwsFormatException);
|
|
}
|
|
});
|
|
test('custom course length survives an actual disk reload', () async {
|
|
final directory = await Directory.systemTemp.createTemp(
|
|
'riverside-length-',
|
|
);
|
|
try {
|
|
final json = AppSnapshot().toJson();
|
|
(json['settings'] as Map)['courseHeightScale'] = 1.8;
|
|
await FileSnapshotStore(directory).save(AppSnapshot.fromJson(json));
|
|
final restored = await FileSnapshotStore(directory).load();
|
|
expect((restored!.toJson()['settings'] as Map)['courseHeightScale'], 1.8);
|
|
} finally {
|
|
await directory.delete(recursive: true);
|
|
}
|
|
});
|
|
testWidgets(
|
|
'length slider persists across reopening and increases equal-duration bars',
|
|
(tester) async {
|
|
final store = MemoryStore();
|
|
final term = Semester(startDate: DateTime(2026, 9, 7), totalWeeks: 1);
|
|
final json = AppSnapshot(
|
|
courses: [
|
|
course(id: 'A', day: 1, first: 3, last: 4),
|
|
course(id: 'B', day: 7, first: 3, last: 4),
|
|
],
|
|
).toJson();
|
|
(json['settings'] as Map)['semester'] = {
|
|
'startDate': '2026-09-07',
|
|
'totalWeeks': 1,
|
|
};
|
|
await store.save(AppSnapshot.fromJson(json));
|
|
final c = AppController(
|
|
store,
|
|
FakeNotifications(),
|
|
FakeCalendar(),
|
|
() => DateTime.utc(2026, 9, 7),
|
|
);
|
|
await c.initialize();
|
|
Future<double> barHeight(AppController controller) async {
|
|
final events = ScheduleEngine.occurrences(
|
|
controller.snapshot.courses,
|
|
term,
|
|
);
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: Scaffold(
|
|
body: SingleChildScrollView(
|
|
child: WeekGrid(
|
|
events: events,
|
|
monday: term.firstMonday,
|
|
today: term.startDate,
|
|
settings: controller.snapshot.settings,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
final a = tester.getRect(find.bySemanticsLabel('矩阵论,示例教师,示例教室').first),
|
|
b = tester.getRect(find.bySemanticsLabel('矩阵论,示例教师,示例教室').last);
|
|
expect(a.height, b.height);
|
|
expect(tester.getRect(find.text('周日')).right, lessThanOrEqualTo(800));
|
|
return a.height;
|
|
}
|
|
|
|
final before = await barHeight(c);
|
|
final alarmsBefore =
|
|
(c.notifications as FakeNotifications).reconciliations;
|
|
final calendarsBefore = (c.calendar as FakeCalendar).synchronizations;
|
|
await tester.pumpWidget(MaterialApp(home: SettingsPage(controller: c)));
|
|
await tester.pumpAndSettle();
|
|
final slider = find.byKey(const Key('course-height-slider'));
|
|
await tester.ensureVisible(slider);
|
|
await tester.pumpAndSettle();
|
|
final rect = tester.getRect(slider);
|
|
await tester.dragFrom(
|
|
Offset(rect.left + rect.width * 0.4, rect.center.dy),
|
|
Offset(rect.width * 0.4, 0),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
final value =
|
|
(c.snapshot.toJson()['settings'] as Map)['courseHeightScale']
|
|
as double;
|
|
expect(value, greaterThan(1.25));
|
|
expect(
|
|
(c.notifications as FakeNotifications).reconciliations,
|
|
alarmsBefore,
|
|
);
|
|
expect((c.calendar as FakeCalendar).synchronizations, calendarsBefore);
|
|
final restarted = AppController(
|
|
store,
|
|
FakeNotifications(),
|
|
FakeCalendar(),
|
|
() => DateTime.utc(2026, 9, 7),
|
|
);
|
|
await restarted.initialize();
|
|
expect(
|
|
(restarted.snapshot.toJson()['settings'] as Map)['courseHeightScale'],
|
|
value,
|
|
);
|
|
expect(await barHeight(restarted), greaterThan(before));
|
|
await tester.pumpWidget(
|
|
MaterialApp(home: SettingsPage(controller: restarted)),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
await tester.ensureVisible(find.text('恢复默认'));
|
|
await tester.tap(find.text('恢复默认'));
|
|
await tester.pumpAndSettle();
|
|
expect(
|
|
(restarted.snapshot.toJson()['settings'] as Map)['courseHeightScale'],
|
|
1.25,
|
|
);
|
|
expect(await barHeight(restarted), before);
|
|
expect(tester.takeException(), isNull);
|
|
c.dispose();
|
|
restarted.dispose();
|
|
},
|
|
);
|
|
}
|