import fs from "node:fs"; import path from "node:path"; import fg from "fast-glob"; import { parseTestCasesFromDoc, parseItDescriptionsFromTest } from "../../lib/parse-doc-test-cases"; import type { CheckError, SyncCheckResult } from "../../lib/sync-check/shared"; import { moduleTestCaseCategories } from "./categories"; function toCamelCase(pascalCase: string): string { return pascalCase.charAt(0).toLowerCase() + pascalCase.slice(1); } export async function runModuleTestCaseSyncCheck( root: string, cwd: string, ): Promise { const errors: CheckError[] = []; let filesChecked = 0; const categories = moduleTestCaseCategories(root); for (const category of categories) { const docPaths = await fg(category.docPattern, { cwd }); for (const docPath of docPaths) { const docFullPath = path.resolve(cwd, docPath); const docContent = fs.readFileSync(docFullPath, "utf-8"); filesChecked += 1; const docTestCases = parseTestCasesFromDoc(docContent); if (docTestCases.length === 0) continue; const docBasename = path.basename(docPath, ".md"); const docsIndex = docPath.indexOf("/docs/"); if (docsIndex === -1) continue; const modulePath = docPath.substring(0, docsIndex); const testFileName = `${toCamelCase(docBasename)}.test.ts`; const testPath = path.join(modulePath, category.testDir, testFileName); const testFullPath = path.resolve(cwd, testPath); if (!fs.existsSync(testFullPath)) { errors.push({ type: "missing-test-file", category: category.name, docPath, sourcePath: testPath, expectedBasename: testFileName, }); continue; } filesChecked += 1; const testContent = fs.readFileSync(testFullPath, "utf-8"); const itDescriptions = parseItDescriptionsFromTest(testContent); const docSet = new Set(docTestCases); const testSet = new Set(itDescriptions); for (const docCase of docSet) { if (!testSet.has(docCase)) { errors.push({ type: "missing-test-case", category: category.name, docPath, sourcePath: testPath, expectedBasename: docCase, }); } } for (const testCase of testSet) { if (!docSet.has(testCase)) { errors.push({ type: "extra-test-case", category: category.name, docPath, sourcePath: testPath, expectedBasename: testCase, }); } } } } return { errors, filesChecked }; }