diff --git a/lib/rules.js b/lib/rules.js index 5547e8ed..bb58717c 100644 --- a/lib/rules.js +++ b/lib/rules.js @@ -898,7 +898,8 @@ module.exports = [ var left = linkText.trimLeft().length !== linkText.length; var right = linkText.trimRight().length !== linkText.length; if (left || right) { - errors.addContext(token.lineNumber, linkText, left, right); + errors.addContext( + token.lineNumber, "[" + linkText + "]", left, right); } } else if (inLink) { linkText += child.content; diff --git a/test/detailed-results-MD001-MD010.md b/test/detailed-results-MD001-MD010.md new file mode 100644 index 00000000..57781ff7 --- /dev/null +++ b/test/detailed-results-MD001-MD010.md @@ -0,0 +1,17 @@ +## One + +#### Two + +### Three ### + +* Alpha + * Bravo + +- Charlie + + * Delta + * Echo + +Text + +Text text diff --git a/test/detailed-results-MD001-MD010.results.json b/test/detailed-results-MD001-MD010.results.json new file mode 100644 index 00000000..2248fae9 --- /dev/null +++ b/test/detailed-results-MD001-MD010.results.json @@ -0,0 +1,74 @@ +[ + { + "lineNumber": 3, + "ruleName": "MD001", + "ruleAlias": "header-increment", + "ruleDescription": "Header levels should only increment by one level at a time", + "errorDetail": "Expected: h3; Actual: h4", + "errorContext": null + }, + { + "lineNumber": 1, + "ruleName": "MD002", + "ruleAlias": "first-header-h1", + "ruleDescription": "First header should be a top level header", + "errorDetail": "Expected: h1; Actual: h2", + "errorContext": null + }, + { + "lineNumber": 5, + "ruleName": "MD003", + "ruleAlias": "header-style", + "ruleDescription": "Header style", + "errorDetail": "Expected: atx; Actual: atx_closed", + "errorContext": null + }, + { + "lineNumber": 10, + "ruleName": "MD004", + "ruleAlias": "ul-style", + "ruleDescription": "Unordered list style", + "errorDetail": "Expected: asterisk; Actual: dash", + "errorContext": null + }, + { + "lineNumber": 8, + "ruleName": "MD005", + "ruleAlias": "list-indent", + "ruleDescription": "Inconsistent indentation for list items at the same level", + "errorDetail": "Expected: 0; Actual: 1", + "errorContext": null + }, + { + "lineNumber": 12, + "ruleName": "MD006", + "ruleAlias": "ul-start-left", + "ruleDescription": "Consider starting bulleted lists at the beginning of the line", + "errorDetail": "Expected: 0; Actual: 1", + "errorContext": null + }, + { + "lineNumber": 12, + "ruleName": "MD007", + "ruleAlias": "ul-indent", + "ruleDescription": "Unordered list indentation", + "errorDetail": "Expected: 2; Actual: 1", + "errorContext": null + }, + { + "lineNumber": 15, + "ruleName": "MD009", + "ruleAlias": "no-trailing-spaces", + "ruleDescription": "Trailing spaces", + "errorDetail": "Expected: 0; Actual: 1", + "errorContext": null + }, + { + "lineNumber": 17, + "ruleName": "MD010", + "ruleAlias": "no-hard-tabs", + "ruleDescription": "Hard tabs", + "errorDetail": "Column: 5", + "errorContext": null + } +] \ No newline at end of file diff --git a/test/detailed-results-MD011-MD021.md b/test/detailed-results-MD011-MD021.md new file mode 100644 index 00000000..28ca6c88 --- /dev/null +++ b/test/detailed-results-MD011-MD021.md @@ -0,0 +1,16 @@ + + +A (reversed)[link] example. + + +123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 + + $ command with no output + +#No space A + +# Multiple spaces A + +#No space B# + +# Multiple spaces B # diff --git a/test/detailed-results-MD011-MD021.results.json b/test/detailed-results-MD011-MD021.results.json new file mode 100644 index 00000000..d33eda0c --- /dev/null +++ b/test/detailed-results-MD011-MD021.results.json @@ -0,0 +1,66 @@ +[ + { + "lineNumber": 3, + "ruleName": "MD011", + "ruleAlias": "no-reversed-links", + "ruleDescription": "Reversed link syntax", + "errorDetail": "(reversed)[link]", + "errorContext": null + }, + { + "lineNumber": 5, + "ruleName": "MD012", + "ruleAlias": "no-multiple-blanks", + "ruleDescription": "Multiple consecutive blank lines", + "errorDetail": "Expected: 1; Actual: 2", + "errorContext": null + }, + { + "lineNumber": 6, + "ruleName": "MD013", + "ruleAlias": "line-length", + "ruleDescription": "Line length", + "errorDetail": "Expected: 80; Actual: 99", + "errorContext": null + }, + { + "lineNumber": 8, + "ruleName": "MD014", + "ruleAlias": "commands-show-output", + "ruleDescription": "Dollar signs used before commands without showing output", + "errorDetail": null, + "errorContext": "$ command with no output" + }, + { + "lineNumber": 10, + "ruleName": "MD018", + "ruleAlias": "no-missing-space-atx", + "ruleDescription": "No space after hash on atx style header", + "errorDetail": null, + "errorContext": "#No space A" + }, + { + "lineNumber": 12, + "ruleName": "MD019", + "ruleAlias": "no-multiple-space-atx", + "ruleDescription": "Multiple spaces after hash on atx style header", + "errorDetail": null, + "errorContext": "# Multiple spaces A" + }, + { + "lineNumber": 14, + "ruleName": "MD020", + "ruleAlias": "no-missing-space-closed-atx", + "ruleDescription": "No space inside hashes on closed atx style header", + "errorDetail": null, + "errorContext": "#No space B#" + }, + { + "lineNumber": 16, + "ruleName": "MD021", + "ruleAlias": "no-multiple-space-closed-atx", + "ruleDescription": "Multiple spaces inside hashes on closed atx style header", + "errorDetail": null, + "errorContext": "# Multiple spaces B #" + } +] \ No newline at end of file diff --git a/test/detailed-results-MD022-MD030.md b/test/detailed-results-MD022-MD030.md new file mode 100644 index 00000000..1673fd69 --- /dev/null +++ b/test/detailed-results-MD022-MD030.md @@ -0,0 +1,13 @@ + # Heading +Text + +# Heading + +## Another heading. + +> Multiple spaces + +> Blank line above + +1. Alpha +2. Beta diff --git a/test/detailed-results-MD022-MD030.results.json b/test/detailed-results-MD022-MD030.results.json new file mode 100644 index 00000000..197651db --- /dev/null +++ b/test/detailed-results-MD022-MD030.results.json @@ -0,0 +1,74 @@ +[ + { + "lineNumber": 1, + "ruleName": "MD022", + "ruleAlias": "blanks-around-headers", + "ruleDescription": "Headers should be surrounded by blank lines", + "errorDetail": null, + "errorContext": "# Heading" + }, + { + "lineNumber": 1, + "ruleName": "MD023", + "ruleAlias": "header-start-left", + "ruleDescription": "Headers must start at the beginning of the line", + "errorDetail": null, + "errorContext": " # Heading" + }, + { + "lineNumber": 4, + "ruleName": "MD024", + "ruleAlias": "no-duplicate-header", + "ruleDescription": "Multiple headers with the same content", + "errorDetail": null, + "errorContext": "# Heading" + }, + { + "lineNumber": 4, + "ruleName": "MD025", + "ruleAlias": "single-h1", + "ruleDescription": "Multiple top level headers in the same document", + "errorDetail": null, + "errorContext": "# Heading" + }, + { + "lineNumber": 6, + "ruleName": "MD026", + "ruleAlias": "no-trailing-punctuation", + "ruleDescription": "Trailing punctuation in header", + "errorDetail": "Punctuation: '.'", + "errorContext": null + }, + { + "lineNumber": 8, + "ruleName": "MD027", + "ruleAlias": "no-multiple-space-blockquote", + "ruleDescription": "Multiple spaces after blockquote symbol", + "errorDetail": null, + "errorContext": "> Multiple spaces" + }, + { + "lineNumber": 9, + "ruleName": "MD028", + "ruleAlias": "no-blanks-blockquote", + "ruleDescription": "Blank line inside blockquote", + "errorDetail": null, + "errorContext": null + }, + { + "lineNumber": 13, + "ruleName": "MD029", + "ruleAlias": "ol-prefix", + "ruleDescription": "Ordered list item prefix", + "errorDetail": "Expected: 1; Actual: 2", + "errorContext": null + }, + { + "lineNumber": 13, + "ruleName": "MD030", + "ruleAlias": "list-marker-space", + "ruleDescription": "Spaces after list markers", + "errorDetail": "Expected: 1; Actual: 2", + "errorContext": null + } +] \ No newline at end of file diff --git a/test/detailed-results-MD031-MD040.md b/test/detailed-results-MD031-MD040.md new file mode 100644 index 00000000..48217cdd --- /dev/null +++ b/test/detailed-results-MD031-MD040.md @@ -0,0 +1,22 @@ +```js +debugger; +``` +* List + +Inline
HTML + +Bare http://example.com link + +--- +*** + +*Emphasis* + +Space * inside * emphasis + +Space ` inside ` code span + +Space [ inside ](link) text + +``` +``` diff --git a/test/detailed-results-MD031-MD040.results.json b/test/detailed-results-MD031-MD040.results.json new file mode 100644 index 00000000..2d258f0e --- /dev/null +++ b/test/detailed-results-MD031-MD040.results.json @@ -0,0 +1,82 @@ +[ + { + "lineNumber": 3, + "ruleName": "MD031", + "ruleAlias": "blanks-around-fences", + "ruleDescription": "Fenced code blocks should be surrounded by blank lines", + "errorDetail": null, + "errorContext": "```" + }, + { + "lineNumber": 4, + "ruleName": "MD032", + "ruleAlias": "blanks-around-lists", + "ruleDescription": "Lists should be surrounded by blank lines", + "errorDetail": null, + "errorContext": "* List" + }, + { + "lineNumber": 6, + "ruleName": "MD033", + "ruleAlias": "no-inline-html", + "ruleDescription": "Inline HTML", + "errorDetail": "Element: hr", + "errorContext": null + }, + { + "lineNumber": 8, + "ruleName": "MD034", + "ruleAlias": "no-bare-urls", + "ruleDescription": "Bare URL used", + "errorDetail": null, + "errorContext": "http://example.com" + }, + { + "lineNumber": 11, + "ruleName": "MD035", + "ruleAlias": "hr-style", + "ruleDescription": "Horizontal rule style", + "errorDetail": "Expected: ---; Actual: ***", + "errorContext": null + }, + { + "lineNumber": 13, + "ruleName": "MD036", + "ruleAlias": "no-emphasis-as-header", + "ruleDescription": "Emphasis used instead of a header", + "errorDetail": null, + "errorContext": "Emphasis" + }, + { + "lineNumber": 15, + "ruleName": "MD037", + "ruleAlias": "no-space-in-emphasis", + "ruleDescription": "Spaces inside emphasis markers", + "errorDetail": null, + "errorContext": "* inside *" + }, + { + "lineNumber": 17, + "ruleName": "MD038", + "ruleAlias": "no-space-in-code", + "ruleDescription": "Spaces inside code span elements", + "errorDetail": null, + "errorContext": "` inside `" + }, + { + "lineNumber": 19, + "ruleName": "MD039", + "ruleAlias": "no-space-in-links", + "ruleDescription": "Spaces inside link text", + "errorDetail": null, + "errorContext": "[ inside ]" + }, + { + "lineNumber": 21, + "ruleName": "MD040", + "ruleAlias": "fenced-code-language", + "ruleDescription": "Fenced code blocks should have a language specified", + "errorDetail": null, + "errorContext": "```" + } +] \ No newline at end of file diff --git a/test/detailed-results-MD041-MD043.json b/test/detailed-results-MD041-MD043.json new file mode 100644 index 00000000..7b669757 --- /dev/null +++ b/test/detailed-results-MD041-MD043.json @@ -0,0 +1,9 @@ +{ + "default": true, + "MD041": true, + "MD043": { + "headers": [ + "# Header" + ] + } +} diff --git a/test/detailed-results-MD041-MD043.md b/test/detailed-results-MD041-MD043.md new file mode 100644 index 00000000..985d782d --- /dev/null +++ b/test/detailed-results-MD041-MD043.md @@ -0,0 +1,3 @@ +Not a header + +An [empty]() link diff --git a/test/detailed-results-MD041-MD043.results.json b/test/detailed-results-MD041-MD043.results.json new file mode 100644 index 00000000..426894f5 --- /dev/null +++ b/test/detailed-results-MD041-MD043.results.json @@ -0,0 +1,26 @@ +[ + { + "lineNumber": 1, + "ruleName": "MD041", + "ruleAlias": "first-line-h1", + "ruleDescription": "First line in file should be a top level header", + "errorDetail": null, + "errorContext": "Not a header" + }, + { + "lineNumber": 3, + "ruleName": "MD042", + "ruleAlias": "no-empty-links", + "ruleDescription": "No empty links", + "errorDetail": null, + "errorContext": "[empty]" + }, + { + "lineNumber": 4, + "ruleName": "MD043", + "ruleAlias": "required-headers", + "ruleDescription": "Required header structure", + "errorDetail": null, + "errorContext": "# Header" + } +] \ No newline at end of file diff --git a/test/markdownlint-test.js b/test/markdownlint-test.js index 426a94ef..a9159666 100644 --- a/test/markdownlint-test.js +++ b/test/markdownlint-test.js @@ -15,15 +15,14 @@ var configSchema = require("../schema/markdownlint-config-schema.json"); function createTestForFile(file) { return function testForFile(test) { test.expect(1); + var detailedResults = /[\/\\]detailed-results-/.test(file); + var resultsFile = file.replace(/\.md$/, ".results.json"); var configFile = file.replace(/\.md$/, ".json"); var actualPromise = Q.nfcall(fs.stat, configFile) .then( function configFileExists() { return Q.nfcall(fs.readFile, configFile, shared.utf8Encoding) - .then( - function configFileContents(contents) { - return JSON.parse(contents); - }); + .then(JSON.parse); }, function noConfigFile() { return {}; @@ -33,30 +32,34 @@ function createTestForFile(file) { var mergedConfig = shared.assign(shared.clone(defaultConfig), config); return Q.nfcall(markdownlint, { "files": [ file ], - "config": mergedConfig + "config": mergedConfig, + "resultVersion": detailedResults ? 1 : 0 }); }); - var expectedPromise = Q.nfcall(fs.readFile, file, shared.utf8Encoding) - .then( - function fileContents(contents) { - var lines = contents.split(shared.newLineRe); - var results = {}; - lines.forEach(function forLine(line, lineNum) { - var regex = /\{(MD\d+)(?::(\d+))?\}/g; - var match = null; - while ((match = regex.exec(line))) { - var rule = match[1]; - var errors = results[rule] || []; - errors.push(match[2] ? parseInt(match[2], 10) : lineNum + 1); - results[rule] = errors; - } + var expectedPromise = detailedResults ? + Q.nfcall(fs.readFile, resultsFile, shared.utf8Encoding) + .then(JSON.parse) : + Q.nfcall(fs.readFile, file, shared.utf8Encoding) + .then( + function fileContents(contents) { + var lines = contents.split(shared.newLineRe); + var results = {}; + lines.forEach(function forLine(line, lineNum) { + var regex = /\{(MD\d+)(?::(\d+))?\}/g; + var match = null; + while ((match = regex.exec(line))) { + var rule = match[1]; + var errors = results[rule] || []; + errors.push(match[2] ? parseInt(match[2], 10) : lineNum + 1); + results[rule] = errors; + } + }); + var sortedResults = {}; + Object.keys(results).sort().forEach(function forKey(key) { + sortedResults[key] = results[key]; + }); + return sortedResults; }); - var sortedResults = {}; - Object.keys(results).sort().forEach(function forKey(key) { - sortedResults[key] = results[key]; - }); - return sortedResults; - }); Q.all([ actualPromise, expectedPromise ]) .then( function compareResults(fulfillments) { @@ -1026,10 +1029,11 @@ module.exports.doc = function doc(test) { module.exports.validateConfigSchema = function validateConfigSchema(test) { var jsonFileRe = /\.json$/i; + var resultsFileRe = /\.results\.json$/i; var testDirectory = __dirname; var testFiles = fs.readdirSync(testDirectory); testFiles.filter(function filterFile(file) { - return jsonFileRe.test(file); + return jsonFileRe.test(file) && !resultsFileRe.test(file); }).forEach(function forFile(file) { var data = fs.readFileSync(path.join(testDirectory, file)); test.ok(