import { describe, it, expect, vi, afterEach } from 'vitest'; import { cleanup, render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import TranscriptionBlock from './TranscriptionBlock.svelte'; afterEach(cleanup); function renderBlock(overrides: Record = {}) { return render(TranscriptionBlock, { blockId: 'block-1', documentId: 'doc-1', blockNumber: 3, text: 'Liebe Mutter,', label: null, active: false, saveState: 'idle' as const, canComment: true, currentUserId: 'user-1', onTextChange: vi.fn(), onFocus: vi.fn(), onDeleteClick: vi.fn(), onRetry: vi.fn(), ...overrides }); } // ─── Rendering ─────────────────────────────────────────────────────────────── describe('TranscriptionBlock — rendering', () => { it('renders block number in turquoise badge', async () => { renderBlock(); await expect.element(page.getByText('3')).toBeInTheDocument(); }); it('renders text in textarea', async () => { renderBlock(); const textarea = page.getByRole('textbox'); await expect.element(textarea).toHaveValue('Liebe Mutter,'); }); it('renders optional label when provided', async () => { renderBlock({ label: 'Anrede' }); await expect.element(page.getByText('Anrede')).toBeInTheDocument(); }); it('does not render label when null', async () => { renderBlock({ label: null }); const label = page.getByText('Anrede'); await expect.element(label).not.toBeInTheDocument(); }); }); // ─── Save states ───────────────────────────────────────────────────────────── describe('TranscriptionBlock — save states', () => { it('shows nothing in idle state', async () => { renderBlock({ saveState: 'idle' }); const saving = page.getByText('Speichere...'); await expect.element(saving).not.toBeInTheDocument(); }); it('shows "Speichere..." in saving state', async () => { renderBlock({ saveState: 'saving' }); await expect.element(page.getByText('Speichere...')).toBeInTheDocument(); }); it('shows "Gespeichert" in saved state', async () => { renderBlock({ saveState: 'saved' }); await expect.element(page.getByText(/Gespeichert/)).toBeInTheDocument(); }); it('shows error with retry button in error state', async () => { const onRetry = vi.fn(); renderBlock({ saveState: 'error', onRetry }); await expect.element(page.getByText('Nicht gespeichert')).toBeInTheDocument(); const retryBtn = page.getByText('Erneut versuchen'); await expect.element(retryBtn).toBeInTheDocument(); }); }); // ─── Active state ──────────────────────────────────────────────────────────── describe('TranscriptionBlock — active border', () => { it('has turquoise left border when active', async () => { renderBlock({ active: true }); await expect.element(page.getByRole('textbox')).toBeInTheDocument(); const block = document.querySelector('[data-block-id="block-1"]')!; expect(block.className).toContain('border-turquoise'); }); it('has error left border when save failed', async () => { renderBlock({ saveState: 'error' }); await expect.element(page.getByRole('textbox')).toBeInTheDocument(); const block = document.querySelector('[data-block-id="block-1"]')!; expect(block.className).toContain('border-error'); }); }); // ─── Interactions ──────────────────────────────────────────────────────────── describe('TranscriptionBlock — interactions', () => { it('calls onTextChange when typing in textarea', async () => { const onTextChange = vi.fn(); renderBlock({ onTextChange }); const textarea = page.getByRole('textbox'); await textarea.fill('Neue Zeile'); expect(onTextChange).toHaveBeenCalled(); }); it('calls onFocus when textarea is focused', async () => { const onFocus = vi.fn(); renderBlock({ onFocus }); const textarea = page.getByRole('textbox'); await textarea.click(); expect(onFocus).toHaveBeenCalled(); }); it('shows Kommentieren button when no comments exist', async () => { renderBlock(); const btn = page.getByText('Kommentieren'); await expect.element(btn).toBeInTheDocument(); }); }); // ─── Reorder controls ──────────────────────────────────────────────────────── describe('TranscriptionBlock — reorder controls', () => { it('shows a drag handle element', async () => { renderBlock(); const handle = document.querySelector('[data-drag-handle]'); expect(handle).not.toBeNull(); }); it('disables move-up button when isFirst', async () => { renderBlock({ isFirst: true }); const btn = page.getByRole('button', { name: 'Nach oben' }); await expect.element(btn).toBeDisabled(); }); it('disables move-down button when isLast', async () => { renderBlock({ isLast: true }); const btn = page.getByRole('button', { name: 'Nach unten' }); await expect.element(btn).toBeDisabled(); }); it('calls onMoveUp when up arrow clicked', async () => { const onMoveUp = vi.fn(); renderBlock({ onMoveUp, isFirst: false }); const btn = page.getByRole('button', { name: 'Nach oben' }); await btn.click(); expect(onMoveUp).toHaveBeenCalled(); }); it('calls onMoveDown when down arrow clicked', async () => { const onMoveDown = vi.fn(); renderBlock({ onMoveDown, isLast: false }); const btn = page.getByRole('button', { name: 'Nach unten' }); await btn.click(); expect(onMoveDown).toHaveBeenCalled(); }); });