import '@testing-library/jest-dom';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import CodeBlockMessage from './CodeBlockMessage';
// Mock clipboard API
Object.assign(navigator, {
clipboard: {
writeText: jest.fn()
}
});
describe('CodeBlockMessage', () => {
beforeEach(() => {
jest.clearAllMocks();
});
it('should render inline code for single-line content', () => {
render(const x = 5;);
const code = screen.getByText('const x = 5;');
expect(code.tagName).toBe('CODE');
expect(code).toHaveClass('pf-chatbot__message-inline-code');
});
it('should render code block for multi-line content', () => {
const multilineCode = 'const x = 5;\nconst y = 10;';
const { container } = render({multilineCode});
const codeElement = container.querySelector('code');
expect(codeElement?.textContent).toBe(multilineCode);
});
it('should display language label', () => {
const code = 'const x = 5;\nconst y = 10;';
render({code});
expect(screen.getByText('javascript')).toBeInTheDocument();
});
it('should render copy button', () => {
const code = 'const x = 5;\nconst y = 10;';
render({code});
expect(screen.getByRole('button', { name: 'Copy code' })).toBeInTheDocument();
});
it('should copy plain string content to clipboard', async () => {
const code = 'const x = 5;\nconst y = 10;';
render({code});
const copyButton = screen.getByRole('button', { name: 'Copy code' });
await userEvent.click(copyButton);
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(code);
});
it('should extract text content from React elements when copying', async () => {
// Simulate what happens with syntax highlighting - children become React elements
const { container } = render(
const x = 5;{'\n'}
const y = 10;
);
const copyButton = screen.getByRole('button', { name: 'Copy code' });
await userEvent.click(copyButton);
// Should extract actual text content from DOM, not "[object Object]"
const codeElement = container.querySelector('code');
const expectedText = codeElement?.textContent || '';
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(expectedText);
expect(expectedText).not.toContain('[object Object]');
});
it('should show check icon after copying', async () => {
const code = 'const x = 5;\nconst y = 10;';
render({code});
const copyButton = screen.getByRole('button', { name: 'Copy code' });
await userEvent.click(copyButton);
// Check icon should be visible (we can verify by checking if CopyIcon is not present)
const svgElement = copyButton.querySelector('svg');
expect(svgElement).toBeInTheDocument();
});
it('should render expandable section when isExpandable is true', () => {
const code = 'const x = 5;\nconst y = 10;';
render({code});
expect(screen.getByRole('button', { name: 'Show more' })).toBeInTheDocument();
});
it('should toggle expandable section', async () => {
const code = 'const x = 5;\nconst y = 10;';
render({code});
const toggleButton = screen.getByRole('button', { name: 'Show more' });
await userEvent.click(toggleButton);
expect(screen.getByRole('button', { name: 'Show less' })).toBeInTheDocument();
});
it('should use custom expanded/collapsed text', () => {
const code = 'const x = 5;\nconst y = 10;';
render(
{code}
);
expect(screen.getByRole('button', { name: 'Reveal' })).toBeInTheDocument();
});
it('should pass through expandableSectionProps', () => {
const code = 'const x = 5;\nconst y = 10;';
const { container } = render(
{code}
);
const expandableSection = container.querySelector('.pf-v6-c-expandable-section.custom-expandable-class');
expect(expandableSection).toBeInTheDocument();
});
it('should render custom actions', () => {
const code = 'const x = 5;\nconst y = 10;';
const customAction = ;
render({code});
expect(screen.getByRole('button', { name: 'Custom action' })).toBeInTheDocument();
});
it('should apply isPrimary class to inline code', () => {
render(const x = 5;);
const code = screen.getByText('const x = 5;');
expect(code).toHaveClass('pf-m-primary');
});
it('should apply shouldRetainStyles class to code block', () => {
const code = 'const x = 5;\nconst y = 10;';
const { container } = render({code});
const codeBlockDiv = container.querySelector('.pf-chatbot__message-code-block');
expect(codeBlockDiv).toHaveClass('pf-m-markdown');
});
it('should use custom aria-label for copy button', () => {
const code = 'const x = 5;\nconst y = 10;';
render({code});
expect(screen.getByRole('button', { name: 'Copy this code' })).toBeInTheDocument();
});
it('should prioritize data-expanded-text over expandedText prop', () => {
const code = 'const x = 5;\nconst y = 10;';
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(jest.fn());
render(
{code}
);
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Message:',
expect.stringContaining('data-expanded-text or data-collapsed-text will override')
);
consoleErrorSpy.mockRestore();
});
});