Skip to content

Commit 6d7124f

Browse files
committed
Add execute tests script
1 parent 92ecb14 commit 6d7124f

File tree

2 files changed

+118
-1
lines changed

2 files changed

+118
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"scripts": {
1818
"build": "tsup",
1919
"dev": "tsx src/index.ts",
20-
"test": "tsx tests/fs.test.ts",
20+
"test": "tsx scripts/execute-tests.ts",
2121
"prepublishOnly": "npm run build",
2222
"check:lint": "biome check . --diagnostic-level=error",
2323
"check:unsafe": "biome check . --write --unsafe --diagnostic-level=error",

scripts/execute-tests.ts

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env tsx
2+
3+
import { spawn } from "node:child_process";
4+
import { readdir } from "node:fs/promises";
5+
import path from "node:path";
6+
import process from "node:process";
7+
import { fileURLToPath } from "node:url";
8+
9+
type RunOptions = {
10+
rootDir: string;
11+
testsDir: string;
12+
filter?: RegExp;
13+
listOnly: boolean;
14+
};
15+
16+
function parseArgs(argv: string[], rootDir: string): RunOptions {
17+
const testsDir = path.join(rootDir, "tests");
18+
let filter: RegExp | undefined;
19+
let listOnly = false;
20+
21+
for (let i = 0; i < argv.length; i++) {
22+
const arg = argv[i];
23+
if (arg === "--list" || arg === "-l") {
24+
listOnly = true;
25+
continue;
26+
}
27+
if (arg === "--filter" || arg === "-f") {
28+
const pattern = argv[i + 1];
29+
if (!pattern) throw new Error("Missing value for --filter");
30+
filter = new RegExp(pattern);
31+
i++;
32+
continue;
33+
}
34+
if (arg === "--help" || arg === "-h") {
35+
console.log(
36+
`Usage: tsx scripts/execute-tests.ts [options]\n\nOptions:\n -l, --list List discovered test files and exit\n -f, --filter <regex> Only run tests whose path matches regex\n -h, --help Show help\n`,
37+
);
38+
process.exit(0);
39+
}
40+
throw new Error(`Unknown argument: ${arg}`);
41+
}
42+
43+
return { rootDir, testsDir, filter, listOnly };
44+
}
45+
46+
async function findTestFiles(dir: string): Promise<string[]> {
47+
const entries = await readdir(dir, { withFileTypes: true });
48+
const files: string[] = [];
49+
50+
for (const entry of entries) {
51+
const fullPath = path.join(dir, entry.name);
52+
if (entry.isDirectory()) {
53+
files.push(...(await findTestFiles(fullPath)));
54+
continue;
55+
}
56+
if (entry.isFile() && entry.name.endsWith(".test.ts")) {
57+
files.push(fullPath);
58+
}
59+
}
60+
61+
return files.sort((a, b) => a.localeCompare(b));
62+
}
63+
64+
async function runOneTest(rootDir: string, testFile: string): Promise<number> {
65+
return await new Promise((resolve, reject) => {
66+
const child = spawn("tsx", [testFile], {
67+
cwd: rootDir,
68+
stdio: "inherit",
69+
});
70+
71+
child.on("error", reject);
72+
child.on("exit", (code) => resolve(code ?? 1));
73+
});
74+
}
75+
76+
async function main(): Promise<void> {
77+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
78+
const rootDir = path.resolve(scriptDir, "..");
79+
const opts = parseArgs(process.argv.slice(2), rootDir);
80+
81+
let testFiles: string[];
82+
try {
83+
testFiles = await findTestFiles(opts.testsDir);
84+
} catch (err) {
85+
const msg = err instanceof Error ? err.message : String(err);
86+
process.exit(1);
87+
}
88+
89+
if (opts.filter) {
90+
testFiles = testFiles.filter((f) => opts.filter!.test(f));
91+
}
92+
93+
if (testFiles.length === 0) {
94+
process.exit(1);
95+
}
96+
97+
if (opts.listOnly) {
98+
for (const file of testFiles)
99+
console.log(path.relative(opts.rootDir, file));
100+
return;
101+
}
102+
103+
let failed = 0;
104+
for (const testFile of testFiles) {
105+
console.log(`\n— Running ${path.relative(opts.rootDir, testFile)} —`);
106+
const exitCode = await runOneTest(opts.rootDir, testFile);
107+
if (exitCode !== 0) failed++;
108+
}
109+
110+
if (failed > 0) {
111+
process.exit(1);
112+
}
113+
114+
console.log(`\nAll ${testFiles.length} test file(s) passed.`);
115+
}
116+
117+
await main();

0 commit comments

Comments
 (0)