Files
TPSchedule/mobile/test/widgets/reminder_tools_test.dart

194 lines
6.5 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:hepan_everyday/app/app_controller.dart';
import 'package:hepan_everyday/domain/app_snapshot.dart';
import 'package:hepan_everyday/domain/app_settings.dart';
import 'package:hepan_everyday/features/profile/reminder_tools.dart';
import '../support/fakes.dart';
class _Notifications extends FakeNotifications {
Completer<Map<String, Object?>>? diagnosis;
int? reconciliationsAtDiagnosis;
final tests = <bool>[];
@override
Future<String> sendTestReminder({required bool scheduled}) async {
tests.add(scheduled);
return scheduled ? '已安排测试提醒' : '已发送测试提醒';
}
@override
Future<Map<String, Object?>> diagnose() async {
reconciliationsAtDiagnosis = reconciliations;
return diagnosis?.future ??
{
'notifications': true,
'exactAlarm': false,
'pendingCount': 2,
'channelImportance': 4,
'channelSound': true,
'system': {
'manufacturer': 'TestPhone',
'model': 'TestModel',
'sdk': 35,
'backgroundRestricted': true,
'standbyBucket': 45,
'startupAlarmAudit': {
'capturedAtEpochMillis': 123,
'cachedCourseCount': 3,
'existingHandleCount': 1,
'missingHandleCount': 2,
'courseName': 'Private course name',
},
'account': 'private@example.com',
},
'courseNames': ['Private course name'],
'account': 'private@example.com',
'errorCode': 'none',
};
}
@override
Future<void> openSettings({required bool background}) async {}
}
Future<void> _open(WidgetTester tester, _Notifications notifications) async {
final controller = AppController(
MemoryStore(AppSnapshot(settings: AppSettings())),
notifications,
FakeCalendar(),
() => DateTime.utc(2026, 10, 14),
);
await controller.initialize();
addTearDown(controller.dispose);
await tester.pumpWidget(
MaterialApp(
home: Scaffold(
body: SingleChildScrollView(
child: ReminderTools(controller: controller),
),
),
),
);
}
void main() {
testWidgets(
'Android 7 does not ask the user to create an unsupported category',
(tester) async {
final notifications = _Notifications()
..diagnosis = Completer<Map<String, Object?>>();
notifications.diagnosis!.complete({
'notifications': true,
'appNotificationsEnabled': true,
'channelExists': false,
'system': {'sdk': 24},
});
await _open(tester, notifications);
await tester.tap(find.text('检查提醒'));
await tester.pumpAndSettle();
expect(find.textContaining('类别尚未建立'), findsNothing);
expect(find.text('系统通知总开关:已允许'), findsOneWidget);
},
);
testWidgets(
'diagnosis separates the application switch from the reminder category',
(tester) async {
final notifications = _Notifications()
..diagnosis = Completer<Map<String, Object?>>();
notifications.diagnosis!.complete({
'notifications': false,
'appNotificationsEnabled': false,
'channelExists': true,
'courseChannelEnabled': true,
});
await _open(tester, notifications);
await tester.tap(find.text('检查提醒'));
await tester.pumpAndSettle();
expect(find.text('系统通知总开关:未允许'), findsOneWidget);
expect(find.text('课前提醒类别:已允许'), findsOneWidget);
},
);
testWidgets('test controls distinguish immediate and background reminders', (
tester,
) async {
final notifications = _Notifications();
await _open(tester, notifications);
await tester.tap(find.text('立即测试提醒'));
await tester.pumpAndSettle();
expect(find.text('已发送测试提醒'), findsOneWidget);
await tester.tap(find.text('10 秒后台测试'));
await tester.pumpAndSettle();
expect(notifications.tests, [false, true]);
expect(find.textContaining('Home'), findsOneWidget);
});
testWidgets('diagnosis refreshes services and prevents overlapping actions', (
tester,
) async {
final notifications = _Notifications()
..diagnosis = Completer<Map<String, Object?>>();
await _open(tester, notifications);
final previous = notifications.reconciliations;
await tester.tap(find.text('检查提醒'));
await tester.pump();
expect(notifications.reconciliationsAtDiagnosis, previous);
for (final button in tester.widgetList<TextButton>(
find.byType(TextButton),
)) {
expect(button.onPressed, isNull);
}
notifications.diagnosis!.complete({'notifications': false});
await tester.pumpAndSettle();
expect(find.textContaining('系统通知已关闭'), findsOneWidget);
expect(find.text('复制提醒诊断'), findsOneWidget);
});
testWidgets('copied diagnostics omit unapproved fields and private values', (
tester,
) async {
String? copied;
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
(call) async {
if (call.method == 'Clipboard.setData') {
copied = (call.arguments as Map)['text'] as String;
}
return null;
},
);
addTearDown(
() => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
SystemChannels.platform,
null,
),
);
await _open(tester, _Notifications());
await tester.tap(find.text('检查提醒'));
await tester.pumpAndSettle();
await tester.ensureVisible(find.text('复制提醒诊断'));
await tester.tap(find.text('复制提醒诊断'));
await tester.pumpAndSettle();
expect(copied, isNotNull);
expect(copied, isNot(contains('private@example.com')));
expect(copied, isNot(contains('Private course name')));
final report = jsonDecode(copied!) as Map;
expect(report['pendingCount'], 2);
expect(report['courseCount'], 0);
expect(report['appVersion'], isNotEmpty);
expect((report['system'] as Map)['model'], 'TestModel');
expect(
((report['system'] as Map)['startupAlarmAudit']
as Map)['missingHandleCount'],
2,
);
expect((report['beforeCheck'] as Map)['pendingCount'], 2);
expect(find.textContaining('系统待机限制'), findsOneWidget);
});
}