feat: release Colour Tools 0.1.0
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
import fc from "fast-check";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
conversionRows,
|
||||
formatColour,
|
||||
parseColour,
|
||||
parseColourList,
|
||||
toSrgbPreview,
|
||||
tryParseColour,
|
||||
} from "../../src/colour";
|
||||
|
||||
describe("colour parsing and conversion", () => {
|
||||
it("converts the RapidTables reference colour", () => {
|
||||
const colour = parseColour("#f8f5ee");
|
||||
expect(colour.space).toBe("srgb");
|
||||
expect(colour.coords).toEqual([248 / 255, 245 / 255, 238 / 255]);
|
||||
expect(formatColour(colour, "hex")).toBe("#f8f5ee");
|
||||
expect(formatColour(colour, "rgb")).toBe("rgb(248, 245, 238)");
|
||||
expect(formatColour(colour, "hsl")).toBe("hsl(42 41.6667% 95.2941%)");
|
||||
});
|
||||
|
||||
it("accepts CSS Color 4, named, bare hex and numeric RGB input", () => {
|
||||
expect(formatColour("rebeccapurple", "hex")).toBe("#663399");
|
||||
expect(formatColour("f8f5ee", "hex")).toBe("#f8f5ee");
|
||||
expect(formatColour("0xff000080", "hex8")).toBe("#ff000080");
|
||||
expect(formatColour("248, 245, 238", "hex")).toBe("#f8f5ee");
|
||||
const p3 = parseColour("color(display-p3 1 0.2 0.1 / 75%)");
|
||||
expect(p3.space).toBe("p3");
|
||||
expect(p3.alpha).toBeCloseTo(0.75);
|
||||
});
|
||||
|
||||
it("parses custom HSV/HSB with angles and alpha", () => {
|
||||
expect(formatColour("hsv(120 50% 60% / 25%)", "rgba")).toBe(
|
||||
"rgba(77, 153, 77, 0.25)",
|
||||
);
|
||||
expect(formatColour("hsb(.333333turn, .5, .6)", "hex")).toBe("#4d994d");
|
||||
expect(parseColour("hsv(-30deg 100% 100%)").coords[0]).toBe(330);
|
||||
});
|
||||
|
||||
it("parses custom CMYK and converts it deterministically", () => {
|
||||
expect(formatColour("cmyk(0% 100% 100% 0% / .5)", "hex8")).toBe(
|
||||
"#ff000080",
|
||||
);
|
||||
expect(formatColour("cmyk(100%, 0%, 100%, 25%)", "rgb")).toBe(
|
||||
"rgb(0, 191, 0)",
|
||||
);
|
||||
expect(formatColour("#000", "cmyk")).toBe("cmyk(0% 0% 0% 100%)");
|
||||
});
|
||||
|
||||
it("returns structured failures without throwing", () => {
|
||||
expect(tryParseColour("")).toMatchObject({
|
||||
ok: false,
|
||||
error: { code: "empty" },
|
||||
});
|
||||
expect(tryParseColour("hsv(20 200% 20%)")).toMatchObject({
|
||||
ok: false,
|
||||
error: { code: "out-of-range" },
|
||||
});
|
||||
expect(tryParseColour("definitely-not-a-colour").ok).toBe(false);
|
||||
});
|
||||
|
||||
it("splits lists only at top-level delimiters", () => {
|
||||
const colours = parseColourList("rgb(1, 2, 3), hsl(20 30% 40%);\n#fff");
|
||||
expect(colours).toHaveLength(3);
|
||||
expect(formatColour(colours[0]!, "rgb")).toBe("rgb(1, 2, 3)");
|
||||
expect(parseColourList('["#f00", "#0f0"]')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("offers copy-ready conversion rows and a safe preview", () => {
|
||||
const rows = conversionRows("#f8f5ee", ["hex", "rgb", "oklch"]);
|
||||
expect(rows.map((row) => row.label)).toEqual(["HEX", "RGB", "OKLCH"]);
|
||||
expect(rows[0]?.copyValue).toBe("#f8f5ee");
|
||||
const preview = toSrgbPreview("color(display-p3 1 0 0)");
|
||||
expect(preview.wasMapped).toBe(true);
|
||||
expect(preview.css).toMatch(/^rgb\(/);
|
||||
expect(preview.rgb.every((channel) => channel >= 0 && channel <= 1)).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("round-trips arbitrary 8-bit RGB colours through HEX", () => {
|
||||
fc.assert(
|
||||
fc.property(
|
||||
fc.integer({ min: 0, max: 255 }),
|
||||
fc.integer({ min: 0, max: 255 }),
|
||||
fc.integer({ min: 0, max: 255 }),
|
||||
(red, green, blue) => {
|
||||
const value = {
|
||||
space: "srgb",
|
||||
coords: [red / 255, green / 255, blue / 255] as [
|
||||
number,
|
||||
number,
|
||||
number,
|
||||
],
|
||||
alpha: 1,
|
||||
};
|
||||
expect(parseColour(formatColour(value, "hex")).coords).toEqual(
|
||||
value.coords,
|
||||
);
|
||||
},
|
||||
),
|
||||
{ numRuns: 200 },
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user