import { describe, it, expect, vi } from 'vitest' import { render, screen, fireEvent } from '@testing-library/react' import { MarkdownContent } from './MarkdownContent' import { preprocessWikilinks } from '../utils/chatWikilinks' describe('MarkdownContent', () => { it('renders bold text', () => { render() const strong = screen.getByText('world') expect(strong.tagName).toBe('STRONG') }) it('renders inline code', () => { render() const code = screen.getByText('console.log') expect(code.tagName).toBe('CODE') }) it('renders fenced code blocks', () => { const { container } = render() const pre = container.querySelector('pre') expect(pre).toBeTruthy() expect(pre!.textContent).toContain('const x = 1') }) it('renders unordered lists', () => { const { container } = render() const items = container.querySelectorAll('li') expect(items).toHaveLength(3) expect(items[0].textContent).toBe('one') }) it('renders ordered lists', () => { const { container } = render() const ol = container.querySelector('ol') expect(ol).toBeTruthy() expect(ol!.querySelectorAll('li')).toHaveLength(2) }) it('renders headers', () => { render() const h2 = screen.getByText('Section Title') expect(h2.tagName).toBe('H2') }) it('renders links', () => { render() const link = screen.getByText('Click here') as HTMLAnchorElement expect(link.tagName).toBe('A') expect(link.getAttribute('href')).toBe('https://example.com') }) it('renders mixed markdown', () => { const { container } = render() expect(screen.getByText('Bold').tagName).toBe('STRONG') expect(screen.getByText('code').tagName).toBe('CODE') expect(container.querySelector('li')).toBeTruthy() }) it('wraps content in .ai-markdown container', () => { const { container } = render() expect(container.querySelector('.ai-markdown')).toBeTruthy() }) it('renders plain text without crashing', () => { render() expect(screen.getByText('Just plain text')).toBeTruthy() }) it('renders blockquotes', () => { const { container } = render() const bq = container.querySelector('blockquote') expect(bq).toBeTruthy() expect(bq!.textContent).toContain('A quote') }) describe('wikilinks', () => { it('preprocessWikilinks converts [[Target]] to markdown links', () => { expect(preprocessWikilinks('See [[My Note]]')).toBe('See [My Note](wikilink://My%20Note)') expect(preprocessWikilinks('[[A]] and [[B]]')).toBe('[A](wikilink://A) and [B](wikilink://B)') expect(preprocessWikilinks('`[[code]]`')).toBe('`[[code]]`') }) it('renders [[Note Title]] as a clickable wikilink chip', () => { const onClick = vi.fn() const { container } = render( , ) const wikilink = container.querySelector('.chat-wikilink') expect(wikilink).toBeTruthy() expect(wikilink!.textContent).toBe('My Note') expect(wikilink!.getAttribute('data-wikilink-target')).toBe('My Note') }) it('fires onWikilinkClick when a wikilink is clicked', () => { const onClick = vi.fn() const { container } = render( , ) const wikilink = container.querySelector('.chat-wikilink')! fireEvent.click(wikilink) expect(onClick).toHaveBeenCalledWith('Daily Log') }) it('renders multiple wikilinks in the same paragraph', () => { const onClick = vi.fn() const { container } = render( , ) const wikilinks = container.querySelectorAll('.chat-wikilink') expect(wikilinks).toHaveLength(2) expect(wikilinks[0].textContent).toBe('Note A') expect(wikilinks[1].textContent).toBe('Note B') }) it('handles pipe syntax [[target|display]]', () => { const onClick = vi.fn() const { container } = render( , ) const wikilink = container.querySelector('.chat-wikilink')! expect(wikilink.textContent).toBe('My Display') expect(wikilink.getAttribute('data-wikilink-target')).toBe('path/to/note') fireEvent.click(wikilink) expect(onClick).toHaveBeenCalledWith('path/to/note') }) it('does not render wikilinks inside inline code', () => { const onClick = vi.fn() const { container } = render( , ) expect(container.querySelector('.chat-wikilink')).toBeNull() }) it('does not render wikilinks inside code blocks', () => { const onClick = vi.fn() const { container } = render( , ) expect(container.querySelector('.chat-wikilink')).toBeNull() }) it('handles notes with special characters in title', () => { const onClick = vi.fn() const { container } = render( , ) const wikilink = container.querySelector('.chat-wikilink')! expect(wikilink.textContent).toBe('Meeting — 2024/01/15') fireEvent.click(wikilink) expect(onClick).toHaveBeenCalledWith('Meeting — 2024/01/15') }) it('does not transform wikilinks when onWikilinkClick is not provided', () => { const { container } = render( , ) expect(container.querySelector('.chat-wikilink')).toBeNull() expect(container.textContent).toContain('[[Some Note]]') }) it('renders wikilinks inside list items', () => { const onClick = vi.fn() const { container } = render( , ) const wikilinks = container.querySelectorAll('.chat-wikilink') expect(wikilinks).toHaveLength(2) }) it('has role="link" and tabIndex for accessibility', () => { const onClick = vi.fn() const { container } = render( , ) const wikilink = container.querySelector('.chat-wikilink')! expect(wikilink.getAttribute('role')).toBe('link') expect(wikilink.getAttribute('tabindex')).toBe('0') }) }) })