Files
jizhi/frontend/test/visual_contract_test.dart
T

220 lines
9.3 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
void main() {
test('登录强制刷新远端资料并按后端隔离本地账号数据', () {
final auth = File('lib/shared/api/auth_api.dart').readAsStringSync();
final session = File(
'lib/shared/services/session_store.dart',
).readAsStringSync();
expect(auth, contains('me(forceRemote: true)'));
expect(auth, contains('if (!forceRemote &&'));
expect(session, contains('BackendIdentity.accountNamespace(userId)'));
expect(session, contains('pending_account_archive_namespace'));
expect(session, contains('last_account_namespace'));
final client = File('lib/shared/api/api_client.dart').readAsStringSync();
expect(client, contains("'auth_token_\${BackendIdentity.scope}'"));
});
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'));
});
}