From 0103ac40540bcbc98dce65b39a5bb93412321522 Mon Sep 17 00:00:00 2001 From: Pedro Gude Teira Date: Wed, 20 Nov 2024 11:13:43 +0100 Subject: [PATCH] feat: allow set markdownlint custom config --- .../code/package-lock.json | 4 +- .../certification-service/code/package.json | 2 +- .../code/src/evaluate/markdownEvaluate.js | 5 +- .../code/src/verify/documentationLinter.js | 8 +- .../code/src/verify/lint.js | 4 +- .../code/test/data/README.md | 7 ++ .../test/evaluate/markdownEvaluate.test.js | 101 +++++++++++++++ .../test/verify/documentationLinter.test.js | 118 ++++++++++++++++++ .../code/test/verify/lint.test.js | 83 ++++++++++++ 9 files changed, 321 insertions(+), 11 deletions(-) create mode 100644 packages/certification-service/code/test/evaluate/markdownEvaluate.test.js create mode 100644 packages/certification-service/code/test/verify/documentationLinter.test.js create mode 100644 packages/certification-service/code/test/verify/lint.test.js diff --git a/packages/certification-service/code/package-lock.json b/packages/certification-service/code/package-lock.json index b675911..64828ce 100644 --- a/packages/certification-service/code/package-lock.json +++ b/packages/certification-service/code/package-lock.json @@ -1,12 +1,12 @@ { "name": "@inditextech/apicertification", - "version": "1.1.0", + "version": "1.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@inditextech/apicertification", - "version": "1.1.0", + "version": "1.3.0", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { diff --git a/packages/certification-service/code/package.json b/packages/certification-service/code/package.json index f2c3eb5..2f0aa86 100644 --- a/packages/certification-service/code/package.json +++ b/packages/certification-service/code/package.json @@ -1,6 +1,6 @@ { "name": "@inditextech/apicertification", - "version": "1.2.0", + "version": "1.3.0", "description": "API scoring service", "author": "InditexTech Open Source Office", "license": "Apache-2.0", diff --git a/packages/certification-service/code/src/evaluate/markdownEvaluate.js b/packages/certification-service/code/src/evaluate/markdownEvaluate.js index f1b6c41..bcc15ba 100644 --- a/packages/certification-service/code/src/evaluate/markdownEvaluate.js +++ b/packages/certification-service/code/src/evaluate/markdownEvaluate.js @@ -7,10 +7,11 @@ const { WARN_SEVERITY } = require("./severity"); const { configValue } = require("../config/config"); const path = require("path"); -const markdownEvaluate = async (filepath, ruleset) => { +const markdownEvaluate = async (filepath, ruleset, customConfig) => { + const defaultConfig = configValue("cerws.markdown.markdown-lint-config"); const options = { files: [filepath], - config: markdownlint.readConfigSync(path.join(process.cwd(), configValue("cerws.markdown.markdown-lint-config"))), + config: customConfig ? customConfig : markdownlint.readConfigSync(path.join(process.cwd(), defaultConfig)), customRules: ruleset.all, }; diff --git a/packages/certification-service/code/src/verify/documentationLinter.js b/packages/certification-service/code/src/verify/documentationLinter.js index a5738c4..a9487e1 100644 --- a/packages/certification-service/code/src/verify/documentationLinter.js +++ b/packages/certification-service/code/src/verify/documentationLinter.js @@ -16,7 +16,7 @@ const CUSTOM_RULES_NAMES = { }; class DocumentationLinter { - static async lintDocumentation(validationType, rootFolder, api, documentation) { + static async lintDocumentation(validationType, rootFolder, api, documentation, customConfig) { if (!validationType || validationType === VALIDATION_TYPE_DOCUMENTATION) { const apiReadmeFile = { fullPath: path.join(rootFolder, api["definition-path"], "README.md"), @@ -25,19 +25,19 @@ class DocumentationLinter { customRules: DocumentationRuleset.API.resolvedRuleset, }; - const readmeIssues = await this.validateReadme(apiReadmeFile); + const readmeIssues = await this.validateReadme(apiReadmeFile, customConfig); documentation.documentationValidation.issues.push(...readmeIssues); } } - static async validateReadme(readmeFile) { + static async validateReadme(readmeFile, customConfig) { const issues = []; const readmePath = readmeFile.fullPath; if (fs.existsSync(readmePath)) { if (this.checkMarkdownContent(readmePath)) { - issues.push(...(await lintFileWithMarkdownLint(readmePath, readmeFile.customRules))); + issues.push(...(await lintFileWithMarkdownLint(readmePath, readmeFile.customRules, customConfig))); issues.forEach((issue) => (issue.fileName = readmeFile.fileName)); } else { issues.push({ diff --git a/packages/certification-service/code/src/verify/lint.js b/packages/certification-service/code/src/verify/lint.js index ac0ec5b..4318593 100644 --- a/packages/certification-service/code/src/verify/lint.js +++ b/packages/certification-service/code/src/verify/lint.js @@ -24,9 +24,9 @@ const lintFilesWithProtolint = async (files, customFlags) => { return evaluateProtolint(files, customFlags); }; -const lintFileWithMarkdownLint = async (file, ruleset) => { +const lintFileWithMarkdownLint = async (file, ruleset, customConfig) => { logger.info(`Linting md file ${file} with ruleset ${ruleset}`); - return Object.values(await markdownEvaluate(file, ruleset))[0]; + return Object.values(await markdownEvaluate(file, ruleset, customConfig))[0]; }; const generateRandomFolder = () => { diff --git a/packages/certification-service/code/test/data/README.md b/packages/certification-service/code/test/data/README.md index 6ba48fe..0f77184 100644 --- a/packages/certification-service/code/test/data/README.md +++ b/packages/certification-service/code/test/data/README.md @@ -5,3 +5,10 @@ SPDX-License-Identifier: Apache-2.0 --> # Title +Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. + +Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. + +Nulla consequat massa quis enim. Donec pede justo, fringilla vel, aliquet nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, venenatis vitae, justo. + +Nullam dictum felis eu pede mollis pretium. Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra quis, feugiat a, \ No newline at end of file diff --git a/packages/certification-service/code/test/evaluate/markdownEvaluate.test.js b/packages/certification-service/code/test/evaluate/markdownEvaluate.test.js new file mode 100644 index 0000000..0084438 --- /dev/null +++ b/packages/certification-service/code/test/evaluate/markdownEvaluate.test.js @@ -0,0 +1,101 @@ +// SPDX-FileCopyrightText: 2024 Industria de Diseño Textil S.A. INDITEX +// +// SPDX-License-Identifier: Apache-2.0 + +const markdownlint = require("markdownlint"); +const path = require("path"); +const { markdownEvaluate } = require("../../src/evaluate/markdownEvaluate"); +const { DocumentationRuleset } = require("../../src/evaluate/documentation/documentationRuleset"); +const { configValue } = require("../../src/config/config"); + +jest.mock("markdownlint"); + +describe("Tests Markdown Evaluation", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test("Should call markdownlint with default config file", async () => { + const config = { + default: true, + extends: null, + MD013: false, + severities: [ + { + id: "MD025", + severity: 1, + }, + ], + }; + const result = { + "README.md": [ + { + lineNumber: 1, + ruleNames: ["EX_MD050", "custom-mandatory-about"], + ruleDescription: "Mandatory About Section", + ruleInformation: null, + errorDetail: null, + errorContext: "Section 'about' must exist", + errorRange: null, + fixInfo: null, + }, + { + lineNumber: 3, + ruleNames: ["MD025", "single-title", "single-h1"], + ruleDescription: "Multiple top-level headings in the same document", + ruleInformation: "https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md025.md", + errorDetail: null, + errorContext: "# Top level heading", + errorRange: null, + fixInfo: null, + }, + ], + }; + jest.spyOn(markdownlint, "readConfigSync").mockReturnValue(config); + jest.spyOn(markdownlint.promises, "markdownlint").mockResolvedValue(result); + + await markdownEvaluate("README.md", DocumentationRuleset.API.resolvedRuleset); + + expect(markdownlint.readConfigSync).toHaveBeenCalledWith( + path.join(process.cwd(), configValue("cerws.markdown.markdown-lint-config")), + ); + expect(markdownlint.promises.markdownlint).toHaveBeenCalledWith({ + files: ["README.md"], + config: config, + customRules: DocumentationRuleset.API.resolvedRuleset.all, + }); + }); + + test("Should call markdownlint with custom config file", async () => { + const customConfig = { + default: true, + extends: null, + MD013: false, + MD001: false, + }; + const result = { + "README.md": [ + { + lineNumber: 1, + ruleNames: ["EX_MD050", "custom-mandatory-about"], + ruleDescription: "Mandatory About Section", + ruleInformation: null, + errorDetail: null, + errorContext: "Section 'about' must exist", + errorRange: null, + fixInfo: null, + }, + ], + }; + + jest.spyOn(markdownlint.promises, "markdownlint").mockResolvedValue(result); + + await markdownEvaluate("README.md", DocumentationRuleset.API.resolvedRuleset, customConfig); + + expect(markdownlint.promises.markdownlint).toHaveBeenCalledWith({ + files: ["README.md"], + config: customConfig, + customRules: DocumentationRuleset.API.resolvedRuleset.all, + }); + }); +}); diff --git a/packages/certification-service/code/test/verify/documentationLinter.test.js b/packages/certification-service/code/test/verify/documentationLinter.test.js new file mode 100644 index 0000000..dd018b3 --- /dev/null +++ b/packages/certification-service/code/test/verify/documentationLinter.test.js @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: 2024 Industria de Diseño Textil S.A. INDITEX +// +// SPDX-License-Identifier: Apache-2.0 + +const path = require("path"); +const { DocumentationLinter } = require("../../src/verify/documentationLinter"); +const { VALIDATION_TYPE_DOCUMENTATION } = require("../../src/verify/types"); +const { DocumentationRuleset } = require("../../src/evaluate/documentation/documentationRuleset"); +const linter = require("../../src/verify/lint"); + +jest.mock("../../src/verify/lint"); + +describe("Documentation Linter tests", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test("Should lint with default config", async () => { + const documentation = { documentationValidation: { validationType: VALIDATION_TYPE_DOCUMENTATION, issues: [] } }; + const customConfig = { + default: true, + extends: null, + MD013: false, + MD001: false, + }; + + jest.spyOn(linter, "lintFileWithMarkdownLint").mockResolvedValueOnce(issues); + + await DocumentationLinter.lintDocumentation( + VALIDATION_TYPE_DOCUMENTATION, + path.join(__dirname, "../data/"), + { "definition-path": "" }, + documentation, + undefined, + ); + + expect(linter.lintFileWithMarkdownLint).toHaveBeenCalledWith( + path.join(__dirname, "../data/README.md"), + DocumentationRuleset.API.resolvedRuleset, + undefined, + ); + + expect(documentation.documentationValidation.issues).toEqual(issues); + }); + + test("Should lint with custom config", async () => { + const documentation = { documentationValidation: { validationType: VALIDATION_TYPE_DOCUMENTATION, issues: [] } }; + const customConfig = { + default: true, + extends: null, + MD013: false, + MD001: false, + }; + + jest.spyOn(linter, "lintFileWithMarkdownLint").mockResolvedValueOnce(issues); + + await DocumentationLinter.lintDocumentation( + undefined, + path.join(__dirname, "../data/"), + { "definition-path": "" }, + documentation, + customConfig, + ); + + expect(linter.lintFileWithMarkdownLint).toHaveBeenCalledWith( + path.join(__dirname, "../data/README.md"), + DocumentationRuleset.API.resolvedRuleset, + customConfig, + ); + + expect(documentation.documentationValidation.issues).toStrictEqual(issues); + }); + + const issues = [ + { + lineNumber: 1, + ruleNames: ["EX_MD050", "custom-mandatory-about"], + ruleDescription: "Mandatory About Section", + ruleInformation: null, + errorDetail: null, + errorContext: "Section 'about' must exist", + errorRange: null, + fixInfo: null, + severity: 1, + fileName: "README.md", + }, + { + lineNumber: 7, + ruleNames: ["MD022", "blanks-around-headings"], + ruleDescription: "Headings should be surrounded by blank lines", + ruleInformation: "https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md", + errorDetail: "Expected: 1; Actual: 0; Below", + errorContext: "# Title", + errorRange: null, + fixInfo: { + lineNumber: 8, + insertText: "\n", + }, + severity: 1, + fileName: "README.md", + }, + { + lineNumber: 14, + ruleNames: ["MD047", "single-trailing-newline"], + ruleDescription: "Files should end with a single newline character", + ruleInformation: "https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md047.md", + errorDetail: null, + errorContext: null, + errorRange: [267, 1], + fixInfo: { + editColumn: 268, + insertText: "\n", + }, + severity: 1, + fileName: "README.md", + }, + ]; +}); diff --git a/packages/certification-service/code/test/verify/lint.test.js b/packages/certification-service/code/test/verify/lint.test.js new file mode 100644 index 0000000..e8c55fa --- /dev/null +++ b/packages/certification-service/code/test/verify/lint.test.js @@ -0,0 +1,83 @@ +// SPDX-FileCopyrightText: 2024 Industria de Diseño Textil S.A. INDITEX +// +// SPDX-License-Identifier: Apache-2.0 + +const { DocumentationRuleset } = require("../../src/evaluate/documentation/documentationRuleset"); +const markdownEvaluate = require("../../src/evaluate/markdownEvaluate"); +const { lintFileWithMarkdownLint } = require("../../src/verify/lint"); + +jest.mock("../../src/evaluate/markdownEvaluate"); + +describe("Lint Tests", () => { + describe("lintFileWithMarkdownLint", () => { + test("Should lint markdown with default", async () => { + const file = "README.md"; + const ruleset = DocumentationRuleset.API.resolvedRuleset; + + jest.spyOn(markdownEvaluate, "markdownEvaluate").mockResolvedValueOnce(lintResult); + + const result = await lintFileWithMarkdownLint(file, ruleset, undefined); + + expect(result).toStrictEqual(lintResult["README.md"]); + expect(markdownEvaluate.markdownEvaluate).toHaveBeenCalledWith(file, ruleset, undefined); + }); + test("Should lint markdown with custom config", async () => { + const file = "README.md"; + const ruleset = DocumentationRuleset.API.resolvedRuleset; + const customConfig = { + default: true, + extends: null, + MD013: false, + MD001: false, + }; + + jest.spyOn(markdownEvaluate, "markdownEvaluate").mockResolvedValueOnce(lintResult); + + const result = await lintFileWithMarkdownLint(file, ruleset, customConfig); + + expect(result).toStrictEqual(lintResult["README.md"]); + expect(markdownEvaluate.markdownEvaluate).toHaveBeenCalledWith(file, ruleset, customConfig); + }); + }); + + const lintResult = { + "README.md": [ + { + lineNumber: 1, + ruleNames: ["EX_MD050", "custom-mandatory-about"], + ruleDescription: "Mandatory About Section", + ruleInformation: null, + errorDetail: null, + errorContext: "Section 'about' must exist", + errorRange: null, + fixInfo: null, + }, + { + lineNumber: 7, + ruleNames: ["MD022", "blanks-around-headings"], + ruleDescription: "Headings should be surrounded by blank lines", + ruleInformation: "https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md022.md", + errorDetail: "Expected: 1; Actual: 0; Below", + errorContext: "# Title", + errorRange: null, + fixInfo: { + lineNumber: 8, + insertText: "\n", + }, + }, + { + lineNumber: 14, + ruleNames: ["MD047", "single-trailing-newline"], + ruleDescription: "Files should end with a single newline character", + ruleInformation: "https://github.com/DavidAnson/markdownlint/blob/v0.34.0/doc/md047.md", + errorDetail: null, + errorContext: null, + errorRange: [267, 1], + fixInfo: { + editColumn: 268, + insertText: "\n", + }, + }, + ], + }; +});