Files
iofficeai--aionui/tests/unit/feedback/MessageToolGroupFeedback.dom.test.tsx
2026-07-13 13:20:22 +08:00

103 lines
3.5 KiB
TypeScript

/**
* @license
* Copyright 2025 AionUi (aionui.com)
* SPDX-License-Identifier: Apache-2.0
*
* Verifies MessageToolGroup renders the FeedbackButton only when a tool call
* has status='Error' and wires it to module=conversation-session.
*/
import React from 'react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { cleanup, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
vi.mock('react-i18next', () => ({
useTranslation: () => ({ t: (k: string) => k, i18n: { language: 'en' } }),
}));
const openFeedbackMock = vi.fn(() => Promise.resolve());
vi.mock('@/renderer/hooks/context/FeedbackContext', () => ({
useFeedback: () => ({ openFeedback: openFeedbackMock }),
}));
// Stub heavy dependencies that MessageToolGroup pulls in so this test can
// render in pure jsdom without the whole app tree.
vi.mock('@renderer/components/chat/CollapsibleContent', () => ({
default: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('@renderer/components/Markdown', () => ({
default: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));
vi.mock('@renderer/components/media/LocalImageView', () => ({
default: () => null,
}));
vi.mock('@/renderer/components/base/FileChangesPanel', () => ({
default: () => null,
}));
vi.mock('@/renderer/hooks/file/useDiffPreviewHandlers', () => ({
useDiffPreviewHandlers: () => ({ openDiff: () => {}, openFile: () => {} }),
}));
vi.mock('@/common', () => ({
ipcBridge: {
acpConversation: { respondToConfirmation: { invoke: vi.fn() } },
conversation: { respondToConfirmation: { invoke: vi.fn() } },
},
}));
import MessageToolGroup from '@/renderer/pages/conversation/Messages/components/MessageToolGroup';
import type { IMessageToolGroup } from '@/common/chat/chatLib';
const buildToolGroup = (status: IMessageToolGroup['content'][number]['status']): IMessageToolGroup =>
({
id: 'tg-1',
type: 'tool_group',
content: [
{
call_id: 'c1',
description: 'ran something',
name: 'Read',
render_output_as_markdown: false,
status,
result_display: 'ENOENT: no such file',
},
],
}) as IMessageToolGroup;
describe('MessageToolGroup — FeedbackButton wiring', () => {
beforeEach(() => {
openFeedbackMock.mockClear();
});
afterEach(() => {
cleanup();
});
it('does not render FeedbackButton on successful tool calls', () => {
render(<MessageToolGroup message={buildToolGroup('Success')} />);
expect(screen.queryByText('settings.oneClickFeedback')).not.toBeInTheDocument();
});
it('does not render FeedbackButton on canceled tool calls', () => {
render(<MessageToolGroup message={buildToolGroup('Canceled')} />);
expect(screen.queryByText('settings.oneClickFeedback')).not.toBeInTheDocument();
});
it('renders FeedbackButton when status=Error', () => {
render(<MessageToolGroup message={buildToolGroup('Error')} />);
expect(screen.getByText('settings.oneClickFeedback')).toBeInTheDocument();
});
it('click opens feedback with module=conversation-session', async () => {
const user = userEvent.setup();
render(<MessageToolGroup message={buildToolGroup('Error')} />);
await user.click(screen.getByText('settings.oneClickFeedback'));
expect(openFeedbackMock).toHaveBeenCalledTimes(1);
expect(openFeedbackMock).toHaveBeenCalledWith({
module: 'conversation-session',
autoScreenshot: true,
});
});
});