Files
hermes-web-ui/tests/client/chat-store-session-command.test.ts
ekko badb17cf8e [codex] integrate goal command workflow (#1025)
* feat: integrate goal command workflow

* fix: keep goal done visible

* fix: add goal done slash command

* fix: promote queued message on run start
2026-05-25 19:26:23 +08:00

133 lines
3.6 KiB
TypeScript

// @vitest-environment jsdom
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createPinia, setActivePinia } from 'pinia'
const chatApi = vi.hoisted(() => ({
registerSessionHandlers: vi.fn(),
unregisterSessionHandlers: vi.fn(),
getChatRunSocket: vi.fn(() => ({ emit: vi.fn() })),
sessionCommandHandlers: [] as Array<(event: any) => void>,
peerUserMessageHandlers: [] as Array<(event: any) => void>,
}))
vi.mock('@/api/hermes/chat', () => ({
startRunViaSocket: vi.fn(),
resumeSession: vi.fn(),
registerSessionHandlers: chatApi.registerSessionHandlers,
unregisterSessionHandlers: chatApi.unregisterSessionHandlers,
getChatRunSocket: chatApi.getChatRunSocket,
respondToolApproval: vi.fn(),
respondClarify: vi.fn(),
onPeerUserMessage: vi.fn((handler: (event: any) => void) => {
chatApi.peerUserMessageHandlers.push(handler)
return vi.fn()
}),
onSessionCommand: vi.fn((handler: (event: any) => void) => {
chatApi.sessionCommandHandlers.push(handler)
return vi.fn()
}),
}))
vi.mock('@/api/client', () => ({
getActiveProfileName: () => 'default',
}))
vi.mock('@/api/hermes/sessions', () => ({
deleteSession: vi.fn(),
fetchSession: vi.fn(),
fetchSessions: vi.fn(),
setSessionModel: vi.fn(),
}))
vi.mock('@/api/hermes/download', () => ({
getDownloadUrl: (_path: string, name: string) => `/download/${name}`,
}))
vi.mock('@/utils/completion-sound', () => ({
primeCompletionSound: vi.fn(),
playCompletionSound: vi.fn(),
}))
import { useChatStore, type Session } from '@/stores/hermes/chat'
function makeSession(): Session {
return {
id: 'session-1',
title: 'session',
messages: [],
createdAt: Date.now(),
updatedAt: Date.now(),
}
}
describe('chat store session.command fanout', () => {
beforeEach(() => {
vi.resetAllMocks()
chatApi.sessionCommandHandlers = []
chatApi.peerUserMessageHandlers = []
setActivePinia(createPinia())
})
it('attaches to a goal resume run started from another window', () => {
const store = useChatStore()
const session = makeSession()
store.sessions = [session]
store.activeSessionId = 'session-1'
store.activeSession = session
expect(chatApi.sessionCommandHandlers).toHaveLength(1)
chatApi.sessionCommandHandlers[0]({
event: 'session.command',
session_id: 'session-1',
command: 'goal',
action: 'resume',
message: 'Goal resumed',
started: true,
terminal: false,
})
expect(store.isStreaming).toBe(true)
expect(chatApi.registerSessionHandlers).toHaveBeenCalledWith('session-1', expect.objectContaining({
onRunStarted: expect.any(Function),
onSessionCommand: expect.any(Function),
}))
expect(store.messages).toEqual([
expect.objectContaining({
role: 'command',
content: 'Goal resumed',
commandAction: 'resume',
}),
])
})
it('does not clear the transcript for goal done commands', () => {
const store = useChatStore()
const session = makeSession()
session.messages = [
{ id: 'user-1', role: 'user', content: 'keep me', timestamp: 1 },
]
store.sessions = [session]
store.activeSessionId = 'session-1'
store.activeSession = session
chatApi.sessionCommandHandlers[0]({
event: 'session.command',
session_id: 'session-1',
command: 'goal',
action: 'clear',
message: 'Goal cleared.',
terminal: true,
})
expect(store.messages).toEqual([
expect.objectContaining({ id: 'user-1', content: 'keep me' }),
expect.objectContaining({
role: 'command',
content: 'Goal cleared.',
commandAction: 'clear',
}),
])
})
})