import { describe, expect, it } from 'vitest'; import { getThemedText, getSubagentTypeColorSet, getTeamColorSet, scaleColorAlpha, TeamColorSet, } from '@renderer/constants/teamColors'; function isValidColorSet(cs: TeamColorSet): boolean { return ( typeof cs.border === 'string' && typeof cs.badge === 'string' && typeof cs.text === 'string' ); } // ============================================================================= // getTeamColorSet // ============================================================================= describe('getTeamColorSet', () => { it('returns blue (default) for empty string', () => { const result = getTeamColorSet(''); expect(result.border).toBe('#3b82f6'); }); it('resolves named colors', () => { expect(getTeamColorSet('green').border).toBe('#22c55e'); expect(getTeamColorSet('red').border).toBe('#ef4444'); expect(getTeamColorSet('purple').border).toBe('#a855f7'); }); it('resolves curated member palette colors for the first roster slots', () => { expect(getTeamColorSet('saffron').border).toBe('#eab308'); expect(getTeamColorSet('turquoise').border).toBe('#14b8a6'); expect(getTeamColorSet('brick').border).toBe('#ef4444'); expect(getTeamColorSet('indigo').border).toBe('#8b5cf6'); expect(getTeamColorSet('forest').border).toBe('#22c55e'); expect(getTeamColorSet('apricot').border).toBe('#fb923c'); expect(getTeamColorSet('rose').border).toBe('#f43f5e'); expect(getTeamColorSet('cerulean').border).toBe('#38bdf8'); expect(getTeamColorSet('olive').border).toBe('#84cc16'); expect(getTeamColorSet('copper').border).toBe('#b45309'); expect(getTeamColorSet('steel').border).toBe('#64748b'); }); it('is case-insensitive for named colors', () => { expect(getTeamColorSet('Green')).toEqual(getTeamColorSet('green')); expect(getTeamColorSet('BLUE')).toEqual(getTeamColorSet('blue')); }); it('generates a color set from hex strings', () => { const result = getTeamColorSet('#ff5500'); expect(result.border).toBe('#ff5500'); expect(result.badge).toBe('rgba(255, 85, 0, 0.15)'); expect(result.badgeLight).toBe('rgba(255, 85, 0, 0.12)'); expect(result.text).toBeTruthy(); expect(result.textLight).toBeTruthy(); expect(getThemedText(result, true)).not.toBe('#ff5500'); }); it('hashes unknown non-hex strings to a valid named color (not always blue)', () => { const result = getTeamColorSet('nonexistent'); // Should be a valid color set from the named palette, not necessarily blue expect(isValidColorSet(result)).toBe(true); // Should be deterministic expect(getTeamColorSet('nonexistent')).toEqual(result); // Different unknown strings should potentially yield different colors const colors = new Set( ['coral', 'sapphire', 'honey', 'arctic', 'chartreuse'].map( (name) => getTeamColorSet(name).border ) ); expect(colors.size).toBeGreaterThan(1); }); }); // ============================================================================= // getSubagentTypeColorSet // ============================================================================= describe('getSubagentTypeColorSet', () => { it('always returns a valid TeamColorSet without agent configs', () => { const types = ['test-agent', 'quality-fixer', 'Explore', 'Plan', 'my-custom-agent', 'anything']; for (const t of types) { const result = getSubagentTypeColorSet(t); expect(isValidColorSet(result)).toBe(true); } }); it('is deterministic — same input always returns same color', () => { const a = getSubagentTypeColorSet('my-custom-agent'); const b = getSubagentTypeColorSet('my-custom-agent'); expect(a).toEqual(b); }); it('different types can produce different colors', () => { const results = new Set( [ 'Explore', 'Plan', 'test-agent', 'quality-fixer', 'claude-md-auditor', 'Bash', 'general-purpose', 'statusline-setup', ].map((t) => getSubagentTypeColorSet(t).border) ); expect(results.size).toBeGreaterThan(1); }); it('uses color from agent config when available', () => { const configs = { 'test-agent': { name: 'test-agent', color: 'red' }, }; const result = getSubagentTypeColorSet('test-agent', configs); // Should use the named "red" color from getTeamColorSet expect(result.border).toBe('#ef4444'); expect(result.text).toBe('#f87171'); }); it('uses hex color from agent config', () => { const configs = { 'my-agent': { name: 'my-agent', color: '#ff00ff' }, }; const result = getSubagentTypeColorSet('my-agent', configs); expect(result.border).toBe('#ff00ff'); }); it('falls back to hash when agent config has no color', () => { const configs = { 'my-agent': { name: 'my-agent' }, }; const withConfig = getSubagentTypeColorSet('my-agent', configs); const withoutConfig = getSubagentTypeColorSet('my-agent'); // Should be the same — both use hash fallback expect(withConfig).toEqual(withoutConfig); }); it('falls back to hash when agent type not in configs', () => { const configs = { 'other-agent': { name: 'other-agent', color: 'green' }, }; const withConfig = getSubagentTypeColorSet('unknown-agent', configs); const withoutConfig = getSubagentTypeColorSet('unknown-agent'); expect(withConfig).toEqual(withoutConfig); }); it('does not interfere with getTeamColorSet', () => { const teamGreen = getTeamColorSet('green'); expect(teamGreen.border).toBe('#22c55e'); const configs = { green: { name: 'green', color: 'purple' } }; getSubagentTypeColorSet('green', configs); // Team API remains unaffected expect(getTeamColorSet('green').border).toBe('#22c55e'); }); }); describe('scaleColorAlpha', () => { it('halves rgba badge opacity', () => { expect(scaleColorAlpha('rgba(59, 130, 246, 0.15)', 0.5)).toBe('rgba(59, 130, 246, 0.075)'); }); it('halves hsla badge opacity', () => { expect(scaleColorAlpha('hsla(220, 80%, 50%, 0.12)', 0.5)).toBe('hsla(220, 80%, 50%, 0.06)'); }); it('halves hex alpha badge opacity', () => { expect(scaleColorAlpha('#ff550026', 0.5)).toBe('#ff550013'); }); });