@react-native-firebase/app 25.1.0 → 26.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/RNFBApp.podspec +7 -13
  3. package/android/build.gradle +25 -6
  4. package/android/src/reactnative/java/io/invertase/firebase/app/{ReactNativeFirebaseAppModule.java → NativeRNFBTurboApp.java} +81 -45
  5. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.java +3 -3
  6. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseVersion.java +2 -2
  7. package/android/src/reactnative/java/io/invertase/firebase/app/generated/java/com/facebook/fbreact/specs/NativeRNFBTurboAppSpec.java +136 -0
  8. package/android/src/reactnative/java/io/invertase/firebase/app/generated/java/com/facebook/fbreact/specs/NativeRNFBTurboUtilsSpec.java +94 -0
  9. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/CMakeLists.txt +36 -0
  10. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/RNFBAppTurboModules-generated.cpp +170 -0
  11. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/RNFBAppTurboModules.h +39 -0
  12. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/react/renderer/components/RNFBAppTurboModules/RNFBAppTurboModulesJSI-generated.cpp +187 -0
  13. package/android/src/reactnative/java/io/invertase/firebase/app/generated/jni/react/renderer/components/RNFBAppTurboModules/RNFBAppTurboModulesJSI.h +606 -0
  14. package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.java +3 -3
  15. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.java +475 -20
  16. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java +8 -0
  17. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java +8 -0
  18. package/android/src/reactnative/java/io/invertase/firebase/utils/{ReactNativeFirebaseUtilsModule.java → NativeRNFBTurboUtils.java} +57 -40
  19. package/android/src/test/java/io/invertase/firebase/app/NativeRNFBTurboAppTest.java +400 -0
  20. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterTest.java +1823 -0
  21. package/app.plugin.js +1 -1
  22. package/dist/module/FirebaseApp.js +0 -9
  23. package/dist/module/FirebaseApp.js.map +1 -1
  24. package/dist/module/common/index.js +0 -645
  25. package/dist/module/common/index.js.map +1 -1
  26. package/dist/module/common/unitTestUtils.js +18 -54
  27. package/dist/module/common/unitTestUtils.js.map +1 -1
  28. package/dist/module/index.js +0 -1
  29. package/dist/module/index.js.map +1 -1
  30. package/dist/module/internal/RNFBNativeEventEmitter.js +12 -6
  31. package/dist/module/internal/RNFBNativeEventEmitter.js.map +1 -1
  32. package/dist/module/internal/constants.js +2 -2
  33. package/dist/module/internal/constants.js.map +1 -1
  34. package/dist/module/internal/index.js +1 -1
  35. package/dist/module/internal/index.js.map +1 -1
  36. package/dist/module/internal/nativeModuleAndroidIos.js +50 -10
  37. package/dist/module/internal/nativeModuleAndroidIos.js.map +1 -1
  38. package/dist/module/internal/nativeModuleWeb.js +12 -4
  39. package/dist/module/internal/nativeModuleWeb.js.map +1 -1
  40. package/dist/module/internal/registry/app.js +20 -12
  41. package/dist/module/internal/registry/app.js.map +1 -1
  42. package/dist/module/internal/registry/modular.js +73 -0
  43. package/dist/module/internal/registry/modular.js.map +1 -0
  44. package/dist/module/internal/registry/nativeModule.js +116 -95
  45. package/dist/module/internal/registry/nativeModule.js.map +1 -1
  46. package/dist/module/modular.js +44 -37
  47. package/dist/module/modular.js.map +1 -1
  48. package/dist/module/types/app.js +0 -3
  49. package/dist/module/types/app.js.map +1 -1
  50. package/dist/module/types/internal.js +0 -2
  51. package/dist/module/utils/UtilsStatics.js +2 -2
  52. package/dist/module/utils/UtilsStatics.js.map +1 -1
  53. package/dist/module/utils/index.js +21 -17
  54. package/dist/module/utils/index.js.map +1 -1
  55. package/dist/module/version.js +1 -1
  56. package/dist/typescript/lib/FirebaseApp.d.ts +1 -2
  57. package/dist/typescript/lib/FirebaseApp.d.ts.map +1 -1
  58. package/dist/typescript/lib/common/index.d.ts +0 -8
  59. package/dist/typescript/lib/common/index.d.ts.map +1 -1
  60. package/dist/typescript/lib/common/unitTestUtils.d.ts +1 -4
  61. package/dist/typescript/lib/common/unitTestUtils.d.ts.map +1 -1
  62. package/dist/typescript/lib/index.d.ts +0 -1
  63. package/dist/typescript/lib/index.d.ts.map +1 -1
  64. package/dist/typescript/lib/internal/NativeFirebaseError.d.ts +1 -1
  65. package/dist/typescript/lib/internal/NativeFirebaseError.d.ts.map +1 -1
  66. package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts.map +1 -1
  67. package/dist/typescript/lib/internal/constants.d.ts +2 -3
  68. package/dist/typescript/lib/internal/constants.d.ts.map +1 -1
  69. package/dist/typescript/lib/internal/index.d.ts +1 -1
  70. package/dist/typescript/lib/internal/index.d.ts.map +1 -1
  71. package/dist/typescript/lib/internal/nativeModuleAndroidIos.d.ts +2 -6
  72. package/dist/typescript/lib/internal/nativeModuleAndroidIos.d.ts.map +1 -1
  73. package/dist/typescript/lib/internal/nativeModuleWeb.d.ts.map +1 -1
  74. package/dist/typescript/lib/internal/registry/app.d.ts +2 -2
  75. package/dist/typescript/lib/internal/registry/app.d.ts.map +1 -1
  76. package/dist/typescript/lib/internal/registry/modular.d.ts +22 -0
  77. package/dist/typescript/lib/internal/registry/modular.d.ts.map +1 -0
  78. package/dist/typescript/lib/internal/registry/nativeModule.d.ts +3 -11
  79. package/dist/typescript/lib/internal/registry/nativeModule.d.ts.map +1 -1
  80. package/dist/typescript/lib/modular.d.ts +21 -7
  81. package/dist/typescript/lib/modular.d.ts.map +1 -1
  82. package/dist/typescript/lib/types/app.d.ts +4 -4
  83. package/dist/typescript/lib/types/app.d.ts.map +1 -1
  84. package/dist/typescript/lib/types/internal.d.ts +1 -33
  85. package/dist/typescript/lib/types/internal.d.ts.map +1 -1
  86. package/dist/typescript/lib/utils/index.d.ts +7 -3
  87. package/dist/typescript/lib/utils/index.d.ts.map +1 -1
  88. package/dist/typescript/lib/version.d.ts +1 -1
  89. package/firebase-schema.json +5 -1
  90. package/ios/RNFBApp/RNFBAppModule.h +1 -3
  91. package/ios/RNFBApp/{RNFBAppModule.m → RNFBAppModule.mm} +74 -77
  92. package/ios/RNFBApp/RNFBUtilsModule.h +1 -3
  93. package/ios/RNFBApp/{RNFBUtilsModule.m → RNFBUtilsModule.mm} +68 -25
  94. package/ios/RNFBApp/RNFBVersion.m +1 -1
  95. package/ios/RNFBApp.xcodeproj/project.pbxproj +12 -12
  96. package/ios/generated/RCTAppDependencyProvider.h +25 -0
  97. package/ios/generated/RCTAppDependencyProvider.mm +55 -0
  98. package/ios/generated/RCTModuleProviders.h +16 -0
  99. package/ios/generated/RCTModuleProviders.mm +52 -0
  100. package/ios/generated/RCTModulesConformingToProtocolsProvider.h +18 -0
  101. package/ios/generated/RCTModulesConformingToProtocolsProvider.mm +33 -0
  102. package/ios/generated/RCTThirdPartyComponentsProvider.h +16 -0
  103. package/ios/generated/RCTThirdPartyComponentsProvider.mm +23 -0
  104. package/ios/generated/RCTUnstableModulesRequiringMainQueueSetupProvider.h +14 -0
  105. package/ios/generated/RCTUnstableModulesRequiringMainQueueSetupProvider.mm +19 -0
  106. package/ios/generated/RNFBAppTurboModules/RNFBAppTurboModules-generated.mm +210 -0
  107. package/ios/generated/RNFBAppTurboModules/RNFBAppTurboModules.h +399 -0
  108. package/ios/generated/RNFBAppTurboModulesJSI-generated.cpp +187 -0
  109. package/ios/generated/RNFBAppTurboModulesJSI.h +606 -0
  110. package/ios/generated/ReactAppDependencyProvider.podspec +34 -0
  111. package/ios/generated/ReactCodegen.podspec +111 -0
  112. package/lib/FirebaseApp.ts +1 -10
  113. package/lib/common/index.ts +0 -742
  114. package/lib/common/unitTestUtils.ts +17 -73
  115. package/lib/index.ts +0 -1
  116. package/lib/internal/NativeFirebaseError.ts +1 -1
  117. package/lib/internal/RNFBNativeEventEmitter.ts +12 -6
  118. package/lib/internal/constants.ts +3 -25
  119. package/lib/internal/global.d.ts +0 -4
  120. package/lib/internal/index.ts +1 -1
  121. package/lib/internal/nativeModuleAndroidIos.ts +69 -12
  122. package/lib/internal/nativeModuleWeb.ts +11 -5
  123. package/lib/internal/registry/app.ts +24 -22
  124. package/lib/internal/registry/modular.ts +97 -0
  125. package/lib/internal/registry/nativeModule.ts +136 -107
  126. package/lib/modular.ts +47 -72
  127. package/lib/types/app.ts +6 -11
  128. package/lib/types/internal.ts +1 -46
  129. package/lib/utils/UtilsStatics.ts +2 -2
  130. package/lib/utils/index.ts +24 -18
  131. package/lib/version.ts +1 -1
  132. package/package.json +41 -3
  133. package/plugin/build/app.plugin.d.ts +2 -0
  134. package/plugin/build/app.plugin.js +6 -0
  135. package/plugin/src/app.plugin.ts +3 -0
  136. package/plugin/tsconfig.tsbuildinfo +1 -1
  137. package/react-native.config.js +2 -0
  138. package/specs/NativeRNFBTurboApp.ts +57 -0
  139. package/specs/NativeRNFBTurboUtils.ts +34 -0
  140. package/typedoc.json +1 -2
  141. package/dist/module/internal/registry/namespace.js +0 -262
  142. package/dist/module/internal/registry/namespace.js.map +0 -1
  143. package/dist/module/namespaced.js +0 -25
  144. package/dist/module/namespaced.js.map +0 -1
  145. package/dist/typescript/lib/internal/registry/namespace.d.ts +0 -26
  146. package/dist/typescript/lib/internal/registry/namespace.d.ts.map +0 -1
  147. package/dist/typescript/lib/namespaced.d.ts +0 -5
  148. package/dist/typescript/lib/namespaced.d.ts.map +0 -1
  149. package/lib/internal/registry/namespace.ts +0 -338
  150. package/lib/namespaced.ts +0 -24
