diff --git a/src/cli.js b/src/cli.js index 4589ccc..820efb9 100644 --- a/src/cli.js +++ b/src/cli.js @@ -10,6 +10,7 @@ import { getCatalogs, getMatchForFilename } from "./catalogs.js"; import { getFiles } from "./glob.js"; import { getFromUrlOrFile } from "./io.js"; import logger from "./logger.js"; +import { getDocumentLocation } from "./output-formatters.js"; import { parseFile } from "./parser.js"; const EXIT = { @@ -61,10 +62,11 @@ async function validateDocument( ); result.valid = valid; result.errors = errors; + const documentLocation = getDocumentLocation(result); if (valid) { - logger.success(`${fileLocation} is valid\n`); + logger.success(`${documentLocation} is valid\n`); } else { - logger.error(`${fileLocation} is invalid\n`); + logger.error(`${documentLocation} is invalid\n`); } result.code = valid ? EXIT.VALID : EXIT.INVALID; @@ -120,12 +122,11 @@ async function validateFile(filename, config, plugins, cache) { let results = []; for (let i = 0; i < documents.length; i++) { - const fileLocation = - documents.length === 1 ? filename : `${filename}[${i}]`; + const documentIndex = documents.length === 1 ? null : i; results.push( await validateDocument( - fileLocation, - i, + filename, + documentIndex, documents[i], schemaLocation, schema, diff --git a/src/cli.spec.js b/src/cli.spec.js index f390ff5..8c13485 100644 --- a/src/cli.spec.js +++ b/src/cli.spec.js @@ -1016,7 +1016,7 @@ describe("CLI", function () { tearDown(); }); - it("should log errors in text format when format is text", async function () { + it("should log errors in text format when format is text (single doc)", async function () { return cli({ patterns: [ "{./testfiles/files/valid.json,./testfiles/files/invalid.json,./testfiles/files/not-supported.txt}", @@ -1032,7 +1032,21 @@ describe("CLI", function () { }); }); - it("should output json report when format is json", async function () { + it("should log errors in text format when format is text (multi doc)", async function () { + return cli({ + patterns: ["./testfiles/files/multi-doc.yaml"], + schema: "./testfiles/schemas/schema.json", + format: "text", + }).then(() => { + assert( + logger.stdout.includes( + "./testfiles/files/multi-doc.yaml[2]#/num must be number\n", + ), + ); + }); + }); + + it("should output json report when format is json (single doc)", async function () { return cli({ patterns: [ "{./testfiles/files/valid.json,./testfiles/files/invalid.json,./testfiles/files/not-supported.txt}", @@ -1056,7 +1070,7 @@ describe("CLI", function () { }, ], fileLocation: "./testfiles/files/invalid.json", - documentIndex: 0, + documentIndex: null, schemaLocation: "./testfiles/schemas/schema.json", valid: false, }, @@ -1072,10 +1086,58 @@ describe("CLI", function () { code: 0, errors: [], fileLocation: "./testfiles/files/valid.json", + documentIndex: null, + schemaLocation: "./testfiles/schemas/schema.json", + valid: true, + }, + ], + }; + assert.deepStrictEqual(JSON.parse(logger.stdout[0]), expected); + }); + }); + + it("should output json report when format is json (multi doc)", async function () { + return cli({ + patterns: ["./testfiles/files/multi-doc.yaml"], + schema: "./testfiles/schemas/schema.json", + format: "json", + }).then(() => { + const expected = { + results: [ + { + code: 0, + errors: [], + fileLocation: "./testfiles/files/multi-doc.yaml", documentIndex: 0, schemaLocation: "./testfiles/schemas/schema.json", valid: true, }, + { + code: 0, + errors: [], + fileLocation: "./testfiles/files/multi-doc.yaml", + documentIndex: 1, + schemaLocation: "./testfiles/schemas/schema.json", + valid: true, + }, + { + code: 99, + errors: [ + { + instancePath: "/num", + keyword: "type", + message: "must be number", + params: { + type: "number", + }, + schemaPath: "#/properties/num/type", + }, + ], + fileLocation: "./testfiles/files/multi-doc.yaml", + documentIndex: 2, + schemaLocation: "./testfiles/schemas/schema.json", + valid: false, + }, ], }; assert.deepStrictEqual(JSON.parse(logger.stdout[0]), expected); diff --git a/src/output-formatters.js b/src/output-formatters.js index 6b69dab..4d02a4f 100644 --- a/src/output-formatters.js +++ b/src/output-formatters.js @@ -1,13 +1,20 @@ import Ajv from "ajv"; -function formatErrors(filename, errors) { +function getDocumentLocation(result) { + if (result.documentIndex == null) { + return result.fileLocation; + } + return `${result.fileLocation}[${result.documentIndex}]`; +} + +function formatErrors(location, errors) { const ajv = new Ajv(); return ( ajv.errorsText(errors, { separator: "\n", - dataVar: filename + "#", + dataVar: location + "#", }) + "\n" ); } -export { formatErrors }; +export { formatErrors, getDocumentLocation }; diff --git a/src/plugins.js b/src/plugins.js index 0bca5ae..03431ee 100644 --- a/src/plugins.js +++ b/src/plugins.js @@ -209,7 +209,9 @@ async function loadAllPlugins(userPlugins) { * @property {number | null} documentIndex - Some file formats allow multiple * documents to be embedded in one file (e.g: * [yaml](https://www.yaml.info/learn/document.html)). In these cases, - * `documentIndex` identifies the sub document within the file. + * `documentIndex` identifies is used to identify the sub document within the + * file. `documentIndex` will be `null` when there is a one-to-one + * relationship between file and document. * @property {string | null} schemaLocation - Location of the schema used to * validate this file if one could be found. `null` if no schema was found. * @property {boolean | null} valid - Result of the validation (true/false) if a diff --git a/src/plugins/output-text.js b/src/plugins/output-text.js index 39bb144..86bcf42 100644 --- a/src/plugins/output-text.js +++ b/src/plugins/output-text.js @@ -1,5 +1,5 @@ import { BasePlugin } from "../plugins.js"; -import { formatErrors } from "../output-formatters.js"; +import { formatErrors, getDocumentLocation } from "../output-formatters.js"; class TextOutput extends BasePlugin { static name = "v8r-plugin-text-output"; @@ -10,7 +10,7 @@ class TextOutput extends BasePlugin { getSingleResultLogMessage(result, fileLocation, format) { if (result.valid === false && format === "text") { - return formatErrors(fileLocation, result.errors); + return formatErrors(getDocumentLocation(result), result.errors); } } }