import { createElement } from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { describe, expect, it } from 'vitest'; import { FlooringPreview, SkirtingPreview } from '../src/components/CutPreview'; import { exactMm, flooringTemplate, skirtingEndBoxes } from '../src/lib/cut-preview'; import type { FlooringPiece, Point, SkirtingPiece } from '../src/lib/types'; const floor: FlooringPiece = { id: 'F1', roomId: 'room-1', row: 1, column: 1, polygon: [], length: 600.001, width: 190, stockId: 'B1', reused: false, cut: true, shaped: false, template: [], }; const template: Point[] = [ { x: 18, y: 0 }, { x: 1182, y: 0 }, { x: 1200, y: 18 }, { x: 0, y: 18 }, ]; const skirt: SkirtingPiece = { id: 'A1', roomId: 'room-1', wallId: 'w1', wallIndex: 0, start: { x: 0, y: 0 }, end: { x: 1164, y: 0 }, length: 1200, backLength: 1164, leftMitre: 45, rightMitre: 45, leftType: 'external', rightType: 'external', stockId: 'S1', template, }; describe('cut shape previews', () => { it('draws rectangular floor cuts too, keeping their precise measured dimensions', () => { const points = flooringTemplate(floor); expect(points).toEqual([ { x: 0, y: 0 }, { x: 600.001, y: 0 }, { x: 600.001, y: 190 }, { x: 0, y: 190 }, ]); const markup = renderToStaticMarkup(createElement(FlooringPreview, { piece: floor })); expect(markup).toContain('aria-label="Flooring shape F1"'); expect(markup).toContain('preserveAspectRatio="xMidYMid meet"'); expect(markup).toContain('Length 600.001 mm × width 190 mm'); expect(exactMm(600.001)).toBe('600.001'); }); it('keeps skirting endpoints in original coordinates and gives both ends equal square windows', () => { const original = structuredClone(template); const boxes = skirtingEndBoxes(template, 18); expect(boxes.left.width).toBe(boxes.left.height); expect(boxes.right.width).toBe(boxes.right.height); expect(boxes.left.width).toBe(boxes.right.width); // Equal x/y scales preserve the measured 18-by-18 mm, 45-degree cut. const scaleX = 120 / boxes.left.width, scaleY = 120 / boxes.left.height; const angle = (Math.atan2( Math.abs(template[3].y - template[0].y) * scaleY, Math.abs(template[3].x - template[0].x) * scaleX, ) * 180) / Math.PI; expect(angle).toBeCloseTo(45, 8); expect(template).toEqual(original); const markup = renderToStaticMarkup( createElement(SkirtingPreview, { piece: skirt, thickness: 18 }), ); expect(markup).toContain('points="18,0 1182,0 1200,18 0,18"'); expect(markup.match(/preserveAspectRatio="xMidYMid meet"/g)).toHaveLength(3); expect(markup).toContain('Full length 1,200 mm × thickness 18 mm'); expect(markup).not.toContain('exaggerat'); }); it('keeps the complete end cut visible even at shallow mitre angles', () => { const shallow = [ { x: 0, y: 0 }, { x: 2000, y: 0 }, { x: 2100, y: 18 }, { x: 1000, y: 18 }, ]; const boxes = skirtingEndBoxes(shallow, 18); for (const [name, indices] of [ ['left', [0, 3]], ['right', [1, 2]], ] as const) { const box = boxes[name]; for (const index of indices) { expect(shallow[index].x).toBeGreaterThan(box.x); expect(shallow[index].x).toBeLessThan(box.x + box.width); expect(shallow[index].y).toBeGreaterThan(box.y); expect(shallow[index].y).toBeLessThan(box.y + box.height); } } }); it('makes thumbnails decorative while retaining true-scale SVGs and visible end breaks', () => { const floorMarkup = renderToStaticMarkup( createElement(FlooringPreview, { piece: floor, compact: true }), ); const skirtMarkup = renderToStaticMarkup( createElement(SkirtingPreview, { piece: skirt, thickness: 18, compact: true }), ); expect(floorMarkup).toContain('aria-hidden="true"'); expect(skirtMarkup.match(/aria-hidden="true"/g)).toHaveLength(3); expect(skirtMarkup).toContain('>//'); expect(skirtMarkup.match(/preserveAspectRatio="xMidYMid meet"/g)).toHaveLength(2); expect(floorMarkup).not.toContain('