fix: expect().resolves and expect().rejects now return promises

Previously, matchers under `expect().resolves` and `expect().rejects`
would return `undefined` instead of a promise. This caused issues when
trying to chain or await matcher results.

This fix adds a helper function `returnMatcherValue()` that checks if
async flags (.resolves/.rejects) are set and returns a resolved promise
instead of undefined. All matchers now use this helper.

Fixes #23420

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Claude Bot
2025-10-10 01:35:30 +00:00
parent 6e3359dd16
commit 0b911e6f7a
72 changed files with 138 additions and 83 deletions

View File

@@ -848,7 +848,7 @@ pub const Expect = struct {
if (existing_value) |saved_value| {
if (strings.eqlLong(pretty_value.slice(), saved_value, true)) {
Jest.runner.?.snapshots.passed += 1;
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
Jest.runner.?.snapshots.failed += 1;
@@ -863,7 +863,7 @@ pub const Expect = struct {
return globalThis.throwPretty(fmt, .{diff_format});
}
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
pub fn getStaticNot(globalThis: *JSGlobalObject, _: JSValue, _: JSValue) bun.JSError!JSValue {
@@ -1231,6 +1231,16 @@ pub const Expect = struct {
vm.autoGarbageCollect();
}
/// Helper function for matchers to return the correct value based on whether
/// .resolves or .rejects was used. When async flags are set, returns a resolved promise.
/// Otherwise returns undefined.
pub inline fn returnMatcherValue(this: *const Expect, globalThis: *JSGlobalObject) JSValue {
if (this.flags.promise != .none) {
return JSPromise.resolvedPromiseValue(globalThis, .js_undefined);
}
return .js_undefined;
}
pub fn doUnreachable(globalThis: *JSGlobalObject, callframe: *CallFrame) bun.JSError!JSValue {
const arg = callframe.arguments_old(1).ptr[0];
@@ -2246,6 +2256,7 @@ const strings = bun.strings;
const jsc = bun.jsc;
const CallFrame = jsc.CallFrame;
const JSGlobalObject = jsc.JSGlobalObject;
const JSPromise = jsc.JSPromise;
const JSValue = jsc.JSValue;
const VirtualMachine = jsc.VirtualMachine;
const ZigString = jsc.ZigString;

View File

@@ -22,7 +22,7 @@ pub fn toBe(
var pass = try right.isSameValue(left, globalThis);
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -9,7 +9,7 @@ pub fn toBeArray(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFra
const not = this.flags.not;
const pass = value.jsType().isArray() != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -24,7 +24,7 @@ pub fn toBeArrayOfSize(this: *Expect, globalThis: *JSGlobalObject, callFrame: *C
var pass = value.jsType().isArray() and @as(i32, @intCast(try value.getLength(globalThis))) == size.toInt32();
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeBoolean(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallF
const not = this.flags.not;
const pass = value.isBoolean() != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -43,7 +43,7 @@ pub fn toBeCloseTo(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallF
}
if (std.math.isPositiveInf(expected) and std.math.isPositiveInf(received)) {
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
const expected_diff = bun.pow(10, -precision) / 2;
@@ -53,7 +53,7 @@ pub fn toBeCloseTo(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallF
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeDate(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFram
const not = this.flags.not;
const pass = value.isDate() != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeDefined(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallF
const not = this.flags.not;
var pass = !value.isUndefined();
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -61,7 +61,7 @@ pub fn toBeEmpty(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFra
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
if (not) {
const signature = comptime getSignature("toBeEmpty", "", true);

View File

@@ -10,7 +10,7 @@ pub fn toBeEmptyObject(this: *Expect, globalThis: *JSGlobalObject, callFrame: *C
var pass = try value.isObjectEmpty(globalThis);
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -34,7 +34,7 @@ pub fn toBeEven(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFram
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -9,7 +9,7 @@ pub fn toBeFalse(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFra
const not = this.flags.not;
const pass = (value.isBoolean() and !value.toBoolean()) != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -14,7 +14,7 @@ pub fn toBeFalsy(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFra
if (!truthy) pass = true;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -15,7 +15,7 @@ pub fn toBeFinite(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFr
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeFunction(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Call
const not = this.flags.not;
const pass = value.isCallable() != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -38,7 +38,7 @@ pub fn toBeGreaterThan(this: *Expect, globalThis: *JSGlobalObject, callFrame: *C
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -38,7 +38,7 @@ pub fn toBeGreaterThanOrEqual(this: *Expect, globalThis: *JSGlobalObject, callFr
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -24,7 +24,7 @@ pub fn toBeInstanceOf(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Ca
const not = this.flags.not;
var pass = value.isInstanceOf(globalThis, expected_value);
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
const expected_fmt = expected_value.toFmt(&formatter);

View File

@@ -9,7 +9,7 @@ pub fn toBeInteger(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallF
const not = this.flags.not;
const pass = value.isAnyInt() != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -38,7 +38,7 @@ pub fn toBeLessThan(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Call
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -38,7 +38,7 @@ pub fn toBeLessThanOrEqual(this: *Expect, globalThis: *JSGlobalObject, callFrame
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -14,7 +14,7 @@ pub fn toBeNaN(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -15,7 +15,7 @@ pub fn toBeNegative(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Call
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeNil(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
const not = this.flags.not;
const pass = value.isUndefinedOrNull() != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeNull(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFram
const not = this.flags.not;
var pass = value.isNull();
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -9,7 +9,7 @@ pub fn toBeNumber(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFr
const not = this.flags.not;
const pass = value.isNumber() != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeObject(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFr
const not = this.flags.not;
const pass = value.isObject() != not;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -32,7 +32,7 @@ pub fn toBeOdd(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -61,7 +61,7 @@ pub fn toBeOneOf(
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -15,7 +15,7 @@ pub fn toBePositive(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Call
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeString(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFr
const not = this.flags.not;
const pass = value.isString() != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeSymbol(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFr
const not = this.flags.not;
const pass = value.isSymbol() != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -9,7 +9,7 @@ pub fn toBeTrue(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFram
const not = this.flags.not;
const pass = (value.isBoolean() and value.toBoolean()) != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -12,7 +12,7 @@ pub fn toBeTruthy(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFr
if (truthy) pass = true;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -65,7 +65,7 @@ pub fn toBeTypeOf(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFr
pass = strings.eql(typeof, whatIsTheType);
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -10,7 +10,7 @@ pub fn toBeUndefined(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Cal
if (value.isUndefined()) pass = true;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -10,7 +10,7 @@ pub fn toBeValidDate(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Cal
var pass = (value.isDate() and !std.math.isNan(value.getUnixTimestamp()));
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -36,7 +36,7 @@ pub fn toBeWithin(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFr
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -73,7 +73,7 @@ pub fn toContain(
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -43,7 +43,7 @@ pub fn toContainAllKeys(
}
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalObject);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true };

View File

@@ -48,7 +48,7 @@ pub fn toContainAllValues(
}
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalObject);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true };

View File

@@ -41,7 +41,7 @@ pub fn toContainAnyKeys(
}
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -42,7 +42,7 @@ pub fn toContainAnyValues(
}
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalObject);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true };

View File

@@ -82,7 +82,7 @@ pub fn toContainEqual(
}
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -28,7 +28,7 @@ pub fn toContainKey(
var pass = try value.hasOwnPropertyValue(globalThis, expected);
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure

View File

@@ -44,7 +44,7 @@ pub fn toContainKeys(
};
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -33,7 +33,7 @@ pub fn toContainValue(
}
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalObject);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true };

View File

@@ -42,7 +42,7 @@ pub fn toContainValues(
}
if (not) pass = !pass;
if (pass) return thisValue;
if (pass) return this.returnMatcherValue(globalObject);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalObject, .quote_strings = true };

View File

@@ -32,7 +32,7 @@ pub fn toEndWith(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFra
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -18,7 +18,7 @@ pub fn toEqual(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
var pass = try value.jestDeepEquals(expected, globalThis);
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
const diff_formatter = DiffFormatter{

View File

@@ -60,7 +60,7 @@ pub fn toEqualIgnoringWhitespace(this: *Expect, globalThis: *JSGlobalObject, cal
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -23,7 +23,7 @@ pub fn toHaveBeenCalled(this: *Expect, globalThis: *JSGlobalObject, callframe: *
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
if (not) {

View File

@@ -19,7 +19,7 @@ pub fn toHaveBeenCalledOnce(this: *Expect, globalThis: *JSGlobalObject, callfram
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
if (not) {

View File

@@ -26,7 +26,7 @@ pub fn toHaveBeenCalledTimes(this: *Expect, globalThis: *JSGlobalObject, callfra
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
if (not) {

View File

@@ -47,7 +47,7 @@ pub fn toHaveBeenCalledWith(this: *Expect, globalThis: *JSGlobalObject, callfram
}
if (pass != this.flags.not) {
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
// handle failure

View File

@@ -43,7 +43,7 @@ pub fn toHaveBeenLastCalledWith(this: *Expect, globalThis: *JSGlobalObject, call
}
if (pass != this.flags.not) {
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
// handle failure

View File

@@ -51,7 +51,7 @@ pub fn toHaveBeenNthCalledWith(this: *Expect, globalThis: *JSGlobalObject, callf
}
if (pass != this.flags.not) {
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
// handle failure

View File

@@ -46,7 +46,7 @@ pub fn toHaveLastReturnedWith(this: *Expect, globalThis: *JSGlobalObject, callfr
}
if (pass != this.flags.not) {
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
// Handle failure

View File

@@ -50,7 +50,7 @@ pub fn toHaveLength(
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
if (not) {

View File

@@ -55,7 +55,7 @@ pub fn toHaveNthReturnedWith(this: *Expect, globalThis: *JSGlobalObject, callfra
}
if (pass != this.flags.not) {
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
// Handle failure

View File

@@ -39,7 +39,7 @@ pub fn toHaveProperty(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Ca
}
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -43,7 +43,7 @@ inline fn toHaveReturnedTimesFn(this: *Expect, globalThis: *JSGlobalObject, call
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
switch (not) {
inline else => |is_not| {

View File

@@ -52,7 +52,7 @@ pub fn toHaveReturnedWith(this: *Expect, globalThis: *JSGlobalObject, callframe:
}
if (pass != this.flags.not) {
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
// Handle failure

View File

@@ -32,7 +32,7 @@ pub fn toInclude(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFra
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -57,7 +57,7 @@ pub fn toIncludeRepeated(this: *Expect, globalThis: *JSGlobalObject, callFrame:
pass = actual_count == countAsNum;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -39,7 +39,7 @@ pub fn toMatch(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
};
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
const expected_fmt = expected_value.toFmt(&formatter);

View File

@@ -37,7 +37,7 @@ pub fn toMatchObject(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Cal
var pass = try received_object.jestDeepMatch(property_matchers, globalThis, true);
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
const diff_formatter = DiffFormatter{

View File

@@ -32,7 +32,7 @@ pub fn toSatisfy(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFra
const not = this.flags.not;
const pass = (result.isBoolean() and result.toBoolean()) != not;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -32,7 +32,7 @@ pub fn toStartWith(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallF
const not = this.flags.not;
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
defer formatter.deinit();

View File

@@ -18,7 +18,7 @@ pub fn toStrictEqual(this: *Expect, globalThis: *JSGlobalObject, callFrame: *Cal
var pass = try value.jestStrictDeepEquals(expected, globalThis);
if (not) pass = !pass;
if (pass) return .js_undefined;
if (pass) return this.returnMatcherValue(globalThis);
// handle failure
const diff_formatter = DiffFormatter{ .received = value, .expected = expected, .globalThis = globalThis, .not = not };

View File

@@ -39,7 +39,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
if (not) {
const signature = comptime getSignature("toThrow", "<green>expected<r>", true);
if (!did_throw) return .js_undefined;
if (!did_throw) return this.returnMatcherValue(globalThis);
const result: JSValue = result_.?;
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
@@ -76,7 +76,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
defer expected_slice.deinit();
const received_slice = try received_message.toSliceOrNull(globalThis);
defer received_slice.deinit();
if (!strings.contains(received_slice.slice(), expected_slice.slice())) return .js_undefined;
if (!strings.contains(received_slice.slice(), expected_slice.slice())) return this.returnMatcherValue(globalThis);
}
return this.throw(globalThis, signature, "\n\nExpected substring: not <green>{any}<r>\nReceived message: <red>{any}<r>\n", .{
@@ -95,7 +95,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
// TODO: REMOVE THIS GETTER! Expose a binding to call .test on the RegExp object directly.
if (try expected_value.get(globalThis, "test")) |test_fn| {
const matches = test_fn.call(globalThis, expected_value, &.{received_message}) catch |err| globalThis.takeException(err);
if (!matches.toBoolean()) return .js_undefined;
if (!matches.toBoolean()) return this.returnMatcherValue(globalThis);
}
return this.throw(globalThis, signature, "\n\nExpected pattern: not <green>{any}<r>\nReceived message: <red>{any}<r>\n", .{
@@ -112,12 +112,12 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
if (globalThis.hasException()) return .zero;
// no partial match for this case
if (!try expected_message.isSameValue(received_message, globalThis)) return .js_undefined;
if (!try expected_message.isSameValue(received_message, globalThis)) return this.returnMatcherValue(globalThis);
return this.throw(globalThis, signature, "\n\nExpected message: not <green>{any}<r>\n", .{expected_message.toFmt(&formatter)});
}
if (!result.isInstanceOf(globalThis, expected_value)) return .js_undefined;
if (!result.isInstanceOf(globalThis, expected_value)) return this.returnMatcherValue(globalThis);
var expected_class = ZigString.Empty;
try expected_value.getClassName(globalThis, &expected_class);
@@ -126,7 +126,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
}
if (did_throw) {
if (expected_value == .zero or expected_value.isUndefined()) return .js_undefined;
if (expected_value == .zero or expected_value.isUndefined()) return this.returnMatcherValue(globalThis);
const result: JSValue = if (result_.?.toError()) |r|
r
@@ -146,7 +146,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
defer expected_slice.deinit();
const received_slice = try received_message.toSlice(globalThis, globalThis.allocator());
defer received_slice.deinit();
if (strings.contains(received_slice.slice(), expected_slice.slice())) return .js_undefined;
if (strings.contains(received_slice.slice(), expected_slice.slice())) return this.returnMatcherValue(globalThis);
}
// error: message from received error does not match expected string
@@ -171,7 +171,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
// TODO: REMOVE THIS GETTER! Expose a binding to call .test on the RegExp object directly.
if (try expected_value.get(globalThis, "test")) |test_fn| {
const matches = test_fn.call(globalThis, expected_value, &.{received_message}) catch |err| globalThis.takeException(err);
if (matches.toBoolean()) return .js_undefined;
if (matches.toBoolean()) return this.returnMatcherValue(globalThis);
}
}
@@ -202,7 +202,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
}
if (is_equal) {
return .js_undefined;
return this.returnMatcherValue(globalThis);
}
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };
@@ -219,7 +219,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
const signature = comptime getSignature("toThrow", "<green>expected<r>", false);
if (_received_message) |received_message| {
if (try received_message.isSameValue(expected_message, globalThis)) return .js_undefined;
if (try received_message.isSameValue(expected_message, globalThis)) return this.returnMatcherValue(globalThis);
}
// error: message from received error does not match expected error message.
@@ -237,7 +237,7 @@ pub fn toThrow(this: *Expect, globalThis: *JSGlobalObject, callFrame: *CallFrame
return this.throw(globalThis, signature, "\n\nExpected message: <green>{any}<r>\nReceived value: <red>{any}<r>\n", .{ expected_fmt, received_fmt });
}
if (result.isInstanceOf(globalThis, expected_value)) return .js_undefined;
if (result.isInstanceOf(globalThis, expected_value)) return this.returnMatcherValue(globalThis);
// error: received error not instance of received error constructor
var formatter = jsc.ConsoleObject.Formatter{ .globalThis = globalThis, .quote_strings = true };

View File

@@ -0,0 +1,44 @@
import { expect, test } from "bun:test";
test("resolves matcher returns a Promise", () => {
const promise = Promise.resolve(42);
const matcherResult = expect(promise).resolves.toBe(42);
expect(matcherResult).toBeInstanceOf(Promise);
return matcherResult;
});
test("rejects matcher returns a Promise", () => {
const promise = Promise.reject(new Error("test error"));
const matcherResult = expect(promise).rejects.toThrow("test error");
expect(matcherResult).toBeInstanceOf(Promise);
return matcherResult;
});
test("resolves.not matcher returns a Promise", () => {
const promise = Promise.resolve(42);
const matcherResult = expect(promise).resolves.not.toBe(100);
expect(matcherResult).toBeInstanceOf(Promise);
return matcherResult;
});
test("rejects.not matcher returns a Promise", () => {
const promise = Promise.reject(42);
const matcherResult = expect(promise).rejects.not.toThrow("wrong error");
expect(matcherResult).toBeInstanceOf(Promise);
return matcherResult;
});
test("multiple resolves matchers can be chained with await", async () => {
const promise1 = Promise.resolve(1);
const promise2 = Promise.resolve(2);
await expect(promise1).resolves.toBe(1);
await expect(promise2).resolves.toBe(2);
});
test("resolves.toEqual returns a Promise", async () => {
const promise = Promise.resolve({ foo: "bar" });
const matcherResult = expect(promise).resolves.toEqual({ foo: "bar" });
expect(matcherResult).toBeInstanceOf(Promise);
await matcherResult;
});