Skip to main content
Glama

documcp

by tosin2013
validate-readme-checklist.test.ts24.9 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 { validateReadmeChecklist, ReadmeChecklistValidator, ValidateReadmeChecklistSchema, } from "../../src/tools/validate-readme-checklist"; describe("README Checklist Validator", () => { let tempDir: string; let validator: ReadmeChecklistValidator; beforeEach(() => { tempDir = tmp.dirSync({ unsafeCleanup: true }).name; validator = new ReadmeChecklistValidator(); }); afterEach(async () => { try { await fs.rmdir(tempDir, { recursive: true }); } catch { // Ignore cleanup errors } }); async function createTestReadme( content: string, filename = "README.md", ): Promise<string> { const readmePath = path.join(tempDir, filename); await fs.writeFile(readmePath, content, "utf-8"); return readmePath; } async function createProjectFile( filename: string, content = "", ): Promise<void> { await fs.writeFile(path.join(tempDir, filename), content, "utf-8"); } describe("Input Validation", () => { it("should validate required fields", () => { expect(() => ValidateReadmeChecklistSchema.parse({})).toThrow(); expect(() => ValidateReadmeChecklistSchema.parse({ readmePath: "", }), ).toThrow(); }); it("should accept valid input with defaults", () => { const input = ValidateReadmeChecklistSchema.parse({ readmePath: "/path/to/README.md", }); expect(input.strict).toBe(false); expect(input.outputFormat).toBe("console"); }); it("should validate output format options", () => { const validFormats = ["json", "markdown", "console"]; for (const format of validFormats) { expect(() => ValidateReadmeChecklistSchema.parse({ readmePath: "/test/README.md", outputFormat: format, }), ).not.toThrow(); } expect(() => ValidateReadmeChecklistSchema.parse({ readmePath: "/test/README.md", outputFormat: "invalid", }), ).toThrow(); }); }); describe("Essential Sections Validation", () => { it("should detect project title", async () => { const goodReadme = await createTestReadme( "# My Project\n\nDescription here", "good-README.md", ); const badReadme = await createTestReadme( "## Not a main title\n\nNo main heading", "bad-README.md", ); const goodInput = ValidateReadmeChecklistSchema.parse({ readmePath: goodReadme, }); const badInput = ValidateReadmeChecklistSchema.parse({ readmePath: badReadme, }); const result = await validateReadmeChecklist(goodInput); const result2 = await validateReadmeChecklist(badInput); const titleCheck = result.categories["Essential Sections"].results.find( (r) => r.item.id === "title", ); const badTitleCheck = result2.categories[ "Essential Sections" ].results.find((r) => r.item.id === "title"); expect(titleCheck?.passed).toBe(true); expect(badTitleCheck?.passed).toBe(false); }); it("should detect project description", async () => { const withSubtitle = await createTestReadme( "# Project\n\n> A great project description", "subtitle-README.md", ); const withParagraph = await createTestReadme( "# Project\n\nThis is a description paragraph", "paragraph-README.md", ); const withoutDesc = await createTestReadme( "# Project\n\n## Installation", "no-desc-README.md", ); const subtitleResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: withSubtitle }), ); const paragraphResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: withParagraph }), ); const noDescResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: withoutDesc }), ); const getDescCheck = (result: any) => result.categories["Essential Sections"].results.find( (r: any) => r.item.id === "description", ); expect(getDescCheck(subtitleResult)?.passed).toBe(true); expect(getDescCheck(paragraphResult)?.passed).toBe(true); expect(getDescCheck(noDescResult)?.passed).toBe(false); }); it("should detect TL;DR section", async () => { const withTldr = await createTestReadme( "# Project\n\n## TL;DR\n\nQuick summary", "tldr-README.md", ); const withQuickStart = await createTestReadme( "# Project\n\n## Quick Start\n\nQuick summary", "quickstart-README.md", ); const withoutTldr = await createTestReadme( "# Project\n\n## Installation", "no-tldr-README.md", ); const tldrInput = ValidateReadmeChecklistSchema.parse({ readmePath: withTldr, }); const quickStartInput = ValidateReadmeChecklistSchema.parse({ readmePath: withQuickStart, }); const noTldrInput = ValidateReadmeChecklistSchema.parse({ readmePath: withoutTldr, }); const result = await validateReadmeChecklist(tldrInput); const result2 = await validateReadmeChecklist(quickStartInput); const result3 = await validateReadmeChecklist(noTldrInput); const getTldrCheck = (result: any) => result.categories["Essential Sections"].results.find( (r: any) => r.item.id === "tldr", ); expect(getTldrCheck(result)?.passed).toBe(true); expect(getTldrCheck(result2)?.passed).toBe(true); expect(getTldrCheck(result3)?.passed).toBe(false); }); it("should detect installation instructions with code blocks", async () => { const goodInstall = await createTestReadme( ` # Project ## Installation \`\`\`bash npm install project \`\`\` `, "good-install-README.md", ); const noCodeBlocks = await createTestReadme( ` # Project ## Installation Just install it somehow `, "no-code-README.md", ); const noInstallSection = await createTestReadme( "# Project\n\nSome content", "no-install-README.md", ); const goodResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: goodInstall }), ); const noCodeResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: noCodeBlocks }), ); const noSectionResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: noInstallSection }), ); const getInstallCheck = (result: any) => result.categories["Essential Sections"].results.find( (r: any) => r.item.id === "installation", ); expect(getInstallCheck(goodResult)?.passed).toBe(true); expect(getInstallCheck(noCodeResult)?.passed).toBe(true); // This should pass because it has Installation section expect(getInstallCheck(noSectionResult)?.passed).toBe(false); }); it("should detect usage examples", async () => { const goodUsage = await createTestReadme( ` # Project ## Usage \`\`\`javascript const lib = require('lib'); lib.doSomething(); \`\`\` `, "good-usage-README.md", ); const noUsage = await createTestReadme( "# Project\n\nNo usage section", "no-usage-README.md", ); const goodResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: goodUsage }), ); const noUsageResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: noUsage }), ); const getUsageCheck = (result: any) => result.categories["Essential Sections"].results.find( (r: any) => r.item.id === "usage", ); expect(getUsageCheck(goodResult)?.passed).toBe(true); expect(getUsageCheck(noUsageResult)?.passed).toBe(false); }); it("should detect license information", async () => { const readmeWithLicense = await createTestReadme( "# Project\n\n## License\n\nMIT", "license-README.md", ); const readmeWithoutLicense = await createTestReadme( "# Project\n\nNo license info", "no-license-README.md", ); // Test without LICENSE file first const withLicenseResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readmeWithLicense, projectPath: tempDir, }), ); const withoutLicenseResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readmeWithoutLicense, projectPath: tempDir, }), ); // Test with LICENSE file await createProjectFile("LICENSE", "MIT License..."); const readmeWithLicenseFile = await createTestReadme( "# Project\n\nSome content", "license-file-README.md", ); const withLicenseFileResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readmeWithLicenseFile, projectPath: tempDir, }), ); const getLicenseCheck = (result: any) => result.categories["Essential Sections"].results.find( (r: any) => r.item.id === "license", ); expect(getLicenseCheck(withLicenseResult)?.passed).toBe(true); expect(getLicenseCheck(withoutLicenseResult)?.passed).toBe(false); expect(getLicenseCheck(withLicenseFileResult)?.passed).toBe(true); }); }); describe("Community Health Validation", () => { it("should detect contributing guidelines", async () => { const readmeWithContributing = await createTestReadme( "# Project\n\n## Contributing\n\nSee CONTRIBUTING.md", ); await createProjectFile("CONTRIBUTING.md", "Contributing guidelines..."); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readmeWithContributing, projectPath: tempDir, }), ); const contributingCheck = result.categories[ "Community Health" ].results.find((r) => r.item.id === "contributing"); expect(contributingCheck?.passed).toBe(true); }); it("should detect code of conduct", async () => { await createProjectFile("CODE_OF_CONDUCT.md", "Code of conduct..."); const readme = await createTestReadme("# Project\n\nSome content"); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readme, projectPath: tempDir, }), ); const cocCheck = result.categories["Community Health"].results.find( (r) => r.item.id === "code-of-conduct", ); expect(cocCheck?.passed).toBe(true); }); it("should detect security policy", async () => { await createProjectFile("SECURITY.md", "Security policy..."); const readme = await createTestReadme("# Project\n\nSome content"); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readme, projectPath: tempDir, }), ); const securityCheck = result.categories["Community Health"].results.find( (r) => r.item.id === "security", ); expect(securityCheck?.passed).toBe(true); }); }); describe("Visual Elements Validation", () => { it("should detect status badges", async () => { const withBadges = await createTestReadme( ` # Project [![Build Status](https://travis-ci.org/user/repo.svg?branch=main)](https://travis-ci.org/user/repo) [![npm version](https://badge.fury.io/js/package.svg)](https://badge.fury.io/js/package) `, "with-badges-README.md", ); const withoutBadges = await createTestReadme( "# Project\n\nNo badges here", "no-badges-README.md", ); const withBadgesResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: withBadges }), ); const withoutBadgesResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: withoutBadges }), ); const getBadgeCheck = (result: any) => result.categories["Visual Elements"].results.find( (r: any) => r.item.id === "badges", ); expect(getBadgeCheck(withBadgesResult)?.passed).toBe(true); expect(getBadgeCheck(withoutBadgesResult)?.passed).toBe(false); }); it("should detect screenshots and images", async () => { const withScreenshots = await createTestReadme( ` # Project ![Screenshot](screenshot.png) ![Demo](demo.gif) `, "with-screenshots-README.md", ); const withoutScreenshots = await createTestReadme( "# Project\n\nNo images", "no-screenshots-README.md", ); const withScreenshotsResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: withScreenshots }), ); const withoutScreenshotsResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: withoutScreenshots }), ); const getScreenshotCheck = (result: any) => result.categories["Visual Elements"].results.find( (r: any) => r.item.id === "screenshots", ); expect(getScreenshotCheck(withScreenshotsResult)?.passed).toBe(true); expect(getScreenshotCheck(withoutScreenshotsResult)?.passed).toBe(false); }); it("should validate markdown formatting", async () => { const goodFormatting = await createTestReadme( ` # Main Title ## Section 1 ### Subsection ## Section 2 `, "good-formatting-README.md", ); const poorFormatting = await createTestReadme( ` # Title #Another Title ##Poor Spacing `, "poor-formatting-README.md", ); const goodResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: goodFormatting }), ); const poorResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: poorFormatting }), ); const getFormattingCheck = (result: any) => result.categories["Visual Elements"].results.find( (r: any) => r.item.id === "formatting", ); expect(getFormattingCheck(goodResult)?.passed).toBe(true); expect(getFormattingCheck(poorResult)?.passed).toBe(false); }); }); describe("Content Quality Validation", () => { it("should detect working code examples", async () => { const withCodeExamples = await createTestReadme( ` # Project \`\`\`javascript const lib = require('lib'); lib.doSomething(); \`\`\` \`\`\`bash npm install lib \`\`\` `, "with-code-README.md", ); const withoutCodeExamples = await createTestReadme( "# Project\n\nNo code examples", "no-code-examples-README.md", ); const withCodeResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: withCodeExamples }), ); const withoutCodeResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: withoutCodeExamples, }), ); const getCodeCheck = (result: any) => result.categories["Content Quality"].results.find( (r: any) => r.item.id === "working-examples", ); expect(getCodeCheck(withCodeResult)?.passed).toBe(true); expect(getCodeCheck(withoutCodeResult)?.passed).toBe(false); }); it("should validate appropriate length", async () => { const shortReadme = await createTestReadme( "# Project\n\nShort content", "short-README.md", ); const longContent = "# Project\n\n" + "Long line of content.\n".repeat(350); const longReadme = await createTestReadme(longContent, "long-README.md"); const shortResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: shortReadme }), ); const longResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: longReadme }), ); const getLengthCheck = (result: any) => result.categories["Content Quality"].results.find( (r: any) => r.item.id === "appropriate-length", ); expect(getLengthCheck(shortResult)?.passed).toBe(true); expect(getLengthCheck(longResult)?.passed).toBe(false); }); it("should validate scannable structure", async () => { const goodStructure = await createTestReadme( ` # Main Title ## Section 1 ### Subsection 1.1 - Item 1 - Item 2 ### Subsection 1.2 ## Section 2 ### Subsection 2.1 - Another item - Yet another item `, "good-structure-README.md", ); const poorStructure = await createTestReadme( ` # Title #### Skipped levels ## Back to level 2 `, "poor-structure-README.md", ); const goodResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: goodStructure }), ); const poorResult = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: poorStructure }), ); const getStructureCheck = (result: any) => result.categories["Content Quality"].results.find( (r: any) => r.item.id === "scannable-structure", ); expect(getStructureCheck(goodResult)?.passed).toBe(true); expect(getStructureCheck(poorResult)?.passed).toBe(false); }); }); describe("Report Generation", () => { it("should generate comprehensive report with all categories", async () => { const readme = await createTestReadme(` # Test Project > A test project description ## TL;DR Quick summary of the project. ## Quick Start \`\`\`bash npm install test-project \`\`\` ## Usage \`\`\`javascript const test = require('test-project'); test.run(); \`\`\` ## License MIT `); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readme }), ); expect(result.overallScore).toBeGreaterThan(0); expect(result.totalItems).toBeGreaterThan(0); expect(result.passedItems).toBeGreaterThan(0); expect(result.categories).toHaveProperty("Essential Sections"); expect(result.categories).toHaveProperty("Community Health"); expect(result.categories).toHaveProperty("Visual Elements"); expect(result.categories).toHaveProperty("Content Quality"); expect(result.wordCount).toBeGreaterThan(0); expect(result.estimatedReadTime).toBeGreaterThan(0); }); it("should calculate scores correctly", async () => { const perfectReadme = await createTestReadme(` # Perfect Project > An amazing project that does everything right [![Build Status](https://travis-ci.org/user/repo.svg)](https://travis-ci.org/user/repo) ## TL;DR This project is perfect and demonstrates all best practices. ## Quick Start \`\`\`bash npm install perfect-project \`\`\` ## Usage \`\`\`javascript const perfect = require('perfect-project'); perfect.doSomething(); \`\`\` ## Contributing See CONTRIBUTING.md for guidelines. ## License MIT © Author `); await createProjectFile("CONTRIBUTING.md", "Guidelines..."); await createProjectFile("LICENSE", "MIT License..."); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: perfectReadme, projectPath: tempDir, }), ); expect(result.overallScore).toBeGreaterThan(70); expect(result.categories["Essential Sections"].score).toBeGreaterThan(80); }); it("should provide helpful recommendations", async () => { const poorReadme = await createTestReadme( "# Poor Project\n\nMinimal content", ); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: poorReadme }), ); expect(result.recommendations.length).toBeGreaterThan(0); expect(result.overallScore).toBeLessThan(50); }); }); describe("Output Formatting", () => { it("should format console output correctly", async () => { const readme = await createTestReadme("# Test\n\nContent"); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readme, outputFormat: "console", }), ); const formatted = validator.formatReport(result, "console"); expect(formatted).toContain("📋 README Checklist Report"); expect(formatted).toContain("Overall Score:"); expect(formatted).toContain("Essential Sections"); expect(formatted).toContain("✅"); expect(formatted).toContain("❌"); }); it("should format markdown output correctly", async () => { const readme = await createTestReadme("# Test\n\nContent"); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readme, outputFormat: "markdown", }), ); const formatted = validator.formatReport(result, "markdown"); expect(formatted).toContain("# README Checklist Report"); expect(formatted).toContain("## Overall Score:"); expect(formatted).toContain("### Essential Sections"); expect(formatted).toContain("- ✅"); expect(formatted).toContain("- ❌"); }); it("should format JSON output correctly", async () => { const readme = await createTestReadme("# Test\n\nContent"); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readme, outputFormat: "json", }), ); const formatted = validator.formatReport(result, "json"); const parsed = JSON.parse(formatted); expect(parsed).toHaveProperty("overallScore"); expect(parsed).toHaveProperty("categories"); expect(parsed).toHaveProperty("recommendations"); }); }); describe("Error Handling", () => { it("should handle non-existent README file", async () => { const nonExistentPath = path.join(tempDir, "nonexistent.md"); await expect( validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: nonExistentPath }), ), ).rejects.toThrow(); }); it("should handle invalid project path gracefully", async () => { const readme = await createTestReadme("# Test\n\nContent"); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: readme, projectPath: "/invalid/path", }), ); // Should still work, just without project file context expect(result.overallScore).toBeGreaterThan(0); }); it("should handle empty README file", async () => { const emptyReadme = await createTestReadme("", "empty-README.md"); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: emptyReadme }), ); // Empty README should pass length test (0 words <= 300) and external links test (no links to fail) // but fail most other tests, resulting in a low overall score expect(result.overallScore).toBeLessThan(20); // Very low score due to missing content expect(result.passedItems).toBe(2); // Only length and external-links should pass expect(result.failedItems).toBe(15); // Most checks should fail }); }); describe("Suggestions Generation", () => { it("should provide specific suggestions for failed checks", async () => { const incompleteReadme = await createTestReadme( "# Project\n\nMinimal content", ); const result = await validateReadmeChecklist( ValidateReadmeChecklistSchema.parse({ readmePath: incompleteReadme }), ); const failedChecks = Object.values(result.categories) .flatMap((cat) => cat.results) .filter((r) => !r.passed && r.suggestions); expect(failedChecks.length).toBeGreaterThan(0); for (const check of failedChecks) { expect(check.suggestions).toBeDefined(); expect(check.suggestions!.length).toBeGreaterThan(0); } }); }); });

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