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); }, ); }