feat: refine AI navigation and accessibility recovery

This commit is contained in:
2026-08-26 22:58:26 +08:00
parent c6f98b5445
commit 9ccbdfead4
15 changed files with 521 additions and 203 deletions
+108 -29
View File
@@ -65,12 +65,12 @@ void main() {
).readAsStringSync();
final addPage = File('lib/features/add/add_page.dart').readAsStringSync();
final chat = File('lib/features/chat/chat_page.dart').readAsStringSync();
final companion = File(
'lib/features/settings/companion_page.dart',
).readAsStringSync();
final onboarding = File(
'lib/features/onboarding/pages/onboarding_page.dart',
).readAsStringSync();
final companion = File(
'lib/features/settings/companion_page.dart',
).readAsStringSync();
final onboarding = File(
'lib/features/onboarding/pages/onboarding_page.dart',
).readAsStringSync();
final me = File('lib/features/settings/me_page.dart').readAsStringSync();
final report = File(
'lib/features/stats/report_page.dart',
@@ -79,30 +79,30 @@ void main() {
expect(controls, contains(': context.jz.card'));
expect(addPage, isNot(contains('selected ? _activeColor : Colors.white')));
expect(chat, isNot(contains('isMe ? AppTheme.primary : Colors.white')));
expect(
companion,
isNot(contains('context.jz.aiBackground : Colors.white')),
);
expect(onboarding, contains(': context.jz.card'));
expect(
onboarding,
isNot(contains('context.jz.aiBackground : Colors.white')),
);
expect(
companion,
isNot(contains('context.jz.aiBackground : Colors.white')),
);
expect(onboarding, contains(': context.jz.card'));
expect(
onboarding,
isNot(contains('context.jz.aiBackground : Colors.white')),
);
expect(me, isNot(contains('context.jz.primaryBackground : Colors.white')));
expect(report, isNot(contains('selected ? Colors.white')));
});
test('首次引导只提交服务端实际返回的 AI 伙伴配置', () {
final onboarding = File(
'lib/features/onboarding/pages/onboarding_page.dart',
).readAsStringSync();
expect(onboarding, contains('_hasValidCatalogSelection'));
expect(onboarding, contains('AI 伙伴配置暂不可用'));
expect(onboarding, contains('重新加载'));
expect(onboarding, isNot(contains('_fallbackAvatars')));
expect(onboarding, isNot(contains('_fallbackPersonas')));
});
expect(report, isNot(contains('selected ? Colors.white')));
});
test('首次引导只提交服务端实际返回的 AI 伙伴配置', () {
final onboarding = File(
'lib/features/onboarding/pages/onboarding_page.dart',
).readAsStringSync();
expect(onboarding, contains('_hasValidCatalogSelection'));
expect(onboarding, contains('AI 伙伴配置暂不可用'));
expect(onboarding, contains('重新加载'));
expect(onboarding, isNot(contains('_fallbackAvatars')));
expect(onboarding, isNot(contains('_fallbackPersonas')));
});
test('聊天附件只保留拍照和相册导入', () {
final source = File('lib/features/chat/chat_page.dart').readAsStringSync();
@@ -114,6 +114,49 @@ void main() {
expect(source, isNot(contains('ScreenshotChannel.capture')));
});
test('底部导航固定保留 AI 入口且关闭状态在聊天二级页处理', () {
final shell = File(
'lib/features/home/pages/main_shell.dart',
).readAsStringSync();
final router = File('lib/app/app.dart').readAsStringSync();
final gate = File(
'lib/shared/widgets/ai_access_gate.dart',
).readAsStringSync();
expect(shell, isNot(contains('showAiEntry')));
expect(shell, contains('_tab(companion.name, AppIcons.chat, 2)'));
expect(router, isNot(contains("state.matchedLocation == '/chat'")));
expect(gate, contains('AiAccessState.aiDisabled'));
expect(gate, contains('AI 功能已关闭'));
expect(gate, contains('手动记账和统计不受影响'));
});
test('报告入口归入统计并继承当前周期', () {
final stats = File('lib/features/stats/stats_page.dart').readAsStringSync();
final report = File(
'lib/features/stats/report_page.dart',
).readAsStringSync();
final me = File('lib/features/settings/me_page.dart').readAsStringSync();
expect(me, isNot(contains("context.push('/report')")));
expect(stats, contains("path: '/report'"));
expect(stats, contains("'period': _period"));
expect(stats, contains("'date': _anchor.toIso8601String()"));
expect(stats, contains('查看完整报告'));
expect(report, contains('widget.initialPeriod'));
expect(report, contains('widget.initialAnchor'));
});
test('聊天输入为空显示附件加号,有文本时显示发送按钮', () {
final chat = File('lib/features/chat/chat_page.dart').readAsStringSync();
expect(chat, contains("ValueKey('add-attachment')"));
expect(chat, contains("ValueKey('send-message')"));
expect(chat, contains('child: hasText'));
expect(chat, contains("tooltip: '添加附件'"));
expect(chat, isNot(contains("'附件'")));
});
test('表情包具备离线缓存、内置兜底和展开刷新', () {
final api = File('lib/shared/api/business_api.dart').readAsStringSync();
final chat = File('lib/features/chat/chat_page.dart').readAsStringSync();
@@ -236,4 +279,40 @@ void main() {
expect(source, contains('setRecognitionToggle(key, false)'));
expect(source, contains('_BackgroundKeepAliveCard'));
});
test('长期识别组件与系统绑定使用默认进程并支持重连状态', () {
final manifest = File(
'android/app/src/main/AndroidManifest.xml',
).readAsStringSync();
final channel = File(
'lib/shared/services/screenshot_channel.dart',
).readAsStringSync();
final activity = File(
'android/app/src/main/kotlin/com/nx/miaoji/MainActivity.kt',
).readAsStringSync();
for (final component in [
'ScreenshotTileService',
'PaymentNotificationListenerService',
'ScreenshotAccessibilityService',
]) {
final declaration = RegExp(
'<service\\s+android:name="\\.$component"[\\s\\S]*?</service>',
).firstMatch(manifest)?.group(0);
expect(declaration, isNotNull);
expect(declaration, contains('android:stopWithTask="false"'));
expect(declaration, isNot(contains('android:process=')));
}
final provider = RegExp(
'<provider\\s+android:name="\\.RecognitionBridgeProvider"[\\s\\S]*?/>',
).firstMatch(manifest)?.group(0);
expect(provider, isNotNull);
expect(provider, isNot(contains('android:process=')));
expect(manifest, contains('android:name=".OneShotProjectionService"'));
expect(manifest, contains('android:process=":recognition"'));
expect(channel, contains('waitForAccessibilityConnection'));
expect(channel, contains('AccessibilityConnectionState.reconnecting'));
expect(activity, contains('"reconnecting"'));
expect(activity, contains('"disconnected"'));
});
}