reworked home screen widgets to mirror the in-app timetable, hidden below android 12
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter_test/flutter_test.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_subjects/timetable_get_subjects_response.dart';
|
||||
import 'package:marianum_mobile/api/marianumconnect/queries/timetable_get_week/timetable_get_week_response.dart';
|
||||
import 'package:marianum_mobile/api/mhsl/custom_timetable_event/custom_timetable_event.dart';
|
||||
import 'package:marianum_mobile/api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart';
|
||||
@@ -33,6 +34,8 @@ McTimetableEntry _lesson({
|
||||
String? teacherName,
|
||||
String? teacherOrgname,
|
||||
String? substText,
|
||||
String lessonType = 'LESSON',
|
||||
List<String> classNames = const [],
|
||||
}) => McTimetableEntry(
|
||||
id: 1,
|
||||
date: date,
|
||||
@@ -50,8 +53,8 @@ McTimetableEntry _lesson({
|
||||
]
|
||||
: const [],
|
||||
rooms: room != null ? [room] : const [],
|
||||
classNames: const [],
|
||||
lessonType: 'LESSON',
|
||||
classNames: classNames,
|
||||
lessonType: lessonType,
|
||||
status: status,
|
||||
substitutionText: substText,
|
||||
lessonText: null,
|
||||
@@ -117,8 +120,18 @@ void main() {
|
||||
|
||||
test('only includes lessons on the anchor day', () {
|
||||
final lessons = [
|
||||
_lesson(date: DateTime(2026, 5, 6), startHhmm: 800, endHhmm: 845, subjectName: 'MA'),
|
||||
_lesson(date: DateTime(2026, 5, 7), startHhmm: 800, endHhmm: 845, subjectName: 'EN'),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 6),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'MA',
|
||||
),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 7),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'EN',
|
||||
),
|
||||
];
|
||||
final data = WidgetDataMapper.buildDayData(
|
||||
now: now,
|
||||
@@ -164,14 +177,11 @@ void main() {
|
||||
rooms: null,
|
||||
holidays: null,
|
||||
);
|
||||
expect(
|
||||
data.lessons.map((l) => l.status).toList(),
|
||||
[
|
||||
WidgetLessonStatus.cancelled,
|
||||
WidgetLessonStatus.irregular,
|
||||
WidgetLessonStatus.teacherChanged,
|
||||
],
|
||||
);
|
||||
expect(data.lessons.map((l) => l.status).toList(), [
|
||||
WidgetLessonStatus.cancelled,
|
||||
WidgetLessonStatus.irregular,
|
||||
WidgetLessonStatus.teacherChanged,
|
||||
]);
|
||||
});
|
||||
|
||||
test('marks day as holiday when in holiday range', () {
|
||||
@@ -224,10 +234,11 @@ void main() {
|
||||
rooms: null,
|
||||
holidays: null,
|
||||
);
|
||||
expect(
|
||||
data.lessons.map((l) => l.subjectShort).toList(),
|
||||
['MA', 'EN', 'BIO'],
|
||||
);
|
||||
expect(data.lessons.map((l) => l.subjectShort).toList(), [
|
||||
'MA',
|
||||
'EN',
|
||||
'BIO',
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -236,9 +247,24 @@ void main() {
|
||||
|
||||
test('long event bumps every regular lesson it covers', () {
|
||||
final lessons = [
|
||||
_lesson(date: DateTime(2026, 5, 6), startHhmm: 800, endHhmm: 845, subjectName: 'MA'),
|
||||
_lesson(date: DateTime(2026, 5, 6), startHhmm: 900, endHhmm: 945, subjectName: 'EN'),
|
||||
_lesson(date: DateTime(2026, 5, 6), startHhmm: 1000, endHhmm: 1045, subjectName: 'BIO'),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 6),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'MA',
|
||||
),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 6),
|
||||
startHhmm: 900,
|
||||
endHhmm: 945,
|
||||
subjectName: 'EN',
|
||||
),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 6),
|
||||
startHhmm: 1000,
|
||||
endHhmm: 1045,
|
||||
subjectName: 'BIO',
|
||||
),
|
||||
];
|
||||
final events = GetCustomTimetableEventResponse([
|
||||
_event(
|
||||
@@ -264,8 +290,18 @@ void main() {
|
||||
|
||||
test('event + same-slot duplicate regular: kept lesson shows +2', () {
|
||||
final lessons = [
|
||||
_lesson(date: DateTime(2026, 5, 6), startHhmm: 900, endHhmm: 945, subjectName: 'EN'),
|
||||
_lesson(date: DateTime(2026, 5, 6), startHhmm: 900, endHhmm: 945, subjectName: 'MA'),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 6),
|
||||
startHhmm: 900,
|
||||
endHhmm: 945,
|
||||
subjectName: 'EN',
|
||||
),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 6),
|
||||
startHhmm: 900,
|
||||
endHhmm: 945,
|
||||
subjectName: 'MA',
|
||||
),
|
||||
];
|
||||
final events = GetCustomTimetableEventResponse([
|
||||
_event(
|
||||
@@ -315,7 +351,12 @@ void main() {
|
||||
|
||||
test('two events covering the same regular lesson bump it twice', () {
|
||||
final lessons = [
|
||||
_lesson(date: DateTime(2026, 5, 6), startHhmm: 900, endHhmm: 945, subjectName: 'EN'),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 6),
|
||||
startHhmm: 900,
|
||||
endHhmm: 945,
|
||||
subjectName: 'EN',
|
||||
),
|
||||
];
|
||||
final events = GetCustomTimetableEventResponse([
|
||||
_event(
|
||||
@@ -361,16 +402,198 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('teacher plan', () {
|
||||
final now = DateTime(2026, 5, 6, 7);
|
||||
final day = DateTime(2026, 5, 6);
|
||||
|
||||
WidgetLesson single(McTimetableEntry lesson) =>
|
||||
WidgetDataMapper.buildDayData(
|
||||
now: now,
|
||||
lessons: [lesson],
|
||||
subjects: null,
|
||||
rooms: null,
|
||||
holidays: null,
|
||||
showClassInsteadOfTeacher: true,
|
||||
).lessons.single;
|
||||
|
||||
test('shows the classes in place of the teacher', () {
|
||||
final lesson = single(
|
||||
_lesson(
|
||||
date: day,
|
||||
startHhmm: 755,
|
||||
endHhmm: 840,
|
||||
subjectName: 'MA',
|
||||
teacherName: 'MUE',
|
||||
teacherOrgname: 'KUN',
|
||||
classNames: const ['10a', '10b'],
|
||||
),
|
||||
);
|
||||
expect(lesson.teacher, '10a, 10b');
|
||||
expect(lesson.originalTeacher, isNull);
|
||||
});
|
||||
|
||||
test('leaves the slot empty for entries without a class', () {
|
||||
final lesson = single(
|
||||
_lesson(
|
||||
date: day,
|
||||
startHhmm: 920,
|
||||
endHhmm: 935,
|
||||
room: 'Hof',
|
||||
teacherName: 'MUE',
|
||||
lessonType: 'BREAK_SUPERVISION',
|
||||
),
|
||||
);
|
||||
expect(lesson.teacher, isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('tile color', () {
|
||||
final now = DateTime(2026, 5, 6, 7);
|
||||
final day = DateTime(2026, 5, 6);
|
||||
|
||||
WidgetLesson single(
|
||||
McTimetableEntry lesson, {
|
||||
TimetableGetSubjectsResponse? subjects,
|
||||
}) => WidgetDataMapper.buildDayData(
|
||||
now: now,
|
||||
lessons: [lesson],
|
||||
subjects: subjects,
|
||||
rooms: null,
|
||||
holidays: null,
|
||||
).lessons.single;
|
||||
|
||||
test('regular lesson without subject color uses the status red', () {
|
||||
final lesson = single(
|
||||
_lesson(
|
||||
date: day,
|
||||
startHhmm: 755,
|
||||
endHhmm: 840,
|
||||
subjectName: 'MA',
|
||||
teacherName: 'MUE',
|
||||
),
|
||||
);
|
||||
expect(lesson.color, '#993333');
|
||||
});
|
||||
|
||||
test('regular lesson takes the personal subject color', () {
|
||||
final lesson = single(
|
||||
_lesson(
|
||||
date: day,
|
||||
startHhmm: 755,
|
||||
endHhmm: 840,
|
||||
subjectName: 'MA',
|
||||
teacherName: 'MUE',
|
||||
),
|
||||
subjects: TimetableGetSubjectsResponse(
|
||||
result: [
|
||||
McSubject(
|
||||
id: 1,
|
||||
shortName: 'MA',
|
||||
longName: 'Mathematik',
|
||||
color: 'indigo',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
expect(lesson.color, '#5C6BC0');
|
||||
});
|
||||
|
||||
test('substitution keeps its signal color despite a subject color', () {
|
||||
final lesson = single(
|
||||
_lesson(
|
||||
date: day,
|
||||
startHhmm: 755,
|
||||
endHhmm: 840,
|
||||
status: 'IRREGULAR',
|
||||
subjectName: 'MA',
|
||||
teacherName: 'MUE',
|
||||
),
|
||||
subjects: TimetableGetSubjectsResponse(
|
||||
result: [
|
||||
McSubject(
|
||||
id: 1,
|
||||
shortName: 'MA',
|
||||
longName: 'Mathematik',
|
||||
color: 'indigo',
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
expect(lesson.status, WidgetLessonStatus.irregular);
|
||||
expect(lesson.color, '#8F19B3');
|
||||
});
|
||||
|
||||
test('supervision is classified as duty with room in the title', () {
|
||||
final lesson = single(
|
||||
_lesson(
|
||||
date: day,
|
||||
startHhmm: 920,
|
||||
endHhmm: 935,
|
||||
room: 'Hof',
|
||||
teacherName: 'MUE',
|
||||
lessonType: 'BREAK_SUPERVISION',
|
||||
),
|
||||
);
|
||||
expect(lesson.status, WidgetLessonStatus.duty);
|
||||
expect(lesson.subjectShort, 'Hof Aufsicht');
|
||||
expect(lesson.color, '#00796B');
|
||||
});
|
||||
|
||||
test('custom event resolves legacy color names', () {
|
||||
final data = WidgetDataMapper.buildDayData(
|
||||
now: now,
|
||||
lessons: const [],
|
||||
subjects: null,
|
||||
rooms: null,
|
||||
holidays: null,
|
||||
customEvents: GetCustomTimetableEventResponse([
|
||||
_event(
|
||||
id: 'e',
|
||||
title: 'Probe',
|
||||
start: DateTime(2026, 5, 6, 14),
|
||||
end: DateTime(2026, 5, 6, 15),
|
||||
),
|
||||
]),
|
||||
);
|
||||
expect(data.lessons.single.color, '#FFA000');
|
||||
});
|
||||
});
|
||||
|
||||
group('buildWeekData', () {
|
||||
final now = DateTime(2026, 5, 5, 10); // Tuesday
|
||||
|
||||
test('covers the 14-day window including next week', () {
|
||||
final lessons = [
|
||||
_lesson(date: DateTime(2026, 5, 4), startHhmm: 800, endHhmm: 845, subjectName: 'MO'),
|
||||
_lesson(date: DateTime(2026, 5, 6), startHhmm: 800, endHhmm: 845, subjectName: 'WE'),
|
||||
_lesson(date: DateTime(2026, 5, 8), startHhmm: 800, endHhmm: 845, subjectName: 'FR'),
|
||||
_lesson(date: DateTime(2026, 5, 11), startHhmm: 800, endHhmm: 845, subjectName: 'NEXT'),
|
||||
_lesson(date: DateTime(2026, 5, 18), startHhmm: 800, endHhmm: 845, subjectName: 'FAR'),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 4),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'MO',
|
||||
),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 6),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'WE',
|
||||
),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 8),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'FR',
|
||||
),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 11),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'NEXT',
|
||||
),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 18),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'FAR',
|
||||
),
|
||||
];
|
||||
final data = WidgetDataMapper.buildWeekData(
|
||||
now: now,
|
||||
@@ -380,10 +603,12 @@ void main() {
|
||||
holidays: null,
|
||||
);
|
||||
expect(data.anchorDate, DateTime(2026, 5, 4));
|
||||
expect(
|
||||
data.lessons.map((l) => l.subjectShort).toList(),
|
||||
['MO', 'WE', 'FR', 'NEXT'],
|
||||
);
|
||||
expect(data.lessons.map((l) => l.subjectShort).toList(), [
|
||||
'MO',
|
||||
'WE',
|
||||
'FR',
|
||||
'NEXT',
|
||||
]);
|
||||
});
|
||||
|
||||
test('window stays on the current week when the anchor rolls forward', () {
|
||||
@@ -391,8 +616,18 @@ void main() {
|
||||
// still starts at the current week's Monday so today stays available.
|
||||
final fridayEvening = DateTime(2026, 5, 8, 18);
|
||||
final lessons = [
|
||||
_lesson(date: DateTime(2026, 5, 8), startHhmm: 800, endHhmm: 845, subjectName: 'FR'),
|
||||
_lesson(date: DateTime(2026, 5, 11), startHhmm: 800, endHhmm: 845, subjectName: 'NEXT'),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 8),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'FR',
|
||||
),
|
||||
_lesson(
|
||||
date: DateTime(2026, 5, 11),
|
||||
startHhmm: 800,
|
||||
endHhmm: 845,
|
||||
subjectName: 'NEXT',
|
||||
),
|
||||
];
|
||||
final data = WidgetDataMapper.buildWeekData(
|
||||
now: fridayEvening,
|
||||
@@ -402,10 +637,7 @@ void main() {
|
||||
holidays: null,
|
||||
);
|
||||
expect(data.anchorDate, DateTime(2026, 5, 11));
|
||||
expect(
|
||||
data.lessons.map((l) => l.subjectShort).toList(),
|
||||
['FR', 'NEXT'],
|
||||
);
|
||||
expect(data.lessons.map((l) => l.subjectShort).toList(), ['FR', 'NEXT']);
|
||||
});
|
||||
|
||||
test('carries per-day holiday info in days', () {
|
||||
@@ -429,8 +661,10 @@ void main() {
|
||||
expect(data.days, hasLength(WidgetDataMapper.weekWindowDays));
|
||||
expect(data.days.first.date, DateTime(2026, 5, 4));
|
||||
expect(data.days.last.date, DateTime(2026, 5, 17));
|
||||
final holidayDays =
|
||||
data.days.where((d) => d.isHoliday).map((d) => d.date).toList();
|
||||
final holidayDays = data.days
|
||||
.where((d) => d.isHoliday)
|
||||
.map((d) => d.date)
|
||||
.toList();
|
||||
expect(holidayDays, [DateTime(2026, 5, 14), DateTime(2026, 5, 15)]);
|
||||
expect(
|
||||
data.days.firstWhere((d) => d.isHoliday).holidayName,
|
||||
|
||||
Reference in New Issue
Block a user