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>? diagnosis; int? reconciliationsAtDiagnosis; final tests = []; @override Future sendTestReminder({required bool scheduled}) async { tests.add(scheduled); return scheduled ? '已安排测试提醒' : '已发送测试提醒'; } @override Future> 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, 'account': 'private@example.com', }, 'courseNames': ['Private course name'], 'account': 'private@example.com', 'errorCode': 'none', }; } @override Future openSettings({required bool background}) async {} } Future _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>(); 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>(); 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>(); await _open(tester, notifications); final previous = notifications.reconciliations; await tester.tap(find.text('检查提醒')); await tester.pump(); expect(notifications.reconciliationsAtDiagnosis, previous + 1); for (final button in tester.widgetList( 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'); }); }