/**
* Theme Schema Validation Integration Test
*
* Tests schema validation for theme configuration in the complete system
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { TestUtils } from '../../helpers/test-utils.js';
import { ConfigManager } from '../../../src/application/config/ConfigManager.js';
import { NodeFileSystem } from '../../../src/infrastructure/filesystem/node-filesystem.js';
import { NodeFileWriter } from '../../../src/infrastructure/filesystem/NodeFileWriter.js';
import { YamlParser } from '../../../src/infrastructure/parsers/YamlParser.js';
import { SimpleSchemaValidator, SimpleThemeSchemaLoader } from '../../../src/application/config/SimpleSchemaValidator.js';
import { writeFileSync } from 'fs';
import { join } from 'path';
describe('Theme Schema Validation Integration', () => {
let tempDir: string;
let configManager: ConfigManager;
beforeEach(async () => {
tempDir = await TestUtils.createTempDir('theme-schema-validation-test-');
// Create config-defaults.yaml with valid theme
const configDefaults = `
theme: "default" # Valid default theme (from ThemeContext)
development:
enabled: false
performance:
batchSize: 32
`;
writeFileSync(join(tempDir, 'config-defaults.yaml'), configDefaults);
// Setup ConfigManager with schema validation
const fileSystem = new NodeFileSystem();
const fileWriter = new NodeFileWriter();
const yamlParser = new YamlParser();
const schemaLoader = new SimpleThemeSchemaLoader();
const validator = new SimpleSchemaValidator(schemaLoader);
// Change to temp directory
const originalCwd = process.cwd();
process.chdir(tempDir);
configManager = new ConfigManager(
fileSystem,
fileWriter,
yamlParser,
validator,
schemaLoader,
'config-defaults.yaml',
'config.yaml'
);
await configManager.load();
// Change back
process.chdir(originalCwd);
});
afterEach(async () => {
await TestUtils.cleanupTempDir(tempDir);
});
describe('Valid theme values', () => {
it('should accept valid theme values through set', async () => {
const originalCwd = process.cwd();
try {
process.chdir(tempDir);
// Test a representative sample of valid theme values (from ThemeContext)
const validThemes = ['default', 'light', 'minimal', 'dracula', 'nord', 'ocean'];
for (const theme of validThemes) {
await configManager.set('theme', theme);
const value = configManager.get('theme');
expect(value).toBe(theme);
// Verify it was saved
const fs = await import('fs');
const savedConfig = fs.readFileSync(join(tempDir, 'config.yaml'), 'utf8');
expect(savedConfig).toContain(`theme: ${theme}`);
}
} finally {
process.chdir(originalCwd);
}
});
it('should validate theme before setting', async () => {
const originalCwd = process.cwd();
try {
process.chdir(tempDir);
// Validate should return true for valid themes
const result = await configManager.validate('theme', 'dracula');
expect(result.valid).toBe(true);
expect(result.errors).toBeUndefined();
} finally {
process.chdir(originalCwd);
}
});
});
describe('Invalid theme values', () => {
it('should reject invalid theme values', async () => {
const originalCwd = process.cwd();
try {
process.chdir(tempDir);
// Try to set invalid theme (including old themes that no longer exist)
await expect(configManager.set('theme', 'invalid')).rejects.toThrow('Validation failed');
// Theme should remain unchanged (default)
expect(configManager.get('theme')).toBe('default');
} finally {
process.chdir(originalCwd);
}
});
it('should provide clear error message for invalid theme', async () => {
const originalCwd = process.cwd();
try {
process.chdir(tempDir);
try {
await configManager.set('theme', 'blue');
expect.fail('Should have thrown validation error');
} catch (error) {
expect(error).toBeInstanceOf(Error);
expect((error as Error).message).toContain('Theme must be one of:');
}
} finally {
process.chdir(originalCwd);
}
});
it('should validate various invalid types', async () => {
const originalCwd = process.cwd();
try {
process.chdir(tempDir);
const invalidValues = [123, true, null, {}, []];
for (const value of invalidValues) {
const result = await configManager.validate('theme', value);
expect(result.valid).toBe(false);
expect(result.errors).toBeDefined();
expect(result.errors?.[0]?.message).toBeDefined();
}
} finally {
process.chdir(originalCwd);
}
});
});
describe('Schema information', () => {
it('should provide schema information', async () => {
const schema = await configManager.getSchema();
expect(schema).toBeDefined();
expect(schema.groups).toBeDefined();
expect(schema.groups.appearance).toBeDefined();
expect(schema.groups.appearance.items.theme).toBeDefined();
const themeItem = schema.groups.appearance.items.theme;
expect(themeItem.type).toBe('enum');
// Should have 16 themes from ThemeContext
expect(themeItem.validation.enum).toHaveLength(16);
expect(themeItem.validation.enum).toContain('default');
expect(themeItem.validation.enum).toContain('dracula');
expect(themeItem.validation.enum).toContain('nord');
});
it('should provide UI hints in schema', async () => {
const schema = await configManager.getSchema();
const themeItem = schema.groups.appearance.items.theme;
expect(themeItem.ui).toBeDefined();
expect(themeItem.ui.component).toBe('select');
expect(themeItem.ui.options).toHaveLength(16); // 16 themes from ThemeContext
expect(themeItem.ui.options[0]).toEqual({ value: 'default', label: 'Default' });
});
});
describe('File persistence with validation', () => {
it('should not save invalid values to file', async () => {
const originalCwd = process.cwd();
try {
process.chdir(tempDir);
// Create initial valid config
await configManager.set('theme', 'dracula');
// Try to set invalid value
try {
await configManager.set('theme', 'invalid');
} catch {
// Expected to fail
}
// Reload and verify theme is still dracula
await configManager.reload();
expect(configManager.get('theme')).toBe('dracula');
} finally {
process.chdir(originalCwd);
}
});
});
});