|
1 | | -import { playwright } from "@vitest/browser-playwright"; |
2 | | -import { defineConfig, defineProject } from "vitest/config"; |
| 1 | +import fs from "node:fs"; |
| 2 | +import os from "node:os"; |
| 3 | +import path from "node:path"; |
| 4 | +import { fileURLToPath } from "node:url"; |
| 5 | +import { defineConfig } from "vitest/config"; |
| 6 | + |
| 7 | +const repoRoot = path.dirname(fileURLToPath(import.meta.url)); |
| 8 | +const isCI = process.env.CI === "true" || process.env.GITHUB_ACTIONS === "true"; |
| 9 | +const isWindows = process.platform === "win32"; |
| 10 | +const localWorkers = Math.max(4, Math.min(16, os.cpus().length)); |
| 11 | +const ciWorkers = isWindows ? 2 : 3; |
| 12 | +// Read from disk rather than a hand-kept list, because every upstream bump adds plugin-sdk modules and a stale list makes each new one fail to resolve at test time. |
| 13 | +const pluginSdkSubpaths = fs |
| 14 | + .readdirSync(path.join(repoRoot, "src", "plugin-sdk")) |
| 15 | + .filter((entry) => entry.endsWith(".ts") && !entry.endsWith(".test.ts")) |
| 16 | + .map((entry) => entry.slice(0, -".ts".length)) |
| 17 | + .filter((subpath) => subpath !== "index"); |
3 | 18 |
|
4 | 19 | export default defineConfig({ |
| 20 | + resolve: { |
| 21 | + // Keep this ordered: the base `openclaw/plugin-sdk` alias is a prefix match. |
| 22 | + alias: [ |
| 23 | + ...pluginSdkSubpaths.map((subpath) => ({ |
| 24 | + find: `openclaw/plugin-sdk/${subpath}`, |
| 25 | + replacement: path.join(repoRoot, "src", "plugin-sdk", `${subpath}.ts`), |
| 26 | + })), |
| 27 | + { |
| 28 | + find: "openclaw/plugin-sdk", |
| 29 | + replacement: path.join(repoRoot, "src", "plugin-sdk", "index.ts"), |
| 30 | + }, |
| 31 | + ], |
| 32 | + }, |
5 | 33 | test: { |
6 | | - projects: [ |
7 | | - defineProject({ |
8 | | - test: { |
9 | | - name: "unit", |
10 | | - include: ["src/**/*.test.ts"], |
11 | | - exclude: ["src/**/*.browser.test.ts", "src/**/*.node.test.ts"], |
12 | | - environment: "jsdom", |
13 | | - setupFiles: ["./src/test-helpers/lit-warnings.setup.ts"], |
14 | | - }, |
15 | | - }), |
16 | | - defineProject({ |
17 | | - test: { |
18 | | - name: "unit-node", |
19 | | - include: ["src/**/*.node.test.ts"], |
20 | | - environment: "jsdom", |
21 | | - setupFiles: ["./src/test-helpers/lit-warnings.setup.ts"], |
22 | | - }, |
23 | | - }), |
24 | | - defineProject({ |
25 | | - test: { |
26 | | - name: "browser", |
27 | | - include: ["src/**/*.browser.test.ts"], |
28 | | - setupFiles: ["./src/test-helpers/lit-warnings.setup.ts"], |
29 | | - browser: { |
30 | | - enabled: true, |
31 | | - provider: playwright(), |
32 | | - instances: [{ browser: "chromium", name: "chromium" }], |
33 | | - headless: true, |
34 | | - ui: false, |
35 | | - }, |
36 | | - }, |
37 | | - }), |
| 34 | + testTimeout: 120_000, |
| 35 | + hookTimeout: isWindows ? 180_000 : 120_000, |
| 36 | + // Many suites rely on `vi.stubEnv(...)` and expect it to be scoped to the test. |
| 37 | + // This is especially important under `pool=vmForks` where env leaks cross-file. |
| 38 | + unstubEnvs: true, |
| 39 | + // Same rationale as unstubEnvs: avoid cross-test pollution under vmForks. |
| 40 | + unstubGlobals: true, |
| 41 | + pool: "forks", |
| 42 | + maxWorkers: isCI ? ciWorkers : localWorkers, |
| 43 | + include: [ |
| 44 | + "src/**/*.test.ts", |
| 45 | + "extensions/**/*.test.ts", |
| 46 | + "test/**/*.test.ts", |
| 47 | + "ui/src/ui/views/agents-utils.test.ts", |
| 48 | + "ui/src/ui/views/usage-render-details.test.ts", |
| 49 | + "ui/src/ui/controllers/agents.test.ts", |
| 50 | + "ui/src/ui/controllers/chat.test.ts", |
38 | 51 | ], |
| 52 | + setupFiles: ["test/setup.ts"], |
| 53 | + exclude: [ |
| 54 | + "dist/**", |
| 55 | + "apps/macos/**", |
| 56 | + "apps/macos/.build/**", |
| 57 | + "**/node_modules/**", |
| 58 | + "**/vendor/**", |
| 59 | + "dist/OpenClaw.app/**", |
| 60 | + "**/*.live.test.ts", |
| 61 | + "**/*.e2e.test.ts", |
| 62 | + ], |
| 63 | + coverage: { |
| 64 | + provider: "v8", |
| 65 | + reporter: ["text", "lcov"], |
| 66 | + // Keep coverage stable without an ever-growing exclude list: |
| 67 | + // only count files actually exercised by the test suite. |
| 68 | + all: false, |
| 69 | + thresholds: { |
| 70 | + lines: 70, |
| 71 | + functions: 70, |
| 72 | + branches: 55, |
| 73 | + statements: 70, |
| 74 | + }, |
| 75 | + // Anchor to repo-root `src/` only. Without this, coverage globs can |
| 76 | + // unintentionally match nested `*/src/**` folders (extensions, apps, etc). |
| 77 | + include: ["./src/**/*.ts"], |
| 78 | + exclude: [ |
| 79 | + // Never count workspace packages/apps toward core coverage thresholds. |
| 80 | + "extensions/**", |
| 81 | + "apps/**", |
| 82 | + "ui/**", |
| 83 | + "test/**", |
| 84 | + "src/**/*.test.ts", |
| 85 | + // Entrypoints and wiring (covered by CI smoke + manual/e2e flows). |
| 86 | + "src/entry.ts", |
| 87 | + "src/index.ts", |
| 88 | + "src/runtime.ts", |
| 89 | + "src/channel-web.ts", |
| 90 | + "src/extensionAPI.ts", |
| 91 | + "src/logging.ts", |
| 92 | + "src/cli/**", |
| 93 | + "src/commands/**", |
| 94 | + "src/daemon/**", |
| 95 | + "src/hooks/**", |
| 96 | + "src/macos/**", |
| 97 | + |
| 98 | + // Large integration surfaces; validated via e2e/manual/contract tests. |
| 99 | + "src/acp/**", |
| 100 | + "src/agents/**", |
| 101 | + "src/channels/**", |
| 102 | + "src/gateway/**", |
| 103 | + "src/line/**", |
| 104 | + "src/media-understanding/**", |
| 105 | + "src/node-host/**", |
| 106 | + "src/plugins/**", |
| 107 | + "src/providers/**", |
| 108 | + |
| 109 | + // Some agent integrations are intentionally validated via manual/e2e runs. |
| 110 | + "src/agents/model-scan.ts", |
| 111 | + "src/agents/pi-embedded-runner.ts", |
| 112 | + "src/agents/sandbox-paths.ts", |
| 113 | + "src/agents/sandbox.ts", |
| 114 | + "src/agents/skills-install.ts", |
| 115 | + "src/agents/pi-tool-definition-adapter.ts", |
| 116 | + "src/agents/tools/discord-actions*.ts", |
| 117 | + "src/agents/tools/slack-actions.ts", |
| 118 | + |
| 119 | + // Hard-to-unit-test modules; exercised indirectly by integration tests. |
| 120 | + "src/infra/state-migrations.ts", |
| 121 | + "src/infra/skills-remote.ts", |
| 122 | + "src/infra/update-check.ts", |
| 123 | + "src/infra/ports-inspect.ts", |
| 124 | + "src/infra/outbound/outbound-session.ts", |
| 125 | + "src/memory/batch-gemini.ts", |
| 126 | + |
| 127 | + // Gateway server integration surfaces are intentionally validated via manual/e2e runs. |
| 128 | + "src/gateway/control-ui.ts", |
| 129 | + "src/gateway/server-bridge.ts", |
| 130 | + "src/gateway/server-channels.ts", |
| 131 | + "src/gateway/server-methods/config.ts", |
| 132 | + "src/gateway/server-methods/send.ts", |
| 133 | + "src/gateway/server-methods/skills.ts", |
| 134 | + "src/gateway/server-methods/talk.ts", |
| 135 | + "src/gateway/server-methods/web.ts", |
| 136 | + "src/gateway/server-methods/wizard.ts", |
| 137 | + |
| 138 | + // Process bridges are hard to unit-test in isolation. |
| 139 | + "src/gateway/call.ts", |
| 140 | + "src/process/tau-rpc.ts", |
| 141 | + "src/process/exec.ts", |
| 142 | + // Interactive UIs/flows are intentionally validated via manual/e2e runs. |
| 143 | + "src/tui/**", |
| 144 | + "src/wizard/**", |
| 145 | + // Channel surfaces are largely integration-tested (or manually validated). |
| 146 | + "src/discord/**", |
| 147 | + "src/imessage/**", |
| 148 | + "src/signal/**", |
| 149 | + "src/slack/**", |
| 150 | + "src/browser/**", |
| 151 | + "src/channels/web/**", |
| 152 | + "src/telegram/index.ts", |
| 153 | + "src/telegram/proxy.ts", |
| 154 | + "src/telegram/webhook-set.ts", |
| 155 | + "src/telegram/**", |
| 156 | + "src/webchat/**", |
| 157 | + "src/gateway/server.ts", |
| 158 | + "src/gateway/client.ts", |
| 159 | + "src/gateway/protocol/**", |
| 160 | + "src/infra/tailscale.ts", |
| 161 | + ], |
| 162 | + }, |
39 | 163 | }, |
40 | 164 | }); |
0 commit comments