import 'package:flutter_test/flutter_test.dart'; import 'package:kouyu_english/core/seed_courses.dart'; void main() { group('对话任务校验', () { test('每个对话都为每一轮声明了任务标签', () { for (final entry in a0Dialogues.entries) { final script = entry.value; expect( script.taskLabels.length, script.prompts.length, reason: '${entry.key} 的任务标签数量要和回合数一致', ); expect( script.requiredTerms.length, script.prompts.length, reason: '${entry.key} 的校验词组数量要和回合数一致', ); for (final group in script.requiredTerms) { expect(group, isNotEmpty); } } for (final entry in a0SegmentDialogues.entries) { expect( entry.value.taskLabels.length, entry.value.prompts.length, reason: '${entry.key} 的任务标签数量要和回合数一致', ); } expect(a0MeetDialogue.taskLabels.length, a0MeetDialogue.prompts.length); expect(a0MeetDialogue.requiredTerms.length, a0MeetDialogue.prompts.length); }); test('示范答案能通过对应回合的校验', () { for (final entry in {...a0Dialogues, 'scene': a0MeetDialogue}.entries) { final script = entry.value; for (var stage = 0; stage < script.hints.length; stage++) { expect( matchesDialogueStage(script, stage, script.hints[stage]), isTrue, reason: '${entry.key} 第 ${stage + 1} 轮的示范答案应当通过', ); } } }); test('跑题回答不再因为关键词沾边而通过', () { // 旧实现用 text.contains('it'),下面这些句子全部会被判为完成任务。 expect(matchesDialogueStage(a0Dialogues['a0-05']!, 0, 'I did it.'), isFalse); expect(matchesDialogueStage(a0Dialogues['a0-08']!, 0, 'It is good.'), isFalse); expect(matchesDialogueStage(a0Dialogues['a0-09']!, 2, 'I like tea.'), isFalse); expect(matchesDialogueStage(a0MeetDialogue, 0, 'Hello.'), isFalse); expect(matchesDialogueStage(a0MeetDialogue, 1, 'I am good.'), isFalse); }); test('自由场景接受同样正确的其他说法', () { // 旧正则只认 good/okay/tired,也不认 my name's。 expect(matchesDialogueStage(a0MeetDialogue, 0, "My name's Shen."), isTrue); expect(matchesDialogueStage(a0MeetDialogue, 0, 'I am Shen.'), isTrue); expect(matchesDialogueStage(a0MeetDialogue, 2, "I'm fine, thanks."), isTrue); expect(matchesDialogueStage(a0MeetDialogue, 2, 'I am great!'), isTrue); expect(matchesDialogueStage(a0MeetDialogue, 3, 'Where are you from?'), isTrue); expect(matchesDialogueStage(a0MeetDialogue, 3, 'Do you like tea?'), isTrue); }); test('只写半句不算完成任务', () { expect(matchesDialogueStage(a0MeetDialogue, 0, "I'm"), isFalse); expect(matchesDialogueStage(a0Dialogues['a0-04']!, 0, 'My number is'), isFalse); expect( matchesDialogueStage(a0Dialogues['a0-04']!, 0, 'My number is one-three-eight.'), isTrue, ); }); test('拼读和数字这类结构化要求可以识别', () { expect(matchesDialogueStage(a0Dialogues['a0-02']!, 1, 'S-H-E-N'), isTrue); expect(matchesDialogueStage(a0Dialogues['a0-02']!, 1, 'Shen'), isFalse); expect(matchesDialogueStage(a0Dialogues['a0-08']!, 2, "It's three o'clock."), isTrue); expect(matchesDialogueStage(a0Dialogues['a0-08']!, 2, "It's Monday."), isFalse); }); test('任务标签可用于提示和总结', () { expect(dialogueTaskLabel(a0MeetDialogue, 0), '介绍姓名'); expect(dialogueTaskLabel(a0MeetDialogue, 9), '完成本轮任务'); }); test('输入校验忽略多余空格与大小写', () { expect( matchesDialogueStage(a0MeetDialogue, 0, " MY NAME IS SHEN "), isTrue, ); expect( matchesDialogueStage(a0Dialogues["a0-02"]!, 1, " S H E N "), isTrue, ); expect( matchesDialogueStage(a0Dialogues["a0-04"]!, 0, "MY NUMBER IS ONE-THREE-EIGHT"), isTrue, ); expect( matchesDialogueStage(a0Dialogues["a0-08"]!, 2, "IT'S THREE O'CLOCK"), isTrue, ); }); test('给 AI 的可用词表随课程递增且不越界', () { final first = taughtLanguageUpTo('a0-01'); final later = taughtLanguageUpTo('a0-09'); expect(first, isNotEmpty); expect(later.length, greaterThan(first.length)); expect(first.every(later.contains), isTrue); expect(later.length, lessThanOrEqualTo(allTaughtLanguage.length)); expect(first.any((word) => word.toLowerCase().contains('coffee')), isFalse); }); }); }