/** * elementToPrimitives Tests (Task C2). * Deckt alle unterstützten Typen + Legacy-Konventionen + Grenzfälle ab. */ import { describe, it, expect } from 'vitest'; import { elementToPrimitives } from '../src/render/pixi/elementDrawers'; import type { CADElement } from '../src/types/cad.types'; function el(id: string, type: string, props: Record = {}, geom: Partial = {}): CADElement { return { id, type, layerId: 'l', x: 0, y: 0, width: 10, height: 10, properties: props, ...geom, } as unknown as CADElement; } describe('elementToPrimitives', () => { it('line: aus properties.x1..y2 mit stroke/sw', () => { const prims = elementToPrimitives(el('l', 'line', { x1: 0, y1: 0, x2: 100, y2: 50, stroke: '#f00', strokeWidth: 3, })); expect(prims.length).toBe(1); expect(prims[0]).toEqual({ kind: 'line', from: { x: 0, y: 0 }, to: { x: 100, y: 50 }, stroke: '#f00', strokeWidth: 3, }); }); it('line ohne Koordinaten ergibt leere Liste', () => { expect(elementToPrimitives(el('l', 'line', {}))).toEqual([]); }); it('rect: x/y ist Zentrum der BBox (Legacy-Konvention)', () => { const prims = elementToPrimitives(el('r', 'rect', { fill: '#123456' }, { x: 100, y: 50, width: 80, height: 40, })); expect(prims.length).toBe(1); const p = prims[0]; if (p.kind !== 'rect') throw new Error('erwartete rect'); expect(p.x).toBe(60); // 100 − 40 expect(p.y).toBe(30); // 50 − 20 expect(p.w).toBe(80); expect(p.h).toBe(40); expect(p.fill).toBe('#123456'); }); it('rect fill none wird zu null', () => { const prims = elementToPrimitives(el('r', 'rect', { fill: 'none' })); const p = prims[0]; if (p.kind !== 'rect') throw new Error('erwartete rect'); expect(p.fill).toBeNull(); }); it('circle: radius mit Fallback width/2', () => { const withExplicit = elementToPrimitives(el('c', 'circle', { radius: 25, fill: '#abcdef' }, { x: 5, y: 5 })); const p = withExplicit[0]; if (p.kind !== 'circle') throw new Error('erwartete circle'); expect(p.radius).toBe(25); expect(p.center).toEqual({ x: 5, y: 5 }); expect(p.fill).toBe('#abcdef'); const byWidth = elementToPrimitives(el('c', 'circle', {}, { width: 120 })); const pb = byWidth[0]; if (pb.kind !== 'circle') throw new Error('erwartete circle'); expect(pb.radius).toBe(60); // fallback }); it('arc: Winkel in Grad wie Legacy', () => { const prims = elementToPrimitives(el('a', 'arc', { radius: 40, startAngle: 0, endAngle: 90, }, { x: 0, y: 0 })); const p = prims[0]; if (p.kind !== 'arc') throw new Error('erwartete arc'); expect(p.startDeg).toBe(0); expect(p.endDeg).toBe(90); expect(p.radius).toBe(40); }); it('polyline/polygon: points-Array, close je nach Typ', () => { const pts = [{ x: 0, y: 0 }, { x: 10, y: 0 }, { x: 10, y: 10 }]; const open = elementToPrimitives(el('pl', 'polyline', { points: pts })); const closed = elementToPrimitives(el('pg', 'polygon', { points: pts })); expect(open.length).toBe(1); expect(closed.length).toBe(1); if (open[0].kind !== 'polyline' || closed[0].kind !== 'polyline') { throw new Error('erwartete polyline'); } expect(open[0].close).toBe(false); expect(closed[0].close).toBe(true); }); it('text: leer nicht gerendert; gefuellt mit fontSize/color', () => { expect(elementToPrimitives(el('t', 'text', { text: '' }))).toEqual([]); const filled = elementToPrimitives(el('t', 'text', { text: 'Bühne A', fontSize: 18, color: '#ffffff', }, { x: 50, y: 75 })); expect(filled.length).toBe(1); const p = filled[0]; if (p.kind !== 'text') throw new Error('erwartete text'); expect(p.text).toBe('Bühne A'); expect(p.fontSize).toBe(18); expect(p.color).toBe('#ffffff'); }); it('unbekannter Typ ergibt leere Liste (renderer-sicher)', () => { expect(elementToPrimitives(el('x', 'definitely-unknown'))).toEqual([]); }); });