@@ -24,16 +24,20 @@ import android.os.Build;
24
24
  import android.os.Environment;
25
25
  import android.provider.Settings;
26
26
  import android.util.Log;
27
- import com.facebook.react.bridge.*;
27
+ import com.facebook.fbreact.specs.NativeRNFBTurboUtilsSpec;
28
+ import com.facebook.react.bridge.Arguments;
29
+ import com.facebook.react.bridge.Promise;
30
+ import com.facebook.react.bridge.ReactApplicationContext;
31
+ import com.facebook.react.bridge.UiThreadUtil;
32
+ import com.facebook.react.bridge.WritableMap;
28
33
  import com.google.android.gms.common.ConnectionResult;
29
34
  import com.google.android.gms.common.GoogleApiAvailability;
30
35
  import io.invertase.firebase.app.ReactNativeFirebaseApp;
31
- import io.invertase.firebase.common.ReactNativeFirebaseModule;
32
36
  import java.io.File;
33
37
  import java.util.HashMap;
34
38
  import java.util.Map;
35
39
 
36
- public class ReactNativeFirebaseUtilsModule extends ReactNativeFirebaseModule {
40
+ public class NativeRNFBTurboUtils extends NativeRNFBTurboUtilsSpec {
37
41
  private static final String TAG = "Utils";
38
42
  private static final String KEY_MAIN_BUNDLE = "MAIN_BUNDLE";
39
43
  private static final String KEY_DOCUMENT_DIRECTORY = "DOCUMENT_DIRECTORY";
@@ -46,15 +50,10 @@ public class ReactNativeFirebaseUtilsModule extends ReactNativeFirebaseModule {
46
50
  private static final String KEY_CACHE_DIRECTORY = "CACHES_DIRECTORY";
47
51
  private static final String FIREBASE_TEST_LAB = "firebase.test.lab";
48
52
 
49
- public ReactNativeFirebaseUtilsModule(ReactApplicationContext reactContext) {
50
- super(reactContext, TAG);
53
+ public NativeRNFBTurboUtils(ReactApplicationContext reactContext) {
54
+ super(reactContext);
51
55
  }
52
56
 
53
- /**
54
- * Is this app running in Firebase Test Lab
55
- *
56
- * @return Boolean
57
- */
58
57
  private static Boolean isRunningInTestLab() {
59
58
  String testLabSetting =
60
59
  Settings.System.getString(
@@ -63,33 +62,32 @@ public class ReactNativeFirebaseUtilsModule extends ReactNativeFirebaseModule {
63
62
  return "true".equals(testLabSetting);
64
63
  }
65
64
 
66
- @ReactMethod
65
+ @Override
67
66
  public void androidGetPlayServicesStatus(Promise promise) {
68
67
  promise.resolve(getPlayServicesStatusMap());
69
68
  }
70
69
 
71
- /** Prompt the device user to update play services */
72
- @ReactMethod
73
- public void androidPromptForPlayServices() {
70
+ @Override
71
+ public void androidPromptForPlayServices(Promise promise) {
74
72
  int status = isGooglePlayServicesAvailable();
75
73
  GoogleApiAvailability gapi = GoogleApiAvailability.getInstance();
76
74
 
77
75
  if (status != ConnectionResult.SUCCESS && gapi.isUserResolvableError(status)) {
78
- Activity activity = getActivity();
76
+ Activity activity = getCurrentActivity();
79
77
  if (activity != null) {
80
78
  gapi.getErrorDialog(activity, status, status).show();
81
79
  }
82
80
  }
81
+ promise.resolve(null);
83
82
  }
84
83
 
85
- /** Prompt the device user to update play services */
86
- @ReactMethod
87
- public void androidResolutionForPlayServices() {
84
+ @Override
85
+ public void androidResolutionForPlayServices(Promise promise) {
88
86
  int status = isGooglePlayServicesAvailable();
89
87
  ConnectionResult connectionResult = new ConnectionResult(status);
90
88
 
91
89
  if (!connectionResult.isSuccess() && connectionResult.hasResolution()) {
92
- Activity activity = getActivity();
90
+ Activity activity = getCurrentActivity();
93
91
  if (activity != null) {
94
92
  try {
95
93
  connectionResult.startResolutionForResult(activity, status);
@@ -98,31 +96,55 @@ public class ReactNativeFirebaseUtilsModule extends ReactNativeFirebaseModule {
98
96
  }
99
97
  }
100
98
  }
99
+ promise.resolve(null);
101
100
  }
102
101
 
103
- /** Prompt the device user to update Play Services */
104
- @ReactMethod
105
- public void androidMakePlayServicesAvailable() {
102
+ @Override
103
+ public void androidMakePlayServicesAvailable(Promise promise) {
106
104
  int status = isGooglePlayServicesAvailable();
107
105
 
108
106
  if (status != ConnectionResult.SUCCESS) {
109
- Activity activity = getActivity();
110
- if (activity != null) {
111
- GoogleApiAvailability.getInstance().makeGooglePlayServicesAvailable(activity);
112
- }
107
+ UiThreadUtil.runOnUiThread(
108
+ () -> {
109
+ Activity activity = getCurrentActivity();
110
+ if (activity != null) {
111
+ GoogleApiAvailability.getInstance()
112
+ .makeGooglePlayServicesAvailable(activity)
113
+ .addOnCompleteListener(
114
+ task -> {
115
+ if (task.isSuccessful()) {
116
+ promise.resolve(null);
117
+ } else if (task.isCanceled()) {
118
+ promise.reject(
119
+ "play-services-update-canceled",
120
+ "Play Services update was canceled by the user");
121
+ } else {
122
+ Exception e = task.getException();
123
+ promise.reject(
124
+ "play-services-update-failed",
125
+ e != null ? e.getMessage() : "Unknown error",
126
+ e);
127
+ }
128
+ });
129
+ } else {
130
+ promise.resolve(null);
131
+ }
132
+ });
133
+ } else {
134
+ promise.resolve(null);
113
135
  }
114
136
  }
115
137
 
116
138
  private int isGooglePlayServicesAvailable() {
117
139
  GoogleApiAvailability gapi = GoogleApiAvailability.getInstance();
118
- return gapi.isGooglePlayServicesAvailable(getContext());
140
+ return gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
119
141
  }
120
142
 
121
143
  private WritableMap getPlayServicesStatusMap() {
122
144
  WritableMap result = Arguments.createMap();
123
145
  GoogleApiAvailability gapi = GoogleApiAvailability.getInstance();
124
146
 
125
- int status = gapi.isGooglePlayServicesAvailable(getContext());
147
+ int status = gapi.isGooglePlayServicesAvailable(getReactApplicationContext());
126
148
  result.putInt("status", status);
127
149
 
128
150
  if (status == ConnectionResult.SUCCESS) {
@@ -138,11 +160,10 @@ public class ReactNativeFirebaseUtilsModule extends ReactNativeFirebaseModule {
138
160
  }
139
161
 
140
162
  @Override
141
- public Map<String, Object> getConstants() {
163
+ protected Map<String, Object> getTypedExportedConstants() {
142
164
  Map<String, Object> constants = new HashMap<>();
143
165
 
144
166
  constants.put("isRunningInTestLab", isRunningInTestLab());
145
- constants.put("androidPlayServices", getPlayServicesStatusMap());
146
167
 
147
168
  Context context = getReactApplicationContext();
148
169
  constants.put(KEY_MAIN_BUNDLE, "");
@@ -155,10 +176,6 @@ public class ReactNativeFirebaseUtilsModule extends ReactNativeFirebaseModule {
155
176
  if (externalDirectory != null) {
156
177
  constants.put(KEY_DOCUMENT_DIRECTORY, externalDirectory.getAbsolutePath());
157
178
  } else {
158
- // The external directory may be null if it is truly external *and*
159
- // the device's external storage environment changes. We will use the regular
160
- // Files directory as a backup and note in the documentation that the directory may
161
- // vary under rare conditions
162
179
  constants.put(KEY_DOCUMENT_DIRECTORY, context.getFilesDir().getAbsolutePath());
163
180
  }
164
181
  }
@@ -167,15 +184,15 @@ public class ReactNativeFirebaseUtilsModule extends ReactNativeFirebaseModule {
167
184
  constants.put(KEY_DOCUMENT_DIRECTORY, context.getFilesDir().getAbsolutePath());
168
185
  }
169
186
 
187
+ File picturesDirectory =
188
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
170
189
  constants.put(
171
- KEY_PICS_DIRECTORY,
172
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
173
- .getAbsolutePath());
190
+ KEY_PICS_DIRECTORY, picturesDirectory != null ? picturesDirectory.getAbsolutePath() : "");
174
191
 
192
+ File moviesDirectory =
193
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES);
175
194
  constants.put(
176
- KEY_MOVIES_DIRECTORY,
177
- Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MOVIES)
178
- .getAbsolutePath());
195
+ KEY_MOVIES_DIRECTORY, moviesDirectory != null ? moviesDirectory.getAbsolutePath() : "");
179
196
 
180
197
  File externalStorageDirectory = Environment.getExternalStorageDirectory();
181
198
  if (externalStorageDirectory != null) {
@@ -0,0 +1,400 @@
1
+ package io.invertase.firebase.app;
2
+
3
+ /*
4
+ * Copyright (c) 2016-present Invertase Limited & Contributors
5
+ *
6
+ * Licensed under the Apache License, Version 2.0 (the "License");
7
+ * you may not use this library except in compliance with the License.
8
+ * You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing, software
13
+ * distributed under the License is distributed on an "AS IS" BASIS,
14
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ * See the License for the specific language governing permissions and
16
+ * limitations under the License.
17
+ *
18
+ */
19
+
20
+ import static org.junit.Assert.assertEquals;
21
+ import static org.junit.Assert.assertFalse;
22
+ import static org.junit.Assert.assertNull;
23
+ import static org.junit.Assert.assertTrue;
24
+ import static org.mockito.Mockito.mock;
25
+ import static org.mockito.Mockito.mockStatic;
26
+ import static org.mockito.Mockito.verify;
27
+ import static org.mockito.Mockito.when;
28
+
29
+ import android.app.Application;
30
+ import com.facebook.react.ReactApplication;
31
+ import com.facebook.react.ReactHost;
32
+ import com.facebook.react.ReactNativeHost;
33
+ import com.facebook.react.bridge.Arguments;
34
+ import com.facebook.react.bridge.Promise;
35
+ import com.facebook.react.bridge.ReactApplicationContext;
36
+ import com.facebook.react.bridge.ReactContext;
37
+ import com.facebook.react.bridge.ReadableMap;
38
+ import com.facebook.react.bridge.WritableMap;
39
+ import com.google.firebase.FirebaseApp;
40
+ import io.invertase.firebase.common.RCTConvertFirebase;
41
+ import io.invertase.firebase.common.ReactNativeFirebaseEventEmitter;
42
+ import io.invertase.firebase.common.ReactNativeFirebaseJSON;
43
+ import io.invertase.firebase.common.ReactNativeFirebaseMeta;
44
+ import io.invertase.firebase.common.ReactNativeFirebasePreferences;
45
+ import java.lang.ref.WeakReference;
46
+ import java.lang.reflect.Constructor;
47
+ import java.lang.reflect.Field;
48
+ import java.lang.reflect.Method;
49
+ import java.util.Collections;
50
+ import java.util.HashMap;
51
+ import java.util.List;
52
+ import java.util.Map;
53
+ import org.junit.Before;
54
+ import org.junit.Test;
55
+ import org.junit.runner.RunWith;
56
+ import org.mockito.MockedStatic;
57
+ import org.robolectric.RobolectricTestRunner;
58
+ import org.robolectric.annotation.Config;
59
+ import org.robolectric.shadows.ShadowLooper;
60
+
61
+ /**
62
+ * JVM LINE coverage for {@link NativeRNFBTurboApp} — lifecycle, constants, app CRUD, events, and
63
+ * preferences bridges.
64
+ */
65
+ @RunWith(RobolectricTestRunner.class)
66
+ @Config(sdk = 34, application = NativeRNFBTurboAppTest.HostApplication.class)
67
+ public class NativeRNFBTurboAppTest {
68
+
69
+ public static class HostApplication extends Application implements ReactApplication {
70
+ ReactHost reactHost;
71
+
72
+ @Override
73
+ public ReactNativeHost getReactNativeHost() {
74
+ throw new RuntimeException("bridgeless-only test host");
75
+ }
76
+
77
+ @Override
78
+ public ReactHost getReactHost() {
79
+ return reactHost;
80
+ }
81
+ }
82
+
83
+ private HostApplication application;
84
+ private ReactHost reactHost;
85
+ private ReactApplicationContext reactContext;
86
+
87
+ @Before
88
+ public void setUp() throws Exception {
89
+ application = (HostApplication) org.robolectric.RuntimeEnvironment.getApplication();
90
+ reactHost = mock(ReactHost.class);
91
+ application.reactHost = reactHost;
92
+
93
+ reactContext = mock(ReactApplicationContext.class);
94
+ when(reactContext.getApplicationContext()).thenReturn(application);
95
+ when(reactContext.hasActiveReactInstance()).thenReturn(true);
96
+ when(reactHost.getCurrentReactContext()).thenReturn(reactContext);
97
+
98
+ NativeRNFBTurboApp.authDomains.clear();
99
+ resetEmitterSharedInstance();
100
+ }
101
+
102
+ @Test
103
+ public void initialize_registersLifecycleListenerAndAttachesEmitter() throws Exception {
104
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
105
+ module.initialize();
106
+ ShadowLooper.idleMainLooper();
107
+
108
+ verify(reactContext).addLifecycleEventListener(module);
109
+ assertEquals(reactContext, attachedContext());
110
+ }
111
+
112
+ @Test
113
+ public void invalidate_removesLifecycleListenerAndDetachesEmitter() throws Exception {
114
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
115
+ ReactNativeFirebaseEventEmitter.getSharedInstance().attachReactContext(reactContext);
116
+ ShadowLooper.idleMainLooper();
117
+
118
+ module.invalidate();
119
+ ShadowLooper.idleMainLooper();
120
+
121
+ verify(reactContext).removeLifecycleEventListener(module);
122
+ assertNull(attachedContext());
123
+ }
124
+
125
+ @Test
126
+ public void onHostResume_reattachesEmitter() throws Exception {
127
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
128
+ module.onHostResume();
129
+ ShadowLooper.idleMainLooper();
130
+ assertEquals(reactContext, attachedContext());
131
+ }
132
+
133
+ @Test
134
+ public void onHostPause_and_onHostDestroy_areNoOps() {
135
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
136
+ module.onHostPause();
137
+ module.onHostDestroy();
138
+ }
139
+
140
+ @Test
141
+ public void getTypedExportedConstants_includesAppsAndRawJson() throws Exception {
142
+ FirebaseApp firebaseApp = mock(FirebaseApp.class);
143
+ Map<String, Object> appMap = new HashMap<>();
144
+ appMap.put("name", "[DEFAULT]");
145
+
146
+ try (MockedStatic<FirebaseApp> firebaseApps = mockStatic(FirebaseApp.class);
147
+ MockedStatic<RCTConvertFirebase> convert = mockStatic(RCTConvertFirebase.class);
148
+ MockedStatic<ReactNativeFirebaseJSON> jsonStatic =
149
+ mockStatic(ReactNativeFirebaseJSON.class)) {
150
+ firebaseApps
151
+ .when(() -> FirebaseApp.getApps(reactContext))
152
+ .thenReturn(Collections.singletonList(firebaseApp));
153
+ convert.when(() -> RCTConvertFirebase.firebaseAppToMap(firebaseApp)).thenReturn(appMap);
154
+
155
+ ReactNativeFirebaseJSON json = mock(ReactNativeFirebaseJSON.class);
156
+ jsonStatic.when(ReactNativeFirebaseJSON::getSharedInstance).thenReturn(json);
157
+ when(json.getRawJSON()).thenReturn("{}");
158
+
159
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
160
+ Method method = NativeRNFBTurboApp.class.getDeclaredMethod("getTypedExportedConstants");
161
+ method.setAccessible(true);
162
+ @SuppressWarnings("unchecked")
163
+ Map<String, Object> constants = (Map<String, Object>) method.invoke(module);
164
+
165
+ assertEquals("{}", constants.get("FIREBASE_RAW_JSON"));
166
+ @SuppressWarnings("unchecked")
167
+ List<Map<String, Object>> apps =
168
+ (List<Map<String, Object>>) constants.get("NATIVE_FIREBASE_APPS");
169
+ assertEquals(1, apps.size());
170
+ assertEquals("[DEFAULT]", apps.get(0).get("name"));
171
+ }
172
+ }
173
+
174
+ @Test
175
+ public void initializeApp_configuresAuthDomainAndResolvesPromise() {
176
+ ReadableMap options = mock(ReadableMap.class);
177
+ ReadableMap appConfig = mock(ReadableMap.class);
178
+ Promise promise = mock(Promise.class);
179
+ FirebaseApp firebaseApp = mock(FirebaseApp.class);
180
+ WritableMap writableMap = mock(WritableMap.class);
181
+
182
+ when(appConfig.getString("name")).thenReturn("secondary");
183
+ when(options.getString("authDomain")).thenReturn("example.firebaseapp.com");
184
+
185
+ try (MockedStatic<RCTConvertFirebase> convert = mockStatic(RCTConvertFirebase.class)) {
186
+ convert
187
+ .when(() -> RCTConvertFirebase.readableMapToFirebaseApp(options, appConfig, reactContext))
188
+ .thenReturn(firebaseApp);
189
+ convert
190
+ .when(() -> RCTConvertFirebase.firebaseAppToWritableMap(firebaseApp))
191
+ .thenReturn(writableMap);
192
+
193
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
194
+ module.initializeApp(options, appConfig, promise);
195
+
196
+ assertEquals("example.firebaseapp.com", NativeRNFBTurboApp.authDomains.get("secondary"));
197
+ verify(promise).resolve(writableMap);
198
+ }
199
+ }
200
+
201
+ @Test
202
+ public void configureAuthDomain_putsAndRemovesEntries() {
203
+ NativeRNFBTurboApp.configureAuthDomain("secondary", "example.firebaseapp.com");
204
+ assertEquals("example.firebaseapp.com", NativeRNFBTurboApp.authDomains.get("secondary"));
205
+ NativeRNFBTurboApp.configureAuthDomain("secondary", null);
206
+ assertFalse(NativeRNFBTurboApp.authDomains.containsKey("secondary"));
207
+ }
208
+
209
+ @Test
210
+ public void setAutomaticDataCollectionEnabled_delegatesToFirebaseApp() {
211
+ FirebaseApp firebaseApp = mock(FirebaseApp.class);
212
+ try (MockedStatic<FirebaseApp> firebaseApps = mockStatic(FirebaseApp.class)) {
213
+ firebaseApps.when(() -> FirebaseApp.getInstance("secondary")).thenReturn(firebaseApp);
214
+
215
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
216
+ module.setAutomaticDataCollectionEnabled("secondary", true);
217
+
218
+ verify(firebaseApp).setDataCollectionDefaultEnabled(true);
219
+ }
220
+ }
221
+
222
+ @Test
223
+ public void deleteApp_deletesWhenInstancePresent() {
224
+ FirebaseApp firebaseApp = mock(FirebaseApp.class);
225
+ Promise promise = mock(Promise.class);
226
+ try (MockedStatic<FirebaseApp> firebaseApps = mockStatic(FirebaseApp.class)) {
227
+ firebaseApps.when(() -> FirebaseApp.getInstance("secondary")).thenReturn(firebaseApp);
228
+
229
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
230
+ module.deleteApp("secondary", promise);
231
+
232
+ verify(firebaseApp).delete();
233
+ verify(promise).resolve(null);
234
+ }
235
+ }
236
+
237
+ @Test
238
+ public void deleteApp_resolvesWhenInstanceNull() {
239
+ Promise promise = mock(Promise.class);
240
+ try (MockedStatic<FirebaseApp> firebaseApps = mockStatic(FirebaseApp.class)) {
241
+ firebaseApps.when(() -> FirebaseApp.getInstance("missing")).thenReturn(null);
242
+
243
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
244
+ module.deleteApp("missing", promise);
245
+
246
+ verify(promise).resolve(null);
247
+ }
248
+ }
249
+
250
+ @Test
251
+ public void eventsNotifyReady_forwardsToEmitter() throws Exception {
252
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
253
+ module.eventsNotifyReady(true);
254
+ Field field = ReactNativeFirebaseEventEmitter.class.getDeclaredField("jsReady");
255
+ field.setAccessible(true);
256
+ assertTrue(field.getBoolean(ReactNativeFirebaseEventEmitter.getSharedInstance()));
257
+ }
258
+
259
+ @Test
260
+ public void eventsGetListeners_resolvesEmitterMap() {
261
+ Promise promise = mock(Promise.class);
262
+ WritableMap listenersMap = mock(WritableMap.class);
263
+ WritableMap eventsMap = mock(WritableMap.class);
264
+
265
+ try (MockedStatic<Arguments> arguments = mockStatic(Arguments.class)) {
266
+ arguments.when(Arguments::createMap).thenReturn(listenersMap, eventsMap);
267
+
268
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
269
+ module.eventsGetListeners(promise);
270
+
271
+ verify(promise).resolve(listenersMap);
272
+ }
273
+ }
274
+
275
+ @Test
276
+ public void eventsPing_sendsEventAndResolvesBody() {
277
+ ReadableMap body = mock(ReadableMap.class);
278
+ WritableMap writable = mock(WritableMap.class);
279
+ Promise promise = mock(Promise.class);
280
+
281
+ try (MockedStatic<RCTConvertFirebase> convert = mockStatic(RCTConvertFirebase.class)) {
282
+ convert.when(() -> RCTConvertFirebase.readableMapToWritableMap(body)).thenReturn(writable);
283
+
284
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
285
+ module.eventsPing("ping_event", body, promise);
286
+
287
+ verify(promise).resolve(writable);
288
+ }
289
+ }
290
+
291
+ @Test
292
+ public void eventsAddAndRemoveListener_delegateToEmitter() throws Exception {
293
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
294
+ module.eventsAddListener("auth_state_changed");
295
+ ShadowLooper.idleMainLooper();
296
+
297
+ Field field = ReactNativeFirebaseEventEmitter.class.getDeclaredField("jsListeners");
298
+ field.setAccessible(true);
299
+ @SuppressWarnings("unchecked")
300
+ Map<String, Integer> listeners =
301
+ (Map<String, Integer>) field.get(ReactNativeFirebaseEventEmitter.getSharedInstance());
302
+ assertEquals(Integer.valueOf(1), listeners.get("auth_state_changed"));
303
+
304
+ module.eventsRemoveListener("auth_state_changed", true);
305
+ assertFalse(listeners.containsKey("auth_state_changed"));
306
+ }
307
+
308
+ @Test
309
+ public void addListener_and_removeListeners_areNoOpsForRnBuiltInEmitter() {
310
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
311
+ module.addListener("rn_builtin");
312
+ module.removeListeners(1);
313
+ }
314
+
315
+ @Test
316
+ public void metaGetAll_resolvesMetaSingleton() {
317
+ Promise promise = mock(Promise.class);
318
+ WritableMap all = mock(WritableMap.class);
319
+ ReactNativeFirebaseMeta meta = mock(ReactNativeFirebaseMeta.class);
320
+ when(meta.getAll()).thenReturn(all);
321
+
322
+ try (MockedStatic<ReactNativeFirebaseMeta> metaStatic =
323
+ mockStatic(ReactNativeFirebaseMeta.class)) {
324
+ metaStatic.when(ReactNativeFirebaseMeta::getSharedInstance).thenReturn(meta);
325
+
326
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
327
+ module.metaGetAll(promise);
328
+ verify(promise).resolve(all);
329
+ }
330
+ }
331
+
332
+ @Test
333
+ public void jsonGetAll_resolvesJsonSingleton() {
334
+ Promise promise = mock(Promise.class);
335
+ WritableMap all = mock(WritableMap.class);
336
+ ReactNativeFirebaseJSON json = mock(ReactNativeFirebaseJSON.class);
337
+ when(json.getAll()).thenReturn(all);
338
+
339
+ try (MockedStatic<ReactNativeFirebaseJSON> jsonStatic =
340
+ mockStatic(ReactNativeFirebaseJSON.class)) {
341
+ jsonStatic.when(ReactNativeFirebaseJSON::getSharedInstance).thenReturn(json);
342
+
343
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
344
+ module.jsonGetAll(promise);
345
+ verify(promise).resolve(all);
346
+ }
347
+ }
348
+
349
+ @Test
350
+ public void preferencesSettersAndGetters_delegateToPreferencesSingleton() {
351
+ Promise promise = mock(Promise.class);
352
+ WritableMap all = mock(WritableMap.class);
353
+ ReactNativeFirebasePreferences prefs = mock(ReactNativeFirebasePreferences.class);
354
+ when(prefs.getAll()).thenReturn(all);
355
+
356
+ try (MockedStatic<ReactNativeFirebasePreferences> prefsStatic =
357
+ mockStatic(ReactNativeFirebasePreferences.class)) {
358
+ prefsStatic.when(ReactNativeFirebasePreferences::getSharedInstance).thenReturn(prefs);
359
+
360
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
361
+ module.preferencesSetBool("k", true, promise);
362
+ verify(prefs).setBooleanValue("k", true);
363
+ verify(promise).resolve(null);
364
+
365
+ module.preferencesSetString("k", "v", promise);
366
+ verify(prefs).setStringValue("k", "v");
367
+
368
+ module.preferencesGetAll(promise);
369
+ verify(promise).resolve(all);
370
+
371
+ module.preferencesClearAll(promise);
372
+ verify(prefs).clearAll();
373
+ }
374
+ }
375
+
376
+ @Test
377
+ public void setLogLevel_isNoOp() {
378
+ NativeRNFBTurboApp module = new NativeRNFBTurboApp(reactContext);
379
+ module.setLogLevel("debug");
380
+ }
381
+
382
+ private static void resetEmitterSharedInstance() throws Exception {
383
+ Constructor<ReactNativeFirebaseEventEmitter> ctor =
384
+ ReactNativeFirebaseEventEmitter.class.getDeclaredConstructor();
385
+ ctor.setAccessible(true);
386
+ ReactNativeFirebaseEventEmitter fresh = ctor.newInstance();
387
+ Field shared = ReactNativeFirebaseEventEmitter.class.getDeclaredField("sharedInstance");
388
+ shared.setAccessible(true);
389
+ shared.set(null, fresh);
390
+ }
391
+
392
+ @SuppressWarnings("unchecked")
393
+ private static ReactContext attachedContext() throws Exception {
394
+ ReactNativeFirebaseEventEmitter emitter = ReactNativeFirebaseEventEmitter.getSharedInstance();
395
+ Field field = ReactNativeFirebaseEventEmitter.class.getDeclaredField("attachedReactContext");
396
+ field.setAccessible(true);
397
+ WeakReference<ReactContext> ref = (WeakReference<ReactContext>) field.get(emitter);
398
+ return ref.get();
399
+ }
400
+ }