mirror of
https://github.com/oven-sh/bun
synced 2026-02-03 15:38:46 +00:00
Compare commits
7 Commits
dylan/byte
...
nektro-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1e682dec6a | ||
|
|
137c7e1db8 | ||
|
|
3604c45d3b | ||
|
|
62f3f88aa0 | ||
|
|
be0b3b9fd9 | ||
|
|
fdb6ef0efa | ||
|
|
868aa95ec2 |
63
packages/bun-types/test.d.ts
vendored
63
packages/bun-types/test.d.ts
vendored
@@ -301,6 +301,21 @@ declare module "bun:test" {
|
||||
* @param milliseconds the number of milliseconds for the default timeout
|
||||
*/
|
||||
export function setDefaultTimeout(milliseconds: number): void;
|
||||
|
||||
// TODO: the usages below can be replaced with `Test` once https://github.com/oven-sh/bun/issues/10885 is resolved.
|
||||
type TestFunc = (
|
||||
label: string,
|
||||
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
|
||||
/**
|
||||
* - If a `number`, sets the timeout for the test in milliseconds.
|
||||
* - If an `object`, sets the options for the test.
|
||||
* - `timeout` sets the timeout for the test in milliseconds.
|
||||
* - `retry` sets the number of times to retry the test if it fails.
|
||||
* - `repeats` sets the number of times to repeat the test, regardless of whether it passed or failed.
|
||||
*/
|
||||
options?: number | TestOptions,
|
||||
) => void;
|
||||
|
||||
export interface TestOptions {
|
||||
/**
|
||||
* Sets the timeout for the test in milliseconds.
|
||||
@@ -326,6 +341,7 @@ declare module "bun:test" {
|
||||
*/
|
||||
repeats?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a test.
|
||||
*
|
||||
@@ -367,11 +383,7 @@ declare module "bun:test" {
|
||||
* @param fn the test function
|
||||
* @param options the test timeout or options
|
||||
*/
|
||||
only(
|
||||
label: string,
|
||||
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
|
||||
options?: number | TestOptions,
|
||||
): void;
|
||||
only: TestFunc;
|
||||
/**
|
||||
* Skips this test.
|
||||
*
|
||||
@@ -379,11 +391,7 @@ declare module "bun:test" {
|
||||
* @param fn the test function
|
||||
* @param options the test timeout or options
|
||||
*/
|
||||
skip(
|
||||
label: string,
|
||||
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
|
||||
options?: number | TestOptions,
|
||||
): void;
|
||||
skip: TestFunc;
|
||||
/**
|
||||
* Marks this test as to be written or to be fixed.
|
||||
*
|
||||
@@ -396,11 +404,7 @@ declare module "bun:test" {
|
||||
* @param fn the test function
|
||||
* @param options the test timeout or options
|
||||
*/
|
||||
todo(
|
||||
label: string,
|
||||
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
|
||||
options?: number | TestOptions,
|
||||
): void;
|
||||
todo: TestFunc;
|
||||
/**
|
||||
* Runs this test, if `condition` is true.
|
||||
*
|
||||
@@ -408,37 +412,19 @@ declare module "bun:test" {
|
||||
*
|
||||
* @param condition if the test should run
|
||||
*/
|
||||
if(
|
||||
condition: boolean,
|
||||
): (
|
||||
label: string,
|
||||
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
|
||||
options?: number | TestOptions,
|
||||
) => void;
|
||||
if(condition: boolean): TestFunc;
|
||||
/**
|
||||
* Skips this test, if `condition` is true.
|
||||
*
|
||||
* @param condition if the test should be skipped
|
||||
*/
|
||||
skipIf(
|
||||
condition: boolean,
|
||||
): (
|
||||
label: string,
|
||||
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
|
||||
options?: number | TestOptions,
|
||||
) => void;
|
||||
skipIf(condition: boolean): TestFunc;
|
||||
/**
|
||||
* Marks this test as to be written or to be fixed, if `condition` is true.
|
||||
*
|
||||
* @param condition if the test should be marked TODO
|
||||
*/
|
||||
todoIf(
|
||||
condition: boolean,
|
||||
): (
|
||||
label: string,
|
||||
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
|
||||
options?: number | TestOptions,
|
||||
) => void;
|
||||
todoIf(condition: boolean): TestFunc;
|
||||
/**
|
||||
* Returns a function that runs for each item in `table`.
|
||||
*
|
||||
@@ -453,6 +439,11 @@ declare module "bun:test" {
|
||||
each<T>(
|
||||
table: T[],
|
||||
): (label: string, fn: (...args: T[]) => void | Promise<unknown>, options?: number | TestOptions) => void;
|
||||
/**
|
||||
* Use `test.failing` when you are writing a test and expecting it to fail.
|
||||
* If `failing` test will throw any errors then it will pass. If it does not throw it will fail.
|
||||
*/
|
||||
failing: TestFunc;
|
||||
}
|
||||
/**
|
||||
* Runs a test.
|
||||
|
||||
@@ -79,8 +79,7 @@ threadlocal var stdout_lock_count: u16 = 0;
|
||||
|
||||
/// https://console.spec.whatwg.org/#formatter
|
||||
pub fn messageWithTypeAndLevel(
|
||||
//console_: ConsoleObject.Type,
|
||||
_: ConsoleObject.Type,
|
||||
console: *ConsoleObject,
|
||||
message_type: MessageType,
|
||||
//message_level: u32,
|
||||
level: MessageLevel,
|
||||
@@ -92,8 +91,6 @@ pub fn messageWithTypeAndLevel(
|
||||
return;
|
||||
}
|
||||
|
||||
var console = global.bunVM().console;
|
||||
|
||||
// Lock/unlock a mutex incase two JS threads are console.log'ing at the same time
|
||||
// We do this the slightly annoying way to avoid assigning a pointer
|
||||
if (level == .Warning or level == .Error or message_type == .Assert) {
|
||||
@@ -3439,7 +3436,7 @@ pub fn takeHeapSnapshot(
|
||||
) callconv(JSC.conv) void {
|
||||
// TODO: this does an extra JSONStringify and we don't need it to!
|
||||
var snapshot: [1]JSValue = .{globalThis.generateHeapSnapshot()};
|
||||
ConsoleObject.messageWithTypeAndLevel(undefined, MessageType.Log, MessageLevel.Debug, globalThis, &snapshot, 1);
|
||||
globalThis.bunVM().console.messageWithTypeAndLevel(.Log, .Debug, globalThis, &snapshot, 1);
|
||||
}
|
||||
pub fn timeStamp(
|
||||
// console
|
||||
|
||||
@@ -4597,8 +4597,7 @@ pub const JSValue = enum(JSValueReprInt) {
|
||||
message_type: ConsoleObject.MessageType,
|
||||
message_level: ConsoleObject.MessageLevel,
|
||||
) void {
|
||||
JSC.ConsoleObject.messageWithTypeAndLevel(
|
||||
undefined,
|
||||
globalObject.bunVM().console.messageWithTypeAndLevel(
|
||||
message_type,
|
||||
message_level,
|
||||
globalObject,
|
||||
|
||||
@@ -7,6 +7,7 @@ const MimeType = bun.http.MimeType;
|
||||
const ZigURL = @import("../../url.zig").URL;
|
||||
const HTTPClient = bun.http;
|
||||
const Environment = bun.Environment;
|
||||
const validators = @import("./../node/util/validators.zig");
|
||||
|
||||
const Snapshots = @import("./snapshot.zig").Snapshots;
|
||||
const expect = @import("./expect.zig");
|
||||
@@ -50,12 +51,14 @@ const is_bindgen: bool = false;
|
||||
|
||||
const ArrayIdentityContext = bun.ArrayIdentityContext;
|
||||
|
||||
pub const Tag = enum(u3) {
|
||||
pub const Tag = enum {
|
||||
pass,
|
||||
fail,
|
||||
only,
|
||||
skip,
|
||||
todo,
|
||||
failing,
|
||||
retry,
|
||||
};
|
||||
const debug = Output.scoped(.jest, false);
|
||||
pub const TestRunner = struct {
|
||||
@@ -256,12 +259,12 @@ pub const TestRunner = struct {
|
||||
};
|
||||
|
||||
pub const Test = struct {
|
||||
status: Status = Status.pending,
|
||||
status: Status = .pending,
|
||||
|
||||
pub const ID = u32;
|
||||
pub const List = std.MultiArrayList(Test);
|
||||
|
||||
pub const Status = enum(u3) {
|
||||
pub const Status = enum {
|
||||
pending,
|
||||
pass,
|
||||
fail,
|
||||
@@ -270,6 +273,7 @@ pub const TestRunner = struct {
|
||||
fail_because_todo_passed,
|
||||
fail_because_expected_has_assertions,
|
||||
fail_because_expected_assertion_count,
|
||||
retry,
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -372,6 +376,11 @@ pub const Jest = struct {
|
||||
ZigString.static("each"),
|
||||
JSC.NewFunction(globalObject, ZigString.static("each"), 2, ThisTestScope.each, false),
|
||||
);
|
||||
test_fn.put(
|
||||
globalObject,
|
||||
ZigString.static("failing"),
|
||||
JSC.NewFunction(globalObject, ZigString.static("failing"), 2, ThisTestScope.failing, false),
|
||||
);
|
||||
|
||||
module.put(
|
||||
globalObject,
|
||||
@@ -595,9 +604,9 @@ pub const TestScope = struct {
|
||||
snapshot_count: usize = 0,
|
||||
|
||||
// null if the test does not set a timeout
|
||||
timeout_millis: u32 = std.math.maxInt(u32),
|
||||
timeout_millis: u32,
|
||||
|
||||
retry_count: u32 = 0, // retry, on fail
|
||||
retry_count: u32, // retry, on fail
|
||||
repeat_count: u32 = 0, // retry, on pass or fail
|
||||
|
||||
pub const Counter = struct {
|
||||
@@ -637,13 +646,15 @@ pub const TestScope = struct {
|
||||
return createIfScope(globalThis, callframe, "test.todoIf()", "todoIf", TestScope, .todo);
|
||||
}
|
||||
|
||||
pub fn failing(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue {
|
||||
return createScope(globalThis, callframe, "test.failing()", true, .failing);
|
||||
}
|
||||
|
||||
pub fn onReject(globalThis: *JSGlobalObject, callframe: *CallFrame) JSValue {
|
||||
debug("onReject", .{});
|
||||
const arguments = callframe.arguments(2);
|
||||
const err = arguments.ptr[0];
|
||||
_ = globalThis.bunVM().uncaughtException(globalThis, err, true);
|
||||
var task: *TestRunnerTask = arguments.ptr[1].asPromisePtr(TestRunnerTask);
|
||||
task.handleResult(.{ .fail = expect.active_test_expectation_counter.actual }, .promise);
|
||||
globalThis.bunVM().autoGarbageCollect();
|
||||
return JSValue.jsUndefined();
|
||||
}
|
||||
@@ -679,7 +690,6 @@ pub const TestScope = struct {
|
||||
} else {
|
||||
debug("done(err)", .{});
|
||||
_ = globalThis.bunVM().uncaughtException(globalThis, err, true);
|
||||
task.handleResult(.{ .fail = expect.active_test_expectation_counter.actual }, .callback);
|
||||
}
|
||||
} else {
|
||||
debug("done()", .{});
|
||||
@@ -697,16 +707,6 @@ pub const TestScope = struct {
|
||||
if (comptime is_bindgen) return undefined;
|
||||
|
||||
var vm = VirtualMachine.get();
|
||||
const func = this.func;
|
||||
defer {
|
||||
for (this.func_arg) |arg| {
|
||||
arg.unprotect();
|
||||
}
|
||||
func.unprotect();
|
||||
this.func = .zero;
|
||||
this.func_has_callback = false;
|
||||
vm.autoGarbageCollect();
|
||||
}
|
||||
JSC.markBinding(@src());
|
||||
debug("test({})", .{bun.fmt.QuotedFormatter{ .text = this.label }});
|
||||
|
||||
@@ -745,7 +745,10 @@ pub const TestScope = struct {
|
||||
_ = vm.uncaughtException(vm.global, initial_value, true);
|
||||
|
||||
if (this.tag == .todo) {
|
||||
return .{ .todo = {} };
|
||||
return .todo;
|
||||
}
|
||||
if (this.tag == .retry) {
|
||||
return .retry;
|
||||
}
|
||||
|
||||
return .{ .fail = expect.active_test_expectation_counter.actual };
|
||||
@@ -772,6 +775,9 @@ pub const TestScope = struct {
|
||||
if (this.tag == .todo) {
|
||||
return .{ .todo = {} };
|
||||
}
|
||||
if (this.tag == .retry) {
|
||||
return .retry;
|
||||
}
|
||||
|
||||
return .{ .fail = expect.active_test_expectation_counter.actual };
|
||||
},
|
||||
@@ -838,27 +844,26 @@ pub const DescribeScope = struct {
|
||||
|
||||
fn isWithinOnlyScope(this: *const DescribeScope) bool {
|
||||
if (this.tag == .only) return true;
|
||||
if (this.parent != null) return this.parent.?.isWithinOnlyScope();
|
||||
if (this.parent) |p| return p.isWithinOnlyScope();
|
||||
return false;
|
||||
}
|
||||
|
||||
fn isWithinSkipScope(this: *const DescribeScope) bool {
|
||||
if (this.tag == .skip) return true;
|
||||
if (this.parent != null) return this.parent.?.isWithinSkipScope();
|
||||
if (this.parent) |p| return p.isWithinSkipScope();
|
||||
return false;
|
||||
}
|
||||
|
||||
fn isWithinTodoScope(this: *const DescribeScope) bool {
|
||||
if (this.tag == .todo) return true;
|
||||
if (this.parent != null) return this.parent.?.isWithinTodoScope();
|
||||
if (this.parent) |p| return p.isWithinTodoScope();
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn shouldEvaluateScope(this: *const DescribeScope) bool {
|
||||
if (this.tag == .skip or
|
||||
this.tag == .todo) return false;
|
||||
if (this.tag == .skip or this.tag == .todo) return false;
|
||||
if (Jest.runner.?.only and this.tag == .only) return true;
|
||||
if (this.parent != null) return this.parent.?.shouldEvaluateScope();
|
||||
if (this.parent) |p| return p.shouldEvaluateScope();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1225,7 +1230,6 @@ pub const DescribeScope = struct {
|
||||
}
|
||||
|
||||
this.pending_tests.deinit(getAllocator(globalThis));
|
||||
this.tests.clearAndFree(getAllocator(globalThis));
|
||||
}
|
||||
|
||||
const ScopeStack = ObjectPool(std.ArrayListUnmanaged(*DescribeScope), null, true, 16);
|
||||
@@ -1274,6 +1278,7 @@ pub const WrappedTestScope = struct {
|
||||
pub const skipIf = wrapTestFunction("test", TestScope.skipIf);
|
||||
pub const todoIf = wrapTestFunction("test", TestScope.todoIf);
|
||||
pub const each = wrapTestFunction("test", TestScope.each);
|
||||
pub const failing = wrapTestFunction("test", TestScope.failing);
|
||||
};
|
||||
|
||||
pub const WrappedDescribeScope = struct {
|
||||
@@ -1381,7 +1386,7 @@ pub const TestRunnerTask = struct {
|
||||
return false;
|
||||
}
|
||||
|
||||
var test_: TestScope = this.describe.tests.items[test_id];
|
||||
var test_: *TestScope = &this.describe.tests.items[test_id];
|
||||
describe.current_test_id = test_id;
|
||||
|
||||
if (test_.func == .zero or !describe.shouldEvaluateScope() or (test_.tag != .only and Jest.runner.?.only)) {
|
||||
@@ -1415,26 +1420,37 @@ pub const TestRunnerTask = struct {
|
||||
|
||||
this.sync_state = .pending;
|
||||
jsc_vm.auto_killer.enable();
|
||||
var result = TestScope.run(&test_, this);
|
||||
var result = blk: while (true) {
|
||||
var result = TestScope.run(test_, this);
|
||||
|
||||
if (this.describe.tests.items.len > test_id) {
|
||||
this.describe.tests.items[test_id].timeout_millis = test_.timeout_millis;
|
||||
}
|
||||
|
||||
// rejected promises should fail the test
|
||||
if (!result.isFailure())
|
||||
globalThis.handleRejectedPromises();
|
||||
|
||||
if (result == .pending and this.sync_state == .pending and (this.done_callback_state == .pending or this.promise_state == .pending)) {
|
||||
this.sync_state = .fulfilled;
|
||||
|
||||
if (this.reported and this.promise_state != .pending) {
|
||||
// An unhandled error was reported.
|
||||
// Let's allow any pending work to run, and then move on to the next test.
|
||||
this.continueRunningTestsAfterMicrotasksRun();
|
||||
if (this.describe.tests.items.len > test_id) {
|
||||
test_.timeout_millis = test_.timeout_millis;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// rejected promises should fail the test
|
||||
if (!result.isFailure())
|
||||
globalThis.handleRejectedPromises();
|
||||
|
||||
if (result == .pending and this.sync_state == .pending and (this.done_callback_state == .pending or this.promise_state == .pending)) {
|
||||
this.sync_state = .fulfilled;
|
||||
|
||||
if (this.reported and this.promise_state != .pending) {
|
||||
// An unhandled error was reported.
|
||||
// Let's allow any pending work to run, and then move on to the next test.
|
||||
this.continueRunningTestsAfterMicrotasksRun();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((result == .retry or result == .fail) and test_.retry_count > 0) {
|
||||
test_.retry_count -= 1;
|
||||
test_.ran = false;
|
||||
this.reported = false;
|
||||
jsc_vm.onUnhandledRejectionCtx = this;
|
||||
continue;
|
||||
}
|
||||
break :blk result;
|
||||
};
|
||||
|
||||
this.handleResultPtr(&result, .sync);
|
||||
|
||||
@@ -1545,14 +1561,11 @@ pub const TestRunnerTask = struct {
|
||||
this.reported = true;
|
||||
|
||||
const test_id = this.test_id;
|
||||
var test_ = this.describe.tests.items[test_id];
|
||||
var test_ = &this.describe.tests.items[test_id];
|
||||
if (from == .timeout) {
|
||||
test_.timeout_millis = @truncate(from.timeout);
|
||||
}
|
||||
|
||||
var describe = this.describe;
|
||||
describe.tests.items[test_id] = test_;
|
||||
|
||||
if (from == .timeout) {
|
||||
const vm = this.globalThis.bunVM();
|
||||
const cancel_result = vm.auto_killer.kill();
|
||||
@@ -1573,11 +1586,26 @@ pub const TestRunnerTask = struct {
|
||||
}
|
||||
|
||||
checkAssertionsCounter(result);
|
||||
processTestResult(this, this.globalThis, result.*, test_, test_id, describe);
|
||||
processTestResult(this, this.globalThis, result.*, test_, test_id, this.describe);
|
||||
}
|
||||
|
||||
fn processTestResult(this: *TestRunnerTask, globalThis: *JSGlobalObject, result: Result, test_: TestScope, test_id: u32, describe: *DescribeScope) void {
|
||||
switch (result.forceTODO(test_.tag == .todo)) {
|
||||
fn processTestResult(this: *TestRunnerTask, globalThis: *JSGlobalObject, result_original: Result, test_: *TestScope, test_id: u32, describe: *DescribeScope) void {
|
||||
var result = result_original.forceTODO(test_.tag == .todo);
|
||||
const test_dot_failing = test_.tag == .failing;
|
||||
switch (result) {
|
||||
.pass => |actual| {
|
||||
if (test_dot_failing) result = .{ .fail = actual };
|
||||
},
|
||||
.fail => |actual| {
|
||||
if (test_dot_failing) result = .{ .pass = actual };
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
if (result == .fail and test_.retry_count > 0) {
|
||||
test_.tag = .retry;
|
||||
return;
|
||||
}
|
||||
switch (result) {
|
||||
.pass => |count| Jest.runner.?.reportPass(
|
||||
test_id,
|
||||
this.source_file_path,
|
||||
@@ -1635,6 +1663,17 @@ pub const TestRunnerTask = struct {
|
||||
);
|
||||
},
|
||||
.pending => @panic("Unexpected pending test"),
|
||||
.retry => {
|
||||
bun.assert(test_.retry_count == 0);
|
||||
Jest.runner.?.reportFailure(
|
||||
test_id,
|
||||
this.source_file_path,
|
||||
test_.label,
|
||||
expect.active_test_expectation_counter.actual,
|
||||
this.started_at.sinceNow(),
|
||||
describe,
|
||||
);
|
||||
},
|
||||
}
|
||||
describe.onTestComplete(globalThis, test_id, result == .skip or (!Jest.runner.?.test_options.run_todo and result == .todo));
|
||||
|
||||
@@ -1672,6 +1711,7 @@ pub const Result = union(TestRunner.Test.Status) {
|
||||
fail_because_todo_passed: u32,
|
||||
fail_because_expected_has_assertions: void,
|
||||
fail_because_expected_assertion_count: Counter,
|
||||
retry: void,
|
||||
|
||||
pub fn isFailure(this: *const Result) bool {
|
||||
return this.* == .fail or this.* == .fail_because_expected_has_assertions or this.* == .fail_because_expected_assertion_count;
|
||||
@@ -1699,7 +1739,7 @@ fn appendParentLabel(
|
||||
try buffer.append(" ");
|
||||
}
|
||||
|
||||
inline fn createScope(
|
||||
fn createScope(
|
||||
globalThis: *JSGlobalObject,
|
||||
callframe: *CallFrame,
|
||||
comptime signature: string,
|
||||
@@ -1732,6 +1772,8 @@ inline fn createScope(
|
||||
}
|
||||
|
||||
var timeout_ms: u32 = std.math.maxInt(u32);
|
||||
var retry_count: u32 = 0;
|
||||
|
||||
if (options.isNumber()) {
|
||||
timeout_ms = @as(u32, @intCast(@max(args[2].coerce(i32, globalThis), 0)));
|
||||
} else if (options.isObject()) {
|
||||
@@ -1743,11 +1785,7 @@ inline fn createScope(
|
||||
timeout_ms = @as(u32, @intCast(@max(timeout.coerce(i32, globalThis), 0)));
|
||||
}
|
||||
if (options.get(globalThis, "retry")) |retries| {
|
||||
if (!retries.isNumber()) {
|
||||
globalThis.throwPretty("{s} expects retry to be a number", .{signature});
|
||||
return .zero;
|
||||
}
|
||||
// TODO: retry_count = @intCast(u32, @max(retries.coerce(i32, globalThis), 0));
|
||||
retry_count = validators.validateUint32(globalThis, retries, "{s}", .{"options.retry"}, false) catch return .zero;
|
||||
}
|
||||
if (options.get(globalThis, "repeats")) |repeats| {
|
||||
if (!repeats.isNumber()) {
|
||||
@@ -1820,6 +1858,7 @@ inline fn createScope(
|
||||
.func_arg = function_args,
|
||||
.func_has_callback = has_callback,
|
||||
.timeout_millis = timeout_ms,
|
||||
.retry_count = retry_count,
|
||||
}) catch unreachable;
|
||||
} else {
|
||||
var scope = allocator.create(DescribeScope) catch unreachable;
|
||||
@@ -1836,11 +1875,11 @@ inline fn createScope(
|
||||
return this;
|
||||
}
|
||||
|
||||
inline fn createIfScope(
|
||||
fn createIfScope(
|
||||
globalThis: *JSGlobalObject,
|
||||
callframe: *CallFrame,
|
||||
comptime property: [:0]const u8,
|
||||
comptime signature: string,
|
||||
comptime property: [:0]const u8,
|
||||
comptime Scope: type,
|
||||
comptime tag: Tag,
|
||||
) JSValue {
|
||||
@@ -1861,6 +1900,8 @@ inline fn createIfScope(
|
||||
.only => @compileError("unreachable"),
|
||||
.skip => .{ Scope.call, Scope.skip },
|
||||
.todo => .{ Scope.call, Scope.todo },
|
||||
.failing => @compileError("unreachable"),
|
||||
.retry => @compileError("unreachable"),
|
||||
};
|
||||
|
||||
switch (@intFromBool(value)) {
|
||||
@@ -2107,6 +2148,7 @@ fn eachBind(
|
||||
.func_arg = function_args,
|
||||
.func_has_callback = has_callback_function,
|
||||
.timeout_millis = timeout_ms,
|
||||
.retry_count = 0,
|
||||
}) catch unreachable;
|
||||
}
|
||||
} else {
|
||||
@@ -2129,11 +2171,11 @@ fn eachBind(
|
||||
return .undefined;
|
||||
}
|
||||
|
||||
inline fn createEach(
|
||||
fn createEach(
|
||||
globalThis: *JSGlobalObject,
|
||||
callframe: *CallFrame,
|
||||
comptime property: [:0]const u8,
|
||||
comptime signature: string,
|
||||
comptime property: [:0]const u8,
|
||||
comptime is_test: bool,
|
||||
) JSValue {
|
||||
const arguments = callframe.arguments(1);
|
||||
|
||||
@@ -846,6 +846,139 @@ describe("bun test", () => {
|
||||
});
|
||||
test.todo("check formatting for %p", () => {});
|
||||
});
|
||||
describe(".failing", () => {
|
||||
test("expect good fail", () => {
|
||||
const stderr = runTest({
|
||||
args: [],
|
||||
input: [
|
||||
`
|
||||
import { test, expect } from "bun:test";
|
||||
test.failing("the", () => {
|
||||
expect(6).toBe(6);
|
||||
});
|
||||
`,
|
||||
],
|
||||
});
|
||||
expect(stderr).toContain(` 0 pass\n 1 fail\n 1 expect() calls\nRan 1 tests across 1 files. `);
|
||||
});
|
||||
test("expect bad pass", () => {
|
||||
const stderr = runTest({
|
||||
args: [],
|
||||
input: [
|
||||
`
|
||||
import { test, expect } from "bun:test";
|
||||
test.failing("the", () => {
|
||||
expect(5).toBe(6);
|
||||
});
|
||||
`,
|
||||
],
|
||||
});
|
||||
expect(stderr).toContain(` 1 pass\n 0 fail\n 1 expect() calls\nRan 1 tests across 1 files. `);
|
||||
});
|
||||
test("done good fail", () => {
|
||||
const stderr = runTest({
|
||||
args: [],
|
||||
input: [
|
||||
`
|
||||
import { test, expect } from "bun:test";
|
||||
test.failing("the", done => {
|
||||
done();
|
||||
});
|
||||
`,
|
||||
],
|
||||
});
|
||||
expect(stderr).toContain(` 0 pass\n 1 fail\nRan 1 tests across 1 files. `);
|
||||
});
|
||||
test("done bad pass", () => {
|
||||
const stderr = runTest({
|
||||
args: [],
|
||||
input: [
|
||||
`
|
||||
import { test, expect } from "bun:test";
|
||||
test.failing("the", done => {
|
||||
done(42);
|
||||
});
|
||||
`,
|
||||
],
|
||||
});
|
||||
expect(stderr).toContain(` 1 pass\n 0 fail\nRan 1 tests across 1 files. `);
|
||||
});
|
||||
test("async good fail", () => {
|
||||
const stderr = runTest({
|
||||
args: [],
|
||||
input: [
|
||||
`
|
||||
import { test, expect } from "bun:test";
|
||||
test.failing("the", async () => {
|
||||
await Promise.resolve(42);
|
||||
});
|
||||
`,
|
||||
],
|
||||
});
|
||||
expect(stderr).toContain(` 0 pass\n 1 fail\nRan 1 tests across 1 files. `);
|
||||
});
|
||||
test("aysnc bad pass", () => {
|
||||
const stderr = runTest({
|
||||
args: [],
|
||||
input: [
|
||||
`
|
||||
import { test, expect } from "bun:test";
|
||||
test.failing("the", async () => {
|
||||
await Promise.reject(42);
|
||||
});
|
||||
`,
|
||||
],
|
||||
});
|
||||
expect(stderr).toContain(` 1 pass\n 0 fail\nRan 1 tests across 1 files. `);
|
||||
});
|
||||
});
|
||||
describe("{ retry }", () => {
|
||||
test("basic", () => {
|
||||
const stderr = runTest({
|
||||
args: [],
|
||||
input: [
|
||||
`
|
||||
import { test, expect } from "bun:test";
|
||||
let j = 0;
|
||||
test("the", async () => {
|
||||
expect(j++).toEqual(1);
|
||||
}, { retry: 1 });
|
||||
`,
|
||||
],
|
||||
});
|
||||
expect(stderr).toContain(` 1 pass\n 0 fail\n 2 expect() calls\nRan 1 tests across 1 files. `);
|
||||
});
|
||||
test("not enough", () => {
|
||||
const stderr = runTest({
|
||||
args: [],
|
||||
input: [
|
||||
`
|
||||
import { test, expect } from "bun:test";
|
||||
let j = 0;
|
||||
test("the", async () => {
|
||||
expect(j++).toEqual(1);
|
||||
}, { retry: 0 });
|
||||
`,
|
||||
],
|
||||
});
|
||||
expect(stderr).toContain(` 0 pass\n 1 fail\n 1 expect() calls\nRan 1 tests across 1 files. `);
|
||||
});
|
||||
test("not enough again", () => {
|
||||
const stderr = runTest({
|
||||
args: [],
|
||||
input: [
|
||||
`
|
||||
import { test, expect } from "bun:test";
|
||||
let j = 0;
|
||||
test("the", async () => {
|
||||
expect(j++).toEqual(5);
|
||||
}, { retry: 3 });
|
||||
`,
|
||||
],
|
||||
});
|
||||
expect(stderr).toContain(` 0 pass\n 1 fail\n 4 expect() calls\nRan 1 tests across 1 files. `);
|
||||
});
|
||||
});
|
||||
|
||||
test("path to a non-test.ts file will work", () => {
|
||||
const stderr = runTest({
|
||||
|
||||
Reference in New Issue
Block a user