Files
English/kouyu_english/test/input_feedback_test.dart

161 lines
5.2 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:kouyu_english/core/ai_service.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/features/lesson/lesson_flow.dart';
void main() {
group('independentShortfall', () {
test('is null when the attempt passes', () {
expect(independentShortfall('a0-04-a', 'zero, one, two'), isNull);
expect(
independentShortfall('a0-01-a', "My name is Alex. Nice to meet you."),
isNull,
);
});
test('names the missing part of a single-segment task', () {
final message = independentShortfall('a0-01-a', 'My name is Alex.');
expect(message, contains('nice to meet you'));
expect(message, isNot(contains('my name is')));
});
test('counts the numbers still needed', () {
expect(
independentShortfall('a0-04-a', 'zero, one'),
contains('目前用上 2 个'),
);
});
test('lists the taught expressions of a split segment', () {
expect(
independentShortfall('a0-08-b', 'It is Monday.'),
contains('friday'),
);
});
});
group('assessmentAnswerGuide', () {
final pack = a0AssessmentPacks.first;
test('shows the correct choice', () {
final task = pack.forSkill(AssessmentSkill.listening).first;
expect(assessmentAnswerGuide(task, '买东西'), '正确答案:自我介绍');
expect(assessmentAnswerGuide(task, '自我介绍'), contains('没有播放音频'));
});
test('explains open speaking and writing tasks', () {
final speaking = pack.forSkill(AssessmentSkill.speaking)[3];
expect(
assessmentAnswerGuide(speaking, 'my mother'),
contains('This is my'),
);
final writing = pack.forSkill(AssessmentSkill.writing).first;
expect(assessmentAnswerGuide(writing, 'abc'), isNotEmpty);
});
});
testWidgets('finishing step 6 after an AI correction counts as assisted', (
tester,
) async {
final messenger = tester.binding.defaultBinaryMessenger;
for (final name in const [
'com.llfbandit.record/messages',
'xyz.luan/audioplayers',
'xyz.luan/audioplayers.global',
]) {
messenger.setMockMethodCallHandler(
MethodChannel(name),
(_) async => null,
);
}
messenger.setMockStreamHandler(
const EventChannel('xyz.luan/audioplayers.global/events'),
MockStreamHandler.inline(onListen: (_, _) {}),
);
final reportError = FlutterError.onError;
FlutterError.onError = (details) {
if (details.exception is! MissingPluginException) {
reportError?.call(details);
}
};
addTearDown(() => FlutterError.onError = reportError);
AiService.instance.setFallbackApiKey('test-key');
final state = AppState()
..aiProvider = AiProviderType.compatible
..aiEndpoint = 'https://api.deepseek.com'
..aiModel = 'deepseek-flash'
..activeLessonId = 'a0-01'
..lessonStep = LessonStep.independent;
final segmentId = lessonById('a0-01').segments.first.id;
final check = jsonEncode({
'schemaVersion': 'writing-feedback-1',
'verdict': 'rewrite',
'feedback': '内容完整,有一个语法错误。',
'suggestion': 'My name is Alex. Nice to meet you. I am happy.',
'missing': ['I is 应为 I am'],
'lessonId': segmentId,
});
final prompts = <String>[];
await http.runWithClient(
() async {
await tester.pumpWidget(
MaterialApp(
home: LessonFlow(
state: state,
onOpenDialogue: () {},
onFinish: () {},
),
),
);
await tester.pump();
expect(find.text('独立完成'), findsOneWidget);
await tester.enterText(
find.byType(TextField),
'My name is Alex. Nice to meet you. I is happy.',
);
await tester.pump();
final aiButton = find.text('提交前 AI 检查语法和拼写(可选)');
await tester.ensureVisible(aiButton);
await tester.tap(aiButton);
await tester.runAsync(
() => Future<void>.delayed(const Duration(milliseconds: 50)),
);
await tester.pump();
expect(find.text('· I is 应为 I am'), findsOneWidget);
final finish = find.text('带帮助完成');
await tester.ensureVisible(finish);
await tester.tap(finish);
await tester.pump();
},
() => MockClient((request) async {
prompts.add(request.body);
return http.Response(
jsonEncode({
'choices': [
{
'message': {'content': check},
},
],
}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
}),
);
expect(prompts.single, contains('I is happy'));
expect(state.lessonStep, LessonStep.complete);
expect(state.independentAttemptAssisted, isTrue);
});
}