Compare commits

...

8 Commits

Author SHA1 Message Date
autofix-ci[bot]
0d49f2bf7a [autofix.ci] apply automated fixes 2025-08-01 03:23:11 +00:00
pfg
57917c2174 fix 2025-07-31 20:18:12 -07:00
pfg
4e3b73bdc0 have to support second number 2025-07-31 20:17:34 -07:00
pfg
9953023ded this is terrible. do we really need types? 2025-07-31 20:00:41 -07:00
pfg
9c0de1b11d remove 2025-07-31 19:53:56 -07:00
pfg
10c7c21965 rmv 2025-07-31 19:52:51 -07:00
pfg
6c8706a0d0 . 2025-07-31 19:51:49 -07:00
Claude Bot
422e47961b Add support for test() options as second parameter
Allows using test() with options as the second parameter:
- test("name", { timeout: 1000 }, () => { ... })
- test("name", 500, () => { ... })

While maintaining backward compatibility with the original syntax:
- test("name", () => { ... }, { timeout: 1000 })
- test("name", () => { ... }, 500)

This feature improves consistency with other testing frameworks
and provides a more intuitive parameter order.

Changes:
- Updated argument parsing in jest.zig createScope function
- Added TypeScript type overloads for test, test.only, test.skip, test.todo
- Added regression tests to ensure both syntaxes work
- Updated type tests to verify TypeScript compatibility

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-01 02:50:08 +00:00
6 changed files with 154 additions and 10 deletions

View File

