Initial project import

This commit is contained in:
2026-07-24 23:11:20 +08:00
commit 6396eabb87
372 changed files with 49682 additions and 0 deletions
+204
View File
@@ -0,0 +1,204 @@
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('启动页只读取本地缓存,不等待远程接口', () {
final source = File(
'lib/features/auth/pages/splash_page.dart',
).readAsStringSync();
expect(source, contains('loadCached'));
expect(source, isNot(contains('PublicConfigApi')));
expect(source, isNot(contains('AuthApi.me')));
expect(source, isNot(contains('ensureLoaded')));
});
test('业务页面不再直接使用系统感强的控件或旧青绿色板', () {
final banned = <String>[
'AlertDialog(',
'DropdownButtonFormField',
'showDatePicker(',
'showTimePicker(',
'PopupMenuButton',
'LinearGradient(',
'RadialGradient(',
'SweepGradient(',
'BackdropFilter(',
'0xFF087F6D',
'0xFF066B5C',
'0xFFE3F2EF',
];
final violations = <String>[];
for (final file
in Directory('lib/features')
.listSync(recursive: true)
.whereType<File>()
.where((file) => file.path.endsWith('.dart'))) {
final source = file.readAsStringSync();
for (final token in banned) {
if (source.contains(token)) {
violations.add('${file.path}: $token');
}
}
}
expect(violations, isEmpty, reason: violations.join('\n'));
});
test('深色模式的选中与未选中状态使用语义色板', () {
final controls = File(
'lib/shared/widgets/app_controls.dart',
).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 me = File('lib/features/settings/me_page.dart').readAsStringSync();
final report = File(
'lib/features/stats/report_page.dart',
).readAsStringSync();
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(me, isNot(contains('context.jz.primaryBackground : Colors.white')));
expect(report, isNot(contains('selected ? Colors.white')));
});
test('聊天附件只保留拍照和相册导入', () {
final source = File('lib/features/chat/chat_page.dart').readAsStringSync();
expect(source, contains('ImageSource.camera'));
expect(source, contains('ImageSource.gallery'));
expect(source, isNot(contains('截屏记账')));
expect(source, isNot(contains('手动输入文字')));
expect(source, isNot(contains('ScreenshotChannel.capture')));
});
test('表情包具备离线缓存、内置兜底和展开刷新', () {
final api = File('lib/shared/api/business_api.dart').readAsStringSync();
final chat = File('lib/features/chat/chat_page.dart').readAsStringSync();
expect(api, contains('global_stickers_v1'));
expect(api, contains('_fallback'));
expect(api, contains('SharedPreferences.getInstance'));
expect(
chat,
contains('setState(() => _composerPanel = _ComposerPanel.stickers)'),
);
expect(chat, contains('_stkLoad();'));
});
test('无障碍只解析明确成功页且使用墙钟时间', () {
final parser = File(
'android/app/src/main/kotlin/com/nx/miaoji/PaymentParser.kt',
).readAsStringSync();
expect(parser, contains('occurredAt = System.currentTimeMillis()'));
expect(parser, contains('historyTitles'));
expect(parser, contains('"扫码支付成功"'));
expect(parser, contains('"转账已完成"'));
expect(parser, contains('STANDALONE_AMOUNT'));
expect(parser, contains('MAX_PAYMENT_SCAN_LINES = 48'));
expect(parser, isNot(contains('listOf("收款成功", "已收款", "到账"')));
expect(parser, isNot(contains('"支出", "消费成功"')));
expect(parser, isNot(contains('listOf("微信支付", "支付宝", "交易成功")')));
final service = File(
'android/app/src/main/kotlin/com/nx/miaoji/ScreenshotAccessibilityService.kt',
).readAsStringSync();
expect(service, contains('AccessibilityEvent.TYPE_WINDOWS_CHANGED'));
expect(service, contains('AccessibilityEvent.TYPE_VIEW_CLICKED'));
expect(service, contains('PaymentFlow('));
expect(service, contains('PAYMENT_FLOW_TTL_MS = 90_000L'));
expect(service, contains('VISUAL_STABILITY_DELAY_MS = 700L'));
expect(service, contains('takeScreenshotOfWindow'));
expect(service, contains('bitmap.recycle()'));
expect(service, contains('CAPTURE_CALLBACK_TIMEOUT_MS = 6_000L'));
expect(service, contains('OCR_CALLBACK_TIMEOUT_MS = 12_000L'));
final localOcr = File(
'android/app/src/main/kotlin/com/nx/miaoji/LocalPaymentOcr.kt',
).readAsStringSync();
expect(localOcr, contains('ChineseTextRecognizerOptions'));
expect(localOcr, contains('InputImage.fromBitmap'));
expect(localOcr, contains('distinctCents.size == 1'));
expect(localOcr, contains('evidenceConfidence = if (highConfidence)'));
final store = File(
'android/app/src/main/kotlin/com/nx/miaoji/RecognitionStore.kt',
).readAsStringSync();
expect(store, contains('"local_ocr" -> 8'));
expect(store, contains('flowSessionId'));
expect(store, contains('ACCESSIBILITY_NOTIFICATION_MASK'));
final gradle = File('android/app/build.gradle.kts').readAsStringSync();
expect(
gradle,
contains('com.google.mlkit:text-recognition-chinese:16.0.1'),
);
});
test('本地 OCR 需要一次性用途确认并提供无隐私诊断', () {
final settings = File(
'lib/features/settings/screenshot_settings_page.dart',
).readAsStringSync();
final legal = File(
'lib/features/settings/legal_document_page.dart',
).readAsStringSync();
final diagnostics = File(
'android/app/src/main/kotlin/com/nx/miaoji/RecognitionDiagnostics.kt',
).readAsStringSync();
final formatter = File(
'lib/shared/services/recognition_diagnostic_formatter.dart',
).readAsStringSync();
expect(settings, contains('local_ocr_consent_v1'));
expect(settings, contains('图片只在内存中由本地 OCR 处理'));
expect(settings, contains('_RecognitionDiagnosticCard'));
expect(settings, contains('Timer.periodic'));
expect(formatter, contains('operation_interrupted'));
expect(formatter, contains('截图失败'));
expect(formatter, contains('OCR 无结果'));
expect(formatter, contains('规则拒绝'));
expect(formatter, contains('已合并'));
expect(legal, contains('ML Kit 中文文字识别(Google'));
expect(diagnostics, isNot(contains('sourceText')));
expect(diagnostics, isNot(contains('image')));
expect(diagnostics, contains('STALE_OPERATION_MS = 20_000L'));
final service = File(
'android/app/src/main/kotlin/com/nx/miaoji/ScreenshotAccessibilityService.kt',
).readAsStringSync();
expect(service, contains('resolveAccessibilityWindowId'));
final localOcr = File(
'android/app/src/main/kotlin/com/nx/miaoji/LocalPaymentOcr.kt',
).readAsStringSync();
expect(localOcr, contains('MlKitContext.initializeIfNeeded'));
expect(localOcr, contains('ocr_model_unavailable'));
expect(localOcr, contains('OcrDiagnosticRedactor'));
expect(localOcr, contains('qualifiesWeakAuto'));
expect(settings, contains('OCR 诊断预览'));
expect(settings, contains('10 分钟后自动关闭'));
expect(diagnostics, contains('statusStrength'));
expect(diagnostics, contains('expectedAmountMatched'));
expect(diagnostics, contains('resultTransitionObserved'));
expect(diagnostics, contains('ocrPreview'));
expect(service, contains('shouldFallbackToDisplay'));
expect(service, contains('ERROR_TAKE_SCREENSHOT_INVALID_WINDOW'));
expect(service, contains('WINDOW_CAPTURE_FALLBACK_DELAY_MS = 450L'));
});
test('智能识别授权未完成时不会提前打开开关', () {
final source = File(
'lib/features/settings/screenshot_settings_page.dart',
).readAsStringSync();
expect(source, contains('_pendingAuthorizationKey'));
expect(source, contains('未完成系统授权,识别开关仍保持关闭'));
expect(source, contains('setRecognitionToggle(key, false)'));
expect(source, contains('_BackgroundKeepAliveCard'));
});
}