1import { execSync, ExecSyncOptionsWithStringEncoding } from "node:child_process";
2
3interface TestResult {
4 suite: string;
5 passed: number;
6 failed: number;
7 skipped: number;
8 duration: number;
9}
10
11const execOptions: ExecSyncOptionsWithStringEncoding = {
12 encoding: "utf-8",
13 stdio: ["pipe", "pipe", "pipe"],
14};
15
16async function runTestSuite(): Promise<TestResult[]> {
17 const results: TestResult[] = [];
18
19
20 console.log("Running unit tests...");
21 const unitStart = Date.now();
22 try {
23 execSync("npx jest --ci --json --outputFile=test-results/unit.json", execOptions);
24 const unitResults = JSON.parse(
25 await readFile("test-results/unit.json", "utf-8"),
26 );
27 results.push({
28 suite: "unit",
29 passed: unitResults.numPassedTests,
30 failed: unitResults.numFailedTests,
31 skipped: unitResults.numPendingTests,
32 duration: Date.now() - unitStart,
33 });
34 } catch (error) {
35 results.push({ suite: "unit", passed: 0, failed: 1, skipped: 0, duration: Date.now() - unitStart });
36 }
37
38
39 console.log("Running integration tests...");
40 const integStart = Date.now();
41 try {
42 execSync("npx jest --ci --config jest.integration.config.js --json --outputFile=test-results/integration.json", execOptions);
43 const integResults = JSON.parse(
44 await readFile("test-results/integration.json", "utf-8"),
45 );
46 results.push({
47 suite: "integration",
48 passed: integResults.numPassedTests,
49 failed: integResults.numFailedTests,
50 skipped: integResults.numPendingTests,
51 duration: Date.now() - integStart,
52 });
53 } catch (error) {
54 results.push({ suite: "integration", passed: 0, failed: 1, skipped: 0, duration: Date.now() - integStart });
55 }
56
57
58 console.log("Running type check...");
59 const typeStart = Date.now();
60 try {
61 execSync("npx tsc --noEmit", execOptions);
62 results.push({ suite: "typecheck", passed: 1, failed: 0, skipped: 0, duration: Date.now() - typeStart });
63 } catch {
64 results.push({ suite: "typecheck", passed: 0, failed: 1, skipped: 0, duration: Date.now() - typeStart });
65 }
66
67 return results;
68}
69
70function generateReport(results: TestResult[]): string {
71 const totalPassed = results.reduce((sum, r) => sum + r.passed, 0);
72 const totalFailed = results.reduce((sum, r) => sum + r.failed, 0);
73 const totalDuration = results.reduce((sum, r) => sum + r.duration, 0);
74
75 let report = `## Test Results\n\n`;
76 report += `| Suite | Passed | Failed | Duration |\n`;
77 report += `|-------|--------|--------|----------|\n`;
78
79 for (const r of results) {
80 const status = r.failed > 0 ? "❌" : "✅";
81 report += `| ${status} ${r.suite} | ${r.passed} | ${r.failed} | ${(r.duration / 1000).toFixed(1)}s |\n`;
82 }
83
84 report += `\n**Total: ${totalPassed} passed, ${totalFailed} failed in ${(totalDuration / 1000).toFixed(1)}s**\n`;
85
86 return report;
87}
88