import 'dart:async'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mattermost_push_plugin/mattermost_push_plugin.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('NotificationOpenedEvent.fromMap', () { test('parses server_url, channel_id, root_id, is_crt_enabled', () { final event = NotificationOpenedEvent.fromMap({ 'server_url': 'https://mm.example.com', 'channel_id': 'channel-123', 'root_id': 'root-456', 'is_crt_enabled': 'true', }); expect(event.serverUrl, 'https://mm.example.com'); expect(event.channelId, 'channel-123'); expect(event.rootId, 'root-456'); expect(event.isCRTEnabled, isTrue); }); test('defaults isCRTEnabled to false when flag is absent or non-true', () { final missing = NotificationOpenedEvent.fromMap({ 'server_url': 'https://mm.example.com', 'channel_id': 'channel-123', }); expect(missing.isCRTEnabled, isFalse); final falsy = NotificationOpenedEvent.fromMap({ 'is_crt_enabled': 'false', }); expect(falsy.isCRTEnabled, isFalse); }); }); group('MattermostPushPlugin.handleNativeEvent', () { late MattermostPushPlugin plugin; final actionChannel = MethodChannel(MattermostPushPlugin.actionChannelName); setUp(() { plugin = MattermostPushPlugin.instance; plugin.onNavigateToChannel = null; plugin.onNavigateToThread = null; plugin.onDeviceTokenReady = null; }); tearDown(() { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(actionChannel, null); }); test('opened event emits NotificationOpenedEvent on stream', () async { final future = plugin.onNotificationOpened.first; plugin.handleNativeEvent({ 'type': PushNotificationType.opened, 'server_url': 'https://mm.example.com', 'channel_id': 'channel-123', }); final event = await future; expect(event.serverUrl, 'https://mm.example.com'); expect(event.channelId, 'channel-123'); }); test( 'message event with userInteraction routes through opened handler', () async { String? navServer; String? navChannel; plugin.onNavigateToChannel = (s, c) { navServer = s; navChannel = c; }; plugin.handleNativeEvent({ 'type': PushNotificationType.message, 'userInteraction': true, 'server_url': 'https://mm.example.com', 'channel_id': 'channel-xyz', }); expect(navServer, 'https://mm.example.com'); expect(navChannel, 'channel-xyz'); }, ); test('opened event with CRT and rootId routes to thread callback', () { String? navServer; String? navRoot; plugin.onNavigateToThread = (s, r) { navServer = s; navRoot = r; }; plugin.handleNativeEvent({ 'type': PushNotificationType.opened, 'server_url': 'https://mm.example.com', 'channel_id': 'channel-123', 'root_id': 'root-789', 'is_crt_enabled': 'true', }); expect(navServer, 'https://mm.example.com'); expect(navRoot, 'root-789'); }); test('opened event without server_url skips navigation callbacks', () { var channelCalled = false; var threadCalled = false; plugin.onNavigateToChannel = (_, _) => channelCalled = true; plugin.onNavigateToThread = (_, _) => threadCalled = true; plugin.handleNativeEvent({ 'type': PushNotificationType.opened, 'channel_id': 'channel-123', }); expect(channelCalled, isFalse); expect(threadCalled, isFalse); }); test('every event is forwarded to onNotification stream', () async { final future = plugin.onNotification.first; plugin.handleNativeEvent({ 'type': PushNotificationType.clear, 'channel_id': 'channel-123', }); final raw = await future; expect(raw['type'], PushNotificationType.clear); expect(raw['channel_id'], 'channel-123'); }); test( 'token_refresh event saves formatted device token and invokes callback', () async { String? channelToken; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(actionChannel, (call) async { if (call.method == 'saveDeviceToken') { final args = call.arguments as Map; channelToken = args['token'] as String?; } return null; }); final completer = Completer(); plugin.onDeviceTokenReady = completer.complete; plugin.handleNativeEvent({ 'type': PushNotificationType.tokenRefresh, 'token': 'raw-token', }); final callbackToken = await completer.future; expect(channelToken, 'android_rn-v2:raw-token'); expect(callbackToken, 'android_rn-v2:raw-token'); }, ); test( 'debugSendNativeEventForTesting invokes native debug method', () async { MethodCall? capturedCall; TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(actionChannel, (call) async { capturedCall = call; return null; }); await plugin.debugSendNativeEventForTesting({ 'type': PushNotificationType.opened, 'server_url': 'https://mm.example.com', 'channel_id': 'channel-debug', }); expect(capturedCall?.method, 'debugSendNativeEvent'); expect(capturedCall?.arguments, { 'type': PushNotificationType.opened, 'server_url': 'https://mm.example.com', 'channel_id': 'channel-debug', }); }, ); }); group('Channel constants', () { test('notification and action channel names are preserved', () { expect( MattermostPushPlugin.notificationChannelName, 'com.tokilabs.mattermost/notifications', ); expect( MattermostPushPlugin.actionChannelName, 'com.tokilabs.mattermost/notification_actions', ); }); }); }