feat: 长辈模式(大字大按钮 + 偏好按账号隔离)+ 语音输入(按住说话 + 实时转写 + 后端代理 DashScope ASR + 患者端专用)+ 健康仪表盘背景渐变
This commit is contained in:
@@ -53,8 +53,8 @@ void main() {
|
||||
source.indexOf('class _HeaderIconButton'),
|
||||
);
|
||||
|
||||
expect(buttonSource, contains('width: 44'));
|
||||
expect(buttonSource, contains('height: 44'));
|
||||
expect(buttonSource, contains('width: elderMode ? 54 : 44'));
|
||||
expect(buttonSource, contains('height: elderMode ? 54 : 44'));
|
||||
});
|
||||
|
||||
test('settings logout action is white with dark text', () {
|
||||
|
||||
113
health_app/test/elder_mode_page_test.dart
Normal file
113
health_app/test/elder_mode_page_test.dart
Normal file
@@ -0,0 +1,113 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:health_app/core/app_theme.dart';
|
||||
import 'package:health_app/core/elder_mode_scope.dart';
|
||||
import 'package:health_app/pages/settings/elder_mode_page.dart';
|
||||
import 'package:health_app/providers/elder_mode_provider.dart';
|
||||
|
||||
class _FakeElderModeNotifier extends ElderModeNotifier {
|
||||
final bool initiallyEnabled;
|
||||
|
||||
_FakeElderModeNotifier(this.initiallyEnabled);
|
||||
|
||||
@override
|
||||
ElderModeState build() => ElderModeState(
|
||||
preferences: ElderModePreferences(enabled: initiallyEnabled),
|
||||
isLoaded: true,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<bool> enable() async {
|
||||
state = state.copyWith(
|
||||
preferences: state.preferences.copyWith(enabled: true),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> disable() async {
|
||||
state = state.copyWith(
|
||||
preferences: state.preferences.copyWith(enabled: false),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> setLargeTextEnabled(bool enabled) async {
|
||||
state = state.copyWith(
|
||||
preferences: state.preferences.copyWith(largeTextEnabled: enabled),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> setVoiceInputEnabled(bool enabled) async {
|
||||
state = state.copyWith(
|
||||
preferences: state.preferences.copyWith(voiceInputEnabled: enabled),
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class _ElderModeHarness extends ConsumerWidget {
|
||||
const _ElderModeHarness();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final state = ref.watch(elderModeProvider);
|
||||
final baseTheme = state.isEnabled
|
||||
? AppTheme.elderLightTheme
|
||||
: AppTheme.lightTheme;
|
||||
return MaterialApp(
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: baseTheme,
|
||||
home: ElderModeScope(
|
||||
enabled: state.isEnabled,
|
||||
largeTextEnabled: state.isEnabled && state.preferences.largeTextEnabled,
|
||||
child: const ElderModePage(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _pumpPage(WidgetTester tester, {required bool enabled}) async {
|
||||
tester.view.physicalSize = const Size(390, 844);
|
||||
tester.view.devicePixelRatio = 1;
|
||||
addTearDown(tester.view.resetPhysicalSize);
|
||||
addTearDown(tester.view.resetDevicePixelRatio);
|
||||
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [
|
||||
elderModeProvider.overrideWith(() => _FakeElderModeNotifier(enabled)),
|
||||
],
|
||||
child: const _ElderModeHarness(),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('长辈模式可立即开启并显示已开启状态', (tester) async {
|
||||
await _pumpPage(tester, enabled: false);
|
||||
|
||||
expect(find.text('开启长辈模式,操作更清晰'), findsOneWidget);
|
||||
expect(find.text('立即开启'), findsOneWidget);
|
||||
|
||||
await tester.tap(find.text('立即开启'));
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(find.text('长辈模式已开启'), findsOneWidget);
|
||||
expect(find.text('大图标和大按钮已启用'), findsOneWidget);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('长辈模式页面在 390x844 下无布局溢出', (tester) async {
|
||||
await _pumpPage(tester, enabled: true);
|
||||
|
||||
expect(find.text('关闭长辈模式'), findsOneWidget);
|
||||
expect(find.byType(Switch), findsNWidgets(2));
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
}
|
||||
@@ -139,11 +139,11 @@ void main() {
|
||||
).readAsStringSync();
|
||||
final drawer = File('lib/widgets/health_drawer.dart').readAsStringSync();
|
||||
|
||||
expect(source, contains('width: 18'));
|
||||
expect(source, contains('height: 18'));
|
||||
expect(source, contains('width: elderMode ? 24 : 18'));
|
||||
expect(source, contains('height: elderMode ? 24 : 18'));
|
||||
expect(source, contains('visual.icon,'));
|
||||
expect(source, contains('size: 13'));
|
||||
expect(source, contains('fontSize: 15'));
|
||||
expect(source, contains('size: elderMode ? 17 : 13'));
|
||||
expect(source, contains('fontSize: elderMode ? 17 : 15'));
|
||||
expect(source, contains('gradient: selected'));
|
||||
expect(source, contains('? AppColors.actionOutlineGradient'));
|
||||
expect(source, contains('selected ? null : Colors.transparent'));
|
||||
@@ -165,9 +165,10 @@ void main() {
|
||||
expect(input, contains('LucideIcons.send'));
|
||||
expect(input, contains('border: InputBorder.none'));
|
||||
expect(input, contains('crossAxisAlignment: CrossAxisAlignment.center'));
|
||||
// 文字模式:附件 / 麦克风 / 发送;语音模式:附件 / 键盘。全部用主渐变。
|
||||
expect(
|
||||
RegExp('gradient: AppColors.primaryGradient').allMatches(input).length,
|
||||
2,
|
||||
5,
|
||||
);
|
||||
expect(input, isNot(contains('Border.all(color: AppColors.borderLight)')));
|
||||
expect(source, isNot(contains('class _RoundToolButton')));
|
||||
|
||||
39
health_app/test/speech_input_test.dart
Normal file
39
health_app/test/speech_input_test.dart
Normal file
@@ -0,0 +1,39 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:health_app/providers/elder_mode_provider.dart';
|
||||
import 'package:health_app/services/realtime_speech_service.dart';
|
||||
|
||||
void main() {
|
||||
test('实时语音地址保留后端基础路径并切换为 WebSocket', () {
|
||||
expect(
|
||||
buildRealtimeSpeechEndpoint('http://192.168.1.34:5000').toString(),
|
||||
'ws://192.168.1.34:5000/api/speech/realtime',
|
||||
);
|
||||
expect(
|
||||
buildRealtimeSpeechEndpoint(
|
||||
'https://erpapi.datalumina.cn/xiaomai/',
|
||||
).toString(),
|
||||
'wss://erpapi.datalumina.cn/xiaomai/api/speech/realtime',
|
||||
);
|
||||
});
|
||||
|
||||
test('旧版长辈设置升级后默认启用语音输入', () {
|
||||
final preferences = ElderModePreferences.fromJson({
|
||||
'schemaVersion': 1,
|
||||
'enabled': true,
|
||||
'largeTextEnabled': true,
|
||||
});
|
||||
|
||||
expect(preferences.voiceInputEnabled, isTrue);
|
||||
});
|
||||
|
||||
test('长辈模式语音输入开关可以持久化', () {
|
||||
const preferences = ElderModePreferences(
|
||||
enabled: true,
|
||||
voiceInputEnabled: false,
|
||||
);
|
||||
final restored = ElderModePreferences.fromJson(preferences.toJson());
|
||||
|
||||
expect(restored.enabled, isTrue);
|
||||
expect(restored.voiceInputEnabled, isFalse);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user