import { describe, it, expect } from 'vitest'; import { render } from 'vitest-browser-svelte'; import { page } from 'vitest/browser'; import AnnotationLayer from './AnnotationLayer.svelte'; import type { Annotation } from '$lib/types'; const annotation: Annotation = { id: 'ann-1', documentId: 'doc-1', pageNumber: 1, x: 0.1, y: 0.2, width: 0.3, height: 0.1, color: '#00c7b1', createdAt: '2026-01-01T00:00:00Z' }; const polygonAnnotation: Annotation = { ...annotation, id: 'ann-poly', polygon: [ [0.1, 0.2], [0.4, 0.21], [0.39, 0.29], [0.11, 0.28] ] }; describe('AnnotationLayer', () => { describe('dimmed prop', () => { it('should hide block number badges when dimmed is true', async () => { render(AnnotationLayer, { annotations: [annotation], canDraw: false, color: '#00c7b1', blockNumbers: { 'ann-1': 1 }, dimmed: true, onDraw: () => {} }); const badge = page.getByText('1'); await expect.element(badge).not.toBeInTheDocument(); }); it('should show block number badges when dimmed is false', async () => { render(AnnotationLayer, { annotations: [annotation], canDraw: false, color: '#00c7b1', blockNumbers: { 'ann-1': 1 }, dimmed: false, onDraw: () => {} }); const badge = page.getByText('1'); await expect.element(badge).toBeInTheDocument(); }); it('should still fire onAnnotationClick when dimmed', async () => { let clickedId: string | undefined; render(AnnotationLayer, { annotations: [annotation], canDraw: false, color: '#00c7b1', dimmed: true, onDraw: () => {}, onAnnotationClick: (id: string) => { clickedId = id; } }); const el = document.querySelector('[data-testid="annotation-ann-1"]')!; el.dispatchEvent(new MouseEvent('click', { bubbles: true })); expect(clickedId).toBe('ann-1'); }); }); describe('isResizable computation', () => { it('passes isResizable=true when canDraw, annotation is active, and has no polygon', async () => { render(AnnotationLayer, { annotations: [annotation], canDraw: true, color: '#00c7b1', activeAnnotationId: 'ann-1', onDraw: () => {} }); const handles = document.querySelectorAll('[data-handle]'); expect(handles).toHaveLength(8); }); it('passes isResizable=false when annotation has a polygon', async () => { render(AnnotationLayer, { annotations: [polygonAnnotation], canDraw: true, color: '#00c7b1', activeAnnotationId: 'ann-poly', onDraw: () => {} }); const handles = document.querySelectorAll('[data-handle]'); expect(handles).toHaveLength(0); }); it('passes isResizable=false when canDraw is false', async () => { render(AnnotationLayer, { annotations: [annotation], canDraw: false, color: '#00c7b1', activeAnnotationId: 'ann-1', onDraw: () => {} }); const handles = document.querySelectorAll('[data-handle]'); expect(handles).toHaveLength(0); }); it('passes isResizable=false when annotation is not active', async () => { render(AnnotationLayer, { annotations: [annotation], canDraw: true, color: '#00c7b1', activeAnnotationId: 'other-id', onDraw: () => {} }); const handles = document.querySelectorAll('[data-handle]'); expect(handles).toHaveLength(0); }); }); describe('flashAnnotationId prop', () => { it('should apply annotation-flash class when flashAnnotationId matches', async () => { render(AnnotationLayer, { annotations: [annotation], canDraw: false, color: '#00c7b1', flashAnnotationId: 'ann-1', onDraw: () => {} }); const el = document.querySelector('[data-testid="annotation-ann-1"]')!; expect(el.classList.contains('annotation-flash')).toBe(true); }); it('should not apply annotation-flash class when flashAnnotationId does not match', async () => { render(AnnotationLayer, { annotations: [annotation], canDraw: false, color: '#00c7b1', flashAnnotationId: 'other-id', onDraw: () => {} }); const el = document.querySelector('[data-testid="annotation-ann-1"]')!; expect(el.classList.contains('annotation-flash')).toBe(false); }); }); });