mirror of
https://github.com/oven-sh/bun
synced 2026-02-16 13:51:47 +00:00
Add --resolve-extensions flag and bunfig.toml property for custom test file patterns
This change allows users to customize which file name patterns are recognized as test files, providing flexibility for projects with different naming conventions. Features: - CLI flag: --resolve-extensions <STR>... (can specify multiple) - bunfig.toml: [test] resolveExtensions = ".check" or [".check", ".verify"] - CLI flag takes precedence over bunfig.toml configuration - Dynamic error messages show configured extensions when no tests found - Works with all JavaScript/TypeScript file extensions (.ts, .tsx, .js, .jsx, etc.) Default behavior (unchanged): - .test, _test, .spec, _spec suffixes continue to work when no custom config Implementation: - Modified Scanner.zig to accept custom test name suffixes - Added resolve_extensions field to TestOptions in cli.zig - Added CLI argument parsing in Arguments.zig - Added bunfig.toml parsing in bunfig.zig with CLI override logic - Updated error messages in test_command.zig to show custom extensions - Added comprehensive test suite covering CLI, bunfig, and override scenarios 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -470,6 +470,37 @@ pub const Bunfig = struct {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if (test_.get("resolveExtensions")) |expr| brk: {
|
||||
// Only apply bunfig value if not already set via CLI
|
||||
if (this.ctx.test_options.resolve_extensions != null) break :brk;
|
||||
|
||||
switch (expr.data) {
|
||||
.e_string => |str| {
|
||||
const pattern = try str.string(allocator);
|
||||
const patterns = try allocator.alloc(string, 1);
|
||||
patterns[0] = pattern;
|
||||
this.ctx.test_options.resolve_extensions = patterns;
|
||||
},
|
||||
.e_array => |arr| {
|
||||
if (arr.items.len == 0) break :brk;
|
||||
|
||||
const patterns = try allocator.alloc(string, arr.items.len);
|
||||
for (arr.items.slice(), 0..) |item, i| {
|
||||
if (item.data != .e_string) {
|
||||
try this.addError(item.loc, "resolveExtensions array must contain only strings");
|
||||
return;
|
||||
}
|
||||
patterns[i] = try item.data.e_string.string(allocator);
|
||||
}
|
||||
this.ctx.test_options.resolve_extensions = patterns;
|
||||
},
|
||||
else => {
|
||||
try this.addError(expr.loc, "resolveExtensions must be a string or array of strings");
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -353,6 +353,7 @@ pub const Command = struct {
|
||||
test_filter_pattern: ?[]const u8 = null,
|
||||
test_filter_regex: ?*RegularExpression = null,
|
||||
max_concurrency: u32 = 20,
|
||||
resolve_extensions: ?[]const []const u8 = null,
|
||||
|
||||
reporters: struct {
|
||||
dots: bool = false,
|
||||
|
||||
@@ -219,6 +219,7 @@ pub const test_only_params = [_]ParamType{
|
||||
clap.parseParam("--dots Enable dots reporter. Shorthand for --reporter=dots.") catch unreachable,
|
||||
clap.parseParam("--only-failures Only display test failures, hiding passing tests.") catch unreachable,
|
||||
clap.parseParam("--max-concurrency <NUMBER> Maximum number of concurrent tests to execute at once. Default is 20.") catch unreachable,
|
||||
clap.parseParam("--resolve-extensions <STR>... Test file name suffixes to match. Defaults to ['.test', '_test', '.spec', '_spec'].") catch unreachable,
|
||||
};
|
||||
pub const test_params = test_only_params ++ runtime_params_ ++ transpiler_params_ ++ base_params_;
|
||||
|
||||
@@ -566,6 +567,15 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
|
||||
std.process.exit(1);
|
||||
};
|
||||
}
|
||||
|
||||
if (args.options("--resolve-extensions").len > 0) {
|
||||
const extensions = args.options("--resolve-extensions");
|
||||
const patterns = try allocator.alloc(string, extensions.len);
|
||||
for (extensions, 0..) |ext, i| {
|
||||
patterns[i] = ext;
|
||||
}
|
||||
ctx.test_options.resolve_extensions = patterns;
|
||||
}
|
||||
}
|
||||
|
||||
ctx.args.absolute_working_dir = cwd;
|
||||
|
||||
@@ -14,6 +14,9 @@ scan_dir_buf: bun.PathBuffer = undefined,
|
||||
options: *BundleOptions,
|
||||
has_iterated: bool = false,
|
||||
search_count: usize = 0,
|
||||
/// Custom test name suffixes to use instead of the default ones.
|
||||
/// If null, uses the default test_name_suffixes.
|
||||
custom_test_name_suffixes: ?[]const []const u8 = null,
|
||||
|
||||
const log = bun.Output.scoped(.jest, .hidden);
|
||||
const Fifo = bun.LinearFifo(ScanEntry, .Dynamic);
|
||||
@@ -129,8 +132,16 @@ pub fn couldBeTestFile(this: *Scanner, name: []const u8, comptime needs_test_suf
|
||||
if (extname.len == 0 or !this.options.loader(extname).isJavaScriptLike()) return false;
|
||||
if (comptime !needs_test_suffix) return true;
|
||||
const name_without_extension = name[0 .. name.len - extname.len];
|
||||
inline for (test_name_suffixes) |suffix| {
|
||||
if (strings.endsWithComptime(name_without_extension, suffix)) return true;
|
||||
|
||||
// Use custom suffixes if provided, otherwise use defaults
|
||||
if (this.custom_test_name_suffixes) |custom_suffixes| {
|
||||
for (custom_suffixes) |suffix| {
|
||||
if (strings.endsWith(name_without_extension, suffix)) return true;
|
||||
}
|
||||
} else {
|
||||
inline for (test_name_suffixes) |suffix| {
|
||||
if (strings.endsWithComptime(name_without_extension, suffix)) return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -1447,6 +1447,7 @@ pub const TestCommand = struct {
|
||||
|
||||
var scanner = bun.handleOom(Scanner.init(ctx.allocator, &vm.transpiler, ctx.positionals.len));
|
||||
defer scanner.deinit();
|
||||
scanner.custom_test_name_suffixes = ctx.test_options.resolve_extensions;
|
||||
const has_relative_path = for (ctx.positionals) |arg| {
|
||||
if (std.fs.path.isAbsolute(arg) or
|
||||
strings.startsWith(arg, "./") or
|
||||
@@ -1590,15 +1591,39 @@ pub const TestCommand = struct {
|
||||
if (ctx.positionals.len < 2) {
|
||||
if (Output.isAIAgent()) {
|
||||
// Be very clear to ai.
|
||||
Output.errGeneric("0 test files matching **{{.test,.spec,_test_,_spec_}}.{{js,ts,jsx,tsx}} in --cwd={f}", .{bun.fmt.quote(bun.fs.FileSystem.instance.top_level_dir)});
|
||||
const suffixes = ctx.test_options.resolve_extensions orelse &Scanner.test_name_suffixes;
|
||||
var buf = bun.MutableString.initEmpty(bun.default_allocator);
|
||||
defer buf.deinit();
|
||||
var writer = buf.writer();
|
||||
writer.writeAll("0 test files matching **{") catch {};
|
||||
for (suffixes, 0..) |suffix, i| {
|
||||
if (i > 0) writer.writeAll(",") catch {};
|
||||
writer.writeAll(suffix) catch {};
|
||||
}
|
||||
writer.writeAll("}.{js,ts,jsx,tsx} in --cwd=") catch {};
|
||||
Output.errGeneric("{s}{f}", .{ buf.list.items, bun.fmt.quote(bun.fs.FileSystem.instance.top_level_dir) });
|
||||
} else {
|
||||
// Be friendlier to humans.
|
||||
const suffixes = ctx.test_options.resolve_extensions orelse &Scanner.test_name_suffixes;
|
||||
var buf = bun.MutableString.initEmpty(bun.default_allocator);
|
||||
defer buf.deinit();
|
||||
var writer = buf.writer();
|
||||
for (suffixes, 0..) |suffix, i| {
|
||||
if (i > 0) {
|
||||
if (i == suffixes.len - 1) {
|
||||
writer.writeAll(" or ") catch {};
|
||||
} else {
|
||||
writer.writeAll(", ") catch {};
|
||||
}
|
||||
}
|
||||
writer.print("\"{s}\"", .{suffix}) catch {};
|
||||
}
|
||||
Output.prettyErrorln(
|
||||
\\<yellow>No tests found!<r>
|
||||
\\
|
||||
\\Tests need ".test", "_test_", ".spec" or "_spec_" in the filename <d>(ex: "MyApp.test.ts")<r>
|
||||
\\Tests need {s} in the filename <d>(ex: "MyApp{s}.ts")<r>
|
||||
\\
|
||||
, .{});
|
||||
, .{ buf.list.items, suffixes[0] });
|
||||
}
|
||||
} else {
|
||||
if (Output.isAIAgent()) {
|
||||
@@ -1624,11 +1649,26 @@ pub const TestCommand = struct {
|
||||
Output.printStartEnd(ctx.start_time, std.time.nanoTimestamp());
|
||||
}
|
||||
|
||||
const suffixes = ctx.test_options.resolve_extensions orelse &Scanner.test_name_suffixes;
|
||||
var buf = bun.MutableString.initEmpty(bun.default_allocator);
|
||||
defer buf.deinit();
|
||||
var writer = buf.writer();
|
||||
for (suffixes, 0..) |suffix, i| {
|
||||
if (i > 0) {
|
||||
if (i == suffixes.len - 1) {
|
||||
writer.writeAll(" or ") catch {};
|
||||
} else {
|
||||
writer.writeAll(", ") catch {};
|
||||
}
|
||||
}
|
||||
writer.print("\"{s}\"", .{suffix}) catch {};
|
||||
}
|
||||
|
||||
Output.prettyErrorln(
|
||||
\\
|
||||
\\
|
||||
\\<blue>note<r><d>:<r> Tests need ".test", "_test_", ".spec" or "_spec_" in the filename <d>(ex: "MyApp.test.ts")<r>
|
||||
, .{});
|
||||
\\<blue>note<r><d>:<r> Tests need {s} in the filename <d>(ex: "MyApp{s}.ts")<r>
|
||||
, .{ buf.list.items, suffixes[0] });
|
||||
|
||||
// print a helpful note
|
||||
if (has_file_like) |i| {
|
||||
|
||||
79
test/cli/test-resolve-extensions.md
Normal file
79
test/cli/test-resolve-extensions.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# Test Resolve Extensions
|
||||
|
||||
The `--resolve-extensions` flag and `resolveExtensions` bunfig.toml property allow you to customize which file name patterns are recognized as test files.
|
||||
|
||||
## Default Behavior
|
||||
|
||||
By default, Bun recognizes test files with these suffixes:
|
||||
- `.test` (e.g., `myfile.test.ts`)
|
||||
- `_test` (e.g., `myfile_test.ts`)
|
||||
- `.spec` (e.g., `myfile.spec.ts`)
|
||||
- `_spec` (e.g., `myfile_spec.ts`)
|
||||
|
||||
## Usage
|
||||
|
||||
### CLI Flag
|
||||
|
||||
```bash
|
||||
# Use a single custom extension
|
||||
bun test --resolve-extensions .check
|
||||
|
||||
# Use multiple custom extensions
|
||||
bun test --resolve-extensions .check --resolve-extensions .verify
|
||||
```
|
||||
|
||||
### bunfig.toml
|
||||
|
||||
```toml
|
||||
[test]
|
||||
# Single extension
|
||||
resolveExtensions = ".check"
|
||||
|
||||
# Multiple extensions
|
||||
resolveExtensions = [".check", ".verify"]
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Custom test suffix
|
||||
|
||||
If you prefer using `.check.ts` for your test files:
|
||||
|
||||
```toml
|
||||
# bunfig.toml
|
||||
[test]
|
||||
resolveExtensions = ".check"
|
||||
```
|
||||
|
||||
Now `myfile.check.ts` will be recognized as a test file, but `myfile.test.ts` will not.
|
||||
|
||||
### Multiple custom patterns
|
||||
|
||||
You can mix different patterns:
|
||||
|
||||
```toml
|
||||
# bunfig.toml
|
||||
[test]
|
||||
resolveExtensions = [".check", ".verify", "_integration"]
|
||||
```
|
||||
|
||||
This will recognize:
|
||||
- `myfile.check.ts`
|
||||
- `myfile.verify.ts`
|
||||
- `myfile_integration.ts`
|
||||
|
||||
### Override via CLI
|
||||
|
||||
The CLI flag takes precedence over bunfig.toml:
|
||||
|
||||
```bash
|
||||
# Even if bunfig.toml has resolveExtensions = ".check"
|
||||
# This will only run .verify files
|
||||
bun test --resolve-extensions .verify
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The extensions are matched against the filename **before** the file extension (`.ts`, `.js`, etc.)
|
||||
- All standard JavaScript/TypeScript file extensions (`.ts`, `.tsx`, `.js`, `.jsx`, `.mjs`, `.cjs`) are still supported
|
||||
- When custom extensions are specified, the default patterns (`.test`, `_test`, `.spec`, `_spec`) are **replaced**, not supplemented
|
||||
379
test/cli/test-resolve-extensions.test.ts
Normal file
379
test/cli/test-resolve-extensions.test.ts
Normal file
@@ -0,0 +1,379 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { bunEnv, bunExe, tempDirWithFiles } from "harness";
|
||||
|
||||
describe("bun test --resolve-extensions", () => {
|
||||
test("CLI flag allows custom test file suffixes", async () => {
|
||||
const dir = tempDirWithFiles("resolve-extensions-cli", {
|
||||
"mytest.check.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("check test", () => {
|
||||
console.log("RUNNING: check test");
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
`,
|
||||
"regular.test.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("regular test", () => {
|
||||
console.log("RUNNING: regular test");
|
||||
expect(2).toBe(2);
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test", "--resolve-extensions", ".check"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const output = stdout + stderr;
|
||||
expect(output).toContain("RUNNING: check test");
|
||||
expect(output).not.toContain("RUNNING: regular test");
|
||||
expect(output).toContain("1 pass");
|
||||
});
|
||||
|
||||
test("CLI flag supports multiple extensions", async () => {
|
||||
const dir = tempDirWithFiles("resolve-extensions-multiple", {
|
||||
"alpha.check.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("check test", () => {
|
||||
console.log("RUNNING: check");
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
`,
|
||||
"beta.verify.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("verify test", () => {
|
||||
console.log("RUNNING: verify");
|
||||
expect(2).toBe(2);
|
||||
});
|
||||
`,
|
||||
"gamma.test.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("regular test", () => {
|
||||
console.log("RUNNING: regular");
|
||||
expect(3).toBe(3);
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test", "--resolve-extensions", ".check", "--resolve-extensions", ".verify"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const output = stdout + stderr;
|
||||
expect(output).toContain("RUNNING: check");
|
||||
expect(output).toContain("RUNNING: verify");
|
||||
expect(output).not.toContain("RUNNING: regular");
|
||||
expect(output).toContain("2 pass");
|
||||
});
|
||||
|
||||
test("bunfig.toml resolveExtensions as string", async () => {
|
||||
const dir = tempDirWithFiles("resolve-extensions-bunfig-string", {
|
||||
"mytest.check.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("check test", () => {
|
||||
console.log("RUNNING: check test");
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
`,
|
||||
"regular.test.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("regular test", () => {
|
||||
console.log("RUNNING: regular test");
|
||||
expect(2).toBe(2);
|
||||
});
|
||||
`,
|
||||
"bunfig.toml": `[test]\nresolveExtensions = ".check"`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const output = stdout + stderr;
|
||||
expect(output).toContain("RUNNING: check test");
|
||||
expect(output).not.toContain("RUNNING: regular test");
|
||||
expect(output).toContain("1 pass");
|
||||
});
|
||||
|
||||
test("bunfig.toml resolveExtensions as array", async () => {
|
||||
const dir = tempDirWithFiles("resolve-extensions-bunfig-array", {
|
||||
"alpha.check.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("check test", () => {
|
||||
console.log("RUNNING: check");
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
`,
|
||||
"beta.verify.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("verify test", () => {
|
||||
console.log("RUNNING: verify");
|
||||
expect(2).toBe(2);
|
||||
});
|
||||
`,
|
||||
"gamma.test.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("regular test", () => {
|
||||
console.log("RUNNING: regular");
|
||||
expect(3).toBe(3);
|
||||
});
|
||||
`,
|
||||
"bunfig.toml": `[test]\nresolveExtensions = [".check", ".verify"]`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const output = stdout + stderr;
|
||||
expect(output).toContain("RUNNING: check");
|
||||
expect(output).toContain("RUNNING: verify");
|
||||
expect(output).not.toContain("RUNNING: regular");
|
||||
expect(output).toContain("2 pass");
|
||||
});
|
||||
|
||||
test("CLI flag overrides bunfig.toml", async () => {
|
||||
const dir = tempDirWithFiles("resolve-extensions-cli-override", {
|
||||
"alpha.check.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("check test", () => {
|
||||
console.log("RUNNING: check");
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
`,
|
||||
"beta.verify.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("verify test", () => {
|
||||
console.log("RUNNING: verify");
|
||||
expect(2).toBe(2);
|
||||
});
|
||||
`,
|
||||
"bunfig.toml": `[test]\nresolveExtensions = ".check"`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test", "--resolve-extensions", ".verify"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const output = stdout + stderr;
|
||||
expect(output).not.toContain("RUNNING: check");
|
||||
expect(output).toContain("RUNNING: verify");
|
||||
expect(output).toContain("1 pass");
|
||||
});
|
||||
|
||||
test("custom extensions work with underscore prefix", async () => {
|
||||
const dir = tempDirWithFiles("resolve-extensions-underscore", {
|
||||
"mytest_check.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("check test", () => {
|
||||
console.log("RUNNING: check");
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
`,
|
||||
"regular.test.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("regular test", () => {
|
||||
console.log("RUNNING: regular");
|
||||
expect(2).toBe(2);
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test", "--resolve-extensions", "_check"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const output = stdout + stderr;
|
||||
expect(output).toContain("RUNNING: check");
|
||||
expect(output).not.toContain("RUNNING: regular");
|
||||
expect(output).toContain("1 pass");
|
||||
});
|
||||
|
||||
test("no tests found shows custom extensions in error message", async () => {
|
||||
const dir = tempDirWithFiles("resolve-extensions-no-tests", {
|
||||
"regular.test.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("regular test", () => expect(1).toBe(1));
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test", "--resolve-extensions", ".custom"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(1);
|
||||
const output = stdout + stderr;
|
||||
expect(output).toContain("No tests found");
|
||||
expect(output).toContain(".custom");
|
||||
});
|
||||
|
||||
test("works with nested directories", async () => {
|
||||
const dir = tempDirWithFiles("resolve-extensions-nested", {
|
||||
"src/feature/alpha.check.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("nested check", () => {
|
||||
console.log("RUNNING: nested check");
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
`,
|
||||
"src/feature/beta.test.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("nested test", () => {
|
||||
console.log("RUNNING: nested test");
|
||||
expect(2).toBe(2);
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test", "--resolve-extensions", ".check"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const output = stdout + stderr;
|
||||
expect(output).toContain("RUNNING: nested check");
|
||||
expect(output).not.toContain("RUNNING: nested test");
|
||||
expect(output).toContain("1 pass");
|
||||
});
|
||||
|
||||
test("works with default extensions when not specified", async () => {
|
||||
const dir = tempDirWithFiles("resolve-extensions-default", {
|
||||
"alpha.test.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("test suffix", () => {
|
||||
console.log("RUNNING: test");
|
||||
expect(1).toBe(1);
|
||||
});
|
||||
`,
|
||||
"beta.spec.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("spec suffix", () => {
|
||||
console.log("RUNNING: spec");
|
||||
expect(2).toBe(2);
|
||||
});
|
||||
`,
|
||||
"gamma_test.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("_test suffix", () => {
|
||||
console.log("RUNNING: _test");
|
||||
expect(3).toBe(3);
|
||||
});
|
||||
`,
|
||||
"delta_spec.ts": `
|
||||
import { test, expect } from "bun:test";
|
||||
test("_spec suffix", () => {
|
||||
console.log("RUNNING: _spec");
|
||||
expect(4).toBe(4);
|
||||
});
|
||||
`,
|
||||
});
|
||||
|
||||
await using proc = Bun.spawn({
|
||||
cmd: [bunExe(), "test"],
|
||||
env: bunEnv,
|
||||
cwd: dir,
|
||||
stderr: "pipe",
|
||||
stdout: "pipe",
|
||||
});
|
||||
|
||||
const [stdout, stderr, exitCode] = await Promise.all([
|
||||
new Response(proc.stdout).text(),
|
||||
new Response(proc.stderr).text(),
|
||||
proc.exited,
|
||||
]);
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const output = stdout + stderr;
|
||||
expect(output).toContain("RUNNING: test");
|
||||
expect(output).toContain("RUNNING: spec");
|
||||
expect(output).toContain("RUNNING: _test");
|
||||
expect(output).toContain("RUNNING: _spec");
|
||||
expect(output).toContain("4 pass");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user