import { describe, expect, it } from 'vitest'; import { clampCrop, initialCrop, outputSize } from './crop'; /** * The crop arithmetic (issue #306). Pure functions on purpose: the canvas * work is a thin shell around these, and getting the bounds wrong is what * would let a number input produce a rectangle outside the image. */ describe('initialCrop', () => { it('takes the whole image when the aspect is free', () => { expect(initialCrop({ width: 900, height: 300 }, false)).toEqual({ x: 0, y: 0, width: 900, height: 300, }); }); it('centres the largest square that fits', () => { expect(initialCrop({ width: 900, height: 300 }, true)).toEqual({ x: 300, y: 0, width: 300, height: 300, }); }); }); describe('outputSize', () => { it('scales the long edge down to the bound and keeps the ratio', () => { expect(outputSize({ x: 0, y: 0, width: 900, height: 300 }, 512)).toEqual({ width: 512, height: 171, }); }); it('never scales UP — enlarging would only invent pixels', () => { expect(outputSize({ x: 0, y: 0, width: 120, height: 40 }, 512)).toEqual({ width: 120, height: 40, }); }); }); describe('clampCrop', () => { const source = { width: 200, height: 100 }; it('keeps the rectangle inside the image', () => { expect(clampCrop({ x: 190, y: 90, width: 50, height: 50 }, source)).toEqual({ x: 150, y: 50, width: 50, height: 50, }); }); it('never lets a size fall below one pixel or exceed the source', () => { expect(clampCrop({ x: 0, y: 0, width: 0, height: 999 }, source)).toEqual({ x: 0, y: 0, width: 1, height: 100, }); }); it('accepts a negative offset by pulling it back to the edge', () => { expect(clampCrop({ x: -30, y: -5, width: 20, height: 20 }, source)).toMatchObject({ x: 0, y: 0, }); }); });