121 lines
3.8 KiB
TypeScript
121 lines
3.8 KiB
TypeScript
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);
|
|
});
|
|
});
|