@thelacanians/vue-native-cli 0.4.11 → 0.4.13
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/dist/cli.js +1 -1
- package/native/android/.editorconfig +25 -0
- package/native/android/VueNativeCore/build.gradle.kts +25 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSPolyfills.kt +17 -10
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/JSRuntime.kt +5 -5
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +13 -13
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/ComponentRegistry.kt +27 -27
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActionSheetFactory.kt +6 -4
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VActivityIndicatorFactory.kt +1 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VAlertDialogFactory.kt +24 -12
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VButtonFactory.kt +5 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VImageFactory.kt +7 -7
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VInputFactory.kt +12 -12
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VKeyboardAvoidingFactory.kt +0 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VListFactory.kt +5 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VModalFactory.kt +5 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPickerFactory.kt +3 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VPressableFactory.kt +5 -3
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VRootFactory.kt +5 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VScrollViewFactory.kt +5 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSectionListFactory.kt +5 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSegmentedControlFactory.kt +3 -3
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VStatusBarFactory.kt +3 -3
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VSwitchFactory.kt +0 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +9 -3
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VWebViewFactory.kt +7 -5
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/NativeComponentFactory.kt +5 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Helpers/GestureHelper.kt +4 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AnimationModule.kt +77 -21
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/AsyncStorageModule.kt +20 -5
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BackgroundTaskModule.kt +12 -3
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BiometryModule.kt +5 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/BluetoothModule.kt +88 -23
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/CalendarModule.kt +24 -11
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ClipboardModule.kt +7 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ContactsModule.kt +24 -12
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/DeviceInfoModule.kt +14 -11
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/FileSystemModule.kt +79 -24
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeolocationModule.kt +10 -7
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HapticsModule.kt +5 -5
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/HttpModule.kt +17 -8
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/IAPModule.kt +20 -5
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/KeyboardModule.kt +4 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/LinkingModule.kt +12 -3
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NetworkModule.kt +4 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NotificationsModule.kt +24 -6
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/OTAModule.kt +13 -5
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PerformanceModule.kt +8 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/PermissionsModule.kt +17 -8
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SecureStorageModule.kt +20 -5
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SensorsModule.kt +16 -4
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/ShareModule.kt +6 -3
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/SocialAuthModule.kt +4 -2
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/WebSocketModule.kt +26 -8
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Styling/StyleEngine.kt +127 -84
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Tags.kt +26 -26
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/VueNativeActivity.kt +1 -1
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentRegistryTest.kt +173 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +436 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleRegistryTest.kt +251 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/StyleEngineTest.kt +482 -0
- package/native/android/build.gradle.kts +1 -0
- package/native/ios/.swiftlint.yml +62 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +4 -1
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentRegistryTests.swift +237 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeBridgeOperationTests.swift +398 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleRegistryTests.swift +203 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/StyleEngineTests.swift +381 -0
- package/package.json +1 -1
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.os.Looper
|
|
5
|
+
import android.view.ViewGroup
|
|
6
|
+
import android.widget.FrameLayout
|
|
7
|
+
import androidx.test.core.app.ApplicationProvider
|
|
8
|
+
import com.google.android.flexbox.FlexboxLayout
|
|
9
|
+
import org.junit.After
|
|
10
|
+
import org.junit.Assert.assertEquals
|
|
11
|
+
import org.junit.Assert.assertFalse
|
|
12
|
+
import org.junit.Assert.assertNotNull
|
|
13
|
+
import org.junit.Assert.assertNull
|
|
14
|
+
import org.junit.Assert.assertTrue
|
|
15
|
+
import org.junit.Before
|
|
16
|
+
import org.junit.Test
|
|
17
|
+
import org.junit.runner.RunWith
|
|
18
|
+
import org.robolectric.RobolectricTestRunner
|
|
19
|
+
import org.robolectric.Shadows
|
|
20
|
+
import org.robolectric.annotation.Config
|
|
21
|
+
|
|
22
|
+
@RunWith(RobolectricTestRunner::class)
|
|
23
|
+
@Config(sdk = [34])
|
|
24
|
+
class NativeBridgeTest {
|
|
25
|
+
|
|
26
|
+
private lateinit var context: Context
|
|
27
|
+
private lateinit var bridge: NativeBridge
|
|
28
|
+
|
|
29
|
+
@Before
|
|
30
|
+
fun setUp() {
|
|
31
|
+
// Reset ComponentRegistry singleton via reflection
|
|
32
|
+
val crField = ComponentRegistry::class.java.getDeclaredField("instance")
|
|
33
|
+
crField.isAccessible = true
|
|
34
|
+
crField.set(null, null)
|
|
35
|
+
|
|
36
|
+
// Reset NativeModuleRegistry singleton via reflection
|
|
37
|
+
val nmrField = NativeModuleRegistry::class.java.getDeclaredField("instance")
|
|
38
|
+
nmrField.isAccessible = true
|
|
39
|
+
nmrField.set(null, null)
|
|
40
|
+
|
|
41
|
+
context = ApplicationProvider.getApplicationContext()
|
|
42
|
+
bridge = NativeBridge(context)
|
|
43
|
+
|
|
44
|
+
// Set up a host container so setRootView can attach views
|
|
45
|
+
val container = FrameLayout(context)
|
|
46
|
+
bridge.hostContainer = container
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@After
|
|
50
|
+
fun tearDown() {
|
|
51
|
+
bridge.clearAllRegistries()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private fun flush() {
|
|
55
|
+
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// -------------------------------------------------------------------------
|
|
59
|
+
// create
|
|
60
|
+
// -------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
@Test
|
|
63
|
+
fun testCreate() {
|
|
64
|
+
bridge.processOperations("""[{"op":"create","args":[1,"VView"]}]""")
|
|
65
|
+
flush()
|
|
66
|
+
|
|
67
|
+
assertNotNull("nodeViews[1] should exist", bridge.nodeViews[1])
|
|
68
|
+
assertEquals("VView", bridge.nodeTypes[1])
|
|
69
|
+
assertTrue("VView should create a FlexboxLayout", bridge.nodeViews[1] is FlexboxLayout)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// -------------------------------------------------------------------------
|
|
73
|
+
// createText
|
|
74
|
+
// -------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
@Test
|
|
77
|
+
fun testCreateText() {
|
|
78
|
+
bridge.processOperations("""[{"op":"createText","args":[2,"Hello"]}]""")
|
|
79
|
+
flush()
|
|
80
|
+
|
|
81
|
+
assertNotNull("nodeViews[2] should exist", bridge.nodeViews[2])
|
|
82
|
+
assertTrue("createText should produce VTextNodeView", bridge.nodeViews[2] is VTextNodeView)
|
|
83
|
+
assertEquals("Hello", (bridge.nodeViews[2] as VTextNodeView).text.toString())
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// -------------------------------------------------------------------------
|
|
87
|
+
// appendChild
|
|
88
|
+
// -------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
@Test
|
|
91
|
+
fun testAppendChild() {
|
|
92
|
+
bridge.processOperations(
|
|
93
|
+
"""[
|
|
94
|
+
{"op":"create","args":[1,"VView"]},
|
|
95
|
+
{"op":"create","args":[2,"VView"]},
|
|
96
|
+
{"op":"appendChild","args":[1,2]}
|
|
97
|
+
]"""
|
|
98
|
+
)
|
|
99
|
+
flush()
|
|
100
|
+
|
|
101
|
+
val parent = bridge.nodeViews[1] as ViewGroup
|
|
102
|
+
val child = bridge.nodeViews[2]!!
|
|
103
|
+
assertTrue("Child should be a child of parent", parent.indexOfChild(child) >= 0)
|
|
104
|
+
assertEquals("nodeParents should track parent", 1, bridge.nodeParents[2])
|
|
105
|
+
assertTrue("nodeChildren should track child", bridge.nodeChildren[1]?.contains(2) == true)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// -------------------------------------------------------------------------
|
|
109
|
+
// removeChild
|
|
110
|
+
// -------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
@Test
|
|
113
|
+
fun testRemoveChild() {
|
|
114
|
+
bridge.processOperations(
|
|
115
|
+
"""[
|
|
116
|
+
{"op":"create","args":[1,"VView"]},
|
|
117
|
+
{"op":"create","args":[2,"VView"]},
|
|
118
|
+
{"op":"appendChild","args":[1,2]}
|
|
119
|
+
]"""
|
|
120
|
+
)
|
|
121
|
+
flush()
|
|
122
|
+
|
|
123
|
+
// Verify child is attached
|
|
124
|
+
val parent = bridge.nodeViews[1] as ViewGroup
|
|
125
|
+
assertNotNull(bridge.nodeViews[2])
|
|
126
|
+
assertTrue(parent.indexOfChild(bridge.nodeViews[2]!!) >= 0)
|
|
127
|
+
|
|
128
|
+
// Remove child
|
|
129
|
+
bridge.processOperations("""[{"op":"removeChild","args":[2]}]""")
|
|
130
|
+
flush()
|
|
131
|
+
|
|
132
|
+
assertFalse("nodeViews should not contain removed child", bridge.nodeViews.containsKey(2))
|
|
133
|
+
assertNull("nodeParents should not contain removed child", bridge.nodeParents[2])
|
|
134
|
+
assertEquals("Parent ViewGroup should have 0 children", 0, parent.childCount)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// -------------------------------------------------------------------------
|
|
138
|
+
// insertBefore
|
|
139
|
+
// -------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
@Test
|
|
142
|
+
fun testInsertBefore() {
|
|
143
|
+
bridge.processOperations(
|
|
144
|
+
"""[
|
|
145
|
+
{"op":"create","args":[1,"VView"]},
|
|
146
|
+
{"op":"create","args":[2,"VView"]},
|
|
147
|
+
{"op":"create","args":[3,"VView"]},
|
|
148
|
+
{"op":"appendChild","args":[1,2]},
|
|
149
|
+
{"op":"insertBefore","args":[1,3,2]}
|
|
150
|
+
]"""
|
|
151
|
+
)
|
|
152
|
+
flush()
|
|
153
|
+
|
|
154
|
+
val parent = bridge.nodeViews[1] as ViewGroup
|
|
155
|
+
val child2 = bridge.nodeViews[2]!!
|
|
156
|
+
val child3 = bridge.nodeViews[3]!!
|
|
157
|
+
|
|
158
|
+
val idx2 = parent.indexOfChild(child2)
|
|
159
|
+
val idx3 = parent.indexOfChild(child3)
|
|
160
|
+
assertTrue("child3 should be before child2", idx3 < idx2)
|
|
161
|
+
assertEquals("child3 should be at index 0", 0, idx3)
|
|
162
|
+
assertEquals("child2 should be at index 1", 1, idx2)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// -------------------------------------------------------------------------
|
|
166
|
+
// updateProp
|
|
167
|
+
// -------------------------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
@Test
|
|
170
|
+
fun testUpdateProp() {
|
|
171
|
+
bridge.processOperations(
|
|
172
|
+
"""[
|
|
173
|
+
{"op":"create","args":[1,"VView"]},
|
|
174
|
+
{"op":"updateProp","args":[1,"backgroundColor","#ff0000"]}
|
|
175
|
+
]"""
|
|
176
|
+
)
|
|
177
|
+
flush()
|
|
178
|
+
|
|
179
|
+
val view = bridge.nodeViews[1]!!
|
|
180
|
+
// After setting backgroundColor, the view should have a GradientDrawable background
|
|
181
|
+
assertNotNull("View background should be set", view.background)
|
|
182
|
+
assertTrue(
|
|
183
|
+
"Background should be a GradientDrawable",
|
|
184
|
+
view.background is android.graphics.drawable.GradientDrawable
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// -------------------------------------------------------------------------
|
|
189
|
+
// updateStyle
|
|
190
|
+
// -------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
@Test
|
|
193
|
+
fun testUpdateStyle() {
|
|
194
|
+
bridge.processOperations(
|
|
195
|
+
"""[
|
|
196
|
+
{"op":"create","args":[1,"VView"]},
|
|
197
|
+
{"op":"updateStyle","args":[1,{"opacity":0.5}]}
|
|
198
|
+
]"""
|
|
199
|
+
)
|
|
200
|
+
flush()
|
|
201
|
+
|
|
202
|
+
val view = bridge.nodeViews[1]!!
|
|
203
|
+
assertEquals("alpha should be 0.5", 0.5f, view.alpha, 0.01f)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// -------------------------------------------------------------------------
|
|
207
|
+
// setText
|
|
208
|
+
// -------------------------------------------------------------------------
|
|
209
|
+
|
|
210
|
+
@Test
|
|
211
|
+
fun testSetText() {
|
|
212
|
+
bridge.processOperations(
|
|
213
|
+
"""[
|
|
214
|
+
{"op":"createText","args":[1,"Hello"]},
|
|
215
|
+
{"op":"setText","args":[1,"World"]}
|
|
216
|
+
]"""
|
|
217
|
+
)
|
|
218
|
+
flush()
|
|
219
|
+
|
|
220
|
+
val textView = bridge.nodeViews[1] as VTextNodeView
|
|
221
|
+
assertEquals("World", textView.text.toString())
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// -------------------------------------------------------------------------
|
|
225
|
+
// addEventListener
|
|
226
|
+
// -------------------------------------------------------------------------
|
|
227
|
+
|
|
228
|
+
@Test
|
|
229
|
+
fun testAddEventListener() {
|
|
230
|
+
bridge.processOperations(
|
|
231
|
+
"""[
|
|
232
|
+
{"op":"create","args":[1,"VView"]},
|
|
233
|
+
{"op":"addEventListener","args":[1,"press"]}
|
|
234
|
+
]"""
|
|
235
|
+
)
|
|
236
|
+
flush()
|
|
237
|
+
|
|
238
|
+
assertTrue(
|
|
239
|
+
"eventHandlers should have entry for '1:press'",
|
|
240
|
+
bridge.eventHandlers.containsKey("1:press")
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// -------------------------------------------------------------------------
|
|
245
|
+
// removeEventListener
|
|
246
|
+
// -------------------------------------------------------------------------
|
|
247
|
+
|
|
248
|
+
@Test
|
|
249
|
+
fun testRemoveEventListener() {
|
|
250
|
+
bridge.processOperations(
|
|
251
|
+
"""[
|
|
252
|
+
{"op":"create","args":[1,"VView"]},
|
|
253
|
+
{"op":"addEventListener","args":[1,"press"]},
|
|
254
|
+
{"op":"removeEventListener","args":[1,"press"]}
|
|
255
|
+
]"""
|
|
256
|
+
)
|
|
257
|
+
flush()
|
|
258
|
+
|
|
259
|
+
assertFalse(
|
|
260
|
+
"eventHandlers should not have entry for '1:press'",
|
|
261
|
+
bridge.eventHandlers.containsKey("1:press")
|
|
262
|
+
)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// -------------------------------------------------------------------------
|
|
266
|
+
// setRootView
|
|
267
|
+
// -------------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
@Test
|
|
270
|
+
fun testSetRootView() {
|
|
271
|
+
bridge.processOperations(
|
|
272
|
+
"""[
|
|
273
|
+
{"op":"create","args":[1,"VView"]},
|
|
274
|
+
{"op":"setRootView","args":[1]}
|
|
275
|
+
]"""
|
|
276
|
+
)
|
|
277
|
+
flush()
|
|
278
|
+
|
|
279
|
+
assertNotNull("rootView should be set", bridge.rootView)
|
|
280
|
+
assertEquals("rootView should be nodeViews[1]", bridge.nodeViews[1], bridge.rootView)
|
|
281
|
+
assertEquals(
|
|
282
|
+
"hostContainer should have 1 child",
|
|
283
|
+
1,
|
|
284
|
+
bridge.hostContainer!!.childCount
|
|
285
|
+
)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// -------------------------------------------------------------------------
|
|
289
|
+
// clearAllRegistries
|
|
290
|
+
// -------------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
@Test
|
|
293
|
+
fun testClearAllRegistries() {
|
|
294
|
+
bridge.processOperations(
|
|
295
|
+
"""[
|
|
296
|
+
{"op":"create","args":[1,"VView"]},
|
|
297
|
+
{"op":"create","args":[2,"VView"]},
|
|
298
|
+
{"op":"appendChild","args":[1,2]}
|
|
299
|
+
]"""
|
|
300
|
+
)
|
|
301
|
+
flush()
|
|
302
|
+
|
|
303
|
+
// Verify state exists
|
|
304
|
+
assertTrue(bridge.nodeViews.isNotEmpty())
|
|
305
|
+
assertTrue(bridge.nodeTypes.isNotEmpty())
|
|
306
|
+
assertTrue(bridge.nodeParents.isNotEmpty())
|
|
307
|
+
assertTrue(bridge.nodeChildren.isNotEmpty())
|
|
308
|
+
|
|
309
|
+
bridge.clearAllRegistries()
|
|
310
|
+
|
|
311
|
+
assertTrue("nodeViews should be empty", bridge.nodeViews.isEmpty())
|
|
312
|
+
assertTrue("nodeTypes should be empty", bridge.nodeTypes.isEmpty())
|
|
313
|
+
assertTrue("eventHandlers should be empty", bridge.eventHandlers.isEmpty())
|
|
314
|
+
assertTrue("nodeParents should be empty", bridge.nodeParents.isEmpty())
|
|
315
|
+
assertTrue("nodeChildren should be empty", bridge.nodeChildren.isEmpty())
|
|
316
|
+
assertNull("rootView should be null", bridge.rootView)
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// -------------------------------------------------------------------------
|
|
320
|
+
// fireEvent
|
|
321
|
+
// -------------------------------------------------------------------------
|
|
322
|
+
|
|
323
|
+
@Test
|
|
324
|
+
fun testFireEvent() {
|
|
325
|
+
var capturedNodeId = -1
|
|
326
|
+
var capturedEventName = ""
|
|
327
|
+
var capturedPayloadJson = ""
|
|
328
|
+
|
|
329
|
+
bridge.onFireEvent = { nodeId, eventName, payloadJson ->
|
|
330
|
+
capturedNodeId = nodeId
|
|
331
|
+
capturedEventName = eventName
|
|
332
|
+
capturedPayloadJson = payloadJson
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
bridge.fireEvent(42, "press", mapOf("x" to 10, "y" to 20))
|
|
336
|
+
|
|
337
|
+
assertEquals(42, capturedNodeId)
|
|
338
|
+
assertEquals("press", capturedEventName)
|
|
339
|
+
assertTrue(
|
|
340
|
+
"Payload should contain x and y",
|
|
341
|
+
capturedPayloadJson.contains("\"x\"") && capturedPayloadJson.contains("\"y\"")
|
|
342
|
+
)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// -------------------------------------------------------------------------
|
|
346
|
+
// dispatchGlobalEvent
|
|
347
|
+
// -------------------------------------------------------------------------
|
|
348
|
+
|
|
349
|
+
@Test
|
|
350
|
+
fun testDispatchGlobalEvent() {
|
|
351
|
+
var capturedEventName = ""
|
|
352
|
+
var capturedPayloadJson = ""
|
|
353
|
+
|
|
354
|
+
bridge.onDispatchGlobalEvent = { eventName, payloadJson ->
|
|
355
|
+
capturedEventName = eventName
|
|
356
|
+
capturedPayloadJson = payloadJson
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
bridge.dispatchGlobalEvent("networkChange", mapOf("connected" to "true"))
|
|
360
|
+
|
|
361
|
+
assertEquals("networkChange", capturedEventName)
|
|
362
|
+
assertTrue(
|
|
363
|
+
"Payload should contain connected",
|
|
364
|
+
capturedPayloadJson.contains("\"connected\"")
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// -------------------------------------------------------------------------
|
|
369
|
+
// invalidJson
|
|
370
|
+
// -------------------------------------------------------------------------
|
|
371
|
+
|
|
372
|
+
@Test
|
|
373
|
+
fun testInvalidJson() {
|
|
374
|
+
// Should not crash
|
|
375
|
+
bridge.processOperations("this is not json")
|
|
376
|
+
flush()
|
|
377
|
+
|
|
378
|
+
// Verify bridge still works after invalid JSON
|
|
379
|
+
bridge.processOperations("""[{"op":"create","args":[1,"VView"]}]""")
|
|
380
|
+
flush()
|
|
381
|
+
assertNotNull(bridge.nodeViews[1])
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// -------------------------------------------------------------------------
|
|
385
|
+
// unknownOperation
|
|
386
|
+
// -------------------------------------------------------------------------
|
|
387
|
+
|
|
388
|
+
@Test
|
|
389
|
+
fun testUnknownOperation() {
|
|
390
|
+
// Should not crash
|
|
391
|
+
bridge.processOperations("""[{"op":"unknown","args":[]}]""")
|
|
392
|
+
flush()
|
|
393
|
+
|
|
394
|
+
// Verify bridge still works after unknown operation
|
|
395
|
+
bridge.processOperations("""[{"op":"create","args":[1,"VView"]}]""")
|
|
396
|
+
flush()
|
|
397
|
+
assertNotNull(bridge.nodeViews[1])
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// -------------------------------------------------------------------------
|
|
401
|
+
// cleanupNode
|
|
402
|
+
// -------------------------------------------------------------------------
|
|
403
|
+
|
|
404
|
+
@Test
|
|
405
|
+
fun testCleanupNode() {
|
|
406
|
+
// Create a parent with a child, child has a grandchild
|
|
407
|
+
bridge.processOperations(
|
|
408
|
+
"""[
|
|
409
|
+
{"op":"create","args":[1,"VView"]},
|
|
410
|
+
{"op":"create","args":[2,"VView"]},
|
|
411
|
+
{"op":"create","args":[3,"VView"]},
|
|
412
|
+
{"op":"appendChild","args":[1,2]},
|
|
413
|
+
{"op":"appendChild","args":[2,3]},
|
|
414
|
+
{"op":"addEventListener","args":[3,"press"]}
|
|
415
|
+
]"""
|
|
416
|
+
)
|
|
417
|
+
flush()
|
|
418
|
+
|
|
419
|
+
// Verify everything exists
|
|
420
|
+
assertNotNull(bridge.nodeViews[2])
|
|
421
|
+
assertNotNull(bridge.nodeViews[3])
|
|
422
|
+
assertTrue(bridge.eventHandlers.containsKey("3:press"))
|
|
423
|
+
|
|
424
|
+
// Remove child 2 (which should also clean up grandchild 3)
|
|
425
|
+
bridge.processOperations("""[{"op":"removeChild","args":[2]}]""")
|
|
426
|
+
flush()
|
|
427
|
+
|
|
428
|
+
assertFalse("nodeViews should not contain child 2", bridge.nodeViews.containsKey(2))
|
|
429
|
+
assertFalse("nodeViews should not contain grandchild 3", bridge.nodeViews.containsKey(3))
|
|
430
|
+
assertFalse("nodeTypes should not contain child 2", bridge.nodeTypes.containsKey(2))
|
|
431
|
+
assertFalse("nodeTypes should not contain grandchild 3", bridge.nodeTypes.containsKey(3))
|
|
432
|
+
assertFalse("eventHandlers for grandchild should be cleaned up", bridge.eventHandlers.containsKey("3:press"))
|
|
433
|
+
assertNull("nodeParents for child 2 should be null", bridge.nodeParents[2])
|
|
434
|
+
assertNull("nodeParents for grandchild 3 should be null", bridge.nodeParents[3])
|
|
435
|
+
}
|
|
436
|
+
}
|
package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleRegistryTest.kt
ADDED
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
package com.vuenative.core
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import androidx.test.core.app.ApplicationProvider
|
|
5
|
+
import org.junit.Assert.assertEquals
|
|
6
|
+
import org.junit.Assert.assertNotNull
|
|
7
|
+
import org.junit.Assert.assertNull
|
|
8
|
+
import org.junit.Assert.assertTrue
|
|
9
|
+
import org.junit.Before
|
|
10
|
+
import org.junit.Test
|
|
11
|
+
import org.junit.runner.RunWith
|
|
12
|
+
import org.robolectric.RobolectricTestRunner
|
|
13
|
+
import org.robolectric.annotation.Config
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Mock NativeModule for testing purposes.
|
|
17
|
+
*/
|
|
18
|
+
class MockNativeModule(override val moduleName: String) : NativeModule {
|
|
19
|
+
var lastMethod: String? = null
|
|
20
|
+
var lastArgs: List<Any?> = emptyList()
|
|
21
|
+
var resultToReturn: Any? = null
|
|
22
|
+
|
|
23
|
+
override fun invoke(method: String, args: List<Any?>, bridge: NativeBridge, callback: (Any?, String?) -> Unit) {
|
|
24
|
+
lastMethod = method
|
|
25
|
+
lastArgs = args
|
|
26
|
+
callback(resultToReturn, null)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
override fun invokeSync(method: String, args: List<Any?>, bridge: NativeBridge): Any? {
|
|
30
|
+
lastMethod = method
|
|
31
|
+
lastArgs = args
|
|
32
|
+
return resultToReturn
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@RunWith(RobolectricTestRunner::class)
|
|
37
|
+
@Config(sdk = [34])
|
|
38
|
+
class NativeModuleRegistryTest {
|
|
39
|
+
|
|
40
|
+
private lateinit var context: Context
|
|
41
|
+
private lateinit var registry: NativeModuleRegistry
|
|
42
|
+
private lateinit var bridge: NativeBridge
|
|
43
|
+
|
|
44
|
+
@Before
|
|
45
|
+
fun setUp() {
|
|
46
|
+
// Reset NativeModuleRegistry singleton via reflection
|
|
47
|
+
val nmrField = NativeModuleRegistry::class.java.getDeclaredField("instance")
|
|
48
|
+
nmrField.isAccessible = true
|
|
49
|
+
nmrField.set(null, null)
|
|
50
|
+
|
|
51
|
+
// Reset ComponentRegistry singleton via reflection (needed for NativeBridge)
|
|
52
|
+
val crField = ComponentRegistry::class.java.getDeclaredField("instance")
|
|
53
|
+
crField.isAccessible = true
|
|
54
|
+
crField.set(null, null)
|
|
55
|
+
|
|
56
|
+
context = ApplicationProvider.getApplicationContext()
|
|
57
|
+
registry = NativeModuleRegistry.getInstance(context)
|
|
58
|
+
bridge = NativeBridge(context)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// -------------------------------------------------------------------------
|
|
62
|
+
// Register and get module
|
|
63
|
+
// -------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
@Test
|
|
66
|
+
fun testRegisterAndGetModule() {
|
|
67
|
+
val mock = MockNativeModule("TestModule")
|
|
68
|
+
registry.register(mock)
|
|
69
|
+
|
|
70
|
+
val retrieved = registry.getModule("TestModule")
|
|
71
|
+
assertNotNull("getModule should return the registered module", retrieved)
|
|
72
|
+
assertEquals("TestModule", retrieved!!.moduleName)
|
|
73
|
+
assertTrue("Retrieved module should be the same instance", retrieved === mock)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// -------------------------------------------------------------------------
|
|
77
|
+
// Get unknown module returns null
|
|
78
|
+
// -------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
@Test
|
|
81
|
+
fun testGetUnknownModule() {
|
|
82
|
+
val result = registry.getModule("nonexistent")
|
|
83
|
+
assertNull("getModule for unknown name should return null", result)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// -------------------------------------------------------------------------
|
|
87
|
+
// invoke — async
|
|
88
|
+
// -------------------------------------------------------------------------
|
|
89
|
+
|
|
90
|
+
@Test
|
|
91
|
+
fun testInvoke() {
|
|
92
|
+
val mock = MockNativeModule("TestModule")
|
|
93
|
+
mock.resultToReturn = mapOf("success" to true)
|
|
94
|
+
registry.register(mock)
|
|
95
|
+
|
|
96
|
+
var callbackResult: Any? = null
|
|
97
|
+
var callbackError: String? = "not_called"
|
|
98
|
+
|
|
99
|
+
registry.invoke("TestModule", "doSomething", listOf("arg1", 42), bridge) { result, error ->
|
|
100
|
+
callbackResult = result
|
|
101
|
+
callbackError = error
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
assertEquals("doSomething", mock.lastMethod)
|
|
105
|
+
assertEquals(listOf("arg1", 42), mock.lastArgs)
|
|
106
|
+
assertNotNull("Callback result should be non-null", callbackResult)
|
|
107
|
+
assertNull("Callback error should be null", callbackError)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// -------------------------------------------------------------------------
|
|
111
|
+
// invoke unknown module — callback receives error
|
|
112
|
+
// -------------------------------------------------------------------------
|
|
113
|
+
|
|
114
|
+
@Test
|
|
115
|
+
fun testInvokeUnknownModule() {
|
|
116
|
+
var callbackResult: Any? = "not_null"
|
|
117
|
+
var callbackError: String? = null
|
|
118
|
+
|
|
119
|
+
registry.invoke("NonExistent", "method", emptyList(), bridge) { result, error ->
|
|
120
|
+
callbackResult = result
|
|
121
|
+
callbackError = error
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
assertNull("Result should be null for unknown module", callbackResult)
|
|
125
|
+
assertNotNull("Error should be non-null for unknown module", callbackError)
|
|
126
|
+
assertTrue(
|
|
127
|
+
"Error should mention the module name",
|
|
128
|
+
callbackError!!.contains("NonExistent")
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// -------------------------------------------------------------------------
|
|
133
|
+
// invokeSync
|
|
134
|
+
// -------------------------------------------------------------------------
|
|
135
|
+
|
|
136
|
+
@Test
|
|
137
|
+
fun testInvokeSync() {
|
|
138
|
+
val mock = MockNativeModule("SyncModule")
|
|
139
|
+
mock.resultToReturn = "sync_result"
|
|
140
|
+
registry.register(mock)
|
|
141
|
+
|
|
142
|
+
val result = registry.invokeSync("SyncModule", "getInfo", listOf("key"), bridge)
|
|
143
|
+
|
|
144
|
+
assertEquals("sync_result", result)
|
|
145
|
+
assertEquals("getInfo", mock.lastMethod)
|
|
146
|
+
assertEquals(listOf("key"), mock.lastArgs)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// -------------------------------------------------------------------------
|
|
150
|
+
// invokeSync unknown module — returns null
|
|
151
|
+
// -------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
@Test
|
|
154
|
+
fun testInvokeSyncUnknownModule() {
|
|
155
|
+
val result = registry.invokeSync("NonExistent", "method", emptyList(), bridge)
|
|
156
|
+
assertNull("invokeSync for unknown module should return null", result)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// -------------------------------------------------------------------------
|
|
160
|
+
// registerDefaults — key modules are present
|
|
161
|
+
// -------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
@Test
|
|
164
|
+
fun testRegisterDefaults() {
|
|
165
|
+
// Some modules (e.g. SecureStorageModule) may throw during initialize()
|
|
166
|
+
// in the Robolectric environment (KeyStore not available). Wrap with try/catch
|
|
167
|
+
// and verify the modules that registered successfully.
|
|
168
|
+
try {
|
|
169
|
+
registry.registerDefaults(bridge)
|
|
170
|
+
} catch (_: Exception) {
|
|
171
|
+
// Some modules may fail to initialize in test — that's OK
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Spot-check modules that register before SecureStorageModule (which throws
|
|
175
|
+
// java.security.KeyStoreException in Robolectric, halting the forEach loop).
|
|
176
|
+
// Modules registered in order: Haptics, AsyncStorage, Clipboard, DeviceInfo,
|
|
177
|
+
// Network, AppState, Linking, Share, Animation, Keyboard, Permissions,
|
|
178
|
+
// Geolocation, Notifications, Http, Biometry, Camera, SecureStorage(throws)...
|
|
179
|
+
assertNotNull("Haptics module should be registered", registry.getModule("Haptics"))
|
|
180
|
+
assertNotNull("AsyncStorage module should be registered", registry.getModule("AsyncStorage"))
|
|
181
|
+
assertNotNull("Clipboard module should be registered", registry.getModule("Clipboard"))
|
|
182
|
+
assertNotNull("DeviceInfo module should be registered", registry.getModule("DeviceInfo"))
|
|
183
|
+
assertNotNull("Network module should be registered", registry.getModule("Network"))
|
|
184
|
+
assertNotNull("Animation module should be registered", registry.getModule("Animation"))
|
|
185
|
+
assertNotNull("Http module should be registered", registry.getModule("Http"))
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// -------------------------------------------------------------------------
|
|
189
|
+
// Register overwrite — second module wins
|
|
190
|
+
// -------------------------------------------------------------------------
|
|
191
|
+
|
|
192
|
+
@Test
|
|
193
|
+
fun testRegisterOverwrite() {
|
|
194
|
+
val first = MockNativeModule("Shared")
|
|
195
|
+
first.resultToReturn = "first"
|
|
196
|
+
registry.register(first)
|
|
197
|
+
|
|
198
|
+
val second = MockNativeModule("Shared")
|
|
199
|
+
second.resultToReturn = "second"
|
|
200
|
+
registry.register(second)
|
|
201
|
+
|
|
202
|
+
val retrieved = registry.getModule("Shared")
|
|
203
|
+
assertNotNull(retrieved)
|
|
204
|
+
|
|
205
|
+
// Invoke and verify second module's result is used
|
|
206
|
+
val result = registry.invokeSync("Shared", "test", emptyList(), bridge)
|
|
207
|
+
assertEquals("second", result)
|
|
208
|
+
assertTrue("Retrieved module should be the second one", retrieved === second)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// -------------------------------------------------------------------------
|
|
212
|
+
// invoke with exception in module
|
|
213
|
+
// -------------------------------------------------------------------------
|
|
214
|
+
|
|
215
|
+
@Test
|
|
216
|
+
fun testInvokeWithModuleException() {
|
|
217
|
+
val throwingModule = object : NativeModule {
|
|
218
|
+
override val moduleName = "Thrower"
|
|
219
|
+
override fun invoke(method: String, args: List<Any?>, bridge: NativeBridge, callback: (Any?, String?) -> Unit) {
|
|
220
|
+
throw RuntimeException("Module exploded")
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
registry.register(throwingModule)
|
|
224
|
+
|
|
225
|
+
var callbackResult: Any? = "not_null"
|
|
226
|
+
var callbackError: String? = null
|
|
227
|
+
|
|
228
|
+
registry.invoke("Thrower", "boom", emptyList(), bridge) { result, error ->
|
|
229
|
+
callbackResult = result
|
|
230
|
+
callbackError = error
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
assertNull("Result should be null when module throws", callbackResult)
|
|
234
|
+
assertNotNull("Error should be set when module throws", callbackError)
|
|
235
|
+
assertTrue(
|
|
236
|
+
"Error message should contain exception message",
|
|
237
|
+
callbackError!!.contains("Module exploded")
|
|
238
|
+
)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// -------------------------------------------------------------------------
|
|
242
|
+
// Singleton behavior
|
|
243
|
+
// -------------------------------------------------------------------------
|
|
244
|
+
|
|
245
|
+
@Test
|
|
246
|
+
fun testSingletonReturnsSameInstance() {
|
|
247
|
+
val instance1 = NativeModuleRegistry.getInstance(context)
|
|
248
|
+
val instance2 = NativeModuleRegistry.getInstance(context)
|
|
249
|
+
assertTrue("getInstance should return the same instance", instance1 === instance2)
|
|
250
|
+
}
|
|
251
|
+
}
|