mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-09-30 12:39:42 +02:00
cmdPattern's empty-matchable unbounded arg group backtracked exponentially on wrapped heredoc/table lines — hovering one froze the tab for minutes. Non-empty tokens + bounded reps make it O(n); regression test extracts the shipped patterns and pins timing on the real killer shapes. worker-src 'self' blob: is now unconditional so terminal-ui's _safeYield tick worker (throttling escape) isn't CSP-blocked on non-gesture installs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
161 lines
5.1 KiB
TypeScript
161 lines
5.1 KiB
TypeScript
/**
|
|
* @fileoverview Tests for template generation functions
|
|
*
|
|
* Tests the CLAUDE.md template generation including
|
|
* default template and custom template loading.
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { existsSync, writeFileSync, unlinkSync, mkdirSync, rmSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import { tmpdir } from 'node:os';
|
|
import { generateClaudeMd } from '../src/templates/claude-md.js';
|
|
|
|
describe('generateClaudeMd', () => {
|
|
const testDir = join(tmpdir(), 'codeman-template-test-' + Date.now());
|
|
|
|
beforeEach(() => {
|
|
if (!existsSync(testDir)) {
|
|
mkdirSync(testDir, { recursive: true });
|
|
}
|
|
});
|
|
|
|
afterEach(() => {
|
|
try {
|
|
rmSync(testDir, { recursive: true, force: true });
|
|
} catch (e) {
|
|
// Ignore cleanup errors
|
|
}
|
|
});
|
|
|
|
describe('default template', () => {
|
|
it('should generate template with case name', () => {
|
|
const result = generateClaudeMd('my-project');
|
|
|
|
expect(result).toContain('**Project Name**: my-project');
|
|
});
|
|
|
|
it('should generate template with description', () => {
|
|
const result = generateClaudeMd('my-project', 'A test project description');
|
|
|
|
expect(result).toContain('**Description**: A test project description');
|
|
});
|
|
|
|
it('should use default description when not provided', () => {
|
|
const result = generateClaudeMd('my-project');
|
|
|
|
expect(result).toContain('**Description**: A new project');
|
|
});
|
|
|
|
it('should include current date', () => {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
const result = generateClaudeMd('my-project');
|
|
|
|
expect(result).toContain(`Generated by Codeman on ${today}`);
|
|
});
|
|
|
|
it('should include Codeman environment section', () => {
|
|
const result = generateClaudeMd('my-project');
|
|
|
|
expect(result).toContain('## Codeman Environment');
|
|
expect(result).toContain('CODEMAN_MUX=1');
|
|
});
|
|
|
|
it('should include workflow rules', () => {
|
|
const result = generateClaudeMd('my-project');
|
|
|
|
expect(result).toContain('## Workflow');
|
|
expect(result).toContain('conventional commits');
|
|
});
|
|
|
|
it('should stay under the 200-line CLAUDE.md guidance', () => {
|
|
const result = generateClaudeMd('my-project');
|
|
|
|
expect(result.split('\n').length).toBeLessThan(200);
|
|
});
|
|
|
|
it('should not include legacy bloat sections', () => {
|
|
const result = generateClaudeMd('my-project');
|
|
|
|
expect(result).not.toContain('## Session Log');
|
|
expect(result).not.toContain('TodoWrite');
|
|
expect(result).not.toContain('## Planning Mode');
|
|
expect(result).not.toContain('[TECHNOLOGIES_USED]');
|
|
// Ralph loop instructions live in the loop prompt (wizard) and plugin,
|
|
// not in every project's CLAUDE.md
|
|
expect(result).not.toContain('RALPH_STATUS');
|
|
expect(result).not.toContain('/ralph-loop:');
|
|
});
|
|
});
|
|
|
|
describe('custom template', () => {
|
|
it('should use custom template when provided and exists', () => {
|
|
const templatePath = join(testDir, 'custom-template.md');
|
|
writeFileSync(
|
|
templatePath,
|
|
`
|
|
# [PROJECT_NAME]
|
|
|
|
Description: [PROJECT_DESCRIPTION]
|
|
Date: [DATE]
|
|
|
|
Custom content here.
|
|
`
|
|
);
|
|
|
|
const result = generateClaudeMd('my-project', 'Test desc', templatePath);
|
|
|
|
expect(result).toContain('# my-project');
|
|
expect(result).toContain('Description: Test desc');
|
|
expect(result).toContain('Custom content here.');
|
|
});
|
|
|
|
it('should replace all placeholder occurrences', () => {
|
|
const templatePath = join(testDir, 'multi-placeholder.md');
|
|
writeFileSync(
|
|
templatePath,
|
|
`
|
|
[PROJECT_NAME] is a project.
|
|
The name is [PROJECT_NAME].
|
|
About [PROJECT_NAME]: [PROJECT_DESCRIPTION]
|
|
`
|
|
);
|
|
|
|
const result = generateClaudeMd('awesome-app', 'Cool stuff', templatePath);
|
|
|
|
expect(result).toContain('awesome-app is a project.');
|
|
expect(result).toContain('The name is awesome-app.');
|
|
expect(result).toContain('About awesome-app: Cool stuff');
|
|
});
|
|
|
|
it('should use default description in template when not provided', () => {
|
|
const templatePath = join(testDir, 'desc-template.md');
|
|
writeFileSync(templatePath, 'Description: [PROJECT_DESCRIPTION]');
|
|
|
|
const result = generateClaudeMd('my-project', '', templatePath);
|
|
|
|
expect(result).toContain('Description: A new project');
|
|
});
|
|
|
|
it('should fall back to default template if custom file does not exist', () => {
|
|
const result = generateClaudeMd('my-project', 'desc', '/nonexistent/template.md');
|
|
|
|
// Should get default template content
|
|
expect(result).toContain('## Codeman Environment');
|
|
expect(result).toContain('**Project Name**: my-project');
|
|
});
|
|
|
|
it('should fall back to default template if custom path is empty', () => {
|
|
const result = generateClaudeMd('my-project', 'desc', '');
|
|
|
|
expect(result).toContain('## Codeman Environment');
|
|
});
|
|
|
|
it('should fall back to default template if custom path is undefined', () => {
|
|
const result = generateClaudeMd('my-project', 'desc', undefined);
|
|
|
|
expect(result).toContain('## Codeman Environment');
|
|
});
|
|
});
|
|
});
|