@thelacanians/vue-native-cli 0.4.15 → 0.6.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 (116) hide show
  1. package/dist/cli.js +329 -15
  2. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +118 -0
  3. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
  4. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
  5. package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
  6. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
  7. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
  8. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
  9. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
  10. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
  11. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
  12. package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
  13. package/native/android/gradle.properties +1 -0
  14. package/native/android/gradlew +1 -1
  15. package/native/ios/VueNativeCore/Package.swift +1 -1
  16. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +1 -0
  17. package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +143 -5
  18. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
  19. package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
  20. package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
  21. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
  22. package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
  23. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
  24. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
  25. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
  26. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
  27. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
  28. package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
  29. package/native/macos/VueNativeMacOS/Package.swift +34 -0
  30. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
  31. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
  32. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
  33. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
  34. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
  35. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
  36. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
  37. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
  38. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
  39. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
  40. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
  41. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
  42. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
  43. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
  44. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
  45. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
  46. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
  47. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
  48. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
  49. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
  50. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
  51. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
  52. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
  53. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
  54. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
  55. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
  56. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
  57. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
  58. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
  59. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
  60. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
  61. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
  62. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
  63. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
  64. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
  65. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
  66. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
  67. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
  68. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
  69. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
  70. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
  71. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
  72. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
  73. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
  74. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
  75. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
  76. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
  77. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
  78. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
  79. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
  80. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
  81. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
  82. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
  83. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
  84. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
  85. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
  86. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
  87. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
  88. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
  89. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
  90. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
  91. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
  92. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
  93. package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
  94. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
  95. package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
  96. package/native/shared/VueNativeShared/AGENTS.md +129 -0
  97. package/native/shared/VueNativeShared/Package.swift +14 -0
  98. package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
  99. package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
  100. package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
  101. package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
  102. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
  103. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
  104. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
  105. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
  106. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
  107. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
  108. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
  109. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
  110. package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
  111. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
  112. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
  113. package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
  114. package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
  115. package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
  116. package/package.json +8 -2
