mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-08-29 10:52:26 +00:00
134 lines
4 KiB
TypeScript
134 lines
4 KiB
TypeScript
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
|
|
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
|
|
const { invokeMock, toastErrorMock } = vi.hoisted(() => ({
|
|
invokeMock: vi.fn(),
|
|
toastErrorMock: vi.fn(),
|
|
}));
|
|
|
|
vi.mock('@/host', () => ({
|
|
useHost: () => ({ ipcRenderer: { invoke: invokeMock } }),
|
|
}));
|
|
|
|
vi.mock('sonner', () => ({
|
|
toast: { success: vi.fn(), error: toastErrorMock },
|
|
}));
|
|
|
|
import { UserMessageCard } from '@/components/ChatBox/MessageItem/UserMessageCard';
|
|
|
|
describe('UserMessageCard', () => {
|
|
beforeEach(() => {
|
|
invokeMock.mockReset();
|
|
invokeMock.mockResolvedValue({ success: true });
|
|
toastErrorMock.mockReset();
|
|
});
|
|
|
|
it('renders as a right-aligned chat bubble with a tighter tail corner', () => {
|
|
const { container } = render(
|
|
<UserMessageCard id="user-message-1" content="Hello from the user" />
|
|
);
|
|
|
|
const root = container.firstElementChild;
|
|
const bubble = root?.firstElementChild;
|
|
|
|
expect(root).toHaveClass('pl-16');
|
|
expect(bubble).toHaveClass('rounded-xl', 'rounded-br-sm');
|
|
});
|
|
|
|
it('keeps rendered skill and connector tags on the body-text baseline', () => {
|
|
const { container } = render(
|
|
<UserMessageCard
|
|
id="user-message-tags"
|
|
content="Use #browser with @github"
|
|
/>
|
|
);
|
|
|
|
for (const token of ['#browser', '@github']) {
|
|
const tag = [...container.querySelectorAll('span')].find(
|
|
(element) => element.textContent === token
|
|
);
|
|
|
|
expect(tag).toHaveClass(
|
|
'align-baseline',
|
|
'!text-body-sm',
|
|
'!font-normal'
|
|
);
|
|
}
|
|
});
|
|
|
|
it('keeps durable attachment names display-only', () => {
|
|
render(
|
|
<UserMessageCard
|
|
id="display-only-attachment"
|
|
content="Restored message"
|
|
attaches={[{ fileName: 'report.pdf' }]}
|
|
/>
|
|
);
|
|
|
|
const attachment = screen.getByTitle('report.pdf').closest('div');
|
|
expect(attachment).toHaveAttribute(
|
|
'data-attachment-capability',
|
|
'display-only'
|
|
);
|
|
fireEvent.click(attachment!);
|
|
expect(invokeMock).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('reveals only a trusted legacy local attachment path', () => {
|
|
render(
|
|
<UserMessageCard
|
|
id="local-attachment"
|
|
content="Local message"
|
|
attaches={[
|
|
{ fileName: 'report.pdf', filePath: '/workspace/report.pdf' },
|
|
]}
|
|
/>
|
|
);
|
|
|
|
const attachment = screen.getByTitle('report.pdf').closest('div');
|
|
expect(attachment).toHaveAttribute('data-attachment-capability', 'reveal');
|
|
fireEvent.click(attachment!);
|
|
expect(invokeMock).toHaveBeenCalledWith(
|
|
'reveal-in-folder',
|
|
'/workspace/report.pdf'
|
|
);
|
|
});
|
|
|
|
it('shows feedback when a restored attachment has lost its session grant', async () => {
|
|
invokeMock.mockResolvedValue({
|
|
success: false,
|
|
error: 'Path is outside the active workspace',
|
|
});
|
|
render(
|
|
<UserMessageCard
|
|
id="restored-local-attachment"
|
|
content="Restored message"
|
|
attaches={[
|
|
{ fileName: 'report.pdf', filePath: '/downloads/report.pdf' },
|
|
]}
|
|
/>
|
|
);
|
|
|
|
fireEvent.click(screen.getByTitle('report.pdf').closest('div')!);
|
|
|
|
await waitFor(() =>
|
|
expect(toastErrorMock).toHaveBeenCalledWith(
|
|
'Path is outside the active workspace'
|
|
)
|
|
);
|
|
});
|
|
});
|