e3532064b5
- utils/accentColor (8): hexToRgb parsing, lighten/darken channel math, rgba clamping, WCAG relativeLuminance (black=0/white=1), contrastingText threshold, varNameFromToken, and derivePrimaryPalette's full 10-token output. - utils/matrix-uia (7): UIA flow helpers — getSupportedUIAFlows, completed/params/session/errcode/error accessors, getUIAFlowForStages (incl. the single-extra-dummy rule), has/requiredStageInFlows, and getLoginTermUrl language fallback. Full suite now 123 tests, all passing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
69 lines
2.8 KiB
TypeScript
69 lines
2.8 KiB
TypeScript
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
hexToRgb,
|
|
lighten,
|
|
darken,
|
|
rgba,
|
|
relativeLuminance,
|
|
contrastingText,
|
|
varNameFromToken,
|
|
derivePrimaryPalette,
|
|
} from './accentColor';
|
|
|
|
test('hexToRgb parses 6-digit hex (with/without #, trimmed)', () => {
|
|
assert.deepEqual(hexToRgb('#ff8800'), { r: 255, g: 136, b: 0 });
|
|
assert.deepEqual(hexToRgb('ff8800'), { r: 255, g: 136, b: 0 });
|
|
assert.deepEqual(hexToRgb(' #FF8800 '), { r: 255, g: 136, b: 0 });
|
|
assert.equal(hexToRgb('#fff'), undefined); // 3-digit not supported
|
|
assert.equal(hexToRgb('nope'), undefined);
|
|
});
|
|
|
|
test('lighten moves channels toward white', () => {
|
|
assert.deepEqual(lighten({ r: 255, g: 0, b: 0 }, 0.5), { r: 255, g: 127.5, b: 127.5 });
|
|
assert.deepEqual(lighten({ r: 0, g: 0, b: 0 }, 1), { r: 255, g: 255, b: 255 });
|
|
assert.deepEqual(lighten({ r: 10, g: 20, b: 30 }, 0), { r: 10, g: 20, b: 30 });
|
|
});
|
|
|
|
test('darken moves channels toward black', () => {
|
|
assert.deepEqual(darken({ r: 200, g: 100, b: 50 }, 0.5), { r: 100, g: 50, b: 25 });
|
|
assert.deepEqual(darken({ r: 255, g: 255, b: 255 }, 1), { r: 0, g: 0, b: 0 });
|
|
assert.deepEqual(darken({ r: 10, g: 20, b: 30 }, 0), { r: 10, g: 20, b: 30 });
|
|
});
|
|
|
|
test('rgba formats and clamps channels', () => {
|
|
assert.equal(rgba({ r: 255, g: 136, b: 0 }, 0.5), 'rgba(255, 136, 0, 0.5)');
|
|
assert.equal(rgba({ r: 300, g: -5, b: 128 }, 1), 'rgba(255, 0, 128, 1)');
|
|
});
|
|
|
|
test('relativeLuminance: black is 0, white is 1', () => {
|
|
assert.ok(Math.abs(relativeLuminance({ r: 0, g: 0, b: 0 })) < 1e-9);
|
|
assert.ok(Math.abs(relativeLuminance({ r: 255, g: 255, b: 255 }) - 1) < 1e-9);
|
|
// green contributes more than blue (per WCAG coefficients)
|
|
assert.ok(relativeLuminance({ r: 0, g: 255, b: 0 }) > relativeLuminance({ r: 0, g: 0, b: 255 }));
|
|
});
|
|
|
|
test('contrastingText picks black on light, white on dark', () => {
|
|
assert.equal(contrastingText({ r: 255, g: 255, b: 255 }), '#000');
|
|
assert.equal(contrastingText({ r: 0, g: 0, b: 0 }), '#fff');
|
|
});
|
|
|
|
test('varNameFromToken extracts the CSS var name', () => {
|
|
assert.equal(varNameFromToken('var(--oq6d07f)'), '--oq6d07f');
|
|
assert.equal(varNameFromToken('--bare'), undefined);
|
|
assert.equal(varNameFromToken('not a token'), undefined);
|
|
});
|
|
|
|
test('derivePrimaryPalette produces the full Primary token set', () => {
|
|
const palette = derivePrimaryPalette({ r: 255, g: 136, b: 0 });
|
|
assert.equal(Object.keys(palette).length, 10);
|
|
assert.equal(palette.Main, '#ff8800');
|
|
assert.equal(palette.MainLine, '#ff8800');
|
|
assert.equal(palette.Container, 'rgba(255, 136, 0, 0.12)');
|
|
assert.equal(palette.ContainerLine, 'rgba(255, 136, 0, 0.4)');
|
|
assert.equal(palette.OnMain, contrastingText({ r: 255, g: 136, b: 0 }));
|
|
// hover/active are valid 6-digit hex strings
|
|
assert.match(palette.MainHover, /^#[0-9a-f]{6}$/);
|
|
assert.match(palette.MainActive, /^#[0-9a-f]{6}$/);
|
|
});
|