diff --git a/packages/client/src/components/ChatView.tsx b/packages/client/src/components/ChatView.tsx index a329a7e6b..f25e55f53 100644 --- a/packages/client/src/components/ChatView.tsx +++ b/packages/client/src/components/ChatView.tsx @@ -332,8 +332,11 @@ const ChatViewInner = forwardRef(function ChatView({ sess // ping-pong bug). A pin sets scrollTop, not scrollHeight, so the next // onChange sees no growth and the loop cannot sustain itself. onChange: () => { + // Virtualizer may fire notify after jsdom teardown (suite-level + // unhandled "window is not defined"). Bail when unmounted / no DOM. + if (typeof window === "undefined") return; const el = scrollRef.current; - if (!el) return; + if (!el || !el.isConnected) return; const prevH = lastScrollHeightRef.current; const nextH = el.scrollHeight; if (nextH === prevH) return; diff --git a/scripts/__tests__/test-up-port-derivation.test.mjs b/scripts/__tests__/test-up-port-derivation.test.mjs index c2d20711f..f21abff39 100644 --- a/scripts/__tests__/test-up-port-derivation.test.mjs +++ b/scripts/__tests__/test-up-port-derivation.test.mjs @@ -159,33 +159,38 @@ describe("test-up.sh state file", () => { }); describe.skipIf(!hasDocker())("compose interpolation (requires docker)", () => { - it("published port tracks listen port + container env", () => { - const out = execFileSync( - "docker", - [ - "compose", - "-f", - path.join(DOCKER_DIR, "compose.yml"), - "-f", - path.join(DOCKER_DIR, "compose.test.yml"), - "config", - ], - { - cwd: DOCKER_DIR, - env: { ...process.env, DASHBOARD_PORT: "18042", PI_GATEWAY_PORT: "19042", HOST_CWD: "/tmp" }, - encoding: "utf8", - }, - ); - // Published host port == in-container listen port (no drift), dashboard. - expect(out).toMatch(/published:\s*"?18042"?/); - expect(out).toMatch(/target:\s*18042/); - // Container env DASHBOARD_PORT tracks the exported value. - expect(out).toMatch(/DASHBOARD_PORT:\s*"?18042"?/); - // Same for the gateway port — guards against the gateway env re-hardcoding. - expect(out).toMatch(/published:\s*"?19042"?/); - expect(out).toMatch(/target:\s*19042/); - expect(out).toMatch(/PI_GATEWAY_PORT:\s*"?19042"?/); - }); + it( + "published port tracks listen port + container env", + () => { + const out = execFileSync( + "docker", + [ + "compose", + "-f", + path.join(DOCKER_DIR, "compose.yml"), + "-f", + path.join(DOCKER_DIR, "compose.test.yml"), + "config", + ], + { + cwd: DOCKER_DIR, + env: { ...process.env, DASHBOARD_PORT: "18042", PI_GATEWAY_PORT: "19042", HOST_CWD: "/tmp" }, + encoding: "utf8", + timeout: 60_000, + }, + ); + // Published host port == in-container listen port (no drift), dashboard. + expect(out).toMatch(/published:\s*"?18042"?/); + expect(out).toMatch(/target:\s*18042/); + // Container env DASHBOARD_PORT tracks the exported value. + expect(out).toMatch(/DASHBOARD_PORT:\s*"?18042"?/); + // Same for the gateway port — guards against the gateway env re-hardcoding. + expect(out).toMatch(/published:\s*"?19042"?/); + expect(out).toMatch(/target:\s*19042/); + expect(out).toMatch(/PI_GATEWAY_PORT:\s*"?19042"?/); + }, + 60_000, + ); }); if (!hasDocker()) {