diff --git a/src/components/RcbMarkdown/RcbMarkdown.test.tsx b/src/components/RcbMarkdown/RcbMarkdown.test.tsx new file mode 100644 index 0000000..e5d5f99 --- /dev/null +++ b/src/components/RcbMarkdown/RcbMarkdown.test.tsx @@ -0,0 +1,243 @@ +import { render, screen } from '@testing-library/react'; +import { describe, expect, test } from 'vitest'; + +import RcbMarkdown from './RcbMarkdown'; + +describe('RcbMarkdown', () => { + test('renders plain text correctly', () => { + render(Hello World); + expect(screen.getByText('Hello World')).toBeInTheDocument(); + }); + + test('renders paragraph with correct styling', () => { + const { container } = render(This is a paragraph); + const paragraph = container.querySelector('p'); + expect(paragraph).toBeInTheDocument(); + expect(paragraph).toHaveStyle({ + lineHeight: '1.4', + textAlign: 'left', + }); + // Check that margin is set (browser converts to shorthand) + const computedStyle = window.getComputedStyle(paragraph!); + expect(computedStyle.marginTop).toBe('0px'); + expect(computedStyle.marginBottom).toBe('0.5em'); + }); + + test('renders bold text using markdown syntax', () => { + render(This is **bold** text); + const boldElement = screen.getByText('bold'); + expect(boldElement.tagName).toBe('STRONG'); + }); + + test('renders italic text using markdown syntax', () => { + render(This is *italic* text); + const italicElement = screen.getByText('italic'); + expect(italicElement.tagName).toBe('EM'); + }); + + test('renders unordered list with correct styling', () => { + const markdown = ` +- Item 1 +- Item 2 +- Item 3 + `; + const { container } = render({markdown}); + const ul = container.querySelector('ul'); + expect(ul).toBeInTheDocument(); + expect(ul).toHaveStyle({ + paddingLeft: 'clamp(8px, 3.5vw, 16px)', + margin: '0', + listStylePosition: 'inside', + }); + + const items = container.querySelectorAll('li'); + expect(items).toHaveLength(3); + expect(screen.getByText('Item 1')).toBeInTheDocument(); + expect(screen.getByText('Item 2')).toBeInTheDocument(); + expect(screen.getByText('Item 3')).toBeInTheDocument(); + }); + + test('renders ordered list with correct styling', () => { + const markdown = ` +1. First +2. Second +3. Third + `; + const { container } = render({markdown}); + const ol = container.querySelector('ol'); + expect(ol).toBeInTheDocument(); + expect(ol).toHaveStyle({ + paddingLeft: 'clamp(8px, 3.5vw, 16px)', + margin: '0', + listStylePosition: 'inside', + }); + + const items = container.querySelectorAll('li'); + expect(items).toHaveLength(3); + }); + + test('renders list items with correct styling', () => { + const markdown = '- Item'; + const { container } = render({markdown}); + const li = container.querySelector('li'); + expect(li).toBeInTheDocument(); + expect(li).toHaveStyle({ + marginBottom: '1px', + lineHeight: '1.4', + }); + }); + + test('renders inline code with correct styling', () => { + const { container } = render(Use `const x = 5;` for variables); + const inlineCode = container.querySelector('p code'); + expect(inlineCode).toBeInTheDocument(); + expect(inlineCode?.textContent).toBe('const x = 5;'); + expect(inlineCode?.textContent).toBe('const x = 5;'); + + const pre = inlineCode?.closest('pre'); + expect(pre).toBeInTheDocument(); + + expect(pre).toHaveStyle({ + backgroundColor: 'rgba(0, 0, 0, 0.3)', + padding: '8px', + borderRadius: '4px', + }); + }); + + test('renders code block with correct styling', () => { + const markdown = ` +\`\`\` +function hello() { + console.log("Hello"); +} +\`\`\` + `; + const { container } = render({markdown}); + const pre = container.querySelector('pre'); + expect(pre).toBeInTheDocument(); + + const code = container.querySelector('pre code'); + expect(code).toBeInTheDocument(); + + // Verify code content is present + expect(code?.textContent).toContain('function hello()'); + expect(code?.textContent).toContain('console.log'); + }); + + test('renders blockquote with correct styling', () => { + const markdown = '> This is a quote'; + const { container } = render({markdown}); + const blockquote = container.querySelector('blockquote'); + expect(blockquote).toBeInTheDocument(); + expect(blockquote).toHaveStyle({ + margin: '0', + paddingLeft: '10px', + borderLeft: '2px solid #ccc', + color: '#666', + fontStyle: 'italic', + }); + }); + + test('renders links with correct styling', () => { + render([Click here](https://example.com)); + const link = screen.getByRole('link', { name: 'Click here' }); + expect(link).toBeInTheDocument(); + expect(link).toHaveAttribute('href', 'https://example.com'); + expect(link).toHaveStyle({ + color: '#ffffff', + textDecoration: 'underline', + }); + }); + + test('renders GFM table (remark-gfm plugin)', () => { + const markdown = ` +| Header 1 | Header 2 | +|----------|----------| +| Cell 1 | Cell 2 | + `; + const { container } = render({markdown}); + const table = container.querySelector('table'); + expect(table).toBeInTheDocument(); + expect(screen.getByText('Header 1')).toBeInTheDocument(); + expect(screen.getByText('Cell 1')).toBeInTheDocument(); + }); + + test('renders strikethrough text (GFM)', () => { + render(~~strikethrough~~); + const delElement = screen.getByText('strikethrough'); + expect(delElement.tagName).toBe('DEL'); + }); + + test('handles empty string children', () => { + const { container } = render({''}); + const div = container.querySelector('div'); + expect(div).toBeInTheDocument(); + expect(div).toHaveStyle({ whiteSpace: 'normal' }); + }); + + test('handles non-string children by rendering empty content', () => { + const { container } = render({123 as any}); + const div = container.querySelector('div'); + expect(div).toBeInTheDocument(); + // Should render empty since non-string is converted to empty string + expect(div?.textContent?.trim()).toBe(''); + }); + + test('renders complex markdown with multiple elements', () => { + const markdown = ` +# Heading + +This is a paragraph with **bold** and *italic* text. + +- List item 1 +- List item 2 + +\`inline code\` and a [link](https://example.com) + +> A blockquote + `; + render({markdown}); + + expect(screen.getByText('Heading')).toBeInTheDocument(); + expect(screen.getByText('bold')).toBeInTheDocument(); + expect(screen.getByText('italic')).toBeInTheDocument(); + expect(screen.getByText('List item 1')).toBeInTheDocument(); + expect(screen.getByText('inline code')).toBeInTheDocument(); + expect(screen.getByRole('link', { name: 'link' })).toBeInTheDocument(); + expect(screen.getByText('A blockquote')).toBeInTheDocument(); + }); + + test('container div has correct whiteSpace style', () => { + const { container } = render(Test); + const div = container.firstChild as HTMLElement; + expect(div).toHaveStyle({ whiteSpace: 'normal' }); + }); + + test('renders multiple paragraphs correctly', () => { + const markdown = ` +First paragraph. + +Second paragraph. + `; + const { container } = render({markdown}); + const paragraphs = container.querySelectorAll('p'); + expect(paragraphs).toHaveLength(2); + }); + + test('preserves line breaks in code blocks', () => { + const markdown = ` +\`\`\` +line 1 +line 2 +line 3 +\`\`\` + `; + const { container } = render({markdown}); + const code = container.querySelector('pre code'); + + // Verify all lines are present in the code block + expect(code?.textContent).toContain('line 1'); + expect(code?.textContent).toContain('line 2'); + expect(code?.textContent).toContain('line 3'); + }); +});