Files
markdown-converter/tests/main/theme-menu-builder.test.js
amitwh a2b4a856f3 feat(theme-registry): buildThemeMenu converts registry → Electron MenuItem[]
Adds src/main/themeMenuBuilder.js — a pure module that consumes
ThemeRegistry.list() + ThemeRegistry.categories() and the injected
setTheme/getCurrentThemeId callbacks to produce the View → Theme
submenu's MenuItemTemplate[] in the same shape as the previous
hardcoded block in src/main.js:1137-1245.

- Radio-style items with checked=true on the active theme id
- Grouped by category in registry order with separators between
  non-empty categories
- No Electron / electron-store imports — keeps the module pure and
  unit-testable under @jest-environment node
- Tests use jest.resetModules() + per-test require to avoid the
  module-cache leakage the bootstrap test surfaced in T2

Amit Haridas
2026-09-14 19:46:15 +05:30

77 lines
2.9 KiB
JavaScript

/**
* @jest-environment node
*
* themeMenuBuilder tests — pure module. We pass in a fake `setTheme` and a
* fake `getCurrentThemeId` so we never touch electron-store or Electron.
*
* Note on isolation: ThemeRegistry holds state in a module-scoped Map.
* `jest.resetModules()` drops the cache so each test re-requires a fresh
* ThemeRegistry + buildThemeMenu pair, preventing leakage across tests.
*/
let ThemeRegistry;
let buildThemeMenu;
beforeEach(() => {
jest.resetModules();
ThemeRegistry = require('../../src/main/ThemeRegistry');
buildThemeMenu = require('../../src/main/themeMenuBuilder').buildThemeMenu;
ThemeRegistry.clear();
});
describe('buildThemeMenu', () => {
test('groups themes by category in registry order', () => {
ThemeRegistry.register({ id: 'l1', label: 'L1', category: 'light', isDark: false });
ThemeRegistry.register({ id: 'l2', label: 'L2', category: 'light', isDark: false });
ThemeRegistry.register({ id: 'd1', label: 'D1', category: 'dark', isDark: true });
ThemeRegistry.register({ id: 'd2', label: 'D2', category: 'dark', isDark: true });
ThemeRegistry.register({ id: 'hc1', label: 'HC1', category: 'high-contrast', isDark: true });
ThemeRegistry.register({ id: 's1', label: 'S1', category: 'seasonal', isDark: false });
const setTheme = jest.fn();
const items = buildThemeMenu({ setTheme, getCurrentThemeId: () => 'd1' });
// Expected order: light (2 items), separator, dark (2 items), separator, hc (1), separator, seasonal (1)
expect(items.map((i) => i.label || i.type)).toEqual([
'L1',
'L2',
'separator',
'D1',
'D2',
'separator',
'HC1',
'separator',
'S1',
]);
});
test('marks the currently-selected theme as checked=true', () => {
ThemeRegistry.register({ id: 'a', label: 'A', category: 'light', isDark: false });
ThemeRegistry.register({ id: 'b', label: 'B', category: 'dark', isDark: true });
const items = buildThemeMenu({ setTheme: jest.fn(), getCurrentThemeId: () => 'b' });
const aItem = items.find((i) => i.label === 'A');
const bItem = items.find((i) => i.label === 'B');
expect(aItem.type).toBe('radio');
expect(aItem.checked).toBe(false);
expect(bItem.type).toBe('radio');
expect(bItem.checked).toBe(true);
});
test('click handler calls setTheme with the theme id', () => {
ThemeRegistry.register({ id: 'a', label: 'A', category: 'light', isDark: false });
const setTheme = jest.fn();
const items = buildThemeMenu({ setTheme, getCurrentThemeId: () => 'a' });
items[0].click();
expect(setTheme).toHaveBeenCalledWith('a');
});
test('falls back to no theme checked when current id unknown', () => {
ThemeRegistry.register({ id: 'a', label: 'A', category: 'light', isDark: false });
const items = buildThemeMenu({
setTheme: jest.fn(),
getCurrentThemeId: () => 'deleted-theme',
});
expect(items.find((i) => i.label === 'A').checked).toBe(false);
});
});