Compare commits

...

2 Commits

Author SHA1 Message Date
Ashcon Partovi
147826d93c Add todoIf, fixme, and fixmeIf to bun:test 2024-02-21 17:16:04 -08:00
Ashcon Partovi
c0a2073dd5 Read .env file in .vscode/launch.json 2024-02-21 16:17:48 -08:00
12 changed files with 697 additions and 69 deletions

124
.vscode/launch.json generated vendored
View File

@@ -13,7 +13,7 @@
"request": "launch",
"name": "bun test [file]",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -27,7 +27,7 @@
"request": "launch",
"name": "bun test [file] (fast)",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -41,7 +41,7 @@
"request": "launch",
"name": "bun test [file] (verbose)",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -50,12 +50,26 @@
},
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test [file] --only",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["--env-file=${workspaceFolder}/.env", "test", "--only", "${file}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test [file] --watch",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "--watch", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "--watch", "${file}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -69,7 +83,7 @@
"request": "launch",
"name": "bun test [file] --hot",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "--hot", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "--hot", "${file}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -83,7 +97,7 @@
"request": "launch",
"name": "bun test [file] --inspect",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -103,7 +117,7 @@
"request": "launch",
"name": "bun test [file] --inspect-brk",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -235,7 +249,7 @@
"request": "launch",
"name": "bun test [...]",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -249,7 +263,7 @@
"request": "launch",
"name": "bun test [...] (fast)",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -263,7 +277,7 @@
"request": "launch",
"name": "bun test [...] (verbose)",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -272,12 +286,26 @@
},
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test [...] --only",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["--env-file=${workspaceFolder}/.env", "test", "--only", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
"BUN_DEBUG_QUIET_LOGS": "1",
"BUN_GARBAGE_COLLECTOR_LEVEL": "2",
},
"console": "internalConsole"
},
{
"type": "lldb",
"request": "launch",
"name": "bun test [...] --watch",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "--watch", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "--watch", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -291,7 +319,7 @@
"request": "launch",
"name": "bun test [...] --hot",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "--hot", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "--hot", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -305,7 +333,7 @@
"request": "launch",
"name": "bun test [...] --inspect",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -325,7 +353,7 @@
"request": "launch",
"name": "bun test [...] --inspect-brk",
"program": "${workspaceFolder}/build/bun-debug",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"env": {
"FORCE_COLOR": "1",
@@ -404,7 +432,7 @@
"request": "launch",
"name": "Windows: bun test [file]",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -426,7 +454,7 @@
"request": "launch",
"name": "Windows: bun test [file] (fast)",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -448,7 +476,7 @@
"request": "launch",
"name": "Windows: bun test [file] (verbose)",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -465,12 +493,34 @@
}
],
},
{
"type": "cppvsdbg",
"request": "launch",
"name": "Windows: bun test [file] --only",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["--env-file=${workspaceFolder}/.env", "test", "--only", "${file}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1"
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1"
},
{
"name": "BUN_GARBAGE_COLLECTOR_LEVEL",
"value": "2"
},
],
},
{
"type": "cppvsdbg",
"request": "launch",
"name": "Windows: bun test [file] --inspect",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -501,7 +551,7 @@
"request": "launch",
"name": "Windows: bun test [file] --inspect-brk",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${file}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${file}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -662,7 +712,7 @@
"request": "launch",
"name": "Windows: bun test [...]",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -684,7 +734,7 @@
"request": "launch",
"name": "Windows: bun test [...] (fast)",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -706,7 +756,7 @@
"request": "launch",
"name": "Windows: bun test [...] (verbose)",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -723,12 +773,34 @@
}
],
},
{
"type": "cppvsdbg",
"request": "launch",
"name": "Windows: bun test [...] --only",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["--env-file=${workspaceFolder}/.env", "test", "--only", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
"name": "FORCE_COLOR",
"value": "1"
},
{
"name": "BUN_DEBUG_QUIET_LOGS",
"value": "1"
},
{
"name": "BUN_GARBAGE_COLLECTOR_LEVEL",
"value": "2"
}
],
},
{
"type": "cppvsdbg",
"request": "launch",
"name": "Windows: bun test [...] --watch",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "--watch", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "--watch", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -750,7 +822,7 @@
"request": "launch",
"name": "Windows: bun test [...] --hot",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "--hot", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "--hot", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -772,7 +844,7 @@
"request": "launch",
"name": "Windows: bun test [...] --inspect",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{
@@ -803,7 +875,7 @@
"request": "launch",
"name": "Windows: bun test [...] --inspect-brk",
"program": "${workspaceFolder}/build/bun-debug.exe",
"args": ["test", "${input:testName}"],
"args": ["--env-file=${workspaceFolder}/.env", "test", "${input:testName}"],
"cwd": "${workspaceFolder}/test",
"environment": [
{

View File

@@ -186,6 +186,12 @@ declare module "bun:test" {
* @param fn the function that defines the tests
*/
todo(label: string, fn?: () => void): void;
/**
* Marks this group of tests as broken and in need of fixing.
* @param label the label for the tests
* @param fn the function that defines the tests
*/
fixme(label: string, fn?: () => void): void;
/**
* Runs this group of tests, only if `condition` is true.
*
@@ -387,6 +393,21 @@ declare module "bun:test" {
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
/**
* Marks this test as broken and in need of fixing.
*
* If the test function fails, it will be marked as `fixme` in the test results
* instead of `fail`. This is useful for marking tests that are known to be broken.
*
* @param label the label for the test
* @param fn the test function
* @param options the test timeout or options
*/
fixme(
label: string,
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
/**
* Runs this test, if `condition` is true.
*
@@ -413,6 +434,30 @@ declare module "bun:test" {
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
/**
* Marks this test as `todo`, if `condition` is true.
*
* @param condition if the test should be marked as `todo`
*/
todoIf(
condition: boolean,
): (
label: string,
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
/**
* Marks this test as `fixme`, if `condition` is true.
*
* @param condition if the test should be marked as `fixme`
*/
fixmeIf(
condition: boolean,
): (
label: string,
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
/**
* Returns a function that runs for each item in `table`.
*

View File

@@ -902,7 +902,7 @@ fn HandlerCallback(
this.global.bunVM().waitForPromise(promise);
const fail = promise.status(this.global.vm()) == .Rejected;
if (fail) {
this.global.bunVM().runErrorHandler(promise.result(this.global.vm()), null);
this.global.bunVM().onUnhandledError(this.global, promise.result(this.global.vm()));
}
return fail;
}

View File

@@ -3923,7 +3923,7 @@ pub const ServerWebSocket = struct {
pub fn onPing(this: *ServerWebSocket, _: uws.AnyWebSocket, data: []const u8) void {
log("onPing: {s}", .{data});
var handler = this.handler;
const handler = this.handler;
var cb = handler.onPing;
if (cb.isEmptyOrUndefinedOrNull()) return;
var globalThis = handler.globalObject;
@@ -3957,14 +3957,14 @@ pub const ServerWebSocket = struct {
if (result.toError()) |err| {
log("onPing error", .{});
handler.globalObject.bunVM().runErrorHandler(err, null);
globalThis.bunVM().runErrorHandler(err, null);
}
}
pub fn onPong(this: *ServerWebSocket, _: uws.AnyWebSocket, data: []const u8) void {
log("onPong: {s}", .{data});
var handler = this.handler;
const handler = this.handler;
var cb = handler.onPong;
if (cb.isEmptyOrUndefinedOrNull()) return;
@@ -3999,7 +3999,7 @@ pub const ServerWebSocket = struct {
if (result.toError()) |err| {
log("onPong error", .{});
handler.globalObject.bunVM().runErrorHandler(err, null);
globalThis.bunVM().runErrorHandler(err, null);
}
}

View File

@@ -2764,8 +2764,10 @@ pub const VirtualMachine = struct {
this.had_errors = true;
defer this.had_errors = prev_had_errors;
if (allow_side_effects and Output.is_github_action) {
defer printGithubAnnotation(exception);
if (comptime allow_side_effects) {
if (Output.is_github_action) {
defer printGithubAnnotation(exception);
}
}
const line_numbers = exception.stack.source_lines_numbers[0..exception.stack.source_lines_len];
@@ -3078,6 +3080,7 @@ pub const VirtualMachine = struct {
defer source_url.deinit();
const file = bun.path.relative(dir, source_url.slice());
const func = frame.function_name.toUTF8(allocator);
defer func.deinit();
if (file.len == 0 and func.len == 0) continue;

View File

@@ -57,6 +57,7 @@ pub const Tag = enum(u3) {
only,
skip,
todo,
fixme,
};
const debug = Output.scoped(.jest, false);
pub const TestRunner = struct {
@@ -185,6 +186,7 @@ pub const TestRunner = struct {
onTestFail: OnTestUpdate,
onTestSkip: OnTestUpdate,
onTestTodo: OnTestUpdate,
onTestFixme: OnTestUpdate,
};
pub fn reportPass(this: *TestRunner, test_id: Test.ID, file: string, label: string, expectations: u32, elapsed_ns: u64, parent: ?*DescribeScope) void {
@@ -207,6 +209,11 @@ pub const TestRunner = struct {
this.callback.onTestTodo(this.callback, test_id, file, label, 0, 0, parent);
}
pub fn reportFixme(this: *TestRunner, test_id: Test.ID, file: string, label: string, parent: ?*DescribeScope) void {
this.tests.items(.status)[test_id] = .fixme;
this.callback.onTestFixme(this.callback, test_id, file, label, 0, 0, parent);
}
pub fn addTestCount(this: *TestRunner, count: u32) u32 {
this.tests.ensureUnusedCapacity(this.allocator, count) catch unreachable;
const start = @as(Test.ID, @truncate(this.tests.len));
@@ -255,6 +262,7 @@ pub const TestRunner = struct {
fail,
skip,
todo,
fixme,
fail_because_todo_passed,
};
};
@@ -338,6 +346,11 @@ pub const Jest = struct {
ZigString.static("todo"),
JSC.NewFunction(globalObject, ZigString.static("todo"), 2, ThisTestScope.todo, false),
);
test_fn.put(
globalObject,
ZigString.static("fixme"),
JSC.NewFunction(globalObject, ZigString.static("fixme"), 2, ThisTestScope.fixme, false),
);
test_fn.put(
globalObject,
ZigString.static("if"),
@@ -348,6 +361,16 @@ pub const Jest = struct {
ZigString.static("skipIf"),
JSC.NewFunction(globalObject, ZigString.static("skipIf"), 2, ThisTestScope.skipIf, false),
);
test_fn.put(
globalObject,
ZigString.static("todoIf"),
JSC.NewFunction(globalObject, ZigString.static("todoIf"), 2, ThisTestScope.todoIf, false),
);
test_fn.put(
globalObject,
ZigString.static("fixmeIf"),
JSC.NewFunction(globalObject, ZigString.static("fixmeIf"), 2, ThisTestScope.fixmeIf, false),
);
test_fn.put(
globalObject,
ZigString.static("each"),
@@ -375,6 +398,11 @@ pub const Jest = struct {
ZigString.static("todo"),
JSC.NewFunction(globalObject, ZigString.static("todo"), 2, ThisDescribeScope.todo, false),
);
describe.put(
globalObject,
ZigString.static("fixme"),
JSC.NewFunction(globalObject, ZigString.static("fixme"), 2, ThisDescribeScope.fixme, false),
);
describe.put(
globalObject,
ZigString.static("if"),
@@ -385,6 +413,16 @@ pub const Jest = struct {
ZigString.static("skipIf"),
JSC.NewFunction(globalObject, ZigString.static("skipIf"), 2, ThisDescribeScope.skipIf, false),
);
describe.put(
globalObject,
ZigString.static("todoIf"),
JSC.NewFunction(globalObject, ZigString.static("todoIf"), 2, ThisDescribeScope.todoIf, false),
);
describe.put(
globalObject,
ZigString.static("fixmeIf"),
JSC.NewFunction(globalObject, ZigString.static("fixmeIf"), 2, ThisDescribeScope.fixmeIf, false),
);
describe.put(
globalObject,
ZigString.static("each"),
@@ -568,16 +606,28 @@ pub const TestScope = struct {
return createScope(globalThis, callframe, "test.todo()", true, .todo);
}
pub fn fixme(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createScope(globalThis, callframe, "test.fixme()", true, .fixme);
}
pub fn each(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createEach(globalThis, callframe, "test.each()", "each", true);
}
pub fn callIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createIfScope(globalThis, callframe, "test.if()", "if", TestScope, false);
return createIfScope(globalThis, callframe, "test.if()", "if", TestScope, false, .skip);
}
pub fn skipIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createIfScope(globalThis, callframe, "test.skipIf()", "skipIf", TestScope, true);
return createIfScope(globalThis, callframe, "test.skipIf()", "skipIf", TestScope, true, .skip);
}
pub fn todoIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createIfScope(globalThis, callframe, "test.todoIf()", "todoIf", TestScope, true, .todo);
}
pub fn fixmeIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createIfScope(globalThis, callframe, "test.fixmeIf()", "fixmeIf", TestScope, true, .fixme);
}
pub fn onReject(globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSValue {
@@ -649,7 +699,8 @@ pub const TestScope = struct {
vm.autoGarbageCollect();
}
JSC.markBinding(@src());
debug("test({})", .{bun.fmt.QuotedFormatter{ .text = this.label }});
debug("test({})", .{bun.fmt.quote(this.label)});
var initial_value = JSValue.zero;
if (test_elapsed_timer) |timer| {
@@ -795,8 +846,8 @@ pub const DescribeScope = struct {
}
pub fn shouldEvaluateScope(this: *const DescribeScope) bool {
if (this.tag == .skip or
this.tag == .todo) return false;
if (this.tag == .skip) return false;
if (this.tag == .todo and !Jest.runner.?.run_todo) return false;
if (Jest.runner.?.only and this.tag == .only) return true;
if (this.parent != null) return this.parent.?.shouldEvaluateScope();
return true;
@@ -1034,16 +1085,28 @@ pub const DescribeScope = struct {
return createScope(globalThis, callframe, "describe.todo()", false, .todo);
}
pub fn fixme(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createScope(globalThis, callframe, "describe.fixme()", false, .fixme);
}
pub fn each(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createEach(globalThis, callframe, "describe.each()", "each", false);
}
pub fn callIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createIfScope(globalThis, callframe, "describe.if()", "if", DescribeScope, false);
return createIfScope(globalThis, callframe, "describe.if()", "if", DescribeScope, false, .skip);
}
pub fn skipIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createIfScope(globalThis, callframe, "describe.skipIf()", "skipIf", DescribeScope, true);
return createIfScope(globalThis, callframe, "describe.skipIf()", "skipIf", DescribeScope, true, .skip);
}
pub fn todoIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createIfScope(globalThis, callframe, "describe.todoIf()", "todoIf", DescribeScope, true, .todo);
}
pub fn fixmeIf(globalThis: *JSGlobalObject, callframe: *CallFrame) callconv(.C) JSValue {
return createIfScope(globalThis, callframe, "describe.fixmeIf()", "fixmeIf", DescribeScope, true, .fixme);
}
pub fn run(this: *DescribeScope, globalObject: *JSC.JSGlobalObject, callback: JSC.JSValue, args: []const JSC.JSValue) JSC.JSValue {
@@ -1052,7 +1115,8 @@ pub const DescribeScope = struct {
defer callback.unprotect();
this.push();
defer this.pop();
debug("describe({})", .{bun.fmt.QuotedFormatter{ .text = this.label }});
debug("describe({})", .{bun.fmt.quote(this.label)});
if (callback == .zero) {
this.runTests(globalObject);
@@ -1208,8 +1272,11 @@ pub const WrappedTestScope = struct {
pub const only = wrapTestFunction("test", TestScope.only);
pub const skip = wrapTestFunction("test", TestScope.skip);
pub const todo = wrapTestFunction("test", TestScope.todo);
pub const fixme = wrapTestFunction("test", TestScope.fixme);
pub const callIf = wrapTestFunction("test", TestScope.callIf);
pub const skipIf = wrapTestFunction("test", TestScope.skipIf);
pub const todoIf = wrapTestFunction("test", TestScope.todoIf);
pub const fixmeIf = wrapTestFunction("test", TestScope.fixmeIf);
pub const each = wrapTestFunction("test", TestScope.each);
};
@@ -1218,8 +1285,11 @@ pub const WrappedDescribeScope = struct {
pub const only = wrapTestFunction("describe", DescribeScope.only);
pub const skip = wrapTestFunction("describe", DescribeScope.skip);
pub const todo = wrapTestFunction("describe", DescribeScope.todo);
pub const fixme = wrapTestFunction("describe", DescribeScope.fixme);
pub const callIf = wrapTestFunction("describe", DescribeScope.callIf);
pub const skipIf = wrapTestFunction("describe", DescribeScope.skipIf);
pub const todoIf = wrapTestFunction("describe", DescribeScope.todoIf);
pub const fixmeIf = wrapTestFunction("describe", DescribeScope.fixmeIf);
pub const each = wrapTestFunction("describe", DescribeScope.each);
};
@@ -1387,7 +1457,7 @@ pub const TestRunnerTask = struct {
}
fn processTestResult(this: *TestRunnerTask, globalThis: *JSC.JSGlobalObject, result: Result, test_: TestScope, test_id: u32, describe: *DescribeScope) void {
switch (result.forceTODO(test_.tag == .todo)) {
switch (result.fromTag(test_.tag)) {
.pass => |count| Jest.runner.?.reportPass(
test_id,
this.source_file_path,
@@ -1412,6 +1482,7 @@ pub const TestRunnerTask = struct {
),
.skip => Jest.runner.?.reportSkip(test_id, this.source_file_path, test_.label, describe),
.todo => Jest.runner.?.reportTodo(test_id, this.source_file_path, test_.label, describe),
.fixme => Jest.runner.?.reportFixme(test_id, this.source_file_path, test_.label, describe),
.fail_because_todo_passed => |count| {
Output.prettyErrorln(" <d>^<r> <red>this test is marked as todo but passes.<r> <d>Remove `.todo` or check that test is correct.<r>", .{});
Jest.runner.?.reportFailure(
@@ -1460,16 +1531,22 @@ pub const Result = union(TestRunner.Test.Status) {
fail: u32,
skip: void,
todo: void,
fixme: u32,
fail_because_todo_passed: u32,
pub fn forceTODO(this: Result, is_todo: bool) Result {
if (is_todo and this == .pass)
return .{ .fail_because_todo_passed = this.pass };
if (is_todo and this == .fail) {
return .{ .todo = {} };
}
return this;
pub fn fromTag(this: Result, tag: Tag) Result {
return switch (tag) {
.todo => switch (this) {
.pass => .{ .fail_because_todo_passed = this.pass },
.fail => .{ .todo = {} },
else => this,
},
.fixme => switch (this) {
.fail => .{ .fixme = this.fail },
else => this,
},
else => this,
};
}
};
@@ -1558,6 +1635,8 @@ inline fn createScope(
if (tag_to_use == .only or parent.tag == .only) {
Jest.runner.?.setOnly();
tag_to_use = .only;
} else if (tag_to_use == .pass and parent.tag == .fixme) {
tag_to_use = .fixme;
} else if (is_test and Jest.runner.?.only and parent.tag != .only) {
return .zero;
}
@@ -1634,6 +1713,7 @@ inline fn createIfScope(
comptime signature: string,
comptime Scope: type,
comptime is_skip: bool,
comptime tag: Tag,
) JSValue {
const arguments = callframe.arguments(1);
const args = arguments.slice();
@@ -1645,8 +1725,8 @@ inline fn createIfScope(
const name = ZigString.static(property);
const value = args[0].toBooleanSlow(globalThis);
const skip = if (is_skip) Scope.skip else Scope.call;
const call = if (is_skip) Scope.call else Scope.skip;
const skip = if (is_skip) @field(Scope, @tagName(tag)) else Scope.call;
const call = if (is_skip) Scope.call else @field(Scope, @tagName(tag));
if (value) {
return JSC.NewFunction(globalThis, name, 2, skip, false);

View File

@@ -306,7 +306,7 @@ pub const Run = struct {
if (vm.loadEntryPoint(this.entry_path)) |promise| {
if (promise.status(vm.global.vm()) == .Rejected) {
vm.runErrorHandler(promise.result(vm.global.vm()), null);
vm.onUnhandledError(vm.global, promise.result(vm.global.vm()));
if (vm.hot_reload != .none) {
vm.eventLoop().tick();

View File

@@ -56,6 +56,7 @@ fn fmtStatusTextLine(comptime status: @Type(.EnumLiteral), comptime emoji_or_col
.fail => Output.prettyFmt("<r><red>✗<r>", emoji_or_color),
.skip => Output.prettyFmt("<r><yellow>»<d>", emoji_or_color),
.todo => Output.prettyFmt("<r><magenta>✎<r>", emoji_or_color),
.fixme => Output.prettyFmt("<r><red>‼<r>", emoji_or_color),
else => @compileError("Invalid status " ++ @tagName(status)),
},
else => switch (status) {
@@ -63,6 +64,7 @@ fn fmtStatusTextLine(comptime status: @Type(.EnumLiteral), comptime emoji_or_col
.fail => Output.prettyFmt("<r><red>(fail)<r>", emoji_or_color),
.skip => Output.prettyFmt("<r><yellow>(skip)<d>", emoji_or_color),
.todo => Output.prettyFmt("<r><magenta>(todo)<r>", emoji_or_color),
.fixme => Output.prettyFmt("<r><red>(fixme)<r>", emoji_or_color),
else => @compileError("Invalid status " ++ @tagName(status)),
},
};
@@ -87,14 +89,16 @@ pub const CommandLineReporter = struct {
failures_to_repeat_buf: std.ArrayListUnmanaged(u8) = .{},
skips_to_repeat_buf: std.ArrayListUnmanaged(u8) = .{},
todos_to_repeat_buf: std.ArrayListUnmanaged(u8) = .{},
fixmes_to_repeat_buf: std.ArrayListUnmanaged(u8) = .{},
pub const Summary = struct {
pass: u32 = 0,
expectations: u32 = 0,
fail: u32 = 0,
skip: u32 = 0,
todo: u32 = 0,
fail: u32 = 0,
fixme: u32 = 0,
files: u32 = 0,
expectations: u32 = 0,
};
const DotColorMap = std.EnumMap(TestRunner.Test.Status, string);
@@ -262,8 +266,29 @@ pub const CommandLineReporter = struct {
this.jest.tests.items(.status)[id] = TestRunner.Test.Status.todo;
}
pub fn handleTestFixme(cb: *TestRunner.Callback, id: Test.ID, _: string, label: string, expectations: u32, elapsed_ns: u64, parent: ?*jest.DescribeScope) void {
var writer_: std.fs.File.Writer = Output.errorWriter();
var this: *CommandLineReporter = @fieldParentPtr(CommandLineReporter, "callback", cb);
// when the tests skip, we want to repeat the failures at the end
// so that you can see them better when there are lots of tests that ran
const initial_length = this.fixmes_to_repeat_buf.items.len;
var writer = this.fixmes_to_repeat_buf.writer(bun.default_allocator);
writeTestStatusLine(.fixme, &writer);
printTestLine(label, elapsed_ns, parent, false, writer);
writer_.writeAll(this.fixmes_to_repeat_buf.items[initial_length..]) catch unreachable;
Output.flush();
// this.updateDots();
this.summary.fixme += 1;
this.summary.expectations += expectations;
this.jest.tests.items(.status)[id] = TestRunner.Test.Status.fixme;
}
pub fn printSummary(this: *CommandLineReporter) void {
const tests = this.summary.fail + this.summary.pass + this.summary.skip + this.summary.todo;
const tests = this.summary.fail + this.summary.pass + this.summary.skip + this.summary.todo + this.summary.fixme;
const files = this.summary.files;
Output.prettyError("Ran {d} tests across {d} files. ", .{ tests, files });
@@ -581,8 +606,6 @@ pub const TestCommand = struct {
pub fn exec(ctx: Command.Context) !void {
if (comptime is_bindgen) unreachable;
Output.is_github_action = Output.isGithubAction();
// print the version so you know its doing stuff if it takes a sec
Output.prettyErrorln("<r><b>bun test <r><d>v" ++ Global.package_json_version_with_sha ++ "<r>", .{});
Output.flush();
@@ -632,6 +655,7 @@ pub const TestCommand = struct {
.onTestFail = CommandLineReporter.handleTestFail,
.onTestSkip = CommandLineReporter.handleTestSkip,
.onTestTodo = CommandLineReporter.handleTestTodo,
.onTestFixme = CommandLineReporter.handleTestFixme,
};
reporter.repeat_count = @max(ctx.test_options.repeat_count, 1);
reporter.jest.callback = &reporter.callback;
@@ -672,6 +696,12 @@ pub const TestCommand = struct {
try vm.bundler.configureDefines();
vm.loadExtraEnv();
if (env_loader.get("GITHUB_ACTIONS")) |value| {
if (strings.eqlComptime(value, "true")) {
Output.is_github_action = true;
}
}
vm.is_main_thread = true;
JSC.VirtualMachine.is_main_thread_vm = true;
@@ -794,6 +824,18 @@ pub const TestCommand = struct {
error_writer.writeAll(reporter.todos_to_repeat_buf.items) catch unreachable;
}
if (reporter.summary.fixme > 0) {
if (reporter.summary.todo > 0) {
Output.prettyError("\n", .{});
}
Output.prettyError("\n<r><d>{d} tests fixme:<r>\n", .{reporter.summary.fixme});
Output.flush();
var error_writer = Output.errorWriter();
error_writer.writeAll(reporter.fixmes_to_repeat_buf.items) catch unreachable;
}
if (reporter.summary.fail > 0) {
if (reporter.summary.skip > 0 or reporter.summary.todo > 0) {
Output.prettyError("\n", .{});
@@ -877,6 +919,10 @@ pub const TestCommand = struct {
Output.prettyError(" <r><magenta>{d:5>} todo<r>\n", .{reporter.summary.todo});
}
if (reporter.summary.fixme > 0) {
Output.prettyError(" <r><red>{d:5>} fixme<r>\n", .{reporter.summary.fixme});
}
if (reporter.summary.fail > 0) {
Output.prettyError("<r><red>", .{});
} else {

View File

@@ -6953,7 +6953,7 @@ pub const Macro = struct {
var loaded_result = try vm.loadMacroEntryPoint(input_specifier, function_name, specifier, hash);
if (loaded_result.status(vm.global.vm()) == JSC.JSPromise.Status.Rejected) {
vm.runErrorHandler(loaded_result.result(vm.global.vm()), null);
vm.onUnhandledError(vm.global, loaded_result.result(vm.global.vm()));
vm.disableMacroMode();
return error.MacroLoadError;
}
@@ -7064,7 +7064,7 @@ pub const Macro = struct {
) MacroError!Expr {
switch (comptime tag) {
.Error => {
this.macro.vm.runErrorHandler(value, null);
this.macro.vm.onUnhandledError(this.macro.vm.global, value);
return this.caller;
},
.Undefined => if (this.is_top_level)

View File

@@ -189,13 +189,6 @@ pub inline fn isEmojiEnabled() bool {
return enable_ansi_colors;
}
pub fn isGithubAction() bool {
if (bun.getenvZ("GITHUB_ACTIONS")) |value| {
return strings.eqlComptime(value, "true");
}
return false;
}
pub fn isVerbose() bool {
// Set by Github Actions when a workflow is run using debug mode.
if (bun.getenvZ("RUNNER_DEBUG")) |value| {

View File

@@ -850,6 +850,395 @@ describe("bun test", () => {
test.todo("check formatting for %p", () => {});
});
describe("test()", () => {
test("should run a test", () => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test("test #1", () => {});
`,
});
expect(stderr).toContain("test #1");
});
test("should run a test without a title", () => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test(() => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("UnreachableError");
});
});
describe("test.skip()", () => {
test("should skip a test", () => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.skip("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).not.toContain("UnreachableError");
});
});
describe("test.skipIf()", () => {
test.each(["true", "1", "{}", '"true"'])("should skip a test with a truthy condition: %s", truthy => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.skipIf(${truthy})("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).not.toContain("UnreachableError");
});
test.each(["false", "0", '""'])("should not skip a test with a falsey condition: %s", falsey => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.skipIf(${falsey})("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).toContain("UnreachableError");
});
});
describe("test.if()", () => {
test.each(["true", "1", "{}", '"true"'])("should run a test with a truthy condition: %s", truthy => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.if(${truthy})("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).toContain("UnreachableError");
});
test.each(["false", "0", '""'])("should not run a test with a falsey condition: %s", falsey => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.if(${falsey})("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).not.toContain("UnreachableError");
});
});
describe("test.todo()", () => {
test("should not run a todo by default", () => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.todo("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).not.toContain("UnreachableError");
});
test("should run a todo when enabled", () => {
const stderr = runTest({
args: ["--todo"],
input: `
import { test, expect } from "bun:test";
test.todo("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).toContain("UnreachableError");
});
});
describe("test.todoIf()", () => {
test.each(["true", "1", "{}", '"true"'])("should not run a todo with a truthy condition: %s", truthy => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.todoIf(${truthy})("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).not.toContain("UnreachableError");
});
test.each(["false", "0", '""'])("should run a todo with a falsey condition: %s", falsey => {
const stderr = runTest({
args: ["--todo"],
input: `
import { test, expect } from "bun:test";
test.todoIf(${falsey})("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).toContain("UnreachableError");
});
});
describe("test.fixme()", () => {
test("should run the test and pass", () => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.fixme("test #1", () => {});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).toContain("1 pass");
});
test("should run the test and fail", () => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.fixme("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).toContain("UnreachableError");
expect(stderr).toContain("0 fail");
expect(stderr).toContain("1 fixme");
});
});
describe("test.fixmeIf()", () => {
test.each(["true", "1", "{}", '"true"'])("should run a fixme with a truthy condition: %s", truthy => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.fixmeIf(${truthy})("test #1", () => {
expect.unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).toContain("UnreachableError");
expect(stderr).toContain("1 fixme");
expect(stderr).toContain("0 fail");
});
test.each(["false", "0", '""'])("should not run a fixme with a falsey condition: %s", falsey => {
const stderr = runTest({
input: `
import { test, expect } from "bun:test";
test.fixmeIf(${falsey})("test #1", () => {
expect().unreachable();
});
`,
});
expect(stderr).toContain("test #1");
expect(stderr).not.toContain("UnreachableError");
});
});
describe("describe.skip()", () => {
test("should skip a describe", () => {
const stderr = runTest({
input: `
import { describe, test, expect } from "bun:test";
describe.skip("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).not.toContain("UnreachableError");
});
});
describe("describe.skipIf()", () => {
test.each(["true", "1", "{}", '"true"'])("should skip a describe with a truthy condition: %s", truthy => {
const stderr = runTest({
input: `
import { describe, test, expect } from "bun:test";
describe.skipIf(${truthy})("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).not.toContain("UnreachableError");
});
test.each(["false", "0", '""'])("should not skip a describe with a falsey condition: %s", falsey => {
const stderr = runTest({
input: `
import { describe, test, expect } from "bun:test";
describe.skipIf(${falsey})("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).toContain("UnreachableError");
});
});
describe("describe.if()", () => {
test.each(["true", "1", "{}", '"true"'])("should run a describe with a truthy condition: %s", truthy => {
const stderr = runTest({
input: `
import { describe, test, expect } from "bun:test";
describe.if(${truthy})("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).toContain("UnreachableError");
});
test.each(["false", "0", '""'])("should not run a describe with a falsey condition: %s", falsey => {
const stderr = runTest({
input: `
import { describe, test, expect } from "bun:test";
describe.if(${falsey})("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).not.toContain("UnreachableError");
});
});
describe("describe.todo()", () => {
test("should not run a todo by default", () => {
const stderr = runTest({
input: `
import { describe, test, expect } from "bun:test";
describe.todo("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).not.toContain("UnreachableError");
});
test("should run a todo when enabled", () => {
const stderr = runTest({
args: ["--todo"],
input: `
import { describe, test, expect } from "bun:test";
describe.todo("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).toContain("UnreachableError");
});
});
describe("describe.todoIf()", () => {
test.each(["true", "1", "{}", '"true"'])("should not run a todo with a truthy condition: %s", truthy => {
const stderr = runTest({
input: `
import { describe, test, expect } from "bun:test";
describe.todoIf(${truthy})("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).not.toContain("UnreachableError");
});
test.each(["false", "0", '""'])("should run a todo with a falsey condition: %s", falsey => {
const stderr = runTest({
args: ["--todo"],
input: `
import { describe, test, expect } from "bun:test";
describe.todoIf(${falsey})("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).toContain("UnreachableError");
});
});
describe("describe.fixme()", () => {
test("should run the describe and pass", () => {
const stderr = runTest({
input: `
import { describe, test, expect } from "bun:test";
describe.fixme("describe #1", () => {
test("test #1", () => {});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).toContain("1 pass");
});
test("should run the describe and fail", () => {
const stderr = runTest({
input: `
import { describe, test, expect } from "bun:test";
describe.fixme("describe #1", () => {
test("test #1", () => {
expect.unreachable();
});
});
`,
});
expect(stderr).toContain("describe #1 > test #1");
expect(stderr).toContain("UnreachableError");
expect(stderr).toContain("0 fail");
expect(stderr).toContain("1 fixme");
});
});
test("path to a non-test.ts file will work", () => {
const stderr = runTest({
args: ["./index.ts"],

View File

@@ -1363,12 +1363,12 @@ it("should support promise returned from error", async () => {
}
if (e.message.endsWith("/async-rejected")) {
throw new Error("");
throw new Error();
}
if (e.message.endsWith("/async-rejected-pending")) {
await Bun.sleep(100);
throw new Error("");
throw new Error();
}
if (e.message.endsWith("/async-pending")) {