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 that opens comment thread', async () => { renderBlock(); const btn = page.getByText('Kommentieren'); await expect.element(btn).toBeInTheDocument(); }); it('shows quote hint when block is active', async () => { renderBlock({ active: true }); await expect.element(page.getByText('Text markieren für Zitat')).toBeInTheDocument(); }); it('hides quote hint when block is not active', async () => { renderBlock({ active: false }); const hint = page.getByText('Text markieren für Zitat'); await expect.element(hint).not.toBeInTheDocument(); }); });