@@ -0,0 +1,585 @@
1
+ #if canImport(UIKit)
2
+ import XCTest
3
+ import UIKit
4
+ @testable import VueNativeCore
5
+
6
+ @MainActor
7
+ final class ComponentFactoryTests: XCTestCase {
8
+
9
+ // MARK: - VViewFactory Tests
10
+
11
+ func testVViewFactoryCreatesUIView() {
12
+ let factory = VViewFactory()
13
+ let view = factory.createView()
14
+ XCTAssertNotNil(view, "VViewFactory should create a UIView")
15
+ // Should be a plain UIView (not a subclass like UILabel, UISwitch etc.)
16
+ XCTAssertTrue(type(of: view) == UIView.self, "VViewFactory should create a plain UIView")
17
+ }
18
+
19
+ func testVViewFactoryAppliesStyleProps() {
20
+ let factory = VViewFactory()
21
+ let view = factory.createView()
22
+
23
+ factory.updateProp(view: view, key: "opacity", value: 0.5)
24
+ XCTAssertEqual(view.alpha, 0.5, accuracy: 0.001, "opacity prop should set alpha")
25
+
26
+ factory.updateProp(view: view, key: "backgroundColor", value: "#ff0000")
27
+ XCTAssertNotNil(view.backgroundColor, "backgroundColor should be set via StyleEngine")
28
+ }
29
+
30
+ func testVViewFactoryRegistersPressTapGesture() {
31
+ let factory = VViewFactory()
32
+ let view = factory.createView()
33
+ var handlerCalled = false
34
+
35
+ factory.addEventListener(view: view, event: "press") { _ in
36
+ handlerCalled = true
37
+ }
38
+
39
+ XCTAssertTrue(view.isUserInteractionEnabled, "User interaction should be enabled after adding press event")
40
+ let tapRecognizers = view.gestureRecognizers?.compactMap { $0 as? UITapGestureRecognizer } ?? []
41
+ XCTAssertFalse(tapRecognizers.isEmpty, "Should have a UITapGestureRecognizer for press event")
42
+ }
43
+
44
+ func testVViewFactoryRemovesPressEvent() {
45
+ let factory = VViewFactory()
46
+ let view = factory.createView()
47
+
48
+ factory.addEventListener(view: view, event: "press") { _ in }
49
+ let countBefore = view.gestureRecognizers?.count ?? 0
50
+ XCTAssertGreaterThan(countBefore, 0, "Should have gesture recognizers after addEventListener")
51
+
52
+ factory.removeEventListener(view: view, event: "press")
53
+ let tapRecognizers = view.gestureRecognizers?.compactMap { $0 as? UITapGestureRecognizer } ?? []
54
+ XCTAssertTrue(tapRecognizers.isEmpty, "Tap gesture recognizer should be removed")
55
+ }
56
+
57
+ // MARK: - VTextFactory Tests
58
+
59
+ func testVTextFactoryCreatesUILabel() {
60
+ let factory = VTextFactory()
61
+ let view = factory.createView()
62
+ XCTAssertTrue(view is UILabel, "VTextFactory should create a UILabel")
63
+ }
64
+
65
+ func testVTextFactoryLabelDefaultsMultiLine() {
66
+ let factory = VTextFactory()
67
+ let view = factory.createView() as! UILabel
68
+ XCTAssertEqual(view.numberOfLines, 0, "Label should default to multi-line (numberOfLines = 0)")
69
+ }
70
+
71
+ func testVTextFactorySetsText() {
72
+ let factory = VTextFactory()
73
+ let label = factory.createView() as! UILabel
74
+
75
+ factory.updateProp(view: label, key: "text", value: "Hello World")
76
+ XCTAssertEqual(label.text, "Hello World", "text prop should set the label's text")
77
+ }
78
+
79
+ func testVTextFactorySetsFontSize() {
80
+ let factory = VTextFactory()
81
+ let label = factory.createView() as! UILabel
82
+
83
+ factory.updateProp(view: label, key: "fontSize", value: 24.0)
84
+ XCTAssertEqual(label.font.pointSize, 24.0, accuracy: 0.1, "fontSize prop should set point size")
85
+ }
86
+
87
+ func testVTextFactorySetsTextColor() {
88
+ let factory = VTextFactory()
89
+ let label = factory.createView() as! UILabel
90
+
91
+ factory.updateProp(view: label, key: "color", value: "#0000ff")
92
+ var r: CGFloat = 0, g: CGFloat = 0, b: CGFloat = 0, a: CGFloat = 0
93
+ label.textColor.getRed(&r, green: &g, blue: &b, alpha: &a)
94
+ XCTAssertEqual(b, 1.0, accuracy: 0.01, "Blue component should be 1.0")
95
+ }
96
+
97
+ func testVTextFactorySetsTextAlignment() {
98
+ let factory = VTextFactory()
99
+ let label = factory.createView() as! UILabel
100
+
101
+ factory.updateProp(view: label, key: "textAlign", value: "center")
102
+ XCTAssertEqual(label.textAlignment, .center, "textAlign 'center' should set .center alignment")
103
+
104
+ factory.updateProp(view: label, key: "textAlign", value: "right")
105
+ XCTAssertEqual(label.textAlignment, .right, "textAlign 'right' should set .right alignment")
106
+ }
107
+
108
+ func testVTextFactorySetsNumberOfLines() {
109
+ let factory = VTextFactory()
110
+ let label = factory.createView() as! UILabel
111
+
112
+ factory.updateProp(view: label, key: "numberOfLines", value: 3)
113
+ XCTAssertEqual(label.numberOfLines, 3, "numberOfLines prop should set the value")
114
+ }
115
+
116
+ func testVTextFactoryTextNilClearsText() {
117
+ let factory = VTextFactory()
118
+ let label = factory.createView() as! UILabel
119
+
120
+ factory.updateProp(view: label, key: "text", value: "Hello")
121
+ factory.updateProp(view: label, key: "text", value: nil)
122
+ XCTAssertNil(label.text, "Setting text to nil should clear label.text")
123
+ }
124
+
125
+ func testVTextFactoryFontWeightMap() {
126
+ XCTAssertEqual(VTextFactory.fontWeightMap["bold"], .bold, "bold should map to .bold")
127
+ XCTAssertEqual(VTextFactory.fontWeightMap["400"], .regular, "400 should map to .regular")
128
+ XCTAssertEqual(VTextFactory.fontWeightMap["700"], .bold, "700 should map to .bold")
129
+ }
130
+
131
+ func testVTextFactoryTextTransformUppercase() {
132
+ let factory = VTextFactory()
133
+ let label = factory.createView() as! UILabel
134
+ factory.updateProp(view: label, key: "text", value: "hello")
135
+ factory.updateProp(view: label, key: "textTransform", value: "uppercase")
136
+ XCTAssertEqual(label.text, "HELLO", "textTransform 'uppercase' should uppercase the text")
137
+ }
138
+
139
+ // MARK: - VButtonFactory Tests
140
+
141
+ func testVButtonFactoryCreatesTouchableView() {
142
+ let factory = VButtonFactory()
143
+ let view = factory.createView()
144
+ XCTAssertTrue(view is TouchableView, "VButtonFactory should create a TouchableView")
145
+ }
146
+
147
+ func testVButtonFactoryDisabledProp() {
148
+ let factory = VButtonFactory()
149
+ let touchable = factory.createView() as! TouchableView
150
+
151
+ factory.updateProp(view: touchable, key: "disabled", value: true)
152
+ XCTAssertTrue(touchable.isDisabled, "disabled=true should set isDisabled")
153
+ XCTAssertFalse(touchable.isUserInteractionEnabled, "disabled should disable user interaction")
154
+
155
+ factory.updateProp(view: touchable, key: "disabled", value: false)
156
+ XCTAssertFalse(touchable.isDisabled, "disabled=false should unset isDisabled")
157
+ }
158
+
159
+ func testVButtonFactoryActiveOpacityProp() {
160
+ let factory = VButtonFactory()
161
+ let touchable = factory.createView() as! TouchableView
162
+
163
+ factory.updateProp(view: touchable, key: "activeOpacity", value: 0.3)
164
+ XCTAssertEqual(touchable.activeOpacity, 0.3, accuracy: 0.001, "activeOpacity should be set to 0.3")
165
+ }
166
+
167
+ func testVButtonFactoryRegistersPress() {
168
+ let factory = VButtonFactory()
169
+ let touchable = factory.createView() as! TouchableView
170
+ var pressed = false
171
+
172
+ factory.addEventListener(view: touchable, event: "press") { _ in
173
+ pressed = true
174
+ }
175
+
176
+ // Simulate press by calling onPress directly
177
+ touchable.onPress?()
178
+ XCTAssertTrue(pressed, "press event handler should be called")
179
+ }
180
+
181
+ func testVButtonFactoryRemovesPressEvent() {
182
+ let factory = VButtonFactory()
183
+ let touchable = factory.createView() as! TouchableView
184
+ var pressed = false
185
+
186
+ factory.addEventListener(view: touchable, event: "press") { _ in
187
+ pressed = true
188
+ }
189
+ factory.removeEventListener(view: touchable, event: "press")
190
+
191
+ touchable.onPress?()
192
+ XCTAssertFalse(pressed, "press handler should be nil after remove")
193
+ }
194
+
195
+ // MARK: - VInputFactory Tests
196
+
197
+ func testVInputFactoryCreatesUITextField() {
198
+ let factory = VInputFactory()
199
+ let view = factory.createView()
200
+ XCTAssertTrue(view is UITextField, "VInputFactory should create a UITextField")
201
+ }
202
+
203
+ func testVInputFactorySetsText() {
204
+ let factory = VInputFactory()
205
+ let textField = factory.createView() as! UITextField
206
+
207
+ factory.updateProp(view: textField, key: "text", value: "Hello Input")
208
+ XCTAssertEqual(textField.text, "Hello Input", "text prop should set text field text")
209
+ }
210
+
211
+ func testVInputFactorySetsPlaceholder() {
212
+ let factory = VInputFactory()
213
+ let textField = factory.createView() as! UITextField
214
+
215
+ factory.updateProp(view: textField, key: "placeholder", value: "Enter text...")
216
+ XCTAssertEqual(textField.placeholder, "Enter text...", "placeholder prop should be set")
217
+ }
218
+
219
+ func testVInputFactorySetsSecureTextEntry() {
220
+ let factory = VInputFactory()
221
+ let textField = factory.createView() as! UITextField
222
+
223
+ factory.updateProp(view: textField, key: "secureTextEntry", value: true)
224
+ XCTAssertTrue(textField.isSecureTextEntry, "secureTextEntry should be true")
225
+
226
+ factory.updateProp(view: textField, key: "secureTextEntry", value: false)
227
+ XCTAssertFalse(textField.isSecureTextEntry, "secureTextEntry should be false")
228
+ }
229
+
230
+ func testVInputFactorySetsKeyboardType() {
231
+ let factory = VInputFactory()
232
+ let textField = factory.createView() as! UITextField
233
+
234
+ factory.updateProp(view: textField, key: "keyboardType", value: "numeric")
235
+ XCTAssertEqual(textField.keyboardType, .numberPad, "keyboardType 'numeric' should set .numberPad")
236
+
237
+ factory.updateProp(view: textField, key: "keyboardType", value: "email")
238
+ XCTAssertEqual(textField.keyboardType, .emailAddress, "keyboardType 'email' should set .emailAddress")
239
+ }
240
+
241
+ func testVInputFactoryEditable() {
242
+ let factory = VInputFactory()
243
+ let textField = factory.createView() as! UITextField
244
+
245
+ factory.updateProp(view: textField, key: "editable", value: false)
246
+ XCTAssertFalse(textField.isEnabled, "editable=false should disable the text field")
247
+
248
+ factory.updateProp(view: textField, key: "editable", value: true)
249
+ XCTAssertTrue(textField.isEnabled, "editable=true should enable the text field")
250
+ }
251
+
252
+ func testVInputFactoryHandlesChangeTextEvent() {
253
+ let factory = VInputFactory()
254
+ let textField = factory.createView() as! UITextField
255
+ var receivedText: String?
256
+
257
+ factory.addEventListener(view: textField, event: "changetext") { payload in
258
+ receivedText = payload as? String
259
+ }
260
+
261
+ // Verify delegate is set up
262
+ XCTAssertNotNil(textField.delegate, "UITextField delegate should be set after addEventListener")
263
+ }
264
+
265
+ func testVInputFactoryReturnKeyType() {
266
+ let factory = VInputFactory()
267
+ let textField = factory.createView() as! UITextField
268
+
269
+ factory.updateProp(view: textField, key: "returnKeyType", value: "done")
270
+ XCTAssertEqual(textField.returnKeyType, .done, "returnKeyType 'done' should set .done")
271
+
272
+ factory.updateProp(view: textField, key: "returnKeyType", value: "search")
273
+ XCTAssertEqual(textField.returnKeyType, .search, "returnKeyType 'search' should set .search")
274
+ }
275
+
276
+ // MARK: - VSwitchFactory Tests
277
+
278
+ func testVSwitchFactoryCreatesUISwitch() {
279
+ let factory = VSwitchFactory()
280
+ let view = factory.createView()
281
+ XCTAssertTrue(view is UISwitch, "VSwitchFactory should create a UISwitch")
282
+ }
283
+
284
+ func testVSwitchFactorySetsValue() {
285
+ let factory = VSwitchFactory()
286
+ let sw = factory.createView() as! UISwitch
287
+
288
+ factory.updateProp(view: sw, key: "value", value: true)
289
+ XCTAssertTrue(sw.isOn, "value=true should set UISwitch on")
290
+
291
+ factory.updateProp(view: sw, key: "value", value: false)
292
+ XCTAssertFalse(sw.isOn, "value=false should set UISwitch off")
293
+ }
294
+
295
+ func testVSwitchFactorySetsDisabled() {
296
+ let factory = VSwitchFactory()
297
+ let sw = factory.createView() as! UISwitch
298
+
299
+ factory.updateProp(view: sw, key: "disabled", value: true)
300
+ XCTAssertFalse(sw.isEnabled, "disabled=true should disable the switch")
301
+ }
302
+
303
+ func testVSwitchFactorySetsOnTintColor() {
304
+ let factory = VSwitchFactory()
305
+ let sw = factory.createView() as! UISwitch
306
+
307
+ factory.updateProp(view: sw, key: "onTintColor", value: "#00ff00")
308
+ XCTAssertNotNil(sw.onTintColor, "onTintColor should be set")
309
+ }
310
+
311
+ func testVSwitchFactoryHandlesChangeEvent() {
312
+ let factory = VSwitchFactory()
313
+ let sw = factory.createView() as! UISwitch
314
+
315
+ factory.addEventListener(view: sw, event: "change") { _ in }
316
+ // The change handler is stored as an associated object
317
+ }
318
+
319
+ // MARK: - VImageFactory Tests
320
+
321
+ func testVImageFactoryCreatesUIImageView() {
322
+ let factory = VImageFactory()
323
+ let view = factory.createView()
324
+ XCTAssertTrue(view is UIImageView, "VImageFactory should create a UIImageView")
325
+ }
326
+
327
+ func testVImageFactoryDefaultContentMode() {
328
+ let factory = VImageFactory()
329
+ let imageView = factory.createView() as! UIImageView
330
+ XCTAssertEqual(imageView.contentMode, .scaleAspectFill, "Default content mode should be .scaleAspectFill")
331
+ }
332
+
333
+ func testVImageFactoryClipsToBounds() {
334
+ let factory = VImageFactory()
335
+ let imageView = factory.createView() as! UIImageView
336
+ XCTAssertTrue(imageView.clipsToBounds, "Image view should clip to bounds by default")
337
+ }
338
+
339
+ func testVImageFactorySetsResizeMode() {
340
+ let factory = VImageFactory()
341
+ let imageView = factory.createView() as! UIImageView
342
+
343
+ factory.updateProp(view: imageView, key: "resizeMode", value: "contain")
344
+ XCTAssertEqual(imageView.contentMode, .scaleAspectFit, "resizeMode 'contain' should set .scaleAspectFit")
345
+
346
+ factory.updateProp(view: imageView, key: "resizeMode", value: "stretch")
347
+ XCTAssertEqual(imageView.contentMode, .scaleToFill, "resizeMode 'stretch' should set .scaleToFill")
348
+
349
+ factory.updateProp(view: imageView, key: "resizeMode", value: "center")
350
+ XCTAssertEqual(imageView.contentMode, .center, "resizeMode 'center' should set .center")
351
+ }
352
+
353
+ func testVImageFactoryClearsImageOnNilSource() {
354
+ let factory = VImageFactory()
355
+ let imageView = factory.createView() as! UIImageView
356
+
357
+ // Set a source with invalid data to test nil handling
358
+ factory.updateProp(view: imageView, key: "source", value: nil)
359
+ XCTAssertNil(imageView.image, "Nil source should clear the image")
360
+ }
361
+
362
+ // MARK: - VScrollViewFactory Tests
363
+
364
+ func testVScrollViewFactoryCreatesUIScrollView() {
365
+ let factory = VScrollViewFactory()
366
+ let view = factory.createView()
367
+ XCTAssertTrue(view is UIScrollView, "VScrollViewFactory should create a UIScrollView")
368
+ }
369
+
370
+ func testVScrollViewFactoryHasContentView() {
371
+ let factory = VScrollViewFactory()
372
+ let scrollView = factory.createView() as! UIScrollView
373
+
374
+ let contentView = VScrollViewFactory.contentView(for: scrollView)
375
+ XCTAssertNotNil(contentView, "VScrollView should have a content view")
376
+ }
377
+
378
+ func testVScrollViewFactoryDefaultProperties() {
379
+ let factory = VScrollViewFactory()
380
+ let scrollView = factory.createView() as! UIScrollView
381
+
382
+ XCTAssertTrue(scrollView.showsVerticalScrollIndicator, "Should show vertical scroll indicator by default")
383
+ XCTAssertFalse(scrollView.showsHorizontalScrollIndicator, "Should hide horizontal scroll indicator by default")
384
+ XCTAssertTrue(scrollView.alwaysBounceVertical, "Should always bounce vertically by default")
385
+ XCTAssertTrue(scrollView.clipsToBounds, "Should clip to bounds by default")
386
+ }
387
+
388
+ func testVScrollViewFactorySetsHorizontal() {
389
+ let factory = VScrollViewFactory()
390
+ let scrollView = factory.createView() as! UIScrollView
391
+
392
+ factory.updateProp(view: scrollView, key: "horizontal", value: true)
393
+ XCTAssertTrue(scrollView.alwaysBounceHorizontal, "horizontal=true should enable horizontal bouncing")
394
+ XCTAssertFalse(scrollView.alwaysBounceVertical, "horizontal=true should disable vertical bouncing")
395
+ }
396
+
397
+ func testVScrollViewFactorySetsScrollEnabled() {
398
+ let factory = VScrollViewFactory()
399
+ let scrollView = factory.createView() as! UIScrollView
400
+
401
+ factory.updateProp(view: scrollView, key: "scrollEnabled", value: false)
402
+ XCTAssertFalse(scrollView.isScrollEnabled, "scrollEnabled=false should disable scrolling")
403
+ }
404
+
405
+ func testVScrollViewFactorySetsBounces() {
406
+ let factory = VScrollViewFactory()
407
+ let scrollView = factory.createView() as! UIScrollView
408
+
409
+ factory.updateProp(view: scrollView, key: "bounces", value: false)
410
+ XCTAssertFalse(scrollView.bounces, "bounces=false should disable bouncing")
411
+ }
412
+
413
+ func testVScrollViewFactorySetsPagingEnabled() {
414
+ let factory = VScrollViewFactory()
415
+ let scrollView = factory.createView() as! UIScrollView
416
+
417
+ factory.updateProp(view: scrollView, key: "pagingEnabled", value: true)
418
+ XCTAssertTrue(scrollView.isPagingEnabled, "pagingEnabled=true should enable paging")
419
+ }
420
+
421
+ // MARK: - VListFactory Tests
422
+
423
+ func testVListFactoryCreatesVListContainerView() {
424
+ let factory = VListFactory()
425
+ let view = factory.createView()
426
+ XCTAssertTrue(view is VListContainerView, "VListFactory should create a VListContainerView")
427
+ }
428
+
429
+ func testVListContainerViewHasTableView() {
430
+ let factory = VListFactory()
431
+ let container = factory.createView() as! VListContainerView
432
+ XCTAssertNotNil(container.tableView, "VListContainerView should have a tableView")
433
+ }
434
+
435
+ func testVListFactorySetsEstimatedItemHeight() {
436
+ let factory = VListFactory()
437
+ let container = factory.createView() as! VListContainerView
438
+
439
+ factory.updateProp(view: container, key: "estimatedItemHeight", value: 80.0)
440
+ XCTAssertEqual(container.estimatedItemHeight, 80.0, accuracy: 0.001, "estimatedItemHeight should be 80")
441
+ }
442
+
443
+ func testVListFactorySetsShowsScrollIndicator() {
444
+ let factory = VListFactory()
445
+ let container = factory.createView() as! VListContainerView
446
+
447
+ factory.updateProp(view: container, key: "showsScrollIndicator", value: false)
448
+ XCTAssertFalse(container.tableView.showsVerticalScrollIndicator, "Should hide scroll indicator")
449
+ }
450
+
451
+ func testVListFactorySetsBounces() {
452
+ let factory = VListFactory()
453
+ let container = factory.createView() as! VListContainerView
454
+
455
+ factory.updateProp(view: container, key: "bounces", value: false)
456
+ XCTAssertFalse(container.tableView.bounces, "Should disable bouncing")
457
+ }
458
+
459
+ func testVListFactoryDefaultItemViewsEmpty() {
460
+ let factory = VListFactory()
461
+ let container = factory.createView() as! VListContainerView
462
+ XCTAssertTrue(container.itemViews.isEmpty, "itemViews should start empty")
463
+ }
464
+
465
+ // MARK: - VSliderFactory Tests
466
+
467
+ func testVSliderFactoryCreatesUISlider() {
468
+ let factory = VSliderFactory()
469
+ let view = factory.createView()
470
+ XCTAssertTrue(view is UISlider, "VSliderFactory should create a UISlider")
471
+ }
472
+
473
+ func testVSliderFactoryDefaultRange() {
474
+ let factory = VSliderFactory()
475
+ let slider = factory.createView() as! UISlider
476
+ XCTAssertEqual(slider.minimumValue, 0, "Default minimum should be 0")
477
+ XCTAssertEqual(slider.maximumValue, 1, "Default maximum should be 1")
478
+ }
479
+
480
+ func testVSliderFactorySetsValue() {
481
+ let factory = VSliderFactory()
482
+ let slider = factory.createView() as! UISlider
483
+
484
+ factory.updateProp(view: slider, key: "value", value: 0.5)
485
+ XCTAssertEqual(slider.value, 0.5, accuracy: 0.001, "value prop should set slider value")
486
+ }
487
+
488
+ func testVSliderFactorySetsMinMax() {
489
+ let factory = VSliderFactory()
490
+ let slider = factory.createView() as! UISlider
491
+
492
+ factory.updateProp(view: slider, key: "minimumValue", value: 10.0)
493
+ XCTAssertEqual(slider.minimumValue, 10.0, accuracy: 0.001, "minimumValue should be set")
494
+
495
+ factory.updateProp(view: slider, key: "maximumValue", value: 100.0)
496
+ XCTAssertEqual(slider.maximumValue, 100.0, accuracy: 0.001, "maximumValue should be set")
497
+ }
498
+
499
+ func testVSliderFactoryHandlesChangeEvent() {
500
+ let factory = VSliderFactory()
501
+ let slider = factory.createView() as! UISlider
502
+
503
+ factory.addEventListener(view: slider, event: "change") { _ in }
504
+ // Verify the event was wired (target/action stored)
505
+ }
506
+
507
+ func testVSliderFactoryRemovesChangeEvent() {
508
+ let factory = VSliderFactory()
509
+ let slider = factory.createView() as! UISlider
510
+
511
+ factory.addEventListener(view: slider, event: "change") { _ in }
512
+ factory.removeEventListener(view: slider, event: "change")
513
+ // Should not crash
514
+ }
515
+
516
+ // MARK: - VModalFactory Tests
517
+
518
+ func testVModalFactoryCreatesPlaceholderView() {
519
+ let factory = VModalFactory()
520
+ let view = factory.createView()
521
+ XCTAssertNotNil(view, "VModalFactory should create a view")
522
+ XCTAssertTrue(view.isHidden, "Modal placeholder should be hidden")
523
+ }
524
+
525
+ func testVModalFactoryPlaceholderIsZeroSize() {
526
+ let factory = VModalFactory()
527
+ let view = factory.createView()
528
+ // The placeholder has flex width(0) height(0) — verify it's created
529
+ XCTAssertNotNil(view, "Placeholder should exist")
530
+ }
531
+
532
+ // MARK: - VActivityIndicatorFactory Tests
533
+
534
+ func testVActivityIndicatorFactoryCreatesUIActivityIndicatorView() {
535
+ let factory = VActivityIndicatorFactory()
536
+ let view = factory.createView()
537
+ XCTAssertTrue(view is UIActivityIndicatorView,
538
+ "VActivityIndicatorFactory should create a UIActivityIndicatorView")
539
+ }
540
+
541
+ func testVActivityIndicatorFactoryStartsAnimating() {
542
+ let factory = VActivityIndicatorFactory()
543
+ let indicator = factory.createView() as! UIActivityIndicatorView
544
+ XCTAssertTrue(indicator.isAnimating, "Activity indicator should start animating by default")
545
+ }
546
+
547
+ func testVActivityIndicatorFactoryHidesWhenStopped() {
548
+ let factory = VActivityIndicatorFactory()
549
+ let indicator = factory.createView() as! UIActivityIndicatorView
550
+ XCTAssertTrue(indicator.hidesWhenStopped, "Should hide when stopped by default")
551
+ }
552
+
553
+ func testVActivityIndicatorFactoryStopsAnimating() {
554
+ let factory = VActivityIndicatorFactory()
555
+ let indicator = factory.createView() as! UIActivityIndicatorView
556
+
557
+ factory.updateProp(view: indicator, key: "animating", value: false)
558
+ XCTAssertFalse(indicator.isAnimating, "animating=false should stop animation")
559
+ }
560
+
561
+ func testVActivityIndicatorFactorySetsColor() {
562
+ let factory = VActivityIndicatorFactory()
563
+ let indicator = factory.createView() as! UIActivityIndicatorView
564
+
565
+ factory.updateProp(view: indicator, key: "color", value: "#ff0000")
566
+ XCTAssertNotNil(indicator.color, "color prop should set indicator color")
567
+ }
568
+
569
+ func testVActivityIndicatorFactorySetsSize() {
570
+ let factory = VActivityIndicatorFactory()
571
+ let indicator = factory.createView() as! UIActivityIndicatorView
572
+
573
+ factory.updateProp(view: indicator, key: "size", value: "large")
574
+ XCTAssertEqual(indicator.style, .large, "size 'large' should set .large style")
575
+ }
576
+
577
+ func testVActivityIndicatorFactorySetsHidesWhenStoppedProp() {
578
+ let factory = VActivityIndicatorFactory()
579
+ let indicator = factory.createView() as! UIActivityIndicatorView
580
+
581
+ factory.updateProp(view: indicator, key: "hidesWhenStopped", value: false)
582
+ XCTAssertFalse(indicator.hidesWhenStopped, "hidesWhenStopped=false should be set")
583
+ }
584
+ }
585
+ #endif