/// Tests Desktop Notification Adapter behavior for the Scheduler Notifications Desktop package. library; import 'dart:io'; import 'package:scheduler_notifications/notifications.dart'; import 'package:test/test.dart'; import '../../scheduler_notifications/test/notification_adapter_contract.dart'; // ignore: avoid_relative_lib_imports import '../lib/desktop_notifications.dart'; void main() { runNotificationAdapterContractTests('desktop', () { final backend = _RecordingBackend(); return NotificationAdapterContractHarness( adapter: DesktopNotificationAdapter(backend: backend), scheduledRequests: () => backend.delivered, cancelledIds: () => backend.cancelled, ); }); test('desktop adapter delegates schedule and cancel to backend', () async { final backend = _RecordingBackend(); final adapter = DesktopNotificationAdapter(backend: backend); final request = _request('reminder-1'); await adapter.schedule(request); await adapter.cancel(' reminder-1 '); expect(backend.delivered, [request]); expect(backend.cancelled, ['reminder-1']); expect(adapter, isA()); }); test('linux backend invokes notify-send', () async { final calls = <_ProcessCall>[]; final logs = []; final backend = createDefaultDesktopNotificationBackend( platform: DesktopNotificationPlatform.linux, processRunner: (executable, arguments) async { calls.add(_ProcessCall(executable, arguments)); return ProcessResult(1, 0, '', ''); }, logSink: logs.add, ); await backend.deliver(_request('reminder-1')); expect(backend.isSupported, isTrue); expect(calls.single.executable, 'notify-send'); expect(calls.single.arguments, contains('Start task')); expect(logs, isEmpty); }); test('macOS backend invokes osascript', () async { final calls = <_ProcessCall>[]; final backend = createDefaultDesktopNotificationBackend( platform: DesktopNotificationPlatform.macos, processRunner: (executable, arguments) async { calls.add(_ProcessCall(executable, arguments)); return ProcessResult(1, 0, '', ''); }, ); await backend.deliver(_request('reminder-1')); expect(backend.isSupported, isTrue); expect(calls.single.executable, 'osascript'); expect(calls.single.arguments, contains('-e')); }); test('unsupported and Windows platforms fall back to log output', () async { final logs = []; final windows = createDefaultDesktopNotificationBackend( platform: DesktopNotificationPlatform.windows, logSink: logs.add, ); final unsupported = createDefaultDesktopNotificationBackend( platform: DesktopNotificationPlatform.unsupported, logSink: logs.add, ); await windows.deliver(_request('windows-reminder')); await unsupported.cancel(' unsupported-reminder '); expect(windows.isSupported, isFalse); expect(unsupported.isSupported, isFalse); expect(logs.first, contains('windows-reminder')); expect(logs.last, contains('unsupported-reminder')); }); test('fake adapter can stand in for scheduling workflows', () async { final adapter = FakeNotificationAdapter(); addTearDown(adapter.close); await _scheduleAndCancelReminder(adapter, _request('reminder-1')); expect(adapter.scheduledRequests, isEmpty); expect(adapter.cancelledIds, ['reminder-1']); }); test('default desktop adapter can be created on supported test platforms', () { if (!Platform.isLinux && !Platform.isMacOS && !Platform.isWindows) { markTestSkipped( 'Desktop notifications are unsupported on this platform.'); } expect(DesktopNotificationAdapter.defaultInstance(), isA()); }); } Future _scheduleAndCancelReminder( NotificationAdapter adapter, NotificationRequest request, ) async { await adapter.schedule(request); await adapter.cancel(request.id); } NotificationRequest _request(String id) { return NotificationRequest( id: id, title: 'Start task', body: 'Time to begin.', scheduledDateTimeUtc: DateTime.utc(2026, 1, 1, 9), ); } final class _RecordingBackend implements DesktopNotificationBackend { final List delivered = []; final List cancelled = []; @override bool get isSupported => true; @override Future deliver(NotificationRequest request) async { delivered.add(request); } @override Future cancel(String id) async { cancelled.add(id); } } final class _ProcessCall { const _ProcessCall(this.executable, this.arguments); final String executable; final List arguments; }