diff --git a/scripts/lib/ci-node-test-plan.mjs b/scripts/lib/ci-node-test-plan.mjs index 9fa7b2676b14..f5d063ebc6f3 100644 --- a/scripts/lib/ci-node-test-plan.mjs +++ b/scripts/lib/ci-node-test-plan.mjs @@ -1129,6 +1129,7 @@ const SPLIT_NODE_SHARDS = new Map([ "test/vitest/vitest.media-understanding.config.ts", "test/vitest/vitest.tui.config.ts", "test/vitest/vitest.ui.config.ts", + "test/vitest/vitest.ui-isolated.config.ts", "test/vitest/vitest.wizard.config.ts", ], requiresDist: false, diff --git a/test/scripts/ci-node-test-plan.test.ts b/test/scripts/ci-node-test-plan.test.ts index 4c1358936952..51500b1505cb 100644 --- a/test/scripts/ci-node-test-plan.test.ts +++ b/test/scripts/ci-node-test-plan.test.ts @@ -664,6 +664,7 @@ describe("scripts/lib/ci-node-test-plan.mjs", () => { "test/vitest/vitest.media-understanding.config.ts", "test/vitest/vitest.tui.config.ts", "test/vitest/vitest.ui.config.ts", + "test/vitest/vitest.ui-isolated.config.ts", "test/vitest/vitest.wizard.config.ts", ], requiresDist: false, diff --git a/test/scripts/test-projects.test.ts b/test/scripts/test-projects.test.ts index dc57b23baa70..76c8acda9dfb 100644 --- a/test/scripts/test-projects.test.ts +++ b/test/scripts/test-projects.test.ts @@ -4731,6 +4731,7 @@ describe("scripts/test-projects full-suite sharding", () => { "test/vitest/vitest.tui.config.ts", "test/vitest/vitest.tui-pty.config.ts", "test/vitest/vitest.ui.config.ts", + "test/vitest/vitest.ui-isolated.config.ts", "test/vitest/vitest.utils.config.ts", "test/vitest/vitest.wizard.config.ts", "test/vitest/vitest.gateway-core.config.ts", diff --git a/test/vitest/vitest.test-shards.mjs b/test/vitest/vitest.test-shards.mjs index 52dbda6b6233..d3982475ed24 100644 --- a/test/vitest/vitest.test-shards.mjs +++ b/test/vitest/vitest.test-shards.mjs @@ -84,6 +84,7 @@ export const fullSuiteVitestShards = [ "test/vitest/vitest.tui.config.ts", "test/vitest/vitest.tui-pty.config.ts", "test/vitest/vitest.ui.config.ts", + "test/vitest/vitest.ui-isolated.config.ts", "test/vitest/vitest.utils.config.ts", "test/vitest/vitest.wizard.config.ts", ], diff --git a/test/vitest/vitest.ui-isolated.config.ts b/test/vitest/vitest.ui-isolated.config.ts new file mode 100644 index 000000000000..f6e3f23a1785 --- /dev/null +++ b/test/vitest/vitest.ui-isolated.config.ts @@ -0,0 +1,23 @@ +// Vitest ui-isolated config runs jsdom ui tests that need a fresh module graph. +// The shared ui shard runs non-isolated for speed, but tests that spy on module +// internals and assert the component uses that spy must not share a module cache +// with stateful predecessor files (see UI_ISOLATED_TEST_FILES). +import { createScopedVitestConfig } from "./vitest.scoped-config.ts"; +import { jsdomOptimizedDeps } from "./vitest.shared.config.ts"; +import { UI_ISOLATED_TEST_FILES } from "./vitest.ui.config.ts"; + +export function createUiIsolatedVitestConfig(env?: Record) { + return createScopedVitestConfig(UI_ISOLATED_TEST_FILES, { + deps: jsdomOptimizedDeps, + environment: "jsdom", + env, + excludeUnitFastTests: false, + includeOpenClawRuntimeSetup: false, + isolate: true, + name: "ui-isolated", + setupFiles: ["ui/src/test-helpers/lit-warnings.setup.ts"], + useNonIsolatedRunner: false, + }); +} + +export default createUiIsolatedVitestConfig(); diff --git a/test/vitest/vitest.ui.config.ts b/test/vitest/vitest.ui.config.ts index 551fecdf5516..950c71f23ce0 100644 --- a/test/vitest/vitest.ui.config.ts +++ b/test/vitest/vitest.ui.config.ts @@ -2,12 +2,32 @@ import { createScopedVitestConfig } from "./vitest.scoped-config.ts"; import { jsdomOptimizedDeps } from "./vitest.shared.config.ts"; +// Full chat-pane lifecycle tests instantiate the pane component, which relies on +// chat-thread/chat-message module-level singletons (thread state maps, module-scoped +// document context-menu listeners) and spies on those modules. Under the non-isolated +// ui runner a stateful predecessor file can leave those modules duplicated across the +// shared graph, so the pane binds to a different instance than the test's spy/registry +// — surfacing as flaky teardown assertions or 120s session-lifecycle hangs, depending +// on file order. These tests run in the isolated ui lane for a fresh module graph; +// keep this list in sync with vitest.ui-isolated.config.ts's include. +export const UI_ISOLATED_TEST_FILES = [ + "ui/src/pages/chat/chat-pane-history.test.ts", + "ui/src/pages/chat/chat-pane-lifecycle.test.ts", + "ui/src/pages/chat/chat-pane-pull-requests.test.ts", + "ui/src/pages/chat/chat-pane.message-cut.test.ts", + "ui/src/pages/chat/chat-pane.read-marker.test.ts", + "ui/src/pages/chat/chat-pane.session-discussion.test.ts", + "ui/src/pages/chat/chat-pane.test.ts", +]; + export function createUiVitestConfig( env?: Record, options?: { includePatterns?: string[]; name?: string }, ) { const includePatterns = options?.includePatterns ?? ["ui/src/**/*.test.ts"]; - const exclude = options?.includePatterns ? [] : ["ui/src/**/*.e2e.test.ts"]; + const exclude = options?.includePatterns + ? [] + : ["ui/src/**/*.e2e.test.ts", ...UI_ISOLATED_TEST_FILES]; return createScopedVitestConfig(includePatterns, { deps: jsdomOptimizedDeps, environment: "jsdom",