diff --git a/docs/guides/2-cli.md b/docs/guides/2-cli.md index 9b095b58e..d8f52641c 100644 --- a/docs/guides/2-cli.md +++ b/docs/guides/2-cli.md @@ -48,6 +48,7 @@ Other options include: --show-documentation-url show documentation url in output result [boolean] [default: false] -v, --verbose increase verbosity [boolean] -q, --quiet no logging - output only [boolean] + --html-include-json-path add json path in html format result [boolean] [default: false] ``` The Spectral CLI supports loading documents as YAML or JSON, and validation of OpenAPI v2/v3 documents via the built-in ruleset. diff --git a/packages/cli/src/commands/lint.ts b/packages/cli/src/commands/lint.ts index d84c6f89e..a7ee0a305 100644 --- a/packages/cli/src/commands/lint.ts +++ b/packages/cli/src/commands/lint.ts @@ -166,6 +166,11 @@ const lintCommand: CommandModule = { description: 'no logging - output only', type: 'boolean', }, + 'html-include-json-path': { + description: 'add json path in html format report', + type: 'boolean', + default: false, + }, }), async handler(args) { @@ -181,11 +186,13 @@ const lintCommand: CommandModule = { ignoreUnknownFormat, failOnUnmatchedGlobs, showDocumentationUrl, + htmlIncludeJsonPath, ...config } = args as unknown as ILintConfig & { documents: Array; failSeverity: FailSeverity; displayOnlyFailures: boolean; + htmlIncludeJsonPath: boolean; }; try { @@ -211,12 +218,11 @@ const lintCommand: CommandModule = { await Promise.all( format.map(f => { - const formattedOutput = formatOutput( - linterResult.results, - f, - { failSeverity: getDiagnosticSeverity(failSeverity) }, - linterResult.resolvedRuleset, - ); + const formatterOptions = { + failSeverity: getDiagnosticSeverity(failSeverity), + ...(f === OutputFormat.HTML && { htmlFormatterOptions: { includeJsonPath: htmlIncludeJsonPath } }), + }; + const formattedOutput = formatOutput(linterResult.results, f, formatterOptions, linterResult.resolvedRuleset); return writeOutput(formattedOutput, output?.[f] ?? ''); }), ); diff --git a/packages/formatters/src/__tests__/html.test.ts b/packages/formatters/src/__tests__/html.test.ts index c9d7c4564..6a1924840 100644 --- a/packages/formatters/src/__tests__/html.test.ts +++ b/packages/formatters/src/__tests__/html.test.ts @@ -8,8 +8,19 @@ describe('HTML formatter', () => { test('should display proper severity levels', () => { const result = parse(html(mixedErrors, { failSeverity: DiagnosticSeverity.Error })); const table = result.querySelector('table tbody'); - expect(table.innerHTML.trim()).toEqual(` - + expect(table.innerHTML.trim()).toEqual(expectedResult); + }); + test('should display proper severity levels with jsonPath', () => { + const result = parse( + html(mixedErrors, { failSeverity: DiagnosticSeverity.Error, htmlFormatterOptions: { includeJsonPath: true } }), + ); + const table = result.querySelector('table tbody'); + expect(table.innerHTML.trim()).toEqual(expectedResultWithJsonPath); + }); +}); + +const expectedResult = ` + [+] /home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json 6 problems (1 error, 1 warning, 3 infos, 1 hint) @@ -19,6 +30,7 @@ describe('HTML formatter', () => { hint Info object should contain \`contact\` object. + @@ -26,6 +38,7 @@ describe('HTML formatter', () => { warning OpenAPI object info \`description\` must be present and non-empty string. + @@ -33,6 +46,7 @@ describe('HTML formatter', () => { error Info must contain Stoplight + @@ -40,6 +54,7 @@ describe('HTML formatter', () => { information Operation \`description\` must be present and non-empty string. + @@ -47,6 +62,7 @@ describe('HTML formatter', () => { information Operation \`description\` must be present and non-empty string. + @@ -54,6 +70,59 @@ describe('HTML formatter', () => { information Operation \`description\` must be present and non-empty string. -`); - }); -}); + +`; + +const expectedResultWithJsonPath = ` + + [+] /home/Stoplight/spectral/src/__tests__/__fixtures__/petstore.oas3.json + 6 problems (1 error, 1 warning, 3 infos, 1 hint) + + + + 3:10 + hint + Info object should contain \`contact\` object. + info + + + + + 3:10 + warning + OpenAPI object info \`description\` must be present and non-empty string. + info + + + + + 5:14 + error + Info must contain Stoplight + info.title + + + + + 17:13 + information + Operation \`description\` must be present and non-empty string. + paths./pets.get + + + + + 64:14 + information + Operation \`description\` must be present and non-empty string. + paths./pets.post + + + + + 86:13 + information + Operation \`description\` must be present and non-empty string. + paths./pets/{petId}.get + +`; diff --git a/packages/formatters/src/html/html-template-message.html b/packages/formatters/src/html/html-template-message.html index 74ac39f98..da747f13f 100644 --- a/packages/formatters/src/html/html-template-message.html +++ b/packages/formatters/src/html/html-template-message.html @@ -2,5 +2,6 @@ <%= line %>:<%= character %> <%= severity %> <%- message %> + <%- jsonpath %> <% if(documentationUrl) { %>documentation<% } %> diff --git a/packages/formatters/src/html/html-template-result.html b/packages/formatters/src/html/html-template-result.html index f4a55933c..67ee5807a 100644 --- a/packages/formatters/src/html/html-template-result.html +++ b/packages/formatters/src/html/html-template-result.html @@ -1,5 +1,5 @@ - + [+] <%- filePath %> <%- summary %> diff --git a/packages/formatters/src/html/index.ts b/packages/formatters/src/html/index.ts index 33bda5257..20a351f7b 100644 --- a/packages/formatters/src/html/index.ts +++ b/packages/formatters/src/html/index.ts @@ -28,6 +28,7 @@ import type { IRuleResult } from '@stoplight/spectral-core'; import { Formatter } from '../types'; import { getHighestSeverity, getSeverityName, getSummary, getSummaryForSource, groupBySource } from '../utils'; import templates from './templates'; +import { printPath, PrintStyle } from '@stoplight/spectral-runtime'; // ------------------------------------------------------------------------------ // Helpers @@ -37,7 +38,7 @@ const pageTemplate = template(templates['html-template-page.html']); const messageTemplate = template(templates['html-template-message.html']); const resultTemplate = template(templates['html-template-result.html']); -function renderMessages(messages: IRuleResult[], parentIndex: number): string { +function renderMessages(messages: IRuleResult[], parentIndex: number, includeJsonPath: boolean): string { return messages .map(message => { const line = message.range.start.line + 1; @@ -50,13 +51,14 @@ function renderMessages(messages: IRuleResult[], parentIndex: number): string { severity: getSeverityName(message.severity), message: message.message, code: message.code, + jsonpath: includeJsonPath ? printPath(message.path, PrintStyle.Dot) : '', documentationUrl: message.documentationUrl, }); }) .join('\n'); } -function renderResults(groupedResults: Dictionary): string { +function renderResults(groupedResults: Dictionary, includeJsonPath = false): string { return Object.keys(groupedResults) .map( (source, index) => @@ -68,7 +70,7 @@ function renderResults(groupedResults: Dictionary): string { : getSeverityName(getHighestSeverity(groupedResults[source])), filePath: source, summary: getSummaryForSource(groupedResults[source]), - }) + renderMessages(groupedResults[source], index), + }) + renderMessages(groupedResults[source], index, includeJsonPath), ) .join('\n'); } @@ -77,7 +79,7 @@ function renderResults(groupedResults: Dictionary): string { // Public Interface // ------------------------------------------------------------------------------ -export const html: Formatter = results => { +export const html: Formatter = (results, options) => { const color = results.length === 0 ? 'success' : getSeverityName(getHighestSeverity(results)); const groupedResults = groupBySource(results); @@ -85,6 +87,6 @@ export const html: Formatter = results => { date: new Date(), color, summary: getSummary(groupedResults), - results: renderResults(groupedResults), + results: renderResults(groupedResults, options.htmlFormatterOptions?.includeJsonPath), }); }; diff --git a/packages/formatters/src/types.ts b/packages/formatters/src/types.ts index 8624af331..4b6811736 100644 --- a/packages/formatters/src/types.ts +++ b/packages/formatters/src/types.ts @@ -1,8 +1,13 @@ import { ISpectralDiagnostic, Ruleset } from '@stoplight/spectral-core'; import type { DiagnosticSeverity } from '@stoplight/types'; +export type HtmlFormatterOptions = { + includeJsonPath: boolean; +}; + export type FormatterOptions = { failSeverity: DiagnosticSeverity; + htmlFormatterOptions?: HtmlFormatterOptions; }; export type FormatterContext = { diff --git a/test-harness/scenarios/documentation-url/results-format-html.scenario b/test-harness/scenarios/documentation-url/results-format-html.scenario index 4fa05cb70..0e2bfc3b4 100644 --- a/test-harness/scenarios/documentation-url/results-format-html.scenario +++ b/test-harness/scenarios/documentation-url/results-format-html.scenario @@ -156,7 +156,7 @@ info: - @@ -165,6 +165,7 @@ info: + @@ -173,12 +174,14 @@ info: + + diff --git a/test-harness/scenarios/formats/results-format-html.scenario b/test-harness/scenarios/formats/results-format-html.scenario index 75342e396..ee6da5eb5 100644 --- a/test-harness/scenarios/formats/results-format-html.scenario +++ b/test-harness/scenarios/formats/results-format-html.scenario @@ -154,7 +154,7 @@ info:
+ [+] {document} 3 problems (0 errors, 3 warnings, 0 infos, 0 hints) 1:1 warning "servers" must be present and non-empty array. documentation
warning Info object must have a "contact" object.
- @@ -164,6 +164,7 @@ info: + @@ -171,6 +172,7 @@ info: + @@ -178,6 +180,7 @@ info: + diff --git a/test-harness/scenarios/formats/too-few-outputs.scenario b/test-harness/scenarios/formats/too-few-outputs.scenario index 22acc57e2..39893dbcd 100644 --- a/test-harness/scenarios/formats/too-few-outputs.scenario +++ b/test-harness/scenarios/formats/too-few-outputs.scenario @@ -31,5 +31,6 @@ Options: --show-documentation-url show documentation url in output result [boolean] [default: false] -v, --verbose increase verbosity [boolean] -q, --quiet no logging - output only [boolean] + --html-include-json-path add json path in html format report [boolean] [default: false] The number of outputs must match the number of formats diff --git a/test-harness/scenarios/formats/too-many-outputs.scenario b/test-harness/scenarios/formats/too-many-outputs.scenario index 5355283d5..4a49cbf8c 100644 --- a/test-harness/scenarios/formats/too-many-outputs.scenario +++ b/test-harness/scenarios/formats/too-many-outputs.scenario @@ -31,5 +31,6 @@ Options: --show-documentation-url show documentation url in output result [boolean] [default: false] -v, --verbose increase verbosity [boolean] -q, --quiet no logging - output only [boolean] + --html-include-json-path add json path in html format report [boolean] [default: false] The number of outputs must match the number of formats diff --git a/test-harness/scenarios/formats/unmatched-outputs.scenario b/test-harness/scenarios/formats/unmatched-outputs.scenario index 84828a5a7..4f87b5e9e 100644 --- a/test-harness/scenarios/formats/unmatched-outputs.scenario +++ b/test-harness/scenarios/formats/unmatched-outputs.scenario @@ -31,5 +31,6 @@ Options: --show-documentation-url show documentation url in output result [boolean] [default: false] -v, --verbose increase verbosity [boolean] -q, --quiet no logging - output only [boolean] + --html-include-json-path add json path in html format report [boolean] [default: false] Missing outputs for the following formats: html diff --git a/test-harness/scenarios/help-no-document.scenario b/test-harness/scenarios/help-no-document.scenario index 4a28bb1b2..e1da8011e 100644 --- a/test-harness/scenarios/help-no-document.scenario +++ b/test-harness/scenarios/help-no-document.scenario @@ -32,5 +32,6 @@ Options: --show-documentation-url show documentation url in output result [boolean] [default: false] -v, --verbose increase verbosity [boolean] -q, --quiet no logging - output only [boolean] + --html-include-json-path add json path in html format report [boolean] [default: false] No documents provided. diff --git a/test-harness/scenarios/strict-options.scenario b/test-harness/scenarios/strict-options.scenario index d1acb40cc..37c56ef83 100644 --- a/test-harness/scenarios/strict-options.scenario +++ b/test-harness/scenarios/strict-options.scenario @@ -32,5 +32,6 @@ Options: --show-documentation-url show documentation url in output result [boolean] [default: false] -v, --verbose increase verbosity [boolean] -q, --quiet no logging - output only [boolean] + --html-include-json-path add json path in html format report [boolean] [default: false] Unknown arguments: i, p
+ [+] {document} 3 problems (0 errors, 3 warnings, 0 infos, 0 hints) warning "servers" must be present and non-empty array.