@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.
- package/CHANGELOG.md +62 -0
- package/README.md +10 -27
- package/RNFBApp.podspec +13 -5
- package/android/build.gradle +3 -0
- package/android/src/main/java/io/invertase/firebase/common/RNFBHandleCollisionException.kt +29 -0
- package/android/src/main/java/io/invertase/firebase/common/RNFBHandleMap.kt +146 -0
- package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.kt +156 -0
- package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.kt +44 -0
- package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.kt +100 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.kt +250 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.kt +64 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/{ReactNativeFirebaseAppInitProvider.java → ReactNativeFirebaseAppInitProvider.kt} +3 -4
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.kt +47 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.kt +33 -0
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseVersion.java +1 -1
- package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.kt +185 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.kt +35 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.kt +662 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.kt +103 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.kt +96 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.kt +140 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.kt +109 -0
- package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.kt +451 -0
- package/android/src/reactnative/java/io/invertase/firebase/interfaces/{ContextProvider.java → ContextProvider.kt} +8 -8
- package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeError.java → NativeError.kt} +8 -8
- package/android/src/reactnative/java/io/invertase/firebase/interfaces/{NativeEvent.java → NativeEvent.kt} +6 -6
- package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.kt +219 -0
- package/android/src/test/java/expo/core/ModuleRegistry.java +9 -0
- package/android/src/test/java/io/invertase/firebase/app/NativeRNFBTurboAppTest.java +182 -59
- package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppComponentsJavaCompatibilityTest.java +70 -0
- package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppJavaCompatibilityTest.java +60 -0
- package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackageTest.java +165 -0
- package/android/src/test/java/io/invertase/firebase/app/ReactNativeFirebaseAppTest.kt +167 -0
- package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseJavaCompatibilityTest.java +112 -0
- package/android/src/test/java/io/invertase/firebase/common/RCTConvertFirebaseTest.kt +317 -0
- package/android/src/test/java/io/invertase/firebase/common/RNFBHandleMapTest.kt +309 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterJavaCompatibilityTest.java +176 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitterTest.java +143 -12
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseEventTest.kt +61 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONJavaCompatibilityTest.java +85 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONTest.kt +296 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaJavaCompatibilityTest.java +67 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseMetaTest.kt +246 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseModuleTest.kt +254 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesJavaCompatibilityTest.java +87 -0
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebasePreferencesTest.kt +260 -0
- package/android/src/test/java/io/invertase/firebase/common/SharedUtilsJavaCompatibilityTest.java +255 -0
- package/android/src/test/java/io/invertase/firebase/common/SharedUtilsTest.kt +674 -0
- package/android/src/test/java/io/invertase/firebase/common/TaskExecutorServiceTest.kt +290 -0
- package/android/src/test/java/io/invertase/firebase/common/UniversalFirebaseModuleJavaCompatibilityTest.java +178 -0
- package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesJavaCompatibilityTest.java +99 -0
- package/android/src/test/java/io/invertase/firebase/common/UniversalFirebasePreferencesTest.kt +217 -0
- package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleJavaCompatibilityTest.java +176 -0
- package/android/src/test/java/io/invertase/firebase/commoncompat/ReactNativeFirebaseModuleKotlinCompatibilityTest.kt +52 -0
- package/android/src/test/java/io/invertase/firebase/interfaces/InterfacesJavaCompatibilityTest.java +144 -0
- package/android/src/test/java/io/invertase/firebase/interfaces/NativeEventTest.kt +56 -0
- package/android/src/test/java/io/invertase/firebase/utils/NativeRNFBTurboUtilsTest.java +517 -21
- package/dist/module/FirebaseApp.js +10 -0
- package/dist/module/FirebaseApp.js.map +1 -1
- package/dist/module/common/deeps.js +3 -0
- package/dist/module/common/deeps.js.map +1 -1
- package/dist/module/internal/FirebaseModule.js.map +1 -1
- package/dist/module/internal/RNFBNativeEventEmitter.js +12 -1
- package/dist/module/internal/RNFBNativeEventEmitter.js.map +1 -1
- package/dist/module/internal/SharedEventEmitter.js +3 -1
- package/dist/module/internal/SharedEventEmitter.js.map +1 -1
- package/dist/module/internal/index.js +1 -0
- package/dist/module/internal/index.js.map +1 -1
- package/dist/module/internal/registry/app.js +7 -1
- package/dist/module/internal/registry/app.js.map +1 -1
- package/dist/module/types/app.js.map +1 -1
- package/dist/module/types/internal.js +2 -0
- package/dist/module/version.js +1 -1
- package/dist/typescript/lib/FirebaseApp.d.ts +7 -0
- package/dist/typescript/lib/FirebaseApp.d.ts.map +1 -1
- package/dist/typescript/lib/common/deeps.d.ts.map +1 -1
- package/dist/typescript/lib/internal/FirebaseModule.d.ts +2 -3
- package/dist/typescript/lib/internal/FirebaseModule.d.ts.map +1 -1
- package/dist/typescript/lib/internal/NativeModules.d.ts +4 -1
- package/dist/typescript/lib/internal/NativeModules.d.ts.map +1 -1
- package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts +2 -2
- package/dist/typescript/lib/internal/RNFBNativeEventEmitter.d.ts.map +1 -1
- package/dist/typescript/lib/internal/SharedEventEmitter.d.ts +3 -3
- package/dist/typescript/lib/internal/SharedEventEmitter.d.ts.map +1 -1
- package/dist/typescript/lib/internal/index.d.ts +1 -0
- package/dist/typescript/lib/internal/index.d.ts.map +1 -1
- package/dist/typescript/lib/internal/registry/app.d.ts.map +1 -1
- package/dist/typescript/lib/types/app.d.ts +10 -3
- package/dist/typescript/lib/types/app.d.ts.map +1 -1
- package/dist/typescript/lib/types/internal.d.ts +12 -0
- package/dist/typescript/lib/types/internal.d.ts.map +1 -1
- package/dist/typescript/lib/version.d.ts +1 -1
- package/firebase-schema.json +4 -0
- package/firebase_spm.rb +552 -96
- package/ios/RNFBApp/RCTConvert+FIRApp.h +2 -5
- package/ios/RNFBApp/RCTConvert+FIRApp.m +11 -6
- package/ios/RNFBApp/RCTConvert+FIROptions.h +2 -5
- package/ios/RNFBApp/RCTConvert+FIROptions.m +11 -10
- package/ios/RNFBApp/RCTConvertFIRApp.swift +86 -0
- package/ios/RNFBApp/RCTConvertFIROptions.swift +259 -0
- package/ios/RNFBApp/RNFBAppCustomAuthDomains.swift +63 -0
- package/ios/RNFBApp/RNFBAppInitializeOptionsMapper.swift +113 -0
- package/ios/RNFBApp/RNFBAppLogLevelMapper.swift +53 -0
- package/ios/RNFBApp/RNFBAppModule+CustomAuthDomain.m +35 -0
- package/ios/RNFBApp/RNFBAppModule.h +6 -2
- package/ios/RNFBApp/RNFBAppModule.mm +65 -228
- package/ios/RNFBApp/RNFBAppModuleFirebase.swift +439 -0
- package/ios/RNFBApp/RNFBAppModuleImplementation.h +68 -0
- package/ios/RNFBApp/RNFBAppModuleImplementation.m +195 -0
- package/ios/RNFBApp/RNFBHandleMap.m +27 -56
- package/ios/RNFBApp/RNFBHandleMapStorage.swift +90 -0
- package/ios/RNFBApp/RNFBInitializeAppArguments.swift +82 -0
- package/ios/RNFBApp/RNFBJSON.m +22 -38
- package/ios/RNFBApp/RNFBJSONImplementation.swift +71 -0
- package/ios/RNFBApp/RNFBMeta.m +24 -26
- package/ios/RNFBApp/RNFBMetaStorage.swift +76 -0
- package/ios/RNFBApp/RNFBNullSentinelDecoding.swift +135 -0
- package/ios/RNFBApp/RNFBNullSentinelInterceptor.h +30 -3
- package/ios/RNFBApp/RNFBNullSentinelInterceptor.m +28 -5
- package/ios/RNFBApp/RNFBPreferences.m +22 -20
- package/ios/RNFBApp/RNFBPreferencesStorage.swift +101 -0
- package/ios/RNFBApp/RNFBRCTEventEmitter.m +36 -93
- package/ios/RNFBApp/RNFBRCTEventEmitterCore.swift +161 -0
- package/ios/RNFBApp/RNFBSharedUtils.h +2 -5
- package/ios/RNFBApp/RNFBSharedUtils.m +109 -198
- package/ios/RNFBApp/RNFBSharedUtilsConfig.swift +72 -0
- package/ios/RNFBApp/RNFBSharedUtilsFIRApp.swift +271 -0
- package/ios/RNFBApp/RNFBSharedUtilsFormatting.swift +59 -0
- package/ios/RNFBApp/RNFBSharedUtilsPromiseRejection.swift +88 -0
- package/ios/RNFBApp/RNFBUtilsHelpers.swift +116 -0
- package/ios/RNFBApp/RNFBUtilsModule+Foundation.m +44 -0
- package/ios/RNFBApp/RNFBUtilsModule+PhotoAssets.m +47 -0
- package/ios/RNFBApp/RNFBUtilsModule.h +11 -1
- package/ios/RNFBApp/RNFBUtilsModule.mm +10 -85
- package/ios/RNFBApp/RNFBUtilsModuleImplementation.h +32 -0
- package/ios/RNFBApp/RNFBUtilsModuleImplementation.m +56 -0
- package/ios/RNFBApp/RNFBVersion.m +1 -1
- package/ios/RNFBApp.xcodeproj/project.pbxproj +100 -0
- package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FIRApp.m +171 -0
- package/ios/RNFBAppUnitTests/HostStubs/FirebaseCore/FirebaseCore.h +78 -0
- package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FIRInstallations.m +7 -0
- package/ios/RNFBAppUnitTests/HostStubs/FirebaseInstallations/FirebaseInstallations.h +8 -0
- package/ios/RNFBAppUnitTests/HostStubs/RNFBAppModuleHostStub.m +55 -0
- package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.h +89 -0
- package/ios/RNFBAppUnitTests/HostStubs/RNFBAppTurboModules.mm +30 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.h +19 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridge.m +32 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTBridgeModule.h +19 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.h +28 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTConvert.m +23 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTEventEmitter.h +9 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTInvalidating.h +24 -0
- package/ios/RNFBAppUnitTests/HostStubs/React/RCTUtils.h +9 -0
- package/ios/RNFBAppUnitTests/HostStubs/module.modulemap +13 -0
- package/ios/RNFBAppUnitTests/RCTConvertFIRAppFacadeTests.m +61 -0
- package/ios/RNFBAppUnitTests/RCTConvertFIRAppTests.swift +73 -0
- package/ios/RNFBAppUnitTests/RCTConvertFIROptionsFacadeTests.m +57 -0
- package/ios/RNFBAppUnitTests/RCTConvertFIROptionsTests.swift +243 -0
- package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsFacadeTests.m +73 -0
- package/ios/RNFBAppUnitTests/RNFBAppCustomAuthDomainsTests.swift +70 -0
- package/ios/RNFBAppUnitTests/RNFBAppInitializeOptionsMapperTests.swift +190 -0
- package/ios/RNFBAppUnitTests/RNFBAppLogLevelFacadeTests.m +76 -0
- package/ios/RNFBAppUnitTests/RNFBAppLogLevelMapperTests.swift +59 -0
- package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseFacadeTests.m +215 -0
- package/ios/RNFBAppUnitTests/RNFBAppModuleFirebaseTests.swift +430 -0
- package/ios/RNFBAppUnitTests/RNFBAppModuleLifecycleTests.m +206 -0
- package/ios/RNFBAppUnitTests/RNFBAppModuleShellTests.mm +367 -0
- package/ios/RNFBAppUnitTests/RNFBAppUnitTests.xcodeproj/project.pbxproj +355 -2
- package/ios/RNFBAppUnitTests/RNFBFirebaseAppClientTests.swift +165 -0
- package/ios/RNFBAppUnitTests/RNFBHandleMapStorageTests.swift +121 -0
- package/ios/RNFBAppUnitTests/RNFBInitializeAppArgumentsTests.swift +256 -0
- package/ios/RNFBAppUnitTests/RNFBJSONImplementationTests.swift +126 -0
- package/ios/RNFBAppUnitTests/RNFBJSONTests.m +172 -0
- package/ios/RNFBAppUnitTests/RNFBMetaStorageTests.swift +106 -0
- package/ios/RNFBAppUnitTests/RNFBMetaTests.m +65 -0
- package/ios/RNFBAppUnitTests/RNFBNullSentinelDecodingTests.swift +286 -0
- package/ios/RNFBAppUnitTests/RNFBNullSentinelInterceptorTests.m +312 -0
- package/ios/RNFBAppUnitTests/RNFBPreferencesStorageTests.swift +105 -0
- package/ios/RNFBAppUnitTests/RNFBPreferencesTests.m +110 -0
- package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterCoreTests.swift +163 -0
- package/ios/RNFBAppUnitTests/RNFBRCTEventEmitterFacadeTests.m +141 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigFacadeTests.m +100 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsConfigTests.swift +234 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppFacadeTests.m +145 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsFIRAppTests.swift +173 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingFacadeTests.m +38 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsFormattingTests.swift +82 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsNullSentinelTests.m +35 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionFacadeTests.m +107 -0
- package/ios/RNFBAppUnitTests/RNFBSharedUtilsPromiseRejectionTests.swift +133 -0
- package/ios/RNFBAppUnitTests/RNFBUtilsHelpersTests.swift +144 -0
- package/ios/RNFBAppUnitTests/RNFBUtilsModuleFacadeTests.m +72 -0
- package/ios/RNFBAppUnitTests/RNFBUtilsModuleShellTests.mm +282 -0
- package/ios/RNFBFirebase/Package.swift +53 -0
- package/ios/RNFBFirebase/Sources/RNFBFirebase/RNFBFirebase.swift +285 -0
- package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/Public/.gitkeep +0 -0
- package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/RNFBFirebaseBridge.m +169 -0
- package/ios/RNFBFirebase/Sources/RNFBFirebaseBridge/include/RNFBFirebaseBridge.h +52 -0
- package/ios/RNFBFirebaseUnitTests/RNFBAppDynamicFacadeTests.swift +240 -0
- package/{android/src/main/java/io/invertase/firebase/common/RNFBHandleCollisionException.java → ios/RNFBFirebaseUnitTests/RNFBFirebaseClientImport.swift} +23 -14
- package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/project.pbxproj +588 -0
- package/ios/RNFBFirebaseUnitTests/RNFBFirebaseUnitTests.xcodeproj/xcshareddata/xcschemes/RNFBFirebaseUnitTests.xcscheme +83 -0
- package/ios_config.sh +50 -22
- package/lib/FirebaseApp.ts +10 -0
- package/lib/common/deeps.ts +3 -0
- package/lib/internal/FirebaseModule.ts +6 -3
- package/lib/internal/NativeModules.ts +4 -1
- package/lib/internal/RNFBNativeEventEmitter.ts +14 -5
- package/lib/internal/SharedEventEmitter.ts +5 -1
- package/lib/internal/index.ts +1 -0
- package/lib/internal/react-native-vendor-emitter.d.ts +11 -0
- package/lib/internal/registry/app.ts +7 -1
- package/lib/types/app.ts +10 -3
- package/lib/types/internal.ts +19 -0
- package/lib/version.ts +1 -1
- package/package.json +8 -8
- package/plugin/build/ios/appDelegate.js +2 -2
- package/plugin/build/ios/podfile.js +16 -16
- package/plugin/build/pluginConfig.d.ts +4 -2
- package/plugin/src/ios/appDelegate.ts +2 -2
- package/plugin/src/ios/podfile.ts +20 -23
- package/plugin/src/pluginConfig.ts +4 -2
- package/android/src/main/java/io/invertase/firebase/common/RNFBHandleMap.java +0 -150
- package/android/src/main/java/io/invertase/firebase/common/TaskExecutorService.java +0 -148
- package/android/src/main/java/io/invertase/firebase/common/UniversalFirebaseModule.java +0 -62
- package/android/src/main/java/io/invertase/firebase/common/UniversalFirebasePreferences.java +0 -91
- package/android/src/reactnative/java/io/invertase/firebase/app/NativeRNFBTurboApp.java +0 -224
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseApp.java +0 -46
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppPackage.java +0 -49
- package/android/src/reactnative/java/io/invertase/firebase/app/ReactNativeFirebaseAppRegistrar.java +0 -35
- package/android/src/reactnative/java/io/invertase/firebase/common/RCTConvertFirebase.java +0 -192
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEvent.java +0 -53
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseEventEmitter.java +0 -606
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseJSON.java +0 -109
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseMeta.java +0 -101
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebaseModule.java +0 -146
- package/android/src/reactnative/java/io/invertase/firebase/common/ReactNativeFirebasePreferences.java +0 -96
- package/android/src/reactnative/java/io/invertase/firebase/common/SharedUtils.java +0 -441
- package/android/src/reactnative/java/io/invertase/firebase/utils/NativeRNFBTurboUtils.java +0 -223
- package/android/src/test/java/io/invertase/firebase/common/RNFBHandleMapTest.java +0 -275
- package/android/src/test/java/io/invertase/firebase/common/ReactNativeFirebaseJSONTest.java +0 -108
- 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
|
+
}
|