expo-modules-core 0.9.2 → 0.10.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 (167) hide show
  1. package/CHANGELOG.md +18 -5
  2. package/android/CMakeLists.txt +154 -0
  3. package/android/build.gradle +293 -5
  4. package/android/src/main/cpp/Exceptions.cpp +22 -0
  5. package/android/src/main/cpp/Exceptions.h +38 -0
  6. package/android/src/main/cpp/ExpoModulesHostObject.cpp +47 -0
  7. package/android/src/main/cpp/ExpoModulesHostObject.h +32 -0
  8. package/android/src/main/cpp/JNIFunctionBody.cpp +29 -0
  9. package/android/src/main/cpp/JNIFunctionBody.h +50 -0
  10. package/android/src/main/cpp/JNIInjector.cpp +19 -0
  11. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +122 -0
  12. package/android/src/main/cpp/JSIInteropModuleRegistry.h +96 -0
  13. package/android/src/main/cpp/JSIObjectWrapper.h +33 -0
  14. package/android/src/main/cpp/JSITypeConverter.h +84 -0
  15. package/android/src/main/cpp/JavaScriptModuleObject.cpp +138 -0
  16. package/android/src/main/cpp/JavaScriptModuleObject.h +122 -0
  17. package/android/src/main/cpp/JavaScriptObject.cpp +125 -0
  18. package/android/src/main/cpp/JavaScriptObject.h +131 -0
  19. package/android/src/main/cpp/JavaScriptRuntime.cpp +127 -0
  20. package/android/src/main/cpp/JavaScriptRuntime.h +87 -0
  21. package/android/src/main/cpp/JavaScriptValue.cpp +172 -0
  22. package/android/src/main/cpp/JavaScriptValue.h +78 -0
  23. package/android/src/main/cpp/MethodMetadata.cpp +230 -0
  24. package/android/src/main/cpp/MethodMetadata.h +92 -0
  25. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +2 -0
  26. package/android/src/main/java/expo/modules/core/errors/ContextDestroyedException.kt +7 -0
  27. package/android/src/main/java/expo/modules/interfaces/permissions/Permissions.java +30 -0
  28. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +49 -1
  29. package/android/src/main/java/expo/modules/kotlin/ConcatIterator.kt +18 -0
  30. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +15 -12
  31. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +39 -3
  32. package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +2 -2
  33. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +13 -0
  34. package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +2 -0
  35. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +19 -14
  36. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +29 -7
  37. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +13 -13
  38. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +18 -0
  39. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +18 -0
  40. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +56 -0
  41. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +28 -0
  42. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +18 -0
  43. package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +39 -0
  44. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +89 -0
  45. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +44 -0
  46. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +113 -0
  47. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +35 -0
  48. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +15 -5
  49. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +65 -111
  50. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +35 -2
  51. package/android/src/main/java/expo/modules/kotlin/providers/AppContextProvider.kt +14 -0
  52. package/android/src/main/java/expo/modules/kotlin/providers/CurrentActivityProvider.kt +22 -0
  53. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +19 -2
  54. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +3 -2
  55. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +7 -2
  56. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +68 -20
  57. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +50 -22
  58. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +18 -2
  59. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +18 -2
  60. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +17 -2
  61. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +43 -3
  62. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +5 -0
  63. package/build/NativeModulesProxy.native.d.ts.map +1 -1
  64. package/build/NativeModulesProxy.native.js +9 -3
  65. package/build/NativeModulesProxy.native.js.map +1 -1
  66. package/ios/AppDelegates/EXAppDelegatesLoader.m +1 -2
  67. package/ios/ExpoModulesCore.podspec +1 -1
  68. package/ios/JSI/EXJSIConversions.mm +6 -0
  69. package/ios/JSI/EXJSIInstaller.h +15 -21
  70. package/ios/JSI/EXJSIInstaller.mm +39 -3
  71. package/ios/JSI/EXJSIUtils.h +47 -3
  72. package/ios/JSI/EXJSIUtils.mm +88 -4
  73. package/ios/JSI/EXJavaScriptObject.h +11 -18
  74. package/ios/JSI/EXJavaScriptObject.mm +37 -18
  75. package/ios/JSI/EXJavaScriptRuntime.h +43 -9
  76. package/ios/JSI/EXJavaScriptRuntime.mm +70 -27
  77. package/ios/JSI/EXJavaScriptTypedArray.h +30 -0
  78. package/ios/JSI/EXJavaScriptTypedArray.mm +29 -0
  79. package/ios/JSI/EXJavaScriptValue.h +3 -2
  80. package/ios/JSI/EXJavaScriptValue.mm +17 -20
  81. package/ios/JSI/EXJavaScriptWeakObject.h +23 -0
  82. package/ios/JSI/EXJavaScriptWeakObject.mm +53 -0
  83. package/ios/JSI/EXObjectDeallocator.h +27 -0
  84. package/ios/JSI/ExpoModulesHostObject.h +3 -3
  85. package/ios/JSI/ExpoModulesHostObject.mm +4 -4
  86. package/ios/JSI/JavaScriptRuntime.swift +38 -1
  87. package/ios/JSI/JavaScriptValue.swift +7 -0
  88. package/ios/JSI/TypedArray.cpp +67 -0
  89. package/ios/JSI/TypedArray.h +46 -0
  90. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +0 -11
  91. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +17 -10
  92. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +85 -77
  93. package/ios/NativeModulesProxy/NativeModulesProxyModule.swift +17 -0
  94. package/ios/Services/EXReactNativeEventEmitter.h +2 -2
  95. package/ios/Services/EXReactNativeEventEmitter.m +11 -6
  96. package/ios/Swift/AppContext.swift +208 -28
  97. package/ios/Swift/Arguments/AnyArgument.swift +18 -0
  98. package/ios/Swift/Arguments/{Types/EnumArgumentType.swift → EnumArgument.swift} +2 -17
  99. package/ios/Swift/Classes/ClassComponent.swift +95 -0
  100. package/ios/Swift/Classes/ClassComponentElement.swift +33 -0
  101. package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +34 -0
  102. package/ios/Swift/Classes/ClassComponentFactories.swift +96 -0
  103. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +44 -0
  104. package/ios/Swift/DynamicTypes/DynamicArrayType.swift +56 -0
  105. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +27 -0
  106. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +27 -0
  107. package/ios/Swift/DynamicTypes/DynamicOptionalType.swift +63 -0
  108. package/ios/Swift/DynamicTypes/DynamicRawType.swift +33 -0
  109. package/ios/Swift/DynamicTypes/DynamicSharedObjectType.swift +37 -0
  110. package/ios/Swift/DynamicTypes/DynamicType.swift +39 -0
  111. package/ios/Swift/DynamicTypes/DynamicTypedArrayType.swift +46 -0
  112. package/ios/Swift/Exceptions/CodedError.swift +1 -1
  113. package/ios/Swift/Exceptions/Exception.swift +8 -6
  114. package/ios/Swift/Exceptions/UnexpectedException.swift +2 -1
  115. package/ios/Swift/ExpoBridgeModule.m +5 -0
  116. package/ios/Swift/ExpoBridgeModule.swift +65 -0
  117. package/ios/Swift/Functions/AnyFunction.swift +33 -31
  118. package/ios/Swift/Functions/AsyncFunctionComponent.swift +196 -59
  119. package/ios/Swift/Functions/SyncFunctionComponent.swift +142 -58
  120. package/ios/Swift/JavaScriptUtils.swift +32 -57
  121. package/ios/Swift/Logging/LogHandlers.swift +39 -0
  122. package/ios/Swift/Logging/LogType.swift +62 -0
  123. package/ios/Swift/Logging/Logger.swift +198 -0
  124. package/ios/Swift/ModuleHolder.swift +19 -54
  125. package/ios/Swift/ModuleRegistry.swift +7 -1
  126. package/ios/Swift/Modules/AnyModule.swift +3 -3
  127. package/ios/Swift/ModulesProvider.swift +2 -0
  128. package/ios/Swift/Objects/JavaScriptObjectBuilder.swift +37 -0
  129. package/ios/Swift/Objects/ObjectDefinition.swift +74 -1
  130. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +77 -68
  131. package/ios/Swift/Objects/PropertyComponent.swift +147 -0
  132. package/ios/Swift/Promise.swift +12 -3
  133. package/ios/Swift/Records/Field.swift +2 -2
  134. package/ios/Swift/SharedObjects/SharedObject.swift +20 -0
  135. package/ios/Swift/SharedObjects/SharedObjectRegistry.swift +129 -0
  136. package/ios/Swift/TypedArrays/AnyTypedArray.swift +11 -0
  137. package/ios/Swift/TypedArrays/ConcreteTypedArrays.swift +56 -0
  138. package/ios/Swift/TypedArrays/GenericTypedArray.swift +49 -0
  139. package/ios/Swift/TypedArrays/TypedArray.swift +80 -0
  140. package/ios/Swift/Utilities.swift +28 -0
  141. package/ios/Swift/Views/ConcreteViewProp.swift +3 -3
  142. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +2 -2
  143. package/ios/Tests/ClassComponentSpec.swift +210 -0
  144. package/ios/Tests/DynamicTypeSpec.swift +336 -0
  145. package/ios/Tests/EnumArgumentSpec.swift +48 -0
  146. package/ios/Tests/ExpoModulesSpec.swift +17 -3
  147. package/ios/Tests/FunctionSpec.swift +167 -118
  148. package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
  149. package/ios/Tests/PropertyComponentSpec.swift +95 -0
  150. package/ios/Tests/SharedObjectRegistrySpec.swift +109 -0
  151. package/ios/Tests/TypedArraysSpec.swift +136 -0
  152. package/package.json +2 -2
  153. package/src/NativeModulesProxy.native.ts +13 -3
  154. package/src/ts-declarations/ExpoModules.d.ts +7 -0
  155. package/tsconfig.json +1 -1
  156. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +0 -15
  157. package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +0 -36
  158. package/ios/Swift/Arguments/AnyArgumentType.swift +0 -13
  159. package/ios/Swift/Arguments/ArgumentType.swift +0 -28
  160. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +0 -42
  161. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +0 -16
  162. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +0 -49
  163. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +0 -15
  164. package/ios/Swift/Arguments/Types/RawArgumentType.swift +0 -25
  165. package/ios/Swift/Functions/ConcreteFunction.swift +0 -103
  166. package/ios/Swift/SwiftInteropBridge.swift +0 -155
  167. package/ios/Tests/ArgumentTypeSpec.swift +0 -143
