diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 62e2a8ac9..c50de248a 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -41,7 +41,6 @@ jobs:
       - name: Configure Swift SDK
         run: echo "SWIFT_SDK_ID=${{ steps.setup-swiftwasm.outputs.swift-sdk-id }}" >> $GITHUB_ENV
       - run: make bootstrap
-      - run: make test
       - run: make unittest
         # Skip unit tests with uwasi because its proc_exit throws
         # unhandled promise rejection.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 2526556c6..38454374a 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -58,14 +58,10 @@ Thank you for considering contributing to JavaScriptKit! We welcome contribution
    ```
 
 ### Running Tests
-- Run unit tests:
-  ```bash
-  make unittest SWIFT_SDK_ID=wasm32-unknown-wasi
-  ```
-- Run integration tests:
-  ```bash
-  make test SWIFT_SDK_ID=wasm32-unknown-wasi
-  ```
+
+```bash
+make unittest SWIFT_SDK_ID=wasm32-unknown-wasi
+```
 
 ### Editing `./Runtime` directory
 
diff --git a/IntegrationTests/TestSuites/Package.swift b/IntegrationTests/TestSuites/Package.swift
index 95b47f94c..3d583d082 100644
--- a/IntegrationTests/TestSuites/Package.swift
+++ b/IntegrationTests/TestSuites/Package.swift
@@ -11,12 +11,6 @@ let package = Package(
         .macOS("12.0"),
     ],
     products: [
-        .executable(
-            name: "PrimaryTests", targets: ["PrimaryTests"]
-        ),
-        .executable(
-            name: "ConcurrencyTests", targets: ["ConcurrencyTests"]
-        ),
         .executable(
             name: "BenchmarkTests", targets: ["BenchmarkTests"]
         ),
@@ -24,17 +18,6 @@ let package = Package(
     dependencies: [.package(name: "JavaScriptKit", path: "../../")],
     targets: [
         .target(name: "CHelpers"),
-        .executableTarget(name: "PrimaryTests", dependencies: [
-            .product(name: "JavaScriptBigIntSupport", package: "JavaScriptKit"),
-            "JavaScriptKit",
-            "CHelpers",
-        ]),
-        .executableTarget(
-            name: "ConcurrencyTests",
-            dependencies: [
-                .product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
-            ]
-        ),
         .executableTarget(name: "BenchmarkTests", dependencies: ["JavaScriptKit", "CHelpers"]),
     ]
 )
diff --git a/IntegrationTests/TestSuites/Sources/ConcurrencyTests/UnitTestUtils.swift b/IntegrationTests/TestSuites/Sources/ConcurrencyTests/UnitTestUtils.swift
deleted file mode 100644
index acd81e6d9..000000000
--- a/IntegrationTests/TestSuites/Sources/ConcurrencyTests/UnitTestUtils.swift
+++ /dev/null
@@ -1,141 +0,0 @@
-import JavaScriptKit
-
-#if compiler(>=5.5)
-var printTestNames = false
-// Uncomment the next line to print the name of each test suite before running it.
-// This will make it easier to debug any errors that occur on the JS side.
-//printTestNames = true
-
-func test(_ name: String, testBlock: () throws -> Void) throws {
-    if printTestNames { print(name) }
-    do {
-        try testBlock()
-    } catch {
-        print("Error in \(name)")
-        print(error)
-        throw error
-    }
-    print("โœ… \(name)")
-}
-
-func asyncTest(_ name: String, testBlock: () async throws -> Void) async throws -> Void {
-    if printTestNames { print(name) }
-    do {
-        try await testBlock()
-    } catch {
-        print("Error in \(name)")
-        print(error)
-        throw error
-    }
-    print("โœ… \(name)")
-}
-
-struct MessageError: Error {
-    let message: String
-    let file: StaticString
-    let line: UInt
-    let column: UInt
-    init(_ message: String, file: StaticString, line: UInt, column: UInt) {
-        self.message = message
-        self.file = file
-        self.line = line
-        self.column = column
-    }
-}
-
-func expectGTE<T: Comparable>(
-    _ lhs: T, _ rhs: T,
-    file: StaticString = #file, line: UInt = #line, column: UInt = #column
-) throws {
-    if lhs < rhs {
-        throw MessageError(
-            "Expected \(lhs) to be greater than or equal to \(rhs)",
-            file: file, line: line, column: column
-        )
-    }
-}
-
-func expectEqual<T: Equatable>(
-    _ lhs: T, _ rhs: T,
-    file: StaticString = #file, line: UInt = #line, column: UInt = #column
-) throws {
-    if lhs != rhs {
-        throw MessageError("Expect to be equal \"\(lhs)\" and \"\(rhs)\"", file: file, line: line, column: column)
-    }
-}
-
-func expectCast<T, U>(
-    _ value: T, to type: U.Type = U.self,
-    file: StaticString = #file, line: UInt = #line, column: UInt = #column
-) throws -> U {
-    guard let value = value as? U else {
-        throw MessageError("Expect \"\(value)\" to be \(U.self)", file: file, line: line, column: column)
-    }
-    return value
-}
-
-func expectObject(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSObject {
-    switch value {
-    case let .object(ref): return ref
-    default:
-        throw MessageError("Type of \(value) should be \"object\"", file: file, line: line, column: column)
-    }
-}
-
-func expectArray(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSArray {
-    guard let array = value.array else {
-        throw MessageError("Type of \(value) should be \"object\"", file: file, line: line, column: column)
-    }
-    return array
-}
-
-func expectFunction(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSFunction {
-    switch value {
-    case let .function(ref): return ref
-    default:
-        throw MessageError("Type of \(value) should be \"function\"", file: file, line: line, column: column)
-    }
-}
-
-func expectBoolean(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> Bool {
-    switch value {
-    case let .boolean(bool): return bool
-    default:
-        throw MessageError("Type of \(value) should be \"boolean\"", file: file, line: line, column: column)
-    }
-}
-
-func expectNumber(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> Double {
-    switch value {
-    case let .number(number): return number
-    default:
-        throw MessageError("Type of \(value) should be \"number\"", file: file, line: line, column: column)
-    }
-}
-
-func expectString(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> String {
-    switch value {
-    case let .string(string): return String(string)
-    default:
-        throw MessageError("Type of \(value) should be \"string\"", file: file, line: line, column: column)
-    }
-}
-
-func expectAsyncThrow<T>(_ body: @autoclosure () async throws -> T, file: StaticString = #file, line: UInt = #line, column: UInt = #column) async throws -> Error {
-    do {
-        _ = try await body()
-    } catch {
-        return error
-    }
-    throw MessageError("Expect to throw an exception", file: file, line: line, column: column)
-}
-
-func expectNotNil<T>(_ value: T?, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws {
-    switch value {
-    case .some: return
-    case .none:
-        throw MessageError("Expect a non-nil value", file: file, line: line, column: column)
-    }
-}
-
-#endif
diff --git a/IntegrationTests/TestSuites/Sources/ConcurrencyTests/main.swift b/IntegrationTests/TestSuites/Sources/ConcurrencyTests/main.swift
deleted file mode 100644
index 1f0764e14..000000000
--- a/IntegrationTests/TestSuites/Sources/ConcurrencyTests/main.swift
+++ /dev/null
@@ -1,221 +0,0 @@
-import JavaScriptEventLoop
-import JavaScriptKit
-#if canImport(WASILibc)
-import WASILibc
-#elseif canImport(Darwin)
-import Darwin
-#endif
-
-func performanceNow() -> Double {
-    return JSObject.global.performance.now().number!
-}
-
-func measure(_ block: () async throws -> Void) async rethrows -> Double {
-    let start = performanceNow()
-    try await block()
-    return performanceNow() - start
-}
-
-func entrypoint() async throws {
-    struct E: Error, Equatable {
-        let value: Int
-    }
-
-    try await asyncTest("Task.init value") {
-        let handle = Task { 1 }
-        try expectEqual(await handle.value, 1)
-    }
-
-    try await asyncTest("Task.init throws") {
-        let handle = Task {
-            throw E(value: 2)
-        }
-        let error = try await expectAsyncThrow(await handle.value)
-        let e = try expectCast(error, to: E.self)
-        try expectEqual(e, E(value: 2))
-    }
-
-    try await asyncTest("await resolved Promise") {
-        let p = JSPromise(resolver: { resolve in
-            resolve(.success(1))
-        })
-        try await expectEqual(p.value, 1)
-        try await expectEqual(p.result, .success(.number(1)))
-    }
-
-    try await asyncTest("await rejected Promise") {
-        let p = JSPromise(resolver: { resolve in
-            resolve(.failure(.number(3)))
-        })
-        let error = try await expectAsyncThrow(await p.value)
-        let jsValue = try expectCast(error, to: JSException.self).thrownValue
-        try expectEqual(jsValue, 3)
-        try await expectEqual(p.result, .failure(.number(3)))
-    }
-
-    try await asyncTest("Continuation") {
-        let value = await withUnsafeContinuation { cont in
-            cont.resume(returning: 1)
-        }
-        try expectEqual(value, 1)
-
-        let error = try await expectAsyncThrow(
-            try await withUnsafeThrowingContinuation { (cont: UnsafeContinuation<Never, Error>) in
-                cont.resume(throwing: E(value: 2))
-            }
-        )
-        let e = try expectCast(error, to: E.self)
-        try expectEqual(e.value, 2)
-    }
-
-    try await asyncTest("Task.sleep(_:)") {
-        let diff = try await measure {
-            try await Task.sleep(nanoseconds: 200_000_000)
-        }
-        try expectGTE(diff, 200)
-    }
-
-    try await asyncTest("Job reordering based on priority") {
-        class Context: @unchecked Sendable {
-            var completed: [String] = []
-        }
-        let context = Context()
-
-        // When no priority, they should be ordered by the enqueued order
-        let t1 = Task(priority: nil) {
-            context.completed.append("t1")
-        }
-        let t2 = Task(priority: nil) {
-            context.completed.append("t2")
-        }
-
-        _ = await (t1.value, t2.value)
-        try expectEqual(context.completed, ["t1", "t2"])
-
-        context.completed = []
-        // When high priority is enqueued after a low one, they should be re-ordered
-        let t3 = Task(priority: .low) {
-            context.completed.append("t3")
-        }
-        let t4 = Task(priority: .high) {
-            context.completed.append("t4")
-        }
-        let t5 = Task(priority: .low) {
-            context.completed.append("t5")
-        }
-
-        _ = await (t3.value, t4.value, t5.value)
-        try expectEqual(context.completed, ["t4", "t3", "t5"])
-    }
-
-    try await asyncTest("Async JSClosure") {
-        let delayClosure = JSClosure.async { _ -> JSValue in
-            try await Task.sleep(nanoseconds: 200_000_000)
-            return JSValue.number(3)
-        }
-        let delayObject = JSObject.global.Object.function!.new()
-        delayObject.closure = delayClosure.jsValue
-
-        let diff = try await measure {
-            let promise = JSPromise(from: delayObject.closure!())
-            try expectNotNil(promise)
-            let result = try await promise!.value
-            try expectEqual(result, .number(3))
-        }
-        try expectGTE(diff, 200)
-    }
-
-    try await asyncTest("Async JSPromise: then") {
-        let promise = JSPromise { resolve in
-            _ = JSObject.global.setTimeout!(
-                JSClosure { _  in
-                    resolve(.success(JSValue.number(3)))
-                    return .undefined
-                }.jsValue,
-                100
-            )
-        }
-        let promise2 = promise.then { result in
-            try await Task.sleep(nanoseconds: 100_000_000)
-            return String(result.number!)
-        }
-        let diff = try await measure {
-            let result = try await promise2.value
-            try expectEqual(result, .string("3.0"))
-        }
-        try expectGTE(diff, 200)
-    }
-
-    try await asyncTest("Async JSPromise: then(success:failure:)") {
-        let promise = JSPromise { resolve in
-            _ = JSObject.global.setTimeout!(
-                JSClosure { _ in
-                    resolve(.failure(JSError(message: "test").jsValue))
-                    return .undefined
-                }.jsValue,
-                100
-            )
-        }
-        let promise2 = promise.then { _ in
-            throw MessageError("Should not be called", file: #file, line: #line, column: #column)
-        } failure: { err in
-            return err
-        }
-        let result = try await promise2.value
-        try expectEqual(result.object?.message, .string("test"))
-    }
-
-    try await asyncTest("Async JSPromise: catch") {
-        let promise = JSPromise { resolve in
-            _ = JSObject.global.setTimeout!(
-                JSClosure { _ in
-                    resolve(.failure(JSError(message: "test").jsValue))
-                    return .undefined
-                }.jsValue,
-                100
-            )
-        }
-        let promise2 = promise.catch { err in
-            try await Task.sleep(nanoseconds: 100_000_000)
-            return err
-        }
-        let diff = try await measure {
-            let result = try await promise2.value
-            try expectEqual(result.object?.message, .string("test"))
-        }
-        try expectGTE(diff, 200)
-    }
-
-    try await asyncTest("Task.sleep(nanoseconds:)") {
-        let diff = try await measure {
-            try await Task.sleep(nanoseconds: 100_000_000)
-        }
-        try expectGTE(diff, 100)
-    }
-
-    #if compiler(>=5.7)
-    try await asyncTest("ContinuousClock.sleep") {
-        let diff = try await measure {
-            let c = ContinuousClock()
-            try await c.sleep(until: .now + .milliseconds(100))
-        }
-        try expectGTE(diff, 99)
-    }
-    try await asyncTest("SuspendingClock.sleep") {
-        let diff = try await measure {
-            let c = SuspendingClock()
-            try await c.sleep(until: .now + .milliseconds(100))
-        }
-        try expectGTE(diff, 99)
-    }
-    #endif
-}
-
-JavaScriptEventLoop.installGlobalExecutor()
-Task {
-    do {
-        try await entrypoint()
-    } catch {
-        print(error)
-    }
-}
diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/I64.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/I64.swift
deleted file mode 100644
index 8d8dda331..000000000
--- a/IntegrationTests/TestSuites/Sources/PrimaryTests/I64.swift
+++ /dev/null
@@ -1,39 +0,0 @@
-import JavaScriptBigIntSupport
-import JavaScriptKit
-
-func testI64() throws {
-    try test("BigInt") {
-        func expectPassesThrough(signed value: Int64) throws {
-            let bigInt = JSBigInt(value)
-            try expectEqual(bigInt.description, value.description)
-            let bigInt2 = JSBigInt(_slowBridge: value)
-            try expectEqual(bigInt2.description, value.description)
-        }
-
-        func expectPassesThrough(unsigned value: UInt64) throws {
-            let bigInt = JSBigInt(unsigned: value)
-            try expectEqual(bigInt.description, value.description)
-            let bigInt2 = JSBigInt(_slowBridge: value)
-            try expectEqual(bigInt2.description, value.description)
-        }
-
-        try expectPassesThrough(signed: 0)
-        try expectPassesThrough(signed: 1 << 62)
-        try expectPassesThrough(signed: -2305)
-        for _ in 0 ..< 100 {
-            try expectPassesThrough(signed: .random(in: .min ... .max))
-        }
-        try expectPassesThrough(signed: .min)
-        try expectPassesThrough(signed: .max)
-
-        try expectPassesThrough(unsigned: 0)
-        try expectPassesThrough(unsigned: 1 << 62)
-        try expectPassesThrough(unsigned: 1 << 63)
-        try expectPassesThrough(unsigned: .min)
-        try expectPassesThrough(unsigned: .max)
-        try expectPassesThrough(unsigned: ~0)
-        for _ in 0 ..< 100 {
-            try expectPassesThrough(unsigned: .random(in: .min ... .max))
-        }
-    }
-}
diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/UnitTestUtils.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/UnitTestUtils.swift
deleted file mode 100644
index 0d51c6ff5..000000000
--- a/IntegrationTests/TestSuites/Sources/PrimaryTests/UnitTestUtils.swift
+++ /dev/null
@@ -1,161 +0,0 @@
-import JavaScriptKit
-
-var printTestNames = false
-// Uncomment the next line to print the name of each test suite before running it.
-// This will make it easier to debug any errors that occur on the JS side.
-//printTestNames = true
-
-func test(_ name: String, testBlock: () throws -> Void) throws {
-    if printTestNames { print(name) }
-    do {
-        try testBlock()
-    } catch {
-        print("Error in \(name)")
-        print(error)
-        throw error
-    }
-    print("โœ… \(name)")
-}
-
-struct MessageError: Error {
-    let message: String
-    let file: StaticString
-    let line: UInt
-    let column: UInt
-    init(_ message: String, file: StaticString, line: UInt, column: UInt) {
-        self.message = message
-        self.file = file
-        self.line = line
-        self.column = column
-    }
-}
-
-func expectEqual<T: Equatable>(
-    _ lhs: T, _ rhs: T,
-    file: StaticString = #file, line: UInt = #line, column: UInt = #column
-) throws {
-    if lhs != rhs {
-        throw MessageError("Expect to be equal \"\(lhs)\" and \"\(rhs)\"", file: file, line: line, column: column)
-    }
-}
-
-func expectNotEqual<T: Equatable>(
-    _ lhs: T, _ rhs: T,
-    file: StaticString = #file, line: UInt = #line, column: UInt = #column
-) throws {
-    if lhs == rhs {
-        throw MessageError("Expect to not be equal \"\(lhs)\" and \"\(rhs)\"", file: file, line: line, column: column)
-    }
-}
-
-func expectObject(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSObject {
-    switch value {
-    case let .object(ref): return ref
-    default:
-        throw MessageError("Type of \(value) should be \"object\"", file: file, line: line, column: column)
-    }
-}
-
-func expectArray(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSArray {
-    guard let array = value.array else {
-        throw MessageError("Type of \(value) should be \"object\"", file: file, line: line, column: column)
-    }
-    return array
-}
-
-func expectFunction(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSFunction {
-    switch value {
-    case let .function(ref): return ref
-    default:
-        throw MessageError("Type of \(value) should be \"function\"", file: file, line: line, column: column)
-    }
-}
-
-func expectBoolean(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> Bool {
-    switch value {
-    case let .boolean(bool): return bool
-    default:
-        throw MessageError("Type of \(value) should be \"boolean\"", file: file, line: line, column: column)
-    }
-}
-
-func expectNumber(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> Double {
-    switch value {
-    case let .number(number): return number
-    default:
-        throw MessageError("Type of \(value) should be \"number\"", file: file, line: line, column: column)
-    }
-}
-
-func expectString(_ value: JSValue, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> String {
-    switch value {
-    case let .string(string): return String(string)
-    default:
-        throw MessageError("Type of \(value) should be \"string\"", file: file, line: line, column: column)
-    }
-}
-
-func expect(_ description: String, _ result: Bool, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws {
-    if !result {
-        throw MessageError(description, file: file, line: line, column: column)
-    }
-}
-
-func expectThrow<T>(_ body: @autoclosure () throws -> T, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> Error {
-    do {
-        _ = try body()
-    } catch {
-        return error
-    }
-    throw MessageError("Expect to throw an exception", file: file, line: line, column: column)
-}
-
-func wrapUnsafeThrowableFunction(_ body: @escaping () -> Void, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> JSValue {
-    JSObject.global.callThrowingClosure.function!(JSClosure { _ in 
-            body() 
-            return .undefined
-    })
-}
-func expectNotNil<T>(_ value: T?, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws {
-    switch value {
-    case .some: return
-    case .none:
-        throw MessageError("Expect a non-nil value", file: file, line: line, column: column)
-    }
-}
-func expectNil<T>(_ value: T?, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws {
-    switch value {
-    case .some:
-        throw MessageError("Expect an nil", file: file, line: line, column: column)
-    case .none: return
-    }
-}
-
-class Expectation {
-    private(set) var isFulfilled: Bool = false
-    private let label: String
-    private let expectedFulfillmentCount: Int
-    private var fulfillmentCount: Int = 0
-
-    init(label: String, expectedFulfillmentCount: Int = 1) {
-        self.label = label
-        self.expectedFulfillmentCount = expectedFulfillmentCount
-    }
-
-    func fulfill() {
-        assert(!isFulfilled, "Too many fulfillment (label: \(label)): expectedFulfillmentCount is \(expectedFulfillmentCount)")
-        fulfillmentCount += 1
-        if fulfillmentCount == expectedFulfillmentCount {
-            isFulfilled = true
-        }
-    }
-
-    static func wait(_ expectations: [Expectation]) {
-        var timer: JSTimer!
-        timer = JSTimer(millisecondsDelay: 5.0, isRepeating: true) {
-            guard expectations.allSatisfy(\.isFulfilled) else { return }
-            assert(timer != nil)
-            timer = nil
-        }
-    }
-}
diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift
deleted file mode 100644
index 12cc91cc9..000000000
--- a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift
+++ /dev/null
@@ -1,918 +0,0 @@
-import JavaScriptKit
-import CHelpers
-
-try test("Literal Conversion") {
-    let global = JSObject.global
-    let inputs: [JSValue] = [
-        .boolean(true),
-        .boolean(false),
-        .string("foobar"),
-        .string("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง Family Emoji"),
-        .number(0),
-        .number(Double(Int32.max)),
-        .number(Double(Int32.min)),
-        .number(Double.infinity),
-        .number(Double.nan),
-        .null,
-        .undefined,
-    ]
-    for (index, input) in inputs.enumerated() {
-        let prop = JSString("prop_\(index)")
-        setJSValue(this: global, name: prop, value: input)
-        let got = getJSValue(this: global, name: prop)
-        switch (got, input) {
-        case let (.number(lhs), .number(rhs)):
-            // Compare bitPattern because nan == nan is always false
-            try expectEqual(lhs.bitPattern, rhs.bitPattern)
-        default:
-            try expectEqual(got, input)
-        }
-    }
-}
-
-try test("Object Conversion") {
-    // Notes: globalObject1 is defined in JavaScript environment
-    //
-    // ```js
-    // global.globalObject1 = {
-    //   "prop_1": {
-    //     "nested_prop": 1,
-    //   },
-    //   "prop_2": 2,
-    //   "prop_3": true,
-    //   "prop_4": [
-    //     3, 4, "str_elm_1", 5,
-    //   ],
-    //   ...
-    // }
-    // ```
-    //
-
-    let globalObject1 = getJSValue(this: .global, name: "globalObject1")
-    let globalObject1Ref = try expectObject(globalObject1)
-    let prop_1 = getJSValue(this: globalObject1Ref, name: "prop_1")
-    let prop_1Ref = try expectObject(prop_1)
-    let nested_prop = getJSValue(this: prop_1Ref, name: "nested_prop")
-    try expectEqual(nested_prop, .number(1))
-    let prop_2 = getJSValue(this: globalObject1Ref, name: "prop_2")
-    try expectEqual(prop_2, .number(2))
-    let prop_3 = getJSValue(this: globalObject1Ref, name: "prop_3")
-    try expectEqual(prop_3, .boolean(true))
-    let prop_4 = getJSValue(this: globalObject1Ref, name: "prop_4")
-    let prop_4Array = try expectObject(prop_4)
-    let expectedProp_4: [JSValue] = [
-        .number(3), .number(4), .string("str_elm_1"), .null, .undefined, .number(5),
-    ]
-    for (index, expectedElement) in expectedProp_4.enumerated() {
-        let actualElement = getJSValue(this: prop_4Array, index: Int32(index))
-        try expectEqual(actualElement, expectedElement)
-    }
-
-    try expectEqual(getJSValue(this: globalObject1Ref, name: "undefined_prop"), .undefined)
-}
-
-try test("Value Construction") {
-    let globalObject1 = getJSValue(this: .global, name: "globalObject1")
-    let globalObject1Ref = try expectObject(globalObject1)
-    let prop_2 = getJSValue(this: globalObject1Ref, name: "prop_2")
-    try expectEqual(Int.construct(from: prop_2), 2)
-    let prop_3 = getJSValue(this: globalObject1Ref, name: "prop_3")
-    try expectEqual(Bool.construct(from: prop_3), true)
-    let prop_7 = getJSValue(this: globalObject1Ref, name: "prop_7")
-    try expectEqual(Double.construct(from: prop_7), 3.14)
-    try expectEqual(Float.construct(from: prop_7), 3.14)
-
-    for source: JSValue in [
-        .number(.infinity), .number(.nan),
-        .number(Double(UInt64.max).nextUp), .number(Double(Int64.min).nextDown)
-    ] {
-        try expectNil(Int.construct(from: source))
-        try expectNil(Int8.construct(from: source))
-        try expectNil(Int16.construct(from: source))
-        try expectNil(Int32.construct(from: source))
-        try expectNil(Int64.construct(from: source))
-        try expectNil(UInt.construct(from: source))
-        try expectNil(UInt8.construct(from: source))
-        try expectNil(UInt16.construct(from: source))
-        try expectNil(UInt32.construct(from: source))
-        try expectNil(UInt64.construct(from: source))
-    }
-}
-
-try test("Array Iterator") {
-    let globalObject1 = getJSValue(this: .global, name: "globalObject1")
-    let globalObject1Ref = try expectObject(globalObject1)
-    let prop_4 = getJSValue(this: globalObject1Ref, name: "prop_4")
-    let array1 = try expectArray(prop_4)
-    let expectedProp_4: [JSValue] = [
-        .number(3), .number(4), .string("str_elm_1"), .null, .undefined, .number(5),
-    ]
-    try expectEqual(Array(array1), expectedProp_4)
-
-    // Ensure that iterator skips empty hole as JavaScript does.
-    let prop_8 = getJSValue(this: globalObject1Ref, name: "prop_8")
-    let array2 = try expectArray(prop_8)
-    let expectedProp_8: [JSValue] = [0, 2, 3, 6]
-    try expectEqual(Array(array2), expectedProp_8)
-}
-
-try test("Array RandomAccessCollection") {
-    let globalObject1 = getJSValue(this: .global, name: "globalObject1")
-    let globalObject1Ref = try expectObject(globalObject1)
-    let prop_4 = getJSValue(this: globalObject1Ref, name: "prop_4")
-    let array1 = try expectArray(prop_4)
-    let expectedProp_4: [JSValue] = [
-        .number(3), .number(4), .string("str_elm_1"), .null, .undefined, .number(5),
-    ]
-    try expectEqual([array1[0], array1[1], array1[2], array1[3], array1[4], array1[5]], expectedProp_4)
-
-    // Ensure that subscript can access empty hole
-    let prop_8 = getJSValue(this: globalObject1Ref, name: "prop_8")
-    let array2 = try expectArray(prop_8)
-    let expectedProp_8: [JSValue] = [
-        0, .undefined, 2, 3, .undefined, .undefined, 6
-    ]
-    try expectEqual([array2[0], array2[1], array2[2], array2[3], array2[4], array2[5], array2[6]], expectedProp_8)
-}
-
-try test("Value Decoder") {
-    struct GlobalObject1: Codable {
-        struct Prop1: Codable {
-            let nested_prop: Int
-        }
-
-        let prop_1: Prop1
-        let prop_2: Int
-        let prop_3: Bool
-        let prop_7: Float
-    }
-    let decoder = JSValueDecoder()
-    let rawGlobalObject1 = getJSValue(this: .global, name: "globalObject1")
-    let globalObject1 = try decoder.decode(GlobalObject1.self, from: rawGlobalObject1)
-    try expectEqual(globalObject1.prop_1.nested_prop, 1)
-    try expectEqual(globalObject1.prop_2, 2)
-    try expectEqual(globalObject1.prop_3, true)
-    try expectEqual(globalObject1.prop_7, 3.14)
-}
-
-try test("Function Call") {
-    // Notes: globalObject1 is defined in JavaScript environment
-    //
-    // ```js
-    // global.globalObject1 = {
-    //   ...
-    //   "prop_5": {
-    //     "func1": function () { return },
-    //     "func2": function () { return 1 },
-    //     "func3": function (n) { return n * 2 },
-    //     "func4": function (a, b, c) { return a + b + c },
-    //     "func5": function (x) { return "Hello, " + x },
-    //     "func6": function (c, a, b) {
-    //       if (c) { return a } else { return b }
-    //     },
-    //   }
-    //   ...
-    // }
-    // ```
-    //
-
-    // Notes: If the size of `RawJSValue` is updated, these test suites will fail.
-    let globalObject1 = getJSValue(this: .global, name: "globalObject1")
-    let globalObject1Ref = try expectObject(globalObject1)
-    let prop_5 = getJSValue(this: globalObject1Ref, name: "prop_5")
-    let prop_5Ref = try expectObject(prop_5)
-
-    let func1 = try expectFunction(getJSValue(this: prop_5Ref, name: "func1"))
-    try expectEqual(func1(), .undefined)
-    let func2 = try expectFunction(getJSValue(this: prop_5Ref, name: "func2"))
-    try expectEqual(func2(), .number(1))
-    let func3 = try expectFunction(getJSValue(this: prop_5Ref, name: "func3"))
-    try expectEqual(func3(2), .number(4))
-    let func4 = try expectFunction(getJSValue(this: prop_5Ref, name: "func4"))
-    try expectEqual(func4(2, 3, 4), .number(9))
-    try expectEqual(func4(2, 3, 4, 5), .number(9))
-    let func5 = try expectFunction(getJSValue(this: prop_5Ref, name: "func5"))
-    try expectEqual(func5("World!"), .string("Hello, World!"))
-    let func6 = try expectFunction(getJSValue(this: prop_5Ref, name: "func6"))
-    try expectEqual(func6(true, 1, 2), .number(1))
-    try expectEqual(func6(false, 1, 2), .number(2))
-    try expectEqual(func6(true, "OK", 2), .string("OK"))
-}
-
-let evalClosure = JSObject.global.globalObject1.eval_closure.function!
-
-try test("Closure Lifetime") {
-    func expectCrashByCall(ofClosure c: JSClosureProtocol) throws {
-        print("======= BEGIN OF EXPECTED FATAL ERROR =====")
-        _ = try expectThrow(try evalClosure.throws(c))
-        print("======= END OF EXPECTED FATAL ERROR =======")
-    }
-
-    do {
-        let c1 = JSClosure { arguments in
-            return arguments[0]
-        }
-        try expectEqual(evalClosure(c1, JSValue.number(1.0)), .number(1.0))
-#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
-        c1.release()
-#endif
-    }
-
-    do {
-        let c1 = JSClosure { _ in .undefined }
-#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
-        c1.release()
-#endif
-    }
-
-    do {
-        let array = JSObject.global.Array.function!.new()
-        let c1 = JSClosure { _ in .number(3) }
-        _ = array.push!(c1)
-        try expectEqual(array[0].function!().number, 3.0)
-#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
-        c1.release()
-#endif
-    }
-
-//    do {
-//        let weakRef = { () -> JSObject in
-//            let c1 = JSClosure { _ in .undefined }
-//            return JSObject.global.WeakRef.function!.new(c1)
-//        }()
-//
-//        // unsure if this will actually work since GC may not run immediately
-//        try expectEqual(weakRef.deref!(), .undefined)
-//    }
-
-#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
-    do {
-        let c1 = JSOneshotClosure { _ in
-            return .boolean(true)
-        }
-        try expectEqual(evalClosure(c1), .boolean(true))
-        // second call will cause `fatalError` that can be caught as a JavaScript exception
-        try expectCrashByCall(ofClosure: c1)
-        // OneshotClosure won't call fatalError even if it's deallocated before `release`
-    }
-#endif
-
-#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
-    // Check diagnostics of use-after-free
-    do {
-        let c1Line = #line + 1
-        let c1 = JSClosure { $0[0] }
-        c1.release()
-        let error = try expectThrow(try evalClosure.throws(c1, JSValue.number(42.0))) as! JSException
-        try expect("Error message should contains definition location", error.thrownValue.description.hasSuffix("PrimaryTests/main.swift:\(c1Line)"))
-    }
-#endif
-
-    do {
-        let c1 = JSClosure { _ in .number(4) }
-        try expectEqual(c1(), .number(4))
-    }
-
-    do {
-        let c1 = JSClosure { _ in fatalError("Crash while closure evaluation") }
-        let error = try expectThrow(try evalClosure.throws(c1)) as! JSException
-        try expectEqual(error.thrownValue.description, "RuntimeError: unreachable")
-    }
-}
-
-try test("Host Function Registration") {
-    // ```js
-    // global.globalObject1 = {
-    //   ...
-    //   "prop_6": {
-    //     "call_host_1": function() {
-    //       return global.globalObject1.prop_6.host_func_1()
-    //     }
-    //   }
-    // }
-    // ```
-    let globalObject1 = getJSValue(this: .global, name: "globalObject1")
-    let globalObject1Ref = try expectObject(globalObject1)
-    let prop_6 = getJSValue(this: globalObject1Ref, name: "prop_6")
-    let prop_6Ref = try expectObject(prop_6)
-
-    var isHostFunc1Called = false
-    let hostFunc1 = JSClosure { (_) -> JSValue in
-        isHostFunc1Called = true
-        return .number(1)
-    }
-
-    setJSValue(this: prop_6Ref, name: "host_func_1", value: .object(hostFunc1))
-
-    let call_host_1 = getJSValue(this: prop_6Ref, name: "call_host_1")
-    let call_host_1Func = try expectFunction(call_host_1)
-    try expectEqual(call_host_1Func(), .number(1))
-    try expectEqual(isHostFunc1Called, true)
-
-#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
-    hostFunc1.release()
-#endif
-
-    let hostFunc2 = JSClosure { (arguments) -> JSValue in
-        do {
-            let input = try expectNumber(arguments[0])
-            return .number(input * 2)
-        } catch {
-            return .string(String(describing: error))
-        }
-    }
-
-    try expectEqual(evalClosure(hostFunc2, 3), .number(6))
-    _ = try expectString(evalClosure(hostFunc2, true))
-
-#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
-    hostFunc2.release()
-#endif
-}
-
-try test("New Object Construction") {
-    // ```js
-    // global.Animal = function(name, age, isCat) {
-    //   this.name = name
-    //   this.age = age
-    //   this.bark = () => {
-    //     return isCat ? "nyan" : "wan"
-    //   }
-    // }
-    // ```
-    let objectConstructor = try expectFunction(getJSValue(this: .global, name: "Animal"))
-    let cat1 = objectConstructor.new("Tama", 3, true)
-    try expectEqual(getJSValue(this: cat1, name: "name"), .string("Tama"))
-    try expectEqual(getJSValue(this: cat1, name: "age"), .number(3))
-    try expectEqual(cat1.isInstanceOf(objectConstructor), true)
-    try expectEqual(cat1.isInstanceOf(try expectFunction(getJSValue(this: .global, name: "Array"))), false)
-    let cat1Bark = try expectFunction(getJSValue(this: cat1, name: "bark"))
-    try expectEqual(cat1Bark(), .string("nyan"))
-
-    let dog1 = objectConstructor.new("Pochi", 3, false)
-    let dog1Bark = try expectFunction(getJSValue(this: dog1, name: "bark"))
-    try expectEqual(dog1Bark(), .string("wan"))
-}
-
-try test("Object Decoding") {
-    /* 
-     ```js
-     global.objectDecodingTest = {
-         obj: {},
-         fn: () => {},
-         sym: Symbol("s"),
-         bi: BigInt(3)
-     };
-     ```
-     */
-    let js: JSValue = JSObject.global.objectDecodingTest
-
-    // I can't use regular name like `js.object` here
-    // cz its conflicting with case name and DML.
-    // so I use abbreviated names
-    let object: JSValue = js.obj
-    let function: JSValue = js.fn
-    let symbol: JSValue = js.sym
-    let bigInt: JSValue = js.bi
-
-    try expectNotNil(JSObject.construct(from: object))
-    try expectEqual(JSObject.construct(from: function).map { $0 is JSFunction }, .some(true))
-    try expectEqual(JSObject.construct(from: symbol).map { $0 is JSSymbol }, .some(true))
-    try expectEqual(JSObject.construct(from: bigInt).map { $0 is JSBigInt }, .some(true))
-
-    try expectNil(JSFunction.construct(from: object))
-    try expectNotNil(JSFunction.construct(from: function))
-    try expectNil(JSFunction.construct(from: symbol))
-    try expectNil(JSFunction.construct(from: bigInt))
-
-    try expectNil(JSSymbol.construct(from: object))
-    try expectNil(JSSymbol.construct(from: function))
-    try expectNotNil(JSSymbol.construct(from: symbol))
-    try expectNil(JSSymbol.construct(from: bigInt))
-
-    try expectNil(JSBigInt.construct(from: object))
-    try expectNil(JSBigInt.construct(from: function))
-    try expectNil(JSBigInt.construct(from: symbol))
-    try expectNotNil(JSBigInt.construct(from: bigInt))
-}
-
-try test("Call Function With This") {
-    // ```js
-    // global.Animal = function(name, age, isCat) {
-    //   this.name = name
-    //   this.age = age
-    //   this.bark = () => {
-    //     return isCat ? "nyan" : "wan"
-    //   }
-    //   this.isCat = isCat
-    //   this.getIsCat = function() {
-    //     return this.isCat
-    //   }
-    // }
-    // ```
-    let objectConstructor = try expectFunction(getJSValue(this: .global, name: "Animal"))
-    let cat1 = objectConstructor.new("Tama", 3, true)
-    let cat1Value = JSValue.object(cat1)
-    let getIsCat = try expectFunction(getJSValue(this: cat1, name: "getIsCat"))
-    let setName = try expectFunction(getJSValue(this: cat1, name: "setName"))
-
-    // Direct call without this
-    _ = try expectThrow(try getIsCat.throws())
-
-    // Call with this
-    let gotIsCat = getIsCat(this: cat1)
-    try expectEqual(gotIsCat, .boolean(true))
-    try expectEqual(cat1.getIsCat!(), .boolean(true))
-    try expectEqual(cat1Value.getIsCat(), .boolean(true))
-
-    // Call with this and argument
-    setName(this: cat1, JSValue.string("Shiro"))
-    try expectEqual(getJSValue(this: cat1, name: "name"), .string("Shiro"))
-    _ = cat1.setName!("Tora")
-    try expectEqual(getJSValue(this: cat1, name: "name"), .string("Tora"))
-    _ = cat1Value.setName("Chibi")
-    try expectEqual(getJSValue(this: cat1, name: "name"), .string("Chibi"))
-}
-
-try test("Object Conversion") {
-    let array1 = [1, 2, 3]
-    let jsArray1 = array1.jsValue.object!
-    try expectEqual(jsArray1.length, .number(3))
-    try expectEqual(jsArray1[0], .number(1))
-    try expectEqual(jsArray1[1], .number(2))
-    try expectEqual(jsArray1[2], .number(3))
-
-    let array2: [ConvertibleToJSValue] = [1, "str", false]
-    let jsArray2 = array2.jsValue.object!
-    try expectEqual(jsArray2.length, .number(3))
-    try expectEqual(jsArray2[0], .number(1))
-    try expectEqual(jsArray2[1], .string("str"))
-    try expectEqual(jsArray2[2], .boolean(false))
-    _ = jsArray2.push!(5)
-    try expectEqual(jsArray2.length, .number(4))
-    _ = jsArray2.push!(jsArray1)
-
-    try expectEqual(jsArray2[4], .object(jsArray1))
-
-    let dict1: [String: JSValue] = [
-        "prop1": 1.jsValue,
-        "prop2": "foo".jsValue,
-    ]
-    let jsDict1 = dict1.jsValue.object!
-    try expectEqual(jsDict1.prop1, .number(1))
-    try expectEqual(jsDict1.prop2, .string("foo"))
-}
-
-try test("ObjectRef Lifetime") {
-    // ```js
-    // global.globalObject1 = {
-    //   "prop_1": {
-    //     "nested_prop": 1,
-    //   },
-    //   "prop_2": 2,
-    //   "prop_3": true,
-    //   "prop_4": [
-    //     3, 4, "str_elm_1", 5,
-    //   ],
-    //   ...
-    // }
-    // ```
-
-    let identity = JSClosure { $0[0] }
-    let ref1 = getJSValue(this: .global, name: "globalObject1").object!
-    let ref2 = evalClosure(identity, ref1).object!
-    try expectEqual(ref1.prop_2, .number(2))
-    try expectEqual(ref2.prop_2, .number(2))
-
-#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
-    identity.release()
-#endif
-}
-
-func checkArray<T>(_ array: [T]) throws where T: TypedArrayElement & Equatable {
-    try expectEqual(toString(JSTypedArray(array).jsValue.object!), jsStringify(array))
-    try checkArrayUnsafeBytes(array)
-}
-
-func toString<T: JSObject>(_ object: T) -> String {
-    return object.toString!().string!
-}
-
-func jsStringify(_ array: [Any]) -> String {
-    array.map({ String(describing: $0) }).joined(separator: ",")
-}
-
-func checkArrayUnsafeBytes<T>(_ array: [T]) throws where T: TypedArrayElement & Equatable {
-    let copyOfArray: [T] = JSTypedArray(array).withUnsafeBytes { buffer in
-        Array(buffer)
-    }
-    try expectEqual(copyOfArray, array)
-}
-
-try test("TypedArray") {
-    let numbers = [UInt8](0 ... 255)
-    let typedArray = JSTypedArray(numbers)
-    try expectEqual(typedArray[12], 12)
-    try expectEqual(numbers.count, typedArray.lengthInBytes)
-
-    let numbersSet = Set(0 ... 255)
-    let typedArrayFromSet = JSTypedArray(numbersSet)
-    try expectEqual(typedArrayFromSet.jsObject.length, 256)
-    try expectEqual(typedArrayFromSet.lengthInBytes, 256 * MemoryLayout<Int>.size)
-
-    try checkArray([0, .max, 127, 1] as [UInt8])
-    try checkArray([0, 1, .max, .min, -1] as [Int8])
-
-    try checkArray([0, .max, 255, 1] as [UInt16])
-    try checkArray([0, 1, .max, .min, -1] as [Int16])
-
-    try checkArray([0, .max, 255, 1] as [UInt32])
-    try checkArray([0, 1, .max, .min, -1] as [Int32])
-
-    try checkArray([0, .max, 255, 1] as [UInt])
-    try checkArray([0, 1, .max, .min, -1] as [Int])
-
-    let float32Array: [Float32] = [0, 1, .pi, .greatestFiniteMagnitude, .infinity, .leastNonzeroMagnitude, .leastNormalMagnitude, 42]
-    let jsFloat32Array = JSTypedArray(float32Array)
-    for (i, num) in float32Array.enumerated() {
-        try expectEqual(num, jsFloat32Array[i])
-    }
-
-    let float64Array: [Float64] = [0, 1, .pi, .greatestFiniteMagnitude, .infinity, .leastNonzeroMagnitude, .leastNormalMagnitude, 42]
-    let jsFloat64Array = JSTypedArray(float64Array)
-    for (i, num) in float64Array.enumerated() {
-        try expectEqual(num, jsFloat64Array[i])
-    }
-}
-
-try test("TypedArray_Mutation") {
-    let array = JSTypedArray<Int>(length: 100)
-    for i in 0..<100 {
-        array[i] = i
-    }
-    for i in 0..<100 {
-        try expectEqual(i, array[i])
-    }
-    try expectEqual(toString(array.jsValue.object!), jsStringify(Array(0..<100)))
-}
-
-try test("Date") {
-    let date1Milliseconds = JSDate.now()
-    let date1 = JSDate(millisecondsSinceEpoch: date1Milliseconds)
-    let date2 = JSDate(millisecondsSinceEpoch: date1.valueOf())
-
-    try expectEqual(date1.valueOf(), date2.valueOf())
-    try expectEqual(date1.fullYear, date2.fullYear)
-    try expectEqual(date1.month, date2.month)
-    try expectEqual(date1.date, date2.date)
-    try expectEqual(date1.day, date2.day)
-    try expectEqual(date1.hours, date2.hours)
-    try expectEqual(date1.minutes, date2.minutes)
-    try expectEqual(date1.seconds, date2.seconds)
-    try expectEqual(date1.milliseconds, date2.milliseconds)
-    try expectEqual(date1.utcFullYear, date2.utcFullYear)
-    try expectEqual(date1.utcMonth, date2.utcMonth)
-    try expectEqual(date1.utcDate, date2.utcDate)
-    try expectEqual(date1.utcDay, date2.utcDay)
-    try expectEqual(date1.utcHours, date2.utcHours)
-    try expectEqual(date1.utcMinutes, date2.utcMinutes)
-    try expectEqual(date1.utcSeconds, date2.utcSeconds)
-    try expectEqual(date1.utcMilliseconds, date2.utcMilliseconds)
-    try expectEqual(date1, date2)
-
-    let date3 = JSDate(millisecondsSinceEpoch: 0)
-    try expectEqual(date3.valueOf(), 0)
-    try expectEqual(date3.utcFullYear, 1970)
-    try expectEqual(date3.utcMonth, 0)
-    try expectEqual(date3.utcDate, 1)
-    // the epoch date was on Friday
-    try expectEqual(date3.utcDay, 4)
-    try expectEqual(date3.utcHours, 0)
-    try expectEqual(date3.utcMinutes, 0)
-    try expectEqual(date3.utcSeconds, 0)
-    try expectEqual(date3.utcMilliseconds, 0)
-    try expectEqual(date3.toISOString(), "1970-01-01T00:00:00.000Z")
-
-    try expectEqual(date3 < date1, true)
-}
-
-// make the timers global to prevent early deallocation
-var timeouts: [JSTimer] = []
-var interval: JSTimer?
-
-try test("Timer") {
-    let start = JSDate().valueOf()
-    let timeoutMilliseconds = 5.0
-    var timeout: JSTimer!
-    timeout = JSTimer(millisecondsDelay: timeoutMilliseconds, isRepeating: false) {
-        // verify that at least `timeoutMilliseconds` passed since the `timeout` timer started
-        try! expectEqual(start + timeoutMilliseconds <= JSDate().valueOf(), true)
-    }
-    timeouts += [timeout]
-
-    timeout = JSTimer(millisecondsDelay: timeoutMilliseconds, isRepeating: false) {
-        fatalError("timer should be cancelled")
-    }
-    timeout = nil
-
-    var count = 0.0
-    let maxCount = 5.0
-    interval = JSTimer(millisecondsDelay: 5, isRepeating: true) {
-        // ensure that JSTimer is living
-        try! expectNotNil(interval)
-        // verify that at least `timeoutMilliseconds * count` passed since the `timeout`
-        // timer started
-        try! expectEqual(start + timeoutMilliseconds * count <= JSDate().valueOf(), true)
-
-        guard count < maxCount else {
-            // stop the timer after `maxCount` reached
-            interval = nil
-            return
-        }
-
-        count += 1
-    }
-}
-
-var timer: JSTimer?
-var expectations: [Expectation] = []
-
-try test("Promise") {
-
-    let p1 = JSPromise.resolve(JSValue.null)
-    let exp1 = Expectation(label: "Promise.then testcase", expectedFulfillmentCount: 4)
-    p1.then { value in
-        try! expectEqual(value, .null)
-        exp1.fulfill()
-        return JSValue.number(1.0)
-    }
-    .then { value in
-        try! expectEqual(value, .number(1.0))
-        exp1.fulfill()
-        return JSPromise.resolve(JSValue.boolean(true))
-    }
-    .then { value in
-        try! expectEqual(value, .boolean(true))
-        exp1.fulfill()
-        return JSValue.undefined
-    }
-    .catch { err -> JSValue in
-        print(err.object!.stack.string!)
-        fatalError("Not fired due to no throw")
-    }
-    .finally { exp1.fulfill() }
-
-    let exp2 = Expectation(label: "Promise.catch testcase", expectedFulfillmentCount: 4)
-    let p2 = JSPromise.reject(JSValue.boolean(false))
-    p2.then { _ -> JSValue in
-        fatalError("Not fired due to no success")
-    }
-    .catch { reason in
-        try! expectEqual(reason, .boolean(false))
-        exp2.fulfill()
-        return JSValue.boolean(true)
-    }
-    .then { value in
-        try! expectEqual(value, .boolean(true))
-        exp2.fulfill()
-        return JSPromise.reject(JSValue.number(2.0))
-    }
-    .catch { reason in
-        try! expectEqual(reason, .number(2.0))
-        exp2.fulfill()
-        return JSValue.undefined
-    }
-    .finally { exp2.fulfill() }
-
-
-    let start = JSDate().valueOf()
-    let timeoutMilliseconds = 5.0
-    let exp3 = Expectation(label: "Promise and Timer testcae", expectedFulfillmentCount: 2)
-
-    let p3 = JSPromise { resolve in
-        timer = JSTimer(millisecondsDelay: timeoutMilliseconds) {
-            exp3.fulfill()
-            resolve(.success(.undefined))
-        }
-    }
-
-    p3.then { _ in
-        // verify that at least `timeoutMilliseconds` passed since the timer started
-        try! expectEqual(start + timeoutMilliseconds <= JSDate().valueOf(), true)
-        exp3.fulfill()
-        return JSValue.undefined
-    }
-
-    let exp4 = Expectation(label: "Promise lifetime")
-    // Ensure that users don't need to manage JSPromise lifetime
-    JSPromise.resolve(JSValue.boolean(true)).then { _ in
-        exp4.fulfill()
-        return JSValue.undefined
-    }
-    expectations += [exp1, exp2, exp3, exp4]
-}
-
-try test("Error") {
-    let message = "test error"
-    let expectedDescription = "Error: test error"
-    let error = JSError(message: message)
-    try expectEqual(error.name, "Error")
-    try expectEqual(error.message, message)
-    try expectEqual(error.description, expectedDescription)
-    try expectEqual(error.stack?.isEmpty, false)
-    try expectEqual(JSError(from: .string("error"))?.description, nil)
-    try expectEqual(JSError(from: .object(error.jsObject))?.description, expectedDescription)
-}
-
-try test("JSValue accessor") {
-    var globalObject1 = JSObject.global.globalObject1
-    try expectEqual(globalObject1.prop_1.nested_prop, .number(1))
-    try expectEqual(globalObject1.object!.prop_1.object!.nested_prop, .number(1))
-
-    try expectEqual(globalObject1.prop_4[0], .number(3))
-    try expectEqual(globalObject1.prop_4[1], .number(4))
-
-    globalObject1.prop_1.nested_prop = "bar"
-    try expectEqual(globalObject1.prop_1.nested_prop, .string("bar"))
-
-    /* TODO: Fix https://github.com/swiftwasm/JavaScriptKit/issues/132 and un-comment this test
-    `nested` should not be set again to `target.nested` by `target.nested.prop = .number(1)`
-
-    let observableObj = globalObject1.observable_obj.object!
-    observableObj.set_called = .boolean(false)
-    observableObj.target.nested.prop = .number(1)
-    try expectEqual(observableObj.set_called, .boolean(false))
-
-    */
-}
-
-try test("Exception") {
-    // ```js
-    // global.globalObject1 = {
-    //   ...
-    //   prop_9: {
-    //       func1: function () {
-    //           throw new Error();
-    //       },
-    //       func2: function () {
-    //           throw "String Error";
-    //       },
-    //       func3: function () {
-    //           throw 3.0
-    //       },
-    //   },
-    //   ...
-    // }
-    // ```
-    //
-    let globalObject1 = JSObject.global.globalObject1
-    let prop_9: JSValue = globalObject1.prop_9
-
-    // MARK: Throwing method calls
-    let error1 = try expectThrow(try prop_9.object!.throwing.func1!())
-    try expectEqual(error1 is JSException, true)
-    let errorObject = JSError(from: (error1 as! JSException).thrownValue)
-    try expectNotNil(errorObject)
-
-    let error2 = try expectThrow(try prop_9.object!.throwing.func2!())
-    try expectEqual(error2 is JSException, true)
-    let errorString = try expectString((error2 as! JSException).thrownValue)
-    try expectEqual(errorString, "String Error")
-
-    let error3 = try expectThrow(try prop_9.object!.throwing.func3!())
-    try expectEqual(error3 is JSException, true)
-    let errorNumber = try expectNumber((error3 as! JSException).thrownValue)
-    try expectEqual(errorNumber, 3.0)
-
-    // MARK: Simple function calls
-    let error4 = try expectThrow(try prop_9.func1.function!.throws())
-    try expectEqual(error4 is JSException, true)
-    let errorObject2 = JSError(from: (error4 as! JSException).thrownValue)
-    try expectNotNil(errorObject2)
-
-    // MARK: Throwing constructor call
-    let Animal = JSObject.global.Animal.function!
-    _ = try Animal.throws.new("Tama", 3, true)
-    let ageError = try expectThrow(try Animal.throws.new("Tama", -3, true))
-    try expectEqual(ageError is JSException, true)
-    let errorObject3 = JSError(from: (ageError as! JSException).thrownValue)
-    try expectNotNil(errorObject3)
-}
-
-try test("Unhandled Exception") {
-    // ```js
-    // global.globalObject1 = {
-    //   ...
-    //   prop_9: {
-    //       func1: function () {
-    //           throw new Error();
-    //       },
-    //       func2: function () {
-    //           throw "String Error";
-    //       },
-    //       func3: function () {
-    //           throw 3.0
-    //       },
-    //   },
-    //   ...
-    // }
-    // ```
-    //
-
-    let globalObject1 = JSObject.global.globalObject1
-    let prop_9: JSValue = globalObject1.prop_9
-
-    // MARK: Throwing method calls
-    let error1 = try wrapUnsafeThrowableFunction { _ = prop_9.object!.func1!() }
-    let errorObject = JSError(from: error1)
-    try expectNotNil(errorObject)
-
-    let error2 = try wrapUnsafeThrowableFunction { _ = prop_9.object!.func2!() }
-    let errorString = try expectString(error2)
-    try expectEqual(errorString, "String Error")
-
-    let error3 = try wrapUnsafeThrowableFunction { _ = prop_9.object!.func3!() }
-    let errorNumber = try expectNumber(error3)
-    try expectEqual(errorNumber, 3.0)
-}
-
-/// If WebAssembly.Memory is not accessed correctly (i.e. creating a new view each time),
-/// this test will fail with `TypeError: Cannot perform Construct on a detached ArrayBuffer`,
-/// since asking to grow memory will detach the backing ArrayBuffer.
-/// See https://github.com/swiftwasm/JavaScriptKit/pull/153
-try test("Grow Memory") {
-    let string = "Hello"
-    let jsString = JSValue.string(string)
-    growMemory(1)
-    try expectEqual(string, jsString.description)
-}
-
-try test("Hashable Conformance") {
-    let globalObject1 = JSObject.global.console.object!
-    let globalObject2 = JSObject.global.console.object!
-    try expectEqual(globalObject1.hashValue, globalObject2.hashValue)
-    // These are 2 different objects in Swift referencing the same object in JavaScript
-    try expectNotEqual(ObjectIdentifier(globalObject1), ObjectIdentifier(globalObject2))
-
-    let sameObjectSet: Set<JSObject> = [globalObject1, globalObject2]
-    try expectEqual(sameObjectSet.count, 1)
-
-    let objectConstructor = JSObject.global.Object.function!
-    let obj = objectConstructor.new()
-    obj.a = 1.jsValue
-    let firstHash = obj.hashValue
-    obj.b = 2.jsValue
-    let secondHash = obj.hashValue
-    try expectEqual(firstHash, secondHash)
-}
-
-try test("Symbols") {
-    let symbol1 = JSSymbol("abc")
-    let symbol2 = JSSymbol("abc")
-    try expectNotEqual(symbol1, symbol2)
-    try expectEqual(symbol1.name, symbol2.name)
-    try expectEqual(symbol1.name, "abc")
-
-    try expectEqual(JSSymbol.iterator, JSSymbol.iterator)
-
-    // let hasInstanceClass = {
-    //   prop: function () {}
-    // }.prop
-    // Object.defineProperty(hasInstanceClass, Symbol.hasInstance, { value: () => true })
-    let hasInstanceObject = JSObject.global.Object.function!.new()
-    hasInstanceObject.prop = JSClosure { _ in .undefined }.jsValue
-    let hasInstanceClass = hasInstanceObject.prop.function!
-    let propertyDescriptor = JSObject.global.Object.function!.new()
-    propertyDescriptor.value = JSClosure { _ in .boolean(true) }.jsValue
-    _ = JSObject.global.Object.function!.defineProperty!(
-        hasInstanceClass, JSSymbol.hasInstance,
-        propertyDescriptor
-    )
-    try expectEqual(hasInstanceClass[JSSymbol.hasInstance].function!().boolean, true)
-    try expectEqual(JSObject.global.Object.isInstanceOf(hasInstanceClass), true)
-}
-
-struct AnimalStruct: Decodable {
-    let name: String
-    let age: Int
-    let isCat: Bool
-}
-
-try test("JSValueDecoder") {
-    let Animal = JSObject.global.Animal.function!
-    let tama = try Animal.throws.new("Tama", 3, true)
-    let decoder = JSValueDecoder()
-    let decodedTama = try decoder.decode(AnimalStruct.self, from: tama.jsValue)
-
-    try expectEqual(decodedTama.name, tama.name.string)
-    try expectEqual(decodedTama.name, "Tama")
-
-    try expectEqual(decodedTama.age, tama.age.number.map(Int.init))
-    try expectEqual(decodedTama.age, 3)
-
-    try expectEqual(decodedTama.isCat, tama.isCat.boolean)
-    try expectEqual(decodedTama.isCat, true)
-}
-
-try testI64()
-Expectation.wait(expectations)
diff --git a/IntegrationTests/bin/concurrency-tests.js b/IntegrationTests/bin/concurrency-tests.js
deleted file mode 100644
index 02489c959..000000000
--- a/IntegrationTests/bin/concurrency-tests.js
+++ /dev/null
@@ -1,8 +0,0 @@
-import { startWasiTask } from "../lib.js";
-
-Error.stackTraceLimit = Infinity;
-
-startWasiTask("./dist/ConcurrencyTests.wasm").catch((err) => {
-    console.log(err);
-    process.exit(1);
-});
diff --git a/IntegrationTests/bin/primary-tests.js b/IntegrationTests/bin/primary-tests.js
deleted file mode 100644
index 36ac65812..000000000
--- a/IntegrationTests/bin/primary-tests.js
+++ /dev/null
@@ -1,110 +0,0 @@
-Error.stackTraceLimit = Infinity;
-
-global.globalObject1 = {
-    prop_1: {
-        nested_prop: 1,
-    },
-    prop_2: 2,
-    prop_3: true,
-    prop_4: [3, 4, "str_elm_1", null, undefined, 5],
-    prop_5: {
-        func1: function () {
-            return;
-        },
-        func2: function () {
-            return 1;
-        },
-        func3: function (n) {
-            return n * 2;
-        },
-        func4: function (a, b, c) {
-            return a + b + c;
-        },
-        func5: function (x) {
-            return "Hello, " + x;
-        },
-        func6: function (c, a, b) {
-            if (c) {
-                return a;
-            } else {
-                return b;
-            }
-        },
-    },
-    prop_6: {
-        call_host_1: () => {
-            return global.globalObject1.prop_6.host_func_1();
-        },
-    },
-    prop_7: 3.14,
-    prop_8: [0, , 2, 3, , , 6],
-    prop_9: {
-        func1: function () {
-            throw new Error();
-        },
-        func2: function () {
-            throw "String Error";
-        },
-        func3: function () {
-            throw 3.0;
-        },
-    },
-    eval_closure: function (fn) {
-        return fn(arguments[1]);
-    },
-    observable_obj: {
-        set_called: false,
-        target: new Proxy(
-            {
-                nested: {},
-            },
-            {
-                set(target, key, value) {
-                    global.globalObject1.observable_obj.set_called = true;
-                    target[key] = value;
-                    return true;
-                },
-            }
-        ),
-    },
-};
-
-global.Animal = function (name, age, isCat) {
-    if (age < 0) {
-        throw new Error("Invalid age " + age);
-    }
-    this.name = name;
-    this.age = age;
-    this.bark = () => {
-        return isCat ? "nyan" : "wan";
-    };
-    this.isCat = isCat;
-    this.getIsCat = function () {
-        return this.isCat;
-    };
-    this.setName = function (name) {
-        this.name = name;
-    };
-};
-
-global.callThrowingClosure = (c) => {
-    try {
-        c();
-    } catch (error) {
-        return error;
-    }
-};
-
-global.objectDecodingTest = {
-    obj: {},
-    fn: () => {},
-    sym: Symbol("s"),
-    bi: BigInt(3)
-};
-
-import { startWasiTask } from "../lib.js";
-
-startWasiTask("./dist/PrimaryTests.wasm").catch((err) => {
-    console.log(err);
-    process.exit(1);
-});
diff --git a/IntegrationTests/lib.js b/IntegrationTests/lib.js
index a2f10e565..d9c424f0e 100644
--- a/IntegrationTests/lib.js
+++ b/IntegrationTests/lib.js
@@ -3,7 +3,6 @@ import { WASI as NodeWASI } from "wasi"
 import { WASI as MicroWASI, useAll } from "uwasi"
 import * as fs from "fs/promises"
 import path from "path";
