import { afterEach, beforeEach, describe, expect, it } from 'vitest' import { cleanup, fireEvent, render, screen } from '@testing-library/react' import { I18nProvider } from '@/lib/i18n-context' import { CodeBlock } from '@/components/CodeBlock' afterEach(() => cleanup()) // In jsdom the async shiki highlighter never resolves, so these tests // exercise the plain-text fallback (code split on newlines), which is the // same per-line row structure the highlighted path produces. describe('CodeBlock', () => { beforeEach(() => { window.localStorage.clear() }) it('renders a header label and truncation badge for long content', () => { const longCode = Array.from({ length: 40 }, (_, index) => `line ${index + 1}`).join('\n') render( ) expect(screen.getByText('TypeScript')).toBeInTheDocument() expect(screen.getByTitle('Copy')).toBeInTheDocument() expect(screen.getByText(/Preview truncated/)).toBeInTheDocument() }) it('renders one line-number cell per source line, aligned with the code', () => { const { container } = render( ) const lineNumbers = container.querySelectorAll('[data-line-number]') expect(lineNumbers).toHaveLength(3) expect(Array.from(lineNumbers).map((el) => el.textContent)).toEqual(['1', '2', '3']) }) it('renders the code inside a
 element for preformatted semantics', () => {
        const { container } = render(
            
                
            
        )

        const pre = container.querySelector('pre.shiki')
        expect(pre).not.toBeNull()
        expect(pre?.querySelector('[data-line-number]')).not.toBeNull()
        expect(pre?.querySelector('[data-code-cell]')).not.toBeNull()
    })

    it('defaults to wrap off: line-number column present, horizontal scroll container, no wrapping', () => {
        const { container } = render(
            
                
            
        )

        expect(container.querySelector('[data-line-number]')).not.toBeNull()
        expect(container.querySelector('.overflow-x-auto')).not.toBeNull()
        const codeCell = container.querySelector('[data-code-cell]') as HTMLElement | null
        expect(codeCell?.style.whiteSpace).not.toBe('pre-wrap')
    })

    it('toggling wrap on keeps the line-number column and wraps the code cells (no horizontal scroll)', () => {
        // Select the toggle by its aria-pressed state, not its localized title.
        const { container } = render(
            
                
            
        )

        fireEvent.click(screen.getByRole('button', { pressed: false }))

        // Line numbers stay (this is the whole point of the per-line layout).
        expect(container.querySelectorAll('[data-line-number]')).toHaveLength(2)
        expect(container.querySelector('.overflow-x-auto')).toBeNull()
        const codeCell = container.querySelector('[data-code-cell]') as HTMLElement | null
        expect(codeCell?.style.whiteSpace).toBe('pre-wrap')
        expect(screen.getByRole('button', { pressed: true })).toBeInTheDocument()
    })

    it('reads the persisted wrap preference on mount and still keeps line numbers', () => {
        window.localStorage.setItem('hapi-code-wrap', '1')

        const { container } = render(
            
                
            
        )

        expect(container.querySelectorAll('[data-line-number]')).toHaveLength(2)
        const codeCell = container.querySelector('[data-code-cell]') as HTMLElement | null
        expect(codeCell?.style.whiteSpace).toBe('pre-wrap')
        expect(screen.getByRole('button', { pressed: true })).toBeInTheDocument()
    })

    it('renders the plain-text fallback as per-line rows when highlighting is unavailable', () => {
        const { container } = render(
            
                
            
        )

        const codeCells = container.querySelectorAll('[data-code-cell]')
        expect(codeCells).toHaveLength(2)
        expect(codeCells[0]).toHaveTextContent('plain one')
        expect(codeCells[1]).toHaveTextContent('plain two')
        expect(container.querySelectorAll('[data-line-number]')).toHaveLength(2)
    })

    it('renders the wrap toggle button by default', () => {
        render(
            
                
            
        )

        expect(screen.getByRole('button', { pressed: false })).toBeInTheDocument()
    })

    it('omits the wrap toggle button when showWrapToggle is false', () => {
        // The wrap toggle is a