mobile bluesky app made with flutter
lazurite.stormlightlabs.org/
mobile
bluesky
flutter
1import 'dart:async';
2
3import 'package:firebase_messaging/firebase_messaging.dart';
4import 'package:flutter_test/flutter_test.dart';
5import 'package:lazurite/features/notifications/data/firebase_push_token_provider.dart';
6import 'package:mocktail/mocktail.dart';
7
8class MockFirebaseMessaging extends Mock implements FirebaseMessaging {}
9
10void main() {
11 const grantedSettings = NotificationSettings(
12 alert: AppleNotificationSetting.enabled,
13 announcement: AppleNotificationSetting.enabled,
14 authorizationStatus: AuthorizationStatus.authorized,
15 badge: AppleNotificationSetting.enabled,
16 carPlay: AppleNotificationSetting.notSupported,
17 lockScreen: AppleNotificationSetting.enabled,
18 notificationCenter: AppleNotificationSetting.enabled,
19 showPreviews: AppleShowPreviewSetting.always,
20 timeSensitive: AppleNotificationSetting.notSupported,
21 criticalAlert: AppleNotificationSetting.notSupported,
22 sound: AppleNotificationSetting.enabled,
23 providesAppNotificationSettings: AppleNotificationSetting.notSupported,
24 );
25
26 group('FirebasePushTokenProvider', () {
27 late MockFirebaseMessaging messaging;
28 late StreamController<String> tokenRefreshController;
29
30 setUp(() {
31 messaging = MockFirebaseMessaging();
32 tokenRefreshController = StreamController<String>.broadcast();
33 when(() => messaging.onTokenRefresh).thenAnswer((_) => tokenRefreshController.stream);
34 when(
35 () => messaging.requestPermission(
36 alert: any(named: 'alert'),
37 badge: any(named: 'badge'),
38 sound: any(named: 'sound'),
39 provisional: any(named: 'provisional'),
40 ),
41 ).thenAnswer((_) async => grantedSettings);
42 when(() => messaging.setAutoInitEnabled(any())).thenAnswer((_) async {});
43 });
44
45 tearDown(() async {
46 await tokenRefreshController.close();
47 });
48
49 test('waits for APNs token availability on Apple platforms before requesting FCM token', () async {
50 var apnsCalls = 0;
51 when(() => messaging.getAPNSToken()).thenAnswer((_) async {
52 apnsCalls += 1;
53 return apnsCalls >= 2 ? 'apns-token' : null;
54 });
55 when(() => messaging.getToken(vapidKey: any(named: 'vapidKey'))).thenAnswer((_) async => 'fcm-token');
56
57 final provider = FirebasePushTokenProvider(
58 messaging: messaging,
59 isApplePlatform: () => true,
60 apnsTokenRetryAttempts: 3,
61 apnsTokenRetryDelay: Duration.zero,
62 delayFn: (_) async {},
63 );
64 addTearDown(provider.dispose);
65
66 final token = await provider.getToken();
67
68 expect(token, 'fcm-token');
69 expect(apnsCalls, greaterThanOrEqualTo(2));
70 verify(() => messaging.setAutoInitEnabled(true)).called(1);
71 });
72
73 test('does not query APNs token on non-Apple platforms', () async {
74 when(() => messaging.getToken(vapidKey: any(named: 'vapidKey'))).thenAnswer((_) async => 'fcm-token');
75
76 final provider = FirebasePushTokenProvider(
77 messaging: messaging,
78 isApplePlatform: () => false,
79 apnsTokenRetryAttempts: 1,
80 );
81 addTearDown(provider.dispose);
82
83 final token = await provider.getToken();
84
85 expect(token, 'fcm-token');
86 verifyNever(() => messaging.getAPNSToken());
87 });
88
89 test('forwards non-empty refreshed tokens', () async {
90 when(() => messaging.getAPNSToken()).thenAnswer((_) async => 'apns-token');
91 when(() => messaging.getToken(vapidKey: any(named: 'vapidKey'))).thenAnswer((_) async => 'fcm-token');
92
93 final provider = FirebasePushTokenProvider(
94 messaging: messaging,
95 isApplePlatform: () => true,
96 apnsTokenRetryAttempts: 1,
97 );
98 addTearDown(provider.dispose);
99
100 await provider.initialize();
101 final firstTokenFuture = provider.onTokenRefresh.first;
102 tokenRefreshController.add(' ');
103 tokenRefreshController.add('new-token');
104 final emitted = await firstTokenFuture;
105 expect(emitted, 'new-token');
106 });
107 });
108}