feat(web): add session model selector
This commit is contained in:
@@ -29,6 +29,7 @@ function snapshotBase(): ConversationSnapshot {
|
||||
sessionId: SID, nodes: [], foldDegraded: false, partial: null, runningCalls: [],
|
||||
pending: [], running: false, removed: false, openState: 'open', openError: null,
|
||||
hasMore: false, loadingOlder: false, promptError: null, lastAgentError: null,
|
||||
modelSelection: { current: null, groups: [], failures: [], status: 'idle', error: null },
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ function snapshotWith(nodes: ToolResultNode[]): ConversationSnapshot {
|
||||
sessionId: SID, nodes, foldDegraded: false, partial: null, runningCalls: [],
|
||||
pending: [], running: false, removed: false, openState: 'open', openError: null,
|
||||
hasMore: false, loadingOlder: false, promptError: null, lastAgentError: null,
|
||||
} as ConversationSnapshot
|
||||
modelSelection: { current: null, groups: [], failures: [], status: 'idle', error: null },
|
||||
}
|
||||
}
|
||||
|
||||
/** Test-owned AppFrame role: declares the layout-owned children and renders the conversation area under the framework session provider. */
|
||||
|
||||
@@ -31,6 +31,7 @@ function snapshotBase(): ConversationSnapshot {
|
||||
sessionId: SID, nodes: [], foldDegraded: false, partial: null, runningCalls: [],
|
||||
pending: [], running: false, removed: false, openState: 'open', openError: null,
|
||||
hasMore: false, loadingOlder: false, promptError: null, lastAgentError: null,
|
||||
modelSelection: { current: null, groups: [], failures: [], status: 'idle', error: null },
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ function snapshotBase(): ConversationSnapshot {
|
||||
sessionId: SID, nodes: [], foldDegraded: false, partial: null, runningCalls: [],
|
||||
pending: [], running: false, removed: false, openState: 'open', openError: null,
|
||||
hasMore: false, loadingOlder: false, promptError: null, lastAgentError: null,
|
||||
} as ConversationSnapshot
|
||||
modelSelection: { current: null, groups: [], failures: [], status: 'idle', error: null },
|
||||
}
|
||||
}
|
||||
|
||||
describe('render branch tails', () => {
|
||||
|
||||
@@ -128,4 +128,11 @@ describe('error strip and variants', () => {
|
||||
expect(view.getByTestId('acc')).toBeTruthy()
|
||||
expect(view.container.querySelector('[class*="hero"]')).not.toBeNull()
|
||||
})
|
||||
|
||||
it('renders the optional composer control immediately before the primary button', () => {
|
||||
const { view } = setup({ control: <button type="button">model</button> })
|
||||
const buttons = view.getAllByRole('button')
|
||||
expect(buttons.map(button => button.textContent)).toEqual(['model', ''])
|
||||
expect(buttons[1]?.getAttribute('aria-label')).toBe('发送')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -30,7 +30,8 @@ function snapshotBase(): ConversationSnapshot {
|
||||
sessionId: SID, nodes: [], foldDegraded: false, partial: null, runningCalls: [],
|
||||
pending: [], running: false, removed: false, openState: 'open', openError: null,
|
||||
hasMore: false, loadingOlder: false, promptError: null, lastAgentError: null,
|
||||
} as ConversationSnapshot
|
||||
modelSelection: { current: null, groups: [], failures: [], status: 'idle', error: null },
|
||||
}
|
||||
}
|
||||
|
||||
function sessionSource(over?: Partial<ConversationSnapshot>) {
|
||||
@@ -58,7 +59,9 @@ function listHook(rows: { id: string; title: string; cwd?: string; parentId?: st
|
||||
describe('ConversationRoot branches', () => {
|
||||
const chatTab: ViewTab = { id: 'chat', label: 'Chat' }
|
||||
/** renderSlot stub in the outlet's baked shape (ring key + only filter marker). */
|
||||
const stubRenderSlot = (() => <div data-testid="view-body" />) as unknown as ConversationRootProps['renderSlot']
|
||||
const stubRenderSlot = ((key: string) =>
|
||||
key === 'conversation.view' ? <div data-testid="view-body" /> : null
|
||||
) as unknown as ConversationRootProps['renderSlot']
|
||||
/** SessionProvider seat stub (render-prop pass-through; ConversationRoot never invokes it). */
|
||||
const SessionProviderStub: ConversationRootProps['SessionProvider'] = ({ children }) => <>{children(SID)}</>
|
||||
|
||||
|
||||
@@ -177,8 +177,11 @@ describe('ConversationRoot', () => {
|
||||
})
|
||||
|
||||
it('hides the tab strip with a single view; composer writes the store draft and sends it', () => {
|
||||
const { chat, send } = bench([tab('chat', 'Chat')])
|
||||
const { chat, send, renderSlot } = bench([tab('chat', 'Chat')])
|
||||
expect(screen.queryByRole('tablist')).toBeNull()
|
||||
expect(renderSlot).toHaveBeenCalledWith('conversation.composer.control', {})
|
||||
expect(screen.getByTestId('view-(all)').getAttribute('data-slot'))
|
||||
.toBe('conversation.composer.control')
|
||||
const box = screen.getByPlaceholderText(/输入消息/)
|
||||
fireEvent.change(box, { target: { value: 'hi' } })
|
||||
// Typing goes through actions.setDraft into the shared store.
|
||||
|
||||
Reference in New Issue
Block a user