165 lines
4.6 KiB
TypeScript
165 lines
4.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 AionUi (aionui.com)
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { renderHook, waitFor } from '@testing-library/react';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { useSlashCommands } from '@/renderer/hooks/chat/useSlashCommands';
|
|
|
|
const { ensureRuntimeInvokeMock, getSlashCommandsInvokeMock } = vi.hoisted(() => ({
|
|
ensureRuntimeInvokeMock: vi.fn(),
|
|
getSlashCommandsInvokeMock: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('@/common', () => ({
|
|
ipcBridge: {
|
|
conversation: {
|
|
ensureRuntime: {
|
|
invoke: ensureRuntimeInvokeMock,
|
|
},
|
|
getSlashCommands: {
|
|
invoke: getSlashCommandsInvokeMock,
|
|
},
|
|
},
|
|
},
|
|
}));
|
|
|
|
describe('useSlashCommands', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
ensureRuntimeInvokeMock.mockResolvedValue({ recovered: false, config_options: [], runtime: null });
|
|
getSlashCommandsInvokeMock.mockResolvedValue([]);
|
|
});
|
|
|
|
it('preserves ACP slash-command metadata from the HTTP command list', async () => {
|
|
getSlashCommandsInvokeMock.mockResolvedValue([
|
|
{
|
|
command: 'review',
|
|
description: 'Review the current diff',
|
|
completion_behavior: 'neutral_tip_on_empty',
|
|
empty_turn_tip_code: 'acp.empty_turn.choose_command',
|
|
empty_turn_tip_params: {
|
|
command_count: 1,
|
|
},
|
|
},
|
|
]);
|
|
|
|
const { result } = renderHook(() =>
|
|
useSlashCommands('conv-1', {
|
|
conversation_type: 'acp',
|
|
agentStatus: 'session_active',
|
|
})
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(result.current).toEqual([
|
|
{
|
|
name: 'review',
|
|
description: 'Review the current diff',
|
|
kind: 'template',
|
|
source: 'acp',
|
|
selectionBehavior: 'insert',
|
|
completionBehavior: 'neutral_tip_on_empty',
|
|
emptyTurnTipCode: 'acp.empty_turn.choose_command',
|
|
emptyTurnTipParams: {
|
|
command_count: 1,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
|
|
it('preserves ACP slash-command metadata from camelCase HTTP fields', async () => {
|
|
getSlashCommandsInvokeMock.mockResolvedValue([
|
|
{
|
|
command: 'review',
|
|
description: 'Review the current diff',
|
|
hint: '⌘R',
|
|
completionBehavior: 'neutral_tip_on_empty',
|
|
emptyTurnTipCode: 'acp.empty_turn.choose_command',
|
|
emptyTurnTipParams: {
|
|
command_count: 1,
|
|
},
|
|
},
|
|
]);
|
|
|
|
const { result } = renderHook(() =>
|
|
useSlashCommands('conv-1', {
|
|
conversation_type: 'acp',
|
|
agentStatus: 'session_active',
|
|
})
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(result.current).toEqual([
|
|
{
|
|
name: 'review',
|
|
description: 'Review the current diff',
|
|
hint: '⌘R',
|
|
kind: 'template',
|
|
source: 'acp',
|
|
selectionBehavior: 'insert',
|
|
completionBehavior: 'neutral_tip_on_empty',
|
|
emptyTurnTipCode: 'acp.empty_turn.choose_command',
|
|
emptyTurnTipParams: {
|
|
command_count: 1,
|
|
},
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
|
|
it('ensures the conversation runtime before loading slash commands', async () => {
|
|
getSlashCommandsInvokeMock.mockResolvedValue([
|
|
{
|
|
command: 'review',
|
|
description: 'Review the current diff',
|
|
},
|
|
]);
|
|
|
|
renderHook(() =>
|
|
useSlashCommands('conv-1', {
|
|
conversation_type: 'acp',
|
|
agentStatus: 'session_active',
|
|
})
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(ensureRuntimeInvokeMock).toHaveBeenCalledWith({ conversation_id: 'conv-1' });
|
|
expect(getSlashCommandsInvokeMock).toHaveBeenCalledWith({ conversation_id: 'conv-1' });
|
|
});
|
|
expect(ensureRuntimeInvokeMock.mock.invocationCallOrder[0]).toBeLessThan(
|
|
getSlashCommandsInvokeMock.mock.invocationCallOrder[0]
|
|
);
|
|
});
|
|
|
|
it('uses injected runtime preparation instead of standalone ensure', async () => {
|
|
const prepareRuntime = vi.fn().mockResolvedValue(undefined);
|
|
getSlashCommandsInvokeMock.mockResolvedValue([
|
|
{
|
|
command: 'review',
|
|
description: 'Review the current diff',
|
|
},
|
|
]);
|
|
|
|
renderHook(() =>
|
|
useSlashCommands('conv-1', {
|
|
conversation_type: 'acp',
|
|
agentStatus: 'session_active',
|
|
prepareRuntime,
|
|
})
|
|
);
|
|
|
|
await waitFor(() => {
|
|
expect(prepareRuntime).toHaveBeenCalled();
|
|
expect(getSlashCommandsInvokeMock).toHaveBeenCalledWith({ conversation_id: 'conv-1' });
|
|
});
|
|
expect(ensureRuntimeInvokeMock).not.toHaveBeenCalled();
|
|
expect(prepareRuntime.mock.invocationCallOrder[0]).toBeLessThan(
|
|
getSlashCommandsInvokeMock.mock.invocationCallOrder[0]
|
|
);
|
|
});
|
|
});
|