-import { Worker, parentPort } from "node:worker_threads";
 
 const WASI = {
     MicroWASI: ({ args }) => {
@@ -53,16 +52,6 @@ const selectWASIBackend = () => {
     return "Node"
 };
 
-function isUsingSharedMemory(module) {
-    const imports = WebAssembly.Module.imports(module);
-    for (const entry of imports) {
-        if (entry.module === "env" && entry.name === "memory" && entry.kind == "memory") {
-            return true;
-        }
-    }
-    return false;
-}
-
 function constructBaseImportObject(wasi, swift) {
     return {
         wasi_snapshot_preview1: wasi.wasiImport,
@@ -74,79 +63,6 @@ function constructBaseImportObject(wasi, swift) {
     }
 }
 
-export async function startWasiChildThread(event) {
-    const { module, programName, memory, tid, startArg } = event;
-    const swift = new SwiftRuntime({
-        sharedMemory: true,
-        threadChannel: {
-            postMessageToMainThread: (message, transfer) => {
-                parentPort.postMessage(message, transfer);
-            },
-            listenMessageFromMainThread: (listener) => {
-                parentPort.on("message", listener)
-            }
-        }
-    });
-    // Use uwasi for child threads because Node.js WASI cannot be used without calling
-    // `WASI.start` or `WASI.initialize`, which is already called in the main thread and
-    // will cause an error if called again.
-    const wasi = WASI.MicroWASI({ programName });
-
-    const importObject = constructBaseImportObject(wasi, swift);
-
-    importObject["wasi"] = {
-        "thread-spawn": () => {
-            throw new Error("Cannot spawn a new thread from a worker thread")
-        }
-    };
-    importObject["env"] = { memory };
-    importObject["JavaScriptEventLoopTestSupportTests"] = {
-        "isMainThread": () => false,
-    }
-
-    const instance = await WebAssembly.instantiate(module, importObject);
-    swift.setInstance(instance);
-    wasi.setInstance(instance);
-    swift.startThread(tid, startArg);
-}
-
-class ThreadRegistry {
-    workers = new Map();
-    nextTid = 1;
-
-    spawnThread(module, programName, memory, startArg) {
-        const tid = this.nextTid++;
-        const selfFilePath = new URL(import.meta.url).pathname;
-        const worker = new Worker(`
-            const { parentPort } = require('node:worker_threads');
-
-            Error.stackTraceLimit = 100;
-            parentPort.once("message", async (event) => {
-                const { selfFilePath } = event;
-                const { startWasiChildThread } = await import(selfFilePath);
-                await startWasiChildThread(event);
-            })
-        `, { type: "module", eval: true })
-
-        worker.on("error", (error) => {
-            console.error(`Worker thread ${tid} error:`, error);
-            throw error;
-        });
-        this.workers.set(tid, worker);
-        worker.postMessage({ selfFilePath, module, programName, memory, tid, startArg });
-        return tid;
-    }
-
-    worker(tid) {
-        return this.workers.get(tid);
-    }
-
-    wakeUpWorkerThread(tid, message, transfer) {
-        const worker = this.workers.get(tid);
-        worker.postMessage(message, transfer);
-    }
-}
-
 export const startWasiTask = async (wasmPath, wasiConstructorKey = selectWASIBackend()) => {
     // Fetch our Wasm File
     const wasmBinary = await fs.readFile(wasmPath);
@@ -157,38 +73,10 @@ export const startWasiTask = async (wasmPath, wasiConstructorKey = selectWASIBac
 
     const module = await WebAssembly.compile(wasmBinary);
 
-    const sharedMemory = isUsingSharedMemory(module);
-    const threadRegistry = new ThreadRegistry();
-    const swift = new SwiftRuntime({
-        sharedMemory,
-        threadChannel: {
-            postMessageToWorkerThread: threadRegistry.wakeUpWorkerThread.bind(threadRegistry),
-            listenMessageFromWorkerThread: (tid, listener) => {
-                const worker = threadRegistry.worker(tid);
-                worker.on("message", listener);
-            }
-        }
-    });
+    const swift = new SwiftRuntime();
 
     const importObject = constructBaseImportObject(wasi, swift);
 
-    importObject["JavaScriptEventLoopTestSupportTests"] = {
-        "isMainThread": () => true,
-    }
-
-    if (sharedMemory) {
-        // We don't have JS API to get memory descriptor of imported memory
-        // at this moment, so we assume 256 pages (16MB) memory is enough
-        // large for initial memory size.
-        const memory = new WebAssembly.Memory({ initial: 1024, maximum: 16384, shared: true })
-        importObject["env"] = { memory };
-        importObject["wasi"] = {
-          "thread-spawn": (startArg) => {
-            return threadRegistry.spawnThread(module, programName, memory, startArg);
-          }
-        }
-    }
-
     // Instantiate the WebAssembly file
     const instance = await WebAssembly.instantiate(module, importObject);
 
diff --git a/Makefile b/Makefile
index ed0727ce8..c8b79b4ab 100644
--- a/Makefile
+++ b/Makefile
@@ -12,19 +12,16 @@ build:
 	swift build --triple wasm32-unknown-wasi
 	npm run build
 
-.PHONY: test
-test:
-	@echo Running integration tests
-	cd IntegrationTests && \
-	    CONFIGURATION=debug   SWIFT_BUILD_FLAGS="$(SWIFT_BUILD_FLAGS)" $(MAKE) test && \
-	    CONFIGURATION=debug   SWIFT_BUILD_FLAGS="$(SWIFT_BUILD_FLAGS) -Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS" $(MAKE) test && \
-	    CONFIGURATION=release SWIFT_BUILD_FLAGS="$(SWIFT_BUILD_FLAGS)" $(MAKE) test && \
-	    CONFIGURATION=release SWIFT_BUILD_FLAGS="$(SWIFT_BUILD_FLAGS) -Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS" $(MAKE) test
-
 .PHONY: unittest
 unittest:
 	@echo Running unit tests
-	swift package --swift-sdk "$(SWIFT_SDK_ID)" js test --prelude ./Tests/prelude.mjs
+	swift package --swift-sdk "$(SWIFT_SDK_ID)" \
+	    --disable-sandbox \
+		-Xlinker --stack-first \
+		-Xlinker --global-base=524288 \
+		-Xlinker -z \
+		-Xlinker stack-size=524288 \
+		js test --prelude ./Tests/prelude.mjs
 
 .PHONY: benchmark_setup
 benchmark_setup:
diff --git a/Package.swift b/Package.swift
index 173add2dd..9b8e1ca38 100644
--- a/Package.swift
+++ b/Package.swift
@@ -34,7 +34,10 @@ let package = Package(
         .target(name: "_CJavaScriptKit"),
         .testTarget(
             name: "JavaScriptKitTests",
-            dependencies: ["JavaScriptKit"]
+            dependencies: ["JavaScriptKit"],
+            swiftSettings: [
+                .enableExperimentalFeature("Extern")
+            ]
         ),
 
         .target(
@@ -42,6 +45,10 @@ let package = Package(
             dependencies: ["_CJavaScriptBigIntSupport", "JavaScriptKit"]
         ),
         .target(name: "_CJavaScriptBigIntSupport", dependencies: ["_CJavaScriptKit"]),
+        .testTarget(
+            name: "JavaScriptBigIntSupportTests",
+            dependencies: ["JavaScriptBigIntSupport", "JavaScriptKit"]
+        ),
 
         .target(
             name: "JavaScriptEventLoop",
diff --git a/Tests/JavaScriptBigIntSupportTests/JavaScriptBigIntSupportTests.swift b/Tests/JavaScriptBigIntSupportTests/JavaScriptBigIntSupportTests.swift
new file mode 100644
index 000000000..e1fb8a96f
--- /dev/null
+++ b/Tests/JavaScriptBigIntSupportTests/JavaScriptBigIntSupportTests.swift
@@ -0,0 +1,50 @@
+import XCTest
+import JavaScriptBigIntSupport
+import JavaScriptKit
+
+class JavaScriptBigIntSupportTests: XCTestCase {
+    func testBigIntSupport() {
+        // Test signed values
+        func testSignedValue(_ value: Int64, file: StaticString = #filePath, line: UInt = #line) {
+            let bigInt = JSBigInt(value)
+            XCTAssertEqual(bigInt.description, value.description, file: file, line: line)
+            let bigInt2 = JSBigInt(_slowBridge: value)
+            XCTAssertEqual(bigInt2.description, value.description, file: file, line: line)
+        }
+        
+        // Test unsigned values
+        func testUnsignedValue(_ value: UInt64, file: StaticString = #filePath, line: UInt = #line) {
+            let bigInt = JSBigInt(unsigned: value)
+            XCTAssertEqual(bigInt.description, value.description, file: file, line: line)
+            let bigInt2 = JSBigInt(_slowBridge: value)
+            XCTAssertEqual(bigInt2.description, value.description, file: file, line: line)
+        }
+        
+        // Test specific signed values
+        testSignedValue(0)
+        testSignedValue(1 << 62)
+        testSignedValue(-2305)
+        
+        // Test random signed values
+        for _ in 0..<100 {
+            testSignedValue(.random(in: .min ... .max))
+        }
+        
+        // Test edge signed values
+        testSignedValue(.min)
+        testSignedValue(.max)
+        
+        // Test specific unsigned values
+        testUnsignedValue(0)
+        testUnsignedValue(1 << 62)
+        testUnsignedValue(1 << 63)
+        testUnsignedValue(.min)
+        testUnsignedValue(.max)
+        testUnsignedValue(~0)
+        
+        // Test random unsigned values
+        for _ in 0..<100 {
+            testUnsignedValue(.random(in: .min ... .max))
+        }
+    }
+}
diff --git a/Tests/JavaScriptEventLoopTests/JSPromiseTests.swift b/Tests/JavaScriptEventLoopTests/JSPromiseTests.swift
new file mode 100644
index 000000000..e19d356e5
--- /dev/null
+++ b/Tests/JavaScriptEventLoopTests/JSPromiseTests.swift
@@ -0,0 +1,97 @@
+import XCTest
+@testable import JavaScriptKit
+
+final class JSPromiseTests: XCTestCase {
+    func testPromiseThen() async throws {
+        var p1 = JSPromise.resolve(JSValue.null)
+        await withCheckedContinuation { continuation in
+            p1 = p1.then { value in
+                XCTAssertEqual(value, .null)
+                continuation.resume()
+                return JSValue.number(1.0)
+            }
+        }
+        await withCheckedContinuation { continuation in
+            p1 = p1.then { value in
+                XCTAssertEqual(value, .number(1.0))
+                continuation.resume()
+                return JSPromise.resolve(JSValue.boolean(true))
+            }
+        }
+        await withCheckedContinuation { continuation in
+            p1 = p1.then { value in
+                XCTAssertEqual(value, .boolean(true))
+                continuation.resume()
+                return JSValue.undefined
+            }
+        }
+        await withCheckedContinuation { continuation in
+            p1 = p1.catch { error in
+                XCTFail("Not fired due to no throw")
+                return JSValue.undefined
+            }
+            .finally { continuation.resume() }
+        }
+    }
+
+    func testPromiseCatch() async throws {
+        var p2 = JSPromise.reject(JSValue.boolean(false))
+        await withCheckedContinuation { continuation in
+            p2 = p2.catch { error in
+                XCTAssertEqual(error, .boolean(false))
+                continuation.resume()
+                return JSValue.boolean(true)
+            }
+        }
+        await withCheckedContinuation { continuation in
+            p2 = p2.then { value in
+                XCTAssertEqual(value, .boolean(true))
+                continuation.resume()
+                return JSPromise.reject(JSValue.number(2.0))
+            }
+        }
+        await withCheckedContinuation { continuation in
+            p2 = p2.catch { error in
+                XCTAssertEqual(error, .number(2.0))
+                continuation.resume()
+                return JSValue.undefined
+            }
+        }
+        await withCheckedContinuation { continuation in
+            p2 = p2.finally { continuation.resume() }
+        }
+    }
+
+    func testPromiseAndTimer() async throws {
+        let start = JSDate().valueOf()
+        let timeoutMilliseconds = 5.0
+        var timer: JSTimer?
+
+        var p3: JSPromise?
+        await withCheckedContinuation { continuation in
+            p3 = JSPromise { resolve in
+                timer = JSTimer(millisecondsDelay: timeoutMilliseconds) {
+                    continuation.resume()
+                    resolve(.success(.undefined))
+                }
+            }
+        }
+
+        await withCheckedContinuation { continuation in
+            p3?.then { _ in
+                XCTAssertEqual(start + timeoutMilliseconds <= JSDate().valueOf(), true)
+                continuation.resume()
+                return JSValue.undefined
+            }
+        }
+
+        // Ensure that users don't need to manage JSPromise lifetime
+        await withCheckedContinuation { continuation in
+            JSPromise.resolve(JSValue.boolean(true)).then { _ in
+                continuation.resume()
+                return JSValue.undefined
+            }
+        }
+        withExtendedLifetime(timer) {}
+    }
+}
diff --git a/Tests/JavaScriptEventLoopTests/JSTimerTests.swift b/Tests/JavaScriptEventLoopTests/JSTimerTests.swift
new file mode 100644
index 000000000..2ee92cebd
--- /dev/null
+++ b/Tests/JavaScriptEventLoopTests/JSTimerTests.swift
@@ -0,0 +1,56 @@
+import XCTest
+
+@testable import JavaScriptKit
+
+final class JSTimerTests: XCTestCase {
+
+    func testOneshotTimerCancelled() {
+        let timeoutMilliseconds = 5.0
+        var timeout: JSTimer!
+        timeout = JSTimer(millisecondsDelay: timeoutMilliseconds, isRepeating: false) {
+            XCTFail("timer should be cancelled")
+        }
+        _ = timeout
+        timeout = nil
+    }
+
+    func testRepeatingTimerCancelled() async throws {
+        var count = 0.0
+        let maxCount = 5.0
+        var interval: JSTimer?
+        let start = JSDate().valueOf()
+        let timeoutMilliseconds = 5.0
+
+        await withCheckedContinuation { continuation in
+            interval = JSTimer(millisecondsDelay: 5, isRepeating: true) {
+                // ensure that JSTimer is living
+                XCTAssertNotNil(interval)
+                // verify that at least `timeoutMilliseconds * count` passed since the `timeout`
+                // timer started
+                XCTAssertTrue(start + timeoutMilliseconds * count <= JSDate().valueOf())
+
+                guard count < maxCount else {
+                    // stop the timer after `maxCount` reached
+                    interval = nil
+                    continuation.resume()
+                    return
+                }
+
+                count += 1
+            }
+        }
+        withExtendedLifetime(interval) {}
+    }
+
+    func testTimer() async throws {
+        let start = JSDate().valueOf()
+        let timeoutMilliseconds = 5.0
+        var timeout: JSTimer!
+        await withCheckedContinuation { continuation in
+            timeout = JSTimer(millisecondsDelay: timeoutMilliseconds, isRepeating: false) {
+                continuation.resume()
+            }
+        }
+        withExtendedLifetime(timeout) {}
+    }
+}
diff --git a/Tests/JavaScriptEventLoopTests/JavaScriptEventLoopTests.swift b/Tests/JavaScriptEventLoopTests/JavaScriptEventLoopTests.swift
new file mode 100644
index 000000000..40eb96af0
--- /dev/null
+++ b/Tests/JavaScriptEventLoopTests/JavaScriptEventLoopTests.swift
@@ -0,0 +1,260 @@
+import JavaScriptEventLoop
+import JavaScriptKit
+import XCTest
+
+final class JavaScriptEventLoopTests: XCTestCase {
+    // Helper utilities for testing
+    struct MessageError: Error {
+        let message: String
+        let file: StaticString
+        let line: UInt
+        let column: UInt
+        init(_ message: String, file: StaticString, line: UInt, column: UInt) {
+            self.message = message
+            self.file = file
+            self.line = line
+            self.column = column
+        }
+    }
+
+    func expectAsyncThrow<T>(
+        _ body: @autoclosure () async throws -> T, file: StaticString = #file, line: UInt = #line,
+        column: UInt = #column
+    ) async throws -> Error {
+        do {
+            _ = try await body()
+        } catch {
+            return error
+        }
+        throw MessageError("Expect to throw an exception", file: file, line: line, column: column)
+    }
+
+    func performanceNow() -> Double {
+        return JSObject.global.performance.now().number!
+    }
+
+    func measureTime(_ block: () async throws -> Void) async rethrows -> Double {
+        let start = performanceNow()
+        try await block()
+        return performanceNow() - start
+    }
+
+    // Error type used in tests
+    struct E: Error, Equatable {
+        let value: Int
+    }
+
+    // MARK: - Task Tests
+
+    func testTaskInit() async throws {
+        // Test Task.init value
+        let handle = Task { 1 }
+        let value = await handle.value
+        XCTAssertEqual(value, 1)
+    }
+
+    func testTaskInitThrows() async throws {
+        // Test Task.init throws
+        let throwingHandle = Task {
+            throw E(value: 2)
+        }
+        let error = try await expectAsyncThrow(await throwingHandle.value)
+        let e = try XCTUnwrap(error as? E)
+        XCTAssertEqual(e, E(value: 2))
+    }
+
+    func testTaskSleep() async throws {
+        // Test Task.sleep(_:)
+        let sleepDiff = try await measureTime {
+            try await Task.sleep(nanoseconds: 200_000_000)
+        }
+        XCTAssertGreaterThanOrEqual(sleepDiff, 200)
+
+        // Test shorter sleep duration
+        let shortSleepDiff = try await measureTime {
+            try await Task.sleep(nanoseconds: 100_000_000)
+        }
+        XCTAssertGreaterThanOrEqual(shortSleepDiff, 100)
+    }
+
+    func testTaskPriority() async throws {
+        // Test Job reordering based on priority
+        class Context: @unchecked Sendable {
+            var completed: [String] = []
+        }
+        let context = Context()
+
+        // When no priority, they should be ordered by the enqueued order
+        let t1 = Task(priority: nil) {
+            context.completed.append("t1")
+        }
+        let t2 = Task(priority: nil) {
+            context.completed.append("t2")
+        }
+
+        _ = await (t1.value, t2.value)
+        XCTAssertEqual(context.completed, ["t1", "t2"])
+
+        context.completed = []
+        // When high priority is enqueued after a low one, they should be re-ordered
+        let t3 = Task(priority: .low) {
+            context.completed.append("t3")
+        }
+        let t4 = Task(priority: .high) {
+            context.completed.append("t4")
+        }
+        let t5 = Task(priority: .low) {
+            context.completed.append("t5")
+        }
+
+        _ = await (t3.value, t4.value, t5.value)
+        XCTAssertEqual(context.completed, ["t4", "t3", "t5"])
+    }
+
+    // MARK: - Promise Tests
+
+    func testPromiseResolution() async throws {
+        // Test await resolved Promise
+        let p = JSPromise(resolver: { resolve in
+            resolve(.success(1))
+        })
+        let resolutionValue = try await p.value
+        XCTAssertEqual(resolutionValue, .number(1))
+        let resolutionResult = await p.result
+        XCTAssertEqual(resolutionResult, .success(.number(1)))
+    }
+
+    func testPromiseRejection() async throws {
+        // Test await rejected Promise
+        let rejectedPromise = JSPromise(resolver: { resolve in
+            resolve(.failure(.number(3)))
+        })
+        let promiseError = try await expectAsyncThrow(try await rejectedPromise.value)
+        let jsValue = try XCTUnwrap(promiseError as? JSException).thrownValue
+        XCTAssertEqual(jsValue, .number(3))
+        let rejectionResult = await rejectedPromise.result
+        XCTAssertEqual(rejectionResult, .failure(.number(3)))
+    }
+
+    func testPromiseThen() async throws {
+        // Test Async JSPromise: then
+        let promise = JSPromise { resolve in
+            _ = JSObject.global.setTimeout!(
+                JSClosure { _ in
+                    resolve(.success(JSValue.number(3)))
+                    return .undefined
+                }.jsValue,
+                100
+            )
+        }
+        let promise2 = promise.then { result in
+            try await Task.sleep(nanoseconds: 100_000_000)
+            return String(result.number!)
+        }
+        let thenDiff = try await measureTime {
+            let result = try await promise2.value
+            XCTAssertEqual(result, .string("3.0"))
+        }
+        XCTAssertGreaterThanOrEqual(thenDiff, 200)
+    }
+
+    func testPromiseThenWithFailure() async throws {
+        // Test Async JSPromise: then(success:failure:)
+        let failingPromise = JSPromise { resolve in
+            _ = JSObject.global.setTimeout!(
+                JSClosure { _ in
+                    resolve(.failure(JSError(message: "test").jsValue))
+                    return .undefined
+                }.jsValue,
+                100
+            )
+        }
+        let failingPromise2 = failingPromise.then { _ in
+            throw MessageError("Should not be called", file: #file, line: #line, column: #column)
+        } failure: { err in
+            return err
+        }
+        let failingResult = try await failingPromise2.value
+        XCTAssertEqual(failingResult.object?.message, .string("test"))
+    }
+
+    func testPromiseCatch() async throws {
+        // Test Async JSPromise: catch
+        let catchPromise = JSPromise { resolve in
+            _ = JSObject.global.setTimeout!(
+                JSClosure { _ in
+                    resolve(.failure(JSError(message: "test").jsValue))
+                    return .undefined
+                }.jsValue,
+                100
+            )
+        }
+        let catchPromise2 = catchPromise.catch { err in
+            try await Task.sleep(nanoseconds: 100_000_000)
+            return err
+        }
+        let catchDiff = try await measureTime {
+            let result = try await catchPromise2.value
+            XCTAssertEqual(result.object?.message, .string("test"))
+        }
+        XCTAssertGreaterThanOrEqual(catchDiff, 200)
+    }
+
+    // MARK: - Continuation Tests
+
+    func testContinuation() async throws {
+        // Test Continuation
+        let continuationValue = await withUnsafeContinuation { cont in
+            cont.resume(returning: 1)
+        }
+        XCTAssertEqual(continuationValue, 1)
+
+        let continuationError = try await expectAsyncThrow(
+            try await withUnsafeThrowingContinuation { (cont: UnsafeContinuation<Never, Error>) in
+                cont.resume(throwing: E(value: 2))
+            }
+        )
+        let errorValue = try XCTUnwrap(continuationError as? E)
+        XCTAssertEqual(errorValue.value, 2)
+    }
+
+    // MARK: - JSClosure Tests
+
+    func testAsyncJSClosure() async throws {
+        // Test Async JSClosure
+        let delayClosure = JSClosure.async { _ -> JSValue in
+            try await Task.sleep(nanoseconds: 200_000_000)
+            return JSValue.number(3)
+        }
+        let delayObject = JSObject.global.Object.function!.new()
+        delayObject.closure = delayClosure.jsValue
+
+        let closureDiff = try await measureTime {
+            let promise = JSPromise(from: delayObject.closure!())
+            XCTAssertNotNil(promise)
+            let result = try await promise!.value
+            XCTAssertEqual(result, .number(3))
+        }
+        XCTAssertGreaterThanOrEqual(closureDiff, 200)
+    }
+
+    // MARK: - Clock Tests
+
+    #if compiler(>=5.7)
+        func testClockSleep() async throws {
+            // Test ContinuousClock.sleep
+            let continuousClockDiff = try await measureTime {
+                let c = ContinuousClock()
+                try await c.sleep(until: .now + .milliseconds(100))
+            }
+            XCTAssertGreaterThanOrEqual(continuousClockDiff, 99)
+
+            // Test SuspendingClock.sleep
+            let suspendingClockDiff = try await measureTime {
+                let c = SuspendingClock()
+                try await c.sleep(until: .now + .milliseconds(100))
+            }
+            XCTAssertGreaterThanOrEqual(suspendingClockDiff, 99)
+        }
+    #endif
+}
diff --git a/Tests/JavaScriptKitTests/JSTypedArrayTests.swift b/Tests/JavaScriptKitTests/JSTypedArrayTests.swift
index 87b81ae16..8e2556f8d 100644
--- a/Tests/JavaScriptKitTests/JSTypedArrayTests.swift
+++ b/Tests/JavaScriptKitTests/JSTypedArrayTests.swift
@@ -1,5 +1,5 @@
-import XCTest
 import JavaScriptKit
+import XCTest
 
 final class JSTypedArrayTests: XCTestCase {
     func testEmptyArray() {
@@ -15,4 +15,86 @@ final class JSTypedArrayTests: XCTestCase {
         _ = JSTypedArray<Float32>([Float32]())
         _ = JSTypedArray<Float64>([Float64]())
     }
+
+    func testTypedArray() {
+        func checkArray<T>(_ array: [T]) where T: TypedArrayElement & Equatable {
+            XCTAssertEqual(toString(JSTypedArray(array).jsValue.object!), jsStringify(array))
+            checkArrayUnsafeBytes(array)
+        }
+
+        func toString<T: JSObject>(_ object: T) -> String {
+            return object.toString!().string!
+        }
+
+        func jsStringify(_ array: [Any]) -> String {
+            array.map({ String(describing: $0) }).joined(separator: ",")
+        }
+
+        func checkArrayUnsafeBytes<T>(_ array: [T]) where T: TypedArrayElement & Equatable {
+            let copyOfArray: [T] = JSTypedArray(array).withUnsafeBytes { buffer in
+                Array(buffer)
+            }
+            XCTAssertEqual(copyOfArray, array)
+        }
+
+        let numbers = [UInt8](0...255)
+        let typedArray = JSTypedArray(numbers)
+        XCTAssertEqual(typedArray[12], 12)
+        XCTAssertEqual(numbers.count, typedArray.lengthInBytes)
+
+        let numbersSet = Set(0...255)
+        let typedArrayFromSet = JSTypedArray(numbersSet)
+        XCTAssertEqual(typedArrayFromSet.jsObject.length, 256)
+        XCTAssertEqual(typedArrayFromSet.lengthInBytes, 256 * MemoryLayout<Int>.size)
+
+        checkArray([0, .max, 127, 1] as [UInt8])
+        checkArray([0, 1, .max, .min, -1] as [Int8])
+
+        checkArray([0, .max, 255, 1] as [UInt16])
+        checkArray([0, 1, .max, .min, -1] as [Int16])
+
+        checkArray([0, .max, 255, 1] as [UInt32])
+        checkArray([0, 1, .max, .min, -1] as [Int32])
+
+        checkArray([0, .max, 255, 1] as [UInt])
+        checkArray([0, 1, .max, .min, -1] as [Int])
+
+        let float32Array: [Float32] = [
+            0, 1, .pi, .greatestFiniteMagnitude, .infinity, .leastNonzeroMagnitude,
+            .leastNormalMagnitude, 42,
+        ]
+        let jsFloat32Array = JSTypedArray(float32Array)
+        for (i, num) in float32Array.enumerated() {
+            XCTAssertEqual(num, jsFloat32Array[i])
+        }
+
+        let float64Array: [Float64] = [
+            0, 1, .pi, .greatestFiniteMagnitude, .infinity, .leastNonzeroMagnitude,
+            .leastNormalMagnitude, 42,
+        ]
+        let jsFloat64Array = JSTypedArray(float64Array)
+        for (i, num) in float64Array.enumerated() {
+            XCTAssertEqual(num, jsFloat64Array[i])
+        }
+    }
+
+    func testTypedArrayMutation() {
+        let array = JSTypedArray<Int>(length: 100)
+        for i in 0..<100 {
+            array[i] = i
+        }
+        for i in 0..<100 {
+            XCTAssertEqual(i, array[i])
+        }
+
+        func toString<T: JSObject>(_ object: T) -> String {
+            return object.toString!().string!
+        }
+
+        func jsStringify(_ array: [Any]) -> String {
+            array.map({ String(describing: $0) }).joined(separator: ",")
+        }
+
+        XCTAssertEqual(toString(array.jsValue.object!), jsStringify(Array(0..<100)))
+    }
 }
diff --git a/Tests/JavaScriptKitTests/JavaScriptKitTests.swift b/Tests/JavaScriptKitTests/JavaScriptKitTests.swift
new file mode 100644
index 000000000..6c90afead
--- /dev/null
+++ b/Tests/JavaScriptKitTests/JavaScriptKitTests.swift
@@ -0,0 +1,674 @@
+import XCTest
+import JavaScriptKit
+
+class JavaScriptKitTests: XCTestCase {
+    func testLiteralConversion() {
+        let global = JSObject.global
+        let inputs: [JSValue] = [
+            .boolean(true),
+            .boolean(false),
+            .string("foobar"),
+            .string("๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง Family Emoji"),
+            .number(0),
+            .number(Double(Int32.max)),
+            .number(Double(Int32.min)),
+            .number(Double.infinity),
+            .number(Double.nan),
+            .null,
+            .undefined,
+        ]
+        for (index, input) in inputs.enumerated() {
+            let prop = JSString("prop_\(index)")
+            setJSValue(this: global, name: prop, value: input)
+            let got = getJSValue(this: global, name: prop)
+            switch (got, input) {
+            case let (.number(lhs), .number(rhs)):
+                // Compare bitPattern because nan == nan is always false
+                XCTAssertEqual(lhs.bitPattern, rhs.bitPattern)
+            default:
+                XCTAssertEqual(got, input)
+            }
+        }
+    }
+    
+    func testObjectConversion() {
+        // Notes: globalObject1 is defined in JavaScript environment
+        //
+        // ```js
+        // global.globalObject1 = {
+        //   "prop_1": {
+        //     "nested_prop": 1,
+        //   },
+        //   "prop_2": 2,
+        //   "prop_3": true,
+        //   "prop_4": [
+        //     3, 4, "str_elm_1", 5,
+        //   ],
+        //   ...
+        // }
+        // ```
+        
+        let globalObject1 = getJSValue(this: .global, name: "globalObject1")
+        let globalObject1Ref = try! XCTUnwrap(globalObject1.object)
+        let prop_1 = getJSValue(this: globalObject1Ref, name: "prop_1")
+        let prop_1Ref = try! XCTUnwrap(prop_1.object)
+        let nested_prop = getJSValue(this: prop_1Ref, name: "nested_prop")
+        XCTAssertEqual(nested_prop, .number(1))
+        let prop_2 = getJSValue(this: globalObject1Ref, name: "prop_2")
+        XCTAssertEqual(prop_2, .number(2))
+        let prop_3 = getJSValue(this: globalObject1Ref, name: "prop_3")
+        XCTAssertEqual(prop_3, .boolean(true))
+        let prop_4 = getJSValue(this: globalObject1Ref, name: "prop_4")
+        let prop_4Array = try! XCTUnwrap(prop_4.object)
+        let expectedProp_4: [JSValue] = [
+            .number(3), .number(4), .string("str_elm_1"), .null, .undefined, .number(5),
+        ]
+        for (index, expectedElement) in expectedProp_4.enumerated() {
+            let actualElement = getJSValue(this: prop_4Array, index: Int32(index))
+            XCTAssertEqual(actualElement, expectedElement)
+        }
+        
+        XCTAssertEqual(getJSValue(this: globalObject1Ref, name: "undefined_prop"), .undefined)
+    }
+    
+    func testValueConstruction() {
+        let globalObject1 = getJSValue(this: .global, name: "globalObject1")
+        let globalObject1Ref = try! XCTUnwrap(globalObject1.object)
+        let prop_2 = getJSValue(this: globalObject1Ref, name: "prop_2")
+        XCTAssertEqual(Int.construct(from: prop_2), 2)
+        let prop_3 = getJSValue(this: globalObject1Ref, name: "prop_3")
+        XCTAssertEqual(Bool.construct(from: prop_3), true)
+        let prop_7 = getJSValue(this: globalObject1Ref, name: "prop_7")
+        XCTAssertEqual(Double.construct(from: prop_7), 3.14)
+        XCTAssertEqual(Float.construct(from: prop_7), 3.14)
+        
+        for source: JSValue in [
+            .number(.infinity), .number(.nan),
+            .number(Double(UInt64.max).nextUp), .number(Double(Int64.min).nextDown)
+        ] {
+            XCTAssertNil(Int.construct(from: source))
+            XCTAssertNil(Int8.construct(from: source))
+            XCTAssertNil(Int16.construct(from: source))
+            XCTAssertNil(Int32.construct(from: source))
+            XCTAssertNil(Int64.construct(from: source))
+            XCTAssertNil(UInt.construct(from: source))
+            XCTAssertNil(UInt8.construct(from: source))
+            XCTAssertNil(UInt16.construct(from: source))
+            XCTAssertNil(UInt32.construct(from: source))
+            XCTAssertNil(UInt64.construct(from: source))
+        }
+    }
+    
+    func testArrayIterator() {
+        let globalObject1 = getJSValue(this: .global, name: "globalObject1")
+        let globalObject1Ref = try! XCTUnwrap(globalObject1.object)
+        let prop_4 = getJSValue(this: globalObject1Ref, name: "prop_4")
+        let array1 = try! XCTUnwrap(prop_4.array)
+        let expectedProp_4: [JSValue] = [
+            .number(3), .number(4), .string("str_elm_1"), .null, .undefined, .number(5),
+        ]
+        XCTAssertEqual(Array(array1), expectedProp_4)
+        
+        // Ensure that iterator skips empty hole as JavaScript does.
+        let prop_8 = getJSValue(this: globalObject1Ref, name: "prop_8")
+        let array2 = try! XCTUnwrap(prop_8.array)
+        let expectedProp_8: [JSValue] = [0, 2, 3, 6]
+        XCTAssertEqual(Array(array2), expectedProp_8)
+    }
+    
+    func testArrayRandomAccessCollection() {
+        let globalObject1 = getJSValue(this: .global, name: "globalObject1")
+        let globalObject1Ref = try! XCTUnwrap(globalObject1.object)
+        let prop_4 = getJSValue(this: globalObject1Ref, name: "prop_4")
+        let array1 = try! XCTUnwrap(prop_4.array)
+        let expectedProp_4: [JSValue] = [
+            .number(3), .number(4), .string("str_elm_1"), .null, .undefined, .number(5),
+        ]
+        XCTAssertEqual([array1[0], array1[1], array1[2], array1[3], array1[4], array1[5]], expectedProp_4)
+        
+        // Ensure that subscript can access empty hole
+        let prop_8 = getJSValue(this: globalObject1Ref, name: "prop_8")
+        let array2 = try! XCTUnwrap(prop_8.array)
+        let expectedProp_8: [JSValue] = [
+            0, .undefined, 2, 3, .undefined, .undefined, 6
+        ]
+        XCTAssertEqual([array2[0], array2[1], array2[2], array2[3], array2[4], array2[5], array2[6]], expectedProp_8)
+    }
+    
+    func testValueDecoder() {
+        struct GlobalObject1: Codable {
+            struct Prop1: Codable {
+                let nested_prop: Int
+            }
+            
+            let prop_1: Prop1
+            let prop_2: Int
+            let prop_3: Bool
+            let prop_7: Float
+        }
+        let decoder = JSValueDecoder()
+        let rawGlobalObject1 = getJSValue(this: .global, name: "globalObject1")
+        let globalObject1 = try! decoder.decode(GlobalObject1.self, from: rawGlobalObject1)
+        XCTAssertEqual(globalObject1.prop_1.nested_prop, 1)
+        XCTAssertEqual(globalObject1.prop_2, 2)
+        XCTAssertEqual(globalObject1.prop_3, true)
+        XCTAssertEqual(globalObject1.prop_7, 3.14)
+    }
+    
+    func testFunctionCall() {
+        // Notes: globalObject1 is defined in JavaScript environment
+        //
+        // ```js
+        // global.globalObject1 = {
+        //   ...
+        //   "prop_5": {
+        //     "func1": function () { return },
+        //     "func2": function () { return 1 },
+        //     "func3": function (n) { return n * 2 },
+        //     "func4": function (a, b, c) { return a + b + c },
+        //     "func5": function (x) { return "Hello, " + x },
+        //     "func6": function (c, a, b) {
+        //       if (c) { return a } else { return b }
+        //     },
+        //   }
+        //   ...
+        // }
+        // ```
+        
+        let globalObject1 = getJSValue(this: .global, name: "globalObject1")
+        let globalObject1Ref = try! XCTUnwrap(globalObject1.object)
+        let prop_5 = getJSValue(this: globalObject1Ref, name: "prop_5")
+        let prop_5Ref = try! XCTUnwrap(prop_5.object)
+        
+        let func1 = try! XCTUnwrap(getJSValue(this: prop_5Ref, name: "func1").function)
+        XCTAssertEqual(func1(), .undefined)
+        let func2 = try! XCTUnwrap(getJSValue(this: prop_5Ref, name: "func2").function)
+        XCTAssertEqual(func2(), .number(1))
+        let func3 = try! XCTUnwrap(getJSValue(this: prop_5Ref, name: "func3").function)
+        XCTAssertEqual(func3(2), .number(4))
+        let func4 = try! XCTUnwrap(getJSValue(this: prop_5Ref, name: "func4").function)
+        XCTAssertEqual(func4(2, 3, 4), .number(9))
+        XCTAssertEqual(func4(2, 3, 4, 5), .number(9))
+        let func5 = try! XCTUnwrap(getJSValue(this: prop_5Ref, name: "func5").function)
+        XCTAssertEqual(func5("World!"), .string("Hello, World!"))
+        let func6 = try! XCTUnwrap(getJSValue(this: prop_5Ref, name: "func6").function)
+        XCTAssertEqual(func6(true, 1, 2), .number(1))
+        XCTAssertEqual(func6(false, 1, 2), .number(2))
+        XCTAssertEqual(func6(true, "OK", 2), .string("OK"))
+    }
+    
+    func testClosureLifetime() {
+        let evalClosure = JSObject.global.globalObject1.eval_closure.function!
+        
+        do {
+            let c1 = JSClosure { arguments in
+                return arguments[0]
+            }
+            XCTAssertEqual(evalClosure(c1, JSValue.number(1.0)), .number(1.0))
+#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
+            c1.release()
+#endif
+        }
+        
+        do {
+            let array = JSObject.global.Array.function!.new()
+            let c1 = JSClosure { _ in .number(3) }
+            _ = array.push!(c1)
+            XCTAssertEqual(array[0].function!().number, 3.0)
+#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
+            c1.release()
+#endif
+        }
+        
+        do {
+            let c1 = JSClosure { _ in .undefined }
+            XCTAssertEqual(c1(), .undefined)
+        }
+
+        do {
+            let c1 = JSClosure { _ in .number(4) }
+            XCTAssertEqual(c1(), .number(4))
+        }
+    }
+    
+    func testHostFunctionRegistration() {
+        // ```js
+        // global.globalObject1 = {
+        //   ...
+        //   "prop_6": {
+        //     "call_host_1": function() {
+        //       return global.globalObject1.prop_6.host_func_1()
+        //     }
+        //   }
+        // }
+        // ```
+        let globalObject1 = getJSValue(this: .global, name: "globalObject1")
+        let globalObject1Ref = try! XCTUnwrap(globalObject1.object)
+        let prop_6 = getJSValue(this: globalObject1Ref, name: "prop_6")
+        let prop_6Ref = try! XCTUnwrap(prop_6.object)
+        
+        var isHostFunc1Called = false
+        let hostFunc1 = JSClosure { (_) -> JSValue in
+            isHostFunc1Called = true
+            return .number(1)
+        }
+        
+        setJSValue(this: prop_6Ref, name: "host_func_1", value: .object(hostFunc1))
+        
+        let call_host_1 = getJSValue(this: prop_6Ref, name: "call_host_1")
+        let call_host_1Func = try! XCTUnwrap(call_host_1.function)
+        XCTAssertEqual(call_host_1Func(), .number(1))
+        XCTAssertEqual(isHostFunc1Called, true)
+        
+#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
+        hostFunc1.release()
+#endif
+        
+        let evalClosure = JSObject.global.globalObject1.eval_closure.function!
+        let hostFunc2 = JSClosure { (arguments) -> JSValue in
+            if let input = arguments[0].number {
+                return .number(input * 2)
+            } else {
+                return .string(String(describing: arguments[0]))
+            }
+        }
+        
+        XCTAssertEqual(evalClosure(hostFunc2, 3), .number(6))
+        XCTAssertTrue(evalClosure(hostFunc2, true).string != nil)
+        
+#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
+        hostFunc2.release()
+#endif
+    }
+    
+    func testNewObjectConstruction() {
+        // ```js
+        // global.Animal = function(name, age, isCat) {
+        //   this.name = name
+        //   this.age = age
+        //   this.bark = () => {
+        //     return isCat ? "nyan" : "wan"
+        //   }
+        // }
+        // ```
+        let objectConstructor = try! XCTUnwrap(getJSValue(this: .global, name: "Animal").function)
+        let cat1 = objectConstructor.new("Tama", 3, true)
+        XCTAssertEqual(getJSValue(this: cat1, name: "name"), .string("Tama"))
+        XCTAssertEqual(getJSValue(this: cat1, name: "age"), .number(3))
+        XCTAssertEqual(cat1.isInstanceOf(objectConstructor), true)
+        XCTAssertEqual(cat1.isInstanceOf(try! XCTUnwrap(getJSValue(this: .global, name: "Array").function)), false)
+        let cat1Bark = try! XCTUnwrap(getJSValue(this: cat1, name: "bark").function)
+        XCTAssertEqual(cat1Bark(), .string("nyan"))
+        
+        let dog1 = objectConstructor.new("Pochi", 3, false)
+        let dog1Bark = try! XCTUnwrap(getJSValue(this: dog1, name: "bark").function)
+        XCTAssertEqual(dog1Bark(), .string("wan"))
+    }
+    
+    func testObjectDecoding() {
+        /* 
+         ```js
+         global.objectDecodingTest = {
+             obj: {},
+             fn: () => {},
+             sym: Symbol("s"),
+             bi: BigInt(3)
+         };
+         ```
+         */
+        let js: JSValue = JSObject.global.objectDecodingTest
+        
+        // I can't use regular name like `js.object` here
+        // cz its conflicting with case name and DML.
+        // so I use abbreviated names
+        let object: JSValue = js.obj
+        let function: JSValue = js.fn
+        let symbol: JSValue = js.sym
+        let bigInt: JSValue = js.bi
+        
+        XCTAssertNotNil(JSObject.construct(from: object))
+        XCTAssertEqual(JSObject.construct(from: function).map { $0 is JSFunction }, .some(true))
+        XCTAssertEqual(JSObject.construct(from: symbol).map { $0 is JSSymbol }, .some(true))
+        XCTAssertEqual(JSObject.construct(from: bigInt).map { $0 is JSBigInt }, .some(true))
+        
+        XCTAssertNil(JSFunction.construct(from: object))
+        XCTAssertNotNil(JSFunction.construct(from: function))
+        XCTAssertNil(JSFunction.construct(from: symbol))
+        XCTAssertNil(JSFunction.construct(from: bigInt))
+        
+        XCTAssertNil(JSSymbol.construct(from: object))
+        XCTAssertNil(JSSymbol.construct(from: function))
+        XCTAssertNotNil(JSSymbol.construct(from: symbol))
+        XCTAssertNil(JSSymbol.construct(from: bigInt))
+        
+        XCTAssertNil(JSBigInt.construct(from: object))
+        XCTAssertNil(JSBigInt.construct(from: function))
+        XCTAssertNil(JSBigInt.construct(from: symbol))
+        XCTAssertNotNil(JSBigInt.construct(from: bigInt))
+    }
+    
+    func testCallFunctionWithThis() {
+        // ```js
+        // global.Animal = function(name, age, isCat) {
+        //   this.name = name
+        //   this.age = age
+        //   this.bark = () => {
+        //     return isCat ? "nyan" : "wan"
+        //   }
+        //   this.isCat = isCat
+        //   this.getIsCat = function() {
+        //     return this.isCat
+        //   }
+        // }
+        // ```
+        let objectConstructor = try! XCTUnwrap(getJSValue(this: .global, name: "Animal").function)
+        let cat1 = objectConstructor.new("Tama", 3, true)
+        let cat1Value = JSValue.object(cat1)
+        let getIsCat = try! XCTUnwrap(getJSValue(this: cat1, name: "getIsCat").function)
+        let setName = try! XCTUnwrap(getJSValue(this: cat1, name: "setName").function)
+        
+        // Direct call without this
+        XCTAssertThrowsError(try getIsCat.throws())
+        
+        // Call with this
+        let gotIsCat = getIsCat(this: cat1)
+        XCTAssertEqual(gotIsCat, .boolean(true))
+        XCTAssertEqual(cat1.getIsCat!(), .boolean(true))
+        XCTAssertEqual(cat1Value.getIsCat(), .boolean(true))
+        
+        // Call with this and argument
+        setName(this: cat1, JSValue.string("Shiro"))
+        XCTAssertEqual(getJSValue(this: cat1, name: "name"), .string("Shiro"))
+        _ = cat1.setName!("Tora")
+        XCTAssertEqual(getJSValue(this: cat1, name: "name"), .string("Tora"))
+        _ = cat1Value.setName("Chibi")
+        XCTAssertEqual(getJSValue(this: cat1, name: "name"), .string("Chibi"))
+    }
+    
+    func testJSObjectConversion() {
+        let array1 = [1, 2, 3]
+        let jsArray1 = array1.jsValue.object!
+        XCTAssertEqual(jsArray1.length, .number(3))
+        XCTAssertEqual(jsArray1[0], .number(1))
+        XCTAssertEqual(jsArray1[1], .number(2))
+        XCTAssertEqual(jsArray1[2], .number(3))
+        
+        let array2: [ConvertibleToJSValue] = [1, "str", false]
+        let jsArray2 = array2.jsValue.object!
+        XCTAssertEqual(jsArray2.length, .number(3))
+        XCTAssertEqual(jsArray2[0], .number(1))
+        XCTAssertEqual(jsArray2[1], .string("str"))
+        XCTAssertEqual(jsArray2[2], .boolean(false))
+        _ = jsArray2.push!(5)
+        XCTAssertEqual(jsArray2.length, .number(4))
+        _ = jsArray2.push!(jsArray1)
+        
+        XCTAssertEqual(jsArray2[4], .object(jsArray1))
+        
+        let dict1: [String: JSValue] = [
+            "prop1": 1.jsValue,
+            "prop2": "foo".jsValue,
+        ]
+        let jsDict1 = dict1.jsValue.object!
+        XCTAssertEqual(jsDict1.prop1, .number(1))
+        XCTAssertEqual(jsDict1.prop2, .string("foo"))
+    }
+    
+    func testObjectRefLifetime() {
+        // ```js
+        // global.globalObject1 = {
+        //   "prop_1": {
+        //     "nested_prop": 1,
+        //   },
+        //   "prop_2": 2,
+        //   "prop_3": true,
+        //   "prop_4": [
+        //     3, 4, "str_elm_1", 5,
+        //   ],
+        //   ...
+        // }
+        // ```
+        
+        let evalClosure = JSObject.global.globalObject1.eval_closure.function!
+        let identity = JSClosure { $0[0] }
+        let ref1 = getJSValue(this: .global, name: "globalObject1").object!
+        let ref2 = evalClosure(identity, ref1).object!
+        XCTAssertEqual(ref1.prop_2, .number(2))
+        XCTAssertEqual(ref2.prop_2, .number(2))
+        
+#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
+        identity.release()
+#endif
+    }
+    
+    func testDate() {
+        let date1Milliseconds = JSDate.now()
+        let date1 = JSDate(millisecondsSinceEpoch: date1Milliseconds)
+        let date2 = JSDate(millisecondsSinceEpoch: date1.valueOf())
+        
+        XCTAssertEqual(date1.valueOf(), date2.valueOf())
+        XCTAssertEqual(date1.fullYear, date2.fullYear)
+        XCTAssertEqual(date1.month, date2.month)
+        XCTAssertEqual(date1.date, date2.date)
+        XCTAssertEqual(date1.day, date2.day)
+        XCTAssertEqual(date1.hours, date2.hours)
+        XCTAssertEqual(date1.minutes, date2.minutes)
+        XCTAssertEqual(date1.seconds, date2.seconds)
+        XCTAssertEqual(date1.milliseconds, date2.milliseconds)
+        XCTAssertEqual(date1.utcFullYear, date2.utcFullYear)
+        XCTAssertEqual(date1.utcMonth, date2.utcMonth)
+        XCTAssertEqual(date1.utcDate, date2.utcDate)
+        XCTAssertEqual(date1.utcDay, date2.utcDay)
+        XCTAssertEqual(date1.utcHours, date2.utcHours)
+        XCTAssertEqual(date1.utcMinutes, date2.utcMinutes)
+        XCTAssertEqual(date1.utcSeconds, date2.utcSeconds)
+        XCTAssertEqual(date1.utcMilliseconds, date2.utcMilliseconds)
+        XCTAssertEqual(date1, date2)
+        
+        let date3 = JSDate(millisecondsSinceEpoch: 0)
+        XCTAssertEqual(date3.valueOf(), 0)
+        XCTAssertEqual(date3.utcFullYear, 1970)
+        XCTAssertEqual(date3.utcMonth, 0)
+        XCTAssertEqual(date3.utcDate, 1)
+        // the epoch date was on Friday
+        XCTAssertEqual(date3.utcDay, 4)
+        XCTAssertEqual(date3.utcHours, 0)
+        XCTAssertEqual(date3.utcMinutes, 0)
+        XCTAssertEqual(date3.utcSeconds, 0)
+        XCTAssertEqual(date3.utcMilliseconds, 0)
+        XCTAssertEqual(date3.toISOString(), "1970-01-01T00:00:00.000Z")
+        
+        XCTAssertTrue(date3 < date1)
+    }
+    
+    func testError() {
+        let message = "test error"
+        let expectedDescription = "Error: test error"
+        let error = JSError(message: message)
+        XCTAssertEqual(error.name, "Error")
+        XCTAssertEqual(error.message, message)
+        XCTAssertEqual(error.description, expectedDescription)
+        XCTAssertFalse(error.stack?.isEmpty ?? true)
+        XCTAssertNil(JSError(from: .string("error"))?.description)
+        XCTAssertEqual(JSError(from: .object(error.jsObject))?.description, expectedDescription)
+    }
+    
+    func testJSValueAccessor() {
+        var globalObject1 = JSObject.global.globalObject1
+        XCTAssertEqual(globalObject1.prop_1.nested_prop, .number(1))
+        XCTAssertEqual(globalObject1.object!.prop_1.object!.nested_prop, .number(1))
+        
+        XCTAssertEqual(globalObject1.prop_4[0], .number(3))
+        XCTAssertEqual(globalObject1.prop_4[1], .number(4))
+        
+        let originalProp1 = globalObject1.prop_1.object!.nested_prop
+        globalObject1.prop_1.nested_prop = "bar"
+        XCTAssertEqual(globalObject1.prop_1.nested_prop, .string("bar"))
+        globalObject1.prop_1.nested_prop = originalProp1
+    }
+    
+    func testException() {
+        // ```js
+        // global.globalObject1 = {
+        //   ...
+        //   prop_9: {
+        //       func1: function () {
+        //           throw new Error();
+        //       },
+        //       func2: function () {
+        //           throw "String Error";
+        //       },
+        //       func3: function () {
+        //           throw 3.0
+        //       },
+        //   },
+        //   ...
+        // }
+        // ```
+        //
+        let globalObject1 = JSObject.global.globalObject1
+        let prop_9: JSValue = globalObject1.prop_9
+        
+        // MARK: Throwing method calls
+        XCTAssertThrowsError(try prop_9.object!.throwing.func1!()) { error in
+            XCTAssertTrue(error is JSException)
+            let errorObject = JSError(from: (error as! JSException).thrownValue)
+            XCTAssertNotNil(errorObject)
+        }
+        
+        XCTAssertThrowsError(try prop_9.object!.throwing.func2!()) { error in
+            XCTAssertTrue(error is JSException)
+            let thrownValue = (error as! JSException).thrownValue
+            XCTAssertEqual(thrownValue.string, "String Error")
+        }
+        
+        XCTAssertThrowsError(try prop_9.object!.throwing.func3!()) { error in
+            XCTAssertTrue(error is JSException)
+            let thrownValue = (error as! JSException).thrownValue
+            XCTAssertEqual(thrownValue.number, 3.0)
+        }
+        
+        // MARK: Simple function calls
+        XCTAssertThrowsError(try prop_9.func1.function!.throws()) { error in
+            XCTAssertTrue(error is JSException)
+            let errorObject = JSError(from: (error as! JSException).thrownValue)
+            XCTAssertNotNil(errorObject)
+        }
+        
+        // MARK: Throwing constructor call
+        let Animal = JSObject.global.Animal.function!
+        XCTAssertNoThrow(try Animal.throws.new("Tama", 3, true))
+        XCTAssertThrowsError(try Animal.throws.new("Tama", -3, true)) { error in
+            XCTAssertTrue(error is JSException)
+            let errorObject = JSError(from: (error as! JSException).thrownValue)
+            XCTAssertNotNil(errorObject)
+        }
+    }
+    
+    func testSymbols() {
+        let symbol1 = JSSymbol("abc")
+        let symbol2 = JSSymbol("abc")
+        XCTAssertNotEqual(symbol1, symbol2)
+        XCTAssertEqual(symbol1.name, symbol2.name)
+        XCTAssertEqual(symbol1.name, "abc")
+        
+        XCTAssertEqual(JSSymbol.iterator, JSSymbol.iterator)
+        
+        // let hasInstanceClass = {
+        //   prop: function () {}
+        // }.prop
+        // Object.defineProperty(hasInstanceClass, Symbol.hasInstance, { value: () => true })
+        let hasInstanceObject = JSObject.global.Object.function!.new()
+        hasInstanceObject.prop = JSClosure { _ in .undefined }.jsValue
+        let hasInstanceClass = hasInstanceObject.prop.function!
+        let propertyDescriptor = JSObject.global.Object.function!.new()
+        propertyDescriptor.value = JSClosure { _ in .boolean(true) }.jsValue
+        _ = JSObject.global.Object.function!.defineProperty!(
+            hasInstanceClass, JSSymbol.hasInstance,
+            propertyDescriptor
+        )
+        XCTAssertEqual(hasInstanceClass[JSSymbol.hasInstance].function!().boolean, true)
+        XCTAssertEqual(JSObject.global.Object.isInstanceOf(hasInstanceClass), true)
+    }
+    
+    func testJSValueDecoder() {
+        struct AnimalStruct: Decodable {
+            let name: String
+            let age: Int
+            let isCat: Bool
+        }
+        
+        let Animal = JSObject.global.Animal.function!
+        let tama = try! Animal.throws.new("Tama", 3, true)
+        let decoder = JSValueDecoder()
+        let decodedTama = try! decoder.decode(AnimalStruct.self, from: tama.jsValue)
+        
+        XCTAssertEqual(decodedTama.name, tama.name.string)
+        XCTAssertEqual(decodedTama.name, "Tama")
+        
+        XCTAssertEqual(decodedTama.age, tama.age.number.map(Int.init))
+        XCTAssertEqual(decodedTama.age, 3)
+        
+        XCTAssertEqual(decodedTama.isCat, tama.isCat.boolean)
+        XCTAssertEqual(decodedTama.isCat, true)
+    }
+    
+    func testConvertibleToJSValue() {
+        let array1 = [1, 2, 3]
+        let jsArray1 = array1.jsValue.object!
+        XCTAssertEqual(jsArray1.length, .number(3))
+        XCTAssertEqual(jsArray1[0], .number(1))
+        XCTAssertEqual(jsArray1[1], .number(2))
+        XCTAssertEqual(jsArray1[2], .number(3))
+        
+        let array2: [ConvertibleToJSValue] = [1, "str", false]
+        let jsArray2 = array2.jsValue.object!
+        XCTAssertEqual(jsArray2.length, .number(3))
+        XCTAssertEqual(jsArray2[0], .number(1))
+        XCTAssertEqual(jsArray2[1], .string("str"))
+        XCTAssertEqual(jsArray2[2], .boolean(false))
+        _ = jsArray2.push!(5)
+        XCTAssertEqual(jsArray2.length, .number(4))
+        _ = jsArray2.push!(jsArray1)
+        
+        XCTAssertEqual(jsArray2[4], .object(jsArray1))
+        
+        let dict1: [String: JSValue] = [
+            "prop1": 1.jsValue,
+            "prop2": "foo".jsValue,
+        ]
+        let jsDict1 = dict1.jsValue.object!
+        XCTAssertEqual(jsDict1.prop1, .number(1))
+        XCTAssertEqual(jsDict1.prop2, .string("foo"))
+    }
+
+    func testGrowMemory() {
+        // If WebAssembly.Memory is not accessed correctly (i.e. creating a new view each time),
+        // this test will fail with `TypeError: Cannot perform Construct on a detached ArrayBuffer`,
+        // since asking to grow memory will detach the backing ArrayBuffer.
+        // See https://github.com/swiftwasm/JavaScriptKit/pull/153
+        let string = "Hello"
+        let jsString = JSValue.string(string)
+        _ = growMemory(0, 1)
+        XCTAssertEqual(string, jsString.description)
+    }
+
+    func testHashableConformance() {
+        let globalObject1 = JSObject.global.console.object!
+        let globalObject2 = JSObject.global.console.object!
+        XCTAssertEqual(globalObject1.hashValue, globalObject2.hashValue)
+        // These are 2 different objects in Swift referencing the same object in JavaScript
+        XCTAssertNotEqual(ObjectIdentifier(globalObject1), ObjectIdentifier(globalObject2))
+
+        let objectConstructor = JSObject.global.Object.function!
+        let obj = objectConstructor.new()
+        obj.a = 1.jsValue
+        let firstHash = obj.hashValue
+        obj.b = 2.jsValue
+        let secondHash = obj.hashValue
+        XCTAssertEqual(firstHash, secondHash)
+    }
+}
+
+@_extern(c, "llvm.wasm.memory.grow.i32")
+func growMemory(_ memory: Int32, _ pages: Int32) -> Int32
diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs
index 53073a850..ab5723587 100644
--- a/Tests/prelude.mjs
+++ b/Tests/prelude.mjs
@@ -1,5 +1,7 @@
 /** @type {import('./../.build/plugins/PackageToJS/outputs/PackageTests/test.d.ts').Prelude["setupOptions"]} */
 export function setupOptions(options, context) {
+    Error.stackTraceLimit = 100;
+    setupTestGlobals(globalThis);
     return {
         ...options,
         addToCoreImports(importObject) {
@@ -10,3 +12,107 @@ export function setupOptions(options, context) {
         }
     }
 }
+
+function setupTestGlobals(global) {
+    global.globalObject1 = {
+        prop_1: {
+            nested_prop: 1,
+        },
+        prop_2: 2,
+        prop_3: true,
+        prop_4: [3, 4, "str_elm_1", null, undefined, 5],
+        prop_5: {
+            func1: function () {
+                return;
+            },
+            func2: function () {
+                return 1;
+            },
+            func3: function (n) {
+                return n * 2;
+            },
+            func4: function (a, b, c) {
+                return a + b + c;
+            },
+            func5: function (x) {
+                return "Hello, " + x;
+            },
+            func6: function (c, a, b) {
+                if (c) {
+                    return a;
+                } else {
+                    return b;
+                }
+            },
+        },
+        prop_6: {
+            call_host_1: () => {
+                return global.globalObject1.prop_6.host_func_1();
+            },
+        },
+        prop_7: 3.14,
+        prop_8: [0, , 2, 3, , , 6],
+        prop_9: {
+            func1: function () {
+                throw new Error();
+            },
+            func2: function () {
+                throw "String Error";
+            },
+            func3: function () {
+                throw 3.0;
+            },
+        },
+        eval_closure: function (fn) {
+            return fn(arguments[1]);
+        },
+        observable_obj: {
+            set_called: false,
+            target: new Proxy(
+                {
+                    nested: {},
+                },
+                {
+                    set(target, key, value) {
+                        global.globalObject1.observable_obj.set_called = true;
+                        target[key] = value;
+                        return true;
+                    },
+                }
+            ),
+        },
+    };
+
+    global.Animal = function (name, age, isCat) {
+        if (age < 0) {
+            throw new Error("Invalid age " + age);
+        }
+        this.name = name;
+        this.age = age;
+        this.bark = () => {
+            return isCat ? "nyan" : "wan";
+        };
+        this.isCat = isCat;
+        this.getIsCat = function () {
+            return this.isCat;
+        };
+        this.setName = function (name) {
+            this.name = name;
+        };
+    };
+
+    global.callThrowingClosure = (c) => {
+        try {
+            c();
+        } catch (error) {
+            return error;
+        }
+    };
+
+    global.objectDecodingTest = {
+        obj: {},
+        fn: () => { },
+        sym: Symbol("s"),
+        bi: BigInt(3)
+    };
+}