feat: deliver Riverside Schedule seven-day grid and school HTML import
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:timezone/data/latest.dart' as tzdata;
|
||||
import 'package:hepan_everyday/features/timetable/week_grid.dart';
|
||||
import 'package:hepan_everyday/domain/schedule_engine.dart';
|
||||
import 'package:hepan_everyday/domain/app_settings.dart';
|
||||
|
||||
void main() {
|
||||
setUpAll(tzdata.initializeTimeZones);
|
||||
for (final scale in [1.0, 2.0]) {
|
||||
testWidgets(
|
||||
'seven days and full teacher/room fit equal duration cards at scale $scale',
|
||||
(tester) async {
|
||||
tester.view.physicalSize = const Size(360, 800);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
final term = Semester(startDate: DateTime(2026, 9, 7), totalWeeks: 1);
|
||||
Course c(String id, int day, String teacher, String room) => Course(
|
||||
id: id,
|
||||
name: '很长的课程名称应当可以省略但点击后显示完整课程名称$id',
|
||||
teacher: teacher,
|
||||
room: room,
|
||||
weekday: day,
|
||||
startPeriod: 3,
|
||||
endPeriod: 4,
|
||||
weeks: {1},
|
||||
source: CourseSource.file,
|
||||
);
|
||||
final events = ScheduleEngine.occurrences([
|
||||
c('a', 1, '张晓玲、杨拥军', '品学楼B102'),
|
||||
c('b', 7, '李老师', '立人楼教学实验中心B412'),
|
||||
], term);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: MediaQueryData(
|
||||
size: const Size(360, 800),
|
||||
textScaler: TextScaler.linear(scale),
|
||||
),
|
||||
child: Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: WeekGrid(
|
||||
events: events,
|
||||
monday: term.firstMonday,
|
||||
today: term.startDate,
|
||||
settings: const AppSettings(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(tester.getRect(find.text('周一')).left, greaterThanOrEqualTo(0));
|
||||
expect(tester.getRect(find.text('周日')).right, lessThanOrEqualTo(360));
|
||||
final first = tester.getRect(
|
||||
find.bySemanticsLabel('${events.first.course.name},张晓玲、杨拥军,品学楼B102'),
|
||||
);
|
||||
final last = tester.getRect(
|
||||
find.bySemanticsLabel('${events.last.course.name},李老师,立人楼教学实验中心B412'),
|
||||
);
|
||||
expect(first.height, last.height);
|
||||
for (final text in ['张晓玲、杨拥军', '品学楼B102', '李老师', '立人楼教学实验中心B412']) {
|
||||
final finder = find.text(text);
|
||||
final widget = tester.widget<Text>(finder);
|
||||
expect(widget.maxLines, isNull);
|
||||
expect(widget.overflow, isNot(TextOverflow.ellipsis));
|
||||
final paragraph = tester.renderObject<RenderParagraph>(finder);
|
||||
expect(paragraph.didExceedMaxLines, isFalse);
|
||||
final rect = tester.getRect(finder);
|
||||
final card = text == '张晓玲、杨拥军' || text == '品学楼B102' ? first : last;
|
||||
expect(rect.bottom, lessThanOrEqualTo(card.bottom));
|
||||
expect(rect.left, greaterThanOrEqualTo(card.left));
|
||||
expect(rect.right, lessThanOrEqualTo(card.right));
|
||||
}
|
||||
expect(tester.takeException(), isNull);
|
||||
await tester.ensureVisible(find.text(events.first.course.name));
|
||||
await tester.tap(find.text(events.first.course.name));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text(events.first.course.name), findsNWidgets(2));
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:timezone/data/latest.dart' as tzdata;
|
||||
import 'package:hepan_everyday/features/timetable/week_grid.dart';
|
||||
import 'package:hepan_everyday/domain/schedule_engine.dart';
|
||||
import 'package:hepan_everyday/domain/app_settings.dart';
|
||||
|
||||
void main() {
|
||||
setUpAll(tzdata.initializeTimeZones);
|
||||
testWidgets(
|
||||
'conflicts keep readable full-width metadata without covering courses',
|
||||
(tester) async {
|
||||
tester.view.physicalSize = const Size(320, 800);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
final term = Semester(startDate: DateTime(2026, 9, 7), totalWeeks: 1);
|
||||
final courses = List.generate(
|
||||
5,
|
||||
(i) => Course(
|
||||
id: '$i',
|
||||
name: '并行课程$i',
|
||||
teacher: '示例教师$i',
|
||||
room: '示例教学楼B10$i',
|
||||
weekday: 1,
|
||||
startPeriod: 1,
|
||||
endPeriod: 2,
|
||||
weeks: {1},
|
||||
source: CourseSource.file,
|
||||
),
|
||||
);
|
||||
final events = ScheduleEngine.occurrences(courses, term);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: MediaQueryData(textScaler: TextScaler.linear(2)),
|
||||
child: Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: WeekGrid(
|
||||
events: events,
|
||||
monday: term.firstMonday,
|
||||
today: term.startDate,
|
||||
settings: const AppSettings(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('冲突列表'), findsOneWidget);
|
||||
expect(find.text('1–2节'), findsNWidgets(5));
|
||||
final rects = <Rect>[];
|
||||
for (final c in courses) {
|
||||
final card = tester.getRect(
|
||||
find.bySemanticsLabel('${c.name},${c.teacher},${c.room}'),
|
||||
);
|
||||
rects.add(card);
|
||||
expect(card.width, greaterThan(30));
|
||||
for (final text in [c.teacher, c.room]) {
|
||||
final finder = find.text(text);
|
||||
final paragraph = tester.renderObject<RenderParagraph>(finder);
|
||||
for (final box in paragraph.getBoxesForSelection(
|
||||
TextSelection(baseOffset: 0, extentOffset: text.length),
|
||||
)) {
|
||||
expect(box.right, lessThanOrEqualTo(paragraph.size.width + 0.01));
|
||||
}
|
||||
expect(tester.getRect(finder).bottom, lessThanOrEqualTo(card.bottom));
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < rects.length; i++) {
|
||||
for (var j = i + 1; j < rects.length; j++) {
|
||||
expect(
|
||||
rects[i].intersect(rects[j]).height,
|
||||
lessThanOrEqualTo(0.01),
|
||||
reason: '${rects[i]} / ${rects[j]}',
|
||||
);
|
||||
}
|
||||
}
|
||||
expect(tester.takeException(), isNull);
|
||||
},
|
||||
);
|
||||
testWidgets(
|
||||
'a lunch-gap arrangement has the same length as another two-lesson card',
|
||||
(tester) async {
|
||||
final term = Semester(startDate: DateTime(2026, 9, 7), totalWeeks: 1);
|
||||
Course c(String id, int day, int first) => Course(
|
||||
id: id,
|
||||
name: id,
|
||||
teacher: '教师',
|
||||
room: '教室',
|
||||
weekday: day,
|
||||
startPeriod: first,
|
||||
endPeriod: first + 1,
|
||||
weeks: {1},
|
||||
source: CourseSource.file,
|
||||
);
|
||||
final events = ScheduleEngine.occurrences([
|
||||
c('A', 1, 3),
|
||||
c('B', 2, 4),
|
||||
], term);
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: SingleChildScrollView(
|
||||
child: WeekGrid(
|
||||
events: events,
|
||||
monday: term.firstMonday,
|
||||
today: term.startDate,
|
||||
settings: const AppSettings(),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
tester.getRect(find.bySemanticsLabel('A,教师,教室')).height,
|
||||
tester.getRect(find.bySemanticsLabel('B,教师,教室')).height,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:hepan_everyday/widgets/glass_dock.dart';
|
||||
import 'package:hepan_everyday/app/hepan_app.dart';
|
||||
import 'package:timezone/data/latest.dart' as tzdata;
|
||||
|
||||
import 'navigation_test.dart' show uiController;
|
||||
|
||||
void main() {
|
||||
setUpAll(tzdata.initializeTimeZones);
|
||||
testWidgets(
|
||||
'glass cursor drags in both directions, taps work, body swipes do not navigate',
|
||||
(tester) async {
|
||||
final c = uiController();
|
||||
await c.initialize();
|
||||
await c.showDemo();
|
||||
await tester.pumpWidget(HepanApp(controller: c));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('Riverside Schedule'), findsOneWidget);
|
||||
expect(tester.getRect(find.text('周日')).right, lessThanOrEqualTo(800));
|
||||
await tester.drag(find.text('周三'), const Offset(-250, 0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('登录学校账户'), findsNothing);
|
||||
await tester.drag(
|
||||
find.byKey(const Key('dock-cursor')),
|
||||
const Offset(100, 0),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('登录学校账户'), findsOneWidget);
|
||||
await tester.drag(find.text('连接学校课表'), const Offset(250, 0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('登录学校账户'), findsOneWidget);
|
||||
await tester.drag(
|
||||
find.byKey(const Key('dock-cursor')),
|
||||
const Offset(-100, 0),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('示例课表'), findsOneWidget);
|
||||
await tester.tap(find.bySemanticsLabel('我的'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('登录学校账户'), findsOneWidget);
|
||||
c.dispose();
|
||||
},
|
||||
);
|
||||
testWidgets('cancelled cursor drag keeps selected tab', (tester) async {
|
||||
int selected = 0;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: StatefulBuilder(
|
||||
builder: (context, setState) => Scaffold(
|
||||
body: Center(
|
||||
child: GlassDock(
|
||||
selectedIndex: selected,
|
||||
onSelected: (i) => setState(() => selected = i),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final gesture = await tester.startGesture(
|
||||
tester.getCenter(find.byKey(const Key('dock-cursor'))),
|
||||
);
|
||||
await gesture.moveBy(const Offset(90, 0));
|
||||
await tester.pump();
|
||||
await gesture.cancel();
|
||||
await tester.pumpAndSettle();
|
||||
expect(selected, 0);
|
||||
});
|
||||
}
|
||||
@@ -46,7 +46,8 @@ void main() {
|
||||
expect(find.text('示例课表'), findsOneWidget);
|
||||
expect(find.text('11'), findsOneWidget);
|
||||
await tester.ensureVisible(find.text('周一'));
|
||||
await tester.drag(find.text('周一'), const Offset(-650, 0));
|
||||
expect(tester.getRect(find.text('周日')).right, lessThanOrEqualTo(360));
|
||||
await tester.drag(find.text('周一'), const Offset(-200, 0));
|
||||
await tester.pumpAndSettle();
|
||||
expect(tester.takeException(), isNull);
|
||||
expect(find.text('周日'), findsOneWidget);
|
||||
|
||||
@@ -45,7 +45,7 @@ void main() {
|
||||
final a = tester.getRect(find.bySemanticsLabel('A,T,R'));
|
||||
final b = tester.getRect(find.bySemanticsLabel('B,T,R'));
|
||||
final cRect = tester.getRect(find.bySemanticsLabel('C,T,R'));
|
||||
expect(a.right, lessThanOrEqualTo(b.left));
|
||||
expect(b.right, lessThanOrEqualTo(cRect.left));
|
||||
expect(a.overlaps(b), isFalse);
|
||||
expect(b.overlaps(cRect), isFalse);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user