@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.
- package/dist/cli.js +329 -15
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Bridge/NativeBridge.kt +118 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Components/Factories/VViewFactory.kt +178 -1
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/GeneratedModuleRegistry.kt +28 -0
- package/native/android/VueNativeCore/src/main/kotlin/com/vuenative/core/Modules/NativeModuleRegistry.kt +3 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ComponentFactoryTest.kt +674 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/ErrorOverlayViewTest.kt +183 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/EventThrottleTest.kt +203 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/HotReloadManagerTest.kt +162 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/JSPolyfillsTest.kt +153 -0
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeBridgeTest.kt +6 -3
- package/native/android/VueNativeCore/src/test/kotlin/com/vuenative/core/NativeModuleTest.kt +475 -0
- package/native/android/gradle.properties +1 -0
- package/native/android/gradlew +1 -1
- package/native/ios/VueNativeCore/Package.swift +1 -1
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/EventThrottle.swift +1 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Bridge/NativeBridge.swift +143 -5
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VTextFactory.swift +43 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Components/Factories/VViewFactory.swift +116 -4
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Helpers/GestureWrapper.swift +100 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/GeneratedModuleRegistry.swift +28 -0
- package/native/ios/VueNativeCore/Sources/VueNativeCore/Modules/NativeModuleRegistry.swift +3 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/CertificatePinningTests.swift +190 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/ComponentFactoryTests.swift +585 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/EventThrottleTests.swift +161 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/HotReloadManagerTests.swift +88 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/JSPolyfillsTests.swift +319 -0
- package/native/ios/VueNativeCore/Tests/VueNativeCoreTests/NativeModuleTests.swift +400 -0
- package/native/macos/VueNativeMacOS/Package.swift +34 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/ErrorOverlayView.swift +112 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/EventThrottle.swift +58 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/HotReloadManager.swift +153 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSPolyfills.swift +696 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/JSRuntime.swift +347 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/NativeBridge.swift +877 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Bridge/VueNativeWindowController.swift +125 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/ComponentRegistry.swift +209 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActionSheetFactory.swift +155 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VActivityIndicatorFactory.swift +85 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VAlertDialogFactory.swift +132 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VButtonFactory.swift +83 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VCheckboxFactory.swift +108 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VDropdownFactory.swift +155 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VImageFactory.swift +270 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VInputFactory.swift +257 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VKeyboardAvoidingFactory.swift +22 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VListFactory.swift +324 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VModalFactory.swift +231 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VOutlineViewFactory.swift +276 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPickerFactory.swift +134 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VPressableFactory.swift +120 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VProgressBarFactory.swift +71 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRadioFactory.swift +193 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VRefreshControlFactory.swift +25 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSafeAreaFactory.swift +46 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VScrollViewFactory.swift +190 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSectionListFactory.swift +374 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSegmentedControlFactory.swift +125 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSliderFactory.swift +131 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSplitViewFactory.swift +215 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VStatusBarFactory.swift +25 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VSwitchFactory.swift +92 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VTextFactory.swift +336 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VToolbarFactory.swift +212 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VVideoFactory.swift +245 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VViewFactory.swift +314 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/Factories/VWebViewFactory.swift +162 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Components/NativeComponentFactory.swift +54 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/ClickableView.swift +100 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/Extensions.swift +23 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/GestureWrapper.swift +183 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Helpers/NSColor+Hex.swift +78 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/FlippedView.swift +19 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Layout/LayoutNode.swift +493 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AnimationModule.swift +354 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/AppStateModule.swift +62 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/BiometryModule.swift +60 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/CameraModule.swift +167 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ClipboardModule.swift +34 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DeviceInfoModule.swift +49 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/DragDropModule.swift +50 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/FileDialogModule.swift +86 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/HapticsModule.swift +42 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/KeyboardModule.swift +28 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/LinkingModule.swift +49 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/MenuModule.swift +95 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NativeModuleRegistry.swift +63 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/NotificationsModule.swift +112 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/PermissionsModule.swift +149 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/ShareModule.swift +37 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Modules/WindowModule.swift +71 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Resources/vue-native-placeholder.js +2 -0
- package/native/macos/VueNativeMacOS/Sources/VueNativeMacOS/Styling/StyleEngine.swift +885 -0
- package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/ComponentFactoryTests.swift +80 -0
- package/native/macos/VueNativeMacOS/Tests/VueNativeMacOSTests/VueNativeMacOSTests.swift +149 -0
- package/native/shared/VueNativeShared/AGENTS.md +129 -0
- package/native/shared/VueNativeShared/Package.swift +14 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/CertificatePinning.swift +134 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/EventThrottle.swift +78 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/HotReloadManager.swift +162 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/JSRuntime.swift +412 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AsyncStorageModule.swift +68 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/AudioModule.swift +359 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/DatabaseModule.swift +259 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/FileSystemModule.swift +233 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/GeolocationModule.swift +156 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/NetworkModule.swift +59 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/PerformanceModule.swift +113 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/SecureStorageModule.swift +119 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/Modules/WebSocketModule.swift +212 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeEventDispatcher.swift +6 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModule.swift +26 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/NativeModuleRegistry.swift +37 -0
- package/native/shared/VueNativeShared/Sources/VueNativeShared/SharedJSPolyfills.swift +673 -0
- package/native/shared/VueNativeShared/Tests/VueNativeSharedTests/VueNativeSharedTests.swift +44 -0
- package/package.json +8 -2
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#if canImport(UIKit)
|
|
2
|
+
import XCTest
|
|
3
|
+
import UIKit
|
|
4
|
+
@testable import VueNativeCore
|
|
5
|
+
|
|
6
|
+
@MainActor
|
|
7
|
+
final class EventThrottleTests: XCTestCase {
|
|
8
|
+
|
|
9
|
+
// MARK: - Initialization
|
|
10
|
+
|
|
11
|
+
func testInitializationWithDefaultInterval() {
|
|
12
|
+
let throttle = EventThrottle { _ in }
|
|
13
|
+
XCTAssertEqual(throttle.interval, 0.016, accuracy: 0.001, "Default interval should be ~16ms")
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
func testInitializationWithCustomInterval() {
|
|
17
|
+
let throttle = EventThrottle(interval: 0.1) { _ in }
|
|
18
|
+
XCTAssertEqual(throttle.interval, 0.1, accuracy: 0.001, "Custom interval should be 0.1s")
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// MARK: - First Event Fires Immediately
|
|
22
|
+
|
|
23
|
+
func testFirstEventFiresImmediately() {
|
|
24
|
+
var firedPayload: Any?
|
|
25
|
+
var fireCount = 0
|
|
26
|
+
|
|
27
|
+
let throttle = EventThrottle(interval: 1.0) { payload in
|
|
28
|
+
firedPayload = payload
|
|
29
|
+
fireCount += 1
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
throttle.fire("hello")
|
|
33
|
+
|
|
34
|
+
XCTAssertEqual(fireCount, 1, "First event should fire immediately")
|
|
35
|
+
XCTAssertEqual(firedPayload as? String, "hello", "Payload should be passed through")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// MARK: - Events Within Throttle Window Are Suppressed
|
|
39
|
+
|
|
40
|
+
func testEventsWithinWindowAreSuppressed() {
|
|
41
|
+
var fireCount = 0
|
|
42
|
+
|
|
43
|
+
let throttle = EventThrottle(interval: 10.0) { _ in
|
|
44
|
+
fireCount += 1
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// First fire — immediate
|
|
48
|
+
throttle.fire("first")
|
|
49
|
+
XCTAssertEqual(fireCount, 1, "First event should fire immediately")
|
|
50
|
+
|
|
51
|
+
// Rapid fires within the throttle window — should NOT fire immediately
|
|
52
|
+
throttle.fire("second")
|
|
53
|
+
throttle.fire("third")
|
|
54
|
+
|
|
55
|
+
// Only the first call fires synchronously; the rest schedule trailing calls
|
|
56
|
+
XCTAssertEqual(fireCount, 1, "Subsequent events within the throttle window should be suppressed synchronously")
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// MARK: - Events After Window Expires Fire
|
|
60
|
+
|
|
61
|
+
func testEventsAfterWindowExpireFire() {
|
|
62
|
+
let expectation = self.expectation(description: "Throttled event fires after window")
|
|
63
|
+
var fireCount = 0
|
|
64
|
+
|
|
65
|
+
let throttle = EventThrottle(interval: 0.05) { _ in
|
|
66
|
+
fireCount += 1
|
|
67
|
+
if fireCount == 2 {
|
|
68
|
+
expectation.fulfill()
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// First fire — immediate
|
|
73
|
+
throttle.fire("first")
|
|
74
|
+
XCTAssertEqual(fireCount, 1, "First event should fire immediately")
|
|
75
|
+
|
|
76
|
+
// Wait for the throttle window to expire, then fire again
|
|
77
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
|
78
|
+
throttle.fire("second")
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
waitForExpectations(timeout: 1.0)
|
|
82
|
+
XCTAssertEqual(fireCount, 2, "Event after window expiry should fire")
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// MARK: - Trailing Call Delivers Latest Payload
|
|
86
|
+
|
|
87
|
+
func testTrailingCallDeliversLatestPayload() {
|
|
88
|
+
let expectation = self.expectation(description: "Trailing call fires")
|
|
89
|
+
var payloads: [String] = []
|
|
90
|
+
|
|
91
|
+
let throttle = EventThrottle(interval: 0.05) { payload in
|
|
92
|
+
if let str = payload as? String {
|
|
93
|
+
payloads.append(str)
|
|
94
|
+
}
|
|
95
|
+
if payloads.count == 2 {
|
|
96
|
+
expectation.fulfill()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// First fire — immediate
|
|
101
|
+
throttle.fire("first")
|
|
102
|
+
|
|
103
|
+
// Rapid fires — only the latest should be delivered as trailing
|
|
104
|
+
throttle.fire("second")
|
|
105
|
+
throttle.fire("third")
|
|
106
|
+
throttle.fire("fourth")
|
|
107
|
+
|
|
108
|
+
waitForExpectations(timeout: 1.0)
|
|
109
|
+
|
|
110
|
+
XCTAssertEqual(payloads.first, "first", "First payload should be 'first'")
|
|
111
|
+
XCTAssertEqual(payloads.last, "fourth", "Trailing call should deliver the latest payload")
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// MARK: - Multiple Throttle Instances Are Independent
|
|
115
|
+
|
|
116
|
+
func testMultipleThrottleInstancesAreIndependent() {
|
|
117
|
+
var countA = 0
|
|
118
|
+
var countB = 0
|
|
119
|
+
|
|
120
|
+
let throttleA = EventThrottle(interval: 10.0) { _ in countA += 1 }
|
|
121
|
+
let throttleB = EventThrottle(interval: 10.0) { _ in countB += 1 }
|
|
122
|
+
|
|
123
|
+
throttleA.fire(nil)
|
|
124
|
+
throttleB.fire(nil)
|
|
125
|
+
|
|
126
|
+
XCTAssertEqual(countA, 1, "Throttle A should fire independently")
|
|
127
|
+
XCTAssertEqual(countB, 1, "Throttle B should fire independently")
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// MARK: - Nil Payload Is Supported
|
|
131
|
+
|
|
132
|
+
func testNilPayloadIsSupported() {
|
|
133
|
+
var fired = false
|
|
134
|
+
|
|
135
|
+
let throttle = EventThrottle(interval: 0.016) { payload in
|
|
136
|
+
XCTAssertNil(payload, "Nil payload should be passed through")
|
|
137
|
+
fired = true
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
throttle.fire(nil)
|
|
141
|
+
XCTAssertTrue(fired, "Handler should fire with nil payload")
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// MARK: - Dictionary Payload Is Supported
|
|
145
|
+
|
|
146
|
+
func testDictionaryPayloadIsSupported() {
|
|
147
|
+
var receivedPayload: [String: Any]?
|
|
148
|
+
|
|
149
|
+
let throttle = EventThrottle(interval: 0.016) { payload in
|
|
150
|
+
receivedPayload = payload as? [String: Any]
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let payload: [String: Any] = ["x": 10.0, "y": 20.0]
|
|
154
|
+
throttle.fire(payload)
|
|
155
|
+
|
|
156
|
+
XCTAssertNotNil(receivedPayload, "Dictionary payload should be received")
|
|
157
|
+
XCTAssertEqual(receivedPayload?["x"] as? Double, 10.0, "x should be 10.0")
|
|
158
|
+
XCTAssertEqual(receivedPayload?["y"] as? Double, 20.0, "y should be 20.0")
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
#endif
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#if canImport(UIKit)
|
|
2
|
+
import XCTest
|
|
3
|
+
import UIKit
|
|
4
|
+
@testable import VueNativeCore
|
|
5
|
+
|
|
6
|
+
@MainActor
|
|
7
|
+
final class HotReloadManagerTests: XCTestCase {
|
|
8
|
+
|
|
9
|
+
// MARK: - Properties
|
|
10
|
+
|
|
11
|
+
private var manager: HotReloadManager!
|
|
12
|
+
|
|
13
|
+
// MARK: - Setup / Teardown
|
|
14
|
+
|
|
15
|
+
override func setUp() {
|
|
16
|
+
super.setUp()
|
|
17
|
+
manager = HotReloadManager.shared
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
override func tearDown() {
|
|
21
|
+
manager.disconnect()
|
|
22
|
+
manager = nil
|
|
23
|
+
super.tearDown()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// MARK: - Singleton Tests
|
|
27
|
+
|
|
28
|
+
func testSharedInstanceIsSingleton() {
|
|
29
|
+
let instance1 = HotReloadManager.shared
|
|
30
|
+
let instance2 = HotReloadManager.shared
|
|
31
|
+
XCTAssertTrue(instance1 === instance2, "HotReloadManager.shared should always return the same instance")
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// MARK: - Initialization Tests
|
|
35
|
+
|
|
36
|
+
func testInitializationWithURL() {
|
|
37
|
+
let url = URL(string: "ws://localhost:8174")!
|
|
38
|
+
// connect should not crash
|
|
39
|
+
manager.connect(to: url)
|
|
40
|
+
// Just verify it doesn't crash — we can't easily test the WebSocket connection
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// MARK: - Disconnect Tests
|
|
44
|
+
|
|
45
|
+
func testDisconnectDoesNotCrash() {
|
|
46
|
+
// Disconnect without connecting first — should be safe
|
|
47
|
+
manager.disconnect()
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func testDisconnectAfterConnectDoesNotCrash() {
|
|
51
|
+
let url = URL(string: "ws://localhost:9999")!
|
|
52
|
+
manager.connect(to: url)
|
|
53
|
+
manager.disconnect()
|
|
54
|
+
// Should not crash
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// MARK: - Multiple Connect Calls
|
|
58
|
+
|
|
59
|
+
func testMultipleConnectCallsDoNotCrash() {
|
|
60
|
+
let url1 = URL(string: "ws://localhost:8174")!
|
|
61
|
+
let url2 = URL(string: "ws://localhost:8175")!
|
|
62
|
+
|
|
63
|
+
manager.connect(to: url1)
|
|
64
|
+
manager.connect(to: url2)
|
|
65
|
+
manager.disconnect()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// MARK: - URLSessionWebSocketDelegate Conformance
|
|
69
|
+
|
|
70
|
+
func testConformsToURLSessionWebSocketDelegate() {
|
|
71
|
+
// Verify HotReloadManager conforms to the delegate protocol
|
|
72
|
+
XCTAssertTrue(manager is URLSessionWebSocketDelegate,
|
|
73
|
+
"HotReloadManager should conform to URLSessionWebSocketDelegate")
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// MARK: - Connect/Disconnect Cycle
|
|
77
|
+
|
|
78
|
+
func testConnectDisconnectCycleDoesNotCrash() {
|
|
79
|
+
let url = URL(string: "ws://localhost:8174")!
|
|
80
|
+
|
|
81
|
+
for _ in 0..<5 {
|
|
82
|
+
manager.connect(to: url)
|
|
83
|
+
manager.disconnect()
|
|
84
|
+
}
|
|
85
|
+
// Multiple cycles should not crash
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
#endif
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
#if canImport(UIKit)
|
|
2
|
+
import XCTest
|
|
3
|
+
import JavaScriptCore
|
|
4
|
+
import UIKit
|
|
5
|
+
@testable import VueNativeCore
|
|
6
|
+
|
|
7
|
+
@MainActor
|
|
8
|
+
final class JSPolyfillsTests: XCTestCase {
|
|
9
|
+
|
|
10
|
+
// MARK: - Properties
|
|
11
|
+
|
|
12
|
+
private var runtime: JSRuntime!
|
|
13
|
+
|
|
14
|
+
// MARK: - Setup / Teardown
|
|
15
|
+
|
|
16
|
+
override func setUp() {
|
|
17
|
+
super.setUp()
|
|
18
|
+
runtime = JSRuntime.shared
|
|
19
|
+
let initExpectation = expectation(description: "JSRuntime initialized")
|
|
20
|
+
runtime.initialize {
|
|
21
|
+
initExpectation.fulfill()
|
|
22
|
+
}
|
|
23
|
+
waitForExpectations(timeout: 5.0)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override func tearDown() {
|
|
27
|
+
runtime = nil
|
|
28
|
+
super.tearDown()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// MARK: - Helper
|
|
32
|
+
|
|
33
|
+
/// Evaluate a script synchronously and return the result.
|
|
34
|
+
private func evalSync(_ script: String) -> JSValue? {
|
|
35
|
+
let exp = expectation(description: "eval")
|
|
36
|
+
var result: JSValue?
|
|
37
|
+
runtime.evaluateScript(script) { value in
|
|
38
|
+
result = value
|
|
39
|
+
exp.fulfill()
|
|
40
|
+
}
|
|
41
|
+
waitForExpectations(timeout: 5.0)
|
|
42
|
+
return result
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// MARK: - console Tests
|
|
46
|
+
|
|
47
|
+
func testConsoleLogDoesNotCrash() {
|
|
48
|
+
// console.log should not throw or crash
|
|
49
|
+
let result = evalSync("console.log('test message'); true")
|
|
50
|
+
XCTAssertNotNil(result, "console.log should not crash")
|
|
51
|
+
XCTAssertTrue(result?.toBool() == true, "Script should evaluate to true")
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func testConsoleWarnDoesNotCrash() {
|
|
55
|
+
let result = evalSync("console.warn('warning message'); true")
|
|
56
|
+
XCTAssertNotNil(result, "console.warn should not crash")
|
|
57
|
+
XCTAssertTrue(result?.toBool() == true, "Script should evaluate to true")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
func testConsoleErrorDoesNotCrash() {
|
|
61
|
+
let result = evalSync("console.error('error message'); true")
|
|
62
|
+
XCTAssertNotNil(result, "console.error should not crash")
|
|
63
|
+
XCTAssertTrue(result?.toBool() == true, "Script should evaluate to true")
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
func testConsoleDebugDoesNotCrash() {
|
|
67
|
+
let result = evalSync("console.debug('debug message'); true")
|
|
68
|
+
XCTAssertNotNil(result, "console.debug should not crash")
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
func testConsoleInfoDoesNotCrash() {
|
|
72
|
+
let result = evalSync("console.info('info message'); true")
|
|
73
|
+
XCTAssertNotNil(result, "console.info should not crash")
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// MARK: - performance.now() Tests
|
|
77
|
+
|
|
78
|
+
func testPerformanceNowReturnsNumber() {
|
|
79
|
+
let result = evalSync("typeof performance.now()")
|
|
80
|
+
XCTAssertEqual(result?.toString(), "number", "performance.now() should return a number")
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
func testPerformanceNowReturnsPositiveValue() {
|
|
84
|
+
let result = evalSync("performance.now() > 0")
|
|
85
|
+
XCTAssertTrue(result?.toBool() == true, "performance.now() should return a positive number")
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
func testPerformanceNowIncreases() {
|
|
89
|
+
let result = evalSync("""
|
|
90
|
+
var a = performance.now();
|
|
91
|
+
var i = 0; while(i < 10000) { i++; }
|
|
92
|
+
var b = performance.now();
|
|
93
|
+
b >= a;
|
|
94
|
+
""")
|
|
95
|
+
XCTAssertTrue(result?.toBool() == true, "performance.now() should be non-decreasing")
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// MARK: - queueMicrotask Tests
|
|
99
|
+
|
|
100
|
+
func testQueueMicrotaskExists() {
|
|
101
|
+
let result = evalSync("typeof queueMicrotask")
|
|
102
|
+
XCTAssertEqual(result?.toString(), "function", "queueMicrotask should be a function")
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
func testQueueMicrotaskCallbackRuns() {
|
|
106
|
+
let result = evalSync("""
|
|
107
|
+
var microtaskRan = false;
|
|
108
|
+
queueMicrotask(function() { microtaskRan = true; });
|
|
109
|
+
// Microtask should execute after the current task via Promise.resolve().then()
|
|
110
|
+
microtaskRan;
|
|
111
|
+
""")
|
|
112
|
+
// Note: the microtask may not have fired yet since it's Promise-based.
|
|
113
|
+
// But after the evaluateScript drain, it should have run.
|
|
114
|
+
XCTAssertNotNil(result, "queueMicrotask should not crash")
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// MARK: - setTimeout Tests
|
|
118
|
+
|
|
119
|
+
func testSetTimeoutExists() {
|
|
120
|
+
let result = evalSync("typeof setTimeout")
|
|
121
|
+
XCTAssertEqual(result?.toString(), "function", "setTimeout should be a function")
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
func testSetTimeoutReturnsTimerId() {
|
|
125
|
+
let result = evalSync("var id = setTimeout(function(){}, 1000); typeof id !== 'undefined'")
|
|
126
|
+
XCTAssertTrue(result?.toBool() == true, "setTimeout should return a timer ID")
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
func testSetTimeoutFiresCallback() {
|
|
130
|
+
let exp = expectation(description: "setTimeout fires")
|
|
131
|
+
|
|
132
|
+
runtime.evaluateScript("""
|
|
133
|
+
globalThis.__testTimeoutFired = false;
|
|
134
|
+
setTimeout(function() {
|
|
135
|
+
globalThis.__testTimeoutFired = true;
|
|
136
|
+
}, 50);
|
|
137
|
+
""")
|
|
138
|
+
|
|
139
|
+
// Check after a delay — use evaluateScript directly to avoid
|
|
140
|
+
// nested waitForExpectations (XCTest API violation).
|
|
141
|
+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
|
|
142
|
+
self?.runtime.evaluateScript("globalThis.__testTimeoutFired") { value in
|
|
143
|
+
XCTAssertTrue(value?.toBool() == true, "setTimeout callback should have fired")
|
|
144
|
+
exp.fulfill()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
waitForExpectations(timeout: 2.0)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// MARK: - clearTimeout Tests
|
|
152
|
+
|
|
153
|
+
func testClearTimeoutExists() {
|
|
154
|
+
let result = evalSync("typeof clearTimeout")
|
|
155
|
+
XCTAssertEqual(result?.toString(), "function", "clearTimeout should be a function")
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
func testClearTimeoutCancelsPendingCallback() {
|
|
159
|
+
// Verify clearTimeout is callable and does not crash.
|
|
160
|
+
// We cannot reliably test cancellation timing in the simulator
|
|
161
|
+
// because JSC timer scheduling depends on the native run loop.
|
|
162
|
+
let result = evalSync("""
|
|
163
|
+
var __clearTestFired = false;
|
|
164
|
+
var tid = setTimeout(function() { __clearTestFired = true; }, 100000);
|
|
165
|
+
clearTimeout(tid);
|
|
166
|
+
typeof tid !== 'undefined';
|
|
167
|
+
""")
|
|
168
|
+
XCTAssertTrue(result?.toBool() == true, "clearTimeout should accept a timer ID")
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// MARK: - setInterval Tests
|
|
172
|
+
|
|
173
|
+
func testSetIntervalExists() {
|
|
174
|
+
let result = evalSync("typeof setInterval")
|
|
175
|
+
XCTAssertEqual(result?.toString(), "function", "setInterval should be a function")
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
func testSetIntervalReturnsTimerId() {
|
|
179
|
+
let result = evalSync("""
|
|
180
|
+
var iid = setInterval(function(){}, 1000);
|
|
181
|
+
clearInterval(iid);
|
|
182
|
+
typeof iid !== 'undefined';
|
|
183
|
+
""")
|
|
184
|
+
XCTAssertTrue(result?.toBool() == true, "setInterval should return a timer ID")
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// MARK: - clearInterval Tests
|
|
188
|
+
|
|
189
|
+
func testClearIntervalExists() {
|
|
190
|
+
let result = evalSync("typeof clearInterval")
|
|
191
|
+
XCTAssertEqual(result?.toString(), "function", "clearInterval should be a function")
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// MARK: - globalThis Tests
|
|
195
|
+
|
|
196
|
+
func testGlobalThisExists() {
|
|
197
|
+
let result = evalSync("typeof globalThis !== 'undefined'")
|
|
198
|
+
XCTAssertTrue(result?.toBool() == true, "globalThis should be defined")
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
func testGlobalThisIsGlobalObject() {
|
|
202
|
+
let result = evalSync("globalThis === this")
|
|
203
|
+
// In JSC, `this` at the top level is the global object
|
|
204
|
+
XCTAssertNotNil(result, "globalThis should reference the global object")
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// MARK: - requestAnimationFrame Tests
|
|
208
|
+
|
|
209
|
+
func testRequestAnimationFrameExists() {
|
|
210
|
+
let result = evalSync("typeof requestAnimationFrame")
|
|
211
|
+
XCTAssertEqual(result?.toString(), "function", "requestAnimationFrame should be a function")
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
func testCancelAnimationFrameExists() {
|
|
215
|
+
let result = evalSync("typeof cancelAnimationFrame")
|
|
216
|
+
XCTAssertEqual(result?.toString(), "function", "cancelAnimationFrame should be a function")
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// MARK: - fetch Tests
|
|
220
|
+
|
|
221
|
+
func testFetchExists() {
|
|
222
|
+
let result = evalSync("typeof fetch")
|
|
223
|
+
XCTAssertEqual(result?.toString(), "function", "fetch should be a function")
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// MARK: - atob / btoa Tests
|
|
227
|
+
|
|
228
|
+
func testBtoaEncodes() {
|
|
229
|
+
let result = evalSync("btoa('hello')")
|
|
230
|
+
XCTAssertEqual(result?.toString(), "aGVsbG8=", "btoa('hello') should return 'aGVsbG8='")
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
func testAtobDecodes() {
|
|
234
|
+
let result = evalSync("atob('aGVsbG8=')")
|
|
235
|
+
XCTAssertEqual(result?.toString(), "hello", "atob('aGVsbG8=') should return 'hello'")
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
func testBtoaAtobRoundTrip() {
|
|
239
|
+
let result = evalSync("atob(btoa('Vue Native!'))")
|
|
240
|
+
XCTAssertEqual(result?.toString(), "Vue Native!", "btoa/atob round-trip should preserve the string")
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// MARK: - TextEncoder / TextDecoder Tests
|
|
244
|
+
|
|
245
|
+
func testTextEncoderExists() {
|
|
246
|
+
let result = evalSync("typeof TextEncoder")
|
|
247
|
+
XCTAssertEqual(result?.toString(), "function", "TextEncoder should be defined")
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
func testTextDecoderExists() {
|
|
251
|
+
let result = evalSync("typeof TextDecoder")
|
|
252
|
+
XCTAssertEqual(result?.toString(), "function", "TextDecoder should be defined")
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
func testTextEncoderDecoderRoundTrip() {
|
|
256
|
+
let result = evalSync("""
|
|
257
|
+
var enc = new TextEncoder();
|
|
258
|
+
var dec = new TextDecoder();
|
|
259
|
+
var encoded = enc.encode('hello');
|
|
260
|
+
dec.decode(encoded);
|
|
261
|
+
""")
|
|
262
|
+
XCTAssertEqual(result?.toString(), "hello", "TextEncoder/TextDecoder round-trip should work")
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// MARK: - URL Tests
|
|
266
|
+
|
|
267
|
+
func testURLConstructorExists() {
|
|
268
|
+
let result = evalSync("typeof URL")
|
|
269
|
+
XCTAssertEqual(result?.toString(), "function", "URL constructor should be defined")
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
func testURLParsingBasic() {
|
|
273
|
+
let result = evalSync("new URL('https://example.com:8080/path?q=1#hash').hostname")
|
|
274
|
+
XCTAssertEqual(result?.toString(), "example.com", "URL should parse hostname")
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
func testURLSearchParamsExists() {
|
|
278
|
+
let result = evalSync("typeof URLSearchParams")
|
|
279
|
+
XCTAssertEqual(result?.toString(), "function", "URLSearchParams should be defined")
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// MARK: - crypto.getRandomValues Tests
|
|
283
|
+
|
|
284
|
+
func testCryptoGetRandomValuesExists() {
|
|
285
|
+
let result = evalSync("typeof crypto.getRandomValues")
|
|
286
|
+
XCTAssertEqual(result?.toString(), "function", "crypto.getRandomValues should be defined")
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
func testCryptoGetRandomValuesProducesBytes() {
|
|
290
|
+
let result = evalSync("""
|
|
291
|
+
var arr = new Uint8Array(4);
|
|
292
|
+
crypto.getRandomValues(arr);
|
|
293
|
+
arr.length;
|
|
294
|
+
""")
|
|
295
|
+
XCTAssertEqual(result?.toInt32(), 4, "getRandomValues should fill a 4-byte array")
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// MARK: - Bridge Stubs Tests
|
|
299
|
+
|
|
300
|
+
func testBridgeStubsExist() {
|
|
301
|
+
let result = evalSync("""
|
|
302
|
+
typeof __VN_handleGlobalEvent === 'function' &&
|
|
303
|
+
typeof __VN_handleEvent === 'function' &&
|
|
304
|
+
typeof __VN_resolveCallback === 'function';
|
|
305
|
+
""")
|
|
306
|
+
XCTAssertTrue(result?.toBool() == true, "Bridge stubs should be defined as functions")
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
func testBridgeStubsDoNotCrash() {
|
|
310
|
+
let result = evalSync("""
|
|
311
|
+
__VN_handleGlobalEvent();
|
|
312
|
+
__VN_handleEvent();
|
|
313
|
+
__VN_resolveCallback();
|
|
314
|
+
true;
|
|
315
|
+
""")
|
|
316
|
+
XCTAssertTrue(result?.toBool() == true, "Bridge stubs should be callable without crashing")
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
#endif
|