108 lines
3.8 KiB
Dart
108 lines
3.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:kouyu_english/core/app_state.dart';
|
|
import 'package:kouyu_english/core/models.dart';
|
|
import 'package:kouyu_english/core/courses/courses.dart';
|
|
import 'package:kouyu_english/core/courses/course_pack.dart';
|
|
import 'package:kouyu_english/core/courses/course_repository.dart';
|
|
import 'package:kouyu_english/features/lesson/lesson_flow.dart';
|
|
|
|
/// Widget smoke test for the JSON units' 听读材料 (listening/reading
|
|
/// comprehension) step: it must be reached only on the last segment of a unit
|
|
/// that has materials, render from the real loaded content, and let the learner
|
|
/// continue to writing once every question is answered.
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
setUpAll(() async {
|
|
await CourseRepository.instance.load();
|
|
});
|
|
|
|
tearDownAll(() => CourseRepository.instance.resetForTest());
|
|
|
|
/// The first loaded unit whose materials contain at least one question.
|
|
CoursePack unitWithQuestions() => CourseRepository.instance.units.firstWhere(
|
|
(u) => u.materials.any((m) => m.questions.isNotEmpty),
|
|
);
|
|
|
|
AppState stateOn(String unitId, int segmentIndex) {
|
|
final state = AppState();
|
|
state.activeLessonId = unitId;
|
|
state.activeSegmentIndexes[unitId] = segmentIndex;
|
|
return state;
|
|
}
|
|
|
|
testWidgets(
|
|
'material step is reached only on the last segment with materials',
|
|
(tester) async {
|
|
final unit = unitWithQuestions();
|
|
final lesson = lessonById(unit.id);
|
|
expect(
|
|
lesson.segments.length,
|
|
greaterThan(1),
|
|
reason: 'need a multi-segment unit to prove the gate',
|
|
);
|
|
|
|
// Finishing reading on an earlier segment skips straight to writing.
|
|
final early = stateOn(unit.id, 0);
|
|
early.lessonStep = LessonStep.reading;
|
|
early.completeReading();
|
|
expect(early.lessonStep, LessonStep.writing);
|
|
|
|
// Finishing reading on the final segment opens the comprehension step.
|
|
final last = stateOn(unit.id, lesson.segments.length - 1);
|
|
last.lessonStep = LessonStep.reading;
|
|
last.completeReading();
|
|
expect(last.lessonStep, LessonStep.material);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'renders the unit materials and continues once every question is answered',
|
|
(tester) async {
|
|
final unit = unitWithQuestions();
|
|
final lesson = lessonById(unit.id);
|
|
final state = stateOn(unit.id, lesson.segments.length - 1);
|
|
state.lessonStep = LessonStep.material;
|
|
|
|
await tester.pumpWidget(
|
|
MaterialApp(
|
|
home: LessonFlow(
|
|
state: state,
|
|
onOpenDialogue: () {},
|
|
onFinish: () {},
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
|
|
// The step rendered from the real loaded content.
|
|
expect(find.text('读一段真实的小材料,做完理解题。'), findsOneWidget);
|
|
// Continue is gated until every question is answered.
|
|
expect(find.text('答对所有理解题后继续'), findsOneWidget);
|
|
expect(find.text('继续写一写'), findsNothing);
|
|
|
|
// Answer every question by tapping its correct option (answer == the
|
|
// parsed contract's first option; the widget shuffles their display order).
|
|
for (final material in unit.materials) {
|
|
for (final question in material.questions) {
|
|
final option = find.text(question.answer).first;
|
|
await tester.ensureVisible(option);
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(option);
|
|
await tester.pumpAndSettle();
|
|
}
|
|
}
|
|
|
|
// Now the button is enabled and advances the lesson to writing.
|
|
final go = find.text('继续写一写');
|
|
expect(go, findsOneWidget);
|
|
await tester.ensureVisible(go);
|
|
await tester.tap(go);
|
|
await tester.pumpAndSettle();
|
|
|
|
expect(state.lessonStep, LessonStep.writing);
|
|
},
|
|
);
|
|
}
|