144 lines
4.7 KiB
TypeScript
144 lines
4.7 KiB
TypeScript
|
|
import { describe, it, expect } from "vitest";
|
|||
|
|
import {
|
|||
|
|
normalizeRatioToken,
|
|||
|
|
greatestCommonDivisor,
|
|||
|
|
formatAspectRatio,
|
|||
|
|
getQuickSetRatioValue,
|
|||
|
|
formatRatioDisplayValue,
|
|||
|
|
getRatioDisplayParts,
|
|||
|
|
parseRatioToAspectCss,
|
|||
|
|
toSupportedImageApiRatio,
|
|||
|
|
normalizeRatioForApi,
|
|||
|
|
} from "./ratioUtils";
|
|||
|
|
|
|||
|
|
describe("normalizeRatioToken", () => {
|
|||
|
|
it("converts fullwidth and mojibake characters", () => {
|
|||
|
|
expect(normalizeRatioToken("800\u00a0\u00a0px")).toBe("800 px");
|
|||
|
|
});
|
|||
|
|
it("replaces * with ×", () => {
|
|||
|
|
expect(normalizeRatioToken("800*800")).toBe("800×800");
|
|||
|
|
});
|
|||
|
|
it("replaces mojibake 脳 with ×", () => {
|
|||
|
|
expect(normalizeRatioToken("800脳800")).toBe("800×800");
|
|||
|
|
});
|
|||
|
|
it("replaces fullwidth colon", () => {
|
|||
|
|
expect(normalizeRatioToken("1:1")).toBe("1:1");
|
|||
|
|
});
|
|||
|
|
it("collapses whitespace and trims", () => {
|
|||
|
|
expect(normalizeRatioToken(" 1 : 1 ")).toBe("1 : 1");
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe("greatestCommonDivisor", () => {
|
|||
|
|
it("computes GCD", () => {
|
|||
|
|
expect(greatestCommonDivisor(12, 8)).toBe(4);
|
|||
|
|
expect(greatestCommonDivisor(1920, 1080)).toBe(120);
|
|||
|
|
});
|
|||
|
|
it("handles zero with fallback to 1", () => {
|
|||
|
|
expect(greatestCommonDivisor(0, 5)).toBe(5);
|
|||
|
|
expect(greatestCommonDivisor(0, 0)).toBe(1);
|
|||
|
|
});
|
|||
|
|
it("handles negatives via abs", () => {
|
|||
|
|
expect(greatestCommonDivisor(-12, 8)).toBe(4);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe("formatAspectRatio", () => {
|
|||
|
|
it("reduces 1920x1080 to 16:9", () => {
|
|||
|
|
expect(formatAspectRatio(1920, 1080)).toBe("16:9");
|
|||
|
|
});
|
|||
|
|
it("reduces 750x1000 to 3:4", () => {
|
|||
|
|
expect(formatAspectRatio(750, 1000)).toBe("3:4");
|
|||
|
|
});
|
|||
|
|
it("reduces 800x800 to 1:1", () => {
|
|||
|
|
expect(formatAspectRatio(800, 800)).toBe("1:1");
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe("getQuickSetRatioValue", () => {
|
|||
|
|
it("passes through a canonical quick-set value", () => {
|
|||
|
|
expect(getQuickSetRatioValue("1:1")).toBe("1:1");
|
|||
|
|
});
|
|||
|
|
it("derives from a WxH size string", () => {
|
|||
|
|
expect(getQuickSetRatioValue("1920×1080px")).toBe("16:9");
|
|||
|
|
expect(getQuickSetRatioValue("750×1000px")).toBe("3:4");
|
|||
|
|
});
|
|||
|
|
it("derives from a raw ratio string", () => {
|
|||
|
|
expect(getQuickSetRatioValue("9:16")).toBe("9:16");
|
|||
|
|
});
|
|||
|
|
it("falls back to 1:1 for unparseable input", () => {
|
|||
|
|
expect(getQuickSetRatioValue("unknown")).toBe("1:1");
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe("formatRatioDisplayValue", () => {
|
|||
|
|
it("formats a WxHpx string with aspect suffix", () => {
|
|||
|
|
expect(formatRatioDisplayValue("1000×1000px 1:1")).toBe("1000×1000px\u00a0\u00a0\u00a01:1");
|
|||
|
|
});
|
|||
|
|
it("reformats 800×800px without explicit aspect", () => {
|
|||
|
|
expect(formatRatioDisplayValue("800×800px")).toBe("800×800px\u00a0\u00a0\u00a01:1");
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe("getRatioDisplayParts", () => {
|
|||
|
|
it("splits size and aspect", () => {
|
|||
|
|
expect(getRatioDisplayParts("1000×1000px 1:1")).toEqual({
|
|||
|
|
size: "1000×1000px",
|
|||
|
|
aspect: "1:1",
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
it("uses 自适应 when no aspect present", () => {
|
|||
|
|
const parts = getRatioDisplayParts("原图");
|
|||
|
|
expect(parts.aspect).toBe("自适应");
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe("parseRatioToAspectCss", () => {
|
|||
|
|
it("extracts CSS aspect-ratio", () => {
|
|||
|
|
expect(parseRatioToAspectCss("1000×1000px 1:1")).toBe("1000 / 1000");
|
|||
|
|
});
|
|||
|
|
it("falls back to 1 / 1", () => {
|
|||
|
|
expect(parseRatioToAspectCss("no numbers")).toBe("1 / 1");
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe("toSupportedImageApiRatio", () => {
|
|||
|
|
it("snaps square to 1:1", () => {
|
|||
|
|
expect(toSupportedImageApiRatio(800, 800)).toBe("1:1");
|
|||
|
|
});
|
|||
|
|
it("snaps 750x1000 to 3:4", () => {
|
|||
|
|
expect(toSupportedImageApiRatio(750, 1000)).toBe("3:4");
|
|||
|
|
});
|
|||
|
|
it("snaps 1920x1080 to 16:9", () => {
|
|||
|
|
expect(toSupportedImageApiRatio(1920, 1080)).toBe("16:9");
|
|||
|
|
});
|
|||
|
|
it("snaps 1080x1920 to 9:16", () => {
|
|||
|
|
expect(toSupportedImageApiRatio(1080, 1920)).toBe("9:16");
|
|||
|
|
});
|
|||
|
|
it("snaps 800x600 to 4:3", () => {
|
|||
|
|
expect(toSupportedImageApiRatio(800, 600)).toBe("4:3");
|
|||
|
|
});
|
|||
|
|
it("returns 1:1 for non-finite or non-positive", () => {
|
|||
|
|
expect(toSupportedImageApiRatio(NaN, 100)).toBe("1:1");
|
|||
|
|
expect(toSupportedImageApiRatio(0, 100)).toBe("1:1");
|
|||
|
|
expect(toSupportedImageApiRatio(-1, 100)).toBe("1:1");
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
describe("normalizeRatioForApi", () => {
|
|||
|
|
it("extracts the explicit ratio from a display string", () => {
|
|||
|
|
expect(normalizeRatioForApi("1000×1000px 1:1")).toBe("1:1");
|
|||
|
|
expect(normalizeRatioForApi("750×1000px 3:4")).toBe("3:4");
|
|||
|
|
});
|
|||
|
|
it("derives ratio from a bare size string", () => {
|
|||
|
|
expect(normalizeRatioForApi("1920×1080px")).toBe("16:9");
|
|||
|
|
});
|
|||
|
|
it("returns 1:1 for unparseable input", () => {
|
|||
|
|
expect(normalizeRatioForApi("")).toBe("1:1");
|
|||
|
|
expect(normalizeRatioForApi("无尺寸信息")).toBe("1:1");
|
|||
|
|
});
|
|||
|
|
it("uses the last explicit ratio when multiple present", () => {
|
|||
|
|
expect(normalizeRatioForApi("4:3 16:9")).toBe("16:9");
|
|||
|
|
});
|
|||
|
|
});
|