mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 18:38:55 +00:00
Implement expect().toHaveReturned() && expect().toHaveReturnedTimes(n) (#11231)
This commit is contained in:
16
packages/bun-types/test.d.ts
vendored
16
packages/bun-types/test.d.ts
vendored
@@ -1444,6 +1444,22 @@ declare module "bun:test" {
|
||||
* @param expected the string to end with
|
||||
*/
|
||||
toEndWith(expected: string): void;
|
||||
/**
|
||||
* Ensures that a mock function has returned successfully at least once.
|
||||
*
|
||||
* A promise that is unfulfilled will be considered a failure. If the
|
||||
* function threw an error, it will be considered a failure.
|
||||
*/
|
||||
toHaveReturned(): void;
|
||||
|
||||
/**
|
||||
* Ensures that a mock function has returned successfully at `times` times.
|
||||
*
|
||||
* A promise that is unfulfilled will be considered a failure. If the
|
||||
* function threw an error, it will be considered a failure.
|
||||
*/
|
||||
toHaveReturnedTimes(times: number): void;
|
||||
|
||||
/**
|
||||
* Ensures that a mock function is called.
|
||||
*/
|
||||
|
||||
@@ -3977,8 +3977,110 @@ pub const Expect = struct {
|
||||
return .zero;
|
||||
}
|
||||
|
||||
pub const toHaveReturned = notImplementedJSCFn;
|
||||
pub const toHaveReturnedTimes = notImplementedJSCFn;
|
||||
const ReturnStatus = enum {
|
||||
throw,
|
||||
@"return",
|
||||
incomplete,
|
||||
|
||||
pub const Map = bun.ComptimeEnumMap(ReturnStatus);
|
||||
};
|
||||
|
||||
inline fn toHaveReturnedTimesFn(this: *Expect, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, comptime known_index: ?i32) JSC.JSValue {
|
||||
JSC.markBinding(@src());
|
||||
|
||||
const thisValue = callframe.this();
|
||||
const arguments = callframe.arguments(1).slice();
|
||||
defer this.postMatch(globalObject);
|
||||
|
||||
const name = comptime if (known_index != null and known_index.? == 0) "toHaveReturned" else "toHaveReturnedTimes";
|
||||
|
||||
const value: JSValue = this.getValue(globalObject, thisValue, name, if (known_index != null and known_index.? == 0) "" else "<green>expected<r>") orelse return .zero;
|
||||
|
||||
incrementExpectCallCounter();
|
||||
|
||||
const returns = JSMockFunction__getReturns(value);
|
||||
|
||||
if (returns == .zero or !returns.jsType().isArray()) {
|
||||
globalObject.throw("Expected value must be a mock function: {}", .{value});
|
||||
return .zero;
|
||||
}
|
||||
|
||||
const return_count: i32 = if (known_index) |index| index else brk: {
|
||||
if (arguments.len < 1 or !arguments[0].isUInt32AsAnyInt()) {
|
||||
globalObject.throwInvalidArguments(name ++ "() requires 1 non-negative integer argument", .{});
|
||||
return .zero;
|
||||
}
|
||||
|
||||
break :brk arguments[0].coerce(i32, globalObject);
|
||||
};
|
||||
|
||||
var pass = false;
|
||||
const index: u32 = @as(u32, @intCast(return_count)) -| 1;
|
||||
|
||||
const times_value = returns.getDirectIndex(
|
||||
globalObject,
|
||||
index,
|
||||
);
|
||||
|
||||
const total_count = returns.getLength(globalObject);
|
||||
|
||||
const return_status: ReturnStatus = brk: {
|
||||
// Returns is an array of:
|
||||
//
|
||||
// { type: "throw" | "incomplete" | "return", value: any}
|
||||
//
|
||||
if (total_count >= return_count and times_value.isCell()) {
|
||||
if (times_value.get(globalObject, "type")) |type_string| {
|
||||
if (type_string.isString()) {
|
||||
break :brk ReturnStatus.Map.fromJS(globalObject, type_string) orelse {
|
||||
if (!globalObject.hasException())
|
||||
globalObject.throw("Expected value must be a mock function with returns: {}", .{value});
|
||||
return .zero;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break :brk ReturnStatus.incomplete;
|
||||
};
|
||||
if (globalObject.hasException())
|
||||
return .zero;
|
||||
|
||||
pass = return_status == ReturnStatus.@"return";
|
||||
|
||||
const not = this.flags.not;
|
||||
if (not) pass = !pass;
|
||||
if (pass) return .undefined;
|
||||
|
||||
if (!pass and return_status == ReturnStatus.throw) {
|
||||
const signature = comptime getSignature(name, "<green>expected<r>", false);
|
||||
const fmt = signature ++ "\n\n" ++ "Function threw an exception\n{any}\n";
|
||||
var formatter = JSC.ConsoleObject.Formatter{
|
||||
.globalThis = globalObject,
|
||||
.quote_strings = true,
|
||||
};
|
||||
globalObject.throwPretty(fmt, .{times_value.get(globalObject, "value").?.toFmt(globalObject, &formatter)});
|
||||
return .zero;
|
||||
}
|
||||
|
||||
switch (not) {
|
||||
inline else => |is_not| {
|
||||
const signature = comptime getSignature(name, "<green>expected<r>", is_not);
|
||||
const fmt = signature ++ "\n\n" ++ "Expected number of successful calls: <green>{d}<r>\n" ++ "Received number of calls: <red>{d}<r>\n";
|
||||
globalObject.throwPretty(fmt, .{ index, total_count });
|
||||
return .zero;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn toHaveReturned(this: *Expect, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
return toHaveReturnedTimesFn(this, globalObject, callframe, 0);
|
||||
}
|
||||
|
||||
pub fn toHaveReturnedTimes(this: *Expect, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue {
|
||||
return toHaveReturnedTimesFn(this, globalObject, callframe, null);
|
||||
}
|
||||
|
||||
pub const toHaveReturnedWith = notImplementedJSCFn;
|
||||
pub const toHaveLastReturnedWith = notImplementedJSCFn;
|
||||
pub const toHaveNthReturnedWith = notImplementedJSCFn;
|
||||
|
||||
@@ -90,6 +90,32 @@ describe("mock()", () => {
|
||||
expect(func).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("toHaveReturned()", () => {
|
||||
const func = jest.fn(() => "the jedi");
|
||||
expect(func).not.toHaveReturned();
|
||||
func();
|
||||
expect(func).toHaveReturned();
|
||||
expect(func).toHaveReturnedTimes(1);
|
||||
expect(func.mock.calls).toHaveLength(1);
|
||||
expect(func.mock.calls[0]).toBeEmpty();
|
||||
func();
|
||||
expect(func).toHaveReturnedTimes(2);
|
||||
const func2 = jest.fn(() => {
|
||||
throw new Error("the jedi");
|
||||
});
|
||||
expect(func2).not.toHaveReturned();
|
||||
try {
|
||||
func2();
|
||||
} catch (e) {}
|
||||
|
||||
expect(func2).not.toHaveReturned();
|
||||
try {
|
||||
expect(func2).toHaveReturned();
|
||||
} catch (e) {
|
||||
expect(e.message).toContain("Function threw an exception");
|
||||
}
|
||||
});
|
||||
|
||||
test("passes this value", () => {
|
||||
const fn = jest.fn(function hey() {
|
||||
"use strict";
|
||||
|
||||
Reference in New Issue
Block a user