Files
jizhi/frontend/test/theme_and_time_test.dart
T

180 lines
5.7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:miaoji_zhang/shared/services/recognition_import_service.dart';
import 'package:miaoji_zhang/shared/services/recognition_diagnostic_formatter.dart';
import 'package:miaoji_zhang/shared/services/screenshot_channel.dart';
import 'package:miaoji_zhang/shared/services/shanghai_time.dart';
import 'package:miaoji_zhang/shared/theme/theme_store.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
test('上海时区周期边界不受设备时区影响', () {
final civil = ShanghaiTime.toCivil(DateTime.utc(2026, 6, 30, 16, 30));
expect(civil, DateTime(2026, 7, 1, 0, 30));
final month = ShanghaiTime.monthRangeUtc(2026, 7);
expect(month.start, DateTime.utc(2026, 6, 30, 16));
expect(month.end, DateTime.utc(2026, 7, 31, 16));
final week = ShanghaiTime.weekRangeUtc(DateTime(2026, 7, 19));
expect(week.start, DateTime.utc(2026, 7, 12, 16));
expect(week.end, DateTime.utc(2026, 7, 19, 16));
});
test('无障碍运行时长不会被当作 Unix 时间戳', () {
final now = DateTime.utc(2026, 7, 22, 8, 30);
expect(RecognitionImportService.validOccurredAtUtc(1234, now: now), now);
expect(
RecognitionImportService.validOccurredAtUtc(
DateTime.utc(2026, 7, 22, 8).millisecondsSinceEpoch,
now: now,
),
DateTime.utc(2026, 7, 22, 8),
);
});
test('三态主题从本地恢复并持久化', () async {
SharedPreferences.setMockInitialValues({'theme_preference': 'dark'});
await ThemeStore.instance.initialize();
expect(ThemeStore.instance.preference, JzThemePreference.dark);
expect(ThemeStore.instance.themeMode, ThemeMode.dark);
await ThemeStore.instance.setPreference(JzThemePreference.light);
final preferences = await SharedPreferences.getInstance();
expect(preferences.getString('theme_preference'), 'light');
expect(ThemeStore.instance.themeMode, ThemeMode.light);
});
test('旧版无时区接口时间按 UTC 解释后显示上海时间', () {
expect(
ShanghaiTime.parseUtcInstant('2026-07-22T14:35:00'),
DateTime.utc(2026, 7, 22, 14, 35),
);
expect(
ShanghaiTime.parseCivil('2026-07-22T14:35:00'),
DateTime(2026, 7, 22, 22, 35),
);
expect(
ShanghaiTime.parseCivil('2026-07-22T14:35:00Z'),
DateTime(2026, 7, 22, 22, 35),
);
});
test('原生识别候选保留红包分类与金额来源', () {
final candidate = RecognitionCandidate.fromJson({
'id': 'candidate-1',
'clientRequestId': 'recognition-candidate-1',
'state': 'auto_ready',
'confidence': 'auto',
'type': 'income',
'source': 'local_ocr',
'appName': '微信',
'amount': 8.88,
'occurredAtEpochMs': DateTime.utc(
2026,
7,
22,
14,
35,
).millisecondsSinceEpoch,
'recognitionKind': 'red_packet_receive',
'categoryHint': '红包',
'amountSource': 'result',
'resultFingerprint': 'abc123',
});
expect(candidate.recognitionKind, 'red_packet_receive');
expect(candidate.categoryHint, '红包');
expect(candidate.amountSource, 'result');
expect(candidate.resultFingerprint, 'abc123');
});
test('AI 批次模型保留动作、原因与可恢复状态', () {
final batch = RecognitionBatch.fromJson({
'id': 'batch-1',
'state': 'ready',
'openedAt': DateTime.utc(2026, 7, 25, 8).millisecondsSinceEpoch,
'completedAt': DateTime.utc(2026, 7, 25, 8, 1).millisecondsSinceEpoch,
'summary': {
'kept': 1,
'updated': 1,
'created': 0,
'dropped': 1,
'fallback': false,
},
'items': [
{
'candidateId': 'candidate-1',
'action': 'drop',
'reason': '同一流程重复结果页',
'state': 'ai_dropped',
'type': 'expense',
'amount': 20,
'merchant': '张三',
'canRestore': true,
},
],
});
expect(batch.completedAt.isUtc, isTrue);
expect(batch.updated, 1);
expect(batch.dropped, 1);
expect(batch.items.single.action, 'drop');
expect(batch.items.single.reason, '同一流程重复结果页');
expect(batch.items.single.canRestore, isTrue);
});
test('智能识别诊断摘要能区分关键失败类型', () {
RecognitionDiagnostic diagnostic(String result, String reason) {
return RecognitionDiagnostic(
at: DateTime.utc(2026, 7, 22, 14, 35),
appName: '微信',
stage: 'ocr',
result: result,
reason: reason,
nodeCount: 12,
amountCandidates: 1,
);
}
expect(
RecognitionDiagnosticDisplay.from(
diagnostic('failed', 'capture_timeout'),
).summaryLabel,
'截图失败',
);
expect(
RecognitionDiagnosticDisplay.from(
diagnostic('failed', 'no_text'),
).summaryLabel,
'OCR 无结果',
);
expect(
RecognitionDiagnosticDisplay.from(
diagnostic('rejected', 'payment_input_page'),
).summaryLabel,
'规则拒绝',
);
expect(
RecognitionDiagnosticDisplay.from(
diagnostic('ignored', 'duplicate_result_surface'),
).summaryLabel,
'已合并',
);
expect(
RecognitionDiagnosticDisplay.from(
diagnostic('confirm', 'expected_amount_fallback'),
).reasonLabel,
'结果页未读到金额,已使用本次付款流程确认的金额',
);
expect(
RecognitionDiagnosticDisplay.from(
diagnostic('confirm', 'result_amount_conflict'),
).reasonLabel,
'结果页金额与本次付款金额不一致,请确认后入账',
);
});
}