diff options
| author | Craig Jennings <c@cjennings.net> | 2026-06-19 11:42:52 -0400 |
|---|---|---|
| committer | Craig Jennings <c@cjennings.net> | 2026-06-19 11:42:52 -0400 |
| commit | 7ef4737d3a06fb8b39eab3342658be76219ac6dc (patch) | |
| tree | 5c90acb2d69a809ff970b4055ea654ff37df2b49 /scripts/theme-studio/test-palette-generator-core.mjs | |
| parent | 3bdf9b23204f4f908d3ef0353b2fe2eb5f9f3d2c (diff) | |
| download | dotemacs-7ef4737d3a06fb8b39eab3342658be76219ac6dc.tar.gz dotemacs-7ef4737d3a06fb8b39eab3342658be76219ac6dc.zip | |
test(theme-studio): cover defensive branches and the palette generator
Added the uncovered fallback branches in app-core (migrateLegacyFace null input, normalizePkgFace's source fallback chain, mergePackagesInto's null/new-app guards, boxCss shading a relief from the bg when no box color is set) and in colormath (apca's equal-luminance return-0 and low-contrast clamp, isPureEndpointHex). New test-palette-generator-core.mjs drives planPaletteGenerator across every scheme, vibe, source mode, and the fill-gaps intents, since those internals are only reachable through the public planner. colormath branch 96 -> 99%, palette-generator-core funcs 97 -> 100%, node suite 237 tests. The remaining gaps are the deep palette-column edge branches, deferred as diminishing returns on already line-covered code.
Diffstat (limited to 'scripts/theme-studio/test-palette-generator-core.mjs')
| -rw-r--r-- | scripts/theme-studio/test-palette-generator-core.mjs | 78 |
1 files changed, 78 insertions, 0 deletions
diff --git a/scripts/theme-studio/test-palette-generator-core.mjs b/scripts/theme-studio/test-palette-generator-core.mjs new file mode 100644 index 000000000..d3d725957 --- /dev/null +++ b/scripts/theme-studio/test-palette-generator-core.mjs @@ -0,0 +1,78 @@ +// Unit tests for the palette generator planner (palette-generator-core.js). +// Only planPaletteGenerator and entriesForGeneratedColumn are exported, so the +// internal scheme / vibe / source-mode / intent logic is exercised by driving +// the planner across each of those input dimensions. +// Run: node --test scripts/theme-studio/ + +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { planPaletteGenerator, entriesForGeneratedColumn } from './palette-generator-core.js'; + +const GROUND = { bg: '#0d0b0a', fg: '#f0fef0' }; +const PAL = [['#0d0b0a', 'bg'], ['#f0fef0', 'fg'], ['#67809c', 'blue'], ['#e8bd30', 'gold']]; +const rng = () => 0.42; // deterministic, so failures repeat + +test('planPaletteGenerator: Normal — every scheme produces a valid plan', () => { + for (const scheme of ['random', 'analogous', 'triadic', 'manual']) { + const plan = planPaletteGenerator(PAL, GROUND, { scheme, accentCount: 4, spanCount: 0, rng }); + assert.equal(plan.scheme, scheme); + assert.ok(Array.isArray(plan.columns), `${scheme} columns`); + assert.equal(typeof plan.summary.generated, 'number'); + } +}); + +test('planPaletteGenerator: Normal — every vibe biases hues without error', () => { + for (const vibe of ['warm', 'cool', 'earthy', 'muted', 'pastel', 'deep', + 'jewel', 'neon', 'strange', 'balanced']) { + const plan = planPaletteGenerator(PAL, GROUND, + { scheme: 'analogous', vibe, accentCount: 5, spanCount: 0, rng }); + assert.equal(plan.vibe, vibe); + assert.ok(Array.isArray(plan.columns), `${vibe} columns`); + } +}); + +test('planPaletteGenerator: Normal — every source mode resolves', () => { + for (const sourceMode of ['bg-fg', 'palette', 'none', 'selected']) { + const plan = planPaletteGenerator(PAL, GROUND, + { sourceMode, selectedHex: '#9b5fd0', scheme: 'analogous', accentCount: 3, spanCount: 0, rng }); + assert.ok(['bg-fg', 'palette', 'none', 'selected'].includes(plan.sourceMode)); + assert.ok(Array.isArray(plan.columns)); + } +}); + +test('planPaletteGenerator: Boundary — selected source with no valid hex falls back to bg-fg', () => { + const plan = planPaletteGenerator(PAL, GROUND, + { sourceMode: 'selected', scheme: 'analogous', accentCount: 2, spanCount: 0, rng }); + assert.equal(plan.sourceMode, 'bg-fg'); +}); + +test('planPaletteGenerator: Normal — fill-gaps and fill-hue-gaps intents produce plans', () => { + for (const intent of ['fill-gaps', 'fill-hue-gaps']) { + const plan = planPaletteGenerator(PAL, GROUND, { intent, accentCount: 4, spanCount: 0, rng }); + assert.equal(plan.intent, intent); + assert.ok(Array.isArray(plan.columns)); + } +}); + +test('planPaletteGenerator: Boundary — an empty palette still plans', () => { + const plan = planPaletteGenerator([], { bg: '#000000', fg: '#ffffff' }, + { scheme: 'analogous', accentCount: 3, spanCount: 0, rng }); + assert.ok(Array.isArray(plan.columns)); + assert.equal(typeof plan.summary.generated, 'number'); +}); + +test('planPaletteGenerator: Boundary — spanCount expands a column into members', () => { + const plan = planPaletteGenerator(PAL, GROUND, + { scheme: 'analogous', accentCount: 2, spanCount: 2, rng }); + if (plan.columns.length) assert.ok(plan.columns[0].members.length >= 1); +}); + +test('entriesForGeneratedColumn: Normal — maps a planned column to palette entries', () => { + const plan = planPaletteGenerator(PAL, GROUND, + { scheme: 'analogous', accentCount: 1, spanCount: 0, rng }); + if (plan.columns.length) { + const entries = entriesForGeneratedColumn(plan.columns[0]); + assert.ok(Array.isArray(entries) && entries.length >= 1); + assert.ok(typeof entries[0][0] === 'string' && entries[0][0].startsWith('#')); + } +}); |
