/**
* Unit tests for template generators
*
* Tests renderTemplate(), generateClaudeMd(), generateMcpJson()
*/
import { describe, it, expect } from 'vitest';
import {
renderTemplate,
generateClaudeMd,
generateAgentsMd,
generateGeminiMd,
generateMcpJson,
getDefaultClaudeMdContext,
type ClaudeMdContext,
} from './generators.js';
describe('generators', () => {
describe('renderTemplate', () => {
it('replaces simple variables', () => {
const result = renderTemplate('Hello {{name}}!', { name: 'World' });
expect(result).toBe('Hello World!');
});
it('replaces multiple variables', () => {
const result = renderTemplate('{{greeting}} {{name}}!', {
greeting: 'Hello',
name: 'World',
});
expect(result).toBe('Hello World!');
});
it('handles missing variables as empty string', () => {
const result = renderTemplate('Hello {{name}}!', {});
expect(result).toBe('Hello !');
});
it('handles null values as empty string', () => {
const result = renderTemplate('Hello {{name}}!', { name: null });
expect(result).toBe('Hello !');
});
it('handles undefined values as empty string', () => {
const result = renderTemplate('Hello {{name}}!', { name: undefined });
expect(result).toBe('Hello !');
});
it('converts non-string values to string', () => {
const result = renderTemplate('Count: {{count}}', { count: 42 });
expect(result).toBe('Count: 42');
});
it('handles if blocks - truthy value', () => {
const template = '{{#if show}}Visible{{/if}}';
expect(renderTemplate(template, { show: true })).toBe('Visible');
});
it('handles if blocks - falsy value (false)', () => {
const template = '{{#if show}}Visible{{/if}}';
expect(renderTemplate(template, { show: false })).toBe('');
});
it('handles if blocks - falsy value (empty string)', () => {
const template = '{{#if show}}Visible{{/if}}';
expect(renderTemplate(template, { show: '' })).toBe('');
});
it('handles if blocks - falsy value (undefined)', () => {
const template = '{{#if show}}Visible{{/if}}';
expect(renderTemplate(template, {})).toBe('');
});
it('handles if blocks with variables inside', () => {
const template = '{{#if site_name}}**Site**: {{site_name}}\n{{/if}}';
const result = renderTemplate(template, { site_name: 'My Site' });
expect(result).toBe('**Site**: My Site\n');
});
it('handles multiple if blocks', () => {
const template = '{{#if a}}A{{/if}}{{#if b}}B{{/if}}';
expect(renderTemplate(template, { a: true, b: false })).toBe('A');
expect(renderTemplate(template, { a: false, b: true })).toBe('B');
expect(renderTemplate(template, { a: true, b: true })).toBe('AB');
});
it('handles multiline content in if blocks', () => {
const template = `{{#if show}}Line 1
Line 2
Line 3{{/if}}`;
const result = renderTemplate(template, { show: true });
expect(result).toBe('Line 1\nLine 2\nLine 3');
});
});
describe('generateClaudeMd', () => {
it('generates markdown with all fields', () => {
const context: ClaudeMdContext = {
site_name: 'Test Site',
site_url: 'https://test.com',
environment: 'production',
generated_date: '2024-01-15',
mcp_version: '2.1.0',
};
const content = generateClaudeMd(context);
expect(content).toContain('# CLAUDE.md - WP Navigator Project');
expect(content).toContain('**Site**: Test Site');
expect(content).toContain('**URL**: https://test.com');
expect(content).toContain('**Environment**: production');
expect(content).toContain('**Generated**: 2024-01-15');
expect(content).toContain('*Generated by WP Navigator v2.1.0*');
});
it('omits site_name when not provided', () => {
const context: ClaudeMdContext = {
environment: 'local',
generated_date: '2024-01-15',
mcp_version: '2.1.0',
};
const content = generateClaudeMd(context);
expect(content).not.toContain('**Site**:');
expect(content).toContain('**Environment**: local');
});
it('omits site_url when not provided', () => {
const context: ClaudeMdContext = {
site_name: 'Test',
environment: 'local',
generated_date: '2024-01-15',
mcp_version: '2.1.0',
};
const content = generateClaudeMd(context);
expect(content).not.toContain('**URL**:');
});
it('includes all required sections', () => {
const context = getDefaultClaudeMdContext();
const content = generateClaudeMd(context);
// Check for required sections
expect(content).toContain('## Overview');
expect(content).toContain('## Safety Defaults');
expect(content).toContain('## Available CLI Commands');
expect(content).toContain('## MCP Tool Categories');
expect(content).toContain('## Common Workflows');
expect(content).toContain('## Troubleshooting');
});
it('includes all 13 tool categories', () => {
const context = getDefaultClaudeMdContext();
const content = generateClaudeMd(context);
const expectedCategories = [
'Core',
'Pages',
'Posts',
'Media',
'Comments',
'Categories',
'Tags',
'Taxonomies',
'Users',
'Plugins',
'Themes',
'Gutenberg',
'Testing',
];
for (const category of expectedCategories) {
expect(content).toContain(`| ${category} |`);
}
});
});
describe('generateAgentsMd', () => {
it('generates markdown with all fields', () => {
const context: ClaudeMdContext = {
site_name: 'Test Site',
site_url: 'https://test.com',
environment: 'production',
generated_date: '2024-01-15',
mcp_version: '2.3.0',
};
const content = generateAgentsMd(context);
expect(content).toContain('# AGENTS.md - WP Navigator Project');
expect(content).toContain('**Site**: Test Site');
expect(content).toContain('**URL**: https://test.com');
expect(content).toContain('**Environment**: production');
expect(content).toContain('**Generated**: 2024-01-15');
expect(content).toContain('*Generated by WP Navigator v2.3.0*');
});
it('omits site_name when not provided', () => {
const context: ClaudeMdContext = {
environment: 'local',
generated_date: '2024-01-15',
mcp_version: '2.3.0',
};
const content = generateAgentsMd(context);
expect(content).not.toContain('**Site**:');
expect(content).toContain('**Environment**: local');
});
it('omits site_url when not provided', () => {
const context: ClaudeMdContext = {
site_name: 'Test',
environment: 'local',
generated_date: '2024-01-15',
mcp_version: '2.3.0',
};
const content = generateAgentsMd(context);
expect(content).not.toContain('**URL**:');
});
it('includes all required sections', () => {
const context = getDefaultClaudeMdContext();
const content = generateAgentsMd(context);
// Check for required sections
expect(content).toContain('## Overview');
expect(content).toContain('## Safety Defaults');
expect(content).toContain('## Available CLI Commands');
expect(content).toContain('## MCP Tool Categories');
expect(content).toContain('## Common Workflows');
expect(content).toContain('## Troubleshooting');
});
it('includes Codex-specific sections', () => {
const context = getDefaultClaudeMdContext();
const content = generateAgentsMd(context);
// Check for Codex-specific sections
expect(content).toContain('## Codex Integration');
expect(content).toContain('### MCP Server Configuration');
expect(content).toContain('config.toml');
expect(content).toContain('[mcp_servers.wpnav]');
});
it('includes Codex tool invocation examples', () => {
const context = getDefaultClaudeMdContext();
const content = generateAgentsMd(context);
expect(content).toContain('### Using WP Navigator Tools');
expect(content).toContain('wpnav_list_pages');
expect(content).toContain('wpnav_get_site_overview');
});
it('includes all 13 tool categories', () => {
const context = getDefaultClaudeMdContext();
const content = generateAgentsMd(context);
const expectedCategories = [
'Core',
'Pages',
'Posts',
'Media',
'Comments',
'Categories',
'Tags',
'Taxonomies',
'Users',
'Plugins',
'Themes',
'Gutenberg',
'Testing',
];
for (const category of expectedCategories) {
expect(content).toContain(`| ${category} |`);
}
});
it('references .codex/ directory in project files', () => {
const context = getDefaultClaudeMdContext();
const content = generateAgentsMd(context);
expect(content).toContain('.codex/');
expect(content).toContain('AGENTS.md');
});
});
describe('generateGeminiMd', () => {
it('generates markdown with all fields', () => {
const context: ClaudeMdContext = {
site_name: 'Test Site',
site_url: 'https://test.com',
environment: 'production',
generated_date: '2024-01-15',
mcp_version: '2.3.0',
};
const content = generateGeminiMd(context);
expect(content).toContain('# GEMINI.md - WP Navigator Project');
expect(content).toContain('**Site**: Test Site');
expect(content).toContain('**URL**: https://test.com');
expect(content).toContain('**Environment**: production');
expect(content).toContain('**Generated**: 2024-01-15');
expect(content).toContain('*Generated by WP Navigator v2.3.0*');
});
it('omits site_name when not provided', () => {
const context: ClaudeMdContext = {
environment: 'local',
generated_date: '2024-01-15',
mcp_version: '2.3.0',
};
const content = generateGeminiMd(context);
expect(content).not.toContain('**Site**:');
expect(content).toContain('**Environment**: local');
});
it('omits site_url when not provided', () => {
const context: ClaudeMdContext = {
site_name: 'Test',
environment: 'local',
generated_date: '2024-01-15',
mcp_version: '2.3.0',
};
const content = generateGeminiMd(context);
expect(content).not.toContain('**URL**:');
});
it('includes all required sections', () => {
const context = getDefaultClaudeMdContext();
const content = generateGeminiMd(context);
// Check for required sections
expect(content).toContain('## Overview');
expect(content).toContain('## Quick Start');
expect(content).toContain('## Safety Defaults');
expect(content).toContain('## Available CLI Commands');
expect(content).toContain('## MCP Tool Categories');
expect(content).toContain('## Common Workflows');
expect(content).toContain('## Troubleshooting');
});
it('includes Gemini-specific sections', () => {
const context = getDefaultClaudeMdContext();
const content = generateGeminiMd(context);
// Check for Gemini-specific sections
expect(content).toContain('## Gemini Integration');
expect(content).toContain('### MCP Server Configuration');
expect(content).toContain('settings.json');
expect(content).toContain('"mcpServers"');
});
it('includes Gemini tool invocation examples', () => {
const context = getDefaultClaudeMdContext();
const content = generateGeminiMd(context);
expect(content).toContain('### Using WP Navigator Tools');
expect(content).toContain('wpnav_list_pages');
expect(content).toContain('wpnav_get_site_overview');
});
it('includes all 13 tool categories', () => {
const context = getDefaultClaudeMdContext();
const content = generateGeminiMd(context);
const expectedCategories = [
'Core',
'Pages',
'Posts',
'Media',
'Comments',
'Categories',
'Tags',
'Taxonomies',
'Users',
'Plugins',
'Themes',
'Gutenberg',
'Testing',
];
for (const category of expectedCategories) {
expect(content).toContain(`| ${category} |`);
}
});
it('references .gemini/ directory in project files', () => {
const context = getDefaultClaudeMdContext();
const content = generateGeminiMd(context);
expect(content).toContain('.gemini/');
expect(content).toContain('GEMINI.md');
});
});
describe('getDefaultClaudeMdContext', () => {
it('returns context with default values', () => {
const context = getDefaultClaudeMdContext();
expect(context.environment).toBe('local');
expect(context.generated_date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
expect(context.mcp_version).toBeDefined();
});
it('allows overriding default values', () => {
const context = getDefaultClaudeMdContext({
site_name: 'Custom Site',
environment: 'production',
});
expect(context.site_name).toBe('Custom Site');
expect(context.environment).toBe('production');
expect(context.generated_date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
});
});
describe('generateMcpJson', () => {
it('generates valid JSON with defaults', () => {
const content = generateMcpJson();
const parsed = JSON.parse(content);
expect(parsed.mcpServers).toBeDefined();
expect(parsed.mcpServers.wpnav).toBeDefined();
expect(parsed.mcpServers.wpnav.command).toBe('npx');
expect(parsed.mcpServers.wpnav.args).toContain('-y');
expect(parsed.mcpServers.wpnav.args).toContain('@littlebearapps/wp-navigator-mcp');
expect(parsed.mcpServers.wpnav.args).toContain('./wpnav.config.json');
});
it('respects enableWrites option', () => {
const withWrites = JSON.parse(generateMcpJson({ enableWrites: true }));
const withoutWrites = JSON.parse(generateMcpJson({ enableWrites: false }));
expect(withWrites.mcpServers.wpnav.env.WPNAV_ENABLE_WRITES).toBe('1');
expect(withoutWrites.mcpServers.wpnav.env.WPNAV_ENABLE_WRITES).toBe('0');
});
it('defaults enableWrites to false', () => {
const parsed = JSON.parse(generateMcpJson());
expect(parsed.mcpServers.wpnav.env.WPNAV_ENABLE_WRITES).toBe('0');
});
it('respects custom configPath', () => {
const content = generateMcpJson({ configPath: './custom-config.json' });
const parsed = JSON.parse(content);
expect(parsed.mcpServers.wpnav.args).toContain('./custom-config.json');
expect(parsed.mcpServers.wpnav.args).not.toContain('./wpnav.config.json');
});
it('generates properly formatted JSON', () => {
const content = generateMcpJson();
// Check it's properly indented (2 spaces)
expect(content).toContain(' "mcpServers"');
expect(content).toContain(' "wpnav"');
});
});
});