Files
markdown-converter/tests/main/theme-registry.test.js
Amit Haridas 1e7e332c24 feat(theme-registry): pure ThemeRegistry module with full API
- 8 exports: register/unregister/list/get/categories/lightThemes/darkThemes/clear
- kebab-case id validator; category whitelist (light/dark/high-contrast/seasonal)
- duplicate-id and shape errors with descriptive messages
- pure CommonJS, no IO, no Electron deps — T2 bootstrap will register all 37 themes
- 10 Jest tests covering register/unregister/get/categories/light+dark filters
- full suite: 77 suites, 924 tests passing; lint + Prettier clean

Amit Haridas
2026-09-14 19:39:52 +05:30

127 lines
4.2 KiB
JavaScript

/**
* @jest-environment node
*
* ThemeRegistry tests — pure module. Tests run against a freshly-cleared
* registry (`clear()` is a test-only helper, but it's exported alongside the
* public API because every other pure module in src/main/ exposes the same
* `reset` helper for tests — see DocQA, DailyNotes, WorkspaceSearch).
*/
const ThemeRegistry = require('../../src/main/ThemeRegistry');
const validTheme = (overrides = {}) => ({
id: 'atomonelight',
label: 'Atom One Light',
category: 'light',
isDark: false,
...overrides,
});
describe('ThemeRegistry.register', () => {
beforeEach(() => ThemeRegistry.clear());
test('adds a theme to list()', () => {
ThemeRegistry.register(validTheme());
expect(ThemeRegistry.list()).toEqual([
{
id: 'atomonelight',
label: 'Atom One Light',
category: 'light',
isDark: false,
},
]);
});
test('throws on duplicate id', () => {
ThemeRegistry.register(validTheme());
expect(() => ThemeRegistry.register(validTheme())).toThrow(/duplicate theme id: atomonelight/);
});
test('throws when theme shape is invalid', () => {
expect(() => ThemeRegistry.register({ id: 'x' })).toThrow(/missing label/);
expect(() => ThemeRegistry.register({ label: 'X' })).toThrow(/missing id/);
expect(() => ThemeRegistry.register({ id: 'x', label: 'X' })).toThrow(/missing category/);
expect(() => ThemeRegistry.register({ id: 'x', label: 'X', category: 'light' })).toThrow(
/missing isDark/
);
expect(() =>
ThemeRegistry.register({
id: 'BAD ID',
label: 'X',
category: 'light',
isDark: false,
})
).toThrow(/kebab-case/);
expect(() =>
ThemeRegistry.register({
id: 'x',
label: 'X',
category: 'bogus',
isDark: false,
})
).toThrow(/invalid category/);
});
test('preserves registration order across many inserts', () => {
ThemeRegistry.register(validTheme({ id: 'a', label: 'A' }));
ThemeRegistry.register(validTheme({ id: 'b', label: 'B' }));
ThemeRegistry.register(validTheme({ id: 'c', label: 'C' }));
expect(ThemeRegistry.list().map((t) => t.id)).toEqual(['a', 'b', 'c']);
});
});
describe('ThemeRegistry.unregister', () => {
beforeEach(() => ThemeRegistry.clear());
test('removes a theme by id', () => {
ThemeRegistry.register(validTheme());
expect(ThemeRegistry.unregister('atomonelight')).toBe(true);
expect(ThemeRegistry.list()).toEqual([]);
});
test('returns false when id not found', () => {
expect(ThemeRegistry.unregister('nope')).toBe(false);
});
});
describe('ThemeRegistry.get', () => {
beforeEach(() => ThemeRegistry.clear());
test('returns the registered theme', () => {
ThemeRegistry.register(validTheme());
expect(ThemeRegistry.get('atomonelight')).toEqual({
id: 'atomonelight',
label: 'Atom One Light',
category: 'light',
isDark: false,
});
});
test('returns null for unknown id', () => {
expect(ThemeRegistry.get('nope')).toBeNull();
});
});
describe('ThemeRegistry.categories', () => {
beforeEach(() => ThemeRegistry.clear());
test('returns unique categories in registration order', () => {
ThemeRegistry.register(validTheme({ id: 'a', label: 'A', category: 'light' }));
ThemeRegistry.register(validTheme({ id: 'b', label: 'B', category: 'dark' }));
ThemeRegistry.register(validTheme({ id: 'c', label: 'C', category: 'light' }));
ThemeRegistry.register(validTheme({ id: 'd', label: 'D', category: 'high-contrast' }));
expect(ThemeRegistry.categories()).toEqual(['light', 'dark', 'high-contrast']);
});
});
describe('ThemeRegistry.lightThemes / darkThemes', () => {
beforeEach(() => ThemeRegistry.clear());
test('filters by category regardless of isDark', () => {
ThemeRegistry.register(validTheme({ id: 'a', label: 'A', category: 'light', isDark: false }));
ThemeRegistry.register(validTheme({ id: 'b', label: 'B', category: 'light', isDark: true }));
ThemeRegistry.register(validTheme({ id: 'c', label: 'C', category: 'dark', isDark: true }));
expect(ThemeRegistry.lightThemes().map((t) => t.id)).toEqual(['a', 'b']);
expect(ThemeRegistry.darkThemes().map((t) => t.id)).toEqual(['c']);
});
});