blob: 97b1459dc0a370994a4721b812a1769febc6dd72 [file]
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { buttonVariants } from '../ui.js';
import { cn } from '../utils.js';
test('Button exposes only the governed 32px and 28px geometry tiers', () => {
const medium = buttonVariants({ size: 'md' });
const small = buttonVariants({ size: 'sm' });
const icon = buttonVariants({ size: 'icon' });
const iconSmall = buttonVariants({ size: 'icon-sm' });
assert.match(medium, /\bh-8\b/);
assert.match(medium, /\bpx-3\b/);
assert.match(small, /\bh-7\b/);
assert.match(small, /\bpx-2\b/);
assert.match(icon, /\bh-8\b/);
assert.match(icon, /\bw-8\b/);
assert.match(iconSmall, /\bh-7\b/);
assert.match(iconSmall, /\bw-7\b/);
});
test('Button neutral variants share one restrained interaction hierarchy', () => {
const secondary = buttonVariants({ variant: 'secondary' });
const ghost = buttonVariants({ variant: 'ghost' });
const quiet = buttonVariants({ variant: 'quiet' });
assert.match(secondary, /\bborder\b/);
assert.match(secondary, /\bbg-transparent\b/);
for (const classes of [secondary, ghost, quiet]) {
assert.match(classes, /\bfont-normal\b/);
assert.match(classes, /hover:bg-\[var\(--state-hover-bg\)\]/);
assert.match(classes, /active:bg-\[var\(--state-selected-bg\)\]/);
}
});
test('Button focus and solid states stay visibly distinct without elevation', () => {
const primary = buttonVariants({ variant: 'default' });
const destructive = buttonVariants({ variant: 'destructive' });
assert.match(primary, /\bfont-medium\b/);
assert.match(destructive, /\bfont-medium\b/);
assert.ok(!primary.includes('shadow-'));
assert.ok(!primary.includes('ring-offset'));
assert.notEqual(primary.match(/hover:[^ ]+/)?.[0], primary.match(/active:[^ ]+/)?.[0]);
assert.notEqual(destructive.match(/hover:[^ ]+/)?.[0], destructive.match(/active:[^ ]+/)?.[0]);
});
test('aria-disabled Button variants keep their resting tone during hover and active', () => {
const quiet = buttonVariants({ variant: 'quiet' });
const primary = buttonVariants({ variant: 'default' });
assert.match(quiet, /aria-disabled:hover:bg-transparent/);
assert.match(quiet, /aria-disabled:active:bg-transparent/);
assert.match(quiet, /aria-disabled:hover:text-foreground-secondary/);
assert.match(primary, /aria-disabled:hover:bg-primary/);
assert.match(primary, /aria-disabled:active:bg-primary/);
});
test('Button pill shape lands on the governed pill radius tier', () => {
const pill = cn(buttonVariants({ size: 'icon', shape: 'pill' }));
const resting = cn(buttonVariants({ size: 'icon' }));
// rounded-full resolves to --radius-pill (999px) per the radius-converge
// contract; tailwind-merge must drop the base rounded-sm so the pill wins.
assert.match(pill, /\brounded-full\b/);
assert.ok(!pill.includes('rounded-sm'), 'pill shape must override the base rounded-sm');
assert.match(resting, /\brounded-sm\b/);
assert.ok(!resting.includes('rounded-full'));
});