mirror of
https://github.com/DavidAnson/markdownlint.git
synced 2025-12-24 01:40:13 +01:00
Reimplement markdownlint-test-scenarios.js to be simpler, more efficient, and use AVA test snapshots for all file-based test scenarios.
This commit is contained in:
parent
7bb80d19b1
commit
820f2699ca
52 changed files with 38769 additions and 4531 deletions
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
const fs = require("fs").promises;
|
||||
const path = require("path");
|
||||
const { promisify } = require("util");
|
||||
const test = require("ava").default;
|
||||
const { version } = require("../package.json");
|
||||
const markdownlint = require("../lib/markdownlint");
|
||||
const { markdownlint } = require("../lib/markdownlint").promises;
|
||||
const helpers = require("../helpers");
|
||||
|
||||
/**
|
||||
|
|
@ -17,152 +15,93 @@ const helpers = require("../helpers");
|
|||
* @returns {Function} Test function.
|
||||
*/
|
||||
function createTestForFile(file) {
|
||||
const markdownlintPromise = promisify(markdownlint);
|
||||
return function testForFile(t) {
|
||||
const detailedResults = /[/\\]detailed-results-/.test(file);
|
||||
t.plan(detailedResults ? 3 : 2);
|
||||
const resultsFile = file.replace(/\.md$/, ".results.json");
|
||||
const fixedFile = file.replace(/\.md$/, ".md.fixed");
|
||||
return (t) => {
|
||||
// Read configuration for test file
|
||||
const contentPromise = fs.readFile(file, "utf8");
|
||||
const configFile = file.replace(/\.md$/, ".json");
|
||||
let config = null;
|
||||
const actualPromise = fs.promises.stat(configFile)
|
||||
return fs.access(configFile)
|
||||
.then(
|
||||
function configFileExists() {
|
||||
return fs.promises.readFile(configFile, "utf8")
|
||||
// @ts-ignore
|
||||
.then(JSON.parse);
|
||||
},
|
||||
function noConfigFile() {
|
||||
return {};
|
||||
() => fs.readFile(configFile, "utf8").then(JSON.parse),
|
||||
() => {}
|
||||
// Read and lint Markdown test file
|
||||
).then((config) => Promise.all([
|
||||
config,
|
||||
contentPromise,
|
||||
markdownlint({
|
||||
"files": [ file ],
|
||||
config
|
||||
})
|
||||
.then(
|
||||
function captureConfig(configResult) {
|
||||
config = configResult;
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function lintWithConfig() {
|
||||
return markdownlintPromise({
|
||||
"files": [ file ],
|
||||
config
|
||||
]))
|
||||
// Compare expected results and snapshot
|
||||
.then((params) => {
|
||||
const [ config, content, results ] = params;
|
||||
// Canonicalize version number
|
||||
const errors = results[file];
|
||||
errors
|
||||
.filter((error) => !!error.ruleInformation)
|
||||
.forEach((error) => {
|
||||
error.ruleInformation =
|
||||
error.ruleInformation.replace(/v\d+\.\d+\.\d+/, "v0.0.0");
|
||||
});
|
||||
})
|
||||
.then(
|
||||
function diffFixedFiles(resultVersion2or3) {
|
||||
return detailedResults ?
|
||||
Promise.all([
|
||||
markdownlintPromise({
|
||||
"files": [ file ],
|
||||
config
|
||||
}),
|
||||
fs.promises.readFile(file, "utf8"),
|
||||
fs.promises.readFile(fixedFile, "utf8")
|
||||
])
|
||||
.then(function validateApplyFixes(fulfillments) {
|
||||
const [ resultVersion3, content, expected ] = fulfillments;
|
||||
const errors = resultVersion3[file];
|
||||
const actual = helpers.applyFixes(content, errors);
|
||||
// Uncomment the following line to update *.md.fixed files
|
||||
// fs.writeFileSync(fixedFile, actual, "utf8");
|
||||
t.is(actual, expected, "Unexpected output from applyFixes.");
|
||||
return resultVersion2or3;
|
||||
}) :
|
||||
resultVersion2or3;
|
||||
}
|
||||
)
|
||||
.then(
|
||||
function convertResultVersion2To0(resultVersion2or3) {
|
||||
const result0 = {};
|
||||
const result2or3 = resultVersion2or3[file];
|
||||
result2or3.forEach(function forResult(result) {
|
||||
const ruleName = result.ruleNames[0];
|
||||
const lineNumbers = result0[ruleName] || [];
|
||||
if (!lineNumbers.includes(result.lineNumber)) {
|
||||
lineNumbers.push(result.lineNumber);
|
||||
}
|
||||
result0[ruleName] = lineNumbers;
|
||||
});
|
||||
return [ result0, result2or3 ];
|
||||
}
|
||||
);
|
||||
const expectedPromise = detailedResults ?
|
||||
fs.promises.readFile(resultsFile, "utf8")
|
||||
.then(
|
||||
function fileContents(contents) {
|
||||
// @ts-ignore
|
||||
const errorObjects = JSON.parse(contents);
|
||||
errorObjects.forEach(function forObject(errorObject) {
|
||||
if (errorObject.ruleInformation) {
|
||||
errorObject.ruleInformation =
|
||||
errorObject.ruleInformation.replace("v0.0.0", `v${version}`);
|
||||
}
|
||||
});
|
||||
return errorObjects;
|
||||
}) :
|
||||
fs.promises.readFile(file, "utf8")
|
||||
.then(
|
||||
function fileContents(contents) {
|
||||
// @ts-ignore
|
||||
const lines = contents.split(helpers.newLineRe);
|
||||
const results = {};
|
||||
lines.forEach(function forLine(line, lineNum) {
|
||||
const regex = /\{(MD\d+)(?::(\d+))?\}/g;
|
||||
let match = null;
|
||||
while ((match = regex.exec(line))) {
|
||||
const rule = match[1];
|
||||
const errors = results[rule] || [];
|
||||
errors.push(
|
||||
match[2] ?
|
||||
Number.parseInt(match[2], 10) :
|
||||
lineNum + 1
|
||||
);
|
||||
results[rule] = errors;
|
||||
}
|
||||
});
|
||||
const sortedResults = {};
|
||||
Object.keys(results).sort().forEach(function forKey(key) {
|
||||
sortedResults[key] = results[key];
|
||||
});
|
||||
return sortedResults;
|
||||
});
|
||||
return Promise.all([ actualPromise, expectedPromise ])
|
||||
.then(
|
||||
function compareResults(fulfillments) {
|
||||
const [ [ actual0, actual2or3 ], expected ] = fulfillments;
|
||||
const actual = detailedResults ? actual2or3 : actual0;
|
||||
t.deepEqual(actual, expected, "Line numbers are not correct.");
|
||||
return actual2or3;
|
||||
})
|
||||
.then(
|
||||
function verifyFixes(errors) {
|
||||
if (detailedResults) {
|
||||
return t.true(true);
|
||||
// Match identified issues by MD### markers
|
||||
const marker = /\{(MD\d+)(?::(\d+))?\}/g;
|
||||
const lines = content.split(helpers.newLineRe);
|
||||
const expected = {};
|
||||
lines.forEach((line, index) => {
|
||||
let match = null;
|
||||
while ((match = marker.exec(line))) {
|
||||
const rule = match[1];
|
||||
// eslint-disable-next-line no-multi-assign
|
||||
const indices = expected[rule] = expected[rule] || [];
|
||||
indices.push(match[2] ? Number.parseInt(match[2], 10) : index + 1);
|
||||
}
|
||||
return fs.promises.readFile(file, "utf8")
|
||||
.then(
|
||||
function applyFixes(content) {
|
||||
const corrections = helpers.applyFixes(content, errors);
|
||||
return markdownlintPromise({
|
||||
"strings": {
|
||||
"input": corrections
|
||||
},
|
||||
config
|
||||
});
|
||||
})
|
||||
.then(
|
||||
function checkFixes(newErrors) {
|
||||
const unfixed = newErrors.input
|
||||
.filter((error) => !!error.fixInfo);
|
||||
t.deepEqual(unfixed, [], "Fixable error was not fixed.");
|
||||
}
|
||||
);
|
||||
})
|
||||
});
|
||||
if (Object.keys(expected).length > 0) {
|
||||
const actual = {};
|
||||
errors.forEach((error) => {
|
||||
const rule = error.ruleNames[0];
|
||||
// eslint-disable-next-line no-multi-assign
|
||||
const indices = actual[rule] = actual[rule] || [];
|
||||
if (indices[indices.length - 1] !== error.lineNumber) {
|
||||
indices.push(error.lineNumber);
|
||||
}
|
||||
});
|
||||
t.deepEqual(actual, expected, "Too few or too many issues found.");
|
||||
}
|
||||
// Create snapshot
|
||||
const fixed = helpers.applyFixes(content, errors)
|
||||
.replace(/\r\n/g, "\n");
|
||||
t.snapshot({
|
||||
errors,
|
||||
fixed
|
||||
});
|
||||
return {
|
||||
config,
|
||||
fixed
|
||||
};
|
||||
})
|
||||
// Identify missing fixes
|
||||
.then((params) => {
|
||||
const { config, fixed } = params;
|
||||
return markdownlint({
|
||||
"strings": {
|
||||
"input": fixed
|
||||
},
|
||||
config
|
||||
}).then((results) => {
|
||||
const unfixed = results.input.filter((error) => !!error.fixInfo);
|
||||
t.deepEqual(unfixed, [], "Fixable error(s) not fixed.");
|
||||
});
|
||||
})
|
||||
.catch()
|
||||
.then(t.done);
|
||||
};
|
||||
}
|
||||
|
||||
fs.readdirSync("./test")
|
||||
require("fs").readdirSync("./test")
|
||||
.filter((file) => /\.md$/.test(file))
|
||||
// @ts-ignore
|
||||
.forEach((file) => test(file, createTestForFile(path.join("./test", file))));
|
||||
.forEach((file) => test(
|
||||
file,
|
||||
createTestForFile(path.join("./test", file))
|
||||
));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue