From a753e6bc860e514c43ba3cf6eba1817c46da1aa4 Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 31 May 2026 11:32:59 +0200 Subject: [PATCH] fix(test): extend e2e vitest watchdog --- scripts/run-vitest.mjs | 44 +++++++++++++++++++++++++++++++-- test/scripts/run-vitest.test.ts | 35 ++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 2 deletions(-) diff --git a/scripts/run-vitest.mjs b/scripts/run-vitest.mjs index 9d509630e97..e198a1e85d7 100644 --- a/scripts/run-vitest.mjs +++ b/scripts/run-vitest.mjs @@ -19,11 +19,16 @@ const ANSI_CSI_SUFFIX_RE = /^[0-?]*[ -/]*[@-~]/u; const SUPPRESSED_VITEST_STDERR_PATTERNS = ["[PLUGIN_TIMINGS]"]; export const DEFAULT_VITEST_NO_OUTPUT_TIMEOUT_MS = 120_000; export const DEFAULT_VITEST_NO_OUTPUT_HEARTBEAT_MS = 60_000; +export const DEFAULT_LONG_RUNNING_VITEST_NO_OUTPUT_TIMEOUT_MS = 300_000; const VITEST_NO_OUTPUT_TIMEOUT_ENV_KEY = "OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS"; const VITEST_NO_OUTPUT_HEARTBEAT_ENV_KEY = "OPENCLAW_VITEST_NO_OUTPUT_HEARTBEAT_MS"; const UI_VITEST_CONFIG = "test/vitest/vitest.ui.config.ts"; const UNIT_UI_VITEST_CONFIG = "test/vitest/vitest.unit-ui.config.ts"; const TOOLING_VITEST_CONFIG = "test/vitest/vitest.tooling.config.ts"; +const LONG_RUNNING_VITEST_CONFIGS = new Set([ + "test/vitest/vitest.e2e.config.ts", + "test/vitest/vitest.ui-e2e.config.ts", +]); const TOOLING_EXCLUDED_TESTS = new Set([ ...boundaryTestFiles, "test/scripts/openclaw-e2e-instance.test.ts", @@ -230,15 +235,16 @@ export function resolveRunVitestSpawnEnv(env = process.env, argv = []) { if (explicitMode !== "run" && !isTruthyEnvValue(env.CI)) { return env; } + const defaultTimeoutMs = resolveDefaultVitestNoOutputTimeoutMs(argv); const hasTimeout = Object.hasOwn(env, VITEST_NO_OUTPUT_TIMEOUT_ENV_KEY); const timeoutMs = hasTimeout ? parsePositiveInt(env[VITEST_NO_OUTPUT_TIMEOUT_ENV_KEY]) - : DEFAULT_VITEST_NO_OUTPUT_TIMEOUT_MS; + : defaultTimeoutMs; const hasHeartbeat = Object.hasOwn(env, VITEST_NO_OUTPUT_HEARTBEAT_ENV_KEY); return { ...env, ...(!hasTimeout - ? { [VITEST_NO_OUTPUT_TIMEOUT_ENV_KEY]: String(DEFAULT_VITEST_NO_OUTPUT_TIMEOUT_MS) } + ? { [VITEST_NO_OUTPUT_TIMEOUT_ENV_KEY]: String(defaultTimeoutMs) } : {}), ...(!hasHeartbeat && timeoutMs !== null && DEFAULT_VITEST_NO_OUTPUT_HEARTBEAT_MS < timeoutMs ? { [VITEST_NO_OUTPUT_HEARTBEAT_ENV_KEY]: String(DEFAULT_VITEST_NO_OUTPUT_HEARTBEAT_MS) } @@ -246,6 +252,40 @@ export function resolveRunVitestSpawnEnv(env = process.env, argv = []) { }; } +export function resolveDefaultVitestNoOutputTimeoutMs(argv = []) { + const config = resolveVitestConfigArg(argv); + if (config !== null && isLongRunningVitestConfig(config)) { + return DEFAULT_LONG_RUNNING_VITEST_NO_OUTPUT_TIMEOUT_MS; + } + return DEFAULT_VITEST_NO_OUTPUT_TIMEOUT_MS; +} + +function resolveVitestConfigArg(argv) { + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === "--") { + return null; + } + if (arg === "--config" || arg === "-c") { + return argv[index + 1] ?? null; + } + if (arg.startsWith("--config=")) { + return arg.slice("--config=".length); + } + } + return null; +} + +function isLongRunningVitestConfig(config) { + const normalized = path.normalize(config).replaceAll(path.sep, "/").replace(/^\.\//u, ""); + for (const candidate of LONG_RUNNING_VITEST_CONFIGS) { + if (normalized === candidate || normalized.endsWith(`/${candidate}`)) { + return true; + } + } + return false; +} + export function resolveVitestSpawnParams(env = process.env, platform = process.platform) { return { env: resolveVitestSpawnEnv(env), diff --git a/test/scripts/run-vitest.test.ts b/test/scripts/run-vitest.test.ts index b9bb434732b..d0b3818b5ff 100644 --- a/test/scripts/run-vitest.test.ts +++ b/test/scripts/run-vitest.test.ts @@ -1,7 +1,9 @@ import { EventEmitter } from "node:events"; import { describe, expect, it, vi } from "vitest"; import { + DEFAULT_LONG_RUNNING_VITEST_NO_OUTPUT_TIMEOUT_MS, installVitestNoOutputWatchdog, + resolveDefaultVitestNoOutputTimeoutMs, resolveDirectNodeVitestArgs, resolveExplicitTestFileNoPassArgs, resolveImplicitVitestArgs, @@ -343,6 +345,39 @@ describe("scripts/run-vitest", () => { }); }); + it("uses a longer default stall watchdog for broad e2e configs", () => { + const timeout = String(DEFAULT_LONG_RUNNING_VITEST_NO_OUTPUT_TIMEOUT_MS); + + expect( + resolveRunVitestSpawnEnv({ PATH: "/usr/bin" }, [ + "run", + "--config", + "test/vitest/vitest.e2e.config.ts", + ]), + ).toEqual({ + PATH: "/usr/bin", + OPENCLAW_VITEST_NO_OUTPUT_HEARTBEAT_MS: "60000", + OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS: timeout, + }); + expect( + resolveRunVitestSpawnEnv({ PATH: "/usr/bin" }, [ + "run", + "--config=./test/vitest/vitest.ui-e2e.config.ts", + ]), + ).toEqual({ + PATH: "/usr/bin", + OPENCLAW_VITEST_NO_OUTPUT_HEARTBEAT_MS: "60000", + OPENCLAW_VITEST_NO_OUTPUT_TIMEOUT_MS: timeout, + }); + expect( + resolveDefaultVitestNoOutputTimeoutMs([ + "run", + "-c", + "/repo/test/vitest/vitest.e2e.config.ts", + ]), + ).toBe(DEFAULT_LONG_RUNNING_VITEST_NO_OUTPUT_TIMEOUT_MS); + }); + it("does not default implicit interactive runs to the stall watchdog", () => { expect(resolveRunVitestSpawnEnv({ PATH: "/usr/bin" }, ["src/foo.test.ts"])).toEqual({ PATH: "/usr/bin",