@react-native-firebase/app 26.4.0 → 27.1.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 (242) hide show
  1. package/CHANGELOG.md +62 -0
  2. package/README.md +10 -27
  3. package/RNFBApp.podspec +13 -5
  4. package/android/build.gradle +3 -0
  5. package/android/src/main/java/io/invertase/firebase/common/RNFBHandleCollisionException.kt +29 -0
  6. package/android/src/main/java/io/invertase/firebase/common/RNFBHandleMap.kt +146 -0
  7. package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.kt +156 -0
  8. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.kt +44 -0
  9. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.kt +100 -0
  10. package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.kt +250 -0
  11. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.kt +64 -0
  12. package/android/src/reactnative/java/io/invertase/firebase/app/{ReactNativeFirebaseAppInitProvider.java → ReactNativeFirebaseAppInitProvider.kt} +3 -4
  13. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.kt +47 -0
  14. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.kt +33 -0
  15. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseVersion.java +1 -1
  16. package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.kt +185 -0
  17. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.kt +35 -0
  18. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.kt +662 -0
  19. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.kt +103 -0
  20. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.kt +96 -0
  21. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.kt +140 -0
  22. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.kt +109 -0
  23. package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.kt +451 -0
  24. package/android/src/reactnative/java/io/invertase/firebase/interfaces/{ContextProvider.java → ContextProvider.kt} +8 -8
  25. package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeError.java → NativeError.kt} +8 -8
  26. package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeEvent.java → NativeEvent.kt} +6 -6
  27. package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.kt +219 -0
  28. package/android/src/test/java/expo/core/ModuleRegistry.java +9 -0
  29. package/android/src/test/java/io/invertase/firebase/app/NativeRNFBTurboAppTest.java +182 -59
  30. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppComponentsJavaCompatibilityTest.java +70 -0
  31. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppJavaCompatibilityTest.java +60 -0
  32. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackageTest.java +165 -0
  33. package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppTest.kt +167 -0
  34. package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseJavaCompatibilityTest.java +112 -0
  35. package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseTest.kt +317 -0
  36. package/android/src/test/java/io/invertase/firebase/common/RNFBHandleMapTest.kt +309 -0
  37. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterJavaCompatibilityTest.java +176 -0
  38. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterTest.java +143 -12
  39. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventTest.kt +61 -0
  40. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONJavaCompatibilityTest.java +85 -0
  41. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONTest.kt +296 -0
  42. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaJavaCompatibilityTest.java +67 -0
  43. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaTest.kt +246 -0
  44. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseModuleTest.kt +254 -0
  45. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesJavaCompatibilityTest.java +87 -0
  46. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesTest.kt +260 -0
  47. package/android/src/test/java/io/invertase/firebase/common/SharedUtilsJavaCompatibilityTest.java +255 -0
  48. package/android/src/test/java/io/invertase/firebase/common/SharedUtilsTest.kt +674 -0
  49. package/android/src/test/java/io/invertase/firebase/common/TaskExecutorServiceTest.kt +290 -0
  50. package/android/src/test/java/io/invertase/firebase/common/UniversalFirebaseModuleJavaCompatibilityTest.java +178 -0
  51. package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesJavaCompatibilityTest.java +99 -0
  52. package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesTest.kt +217 -0
  53. package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleJavaCompatibilityTest.java +176 -0
  54. package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleKotlinCompatibilityTest.kt +52 -0
  55. package/android/src/test/java/io/invertase/firebase/interfaces/InterfacesJavaCompatibilityTest.java +144 -0
  56. package/android/src/test/java/io/invertase/firebase/interfaces/NativeEventTest.kt +56 -0
  57. package/android/src/test/java/io/invertase/firebase/utils/NativeRNFBTurboUtilsTest.java +517 -21
  58. package/dist/module/FirebaseApp.js +10 -0
  59. package/dist/module/FirebaseApp.js.map +1 -1
  60. package/dist/module/common/deeps.js +3 -0
  61. package/dist/module/common/deeps.js.map +1 -1
  62. package/dist/module/internal/FirebaseModule.js.map +1 -1
  63. package/dist/module/internal/RNFBNativeEventEmitter.js +12 -1
  64. package/dist/module/internal/RNFBNativeEventEmitter.js.map +1 -1
  65. package/dist/module/internal/SharedEventEmitter.js +3 -1
  66. package/dist/module/internal/SharedEventEmitter.js.map +1 -1
  67. package/dist/module/internal/index.js +1 -0
  68. package/dist/module/internal/index.js.map +1 -1
  69. package/dist/module/internal/registry/app.js +7 -1
  70. package/dist/module/internal/registry/app.js.map +1 -1
  71. package/dist/module/types/app.js.map +1 -1
  72. package/dist/module/types/internal.js +2 -0
  73. package/dist/module/version.js +1 -1
  74. package/dist/typescript/lib/FirebaseApp.d.ts +7 -0
  75. package/dist/typescript/lib/FirebaseApp.d.ts.map +1 -1
  76. package/dist/typescript/lib/common/deeps.d.ts.map +1 -1
  77. package/dist/typescript/lib/internal/FirebaseModule.d.ts +2 -3
  78. package/dist/typescript/lib/internal/FirebaseModule.d.ts.map +1 -1
  79. package/dist/typescript/lib/internal/NativeModules.d.ts +4 -1
  80. package/dist/typescript/lib/internal/NativeModules.d.ts.map +1 -1
  81. package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts +2 -2
  82. package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts.map +1 -1
  83. package/dist/typescript/lib/internal/SharedEventEmitter.d.ts +3 -3
  84. package/dist/typescript/lib/internal/SharedEventEmitter.d.ts.map +1 -1
  85. package/dist/typescript/lib/internal/index.d.ts +1 -0
  86. package/dist/typescript/lib/internal/index.d.ts.map +1 -1
  87. package/dist/typescript/lib/internal/registry/app.d.ts.map +1 -1
  88. package/dist/typescript/lib/types/app.d.ts +10 -3
  89. package/dist/typescript/lib/types/app.d.ts.map +1 -1
  90. package/dist/typescript/lib/types/internal.d.ts +12 -0
  91. package/dist/typescript/lib/types/internal.d.ts.map +1 -1
  92. package/dist/typescript/lib/version.d.ts +1 -1
  93. package/firebase-schema.json +4 -0
  94. package/firebase_spm.rb +552 -96
  95. package/ios/RNFBApp/RCTConvert+FIRApp.h +2 -5
  96. package/ios/RNFBApp/RCTConvert+FIRApp.m +11 -6
  97. package/ios/RNFBApp/RCTConvert+FIROptions.h +2 -5
  98. package/ios/RNFBApp/RCTConvert+FIROptions.m +11 -10
  99. package/ios/RNFBApp/RCTConvertFIRApp.swift +86 -0
  100. package/ios/RNFBApp/RCTConvertFIROptions.swift +259 -0
  101. package/ios/RNFBApp/RNFBAppCustomAuthDomains.swift +63 -0
  102. package/ios/RNFBApp/RNFBAppInitializeOptionsMapper.swift +113 -0
  103. package/ios/RNFBApp/RNFBAppLogLevelMapper.swift +53 -0
  104. package/ios/RNFBApp/RNFBAppModule+CustomAuthDomain.m +35 -0
  105. package/ios/RNFBApp/RNFBAppModule.h +6 -2
  106. package/ios/RNFBApp/RNFBAppModule.mm +65 -228
  107. package/ios/RNFBApp/RNFBAppModuleFirebase.swift +439 -0
  108. package/ios/RNFBApp/RNFBAppModuleImplementation.h +68 -0
  109. package/ios/RNFBApp/RNFBAppModuleImplementation.m +195 -0
  110. package/ios/RNFBApp/RNFBHandleMap.m +27 -56
  111. package/ios/RNFBApp/RNFBHandleMapStorage.swift +90 -0
  112. package/ios/RNFBApp/RNFBInitializeAppArguments.swift +82 -0
  113. package/ios/RNFBApp/RNFBJSON.m +22 -38
  114. package/ios/RNFBApp/RNFBJSONImplementation.swift +71 -0
  115. package/ios/RNFBApp/RNFBMeta.m +24 -26
  116. package/ios/RNFBApp/RNFBMetaStorage.swift +76 -0
  117. package/ios/RNFBApp/RNFBNullSentinelDecoding.swift +135 -0
  118. package/ios/RNFBApp/RNFBNullSentinelInterceptor.h +30 -3
  119. package/ios/RNFBApp/RNFBNullSentinelInterceptor.m +28 -5
  120. package/ios/RNFBApp/RNFBPreferences.m +22 -20
  121. package/ios/RNFBApp/RNFBPreferencesStorage.swift +101 -0
  122. package/ios/RNFBApp/RNFBRCTEventEmitter.m +36 -93
  123. package/ios/RNFBApp/RNFBRCTEventEmitterCore.swift +161 -0
  124. package/ios/RNFBApp/RNFBSharedUtils.h +2 -5
  125. package/ios/RNFBApp/RNFBSharedUtils.m +109 -198
  126. package/ios/RNFBApp/RNFBSharedUtilsConfig.swift +72 -0
  127. package/ios/RNFBApp/RNFBSharedUtilsFIRApp.swift +271 -0
  128. package/ios/RNFBApp/RNFBSharedUtilsFormatting.swift +59 -0
  129. package/ios/RNFBApp/RNFBSharedUtilsPromiseRejection.swift +88 -0
  130. package/ios/RNFBApp/RNFBUtilsHelpers.swift +116 -0
  131. package/ios/RNFBApp/RNFBUtilsModule+Foundation.m +44 -0
  132. package/ios/RNFBApp/RNFBUtilsModule+PhotoAssets.m +47 -0
  133. package/ios/RNFBApp/RNFBUtilsModule.h +11 -1
  134. package/ios/RNFBApp/RNFBUtilsModule.mm +10 -85
  135. package/ios/RNFBApp/RNFBUtilsModuleImplementation.h +32 -0
  136. package/ios/RNFBApp/RNFBUtilsModuleImplementation.m +56 -0
  137. package/ios/RNFBApp/RNFBVersion.m +1 -1
  138. package/ios/RNFBApp.xcodeproj/project.pbxproj +100 -0
  139. package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FIRApp.m +171 -0
  140. package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FirebaseCore.h +78 -0
  141. package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FIRInstallations.m +7 -0
  142. package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FirebaseInstallations.h +8 -0
  143. package/ios/RNFBAppUnitTests/HostStubs/RNFBAppModuleHostStub.m +55 -0
  144. package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.h +89 -0
  145. package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.mm +30 -0
  146. package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.h +19 -0
  147. package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.m +32 -0
  148. package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridgeModule.h +19 -0
  149. package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.h +28 -0
  150. package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.m +23 -0
  151. package/ios/RNFBAppUnitTests/HostStubs/React/RCTEventEmitter.h +9 -0
  152. package/ios/RNFBAppUnitTests/HostStubs/React/RCTInvalidating.h +24 -0
  153. package/ios/RNFBAppUnitTests/HostStubs/React/RCTUtils.h +9 -0
  154. package/ios/RNFBAppUnitTests/HostStubs/module.modulemap +13 -0
  155. package/ios/RNFBAppUnitTests/RCTConvertFIRAppFacadeTests.m +61 -0
  156. package/ios/RNFBAppUnitTests/RCTConvertFIRAppTests.swift +73 -0
  157. package/ios/RNFBAppUnitTests/RCTConvertFIROptionsFacadeTests.m +57 -0
  158. package/ios/RNFBAppUnitTests/RCTConvertFIROptionsTests.swift +243 -0
  159. package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsFacadeTests.m +73 -0
  160. package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsTests.swift +70 -0
  161. package/ios/RNFBAppUnitTests/RNFBAppInitializeOptionsMapperTests.swift +190 -0
  162. package/ios/RNFBAppUnitTests/RNFBAppLogLevelFacadeTests.m +76 -0
  163. package/ios/RNFBAppUnitTests/RNFBAppLogLevelMapperTests.swift +59 -0
  164. package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseFacadeTests.m +215 -0
  165. package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseTests.swift +430 -0
  166. package/ios/RNFBAppUnitTests/RNFBAppModuleLifecycleTests.m +206 -0
  167. package/ios/RNFBAppUnitTests/RNFBAppModuleShellTests.mm +367 -0
  168. package/ios/RNFBAppUnitTests/RNFBAppUnitTests.xcodeproj/project.pbxproj +355 -2
  169. package/ios/RNFBAppUnitTests/RNFBFirebaseAppClientTests.swift +165 -0
  170. package/ios/RNFBAppUnitTests/RNFBHandleMapStorageTests.swift +121 -0
  171. package/ios/RNFBAppUnitTests/RNFBInitializeAppArgumentsTests.swift +256 -0
  172. package/ios/RNFBAppUnitTests/RNFBJSONImplementationTests.swift +126 -0
  173. package/ios/RNFBAppUnitTests/RNFBJSONTests.m +172 -0
  174. package/ios/RNFBAppUnitTests/RNFBMetaStorageTests.swift +106 -0
  175. package/ios/RNFBAppUnitTests/RNFBMetaTests.m +65 -0
  176. package/ios/RNFBAppUnitTests/RNFBNullSentinelDecodingTests.swift +286 -0
  177. package/ios/RNFBAppUnitTests/RNFBNullSentinelInterceptorTests.m +312 -0
  178. package/ios/RNFBAppUnitTests/RNFBPreferencesStorageTests.swift +105 -0
  179. package/ios/RNFBAppUnitTests/RNFBPreferencesTests.m +110 -0
  180. package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterCoreTests.swift +163 -0
  181. package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterFacadeTests.m +141 -0
  182. package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigFacadeTests.m +100 -0
  183. package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigTests.swift +234 -0
  184. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppFacadeTests.m +145 -0
  185. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppTests.swift +173 -0
  186. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingFacadeTests.m +38 -0
  187. package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingTests.swift +82 -0
  188. package/ios/RNFBAppUnitTests/RNFBSharedUtilsNullSentinelTests.m +35 -0
  189. package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionFacadeTests.m +107 -0
  190. package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionTests.swift +133 -0
  191. package/ios/RNFBAppUnitTests/RNFBUtilsHelpersTests.swift +144 -0
  192. package/ios/RNFBAppUnitTests/RNFBUtilsModuleFacadeTests.m +72 -0
  193. package/ios/RNFBAppUnitTests/RNFBUtilsModuleShellTests.mm +282 -0
  194. package/ios/RNFBFirebase/Package.swift +53 -0
  195. package/ios/RNFBFirebase/Sources/RNFBFirebase/RNFBFirebase.swift +285 -0
  196. package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/Public/.gitkeep +0 -0
  197. package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/RNFBFirebaseBridge.m +169 -0
  198. package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/include/RNFBFirebaseBridge.h +52 -0
  199. package/ios/RNFBFirebaseUnitTests/RNFBAppDynamicFacadeTests.swift +240 -0
  200. package/{android/src/main/java/io/invertase/firebase/common/RNFBHandleCollisionException.java → ios/RNFBFirebaseUnitTests/RNFBFirebaseClientImport.swift} +23 -14
  201. package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/project.pbxproj +588 -0
  202. package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBFirebaseUnitTests.xcscheme +83 -0
  203. package/ios_config.sh +50 -22
  204. package/lib/FirebaseApp.ts +10 -0
  205. package/lib/common/deeps.ts +3 -0
  206. package/lib/internal/FirebaseModule.ts +6 -3
  207. package/lib/internal/NativeModules.ts +4 -1
  208. package/lib/internal/RNFBNativeEventEmitter.ts +14 -5
  209. package/lib/internal/SharedEventEmitter.ts +5 -1
  210. package/lib/internal/index.ts +1 -0
  211. package/lib/internal/react-native-vendor-emitter.d.ts +11 -0
  212. package/lib/internal/registry/app.ts +7 -1
  213. package/lib/types/app.ts +10 -3
  214. package/lib/types/internal.ts +19 -0
  215. package/lib/version.ts +1 -1
  216. package/package.json +8 -8
  217. package/plugin/build/ios/appDelegate.js +2 -2
  218. package/plugin/build/ios/podfile.js +16 -16
  219. package/plugin/build/pluginConfig.d.ts +4 -2
  220. package/plugin/src/ios/appDelegate.ts +2 -2
  221. package/plugin/src/ios/podfile.ts +20 -23
  222. package/plugin/src/pluginConfig.ts +4 -2
  223. package/android/src/main/java/io/invertase/firebase/common/RNFBHandleMap.java +0 -150
  224. package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.java +0 -148
  225. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.java +0 -62
  226. package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.java +0 -91
  227. package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.java +0 -224
  228. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.java +0 -46
  229. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.java +0 -49
  230. package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.java +0 -35
  231. package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.java +0 -192
  232. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.java +0 -53
  233. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.java +0 -606
  234. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.java +0 -109
  235. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java +0 -101
  236. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.java +0 -146
  237. package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java +0 -96
  238. package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.java +0 -441
  239. package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.java +0 -223
  240. package/android/src/test/java/io/invertase/firebase/common/RNFBHandleMapTest.java +0 -275
  241. package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONTest.java +0 -108
  242. package/android/src/test/java/io/invertase/firebase/common/TaskExecutorServiceTest.java +0 -97
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ #import <XCTest/XCTest.h>
19
+ #import <objc/runtime.h>
20
+
21
+ #import "RNFBNullSentinelInterceptor.h"
22
+
23
+ /** Set by the original (pre-swizzle) IMPs of every runtime-created stand-in class. */
24
+ static id gLastJsonSeenByOriginalIMP = nil;
25
+
26
+ static NSString *const kSpecSelector = @"JS_NativeRNFBTurboFunctions_SpecHttpsCallableData:";
27
+ /** Prefix matches JS_NativeRNFBTurbo* but lacks _Spec: must not be wrapped. */
28
+ static NSString *const kNotASpecSelector = @"JS_NativeRNFBTurboFunctions_NotASpecMethod:";
29
+ static NSString *const kUnrelatedSelector = @"unrelatedConverter:";
30
+ static NSString *const kCxxConvertSpecSelector = @"JS_NativeRNFBTurboApp_SpecData:";
31
+ static NSString *const kCxxConvertClassName = @"RCTCxxConvert";
32
+
33
+ @interface RNFBNullSentinelInterceptorTests : XCTestCase
34
+ @end
35
+
36
+ /**
37
+ * Each test that needs a class builds its own uniquely named one at runtime and disposes of it in
38
+ * tearDown, so no test observes IMP wrapping or a global RCTCxxConvert registered by another test.
39
+ * That makes the suite independent of XCTest execution order and safe to run singly.
40
+ */
41
+ @implementation RNFBNullSentinelInterceptorTests {
42
+ // Class pointers are stored as NSValue so ARC never retains/releases a class after disposal.
43
+ // Per-test stand-ins: scheduled blocks may capture these, so tearDown spares them on a drain
44
+ // timeout.
45
+ NSMutableArray<NSValue *> *_standInClasses;
46
+ // Fake global RCTCxxConvert: nothing queued captures it, so tearDown always disposes it.
47
+ NSMutableArray<NSValue *> *_globalClasses;
48
+ // Class pointer -> the IMPs of the block-backed methods this test added to it.
49
+ NSMutableDictionary<NSValue *, NSArray<NSValue *> *> *_originalIMPsByClass;
50
+ // Heap-allocated dispatch_once_t storage (see -newOnceToken), freed in tearDown.
51
+ NSMutableArray<NSValue *> *_onceTokens;
52
+ }
53
+
54
+ - (void)setUp {
55
+ [super setUp];
56
+ gLastJsonSeenByOriginalIMP = nil;
57
+ _standInClasses = [NSMutableArray array];
58
+ _globalClasses = [NSMutableArray array];
59
+ _originalIMPsByClass = [NSMutableDictionary dictionary];
60
+ _onceTokens = [NSMutableArray array];
61
+ }
62
+
63
+ - (void)tearDown {
64
+ // schedule...WithOnceToken: dispatches a block to the main queue that captures the once token
65
+ // pointer and the stand-in class. Drain the main queue (FIFO, so every block queued by the test
66
+ // has run once the drain block does) before disposing those or freeing the token. If the drain
67
+ // times out, leak them instead of risking a use-after-free in a late block. The global
68
+ // RCTCxxConvert is never captured by a queued block, so it is always disposed; otherwise a
69
+ // leaked registration would fail every later RCTCxxConvert test.
70
+ BOOL drained = [self drainMainQueue];
71
+ [self disposeClasses:_globalClasses];
72
+ if (drained) {
73
+ [self disposeClasses:_standInClasses];
74
+ for (NSValue *tokenValue in _onceTokens) {
75
+ free(tokenValue.pointerValue);
76
+ }
77
+ }
78
+ [_standInClasses removeAllObjects];
79
+ [_globalClasses removeAllObjects];
80
+ [_originalIMPsByClass removeAllObjects];
81
+ [_onceTokens removeAllObjects];
82
+ gLastJsonSeenByOriginalIMP = nil;
83
+ [super tearDown];
84
+ }
85
+
86
+ /**
87
+ * Disposes of the given classes and removes their block-backed IMPs. Each block IMP must be
88
+ * removed exactly once (a second imp_removeBlock crashes), so gather the unique set per class: the
89
+ * original stand-in IMPs plus any wrapper IMPs the interceptor swizzled in.
90
+ */
91
+ - (void)disposeClasses:(NSArray<NSValue *> *)classValues {
92
+ for (NSValue *classValue in classValues) {
93
+ __unsafe_unretained Class cls = (__bridge Class)classValue.pointerValue;
94
+ NSMutableSet<NSValue *> *blockIMPs =
95
+ [NSMutableSet setWithArray:_originalIMPsByClass[classValue] ?: @[]];
96
+ unsigned int count = 0;
97
+ Method *methods = class_copyMethodList(object_getClass(cls), &count);
98
+ for (unsigned int i = 0; i < count; i++) {
99
+ [blockIMPs
100
+ addObject:[NSValue valueWithPointer:(const void *)method_getImplementation(methods[i])]];
101
+ }
102
+ free(methods);
103
+ objc_disposeClassPair(cls);
104
+ for (NSValue *value in blockIMPs) {
105
+ imp_removeBlock((IMP)value.pointerValue);
106
+ }
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Creates and registers a class named `name` whose class methods (one per selector name) record
112
+ * the received json in gLastJsonSeenByOriginalIMP and return it. Disposed in tearDown. `global`
113
+ * marks a class registered under a well-known global name (RCTCxxConvert).
114
+ */
115
+ - (Class)registerClassNamed:(NSString *)name
116
+ selectorNames:(NSArray<NSString *> *)selectorNames
117
+ global:(BOOL)global {
118
+ Class cls = objc_allocateClassPair([NSObject class], name.UTF8String, 0);
119
+ XCTAssertTrue(cls != Nil, @"could not allocate class %@", name);
120
+ if (cls == Nil) {
121
+ return Nil;
122
+ }
123
+ Class meta = object_getClass(cls);
124
+ NSMutableArray<NSValue *> *imps = [NSMutableArray array];
125
+ for (NSString *selectorName in selectorNames) {
126
+ IMP imp = imp_implementationWithBlock(^id(__unused id receiver, id json) {
127
+ gLastJsonSeenByOriginalIMP = json;
128
+ return json;
129
+ });
130
+ BOOL added = class_addMethod(meta, NSSelectorFromString(selectorName), imp, "@@:@");
131
+ XCTAssertTrue(added, @"could not add class method %@ to %@", selectorName, name);
132
+ [imps addObject:[NSValue valueWithPointer:(const void *)imp]];
133
+ }
134
+ objc_registerClassPair(cls);
135
+ NSValue *classValue = [NSValue valueWithPointer:(__bridge const void *)cls];
136
+ _originalIMPsByClass[classValue] = imps;
137
+ [global ? _globalClasses : _standInClasses addObject:classValue];
138
+ return cls;
139
+ }
140
+
141
+ /** Stand-in for RCTCxxConvert codegen categories, uniquely named per call. */
142
+ - (Class)makeStandInConvertClass {
143
+ static NSUInteger counter = 0; // XCTest runs test methods serially on one thread.
144
+ NSString *name =
145
+ [NSString stringWithFormat:@"RNFBNullSentinelStandInConvert_%lu", (unsigned long)++counter];
146
+ return [self registerClassNamed:name
147
+ selectorNames:@[ kSpecSelector, kNotASpecSelector, kUnrelatedSelector ]
148
+ global:NO];
149
+ }
150
+
151
+ - (NSDictionary *)nullSentinel {
152
+ return @{@"__rnfbNull" : @YES};
153
+ }
154
+
155
+ - (NSDictionary *)unrelatedOneKeyDictionary {
156
+ return @{@".sv" : @"timestamp"};
157
+ }
158
+
159
+ - (IMP)classMethodIMP:(Class)cls selector:(SEL)selector {
160
+ Method method = class_getClassMethod(cls, selector);
161
+ XCTAssertTrue(method != NULL, @"missing class method %@", NSStringFromSelector(selector));
162
+ return method_getImplementation(method);
163
+ }
164
+
165
+ /** Invokes the class method's current IMP, so swizzled wrappers are exercised. */
166
+ - (id)callConvert:(Class)cls selectorName:(NSString *)selectorName json:(id)json {
167
+ typedef id (*ConvertFunc)(id, SEL, id);
168
+ SEL selector = NSSelectorFromString(selectorName);
169
+ ConvertFunc convert = (ConvertFunc)[self classMethodIMP:cls selector:selector];
170
+ return convert(cls, selector, json);
171
+ }
172
+
173
+ /**
174
+ * Heap-allocated once token. A stack token would dangle if a dispatched block ran after the test
175
+ * method returned. Freed in tearDown, after the main queue is drained.
176
+ */
177
+ - (dispatch_once_t *)newOnceToken {
178
+ dispatch_once_t *token = calloc(1, sizeof(dispatch_once_t));
179
+ [_onceTokens addObject:[NSValue valueWithPointer:token]];
180
+ return token;
181
+ }
182
+
183
+ /** Returns YES once every main-queue block queued before this call has run. */
184
+ - (BOOL)drainMainQueue {
185
+ XCTestExpectation *drained = [self expectationWithDescription:@"main queue drained"];
186
+ dispatch_async(dispatch_get_main_queue(), ^{
187
+ [drained fulfill];
188
+ });
189
+ XCTWaiterResult result = [XCTWaiter waitForExpectations:@[ drained ] timeout:2.0];
190
+ XCTAssertEqual(result, XCTWaiterResultCompleted, @"main queue did not drain in time");
191
+ return result == XCTWaiterResultCompleted;
192
+ }
193
+
194
+ - (void)testSwizzleTurboModuleConversions_nullSentinel_handsNSNullToOriginalIMP {
195
+ Class standIn = [self makeStandInConvertClass];
196
+ SEL selector = NSSelectorFromString(kSpecSelector);
197
+ IMP before = [self classMethodIMP:standIn selector:selector];
198
+
199
+ [RNFBNullSentinelInterceptor swizzleTurboModuleConversions:standIn];
200
+
201
+ IMP after = [self classMethodIMP:standIn selector:selector];
202
+ XCTAssertNotEqual(before, after);
203
+
204
+ gLastJsonSeenByOriginalIMP = nil;
205
+ id result = [self callConvert:standIn selectorName:kSpecSelector json:[self nullSentinel]];
206
+ XCTAssertEqualObjects(gLastJsonSeenByOriginalIMP, [NSNull null]);
207
+ XCTAssertEqualObjects(result, [NSNull null]);
208
+ }
209
+
210
+ - (void)testSwizzleTurboModuleConversions_unrelatedOneKeyDictionary_unchanged {
211
+ Class standIn = [self makeStandInConvertClass];
212
+ [RNFBNullSentinelInterceptor swizzleTurboModuleConversions:standIn];
213
+
214
+ gLastJsonSeenByOriginalIMP = nil;
215
+ NSDictionary *payload = [self unrelatedOneKeyDictionary];
216
+ id result = [self callConvert:standIn selectorName:kSpecSelector json:payload];
217
+
218
+ XCTAssertEqualObjects(gLastJsonSeenByOriginalIMP, payload);
219
+ XCTAssertEqualObjects(result, payload);
220
+ }
221
+
222
+ - (void)testScheduleSwizzle_doesNotSwizzleUntilMainQueueRuns {
223
+ Class standIn = [self makeStandInConvertClass];
224
+ SEL selector = NSSelectorFromString(kSpecSelector);
225
+ IMP before = [self classMethodIMP:standIn selector:selector];
226
+
227
+ dispatch_once_t *onceToken = [self newOnceToken];
228
+ [RNFBNullSentinelInterceptor scheduleSwizzleOnMainQueueWithOnceToken:onceToken
229
+ turboConvertClass:standIn];
230
+
231
+ IMP afterSchedule = [self classMethodIMP:standIn selector:selector];
232
+ XCTAssertEqual(before, afterSchedule, @"swizzle must not run synchronously from schedule");
233
+
234
+ [self drainMainQueue];
235
+
236
+ IMP afterDrain = [self classMethodIMP:standIn selector:selector];
237
+ XCTAssertNotEqual(before, afterDrain, @"swizzle must run once the main queue drains");
238
+
239
+ gLastJsonSeenByOriginalIMP = nil;
240
+ [self callConvert:standIn selectorName:kSpecSelector json:[self nullSentinel]];
241
+ XCTAssertEqualObjects(gLastJsonSeenByOriginalIMP, [NSNull null]);
242
+ }
243
+
244
+ - (void)testScheduleSwizzle_secondCallDoesNotDoubleWrap {
245
+ Class standIn = [self makeStandInConvertClass];
246
+ SEL selector = NSSelectorFromString(kSpecSelector);
247
+
248
+ dispatch_once_t *onceToken = [self newOnceToken];
249
+ [RNFBNullSentinelInterceptor scheduleSwizzleOnMainQueueWithOnceToken:onceToken
250
+ turboConvertClass:standIn];
251
+ [self drainMainQueue];
252
+ IMP afterFirst = [self classMethodIMP:standIn selector:selector];
253
+
254
+ [RNFBNullSentinelInterceptor scheduleSwizzleOnMainQueueWithOnceToken:onceToken
255
+ turboConvertClass:standIn];
256
+ [self drainMainQueue];
257
+ IMP afterSecond = [self classMethodIMP:standIn selector:selector];
258
+
259
+ XCTAssertEqual(afterFirst, afterSecond, @"dispatch_once must prevent a second IMP wrap");
260
+ }
261
+
262
+ // Nil arm of `if (cxxConvertClass)`.
263
+ // Precondition: the unit host has no React runtime (RNFBAppUnitTests.xcodeproj links no React), so
264
+ // RCTCxxConvert is absent. The original missing-case test made the same assumption. The only test
265
+ // that registers a global RCTCxxConvert (the "present" test) disposes of it in tearDown, so the
266
+ // class is also absent here regardless of which test ran first.
267
+ - (void)testSwizzleRCTConvertMethods_missingRCTCxxConvert_isNoOp {
268
+ XCTAssertNil(NSClassFromString(kCxxConvertClassName));
269
+ XCTAssertNoThrow([RNFBNullSentinelInterceptor swizzleRCTConvertMethods]);
270
+ }
271
+
272
+ // Non-nil arm of `if (cxxConvertClass)`. Registers a global RCTCxxConvert stand-in (disposed in
273
+ // tearDown). Same no-React-in-the-host precondition as the missing-class test above.
274
+ - (void)testSwizzleRCTConvertMethods_whenRCTCxxConvertPresent_swizzlesSpecMethods {
275
+ XCTAssertNil(NSClassFromString(kCxxConvertClassName),
276
+ @"no RCTCxxConvert may exist before this test registers its stand-in");
277
+ Class convertClass = [self registerClassNamed:kCxxConvertClassName
278
+ selectorNames:@[ kCxxConvertSpecSelector ]
279
+ global:YES];
280
+ XCTAssertNotNil(convertClass);
281
+ if (convertClass == Nil) {
282
+ return;
283
+ }
284
+
285
+ SEL selector = NSSelectorFromString(kCxxConvertSpecSelector);
286
+ IMP before = [self classMethodIMP:convertClass selector:selector];
287
+ [RNFBNullSentinelInterceptor swizzleRCTConvertMethods];
288
+ IMP after = [self classMethodIMP:convertClass selector:selector];
289
+ XCTAssertNotEqual(before, after);
290
+
291
+ gLastJsonSeenByOriginalIMP = nil;
292
+ id result = [self callConvert:convertClass
293
+ selectorName:kCxxConvertSpecSelector
294
+ json:[self nullSentinel]];
295
+ XCTAssertEqualObjects(gLastJsonSeenByOriginalIMP, [NSNull null]);
296
+ XCTAssertEqualObjects(result, [NSNull null]);
297
+ }
298
+
299
+ - (void)testSwizzleTurboModuleConversions_prefixWithoutSpec_leavesIMPUnchanged {
300
+ Class standIn = [self makeStandInConvertClass];
301
+ SEL notASpec = NSSelectorFromString(kNotASpecSelector);
302
+ SEL unrelated = NSSelectorFromString(kUnrelatedSelector);
303
+ IMP notASpecBefore = [self classMethodIMP:standIn selector:notASpec];
304
+ IMP unrelatedBefore = [self classMethodIMP:standIn selector:unrelated];
305
+
306
+ [RNFBNullSentinelInterceptor swizzleTurboModuleConversions:standIn];
307
+
308
+ XCTAssertEqual(notASpecBefore, [self classMethodIMP:standIn selector:notASpec]);
309
+ XCTAssertEqual(unrelatedBefore, [self classMethodIMP:standIn selector:unrelated]);
310
+ }
311
+
312
+ @end
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import Foundation
18
+ import XCTest
19
+
20
+ final class RNFBPreferencesStorageTests: XCTestCase {
21
+ private var domainIdentifier: String!
22
+ private var userDefaults: UserDefaults!
23
+ private var storage: RNFBPreferencesStorage!
24
+
25
+ override func setUp() {
26
+ super.setUp()
27
+ domainIdentifier = "io.invertase.firebase.tests.\(UUID().uuidString)"
28
+ userDefaults = UserDefaults(suiteName: domainIdentifier)
29
+ userDefaults.removePersistentDomain(forName: domainIdentifier)
30
+ storage = RNFBPreferencesStorage(
31
+ userDefaults: userDefaults,
32
+ domainIdentifier: domainIdentifier
33
+ )
34
+ }
35
+
36
+ override func tearDown() {
37
+ userDefaults.removePersistentDomain(forName: domainIdentifier)
38
+ storage = nil
39
+ userDefaults = nil
40
+ domainIdentifier = nil
41
+ super.tearDown()
42
+ }
43
+
44
+ func testMissingKeysUseDefaultsAndAreNotContained() {
45
+ XCTAssertFalse(storage.contains("missing"))
46
+ XCTAssertTrue(storage.getBooleanValue("missing", defaultValue: true))
47
+ XCTAssertEqual(storage.getIntegerValue("missing", defaultValue: Int.max), Int.max)
48
+ XCTAssertEqual(storage.getStringValue("missing", defaultValue: "fallback"), "fallback")
49
+ }
50
+
51
+ func testSettersAndGettersPreserveValuesAndDictionaryRepresentation() {
52
+ storage.setBooleanValue("bool", boolValue: true)
53
+ storage.setIntegerValue("integer", integerValue: Int.max)
54
+ storage.setStringValue("string", stringValue: "value")
55
+
56
+ XCTAssertTrue(storage.contains("bool"))
57
+ XCTAssertTrue(storage.getBooleanValue("bool", defaultValue: false))
58
+ XCTAssertEqual(storage.getIntegerValue("integer", defaultValue: 0), Int.max)
59
+ XCTAssertEqual(storage.getStringValue("string", defaultValue: "fallback"), "value")
60
+
61
+ let all = storage.getAll()
62
+ XCTAssertEqual(all["bool"] as? Bool, true)
63
+ XCTAssertEqual(all["integer"] as? Int, Int.max)
64
+ XCTAssertEqual(all["string"] as? String, "value")
65
+ }
66
+
67
+ func testStringForPresentNonStringValueMatchesNSUserDefaults() {
68
+ userDefaults.set(["value"], forKey: "notString")
69
+
70
+ XCTAssertTrue(storage.contains("notString"))
71
+ XCTAssertNil(storage.getStringValue("notString", defaultValue: "fallback"))
72
+ }
73
+
74
+ func testRemoveAndClearAllUseTheConfiguredPersistentDomain() {
75
+ storage.setStringValue("first", stringValue: "one")
76
+ storage.setStringValue("second", stringValue: "two")
77
+
78
+ storage.remove("first")
79
+ XCTAssertFalse(storage.contains("first"))
80
+ XCTAssertTrue(storage.contains("second"))
81
+
82
+ storage.clearAll()
83
+ XCTAssertFalse(storage.contains("second"))
84
+ let clearedDomain = userDefaults.persistentDomain(forName: domainIdentifier)
85
+ XCTAssertTrue(
86
+ clearedDomain == nil || clearedDomain?.isEmpty == true,
87
+ "clearAll must leave the persistent domain nil or empty; got \(String(describing: clearedDomain))"
88
+ )
89
+ }
90
+
91
+ func testDefaultInitializerUsesRNFBPreferencesSuite() {
92
+ let suiteName = "io.invertase.firebase"
93
+ let productionDefaults = UserDefaults(suiteName: suiteName)!
94
+ let key = "RNFBPreferencesStorageTests.\(UUID().uuidString)"
95
+ productionDefaults.removeObject(forKey: key)
96
+ defer {
97
+ productionDefaults.removeObject(forKey: key)
98
+ }
99
+
100
+ let productionStorage = RNFBPreferencesStorage()
101
+ productionStorage.setStringValue(key, stringValue: "suite-value")
102
+
103
+ XCTAssertEqual(productionDefaults.string(forKey: key), "suite-value")
104
+ }
105
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ #import <XCTest/XCTest.h>
18
+
19
+ #import "RNFBHandleMapStorage-Swift.inc"
20
+ #import "RNFBPreferences.h"
21
+
22
+ @interface RNFBPreferencesTests : XCTestCase
23
+ @property(nonatomic, strong) NSString *domainIdentifier;
24
+ @property(nonatomic, strong) NSUserDefaults *userDefaults;
25
+ @property(nonatomic, strong) RNFBPreferences *preferences;
26
+ @end
27
+
28
+ @implementation RNFBPreferencesTests
29
+
30
+ - (void)setUp {
31
+ [super setUp];
32
+ self.domainIdentifier =
33
+ [@"io.invertase.firebase.tests." stringByAppendingString:NSUUID.UUID.UUIDString];
34
+ self.userDefaults = [[NSUserDefaults alloc] initWithSuiteName:self.domainIdentifier];
35
+ [self.userDefaults removePersistentDomainForName:self.domainIdentifier];
36
+ RNFBPreferencesStorage *storage =
37
+ [[RNFBPreferencesStorage alloc] initWithUserDefaults:self.userDefaults
38
+ domainIdentifier:self.domainIdentifier];
39
+ self.preferences = [[RNFBPreferences alloc] init];
40
+ [self.preferences setValue:storage forKey:@"storage"];
41
+ }
42
+
43
+ - (void)tearDown {
44
+ [self.userDefaults removePersistentDomainForName:self.domainIdentifier];
45
+ self.preferences = nil;
46
+ self.userDefaults = nil;
47
+ self.domainIdentifier = nil;
48
+ [super tearDown];
49
+ }
50
+
51
+ - (void)testSharedIsAvailableAndStable {
52
+ XCTAssertNotNil(RNFBPreferences.shared);
53
+ XCTAssertIdentical(RNFBPreferences.shared, RNFBPreferences.shared);
54
+ }
55
+
56
+ - (void)testPublicSelectorsAndIntegerWidthsAreUnchanged {
57
+ NSArray<NSString *> *selectorNames = @[
58
+ @"contains:",
59
+ @"getBooleanValue:defaultValue:",
60
+ @"setBooleanValue:boolValue:",
61
+ @"setIntegerValue:integerValue:",
62
+ @"setStringValue:stringValue:",
63
+ @"getStringValue:defaultValue:",
64
+ @"getIntegerValue:defaultValue:",
65
+ @"getAll",
66
+ @"clearAll",
67
+ @"remove:",
68
+ ];
69
+ for (NSString *selectorName in selectorNames) {
70
+ XCTAssertTrue([self.preferences respondsToSelector:NSSelectorFromString(selectorName)]);
71
+ }
72
+
73
+ NSMethodSignature *setter =
74
+ [RNFBPreferences instanceMethodSignatureForSelector:@selector(setIntegerValue:integerValue:)];
75
+ XCTAssertEqual(strcmp([setter getArgumentTypeAtIndex:3], @encode(NSInteger)), 0);
76
+
77
+ NSMethodSignature *getter =
78
+ [RNFBPreferences instanceMethodSignatureForSelector:@selector(getIntegerValue:defaultValue:)];
79
+ XCTAssertEqual(strcmp(getter.methodReturnType, @encode(NSInteger)), 0);
80
+ XCTAssertEqual(strcmp([getter getArgumentTypeAtIndex:3], @encode(NSInteger)), 0);
81
+ }
82
+
83
+ - (void)testFacadeForwardsEveryPreferenceOperation {
84
+ XCTAssertFalse([self.preferences contains:@"missing"]);
85
+ XCTAssertTrue([self.preferences getBooleanValue:@"missing" defaultValue:YES]);
86
+ XCTAssertEqual([self.preferences getIntegerValue:@"missing" defaultValue:NSIntegerMax],
87
+ NSIntegerMax);
88
+ XCTAssertEqualObjects([self.preferences getStringValue:@"missing" defaultValue:@"fallback"],
89
+ @"fallback");
90
+
91
+ [self.preferences setBooleanValue:@"bool" boolValue:YES];
92
+ [self.preferences setIntegerValue:@"integer" integerValue:NSIntegerMax];
93
+ [self.preferences setStringValue:@"string" stringValue:@"value"];
94
+
95
+ XCTAssertTrue([self.preferences contains:@"bool"]);
96
+ XCTAssertTrue([self.preferences getBooleanValue:@"bool" defaultValue:NO]);
97
+ XCTAssertEqual([self.preferences getIntegerValue:@"integer" defaultValue:0], NSIntegerMax);
98
+ XCTAssertEqualObjects([self.preferences getStringValue:@"string" defaultValue:@"fallback"],
99
+ @"value");
100
+ XCTAssertEqualObjects([self.preferences getAll][@"string"], @"value");
101
+
102
+ [self.preferences remove:@"bool"];
103
+ XCTAssertFalse([self.preferences contains:@"bool"]);
104
+
105
+ [self.preferences clearAll];
106
+ XCTAssertFalse([self.preferences contains:@"integer"]);
107
+ XCTAssertFalse([self.preferences contains:@"string"]);
108
+ }
109
+
110
+ @end
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Copyright (c) 2016-present Invertase Limited & Contributors
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this library except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import Foundation
18
+ import XCTest
19
+
20
+ final class RNFBRCTEventEmitterCoreTests: XCTestCase {
21
+ private var bridgePresent = false
22
+ private var emitted: [(String, Any?)] = []
23
+ private var core: RNFBRCTEventEmitterCore!
24
+
25
+ override func setUp() {
26
+ super.setUp()
27
+ bridgePresent = false
28
+ emitted = []
29
+ core = RNFBRCTEventEmitterCore(
30
+ isBridgePresent: { [unowned self] in self.bridgePresent },
31
+ emitHandler: { [unowned self] name, body in
32
+ self.emitted.append((name, body))
33
+ }
34
+ )
35
+ }
36
+
37
+ override func tearDown() {
38
+ core = nil
39
+ super.tearDown()
40
+ }
41
+
42
+ func testInvalidateResetsState() {
43
+ core.sendEvent(name: "a", body: ["x": 1])
44
+ core.addListener("a")
45
+ core.notifyJsReady(true)
46
+
47
+ core.invalidate()
48
+
49
+ let dict = core.getListenersDictionary()
50
+ XCTAssertEqual(dict["listeners"] as? Int, 0)
51
+ XCTAssertEqual(dict["queued"] as? Int, 0)
52
+ XCTAssertEqual((dict["events"] as? NSDictionary)?.count, 0)
53
+ }
54
+
55
+ func testSendEventQueuesWhenNotReadyToEmit() {
56
+ core.sendEvent(name: "evt", body: ["k": "v"])
57
+
58
+ let dict = core.getListenersDictionary()
59
+ XCTAssertEqual(dict["queued"] as? Int, 1)
60
+ XCTAssertTrue(emitted.isEmpty)
61
+ }
62
+
63
+ func testSendEventEmitsWhenBridgeObservingAndRegistered() {
64
+ bridgePresent = true
65
+ core.notifyJsReady(true)
66
+ core.addListener("evt")
67
+ emitted = []
68
+
69
+ core.sendEvent(name: "evt", body: ["k": "v"])
70
+
71
+ XCTAssertEqual(emitted.count, 1)
72
+ XCTAssertEqual(emitted[0].0, "evt")
73
+ XCTAssertEqual((emitted[0].1 as? NSDictionary)?["k"] as? String, "v")
74
+ XCTAssertEqual(core.getListenersDictionary()["queued"] as? Int, 0)
75
+ }
76
+
77
+ func testNotifyJsReadyFlushesQueue() {
78
+ bridgePresent = true
79
+ core.addListener("evt")
80
+ core.sendEvent(name: "evt", body: ["k": 1])
81
+ emitted = []
82
+
83
+ core.notifyJsReady(true)
84
+
85
+ XCTAssertEqual(emitted.count, 1)
86
+ XCTAssertEqual(emitted[0].0, "evt")
87
+ XCTAssertEqual(core.getListenersDictionary()["queued"] as? Int, 0)
88
+ }
89
+
90
+ func testAddListenerFlushesMatchingQueuedEvents() {
91
+ bridgePresent = true
92
+ core.notifyJsReady(true)
93
+ core.sendEvent(name: "evt", body: ["k": 1])
94
+ core.sendEvent(name: "other", body: ["k": 2])
95
+ emitted = []
96
+
97
+ core.addListener("evt")
98
+
99
+ XCTAssertEqual(emitted.count, 1)
100
+ XCTAssertEqual(emitted[0].0, "evt")
101
+ XCTAssertEqual(core.getListenersDictionary()["queued"] as? Int, 1)
102
+ }
103
+
104
+ func testAddListenerIncrementsCounts() {
105
+ core.addListener("evt")
106
+ core.addListener("evt")
107
+
108
+ let dict = core.getListenersDictionary()
109
+ XCTAssertEqual(dict["listeners"] as? Int, 2)
110
+ XCTAssertEqual((dict["events"] as? NSDictionary)?["evt"] as? Int, 2)
111
+ }
112
+
113
+ func testRemoveListenersDecrementsAndRemoves() {
114
+ core.addListener("evt")
115
+ core.addListener("evt")
116
+
117
+ core.removeListeners("evt", all: false)
118
+ XCTAssertEqual((core.getListenersDictionary()["events"] as? NSDictionary)?["evt"] as? Int, 1)
119
+ XCTAssertEqual(core.getListenersDictionary()["listeners"] as? Int, 1)
120
+
121
+ core.removeListeners("evt", all: false)
122
+ XCTAssertNil((core.getListenersDictionary()["events"] as? NSDictionary)?["evt"])
123
+ XCTAssertEqual(core.getListenersDictionary()["listeners"] as? Int, 0)
124
+ }
125
+
126
+ func testRemoveListenersAllRemovesEntireCount() {
127
+ core.addListener("evt")
128
+ core.addListener("evt")
129
+ core.addListener("evt")
130
+
131
+ core.removeListeners("evt", all: true)
132
+
133
+ XCTAssertNil((core.getListenersDictionary()["events"] as? NSDictionary)?["evt"])
134
+ XCTAssertEqual(core.getListenersDictionary()["listeners"] as? Int, 0)
135
+ }
136
+
137
+ func testRemoveListenersNoOpWhenMissing() {
138
+ core.removeListeners("missing", all: true)
139
+ XCTAssertEqual(core.getListenersDictionary()["listeners"] as? Int, 0)
140
+ }
141
+
142
+ func testSendEventWithNilBodyWhenEmitting() {
143
+ bridgePresent = true
144
+ core.notifyJsReady(true)
145
+ core.addListener("evt")
146
+ emitted = []
147
+
148
+ core.sendEvent(name: "evt", body: nil)
149
+
150
+ XCTAssertEqual(emitted.count, 1)
151
+ XCTAssertEqual(emitted[0].0, "evt")
152
+ XCTAssertNil(emitted[0].1)
153
+ }
154
+
155
+ func testIsObservingRequiresJsReady() {
156
+ bridgePresent = true
157
+ core.addListener("evt")
158
+ core.sendEvent(name: "evt", body: ["k": 1])
159
+
160
+ XCTAssertTrue(emitted.isEmpty)
161
+ XCTAssertEqual(core.getListenersDictionary()["queued"] as? Int, 1)
162
+ }
163
+ }