From 45eee34fafbd2e44522f75fe7b049d973e96f58c Mon Sep 17 00:00:00 2001 From: imccyu <276526105+imccyu@users.noreply.github.com> Date: Mon, 27 Jul 2026 04:28:42 +0800 Subject: [PATCH] test: adapt suites to the provider-hosted conversation shell Test-side catch-up with the session-maybe conversation architecture: the provide channel's descriptor shape and maybeProvideInfo in fakes, the shared chat-store handle asserted on conversation.session (the session-maybe shell carries no store), startSession fakes exposing the workspace list snapshot, strict session slots declining (not throwing) without a session, AppFrame's removed empty seat and loading gate, and the hero draft asserted on the machine (the chat-store mirror binds with ConversationSession). Plus three lint fixes (max-len split, boolean-compare, arrow-parens/unbound-method). --- .../ui-conversation/tests/skeleton.spec.tsx | 6 +++-- .../client/ui-layout/tests/app-frame.spec.tsx | 24 +++++++++---------- packages/client/ui-layout/tests/apply.spec.ts | 2 +- .../client/ui-sidebar/tests/apply.spec.tsx | 5 +++- .../client/ui-workspace/tests/apply.spec.ts | 5 +++- .../web-react/tests/scoped-slots.spec.tsx | 11 +++++---- 6 files changed, 31 insertions(+), 22 deletions(-) diff --git a/packages/client/ui-conversation/tests/skeleton.spec.tsx b/packages/client/ui-conversation/tests/skeleton.spec.tsx index 037a196b15..0e3337d4ae 100644 --- a/packages/client/ui-conversation/tests/skeleton.spec.tsx +++ b/packages/client/ui-conversation/tests/skeleton.spec.tsx @@ -162,10 +162,12 @@ describe('ConversationRoot resident composer', () => { // Hero chrome present, view ring absent. expect(b.view.getByText("Let's start building")).toBeTruthy() expect(b.view.queryByTestId('view-chat')).toBeNull() - // The same machine-backed textarea is live in the hero. + // The same machine-backed textarea is live in the hero. The chat-store + // mirror binds with ConversationSession (unmounted in hero), so the + // draft's truth here is the machine itself. const box = b.view.getByRole('textbox') fireEvent.change(box, { target: { value: 'draft in hero' } }) - expect(b.chat.store.getSnapshot().draft).toBe('draft in hero') + expect((box as HTMLTextAreaElement).value).toBe('draft in hero') // Picker: open through the chip; a pick switches to the other // workspace's blank session (draft carry is apply-layer wiring). fireEvent.click(b.view.getByRole('button', { name: 'Choose workspace' })) diff --git a/packages/client/ui-layout/tests/app-frame.spec.tsx b/packages/client/ui-layout/tests/app-frame.spec.tsx index 99a2f633e3..05bd6fac19 100644 --- a/packages/client/ui-layout/tests/app-frame.spec.tsx +++ b/packages/client/ui-layout/tests/app-frame.spec.tsx @@ -151,22 +151,22 @@ describe('AppFrame', () => { expect(slotCalls.find((c) => c.key === 'details')!.props).toEqual({}) }) - it('renders the New Session view state through the empty seat while no session is current', () => { - // No current session = the pure view state: the conversation.empty slot - // renders in the center column; no session slot dispatches. + it('keeps the conversation slot mounted while no session is current', () => { + // No current session: the session-maybe conversation shell owns the New + // Session view itself — the center column renders it unconditionally. sessionMode.current = false - const { slotCalls, getByTestId, queryByTestId } = mountFrame() - expect(getByTestId('empty-content')).toBeTruthy() - expect(queryByTestId('center-content')).toBeNull() - expect(slotCalls.map((c) => c.key)).toContain('conversation.empty') - expect(slotCalls.map((c) => c.key)).not.toContain('conversation') + const { slotCalls, getByTestId } = mountFrame() + expect(getByTestId('center-content')).toBeTruthy() + expect(slotCalls.map((c) => c.key)).toContain('conversation') }) - it('keeps the loading branch until both object-layer baselines are ready', () => { + it('renders both column occupants before baselines settle (no loading gate)', () => { + // The loading branch is gone: fixed tree positions from first paint, the + // occupants render their own pending states. baselinesReady.current = false - const { slotCalls, getByRole } = mountFrame() - expect(getByRole('status').textContent).toContain('Loading workspaces and sessions') - expect(slotCalls.map((c) => c.key)).not.toContain('conversation') + const { slotCalls } = mountFrame() + expect(slotCalls.map((c) => c.key)).toContain('conversation') + expect(slotCalls.map((c) => c.key)).toContain('details') }) it('sidebar slot receives live concession output as owner props', () => { diff --git a/packages/client/ui-layout/tests/apply.spec.ts b/packages/client/ui-layout/tests/apply.spec.ts index f993413bbf..1382f5160d 100644 --- a/packages/client/ui-layout/tests/apply.spec.ts +++ b/packages/client/ui-layout/tests/apply.spec.ts @@ -40,7 +40,7 @@ describe('ui-layout client apply', () => { expect(slots.entries('root')).toHaveLength(1) // …and declared the three children in the ledger. expect(slots.spec('sidebar')).toEqual({ kind: 'single', scope: 'root' }) - expect(slots.spec('conversation')).toEqual({ kind: 'single', scope: 'session' }) + expect(slots.spec('conversation')).toEqual({ kind: 'single', scope: 'session-maybe' }) expect(slots.spec('details')).toEqual({ kind: 'single', scope: 'session' }) }) diff --git a/packages/client/ui-sidebar/tests/apply.spec.tsx b/packages/client/ui-sidebar/tests/apply.spec.tsx index d9182fc53e..799e873cca 100644 --- a/packages/client/ui-sidebar/tests/apply.spec.tsx +++ b/packages/client/ui-sidebar/tests/apply.spec.tsx @@ -9,7 +9,10 @@ async function bench(declare = true) { const ctx = new Context() await ctx.plugin(SlotsService).await() const layout = { toggleSidebar: vi.fn() } - const workspaces = { connectWorkspace: vi.fn(async () => 'blank-1' as never) } + const workspaces = { + connectWorkspace: vi.fn(async () => 'blank-1' as never), + list: { getSnapshot: () => ({ recentWorkspaceId: undefined }) }, + } const sessions = { open: vi.fn(), clear: vi.fn() } ctx.provide('layout', layout) ctx.provide('sessions', sessions as never) diff --git a/packages/client/ui-workspace/tests/apply.spec.ts b/packages/client/ui-workspace/tests/apply.spec.ts index 9ab8556101..b50eb6651e 100644 --- a/packages/client/ui-workspace/tests/apply.spec.ts +++ b/packages/client/ui-workspace/tests/apply.spec.ts @@ -19,7 +19,10 @@ async function bench() { const insertSessionBefore = vi.fn(async () => ({})) const open = vi.fn() const clear = vi.fn() - ctx.provide('workspaces', { create, connectWorkspace, rename, insertSessionBefore } as never) + ctx.provide('workspaces', { + create, connectWorkspace, rename, insertSessionBefore, + list: { getSnapshot: () => ({ recentWorkspaceId: undefined }) }, + } as never) ctx.provide('sessions', { open, clear } as never) return { ctx, slots: ctx.get('slots') as SlotsService, create, connectWorkspace, rename, insertSessionBefore, open, clear } } diff --git a/packages/client/web-react/tests/scoped-slots.spec.tsx b/packages/client/web-react/tests/scoped-slots.spec.tsx index ba61c56186..5005c7d6ba 100644 --- a/packages/client/web-react/tests/scoped-slots.spec.tsx +++ b/packages/client/web-react/tests/scoped-slots.spec.tsx @@ -662,14 +662,15 @@ describe('standard-kit synthesis', () => { expect(seen2.at(-1)!['SessionProvider']).toBeUndefined() }) - it('fails loud when a session slot renders outside SessionProvider', () => { + it('renders nothing for a strict session slot while no session is current', () => { + // Strict session entries decline (render null) without a session; the + // loud path is reserved for a missing root binding provider. const h = makeHost() h.declare('k.session', SINGLE_SESSION) h.add('k.session', { component: () => x }) - const spy = vi.spyOn(console, 'error').mockImplementation(() => {}) - expect(() => mountRoot(h, { 'k.session': SINGLE_SESSION }, - (renderSlot) => renderSlot('k.session', {}))).toThrow(/outside SessionProvider/) - spy.mockRestore() + const { view } = mountRoot(h, { 'k.session': SINGLE_SESSION }, + (renderSlot) => renderSlot('k.session', {})) + expect(view.container.querySelector('b')).toBeNull() }) it('delivers the store pair for store-declaring entries and writes through baked actions', () => {