diff --git a/packages/docs-test/run.test.ts b/packages/docs-test/run.test.ts new file mode 100644 index 00000000..142d27e9 --- /dev/null +++ b/packages/docs-test/run.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from "bun:test" +import path from "node:path" +import { getPythonExecutable } from "./run" + +describe("getPythonExecutable", () => { + const baseDir = path.join("repo", "packages", "docs-test") + + it("uses the Windows virtual environment layout", () => { + expect(getPythonExecutable("win32", baseDir)).toBe( + path.join(baseDir, ".venv", "Scripts", "python.exe"), + ) + }) + + it("uses the Unix virtual environment layout on Linux and macOS", () => { + for (const platform of ["linux", "darwin"] as const) { + expect(getPythonExecutable(platform, baseDir)).toBe( + path.join(baseDir, ".venv", "bin", "python3"), + ) + } + }) +}) diff --git a/packages/docs-test/run.ts b/packages/docs-test/run.ts index be9a588c..487ec90a 100644 --- a/packages/docs-test/run.ts +++ b/packages/docs-test/run.ts @@ -1,6 +1,6 @@ #!/usr/bin/env bun -import { spawn } from "child_process" -import path from "path" +import { spawn } from "node:child_process" +import path from "node:path" const args = process.argv.slice(2) const filter = args[0] // e.g., "typescript", "python", "integrations", or specific file @@ -13,6 +13,15 @@ interface TestFile { type: "ts" | "py" } +export function getPythonExecutable( + platform = process.platform, + baseDir = import.meta.dir, +): string { + return platform === "win32" + ? path.join(baseDir, ".venv", "Scripts", "python.exe") + : path.join(baseDir, ".venv", "bin", "python3") +} + function getTests(): TestFile[] { const tests: TestFile[] = [] @@ -64,16 +73,24 @@ async function runTest(test: TestFile): Promise { console.log(`Running: ${test.name}`) console.log("=".repeat(60)) - const cmd = - test.type === "ts" - ? "bun" - : path.join(import.meta.dir, ".venv", "bin", "python3") + const cmd = test.type === "ts" ? "bun" : getPythonExecutable() const proc = spawn(cmd, [test.path], { stdio: "inherit", env: { ...process.env }, }) - proc.on("close", (code) => { + proc.once("error", (error) => { + console.error(`Failed to start ${test.name}: ${error.message}`) + if (test.type === "py" && "code" in error && error.code === "ENOENT") { + console.error(`Expected Python executable at: ${cmd}`) + console.error( + 'Create it with "python -m venv .venv" and install requirements.txt.', + ) + } + resolve(false) + }) + + proc.once("close", (code) => { resolve(code === 0) }) }) @@ -95,7 +112,9 @@ async function main() { if (tests.length === 0) { console.log("No tests matched the filter:", filter) console.log("\nAvailable tests:") - getTests().forEach((t) => console.log(` - ${t.name} (${t.type})`)) + getTests().forEach((t) => { + console.log(` - ${t.name} (${t.type})`) + }) process.exit(1) } @@ -127,4 +146,9 @@ async function main() { } } -main().catch(console.error) +if (import.meta.main) { + main().catch((error) => { + console.error(error) + process.exitCode = 1 + }) +}