forked from swiftwasm/JavaScriptKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExportAPITests.swift
More file actions
466 lines (389 loc) · 11.4 KB
/
ExportAPITests.swift
File metadata and controls
466 lines (389 loc) · 11.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
import XCTest
import JavaScriptKit
import JavaScriptEventLoop
@_extern(wasm, module: "BridgeJSRuntimeTests", name: "runJsWorks")
@_extern(c)
func runJsWorks() -> Void
@JS func roundTripVoid() -> Void {
return
}
@JS func roundTripInt(v: Int) -> Int {
return v
}
@JS func roundTripFloat(v: Float) -> Float {
return v
}
@JS func roundTripDouble(v: Double) -> Double {
return v
}
@JS func roundTripBool(v: Bool) -> Bool {
return v
}
@JS func roundTripString(v: String) -> String {
return v
}
@JS func roundTripSwiftHeapObject(v: Greeter) -> Greeter {
return v
}
@JS func roundTripJSObject(v: JSObject) -> JSObject {
return v
}
struct TestError: Error {
let message: String
}
@JS func throwsSwiftError(shouldThrow: Bool) throws(JSException) -> Void {
if shouldThrow {
throw JSException(JSError(message: "TestError").jsValue)
}
}
@JS func throwsWithIntResult() throws(JSException) -> Int { return 1 }
@JS func throwsWithStringResult() throws(JSException) -> String { return "Ok" }
@JS func throwsWithBoolResult() throws(JSException) -> Bool { return true }
@JS func throwsWithFloatResult() throws(JSException) -> Float { return 1.0 }
@JS func throwsWithDoubleResult() throws(JSException) -> Double { return 1.0 }
@JS func throwsWithSwiftHeapObjectResult() throws(JSException) -> Greeter { return Greeter(name: "Test") }
@JS func throwsWithJSObjectResult() throws(JSException) -> JSObject { return JSObject() }
@JS func asyncRoundTripVoid() async -> Void { return }
@JS func asyncRoundTripInt(v: Int) async -> Int { return v }
@JS func asyncRoundTripFloat(v: Float) async -> Float { return v }
@JS func asyncRoundTripDouble(v: Double) async -> Double { return v }
@JS func asyncRoundTripBool(v: Bool) async -> Bool { return v }
@JS func asyncRoundTripString(v: String) async -> String { return v }
@JS func asyncRoundTripSwiftHeapObject(v: Greeter) async -> Greeter { return v }
@JS func asyncRoundTripJSObject(v: JSObject) async -> JSObject { return v }
@JS class Greeter {
@JS var name: String
@JS let prefix: String = "Hello"
nonisolated(unsafe) static var onDeinit: () -> Void = {}
@JS init(name: String) {
self.name = name
}
@JS func greet() -> String {
return "\(prefix), \(name)!"
}
@JS func changeName(name: String) {
self.name = name
}
deinit {
Self.onDeinit()
}
}
@JS func takeGreeter(g: Greeter, name: String) {
g.changeName(name: name)
}
// Test class without @JS init constructor
@JS class Calculator {
nonisolated(unsafe) static var onDeinit: () -> Void = {}
@JS func square(value: Int) -> Int {
return value * value
}
@JS func add(a: Int, b: Int) -> Int {
return a + b
}
deinit {
Self.onDeinit()
}
}
@JS internal class InternalGreeter {}
@JS public class PublicGreeter {}
@JS package class PackageGreeter {}
@JS func createCalculator() -> Calculator {
return Calculator()
}
@JS func useCalculator(calc: Calculator, x: Int, y: Int) -> Int {
return calc.add(a: calc.square(value: x), b: y)
}
@JS func testGreeterToJSValue() -> JSObject {
let greeter = Greeter(name: "Test")
return greeter.jsValue.object!
}
@JS func testCalculatorToJSValue() -> JSObject {
let calc = Calculator()
return calc.jsValue.object!
}
@JS func testSwiftClassAsJSValue(greeter: Greeter) -> JSObject {
return greeter.jsValue.object!
}
// MARK: - Enum Tests
@JS enum Direction {
case north
case south
case east
case west
}
@JS enum Status {
case loading
case success
case error
}
@JS enum Theme: String {
case light = "light"
case dark = "dark"
case auto = "auto"
}
@JS enum HttpStatus: Int {
case ok = 200
case notFound = 404
case serverError = 500
}
@JS(enumStyle: .tsEnum) enum TSDirection {
case north
case south
case east
case west
}
@JS(enumStyle: .tsEnum) enum TSTheme: String {
case light = "light"
case dark = "dark"
case auto = "auto"
}
@JS func setDirection(_ direction: Direction) -> Direction {
return direction
}
@JS func getDirection() -> Direction {
return .north
}
@JS func processDirection(_ input: Direction) -> Status {
switch input {
case .north, .south: return .success
case .east, .west: return .loading
}
}
@JS func setTheme(_ theme: Theme) -> Theme {
return theme
}
@JS func getTheme() -> Theme {
return .light
}
@JS func setHttpStatus(_ status: HttpStatus) -> HttpStatus {
return status
}
@JS func getHttpStatus() -> HttpStatus {
return .ok
}
@JS func processTheme(_ theme: Theme) -> HttpStatus {
switch theme {
case .light: return .ok
case .dark: return .notFound
case .auto: return .serverError
}
}
@JS func setTSDirection(_ direction: TSDirection) -> TSDirection {
return direction
}
@JS func getTSDirection() -> TSDirection {
return .north
}
@JS func setTSTheme(_ theme: TSTheme) -> TSTheme {
return theme
}
@JS func getTSTheme() -> TSTheme {
return .light
}
// MARK: - Namespace Enums
@JS enum Utils {
@JS class Converter {
@JS init() {}
@JS func toString(value: Int) -> String {
return String(value)
}
}
}
@JS enum Networking {
@JS enum API {
@JS enum Method {
case get
case post
case put
case delete
}
@JS class HTTPServer {
@JS init() {}
@JS func call(_ method: Method) {}
}
}
}
@JS enum Configuration {
@JS enum LogLevel: String {
case debug = "debug"
case info = "info"
case warning = "warning"
case error = "error"
}
@JS enum Port: Int {
case http = 80
case https = 443
case development = 3000
}
}
@JS(namespace: "Networking.APIV2")
enum Internal {
@JS enum SupportedMethod {
case get
case post
}
@JS class TestServer {
@JS init() {}
@JS func call(_ method: SupportedMethod) {}
}
}
@JS func echoNetworkingAPIMethod(_ method: Networking.API.Method) -> Networking.API.Method {
return method
}
@JS func echoConfigurationLogLevel(_ level: Configuration.LogLevel) -> Configuration.LogLevel {
return level
}
@JS func echoConfigurationPort(_ port: Configuration.Port) -> Configuration.Port {
return port
}
@JS func processConfigurationLogLevel(_ level: Configuration.LogLevel) -> Configuration.Port {
switch level {
case .debug: return .development
case .info: return .http
case .warning: return .https
case .error: return .development
}
}
@JS func echoInternalSupportedMethod(_ method: Internal.SupportedMethod) -> Internal.SupportedMethod {
return method
}
// MARK: - Property Tests
// Simple class for SwiftHeapObject property testing
@JS class SimplePropertyHolder {
@JS var value: Int
@JS init(value: Int) {
self.value = value
}
}
// Test class for various property types
@JS class PropertyHolder {
// Primitive properties
@JS var intValue: Int
@JS var floatValue: Float
@JS var doubleValue: Double
@JS var boolValue: Bool
@JS var stringValue: String
// Readonly primitive properties
@JS let readonlyInt: Int = 42
@JS let readonlyFloat: Float = 3.14
@JS let readonlyDouble: Double = 2.718281828
@JS let readonlyBool: Bool = true
@JS let readonlyString: String = "constant"
// JSObject property
@JS var jsObject: JSObject
// SwiftHeapObject property
@JS var sibling: SimplePropertyHolder
// Lazy stored property
@JS lazy var lazyValue: String = "computed lazily"
// Computed property with getter only (readonly)
@JS var computedReadonly: Int {
return intValue * 2
}
// Computed property with getter and setter
@JS var computedReadWrite: String {
get {
return "Value: \(intValue)"
}
set {
// Parse the number from "Value: X" format
if let range = newValue.range(of: "Value: "),
let number = Int(String(newValue[range.upperBound...]))
{
intValue = number
}
}
}
// Property with property observers
@JS var observedProperty: Int {
willSet {
Self.willSetCallCount += 1
Self.lastWillSetOldValue = self.observedProperty
Self.lastWillSetNewValue = newValue
}
didSet {
Self.didSetCallCount += 1
Self.lastDidSetOldValue = oldValue
Self.lastDidSetNewValue = self.observedProperty
}
}
// Static properties to track observer calls
nonisolated(unsafe) static var willSetCallCount: Int = 0
nonisolated(unsafe) static var didSetCallCount: Int = 0
nonisolated(unsafe) static var lastWillSetOldValue: Int = 0
nonisolated(unsafe) static var lastWillSetNewValue: Int = 0
nonisolated(unsafe) static var lastDidSetOldValue: Int = 0
nonisolated(unsafe) static var lastDidSetNewValue: Int = 0
@JS init(
intValue: Int,
floatValue: Float,
doubleValue: Double,
boolValue: Bool,
stringValue: String,
jsObject: JSObject,
sibling: SimplePropertyHolder
) {
self.intValue = intValue
self.floatValue = floatValue
self.doubleValue = doubleValue
self.boolValue = boolValue
self.stringValue = stringValue
self.jsObject = jsObject
self.sibling = sibling
self.observedProperty = intValue // Initialize observed property
}
@JS func getAllValues() -> String {
return "int:\(intValue),float:\(floatValue),double:\(doubleValue),bool:\(boolValue),string:\(stringValue)"
}
}
@JS func createPropertyHolder(
intValue: Int,
floatValue: Float,
doubleValue: Double,
boolValue: Bool,
stringValue: String,
jsObject: JSObject
) -> PropertyHolder {
let sibling = SimplePropertyHolder(value: 999)
return PropertyHolder(
intValue: intValue,
floatValue: floatValue,
doubleValue: doubleValue,
boolValue: boolValue,
stringValue: stringValue,
jsObject: jsObject,
sibling: sibling
)
}
@JS func testPropertyHolder(holder: PropertyHolder) -> String {
return holder.getAllValues()
}
@JS func resetObserverCounts() {
PropertyHolder.willSetCallCount = 0
PropertyHolder.didSetCallCount = 0
PropertyHolder.lastWillSetOldValue = 0
PropertyHolder.lastWillSetNewValue = 0
PropertyHolder.lastDidSetOldValue = 0
PropertyHolder.lastDidSetNewValue = 0
}
@JS func getObserverStats() -> String {
return
"willSet:\(PropertyHolder.willSetCallCount),didSet:\(PropertyHolder.didSetCallCount),willSetOld:\(PropertyHolder.lastWillSetOldValue),willSetNew:\(PropertyHolder.lastWillSetNewValue),didSetOld:\(PropertyHolder.lastDidSetOldValue),didSetNew:\(PropertyHolder.lastDidSetNewValue)"
}
class ExportAPITests: XCTestCase {
func testAll() {
var hasDeinitGreeter = false
var hasDeinitCalculator = false
Greeter.onDeinit = {
hasDeinitGreeter = true
}
Calculator.onDeinit = {
hasDeinitCalculator = true
}
runJsWorks()
XCTAssertTrue(hasDeinitGreeter, "Greeter (with @JS init) should have been deinitialized")
XCTAssertTrue(hasDeinitCalculator, "Calculator (without @JS init) should have been deinitialized")
}
func testAllAsync() async throws {
_ = try await runAsyncWorks().value()
}
}