sc-theme-builder/test/deepOverlay.test.js
2026-07-01 20:07:28 -05:00

72 lines
3.5 KiB
JavaScript

const { test } = require("node:test");
const assert = require("node:assert");
const { colorContrast, shade, tint, toHex, parseHexColor } = require("../lib/bootstrapColor");
const { solidButtonRule, outlineButtonRule, emitButtonRules } = require("../lib/deepOverlay");
// Ground truth extracted from the vendored Bootstrap 5.3.3 compiled CSS:
// each variant -> { bg, color, hoverBg, activeBorder }.
const ORACLES = {
primary: { bg: "#0d6efd", color: "#ffffff", hoverBg: "#0b5ed7", activeBorder: "#0a53be" },
secondary: { bg: "#6c757d", color: "#ffffff", hoverBg: "#5c636a", activeBorder: "#51585e" },
success: { bg: "#198754", color: "#ffffff", hoverBg: "#157347", activeBorder: "#13653f" },
info: { bg: "#0dcaf0", color: "#000000", hoverBg: "#31d2f2", activeBorder: "#25cff2" },
warning: { bg: "#ffc107", color: "#000000", hoverBg: "#ffca2c", activeBorder: "#ffc720" },
danger: { bg: "#dc3545", color: "#ffffff", hoverBg: "#bb2d3b", activeBorder: "#a52834" },
light: { bg: "#f8f9fa", color: "#000000", hoverBg: "#d3d4d5", activeBorder: "#babbbc" },
dark: { bg: "#212529", color: "#ffffff", hoverBg: "#424649", activeBorder: "#373b3e" },
};
test("colorContrast matches Bootstrap's --bs-btn-color for all 8 variants", () => {
for (const [name, o] of Object.entries(ORACLES)) {
assert.equal(colorContrast(o.bg), o.color, `${name} (${o.bg})`);
}
});
test("solid .btn-<variant> rules match Bootstrap's compiled hover/active colors", () => {
for (const [name, o] of Object.entries(ORACLES)) {
const rule = solidButtonRule(name, o.bg);
assert.ok(rule.includes(`--bs-btn-hover-bg:${o.hoverBg};`), `${name} hover-bg -> ${o.hoverBg}\n${rule}`);
assert.ok(rule.includes(`--bs-btn-active-border-color:${o.activeBorder};`), `${name} active-border -> ${o.activeBorder}`);
assert.ok(rule.includes(`--bs-btn-bg:${o.bg};`), `${name} bg`);
}
});
test("primary focus-shadow-rgb matches Bootstrap (49, 132, 253)", () => {
assert.ok(solidButtonRule("primary", "#0d6efd").includes("--bs-btn-focus-shadow-rgb:49, 132, 253;"));
assert.ok(solidButtonRule("warning", "#ffc107").includes("--bs-btn-focus-shadow-rgb:217, 164, 6;"));
});
test("shade/tint match Bootstrap's Sass math", () => {
assert.equal(toHex(shade(parseHexColor("#0d6efd"), 15)), "#0b5ed7");
assert.equal(toHex(tint(parseHexColor("#ffc107"), 15)), "#ffca2c");
assert.equal(toHex(shade(parseHexColor("#f8f9fa"), 25)), "#babbbc"); // light force-shade
assert.equal(toHex(tint(parseHexColor("#212529"), 10)), "#373b3e"); // dark force-tint
});
test("outline button inverts to the variant color", () => {
const r = outlineButtonRule("primary", "#0d6efd");
assert.ok(r.includes("--bs-btn-color:#0d6efd;"));
assert.ok(r.includes("--bs-btn-hover-bg:#0d6efd;"));
assert.ok(r.includes("--bs-btn-disabled-bg:transparent;"));
});
test("a recolored primary produces a recolored button (the whole point)", () => {
const css = emitButtonRules({ primary: "#e83e8c" });
assert.match(css, /\.btn-primary\{[^}]*--bs-btn-bg:#e83e8c;/);
// #e83e8c gets BLACK text via color-contrast -> tint mode -> hover LIGHTENS:
assert.match(css, /--bs-btn-hover-bg:#eb5b9d;/); // tint(#e83e8c,15%)
assert.match(css, /--bs-btn-color:#000000;/); // contrast picks dark text
});
test("non-hex variant colors are skipped (no throw)", () => {
const css = emitButtonRules({ primary: "var(--x)", success: "#198754" });
assert.doesNotMatch(css, /\.btn-primary\{/);
assert.match(css, /\.btn-success\{/);
});