Sfoglia il codice sorgente

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).
imccyu 1 mese fa
parent
commit
45eee34faf

+ 4 - 2
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' }))

+ 12 - 12
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', () => {

+ 1 - 1
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' })
   })
 

+ 4 - 1
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)

+ 4 - 1
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 }
 }

+ 6 - 5
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: () => <b>x</b> })
-    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', () => {