From 60620ec36c41864b07efcca5aba5d84857f95536 Mon Sep 17 00:00:00 2001
From: imccyu <276526105+imccyu@users.noreply.github.com>
Date: Thu, 30 Jul 2026 11:09:03 +0800
Subject: [PATCH] Green the jsdom lane for the composer-seat ResizeObserver and
new StatsLine format
The seat's height publisher needs a ResizeObserver stub in every spec that
renders ConversationRoot (jsdom has none), and the two branch-tail StatsLine
assertions move to the grouped detail-row output.
---
.../tests/assembly-surfaces.spec.tsx | 13 ++++++++++++-
.../tests/chat-branch-tails.spec.tsx | 2 +-
.../tests/chat-code-subcalls.spec.tsx | 13 ++++++++++++-
.../tests/chat-toolview-slot.spec.tsx | 13 ++++++++++++-
.../tests/gate-branch-tails.spec.tsx | 2 +-
.../ui-conversation/tests/skeleton.spec.tsx | 17 +++++++++++++++--
6 files changed, 53 insertions(+), 7 deletions(-)
diff --git a/packages/client/ui-conversation/tests/assembly-surfaces.spec.tsx b/packages/client/ui-conversation/tests/assembly-surfaces.spec.tsx
index 5d7f4c05e7..4322ae508b 100644
--- a/packages/client/ui-conversation/tests/assembly-surfaces.spec.tsx
+++ b/packages/client/ui-conversation/tests/assembly-surfaces.spec.tsx
@@ -29,9 +29,20 @@ import { apply, inject } from '@deepseek-ai/dsh-client-ui-conversation/client'
const SID = 's1' as SessionId
-afterEach(cleanup)
+/** jsdom has no ResizeObserver; the composer seat publishes its height through one. */
+class ResizeObserverStub {
+ observe(): void {}
+ unobserve(): void {}
+ disconnect(): void {}
+}
+
+afterEach(() => {
+ cleanup()
+ vi.unstubAllGlobals()
+})
beforeEach(() => {
localStorage.clear()
+ vi.stubGlobal('ResizeObserver', ResizeObserverStub)
})
const TODOS: TodoItem[] = [
diff --git a/packages/client/ui-conversation/tests/chat-branch-tails.spec.tsx b/packages/client/ui-conversation/tests/chat-branch-tails.spec.tsx
index 9bb6ba539a..c3a5543627 100644
--- a/packages/client/ui-conversation/tests/chat-branch-tails.spec.tsx
+++ b/packages/client/ui-conversation/tests/chat-branch-tails.spec.tsx
@@ -216,6 +216,6 @@ describe('small branch tails', () => {
const view = render(
,
)
- expect(view.getByText('10 tokens · 1 turns · 1 steps')).toBeTruthy()
+ expect(view.container.textContent).toBe('1 turns · 1 steps|Input 0 tok · Output 10 tok')
})
})
diff --git a/packages/client/ui-conversation/tests/chat-code-subcalls.spec.tsx b/packages/client/ui-conversation/tests/chat-code-subcalls.spec.tsx
index 6b75f940d4..bd1ac63f1f 100644
--- a/packages/client/ui-conversation/tests/chat-code-subcalls.spec.tsx
+++ b/packages/client/ui-conversation/tests/chat-code-subcalls.spec.tsx
@@ -23,9 +23,20 @@ import { apply, inject } from '@deepseek-ai/dsh-client-ui-conversation/client'
const SID = 's1' as SessionId
-afterEach(cleanup)
+/** jsdom has no ResizeObserver; the composer seat publishes its height through one. */
+class ResizeObserverStub {
+ observe(): void {}
+ unobserve(): void {}
+ disconnect(): void {}
+}
+
+afterEach(() => {
+ cleanup()
+ vi.unstubAllGlobals()
+})
beforeEach(() => {
localStorage.clear()
+ vi.stubGlobal('ResizeObserver', ResizeObserverStub)
})
const PROGRAM = 'const listing = await tools.bash({ command: "ls notes", description: "List notes" })\nreturn listing'
diff --git a/packages/client/ui-conversation/tests/chat-toolview-slot.spec.tsx b/packages/client/ui-conversation/tests/chat-toolview-slot.spec.tsx
index 02bb6b92dc..ccf940d430 100644
--- a/packages/client/ui-conversation/tests/chat-toolview-slot.spec.tsx
+++ b/packages/client/ui-conversation/tests/chat-toolview-slot.spec.tsx
@@ -21,10 +21,21 @@ import type { ToolRowProps } from '@deepseek-ai/dsh-client-ui-conversation/clien
const SID = 's1' as SessionId
-afterEach(cleanup)
+/** jsdom has no ResizeObserver; the composer seat publishes its height through one. */
+class ResizeObserverStub {
+ observe(): void {}
+ unobserve(): void {}
+ disconnect(): void {}
+}
+
+afterEach(() => {
+ cleanup()
+ vi.unstubAllGlobals()
+})
// The chat store persists under its declared key; clear between cases.
beforeEach(() => {
localStorage.clear()
+ vi.stubGlobal('ResizeObserver', ResizeObserverStub)
})
const toolResult = (seq: number, callId: string, name: string, args = '{"command":"make build","description":"Build"}'): ToolResultNode => ({
diff --git a/packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx b/packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx
index 6d58932ece..08190d917d 100644
--- a/packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx
+++ b/packages/client/ui-conversation/tests/gate-branch-tails.spec.tsx
@@ -49,7 +49,7 @@ describe('render branch tails', () => {
const view = render(
} />,
)
- expect(view.getByText('cache hit 0% · 15 tokens · 2 turns · 3 steps')).toBeTruthy()
+ expect(view.container.textContent).toBe('2 turns · 3 steps|Cache hit 0%|Input 9 tok · Output 6 tok')
})
it('AssistantMarkdown reasoning as the streaming tail renders the running ring', () => {
diff --git a/packages/client/ui-conversation/tests/skeleton.spec.tsx b/packages/client/ui-conversation/tests/skeleton.spec.tsx
index b174bace53..3ed459b5a9 100644
--- a/packages/client/ui-conversation/tests/skeleton.spec.tsx
+++ b/packages/client/ui-conversation/tests/skeleton.spec.tsx
@@ -28,8 +28,21 @@ function fakeWiring() {
return { wiring: shell, sink, shell }
}
-afterEach(cleanup)
-beforeEach(() => { localStorage.clear() })
+/** jsdom has no ResizeObserver; the composer seat publishes its height through one. */
+class ResizeObserverStub {
+ observe(): void {}
+ unobserve(): void {}
+ disconnect(): void {}
+}
+
+afterEach(() => {
+ cleanup()
+ vi.unstubAllGlobals()
+})
+beforeEach(() => {
+ localStorage.clear()
+ vi.stubGlobal('ResizeObserver', ResizeObserverStub)
+})
const sid = (id: string) => id as SessionId
const wid = (id: string) => id as WorkspaceId