diff options
author | 2023-01-12 12:56:03 -0800 | |
---|---|---|
committer | 2023-01-12 13:11:05 -0800 | |
commit | 1a4685213b2eec3e5a817b4825c86a5ac8a25b31 (patch) | |
tree | da8821a6abf2da487b24a792d8c052989dfa8053 | |
parent | 76e6a178e3a0f1f12f42cd9980349e18145258f2 (diff) | |
download | bun-1a4685213b2eec3e5a817b4825c86a5ac8a25b31.tar.gz bun-1a4685213b2eec3e5a817b4825c86a5ac8a25b31.tar.zst bun-1a4685213b2eec3e5a817b4825c86a5ac8a25b31.zip |
[bun:test] Support `async` in `expect(async () => { await 1; }).toThrow()`
-rw-r--r-- | src/bun.js/javascript.zig | 28 | ||||
-rw-r--r-- | src/bun.js/test/jest.zig | 45 |
2 files changed, 67 insertions, 6 deletions
diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 410f3a776..c2c43cf81 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -429,7 +429,7 @@ pub const VirtualMachine = struct { auto_install_dependencies: bool = false, load_builtins_from_path: []const u8 = "", - onUnhandledRejection: *const fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void = defaultOnUnhandledRejection, + onUnhandledRejection: *const OnUnhandledRejection = defaultOnUnhandledRejection, onUnhandledRejectionCtx: ?*anyopaque = null, unhandled_error_counter: usize = 0, @@ -438,6 +438,8 @@ pub const VirtualMachine = struct { gc_controller: JSC.GarbageCollectionController = .{}, + pub const OnUnhandledRejection = fn (*VirtualMachine, globalObject: *JSC.JSGlobalObject, JSC.JSValue) void; + const VMHolder = struct { pub threadlocal var vm: ?*VirtualMachine = null; }; @@ -454,6 +456,30 @@ pub const VirtualMachine = struct { pub threadlocal var is_main_thread_vm: bool = false; + pub const UnhandledRejectionScope = struct { + ctx: ?*anyopaque = null, + onUnhandledRejection: *const OnUnhandledRejection = undefined, + count: usize = 0, + + pub fn apply(this: *UnhandledRejectionScope, vm: *JSC.VirtualMachine) void { + vm.onUnhandledRejection = this.onUnhandledRejection; + vm.onUnhandledRejectionCtx = this.ctx; + vm.unhandled_error_counter = this.count; + } + }; + + pub fn onQuietUnhandledRejectionHandler(this: *VirtualMachine, _: *JSC.JSGlobalObject, _: JSC.JSValue) void { + this.unhandled_error_counter += 1; + } + + pub fn unhandledRejectionScope(this: *VirtualMachine) UnhandledRejectionScope { + return .{ + .onUnhandledRejection = this.onUnhandledRejection, + .ctx = this.onUnhandledRejectionCtx, + .count = this.unhandled_error_counter, + }; + } + pub fn resetUnhandledRejection(this: *VirtualMachine) void { this.onUnhandledRejection = defaultOnUnhandledRejection; } diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 37c4c79cb..6ff8d90ff 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -1124,12 +1124,37 @@ pub const Expect = struct { } const not = this.op.contains(.not); - const result_ = value.call(globalObject, &.{}).toError(); + + const result_: ?JSValue = brk: { + var vm = globalObject.bunVM(); + var scope = vm.unhandledRejectionScope(); + defer scope.apply(vm); + vm.onUnhandledRejection = &VirtualMachine.onQuietUnhandledRejectionHandler; + const return_value: JSValue = value.call(globalObject, &.{}); + + if (return_value.asAnyPromise()) |promise| { + globalObject.bunVM().waitForPromise(promise); + const promise_result = promise.result(globalObject.vm()); + + switch (promise.status(globalObject.vm())) { + .Fulfilled => { + break :brk null; + }, + .Rejected => { + // since we know for sure it rejected, we should always return the error + break :brk promise_result.toError() orelse promise_result; + }, + .Pending => unreachable, + } + } + + break :brk return_value.toError(); + }; + const did_throw = result_ != null; const matched_expectation = did_throw == !not; - if (matched_expectation) return thisValue; - if (expected_value.isEmptyOrUndefinedOrNull()) { + if (!matched_expectation) { if (!not) globalObject.throw("Expected function to throw", .{}) else { @@ -1139,7 +1164,14 @@ pub const Expect = struct { return .zero; } - const result = result_.?; + + // If you throw a string, it's treated as the message of an Error + // If you are expected not to throw and you didn't throw, then you pass + // If you are expected to throw a specific message and you throw a different one, then you fail. + if (matched_expectation and (!expected_value.isCell() or not)) + return thisValue; + + const result = result_ orelse JSC.JSValue.jsUndefined(); const expected_error = expected_value.toError(); @@ -1148,7 +1180,10 @@ pub const Expect = struct { if (expected_value.isString()) break :brk expected_value; break :brk expected_error.?.get(globalObject, "message"); }; - const actual = result.get(globalObject, "message"); + const actual: ?JSValue = if (result.isObject()) + result.get(globalObject, "message") + else + null; // TODO support partial match const pass = brk: { if (expected) |expected_message| |