diff --git a/package.json b/package.json index 4d1a21b71..ae2aab470 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "format:swift": "swiftformat --lint --config .swiftformat apps/macos/Sources apps/ios/Sources apps/shared/ClawdbotKit/Sources", "format:all": "pnpm format && pnpm format:swift", "format:fix": "oxfmt --write src test", - "test": "vitest run", + "test": "node scripts/test-parallel.mjs", "test:watch": "vitest", "test:ui": "pnpm --dir ui test", "test:force": "node --import tsx scripts/test-force.ts", diff --git a/scripts/test-parallel.mjs b/scripts/test-parallel.mjs new file mode 100644 index 000000000..82261fe73 --- /dev/null +++ b/scripts/test-parallel.mjs @@ -0,0 +1,42 @@ +import { spawn } from "node:child_process"; + +const pnpm = process.platform === "win32" ? "pnpm.cmd" : "pnpm"; + +const runs = [ + { + name: "unit", + args: ["vitest", "run", "--config", "vitest.unit.config.ts"], + }, + { + name: "gateway", + args: ["vitest", "run", "--config", "vitest.gateway.config.ts"], + }, +]; + +const children = new Set(); + +const run = (entry) => + new Promise((resolve) => { + const child = spawn(pnpm, entry.args, { + stdio: "inherit", + env: { ...process.env, VITEST_GROUP: entry.name }, + }); + children.add(child); + child.on("exit", (code, signal) => { + children.delete(child); + resolve(code ?? (signal ? 1 : 0)); + }); + }); + +const shutdown = (signal) => { + for (const child of children) { + child.kill(signal); + } +}; + +process.on("SIGINT", () => shutdown("SIGINT")); +process.on("SIGTERM", () => shutdown("SIGTERM")); + +const codes = await Promise.all(runs.map(run)); +const failed = codes.find((code) => code !== 0); +process.exit(failed ?? 0); diff --git a/vitest.gateway.config.ts b/vitest.gateway.config.ts new file mode 100644 index 000000000..3440d797f --- /dev/null +++ b/vitest.gateway.config.ts @@ -0,0 +1,15 @@ +import { defineConfig, mergeConfig } from "vitest/config"; +import baseConfig from "./vitest.config.ts"; + +const baseTest = (baseConfig as { test?: { exclude?: string[] } }).test ?? {}; +const exclude = baseTest.exclude ?? []; + +export default mergeConfig( + baseConfig, + defineConfig({ + test: { + include: ["src/gateway/**/*.test.ts", "extensions/**/*.test.ts"], + exclude, + }, + }), +); diff --git a/vitest.unit.config.ts b/vitest.unit.config.ts new file mode 100644 index 000000000..697063180 --- /dev/null +++ b/vitest.unit.config.ts @@ -0,0 +1,20 @@ +import { defineConfig, mergeConfig } from "vitest/config"; +import baseConfig from "./vitest.config.ts"; + +const baseTest = (baseConfig as { test?: { include?: string[]; exclude?: string[] } }).test ?? {}; +const include = baseTest.include ?? [ + "src/**/*.test.ts", + "extensions/**/*.test.ts", + "test/format-error.test.ts", +]; +const exclude = baseTest.exclude ?? []; + +export default mergeConfig( + baseConfig, + defineConfig({ + test: { + include, + exclude: [...exclude, "src/gateway/**", "extensions/**"], + }, + }), +);