@@ -0,0 +1,336 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ import CoreGraphics
4
+ import ExpoModulesTestCore
5
+
6
+ @testable import ExpoModulesCore
7
+
8
+ final class DynamicTypeSpec: ExpoSpec {
9
+ override func spec() {
10
+ // MARK: - DynamicRawType
11
+
12
+ describe("DynamicRawType") {
13
+ it("is created") {
14
+ expect(~String.self).to(beAKindOf(DynamicRawType<String>.self))
15
+ expect(~Bool.self).to(beAKindOf(DynamicRawType<Bool>.self))
16
+ expect(~ExpoSpec.self).to(beAKindOf(DynamicRawType<ExpoSpec>.self))
17
+ }
18
+ describe("casts") {
19
+ it("succeeds") {
20
+ expect(try (~String.self).cast("expo") as? String) == "expo"
21
+ expect(try (~Double.self).cast(2.1) as? Double) == 2.1
22
+ expect(try (~Bool.self).cast(false) as? Bool) == false
23
+ }
24
+ it("throws NullCastException") {
25
+ let value: Double? = nil
26
+ let anyValue = value as Any
27
+
28
+ expect { try (~Double.self).cast(anyValue) }.to(
29
+ throwError(errorType: Conversions.NullCastException<Double>.self)
30
+ )
31
+ }
32
+ it("throws CastingException") {
33
+ expect { try (~String.self).cast(true) }.to(
34
+ throwError(errorType: Conversions.CastingException<String>.self)
35
+ )
36
+ }
37
+ }
38
+ describe("wraps") {
39
+ it("is true") {
40
+ expect(~Double.self ~> Double.self) == true
41
+ expect(~Bool.self ~> Bool.self) == true
42
+ expect(~DynamicTypeSpec.self ~> DynamicTypeSpec.self) == true
43
+ }
44
+ it("is false") {
45
+ expect(~Double.self !~> Bool.self) == true
46
+ expect(~Bool.self !~> Double.self) == true
47
+ expect(~DynamicTypeSpec.self !~> ExpoSpec.self) == true
48
+ }
49
+ }
50
+ describe("equals") {
51
+ it("is true") {
52
+ expect(~Int.self == ~Int.self) == true
53
+ expect(~Double.self == ~Double.self) == true
54
+ expect(~Bool.self == ~Bool.self) == true
55
+ expect(~ExpoSpec.self == ~ExpoSpec.self) == true
56
+ }
57
+ it("is false") {
58
+ expect(~Int.self != ~String.self) == true
59
+ expect(~String.self != ~CGSize.self) == true
60
+ expect(~Bool.self != ~Promise.self) == true
61
+ }
62
+ }
63
+ }
64
+
65
+ // MARK: - DynamicArrayType
66
+
67
+ describe("DynamicArrayType") {
68
+ it("is created") {
69
+ expect(~[Double].self).to(beAKindOf(DynamicArrayType.self))
70
+ expect(~[String?].self).to(beAKindOf(DynamicArrayType.self))
71
+ expect(~[[Int]].self).to(beAKindOf(DynamicArrayType.self))
72
+ }
73
+ describe("casts") {
74
+ it("succeeds") {
75
+ expect(try (~[Double].self).cast([1.2, 3.4]) as? [Double]) == [1.2, 3.4]
76
+ expect(try (~[[String]].self).cast([["hello", "expo"]]) as? [[String]]) == [["hello", "expo"]]
77
+ }
78
+ it("casts arrays") {
79
+ let value = 9.9
80
+ let anyValue = [value] as [Any]
81
+ let result = try (~[Double].self).cast(anyValue) as! [Any]
82
+
83
+ expect(result).to(beAKindOf([Double].self))
84
+ expect(result as? [Double]) == [value]
85
+ }
86
+ it("arrayizes single element") {
87
+ // The dynamic array type can arrayize the single element
88
+ // if only the array element's dynamic type can cast it.
89
+ expect(try (~[Int].self).cast(50) as? [Int]) == [50]
90
+ expect(try (~[String].self).cast("not an array") as? [String]) == ["not an array"]
91
+ }
92
+ it("throws CastingException") {
93
+ expect { try (~[String].self).cast(84) }.to(
94
+ throwError(errorType: Conversions.CastingException<String>.self)
95
+ )
96
+ }
97
+ }
98
+ describe("wraps") {
99
+ it("is true") {
100
+ expect(~[Double].self ~> [Double].self) == true
101
+ expect(~[[String]].self ~> [[String]].self) == true
102
+ expect(~[CGPoint].self ~> [CGPoint].self) == true
103
+ }
104
+ it("is false") {
105
+ expect(~[String].self !~> [Int].self) == true
106
+ expect(~[Promise].self !~> Promise.self) == true
107
+ }
108
+ }
109
+ describe("equals") {
110
+ it("is true") {
111
+ expect(~[String].self == ~[String].self) == true
112
+ expect(~[CGSize].self == ~[CGSize].self) == true
113
+ expect(~[[[Double]]].self == ~[[[Double]]].self) == true
114
+ }
115
+ it("is false") {
116
+ expect(~[Int].self != ~[Double].self) == true
117
+ expect(~[[String]].self != ~[String].self) == true
118
+ expect(~[URL].self != ~[String].self) == true
119
+ }
120
+ }
121
+ }
122
+
123
+ // MARK: - DynamicConvertibleType
124
+
125
+ describe("DynamicConvertibleType") {
126
+ it("is created") {
127
+ expect(~CGPoint.self).to(beAKindOf(DynamicConvertibleType.self))
128
+ expect(~CGRect.self).to(beAKindOf(DynamicConvertibleType.self))
129
+ expect(~CGColor.self).to(beAKindOf(DynamicConvertibleType.self))
130
+ expect(~URL.self).to(beAKindOf(DynamicConvertibleType.self))
131
+ }
132
+ describe("casts") {
133
+ it("succeeds") {
134
+ expect(try (~CGPoint.self).cast([2.1, 3.7]) as? CGPoint) == CGPoint(x: 2.1, y: 3.7)
135
+ expect(try (~CGVector.self).cast(["dx": 0.8, "dy": 4.1]) as? CGVector) == CGVector(dx: 0.8, dy: 4.1)
136
+ expect(try (~URL.self).cast("/test/path") as? URL) == URL(fileURLWithPath: "/test/path")
137
+ }
138
+ it("throws ConvertingException") {
139
+ expect { try (~CGRect.self).cast("not a rect") as? CGRect }.to(
140
+ throwError(errorType: Conversions.ConvertingException<CGRect>.self)
141
+ )
142
+ }
143
+ }
144
+ describe("wraps") {
145
+ it("is true") {
146
+ expect(~CGRect.self ~> CGRect.self) == true
147
+ expect(~CGColor.self ~> CGColor.self) == true
148
+ expect(~URL.self ~> URL.self) == true
149
+ }
150
+ it("is false") {
151
+ expect(~CGRect.self !~> Double.self) == true
152
+ expect(~CGColor.self !~> CGRect.self) == true
153
+ expect(~URL.self !~> String.self) == true
154
+ }
155
+ }
156
+ describe("equals") {
157
+ it("is true") {
158
+ expect(~CGSize.self == ~CGSize.self) == true
159
+ expect(~URL.self == ~URL.self) == true
160
+ expect(~UIColor.self == ~UIColor.self) == true
161
+ }
162
+ it("is false") {
163
+ expect(~CGSize.self != ~CGRect.self) == true
164
+ expect(~CGPoint.self != ~CGVector.self) == true
165
+ expect(~URL.self != ~String.self) == true
166
+ }
167
+ }
168
+ }
169
+
170
+ // MARK: - DynamicEnumType
171
+
172
+ describe("DynamicEnumType") {
173
+ enum StringTestEnum: String, EnumArgument {
174
+ case hello
175
+ case expo
176
+ }
177
+ enum IntTestEnum: Int, EnumArgument {
178
+ case negative = -1
179
+ case positive = 1
180
+ }
181
+
182
+ it("is created") {
183
+ expect(~StringTestEnum.self).to(beAKindOf(DynamicEnumType.self))
184
+ expect(~IntTestEnum.self).to(beAKindOf(DynamicEnumType.self))
185
+ }
186
+ describe("casts") {
187
+ it("succeeds") {
188
+ expect(try (~StringTestEnum.self).cast("expo") as? StringTestEnum) == .expo
189
+ expect(try (~IntTestEnum.self).cast(1) as? IntTestEnum) == .positive
190
+ }
191
+ it("throws EnumNoSuchValueException") {
192
+ expect { try (~StringTestEnum.self).cast("react native") as? StringTestEnum }.to(
193
+ throwError(errorType: EnumNoSuchValueException.self)
194
+ )
195
+ }
196
+ it("throws EnumCastingException") {
197
+ expect { try (~IntTestEnum.self).cast(true) as? StringTestEnum }.to(
198
+ throwError(errorType: EnumCastingException.self)
199
+ )
200
+ }
201
+ }
202
+ describe("wraps") {
203
+ it("is true") {
204
+ expect(~StringTestEnum.self ~> StringTestEnum.self) == true
205
+ expect(~IntTestEnum.self ~> IntTestEnum.self) == true
206
+ }
207
+ it("is false") {
208
+ expect(~StringTestEnum.self !~> IntTestEnum.self) == true
209
+ expect(~IntTestEnum.self !~> StringTestEnum.self) == true
210
+ }
211
+ }
212
+
213
+ describe("equals") {
214
+ it("is true") {
215
+ expect(~StringTestEnum.self == ~StringTestEnum.self) == true
216
+ expect(~IntTestEnum.self == ~IntTestEnum.self) == true
217
+ }
218
+ it("is false") {
219
+ expect(~StringTestEnum.self != ~IntTestEnum.self) == true
220
+ expect(~IntTestEnum.self != ~StringTestEnum.self) == true
221
+ expect(~StringTestEnum.self != ~Double.self) == true
222
+ expect(~IntTestEnum.self != ~Int.self) == true
223
+ }
224
+ }
225
+ }
226
+
227
+ // MARK: - DynamicOptionalType
228
+
229
+ describe("DynamicOptionalType") {
230
+ it("is created") {
231
+ expect(~String?.self).to(beAKindOf(DynamicOptionalType.self))
232
+ expect(~[Double]?.self).to(beAKindOf(DynamicOptionalType.self))
233
+ expect(~[Int?]?.self).to(beAKindOf(DynamicOptionalType.self))
234
+ }
235
+ describe("casts") {
236
+ it("succeeds") {
237
+ expect(try (~String?.self).cast("expo") as? String) == "expo"
238
+ expect(try (~Bool?.self).cast(false) as? Bool) == false
239
+ }
240
+ it("succeeds with nil") {
241
+ let value: Double? = nil
242
+ let result = try (~Double?.self).cast(value as Any)
243
+
244
+ expect(result).to(beAKindOf(Double?.self))
245
+
246
+ // Since this `nil` is in fact of non-optional `Any` type, under the hood it's described as `Optional` enum.
247
+ // Simply checking `result == nil` does NOT work here, see `Optional.isNil` extension implementation.
248
+ expect(Optional.isNil(result)) == true
249
+ }
250
+ it("succeeds with NSNull") {
251
+ let value = NSNull()
252
+ let result = try (~Double?.self).cast(value as Any)
253
+ expect(result).to(beAKindOf(Double?.self))
254
+ expect(Optional.isNil(result)) == true
255
+ }
256
+ it("throws CastingException") {
257
+ expect { try (~Double?.self).cast("a string") as? Double }.to(
258
+ throwError(errorType: Conversions.CastingException<Double>.self)
259
+ )
260
+ }
261
+ }
262
+ describe("wraps") {
263
+ it("is true") {
264
+ expect(~Double?.self ~> Double?.self) == true
265
+ expect(~String?.self ~> String?.self) == true
266
+ expect(~URL?.self ~> URL?.self) == true
267
+ }
268
+ it("is false") {
269
+ expect(~Double?.self !~> Double.self) == true
270
+ expect(~[URL]?.self !~> URL.self) == true
271
+ expect(~[String]?.self !~> [String?].self) == true
272
+ }
273
+ }
274
+ describe("equals") {
275
+ it("is true") {
276
+ expect(~Int?.self == ~Int?.self) == true
277
+ expect(~ExpoSpec.self == ~ExpoSpec.self) == true
278
+ }
279
+ it("is false") {
280
+ expect(~Double?.self != ~Double.self) == true
281
+ expect(~Int?.self != ~Double?.self) == true
282
+ expect(~[Bool]?.self != ~[Bool].self) == true
283
+ }
284
+ }
285
+ }
286
+
287
+ // MARK: - DynamicSharedObjectType
288
+
289
+ describe("DynamicSharedObjectType") {
290
+ class TestSharedObject: SharedObject {}
291
+
292
+ it("is created") {
293
+ expect(~TestSharedObject.self).to(beAKindOf(DynamicSharedObjectType.self))
294
+ }
295
+ describe("casts") {
296
+ it("succeeds") {
297
+ let appContext = AppContext.create()
298
+ let nativeObject = TestSharedObject()
299
+ let jsObjectValue = try appContext.runtime!.eval("({})")
300
+
301
+ SharedObjectRegistry.add(native: nativeObject, javaScript: try jsObjectValue.asObject())
302
+
303
+ // `DynamicSharedObjectType` only supports casting
304
+ // from `JavaScriptValue`, but not from `JavaScriptObject`.
305
+ expect(try (~TestSharedObject.self).cast(jsObjectValue) as? TestSharedObject) === nativeObject
306
+ }
307
+ it("throws NativeSharedObjectNotFoundException") {
308
+ expect { try (~TestSharedObject.self).cast("a string") as? TestSharedObject }.to(
309
+ throwError(errorType: NativeSharedObjectNotFoundException.self)
310
+ )
311
+ }
312
+ }
313
+ describe("wraps") {
314
+ it("is true") {
315
+ expect(~TestSharedObject.self ~> TestSharedObject.self) == true
316
+ }
317
+ it("is false") {
318
+ expect(~TestSharedObject.self !~> SharedObject.self) == true
319
+ expect(~TestSharedObject.self !~> String?.self) == true
320
+ }
321
+ }
322
+ describe("equals") {
323
+ it("is true") {
324
+ expect(~TestSharedObject.self == ~TestSharedObject.self) == true
325
+ expect(~TestSharedObject?.self == ~TestSharedObject?.self) == true
326
+ expect(~[TestSharedObject].self == ~[TestSharedObject].self) == true
327
+ }
328
+ it("is false") {
329
+ expect(~TestSharedObject.self != ~TestSharedObject?.self) == true
330
+ expect(~TestSharedObject.self != ~[String: Any].self) == true
331
+ expect(~TestSharedObject.self != ~[TestSharedObject].self) == true
332
+ }
333
+ }
334
+ }
335
+ }
336
+ }
@@ -0,0 +1,48 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ import ExpoModulesTestCore
4
+
5
+ @testable import ExpoModulesCore
6
+
7
+ final class EnumArgumentSpec: ExpoSpec {
8
+ override func spec() {
9
+ describe("static createFromRawValue") {
10
+ it("succeeds") {
11
+ expect(try Position.create(fromRawValue: "top")) == .top
12
+ expect(try Position.create(fromRawValue: "right")) == .right
13
+ }
14
+ it("throws EnumNoSuchValueException") {
15
+ expect { try Position.create(fromRawValue: "top-right") }.to(
16
+ throwError(errorType: EnumNoSuchValueException.self)
17
+ )
18
+ }
19
+ it("throws EnumCastingException") {
20
+ expect { try Position.create(fromRawValue: 4729) }.to(
21
+ throwError(errorType: EnumCastingException.self)
22
+ )
23
+ expect { try Position.create(fromRawValue: ["left"]) }.to(
24
+ throwError(errorType: EnumCastingException.self)
25
+ )
26
+ }
27
+ }
28
+
29
+ describe("anyRawValue") {
30
+ it("returns type-erased raw value") {
31
+ expect(Position.left.anyRawValue as? String) == Position.left.rawValue
32
+ }
33
+ }
34
+
35
+ describe("allRawValues") {
36
+ it("returns all raw values") {
37
+ expect(Position.allRawValues as? [String]) == ["top", "right", "bottom", "left"]
38
+ }
39
+ }
40
+ }
41
+ }
42
+
43
+ fileprivate enum Position: String, EnumArgument {
44
+ case top
45
+ case right
46
+ case bottom
47
+ case left
48
+ }
@@ -7,24 +7,27 @@ import ExpoModulesTestCore
7
7
  class ExpoModulesSpec: ExpoSpec {
8
8
  override func spec() {
9
9
  let appContext = AppContext.create()
10
- let interopBridge = SwiftInteropBridge(appContext: appContext)
11
10
  let runtime = appContext.runtime
12
11
  let testModuleName = "TestModule"
13
12
  let testFunctionName = "testFunction"
13
+ let throwingFunctionName = "throwingFunction"
14
+ let exceptionToThrow = Exception(name: "Some exception", description: "Exception description")
14
15
  let constantsDict: [String: Any] = [
15
16
  "expo": "is cool",
16
17
  "sdk": 45,
17
18
  ]
18
19
 
19
20
  beforeSuite {
20
- try! appContext.installExpoModulesHostObject(interopBridge)
21
-
22
21
  appContext.moduleRegistry.register(holder: mockModuleHolder(appContext) {
23
22
  Name(testModuleName)
24
23
 
25
24
  Constants(constantsDict)
26
25
 
27
26
  Function(testFunctionName) { Double.pi }
27
+
28
+ Function(throwingFunctionName) {
29
+ throw exceptionToThrow
30
+ }
28
31
  })
29
32
  }
30
33
 
@@ -70,6 +73,17 @@ class ExpoModulesSpec: ExpoSpec {
70
73
  it("calls function") {
71
74
  expect(try! runtime?.eval("ExpoModules.TestModule.\(testFunctionName)()").asDouble()) == Double.pi
72
75
  }
76
+
77
+ it("throws from sync function") {
78
+ // Invoke the throwing function and return the error (eval shouldn't rethrow here)
79
+ let error = try! runtime!.eval("try { ExpoModules.TestModule.\(throwingFunctionName)() } catch (error) { error }").asObject()
80
+
81
+ // We just check if it contains the description — they won't be equal for the following reasons:
82
+ // - the `exceptionToThrow` is just the root cause, in fact it returns `FunctionCallException`
83
+ // - the debug description contains the file and line number, so it's hard to mock the `FunctionCallException`
84
+ // Ideally if we have a better way (error codes/names) to identify them w/o relying on the description that may change over time.
85
+ expect(error.getProperty("message").getString()).to(contain(exceptionToThrow.debugDescription))
86
+ }
73
87
  }
74
88
  }
75
89
  }