import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' import { afterEach, describe, expect, it } from 'vitest' import { DEFAULTS, getConfig, getThemeCss, resolveConfig } from '../src/main/config' import { DEFAULT_CONFIG as RENDERER_DEFAULTS } from '../src/renderer/src/types' let dir = '' afterEach(async () => { if (dir) await rm(dir, { recursive: true, force: true }) }) describe('resolveConfig', () => { it('creates config.default.json + theme.css and yields defaults for a fresh project', async () => { dir = await mkdtemp(join(tmpdir(), 'helder-cfg-')) await resolveConfig(dir) const def = JSON.parse(await readFile(join(dir, '.helder/config.default.json'), 'utf8')) expect(def).toEqual(DEFAULTS) const theme = await readFile(join(dir, '.helder/theme.css'), 'utf8') expect(theme).toContain('--code-font') expect(getConfig().ai.command).toBe('claude') expect(getThemeCss()).toContain('Helder theme') }) it('deep-merges a sparse config.json over defaults', async () => { dir = await mkdtemp(join(tmpdir(), 'helder-cfg-')) await resolveConfig(dir) await writeFile(join(dir, '.helder/config.json'), JSON.stringify({ ai: { command: 'claude --model opus' }, editor: { tabSize: 2 } })) await resolveConfig(dir) const c = getConfig() expect(c.ai.command).toBe('claude --model opus') // overridden expect(c.ai.autoLaunch).toBe(true) // default kept expect(c.editor.tabSize).toBe(2) // overridden expect(c.editor.autoSave).toBe(false) // default kept expect(c.git.confirmDiscard).toBe(true) // default kept }) it('always regenerates config.default.json with full built-in defaults', async () => { dir = await mkdtemp(join(tmpdir(), 'helder-cfg-')) await resolveConfig(dir) await writeFile(join(dir, '.helder/config.json'), JSON.stringify({ ai: { command: 'x' } })) await resolveConfig(dir) const def = JSON.parse(await readFile(join(dir, '.helder/config.default.json'), 'utf8')) expect(def.ai.command).toBe('claude') }) it('does not overwrite an existing theme.css', async () => { dir = await mkdtemp(join(tmpdir(), 'helder-cfg-')) await resolveConfig(dir) await writeFile(join(dir, '.helder/theme.css'), ':root{--accent:#ff0000}') await resolveConfig(dir) expect(getThemeCss()).toContain('#ff0000') }) }) describe('terminal defaults', () => { it('mirrors the main-process schema in the renderer', () => { // Two copies of one schema drift silently; the terminal block is the one // the user edits by hand, so it is worth asserting they still agree. expect(RENDERER_DEFAULTS.terminal).toEqual(DEFAULTS.terminal) }) it('carries a full ANSI table, so a partial config.json still resolves', () => { const t = DEFAULTS.terminal.theme for (const key of ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'] as const) { expect(t[key]).toMatch(/^#[0-9a-f]{6}$/i) expect(t[`bright${key[0].toUpperCase()}${key.slice(1)}` as keyof typeof t]).toMatch(/^#[0-9a-f]{6}$/i) } }) })