Skip to main content
Glama

documcp

by tosin2013
generate-readme-template.test.ts16 kB
import { describe, it, expect, beforeEach, afterEach } from "@jest/globals"; import { promises as fs } from "fs"; import * as path from "path"; import * as tmp from "tmp"; import { generateReadmeTemplate, ReadmeTemplateGenerator, GenerateReadmeTemplateSchema, TemplateType, } from "../../src/tools/generate-readme-template"; describe("README Template Generator", () => { let tempDir: string; let generator: ReadmeTemplateGenerator; beforeEach(() => { tempDir = tmp.dirSync({ unsafeCleanup: true }).name; generator = new ReadmeTemplateGenerator(); }); afterEach(async () => { try { await fs.rmdir(tempDir, { recursive: true }); } catch { // Ignore cleanup errors } }); describe("Input Validation", () => { it("should validate required fields", () => { expect(() => GenerateReadmeTemplateSchema.parse({})).toThrow(); expect(() => GenerateReadmeTemplateSchema.parse({ projectName: "", description: "test", }), ).toThrow(); expect(() => GenerateReadmeTemplateSchema.parse({ projectName: "test", description: "", }), ).toThrow(); }); it("should accept valid input with defaults", () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "test-project", description: "A test project", templateType: "library", }); expect(input.license).toBe("MIT"); expect(input.includeScreenshots).toBe(false); expect(input.includeBadges).toBe(true); expect(input.includeContributing).toBe(true); }); it("should validate template types", () => { expect(() => GenerateReadmeTemplateSchema.parse({ projectName: "test", description: "test", templateType: "invalid-type", }), ).toThrow(); const validTypes: TemplateType[] = [ "library", "application", "cli-tool", "api", "documentation", ]; for (const type of validTypes) { expect(() => GenerateReadmeTemplateSchema.parse({ projectName: "test", description: "test", templateType: type, }), ).not.toThrow(); } }); }); describe("Template Generation", () => { it("should generate library template correctly", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "awesome-lib", description: "An awesome JavaScript library", templateType: "library", author: "john-doe", }); const result = await generateReadmeTemplate(input); expect(result.content).toContain("# awesome-lib"); expect(result.content).toContain("> An awesome JavaScript library"); expect(result.content).toContain("npm install awesome-lib"); expect(result.content).toContain( "const awesomeLib = require('awesome-lib')", ); expect(result.content).toContain("## TL;DR"); expect(result.content).toContain("## Quick Start"); expect(result.content).toContain("## API Documentation"); expect(result.content).toContain("MIT © john-doe"); expect(result.metadata.templateType).toBe("library"); expect(result.metadata.estimatedLength).toBe(150); }); it("should generate application template correctly", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "my-app", description: "A web application", templateType: "application", author: "jane-doe", includeScreenshots: true, }); const result = await generateReadmeTemplate(input); expect(result.content).toContain("# my-app"); expect(result.content).toContain("> A web application"); expect(result.content).toContain("## What This Does"); expect(result.content).toContain( "git clone https://github.com/jane-doe/my-app.git", ); expect(result.content).toContain("npm start"); expect(result.content).toContain("## Configuration"); expect(result.content).toContain("![my-app Screenshot]"); expect(result.metadata.templateType).toBe("application"); }); it("should generate CLI tool template correctly", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "my-cli", description: "A command line tool", templateType: "cli-tool", author: "dev-user", }); const result = await generateReadmeTemplate(input); expect(result.content).toContain("# my-cli"); expect(result.content).toContain("npm install -g my-cli"); expect(result.content).toContain("npx my-cli --help"); expect(result.content).toContain("## Usage"); expect(result.content).toContain("## Options"); expect(result.content).toContain("| Option | Description | Default |"); expect(result.metadata.templateType).toBe("cli-tool"); }); it("should handle camelCase conversion correctly", () => { const testCases = [ { input: "my-awesome-lib", expected: "myAwesomeLib" }, { input: "simple_package", expected: "simplePackage" }, { input: "Mixed-Case_Name", expected: "mixedCaseName" }, { input: "single", expected: "single" }, ]; for (const testCase of testCases) { const generator = new ReadmeTemplateGenerator(); const input = GenerateReadmeTemplateSchema.parse({ projectName: testCase.input, description: "test", templateType: "library", }); const result = generator.generateTemplate(input); expect(result).toContain( `const ${testCase.expected} = require('${testCase.input}')`, ); } }); }); describe("Badge Generation", () => { it("should include badges when enabled", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "badge-lib", description: "Library with badges", templateType: "library", author: "dev", includeBadges: true, }); const result = await generateReadmeTemplate(input); expect(result.content).toContain("[![npm version]"); expect(result.content).toContain("[![Build Status]"); expect(result.content).toContain("[![License: MIT]"); expect(result.content).toContain("dev/badge-lib"); }); it("should exclude badges when disabled", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "no-badge-lib", description: "Library without badges", templateType: "library", includeBadges: false, }); const result = await generateReadmeTemplate(input); expect(result.content).not.toContain("[!["); }); it("should customize badge URLs with author", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "app-with-badges", description: "App with custom badges", templateType: "application", author: "dev", includeBadges: true, }); const result = await generateReadmeTemplate(input); expect(result.content).toContain("dev/app-with-badges"); }); }); describe("Screenshot Handling", () => { it("should include screenshot placeholder when enabled", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "screenshot-app", description: "App with screenshots", templateType: "application", includeScreenshots: true, }); const result = await generateReadmeTemplate(input); expect(result.content).toContain( "![screenshot-app Screenshot](docs/screenshot.png)", ); expect(result.content).toContain("*Add a screenshot or demo GIF here*"); }); it("should exclude screenshots when disabled", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "no-screenshot-app", description: "App without screenshots", templateType: "application", includeScreenshots: false, }); const result = await generateReadmeTemplate(input); expect(result.content).not.toContain("![visual-app Screenshot]"); }); }); describe("Contributing Section", () => { it("should include contributing section when enabled", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "contrib-lib", description: "Library with contributing section", templateType: "library", includeContributing: true, }); const result = await generateReadmeTemplate(input); expect(result.content).toContain("## Contributing"); expect(result.content).toContain("CONTRIBUTING.md"); }); it("should exclude contributing section when disabled", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "no-contrib-lib", description: "Library without contributing section", templateType: "library", includeContributing: false, }); const result = await generateReadmeTemplate(input); expect(result.content).not.toContain("## Contributing"); }); }); describe("File Output", () => { it("should write to file when outputPath is specified", async () => { const outputPath = path.join(tempDir, "README.md"); const input = GenerateReadmeTemplateSchema.parse({ projectName: "output-lib", description: "Library with file output", templateType: "library", outputPath: outputPath, }); const result = await generateReadmeTemplate(input); await expect(fs.access(outputPath)).resolves.toBeUndefined(); const fileContent = await fs.readFile(outputPath, "utf-8"); expect(fileContent).toBe(result.content); expect(fileContent).toContain("# output-lib"); }); it("should not write file when outputPath is not specified", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "no-file-test", description: "Library without file output", templateType: "library", }); await generateReadmeTemplate(input); const possiblePath = path.join(tempDir, "README.md"); await expect(fs.access(possiblePath)).rejects.toThrow(); }); }); describe("Template Metadata", () => { it("should return correct metadata for each template type", () => { const templateTypes: TemplateType[] = [ "library", "application", "cli-tool", ]; for (const type of templateTypes) { const info = generator.getTemplateInfo(type); expect(info).toBeDefined(); expect(info!.type).toBe(type); expect(info!.estimatedLength).toBeGreaterThan(0); } }); it("should return null for invalid template type", () => { const info = generator.getTemplateInfo("invalid" as TemplateType); expect(info).toBeNull(); }); it("should count sections correctly", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "error-lib", description: "Library that causes error", templateType: "library", }); const result = await generateReadmeTemplate(input); const sectionCount = (result.content.match(/^##\s/gm) || []).length; expect(result.metadata.sectionsIncluded).toBeGreaterThanOrEqual( sectionCount, ); expect(result.metadata.sectionsIncluded).toBeGreaterThan(3); }); }); describe("Available Templates", () => { it("should return list of available template types", () => { const availableTypes = generator.getAvailableTemplates(); expect(availableTypes).toContain("library"); expect(availableTypes).toContain("application"); expect(availableTypes).toContain("cli-tool"); expect(availableTypes.length).toBeGreaterThan(0); }); }); describe("Error Handling", () => { it("should throw error for unsupported template type", async () => { const generator = new ReadmeTemplateGenerator(); expect(() => generator.generateTemplate({ projectName: "test", description: "test", templateType: "unsupported" as TemplateType, license: "MIT", includeScreenshots: false, includeBadges: true, includeContributing: true, }), ).toThrow('Template type "unsupported" not supported'); }); it("should handle file write errors gracefully", async () => { const invalidPath = "/invalid/nonexistent/path/README.md"; const input = GenerateReadmeTemplateSchema.parse({ projectName: "error-test", description: "test error handling", templateType: "library", outputPath: invalidPath, }); await expect(generateReadmeTemplate(input)).rejects.toThrow(); }); }); describe("Variable Replacement", () => { it("should replace all template variables correctly", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "license-lib", description: "Library with custom license", templateType: "library", author: "dev", license: "Apache-2.0", }); const result = await generateReadmeTemplate(input); expect(result.content).not.toContain("{{projectName}}"); expect(result.content).not.toContain("{{description}}"); expect(result.content).not.toContain("{{author}}"); expect(result.content).not.toContain("{{license}}"); expect(result.content).toContain("license-lib"); expect(result.content).toContain("Library with custom license"); expect(result.content).toContain("dev"); expect(result.content).toContain("Apache-2.0"); }); it("should use default values for missing optional fields", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "time-lib", description: "Library with timing", templateType: "library", }); const result = await generateReadmeTemplate(input); expect(result.content).toContain("your-username"); expect(result.content).toContain("MIT"); }); }); describe("Template Structure Validation", () => { it("should generate valid markdown structure", async () => { const input = GenerateReadmeTemplateSchema.parse({ projectName: "structure-test", description: "test structure", templateType: "library", }); const result = await generateReadmeTemplate(input); // Check for proper heading hierarchy const lines = result.content.split("\n"); const headings = lines.filter((line) => line.startsWith("#")); expect(headings.length).toBeGreaterThan(0); expect(headings[0]).toMatch(/^#\s+/); // Main title // Check for code blocks expect(result.content).toMatch(/```[\s\S]*?```/); // Check for proper spacing expect(result.content).not.toMatch(/#{1,6}\s*\n\s*#{1,6}/); }); it("should maintain consistent formatting across templates", async () => { const templateTypes: TemplateType[] = [ "library", "application", "cli-tool", ]; for (const type of templateTypes) { const input = GenerateReadmeTemplateSchema.parse({ projectName: "format-test", description: "test format", templateType: type, }); const result = await generateReadmeTemplate(input); // All templates should have main title expect(result.content).toMatch(/^#\s+format-test/m); // All templates should have license section expect(result.content).toContain("## License"); // All templates should end with license info expect(result.content.trim()).toMatch(/MIT © your-username$/); } }); }); });

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/tosin2013/documcp'

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