// ========= 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( ); 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( ); 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( ); 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( ); 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( ); fireEvent.click(screen.getByTitle('report.pdf').closest('div')!); await waitFor(() => expect(toastErrorMock).toHaveBeenCalledWith( 'Path is outside the active workspace' ) ); }); });