已完成用户APP首期功能开发
交付用户端首期页面、配套接口、后台资源及测试文档。用户APP构建、静态分析和三个管理后台构建通过;完整测试仍有2项失败,后端模型注释检查未通过,详见交付记录。
This commit is contained in:
165
apps/user_app/test/ui/repair_speech_test.dart
Normal file
165
apps/user_app/test/ui/repair_speech_test.dart
Normal file
@@ -0,0 +1,165 @@
|
||||
// 功能描述:语音结果选区替换、重复中间结果、拒绝权限、离页取消与长度边界回归。
|
||||
// 版本:1.0.0。
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:user_app/data/services/repair_speech.dart';
|
||||
import 'package:user_app/ui/features/tickets/repair_speech_button.dart';
|
||||
|
||||
class _Speech implements RepairSpeech {
|
||||
bool available = true;
|
||||
int cancels = 0, starts = 0;
|
||||
Completer<bool>? pending;
|
||||
late void Function(String) words, error;
|
||||
late void Function() done;
|
||||
@override
|
||||
Future<bool> start({
|
||||
required void Function(String) onWords,
|
||||
required void Function(String) onError,
|
||||
required void Function() onDone,
|
||||
}) async {
|
||||
starts++;
|
||||
words = onWords;
|
||||
error = onError;
|
||||
done = onDone;
|
||||
return pending?.future ?? available;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> stop() async {
|
||||
done();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> cancel() async {
|
||||
cancels++;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('权限弹窗不取消初始化,切后台立即取消', (tester) async {
|
||||
final speech = _Speech()..pending = Completer<bool>();
|
||||
final controller = TextEditingController(text: '保留');
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: RepairSpeechButton(controller: controller, onBusy: (_) {}, speech: speech),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('语音输入'));
|
||||
await tester.pump();
|
||||
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.inactive);
|
||||
await tester.pump();
|
||||
expect(speech.cancels, 0);
|
||||
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.paused);
|
||||
await tester.pump();
|
||||
expect(speech.cancels, 1);
|
||||
speech.pending!.complete(true);
|
||||
speech.words('迟到');
|
||||
await tester.pump();
|
||||
expect(controller.text, '保留');
|
||||
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.resumed);
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
controller.dispose();
|
||||
});
|
||||
testWidgets('选区替换和中间结果不重复追加,结束后可再次录入', (tester) async {
|
||||
final speech = _Speech(), controller = TextEditingController(text: '前旧后');
|
||||
controller.selection = const TextSelection(baseOffset: 1, extentOffset: 2);
|
||||
final busy = <bool>[];
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: RepairSpeechButton(controller: controller, onBusy: busy.add, speech: speech),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('语音输入'));
|
||||
await tester.pump();
|
||||
speech.words('阀门');
|
||||
speech.words('阀门异响');
|
||||
await tester.pump();
|
||||
expect(controller.text, '前阀门异响后');
|
||||
await tester.tap(find.text('结束识别'));
|
||||
await tester.pump();
|
||||
speech.words('迟到结果');
|
||||
expect(controller.text, '前阀门异响后');
|
||||
expect(busy.last, false);
|
||||
await tester.tap(find.text('语音输入'));
|
||||
await tester.pump();
|
||||
speech.words('补充');
|
||||
speech.done();
|
||||
await tester.pump();
|
||||
expect(controller.text, '前阀门异响补充后');
|
||||
expect(speech.starts, 2);
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
controller.dispose();
|
||||
});
|
||||
testWidgets('不可用和权限拒绝保留输入并解除忙碌', (tester) async {
|
||||
final speech = _Speech()..available = false, controller = TextEditingController(text: '已有内容');
|
||||
final busy = <bool>[];
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: RepairSpeechButton(controller: controller, onBusy: busy.add, speech: speech),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('语音输入'));
|
||||
await tester.pump();
|
||||
expect(find.textContaining('无法使用语音识别'), findsOneWidget);
|
||||
expect(controller.text, '已有内容');
|
||||
expect(busy.last, false);
|
||||
speech.available = true;
|
||||
await tester.tap(find.text('语音输入'));
|
||||
await tester.pump();
|
||||
speech.error('error_permission');
|
||||
await tester.pump();
|
||||
expect(find.textContaining('未获得麦克风'), findsOneWidget);
|
||||
expect(controller.text, '已有内容');
|
||||
expect(busy.last, false);
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
controller.dispose();
|
||||
});
|
||||
testWidgets('初始化中离页取消,迟到结果不改变文字', (tester) async {
|
||||
final speech = _Speech()..pending = Completer<bool>(),
|
||||
controller = TextEditingController(text: '保留');
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: RepairSpeechButton(controller: controller, onBusy: (_) {}, speech: speech),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('语音输入'));
|
||||
await tester.pump();
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
speech.pending!.complete(true);
|
||||
speech.words('迟到');
|
||||
await tester.pump();
|
||||
expect(speech.cancels, 1);
|
||||
expect(controller.text, '保留');
|
||||
expect(tester.takeException(), isNull);
|
||||
controller.dispose();
|
||||
});
|
||||
testWidgets('识别遵守2000字符上限,不截断表情字符', (tester) async {
|
||||
final speech = _Speech(),
|
||||
controller = TextEditingController(text: List.filled(1999, '字').join());
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: Scaffold(
|
||||
body: RepairSpeechButton(controller: controller, onBusy: (_) {}, speech: speech),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.tap(find.text('语音输入'));
|
||||
await tester.pump();
|
||||
speech.words('😀多余');
|
||||
speech.done();
|
||||
await tester.pump();
|
||||
expect(controller.text.characters.length, 2000);
|
||||
expect(controller.text.endsWith('😀'), true);
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
controller.dispose();
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user