src/lib/picture.test.ts
Rama main · réplica de lectura del repositorio original.
import { describe, expect, it } from "vitest";
import {
PICTURE_RATIO,
pictureSizeProblem,
readPictureType,
sniffPictureType,
toPictureCrop,
type PictureCrop,
type PictureKind,
} from "./picture";
const JPEG_HEAD = [0xff, 0xd8, 0xff, 0xe0];
const PNG_HEAD = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
describe("the type is read off the bytes", () => {
it("knows a JPEG and a PNG by their signatures", () => {
expect(sniffPictureType(new Uint8Array(JPEG_HEAD))).toBe("image/jpeg");
expect(sniffPictureType(new Uint8Array(PNG_HEAD))).toBe("image/png");
});
it("refuses the formats the API keeps no original of", () => {
// RIFF....WEBP and GIF89a: both display in a browser, and both are a 415.
const webp = [0x52, 0x49, 0x46, 0x46, 0, 0, 0, 0, 0x57, 0x45, 0x42, 0x50];
const gif = [0x47, 0x49, 0x46, 0x38, 0x39, 0x61];
expect(sniffPictureType(new Uint8Array(webp))).toBeNull();
expect(sniffPictureType(new Uint8Array(gif))).toBeNull();
expect(sniffPictureType(new Uint8Array([]))).toBeNull();
});
it("reads a file's own bytes rather than its declared type", async () => {
// What a HEIC renamed `.jpg` looks like to `File.type`.
const disguised = new File([new Uint8Array([0, 0, 0, 0x18])], "a.jpg", {
type: "image/jpeg",
});
const undeclared = new File([new Uint8Array(PNG_HEAD)], "a", { type: "" });
expect(await readPictureType(disguised)).toBeNull();
expect(await readPictureType(undeclared)).toBe("image/png");
});
});
describe("what the API would refuse by size", () => {
it("passes a 12 MP phone photo either way round, as a JPEG or a PNG", () => {
for (const type of ["image/jpeg", "image/png"] as const) {
expect(pictureSizeProblem(type, 4032, 3024)).toBeNull();
expect(pictureSizeProblem(type, 3024, 4032)).toBeNull();
}
});
it("holds a PNG to the decode cap, and says what the cap is", () => {
// An iPhone screenshot passes; a 24 MP PNG does not.
expect(pictureSizeProblem("image/png", 1179, 2556)).toBeNull();
const problem = pictureSizeProblem("image/png", 5712, 4284);
expect(problem).toMatch(/4032 × 3024/);
expect(problem).toMatch(/5712 × 4284/);
});
it("passes a 24 MP JPEG, which the API decodes reduced", () => {
expect(pictureSizeProblem("image/jpeg", 5712, 4284)).toBeNull();
expect(pictureSizeProblem("image/jpeg", 8000, 6000)).toBeNull();
});
it("holds a JPEG to the 50 MP it may declare", () => {
expect(pictureSizeProblem("image/jpeg", 10000, 5001)).toMatch(
/50 megapixels/,
);
});
it("passes a panorama, which the API reduces however narrow it is", () => {
expect(pictureSizeProblem("image/jpeg", 15000, 1000)).toBeNull();
expect(pictureSizeProblem("image/jpeg", 40000, 500)).toBeNull();
});
});
describe("the crop the API is sent", () => {
const withinRatio = (crop: PictureCrop, kind: PictureKind) => {
const [width, height] = PICTURE_RATIO[kind];
return (
Math.abs(crop.width * height - crop.height * width) <=
Math.max(width, height)
);
};
it("passes the cropper's own rectangle through untouched", () => {
const area = { x: 0, y: 72, width: 3024, height: 3888 };
expect(
toPictureCrop(area, { width: 3024, height: 4032 }, "portrait"),
).toEqual(area);
});
it("brings a rectangle one pixel over the edge back inside", () => {
const crop = toPictureCrop(
{ x: 1, y: 0, width: 700, height: 900 },
{ width: 700, height: 900 },
"portrait",
);
expect(crop.x + crop.width).toBeLessThanOrEqual(700);
expect(crop.y + crop.height).toBeLessThanOrEqual(900);
expect(withinRatio(crop, "portrait")).toBe(true);
});
it("puts a fractional, off-ratio rectangle on whole pixels at the ratio", () => {
for (const kind of ["avatar", "portrait"] as const) {
const crop = toPictureCrop(
{ x: 10.4, y: 20.6, width: 333.3, height: 401.9 },
{ width: 1000, height: 1000 },
kind,
);
for (const value of Object.values(crop)) {
expect(Number.isInteger(value)).toBe(true);
}
expect(withinRatio(crop, kind)).toBe(true);
}
});
it("shrinks a height the width cannot follow", () => {
// A 7:9 crop 900 high needs 700 wide, and this image has 650.
const crop = toPictureCrop(
{ x: 0, y: 0, width: 700, height: 900 },
{ width: 650, height: 900 },
"portrait",
);
expect(crop.width).toBeLessThanOrEqual(650);
expect(crop.height).toBeLessThan(900);
expect(withinRatio(crop, "portrait")).toBe(true);
});
});