Skip to main content
Glama
claude-setup.test.ts11.2 kB
/** * Tests for WP Navigator Claude Setup Command * * Tests CLAUDE.md generation, .mcp.json creation, and smoke test. * * @package WP_Navigator_MCP * @since 2.3.0 */ import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import * as fs from 'fs'; import * as path from 'path'; import * as os from 'os'; import { performClaudeSetup, handleClaudeSetup, type ClaudeSetupResult } from './claude-setup.js'; describe('Claude Setup Command', () => { let tempDir: string; beforeEach(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'wpnav-claude-setup-test-')); }); afterEach(() => { // Clean up temp directory fs.rmSync(tempDir, { recursive: true, force: true }); }); describe('performClaudeSetup', () => { it('creates CLAUDE.md in the project directory', () => { // Create wpnav.config.json (required) fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); const result = performClaudeSetup(tempDir); expect(result.claudeMdCreated).toBe(true); expect(fs.existsSync(path.join(tempDir, 'CLAUDE.md'))).toBe(true); }); it('creates .mcp.json in the project directory', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); const result = performClaudeSetup(tempDir); expect(result.mcpJsonCreated).toBe(true); expect(fs.existsSync(path.join(tempDir, '.mcp.json'))).toBe(true); }); it('generates valid .mcp.json with correct structure', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); performClaudeSetup(tempDir); const mcpJson = JSON.parse(fs.readFileSync(path.join(tempDir, '.mcp.json'), 'utf8')); expect(mcpJson.mcpServers).toBeDefined(); expect(mcpJson.mcpServers.wpnav).toBeDefined(); expect(mcpJson.mcpServers.wpnav.command).toBe('npx'); expect(mcpJson.mcpServers.wpnav.args).toContain('@littlebearapps/wp-navigator-mcp'); }); it('overwrites existing CLAUDE.md', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); fs.writeFileSync(path.join(tempDir, 'CLAUDE.md'), '# Old Content'); const result = performClaudeSetup(tempDir); expect(result.claudeMdCreated).toBe(true); expect(result.claudeMdOverwritten).toBe(true); const content = fs.readFileSync(path.join(tempDir, 'CLAUDE.md'), 'utf8'); expect(content).toContain('# CLAUDE.md - WP Navigator Project'); expect(content).not.toContain('# Old Content'); }); it('overwrites existing .mcp.json', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); fs.writeFileSync(path.join(tempDir, '.mcp.json'), '{"old": true}'); const result = performClaudeSetup(tempDir); expect(result.mcpJsonCreated).toBe(true); expect(result.mcpJsonOverwritten).toBe(true); const content = JSON.parse(fs.readFileSync(path.join(tempDir, '.mcp.json'), 'utf8')); expect(content.old).toBeUndefined(); expect(content.mcpServers).toBeDefined(); }); it('reads site_url from wpnav.config.json', () => { const config = { environments: { default: { site_url: 'https://example.com', }, }, }; fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), JSON.stringify(config)); performClaudeSetup(tempDir); const content = fs.readFileSync(path.join(tempDir, 'CLAUDE.md'), 'utf8'); expect(content).toContain('**URL**: https://example.com'); }); it('handles wpnav.config.json without environments', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); // Should not throw const result = performClaudeSetup(tempDir); expect(result.claudeMdCreated).toBe(true); expect(result.mcpJsonCreated).toBe(true); }); it('generates CLAUDE.md with Claude-specific content', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); performClaudeSetup(tempDir); const content = fs.readFileSync(path.join(tempDir, 'CLAUDE.md'), 'utf8'); expect(content).toContain('# CLAUDE.md - WP Navigator Project'); expect(content).toContain('## Overview'); expect(content).toContain('.mcp.json'); }); it('sets smokeTestPassed to null initially', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); const result = performClaudeSetup(tempDir); expect(result.smokeTestPassed).toBeNull(); }); }); describe('handleClaudeSetup', () => { let consoleLogSpy: ReturnType<typeof vi.spyOn>; let consoleErrorSpy: ReturnType<typeof vi.spyOn>; let processCwdSpy: ReturnType<typeof vi.spyOn>; beforeEach(() => { consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => {}); consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {}); processCwdSpy = vi.spyOn(process, 'cwd').mockReturnValue(tempDir); }); afterEach(() => { consoleLogSpy.mockRestore(); consoleErrorSpy.mockRestore(); processCwdSpy.mockRestore(); }); it('fails when wpnav.config.json does not exist', async () => { const exitCode = await handleClaudeSetup({}); expect(exitCode).toBe(1); }); it('fails with JSON error when config missing', async () => { const exitCode = await handleClaudeSetup({ json: true }); expect(exitCode).toBe(1); expect(consoleLogSpy).toHaveBeenCalled(); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.success).toBe(false); expect(output.error.code).toBe('CONFIG_NOT_FOUND'); }); it('succeeds when wpnav.config.json exists', async () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); const exitCode = await handleClaudeSetup({}); expect(exitCode).toBe(0); expect(fs.existsSync(path.join(tempDir, 'CLAUDE.md'))).toBe(true); expect(fs.existsSync(path.join(tempDir, '.mcp.json'))).toBe(true); }); it('returns JSON output with success data', async () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); const exitCode = await handleClaudeSetup({ json: true }); expect(exitCode).toBe(0); expect(consoleLogSpy).toHaveBeenCalled(); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.success).toBe(true); expect(output.command).toBe('claude-setup'); expect(output.data.claude_md_created).toBe(true); expect(output.data.mcp_json_created).toBe(true); }); it('returns claude_md_overwritten true when CLAUDE.md exists', async () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); fs.writeFileSync(path.join(tempDir, 'CLAUDE.md'), '# Old'); const exitCode = await handleClaudeSetup({ json: true }); expect(exitCode).toBe(0); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.claude_md_overwritten).toBe(true); }); it('returns mcp_json_overwritten true when .mcp.json exists', async () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); fs.writeFileSync(path.join(tempDir, '.mcp.json'), '{}'); const exitCode = await handleClaudeSetup({ json: true }); expect(exitCode).toBe(0); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect(output.data.mcp_json_overwritten).toBe(true); }); it('is idempotent (safe to run twice)', async () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); // First run const exitCode1 = await handleClaudeSetup({ json: true }); expect(exitCode1).toBe(0); // Second run const exitCode2 = await handleClaudeSetup({ json: true }); expect(exitCode2).toBe(0); // Files should still exist and be valid expect(fs.existsSync(path.join(tempDir, 'CLAUDE.md'))).toBe(true); expect(fs.existsSync(path.join(tempDir, '.mcp.json'))).toBe(true); }); it('includes smoke_test_passed in JSON output', async () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); const exitCode = await handleClaudeSetup({ json: true }); expect(exitCode).toBe(0); const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string); expect('smoke_test_passed' in output.data).toBe(true); }); }); describe('CLAUDE.md content', () => { it('includes required sections for Claude Code users', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); performClaudeSetup(tempDir); const content = fs.readFileSync(path.join(tempDir, 'CLAUDE.md'), 'utf8'); // Must have these 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('## Project Files'); expect(content).toContain('## Troubleshooting'); expect(content).toContain('## Resources'); }); it('references .mcp.json in project files table', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); performClaudeSetup(tempDir); const content = fs.readFileSync(path.join(tempDir, 'CLAUDE.md'), 'utf8'); expect(content).toContain('.mcp.json'); }); it('includes all 13 tool categories', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); performClaudeSetup(tempDir); const content = fs.readFileSync(path.join(tempDir, 'CLAUDE.md'), 'utf8'); 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('.mcp.json content', () => { it('has correct MCP server configuration', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); performClaudeSetup(tempDir); const mcpJson = JSON.parse(fs.readFileSync(path.join(tempDir, '.mcp.json'), 'utf8')); expect(mcpJson.mcpServers.wpnav.command).toBe('npx'); expect(mcpJson.mcpServers.wpnav.args).toEqual([ '-y', '@littlebearapps/wp-navigator-mcp', './wpnav.config.json', ]); }); it('sets writes disabled by default', () => { fs.writeFileSync(path.join(tempDir, 'wpnav.config.json'), '{}'); performClaudeSetup(tempDir); const mcpJson = JSON.parse(fs.readFileSync(path.join(tempDir, '.mcp.json'), 'utf8')); expect(mcpJson.mcpServers.wpnav.env.WPNAV_ENABLE_WRITES).toBe('0'); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/littlebearapps/wp-navigator-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server