@@ -229,6 +229,9 @@ declare module "bun:test" {
each<T extends Readonly<[any, ...any[]]>>(
table: readonly T[],
): (label: DescribeLabel, fn: (...args: [...T]) => void | Promise<unknown>, options?: number | TestOptions) => void;
each<T extends Readonly<[any, ...any[]]>>(
table: readonly T[],
): (label: DescribeLabel, options: number | TestOptions, fn: (...args: [...T]) => void | Promise<unknown>) => void;
each<T extends any[]>(
table: readonly T[],
): (
@@ -236,9 +239,19 @@ declare module "bun:test" {
fn: (...args: Readonly<T>) => void | Promise<unknown>,
options?: number | TestOptions,
) => void;
each<T extends any[]>(
table: readonly T[],
): (
label: DescribeLabel,
options: number | TestOptions,
fn: (...args: Readonly<T>) => void | Promise<unknown>,
) => void;
each<T>(
table: T[],
): (label: DescribeLabel, fn: (...args: T[]) => void | Promise<unknown>, options?: number | TestOptions) => void;
each<T>(
table: T[],
): (label: DescribeLabel, options: number | TestOptions, fn: (...args: T[]) => void | Promise<unknown>) => void;
}
/**
* Describes a group of related tests.
@@ -379,6 +392,18 @@ declare module "bun:test" {
*/
options?: number | TestOptions,
): void;
(
label: string,
/**
* - 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,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
): void;
/**
* Skips all other tests, except this test when run with the `--only` option.
*
@@ -391,6 +416,11 @@ declare module "bun:test" {
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
only(
label: string,
options: number | TestOptions,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
): void;
/**
* Skips this test.
*
@@ -403,6 +433,11 @@ declare module "bun:test" {
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
skip(
label: string,
options: number | TestOptions,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
): void;
/**
* Marks this test as to be written or to be fixed.
*
@@ -420,6 +455,11 @@ declare module "bun:test" {
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
todo(
label: string,
options: number | TestOptions,
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
): void;
/**
* Marks this test as failing.
*
@@ -440,6 +480,11 @@ declare module "bun:test" {
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
): void;
failing(
label: string,
options: number | TestOptions,
fn?: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
): void;
/**
* Runs this test, if `condition` is true.
*
@@ -454,6 +499,13 @@ declare module "bun:test" {
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
if(
condition: boolean,
): (
label: string,
options: number | TestOptions,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
) => void;
/**
* Skips this test, if `condition` is true.
*
@@ -466,6 +518,13 @@ declare module "bun:test" {
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
skipIf(
condition: boolean,
): (
label: string,
options: number | TestOptions,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
) => void;
/**
* Marks this test as to be written or to be fixed, if `condition` is true.
*
@@ -478,6 +537,13 @@ declare module "bun:test" {
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
options?: number | TestOptions,
) => void;
todoIf(
condition: boolean,
): (
label: string,
options: number | TestOptions,
fn: (() => void | Promise<unknown>) | ((done: (err?: unknown) => void) => void),
) => void;
/**
* Returns a function that runs for each item in `table`.
*
@@ -486,12 +552,21 @@ declare module "bun:test" {
each<T extends Readonly<[any, ...any[]]>>(
table: readonly T[],
): (label: string, fn: (...args: [...T]) => void | Promise<unknown>, options?: number | TestOptions) => void;
each<T extends Readonly<[any, ...any[]]>>(
table: readonly T[],
): (label: string, options: number | TestOptions, fn: (...args: [...T]) => void | Promise<unknown>) => void;
each<T extends any[]>(
table: readonly T[],
): (label: string, fn: (...args: Readonly<T>) => void | Promise<unknown>, options?: number | TestOptions) => void;
each<T extends any[]>(
table: readonly T[],
): (label: string, options: number | TestOptions, fn: (...args: Readonly<T>) => void | Promise<unknown>) => void;
each<T>(
table: T[],
): (label: string, fn: (...args: T[]) => void | Promise<unknown>, options?: number | TestOptions) => void;
each<T>(
table: T[],
): (label: string, options: number | TestOptions, fn: (...args: T[]) => void | Promise<unknown>) => void;
}
/**
* Runs a test.

8
simple.test.js Normal file
View File

@@ -0,0 +1,8 @@
// Test just the original syntax first to make sure it works
import { test, expect } from "bun:test";
test("original syntax still works", () => {
expect(true).toBe(true);
}, { timeout: 1000 });
console.log("Original syntax test defined successfully");

View File

@@ -1802,18 +1802,14 @@ inline fn createScope(
comptime tag: Tag,
) bun.JSError!JSValue {
const this = callframe.this();
const arguments = callframe.arguments_old(3);
const args = arguments.slice();
if (args.len == 0) {
if (callframe.argumentsCount() == 0) {
return globalThis.throwPretty("{s} expects a description or function", .{signature});
}
var description = args[0];
var function = if (args.len > 1) args[1] else .zero;
var options = if (args.len > 2) args[2] else .zero;
var description, var function, var options = callframe.argumentsAsArray(3);
if (args.len == 1 and description.isFunction()) {
if (description.isFunction()) {
function = description;
description = .zero;
} else {
@@ -1827,9 +1823,14 @@ inline fn createScope(
return globalThis.throwPretty("{s} expects first argument to be a named class, named function, number, or string", .{signature});
}
// Handle the case where options are the second parameter and function is the third
if (!function.isFunction()) {
std.mem.swap(JSValue, &function, &options);
}
if (!function.isFunction()) {
if (tag != .todo and tag != .skip) {
return globalThis.throwPretty("{s} expects second argument to be a function", .{signature});
return globalThis.throwPretty("{s} expects second argument to be a function or object, and third argument to be a function", .{signature});
}
}
}
@@ -1866,7 +1867,7 @@ inline fn createScope(
var timeout_ms: u32 = std.math.maxInt(u32);
if (options.isNumber()) {
timeout_ms = @as(u32, @intCast(@max(try args[2].coerce(i32, globalThis), 0)));
timeout_ms = @as(u32, @intCast(@max(try options.coerce(i32, globalThis), 0)));
} else if (options.isObject()) {
if (try options.get(globalThis, "timeout")) |timeout| {
if (!timeout.isNumber()) {

View File

@@ -53,6 +53,31 @@ describe("bun:test", () => {
expect(undefined).toBeUndefined();
expect(undefined).not.toBeDefined();
});
// Test the new syntax - options as second parameter
test("new syntax - options as second parameter", { timeout: 1000 }, () => {
expect(true).toBe(true);
});
// Test with number options as second parameter
test("new syntax - number as second parameter", 500, () => {
expect(true).toBe(true);
});
// Test with only() using new syntax
test.only("only with new syntax", { timeout: 1000 }, () => {
expect(true).toBe(true);
});
// Test with skip() using new syntax
test.skip("skip with new syntax", { timeout: 1000 }, () => {
expect(true).toBe(true);
});
// Test with todo() using new syntax
test.todo("todo with new syntax", { timeout: 1000 }, () => {
expect(true).toBe(true);
});
});
// inference should work when data is passed directly in

View File

@@ -3,7 +3,7 @@
" == undefined": 0,
"!= alloc.ptr": 0,
"!= allocator.ptr": 0,
".arguments_old(": 280,
".arguments_old(": 279,
".stdDir()": 40,
".stdFile()": 18,
"// autofix": 168,

View File

@@ -0,0 +1,35 @@
// Test that both the original and new syntax work correctly
// This is a regression test to ensure the new parameter order doesn't break existing usage
describe("test() parameter order", () => {
// Original syntax: test(name, fn, options)
test(
"original syntax works",
() => {
expect(true).toBe(true);
},
{ timeout: 1000 },
);
test("original syntax with number timeout", () => {
expect(true).toBe(true);
}, 500);
// Vitest compatability syntax: test(name, options, fn)
test("vitest compatability syntax - options object as second parameter", { timeout: 1000 }, () => {
expect(true).toBe(true);
});
test("vitest compatability syntax - number as second parameter", 500, () => {
expect(true).toBe(true);
});
// Test other methods work with vitest compatability syntax
test.skip("skip with vitest compatability syntax", { timeout: 1000 }, () => {
expect(true).toBe(true);
});
test.todo("todo with vitest-compatible syntax", { timeout: 1000 });
test.skip("skip with options but no function", 300);
});