import { describe, expect, it } from 'vitest'; import { AppState } from '../src/lib/app-state.svelte'; const image = { width: 100, height: 80, data: new Uint8ClampedArray(100 * 80 * 4) }; function withImage() { const app = new AppState(); app.setImage(image); return app; } describe('AppState', () => { it('starts with sensible defaults', () => { const app = new AppState(); expect(app.marginMm).toBe(-2); expect(app.mode).toBe('auto'); expect(app.tool).toBe('none'); expect(app.shapePx).toBeNull(); expect(app.exportShape).toBeNull(); }); it('sets the scale from two clicks, then leaves the scale tool', () => { const app = withImage(); app.tool = 'scale'; app.handleImageClick({ x: 10, y: 10 }); expect(app.tool).toBe('scale'); app.handleImageClick({ x: 70, y: 10 }); expect(app.tool).toBe('none'); app.lengthMm = 30; expect(app.pxPerMm).toBeCloseTo(2); expect(app.marginPx).toBeCloseTo(-4); }); it('restarts the scale line on a third click', () => { const app = withImage(); app.tool = 'scale'; app.handleImageClick({ x: 10, y: 10 }); app.handleImageClick({ x: 70, y: 10 }); app.tool = 'scale'; app.handleImageClick({ x: 5, y: 5 }); expect(app.scalePoints).toEqual([{ x: 5, y: 5 }]); expect(app.pxPerMm).toBeNull(); }); it('flags an invalid scale and keeps the margin at 0 until the scale is valid', () => { const app = withImage(); app.tool = 'scale'; app.handleImageClick({ x: 10, y: 10 }); app.handleImageClick({ x: 10, y: 10 }); app.lengthMm = 50; expect(app.scaleInvalid).toBe(true); expect(app.marginPx).toBe(0); }); it('ignores clicks outside the image', () => { const app = withImage(); app.tool = 'seed'; app.handleImageClick({ x: -1, y: 10 }); app.handleImageClick({ x: 50, y: 80 }); expect(app.seed).toBeNull(); expect(app.tool).toBe('seed'); }); it('sets the seed and leaves the seed tool', () => { const app = withImage(); app.tool = 'seed'; app.handleImageClick({ x: 20, y: 30 }); expect(app.seed).toEqual({ x: 20, y: 30 }); expect(app.tool).toBe('none'); }); it('draws an outline then holes, switching to manual mode', () => { const app = withImage(); app.tool = 'draw'; [{ x: 1, y: 1 }, { x: 90, y: 1 }, { x: 90, y: 70 }].forEach((p) => app.handleImageClick(p)); app.finishPolygon(); expect(app.manualOuter).toHaveLength(3); expect(app.mode).toBe('manual'); [{ x: 20, y: 20 }, { x: 30, y: 20 }, { x: 30, y: 30 }].forEach((p) => app.handleImageClick(p)); app.finishPolygon(); expect(app.manualHoles).toHaveLength(1); expect(app.drawing).toEqual([]); }); it('discards a polygon with fewer than 3 points', () => { const app = withImage(); app.tool = 'draw'; app.handleImageClick({ x: 1, y: 1 }); app.finishPolygon(); expect(app.manualOuter).toEqual([]); expect(app.mode).toBe('auto'); }); it('clearManual returns to auto mode', () => { const app = withImage(); app.tool = 'draw'; [{ x: 1, y: 1 }, { x: 90, y: 1 }, { x: 90, y: 70 }].forEach((p) => app.handleImageClick(p)); app.finishPolygon(); app.clearManual(); expect(app.mode).toBe('auto'); expect(app.manualOuter).toEqual([]); expect(app.tool).toBe('none'); }); it('a new image resets scale, seed and manual work', () => { const app = withImage(); app.tool = 'scale'; app.handleImageClick({ x: 10, y: 10 }); app.seed = { x: 5, y: 5 }; app.manualOuter = [{ x: 1, y: 1 }, { x: 2, y: 2 }, { x: 3, y: 1 }]; app.mode = 'manual'; app.setImage(image); expect(app.scalePoints).toEqual([]); expect(app.seed).toBeNull(); expect(app.manualOuter).toEqual([]); expect(app.mode).toBe('auto'); expect(app.tool).toBe('none'); expect(app.threshold).toBe(128); }); });