aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-01-12 12:56:03 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-01-12 13:11:05 -0800
commit1a4685213b2eec3e5a817b4825c86a5ac8a25b31 (patch)
treeda8821a6abf2da487b24a792d8c052989dfa8053
parent76e6a178e3a0f1f12f42cd9980349e18145258f2 (diff)
downloadbun-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.zig28
-rw-r--r--src/bun.js/test/jest.zig45
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|