File size: 6,597 Bytes
52efc7b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | /**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import {
createCustomTheme,
validateCustomTheme,
pickDefaultThemeName,
darkTheme,
type Theme,
} from './theme.js';
import { themeManager } from './theme-manager.js';
import type { CustomTheme } from '@google/gemini-cli-core';
describe('createCustomTheme', () => {
const baseTheme: CustomTheme = {
type: 'custom',
name: 'Test Theme',
Background: '#000000',
Foreground: '#ffffff',
LightBlue: '#ADD8E6',
AccentBlue: '#0000FF',
AccentPurple: '#800080',
AccentCyan: '#00FFFF',
AccentGreen: '#008000',
AccentYellow: '#FFFF00',
AccentRed: '#FF0000',
DiffAdded: '#00FF00',
DiffRemoved: '#FF0000',
Comment: '#808080',
Gray: '#cccccc',
// DarkGray intentionally omitted to test fallback
};
it('should interpolate DarkGray when not provided', () => {
const theme = createCustomTheme(baseTheme);
// Interpolate between Background (#000000) and Gray (#cccccc) at 0.4
// #cccccc is RGB(204, 204, 204)
// #000000 is RGB(0, 0, 0)
// Result is RGB(82, 82, 82) which is #525252
expect(theme.colors.DarkGray).toBe('#525252');
});
it('should use provided DarkGray', () => {
const theme = createCustomTheme({
...baseTheme,
DarkGray: '#123456',
});
expect(theme.colors.DarkGray).toBe('#123456');
});
it('should interpolate DarkGray when text.secondary is provided but DarkGray is not', () => {
const customTheme: CustomTheme = {
type: 'custom',
name: 'Test',
text: {
secondary: '#cccccc', // Gray source
},
background: {
primary: '#000000', // Background source
},
};
const theme = createCustomTheme(customTheme);
// Should be interpolated between #000000 and #cccccc at 0.4 -> #525252
expect(theme.colors.DarkGray).toBe('#525252');
});
it('should prefer text.secondary over Gray for interpolation', () => {
const customTheme: CustomTheme = {
type: 'custom',
name: 'Test',
text: {
secondary: '#cccccc', // Should be used
},
Gray: '#aaaaaa', // Should be ignored
background: {
primary: '#000000',
},
};
const theme = createCustomTheme(customTheme);
// Interpolate between #000000 and #cccccc -> #525252
expect(theme.colors.DarkGray).toBe('#525252');
});
});
describe('validateCustomTheme', () => {
const validTheme: CustomTheme = {
type: 'custom',
name: 'My Custom Theme',
Background: '#FFFFFF',
Foreground: '#000000',
LightBlue: '#ADD8E6',
AccentBlue: '#0000FF',
AccentPurple: '#800080',
AccentCyan: '#00FFFF',
AccentGreen: '#008000',
AccentYellow: '#FFFF00',
AccentRed: '#FF0000',
DiffAdded: '#00FF00',
DiffRemoved: '#FF0000',
Comment: '#808080',
Gray: '#808080',
};
it('should return isValid: true for a valid theme', () => {
const result = validateCustomTheme(validTheme);
expect(result.isValid).toBe(true);
expect(result.error).toBeUndefined();
});
it('should return isValid: false for a theme with an invalid name', () => {
const invalidTheme = { ...validTheme, name: ' ' };
const result = validateCustomTheme(invalidTheme);
expect(result.isValid).toBe(false);
expect(result.error).toBe('Invalid theme name: ');
});
it('should return isValid: true for a theme missing optional DiffAdded and DiffRemoved colors', () => {
const legacyTheme: Partial<CustomTheme> = { ...validTheme };
delete legacyTheme.DiffAdded;
delete legacyTheme.DiffRemoved;
const result = validateCustomTheme(legacyTheme);
expect(result.isValid).toBe(true);
expect(result.error).toBeUndefined();
});
it('should return isValid: false for a theme with a very long name', () => {
const invalidTheme = { ...validTheme, name: 'a'.repeat(51) };
const result = validateCustomTheme(invalidTheme);
expect(result.isValid).toBe(false);
expect(result.error).toBe(`Invalid theme name: ${'a'.repeat(51)}`);
});
});
describe('themeManager.loadCustomThemes', () => {
const baseTheme: Omit<CustomTheme, 'DiffAdded' | 'DiffRemoved'> & {
DiffAdded?: string;
DiffRemoved?: string;
} = {
type: 'custom',
name: 'Test Theme',
Background: '#FFF',
Foreground: '#000',
LightBlue: '#ADD8E6',
AccentBlue: '#00F',
AccentPurple: '#808',
AccentCyan: '#0FF',
AccentGreen: '#080',
AccentYellow: '#FF0',
AccentRed: '#F00',
Comment: '#888',
Gray: '#888',
};
it('should use values from DEFAULT_THEME when DiffAdded and DiffRemoved are not provided', () => {
const legacyTheme: Partial<CustomTheme> = { ...baseTheme };
delete legacyTheme.DiffAdded;
delete legacyTheme.DiffRemoved;
themeManager.loadCustomThemes({
'Legacy Custom Theme': legacyTheme as CustomTheme,
});
const result = themeManager.getTheme('Legacy Custom Theme')!;
expect(result.colors.DiffAdded).toBe(darkTheme.DiffAdded);
expect(result.colors.DiffRemoved).toBe(darkTheme.DiffRemoved);
expect(result.colors.AccentBlue).toBe(legacyTheme.AccentBlue);
expect(result.name).toBe(legacyTheme.name);
});
});
describe('pickDefaultThemeName', () => {
const mockThemes = [
{ name: 'Dark Theme', type: 'dark', colors: { Background: '#000000' } },
{ name: 'Light Theme', type: 'light', colors: { Background: '#ffffff' } },
{ name: 'Blue Theme', type: 'dark', colors: { Background: '#0000ff' } },
] as unknown as Theme[];
it('should return exact match if found', () => {
expect(
pickDefaultThemeName('#0000ff', mockThemes, 'Dark Theme', 'Light Theme'),
).toBe('Blue Theme');
});
it('should return exact match (case insensitive)', () => {
expect(
pickDefaultThemeName('#FFFFFF', mockThemes, 'Dark Theme', 'Light Theme'),
).toBe('Light Theme');
});
it('should return default light theme for light background if no match', () => {
expect(
pickDefaultThemeName('#eeeeee', mockThemes, 'Dark Theme', 'Light Theme'),
).toBe('Light Theme');
});
it('should return default dark theme for dark background if no match', () => {
expect(
pickDefaultThemeName('#111111', mockThemes, 'Dark Theme', 'Light Theme'),
).toBe('Dark Theme');
});
it('should return default dark theme if background is undefined', () => {
expect(
pickDefaultThemeName(undefined, mockThemes, 'Dark Theme', 'Light Theme'),
).toBe('Dark Theme');
});
});
|